Calendar container
This commit is contained in:
parent
7cffb316d9
commit
02ef330cd9
121
src/components/Calendar.tsx
Normal file
121
src/components/Calendar.tsx
Normal file
@ -0,0 +1,121 @@
|
||||
import * as React from "react"
|
||||
import { dowToString } from "../lib/utils";
|
||||
import type { Event, SecretsCalendar } from "../lib/interfaces";
|
||||
import * as styles from "../styles/containers/Calendar.module.css"
|
||||
|
||||
const CALENDAR_REFRESH_INTERVAL = 15 * 60 * 1000;
|
||||
const CALENDAR_TOKEN_REFRESH_INTERVAL = 45 * 60 * 1000;
|
||||
|
||||
function daysDifference(date1: Date, date2: Date) {
|
||||
if (date1.getTime() > date2.getTime()) {
|
||||
let tmp = date2;
|
||||
date2 = date1;
|
||||
date1 = tmp;
|
||||
}
|
||||
return Math.ceil((date2.getTime() - date1.getTime()) / 1000 / 60 / 60 / 24);
|
||||
}
|
||||
|
||||
const Calendar = ({ secrets }: { secrets: SecretsCalendar }) => {
|
||||
const [token, setToken] = React.useState("")
|
||||
const [events, setEvents] = React.useState([])
|
||||
|
||||
React.useEffect(() => {
|
||||
requestToken().then(pullCalendar)
|
||||
const calendarInterval = setInterval(pullCalendar, CALENDAR_REFRESH_INTERVAL);
|
||||
const calendarTokenInterval = setInterval(requestToken, CALENDAR_TOKEN_REFRESH_INTERVAL);
|
||||
|
||||
return () => {
|
||||
clearInterval(calendarInterval);
|
||||
clearInterval(calendarTokenInterval);
|
||||
}
|
||||
}, [])
|
||||
|
||||
const processEventData = (events: Event[]) => {
|
||||
const eventTable = [];
|
||||
let lastDate = "";
|
||||
let i = 0;
|
||||
for (const event of events) {
|
||||
const startDate = new Date(event.start.date ? event.start.date : event.start.dateTime)
|
||||
const startDateString = `${startDate.getFullYear()}${startDate.getMonth()}${startDate.getDate()}`
|
||||
if (startDateString !== lastDate) {
|
||||
lastDate = startDateString;
|
||||
const dayDiff = daysDifference(new Date(), startDate);
|
||||
let dayDiffString: string;
|
||||
switch (dayDiff) {
|
||||
case 0: dayDiffString = "Heute"
|
||||
break;
|
||||
case 1: dayDiffString = "Morgen"
|
||||
break;
|
||||
default: dayDiffString = `${dayDiff} Tage`;
|
||||
break;
|
||||
}
|
||||
eventTable.push(
|
||||
<tr key={startDateString}><td colSpan={2} className={styles.calendarDateHeader}>
|
||||
{dowToString(startDate.getDay())}, {startDate.getDate()}. {startDate.getMonth() + 1}
|
||||
<span className={styles.calendarDateHeaderSub}>({dayDiffString})</span>
|
||||
</td></tr>
|
||||
);
|
||||
}
|
||||
|
||||
if (event.start.date) {
|
||||
eventTable.push(
|
||||
<tr key={++i} className={styles.calendarEntry}><td colSpan={2}>{event.summary}</td></tr>
|
||||
);
|
||||
} else {
|
||||
eventTable.push(
|
||||
<tr key={++i} className={styles.calendarEntry}>
|
||||
<td>{event.summary}</td>
|
||||
<td>{startDate.getHours()}:{startDate.getMinutes().toString().padStart(2, "0")}</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
}
|
||||
return eventTable;
|
||||
}
|
||||
|
||||
const requestToken = async () => {
|
||||
const response = await fetch("https://accounts.google.com/o/oauth2/token", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
grant_type: "refresh_token",
|
||||
refresh_token: secrets.refreshToken,
|
||||
client_id: secrets.clientId,
|
||||
clientSecret: secrets.clientSecret
|
||||
})
|
||||
});
|
||||
const token = await response.json();
|
||||
setToken(token.access_token);
|
||||
return token.access_token;
|
||||
}
|
||||
|
||||
const pullCalendar = async (provToken: string) => {
|
||||
const correctToken = provToken || token;
|
||||
if (!correctToken || correctToken === "") return;
|
||||
|
||||
const timeMin = new Date();
|
||||
timeMin.setHours(timeMin.getHours() - 1);
|
||||
const params = new URLSearchParams({
|
||||
orderBy: "startTime",
|
||||
fields: "items(creator,start,summary)",
|
||||
singleEvents: "true",
|
||||
maxResults: "10",
|
||||
timeMin: timeMin.toISOString(),
|
||||
key: (secrets.apiKey as string)
|
||||
});
|
||||
|
||||
const response = await fetch(`https://www.googleapis.com/calendar/v3/calendars/${secrets.calendarId}/events?${params.toString()}`, {
|
||||
headers: { Authorization: `Bearer ${correctToken}` }
|
||||
});
|
||||
const events = await response.json();
|
||||
setEvents(processEventData(events.items));
|
||||
}
|
||||
|
||||
return <div className={`container ${styles.container}`}>
|
||||
<table><tbody>
|
||||
{events}
|
||||
</tbody></table>
|
||||
</div>
|
||||
|
||||
}
|
||||
|
||||
export default Calendar;
|
@ -1,46 +1,16 @@
|
||||
import * as React from "react"
|
||||
import type { WeatherInfo } from "../lib/interfaces";
|
||||
import { dowToString, monthToString } from "../lib/utils";
|
||||
import type { SecretsWeather, WeatherInfo } from "../lib/interfaces";
|
||||
import * as styles from "../styles/containers/WeatherAndTime.module.css";
|
||||
|
||||
const WEATHER_REFRESH_INTERVAL = 15 * 60 * 1000;
|
||||
|
||||
function dowToString(dow: number) {
|
||||
switch (dow) {
|
||||
case 0: return "Sonntag";
|
||||
case 1: return "Montag";
|
||||
case 2: return "Dienstag";
|
||||
case 3: return "Mittwoch";
|
||||
case 4: return "Donnerstag";
|
||||
case 5: return "Freitag";
|
||||
case 6: return "Samstag";
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
|
||||
function monthToString(month: number) {
|
||||
switch (month) {
|
||||
case 0: return "Januar";
|
||||
case 1: return "Februar";
|
||||
case 2: return "März";
|
||||
case 3: return "April";
|
||||
case 4: return "Mail";
|
||||
case 5: return "Juni";
|
||||
case 6: return "Juli";
|
||||
case 7: return "August";
|
||||
case 8: return "September";
|
||||
case 9: return "Oktober";
|
||||
case 10: return "November";
|
||||
case 11: return "Dezember";
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
|
||||
function getWeatherIcon(icon: string) {
|
||||
const ImportedIcon = require(`../images/weather/${icon}.svg`);
|
||||
return <ImportedIcon />
|
||||
}
|
||||
|
||||
const WeatherAndTimeContainer = ({ apiKey, coords }) => {
|
||||
const WeatherAndTime = ({ secrets }: { secrets: SecretsWeather }) => {
|
||||
const [date, setDate] = React.useState(new Date());
|
||||
const [weather, setWeather] = React.useState<WeatherInfo>({
|
||||
currently: {
|
||||
@ -77,7 +47,7 @@ const WeatherAndTimeContainer = ({ apiKey, coords }) => {
|
||||
}, [])
|
||||
|
||||
const pullWeather = () => {
|
||||
fetch(`https://api.pirateweather.net/forecast/${apiKey}/${coords}?exclude=minutely,hourly&lang=de&units=ca`)
|
||||
fetch(`https://api.pirateweather.net/forecast/${secrets.apiKey}/${secrets.coords}?exclude=minutely,hourly&lang=de&units=ca`)
|
||||
.then(resp => resp.json())
|
||||
.then(setWeather);
|
||||
}
|
||||
@ -135,4 +105,4 @@ const WeatherAndTimeContainer = ({ apiKey, coords }) => {
|
||||
</div>)
|
||||
}
|
||||
|
||||
export default WeatherAndTimeContainer;
|
||||
export default WeatherAndTime;
|
@ -1,3 +1,16 @@
|
||||
export interface SecretsWeather {
|
||||
apiKey: string;
|
||||
coords: string;
|
||||
}
|
||||
|
||||
export interface SecretsCalendar {
|
||||
apiKey: string;
|
||||
calendarId: string;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
refreshToken: string;
|
||||
}
|
||||
|
||||
export interface WeatherInfo {
|
||||
currently: {
|
||||
icon: string;
|
||||
@ -13,3 +26,8 @@ export interface WeatherInfo {
|
||||
}[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface Event {
|
||||
start: { dateTime: string; date?: string; };
|
||||
summary: string;
|
||||
}
|
30
src/lib/utils.ts
Normal file
30
src/lib/utils.ts
Normal file
@ -0,0 +1,30 @@
|
||||
export function dowToString(dow: number) {
|
||||
switch (dow) {
|
||||
case 0: return "Sonntag";
|
||||
case 1: return "Montag";
|
||||
case 2: return "Dienstag";
|
||||
case 3: return "Mittwoch";
|
||||
case 4: return "Donnerstag";
|
||||
case 5: return "Freitag";
|
||||
case 6: return "Samstag";
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
|
||||
export function monthToString(month: number) {
|
||||
switch (month) {
|
||||
case 0: return "Januar";
|
||||
case 1: return "Februar";
|
||||
case 2: return "März";
|
||||
case 3: return "April";
|
||||
case 4: return "Mail";
|
||||
case 5: return "Juni";
|
||||
case 6: return "Juli";
|
||||
case 7: return "August";
|
||||
case 8: return "September";
|
||||
case 9: return "Oktober";
|
||||
case 10: return "November";
|
||||
case 11: return "Dezember";
|
||||
default: return;
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import * as React from "react"
|
||||
import secrets from "../../secrets.json"
|
||||
import Calendar from "../components/Calendar";
|
||||
import WeatherAndTimeContainer from "../components/WeatherAndTime"
|
||||
|
||||
function importAll(r) {
|
||||
@ -29,7 +30,8 @@ const IndexPage = () => {
|
||||
}
|
||||
|
||||
return (<main style={{ backgroundImage: `url(${images[currentBg].default})` }}>
|
||||
<WeatherAndTimeContainer apiKey={secrets.weather.apiKey} coords={secrets.weather.coords}/>
|
||||
<WeatherAndTimeContainer secrets={secrets.weather} />
|
||||
<Calendar secrets={secrets.calendar} />
|
||||
</main>)
|
||||
}
|
||||
|
||||
|
27
src/styles/containers/Calendar.module.css
Normal file
27
src/styles/containers/Calendar.module.css
Normal file
@ -0,0 +1,27 @@
|
||||
.container {
|
||||
grid-area: calendar;
|
||||
}
|
||||
.container table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.calendarDateHeader {
|
||||
font-weight: bold;
|
||||
font-size: 2vh;
|
||||
background: rgba(0, 0, 0, .1);
|
||||
}
|
||||
.calendarDateHeaderSub {
|
||||
font-size: 1.5vh;
|
||||
padding-top: 0.4vh;
|
||||
float: right;
|
||||
}
|
||||
.container table td {
|
||||
padding: 5px;
|
||||
}
|
||||
.container tr td:first-of-type {
|
||||
width: 100%;
|
||||
}
|
||||
.container tr.calendarEntry {
|
||||
border-top: 1px solid #000000;
|
||||
font-size: 1.8vh;
|
||||
}
|
Loading…
Reference in New Issue
Block a user