diff --git a/web/bun.lockb b/web/bun.lockb index aa6e75d..67dd111 100755 Binary files a/web/bun.lockb and b/web/bun.lockb differ diff --git a/web/package.json b/web/package.json index 8284321..facb215 100644 --- a/web/package.json +++ b/web/package.json @@ -11,32 +11,32 @@ "astro": "astro" }, "dependencies": { - "@astrojs/check": "^0.7.0", - "@astrojs/mdx": "^3.1.2", + "@astrojs/check": "^0.9.3", + "@astrojs/mdx": "^3.1.6", "@astrojs/tailwind": "^5.1.0", - "@astrojs/ts-plugin": "^1.8.0", - "@photo-sphere-viewer/core": "^5.8.1", - "@photo-sphere-viewer/markers-plugin": "^5.8.1", - "@photo-sphere-viewer/virtual-tour-plugin": "^5.8.1", + "@astrojs/ts-plugin": "^1.10.2", + "@photo-sphere-viewer/core": "^5.10.0", + "@photo-sphere-viewer/markers-plugin": "^5.10.0", + "@photo-sphere-viewer/virtual-tour-plugin": "^5.10.0", "@unom/style": "git+https://git.unom.io/unom/style.git", - "astro": "^4.11.3", - "autoprefixer": "^10.4.19", + "astro": "^4.15.6", + "autoprefixer": "^10.4.20", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "ical": "^0.8.0", "keen-slider": "^6.8.6", - "lucide-astro": "^0.399.0", - "marked": "^13.0.1", + "lucide-astro": "^0.441.0", + "marked": "^14.1.2", "motion": "^10.18.0", "pannellum": "^2.5.6", - "postcss": "^8.4.39", - "postcss-custom-media": "^10.0.7", + "postcss": "^8.4.47", + "postcss-custom-media": "^11.0.1", "postcss-import": "^16.1.0", - "prettier-plugin-astro": "^0.14.0", - "tailwind-merge": "^2.3.0", + "prettier-plugin-astro": "^0.14.1", + "tailwind-merge": "^2.5.2", "tailwindcss": "^3.4.4", "tailwindcss-animate": "^1.0.7", - "typescript": "^5.5.3" + "typescript": "^5.6.2" }, "devDependencies": { "@types/ical": "^0.8.3", diff --git a/web/src/lib/events-api.ts b/web/src/lib/events-api.ts index 58680fb..df50a89 100644 --- a/web/src/lib/events-api.ts +++ b/web/src/lib/events-api.ts @@ -1,7 +1,7 @@ import ICal from "ical"; const CALENDAR_ICAL_URL = - "https://hub.vspace.one/remote.php/dav/public-calendars/f6MfGLnsGScRqd4Y?export"; + "https://hub.vspace.one/remote.php/dav/public-calendars/f6MfGLnsGScRqd4Y?export"; //const CALENDAR_URL = "https://hub.vspace.one/apps/calendar/p/f6MfGLnsGScRqd4Y"; // Only look at this much next occurences, to prevent infinite loop @@ -11,130 +11,126 @@ const DESIGNATOR_LINK = "Link"; const DESIGNATOR_DOWNLOAD = "Download"; export async function getCalenderIcs() { - const response = await fetch(CALENDAR_ICAL_URL); - const data = await response.text(); + const response = await fetch(CALENDAR_ICAL_URL); + const data = await response.text(); - return data; + return data; } export type Event = { - title: string; - start: Date; - end: Date; - location: string; - description?: string; - link: string | undefined; - download: string | undefined; - isRecurring: boolean; + title: string; + start: Date | undefined; + end: Date | undefined; + location: string; + description?: string; + link: string | undefined; + download: string | undefined; + isRecurring: boolean; }; export async function getEvents() { - // Fetch the calender ics - const icsData = await getCalenderIcs(); + // Fetch the calender ics + const icsData = await getCalenderIcs(); - const events = ICal.parseICS(icsData); + const events = ICal.parseICS(icsData); - // Filters are split up for readability - const futureAndRecurringEvents = Object.values(events) - // Only include events of type VEVENT - .filter((e) => e.type === "VEVENT") - // Exclude Past events that aren't recurring - .filter((e) => { - const isFuture = Number(e.end) > Date.now(); - const isRecurring = !!e.rrule; + // Filters are split up for readability + const futureAndRecurringEvents = Object.values(events) + // Only include events of type VEVENT + .filter((e) => e.type === "VEVENT") + // Exclude Past events that aren't recurring + .filter((e) => { + const isFuture = Number(e.end) > Date.now(); + const isRecurring = !!e.rrule; - return isFuture || isRecurring; - }) - // Get latest reccurance of reccuring events - .map((e) => { - const isRecurring = !!e.rrule; + return isFuture || isRecurring; + }) + // Get latest reccurance of reccuring events + .map((e) => { + const isRecurring = !!e.rrule; - if (!isRecurring || !e.rrule) return e; + if (!isRecurring || !e.rrule) return e; - else { + const latest = e.recurrences?.[e.recurrences.length - 1]; - const latest = e.recurrences?.[e.recurrences.length - 1]; + if (!latest) { + const duration = + e.end !== undefined && e.start !== undefined + ? e.end.getTime() - e.start.getTime() + : 0; + const next = e.rrule.after(new Date(Date.now()), true); - if (!latest) { - const duration = e.end !== undefined && e.start !== undefined ? e.end.getTime() - e.start.getTime() : 0; - const next = e.rrule.after(new Date(Date.now()), true); + if (!next) { + return e; + } - if (!next) { - return e; - } + e.start = next; + e.end = new Date(next.getTime() + duration); - e.start = next; - e.end = new Date(next.getTime() + duration); + return e; + } + return latest; + }) + .filter((e) => { + const isFuture = Number(e.end) > Date.now(); - return e + return isFuture; + }) + // Exclude cancelled events + .filter((e) => { + const isCancelled = e.status !== undefined && e.status === "CANCELLED"; - } else { - return latest - } - } - }) - .filter((e) => { - const isFuture = Number(e.end) > Date.now(); + // Only return events that aren't cancelled + return !isCancelled; + }) + // Get designator values from descriptions and turn into event type + .map((e) => { + let download: string | undefined = undefined; + let link: string | undefined = undefined; + let description = ""; - return isFuture; - }) - // Exclude cancelled events - .filter((e) => { - const isCancelled = e.status !== undefined && e.status === "CANCELLED"; + const isRecurring = !!e.rrule; - // Only return events that aren't cancelled - return !isCancelled; - }) - // Get designator values from descriptions and turn into event type - .map((e) => { - let download: string | undefined = undefined; - let link: string | undefined = undefined; - let description = ""; + if (e.description) { + const lines = e.description.split("\n"); - const isRecurring = !!e.rrule; + for (const line of lines) { + const designator = line.substring(0, line.search(":")); - // console.log(e.description); + switch (designator) { + case DESIGNATOR_DOWNLOAD: + download = line + .substring(DESIGNATOR_DOWNLOAD.length + 1, line.length) + .trim(); + break; + case DESIGNATOR_LINK: + link = line + .substring(DESIGNATOR_LINK.length + 1, line.length) + .trim(); + break; + default: + description += `${line} `; + } + } + } - if (e.description) { - const lines = e.description.split("\n"); + description = description.trim(); - for (let line of lines) { - const designator = line.substring(0, line.search(":")); + const event: Event = { + title: e.summary || "Kein Titel", + start: e.start, + end: e.end, + location: e.location || "Kein Standort", + description, + link, + download, + isRecurring, + }; - switch (designator) { - case DESIGNATOR_DOWNLOAD: - download = line - .substring(DESIGNATOR_DOWNLOAD.length + 1, line.length) - .trim(); - break; - case DESIGNATOR_LINK: - link = line - .substring(DESIGNATOR_LINK.length + 1, line.length) - .trim(); - break; - default: - description += line + " "; - } - } - } + return event; + }) + // Sort the events + .sort((a, b) => (a.start?.getTime() || 0) - (b.start?.getTime() || 0)); - description = description.trim(); - - const event: Event = { - title: e.summary || "Kein Titel", - start: e.start!, - end: e.end!, - location: e.location || "Kein Standort", - description, - link, - download, - isRecurring, - }; - - return event; - }) - // Sort the events - .sort((a, b) => a.start.getTime() - b.start.getTime()); - - return futureAndRecurringEvents; + return futureAndRecurringEvents; }