summaryrefslogtreecommitdiff
path: root/src/services/calendar.ts
diff options
context:
space:
mode:
author2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-03-25 21:51:59 +1100
committer2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-03-25 21:51:59 +1100
commite8a40f31c904baeaa1817cd3e418df5ce71302c1 (patch)
treede5b15228d2e7227560db91131006ee55af82049 /src/services/calendar.ts
parentsidebar: media handle no players (diff)
downloadcaelestia-shell-e8a40f31c904baeaa1817cd3e418df5ce71302c1.tar.gz
caelestia-shell-e8a40f31c904baeaa1817cd3e418df5ce71302c1.tar.bz2
caelestia-shell-e8a40f31c904baeaa1817cd3e418df5ce71302c1.zip
sidebar: create upcoming module
Requires ical.js and curl
Diffstat (limited to 'src/services/calendar.ts')
-rw-r--r--src/services/calendar.ts116
1 files changed, 116 insertions, 0 deletions
diff --git a/src/services/calendar.ts b/src/services/calendar.ts
index e69de29..bc7c075 100644
--- a/src/services/calendar.ts
+++ b/src/services/calendar.ts
@@ -0,0 +1,116 @@
+import { execAsync, GObject, property, register } from "astal";
+import { calendar as config } from "config";
+import ical from "ical.js";
+
+export interface IEvent {
+ calendar: string;
+ event: ical.Event;
+}
+
+@register({ GTypeName: "Calendar" })
+export default class Calendar extends GObject.Object {
+ static instance: Calendar;
+ static get_default() {
+ if (!this.instance) this.instance = new Calendar();
+
+ return this.instance;
+ }
+
+ #calCount = 1;
+ #loading = false;
+ #calendars: { [name: string]: ical.Component } = {};
+ #upcoming: { [date: string]: IEvent[] } = {};
+
+ @property(Boolean)
+ get loading() {
+ return this.#loading;
+ }
+
+ @property(Object)
+ get calendars() {
+ return this.#calendars;
+ }
+
+ @property(Object)
+ get upcoming() {
+ return this.#upcoming;
+ }
+
+ @property(Number)
+ get numUpcoming() {
+ return Object.values(this.#upcoming).reduce((acc, e) => acc + e.length, 0);
+ }
+
+ getCalendarIndex(name: string) {
+ return Object.keys(this.#calendars).indexOf(name) + 1;
+ }
+
+ async updateCalendars() {
+ this.#loading = true;
+ this.notify("loading");
+
+ this.#calendars = {};
+ this.#calCount = 1;
+
+ const cals = await Promise.allSettled(config.webcals.get().map(c => execAsync(["curl", c])));
+ for (const cal of cals) {
+ if (cal.status === "fulfilled") {
+ const comp = new ical.Component(ical.parse(cal.value));
+ const name = (comp.getFirstPropertyValue("x-wr-calname") ?? `Calendar ${this.#calCount++}`) as string;
+ this.#calendars[name] = comp;
+ } else console.error(`Failed to get calendar: ${cal.reason}`);
+ }
+ this.notify("calendars");
+
+ this.updateUpcoming();
+
+ this.#loading = false;
+ this.notify("loading");
+ }
+
+ updateUpcoming() {
+ this.#upcoming = {};
+
+ for (const [name, cal] of Object.entries(this.#calendars)) {
+ const today = ical.Time.now();
+ const upcoming = ical.Time.now().adjust(config.upcomingDays.get(), 0, 0, 0);
+
+ for (const e of cal.getAllSubcomponents()) {
+ const event = new ical.Event(e);
+
+ // Skip invalid events
+ if (!event.startDate) continue;
+
+ if (event.isRecurring()) {
+ // Recurring events
+ const iter = event.iterator();
+ for (let next = iter.next(); next && next.compare(upcoming) <= 0; next = iter.next())
+ if (next.compare(today) >= 0) {
+ const date = next.toJSDate().toDateString();
+ if (!this.#upcoming.hasOwnProperty(date)) this.#upcoming[date] = [];
+ this.#upcoming[date].push({ calendar: name, event });
+ }
+ } else if (event.startDate.compare(today) >= 0 && event.startDate.compare(upcoming) <= 0) {
+ // Add to upcoming if in upcoming range
+ const date = event.startDate.toJSDate().toDateString();
+ if (!this.#upcoming.hasOwnProperty(date)) this.#upcoming[date] = [];
+ this.#upcoming[date].push({ calendar: name, event });
+ }
+ }
+ }
+
+ for (const events of Object.values(this.#upcoming))
+ events.sort((a, b) => a.event.startDate.compare(b.event.startDate));
+
+ this.notify("upcoming");
+ this.notify("num-upcoming");
+ }
+
+ constructor() {
+ super();
+
+ this.updateCalendars().catch(console.error);
+ config.webcals.subscribe(() => this.updateCalendars().catch(console.error));
+ config.upcomingDays.subscribe(() => this.updateUpcoming());
+ }
+}