@luckydye/calendar 1.3.2 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/calendar.js +2344 -2061
- package/package.json +7 -1
- package/src/ActiveCalendarStore.ts +88 -88
- package/src/CalDAVConfig.ts +611 -514
- package/src/CalDAVSource.ts +561 -466
- package/src/CalendarIntegration.ts +64 -47
- package/src/CalendarInternal.ts +645 -614
- package/src/CalendarLayer.ts +1 -0
- package/src/CalendarStorage.ts +51 -48
- package/src/CalendarView.ts +883 -507
- package/src/Color.ts +48 -54
- package/src/GoogleCalendarSource.ts +758 -662
- package/src/ICal.ts +420 -348
- package/src/InMemorySource.ts +56 -48
- package/src/IndexedDBStorage.ts +444 -398
- package/src/InhouseBookingSource.ts +614 -523
- package/src/Keybinds.ts +6 -1
- package/src/NotificationScheduler.ts +11 -8
- package/src/StatusBar.ts +12 -8
- package/src/StatusMessage.ts +2 -2
- package/src/Theme.ts +21 -7
- package/src/TimeseriesJson.ts +98 -98
- package/src/app.ts +153 -78
- package/src/layers/EventsLayer.ts +530 -400
- package/src/layers/GridLayer.ts +45 -125
- package/src/layers/TimeseriesHeatmapLayer.ts +123 -120
- package/src/service-worker.js +3 -2
package/src/app.ts
CHANGED
|
@@ -1,32 +1,36 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { activeCalendarStore } from "./ActiveCalendarStore.js";
|
|
2
2
|
import "./CalDAVConfig.ts";
|
|
3
3
|
import type { CalDAVConfigElement } from "./CalDAVConfig.ts";
|
|
4
|
+
import { CalDAVSource } from "./CalDAVSource.js";
|
|
4
5
|
import {
|
|
5
|
-
CalendarIntegration,
|
|
6
6
|
type Calendar,
|
|
7
|
+
CalendarIntegration,
|
|
7
8
|
type CalendarSource,
|
|
8
9
|
} from "./CalendarIntegration.js";
|
|
9
|
-
import { CalDAVSource } from "./CalDAVSource.js";
|
|
10
|
-
import { fetchICalEventsWithNotifications, parseICalEvents, parseICalEventsWithNotifications } from "./ICal.js";
|
|
11
|
-
import { initializeTheme } from "./Theme.js";
|
|
12
|
-
import { InMemorySource } from "./InMemorySource.js";
|
|
13
|
-
import { GoogleCalendarSource } from "./GoogleCalendarSource.js";
|
|
14
|
-
import { InhouseBookingSource } from "./InhouseBookingSource.js";
|
|
15
|
-
import { fetchTimeseriesJsonEvents } from "./TimeseriesJson.js";
|
|
16
|
-
import { queueStatus } from "./StatusMessage.js";
|
|
17
|
-
import { activeCalendarStore } from "./ActiveCalendarStore.js";
|
|
18
10
|
import type { CalendarEvent } from "./CalendarInternal.js";
|
|
19
11
|
import { sanitizeEventDescription } from "./DescriptionSanitizer.js";
|
|
12
|
+
import { GoogleCalendarSource } from "./GoogleCalendarSource.js";
|
|
13
|
+
import {
|
|
14
|
+
fetchICalEventsWithNotifications,
|
|
15
|
+
parseICalEvents,
|
|
16
|
+
parseICalEventsWithNotifications,
|
|
17
|
+
} from "./ICal.js";
|
|
18
|
+
import { InMemorySource } from "./InMemorySource.js";
|
|
19
|
+
import { InhouseBookingSource } from "./InhouseBookingSource.js";
|
|
20
20
|
import { registerKeybinds } from "./Keybinds.js";
|
|
21
|
-
import "./StatusBar.js";
|
|
22
21
|
import { NotificationScheduler } from "./NotificationScheduler.js";
|
|
23
|
-
import
|
|
22
|
+
import "./StatusBar.js";
|
|
24
23
|
import type { StatusBarElement } from "./StatusBar.js";
|
|
24
|
+
import { queueStatus } from "./StatusMessage.js";
|
|
25
|
+
import { initializeTheme } from "./Theme.js";
|
|
26
|
+
import { fetchTimeseriesJsonEvents } from "./TimeseriesJson.js";
|
|
27
|
+
import { CalendarViewElement } from "./lib.ts";
|
|
28
|
+
import serviceWorkerUrl from "./service-worker.js?url";
|
|
25
29
|
|
|
26
30
|
try {
|
|
27
|
-
|
|
28
|
-
} catch(err) {
|
|
29
|
-
|
|
31
|
+
customElements.define("calendar-view", CalendarViewElement);
|
|
32
|
+
} catch (err) {
|
|
33
|
+
console.error(err);
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
// Initialize theme on app start
|
|
@@ -52,9 +56,8 @@ function getPromptApiLanguageModel(): PromptApiLanguageModel | null {
|
|
|
52
56
|
const fromGlobal = (globalThis as { LanguageModel?: unknown }).LanguageModel;
|
|
53
57
|
if (fromGlobal) return fromGlobal as PromptApiLanguageModel;
|
|
54
58
|
|
|
55
|
-
const fromWindow = (
|
|
56
|
-
|
|
57
|
-
).ai?.languageModel;
|
|
59
|
+
const fromWindow = (window as { ai?: { languageModel?: unknown } }).ai
|
|
60
|
+
?.languageModel;
|
|
58
61
|
if (!fromWindow) return null;
|
|
59
62
|
return fromWindow as PromptApiLanguageModel;
|
|
60
63
|
}
|
|
@@ -80,7 +83,10 @@ function abortActiveDescriptionSummary(): void {
|
|
|
80
83
|
summaryRequestToken++;
|
|
81
84
|
}
|
|
82
85
|
|
|
83
|
-
async function streamDescriptionSummary(
|
|
86
|
+
async function streamDescriptionSummary(
|
|
87
|
+
key: string,
|
|
88
|
+
description: string,
|
|
89
|
+
): Promise<void> {
|
|
84
90
|
const sanitizedDescription = sanitizeEventDescription(description);
|
|
85
91
|
abortActiveDescriptionSummary();
|
|
86
92
|
activeSummaryKey = key;
|
|
@@ -89,7 +95,10 @@ async function streamDescriptionSummary(key: string, description: string): Promi
|
|
|
89
95
|
|
|
90
96
|
const languageModel = getPromptApiLanguageModel();
|
|
91
97
|
if (!languageModel) {
|
|
92
|
-
calendarElement.failDescriptionSummary(
|
|
98
|
+
calendarElement.failDescriptionSummary(
|
|
99
|
+
key,
|
|
100
|
+
"Prompt API not available in this browser.",
|
|
101
|
+
);
|
|
93
102
|
activeSummaryKey = null;
|
|
94
103
|
return;
|
|
95
104
|
}
|
|
@@ -167,16 +176,23 @@ async function streamDescriptionSummary(key: string, description: string): Promi
|
|
|
167
176
|
}
|
|
168
177
|
}
|
|
169
178
|
|
|
170
|
-
registerKeybinds(
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
179
|
+
registerKeybinds([
|
|
180
|
+
{
|
|
181
|
+
key: "r",
|
|
182
|
+
cmdOrCtrl: true,
|
|
183
|
+
shift: true,
|
|
184
|
+
action: () => calendarElement.forceSync(),
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
key: "c",
|
|
188
|
+
cmdOrCtrl: true,
|
|
189
|
+
action: () => calendarElement.copySelectedEvents(),
|
|
190
|
+
},
|
|
191
|
+
{ key: "Backspace", action: () => calendarElement.deleteSelectedEvents() },
|
|
192
|
+
{ key: "Delete", action: () => calendarElement.deleteSelectedEvents() },
|
|
193
|
+
{ key: "Escape", action: () => calendarElement.escape() },
|
|
194
|
+
{ key: "t", action: () => calendarElement.scrollToToday() },
|
|
195
|
+
]);
|
|
180
196
|
|
|
181
197
|
let workerPromise: Promise<ServiceWorkerRegistration> | undefined;
|
|
182
198
|
|
|
@@ -192,10 +208,14 @@ if ("serviceWorker" in navigator) {
|
|
|
192
208
|
// Initialize notification scheduler
|
|
193
209
|
const notificationScheduler = new NotificationScheduler(() => workerPromise);
|
|
194
210
|
|
|
195
|
-
async function scheduleNotificationsForEvents(
|
|
211
|
+
async function scheduleNotificationsForEvents(
|
|
212
|
+
events: CalendarEvent[],
|
|
213
|
+
): Promise<void> {
|
|
196
214
|
const now = new Date();
|
|
197
215
|
const next24Hours = new Date(now.getTime() + 24 * 60 * 60 * 1000);
|
|
198
|
-
const upcoming = events.filter(
|
|
216
|
+
const upcoming = events.filter(
|
|
217
|
+
(e) => e.start >= now && e.start <= next24Hours && e.reminders?.length,
|
|
218
|
+
);
|
|
199
219
|
for (const event of upcoming) {
|
|
200
220
|
await notificationScheduler.scheduleEventNotifications(event);
|
|
201
221
|
}
|
|
@@ -236,9 +256,13 @@ async function requestNotificationPermission() {
|
|
|
236
256
|
}
|
|
237
257
|
}
|
|
238
258
|
|
|
239
|
-
calendarElement.addEventListener(
|
|
240
|
-
|
|
241
|
-
|
|
259
|
+
calendarElement.addEventListener(
|
|
260
|
+
"event-click",
|
|
261
|
+
() => {
|
|
262
|
+
requestNotificationPermission();
|
|
263
|
+
},
|
|
264
|
+
{ once: true },
|
|
265
|
+
);
|
|
242
266
|
|
|
243
267
|
calendarElement.addEventListener("request-description-summary", (e: Event) => {
|
|
244
268
|
const { key, description } = (e as CustomEvent).detail as {
|
|
@@ -258,7 +282,10 @@ calendarElement.addEventListener("cancel-description-summary", (e: Event) => {
|
|
|
258
282
|
// Load iCal files
|
|
259
283
|
const integration = new CalendarIntegration(calendar);
|
|
260
284
|
|
|
261
|
-
async function sync(
|
|
285
|
+
async function sync(
|
|
286
|
+
cal: Calendar,
|
|
287
|
+
options?: { force?: boolean },
|
|
288
|
+
): Promise<void> {
|
|
262
289
|
const events = await integration.sync(cal, options);
|
|
263
290
|
await scheduleNotificationsForEvents(events);
|
|
264
291
|
}
|
|
@@ -270,9 +297,14 @@ let mutationsSettledResolve: (() => void) | null = null;
|
|
|
270
297
|
let mutationsSettledPromise: Promise<void> | null = null;
|
|
271
298
|
const dirtyCalendars = new Set<Calendar>();
|
|
272
299
|
|
|
273
|
-
async function withMutation(
|
|
300
|
+
async function withMutation(
|
|
301
|
+
cal: Calendar,
|
|
302
|
+
fn: () => Promise<void>,
|
|
303
|
+
): Promise<void> {
|
|
274
304
|
if (pendingMutations === 0) {
|
|
275
|
-
mutationsSettledPromise = new Promise(r => {
|
|
305
|
+
mutationsSettledPromise = new Promise((r) => {
|
|
306
|
+
mutationsSettledResolve = r;
|
|
307
|
+
});
|
|
276
308
|
}
|
|
277
309
|
pendingMutations++;
|
|
278
310
|
try {
|
|
@@ -304,11 +336,11 @@ async function waitForMutations(): Promise<void> {
|
|
|
304
336
|
await calendar.initPromise;
|
|
305
337
|
|
|
306
338
|
// Register sample source
|
|
307
|
-
const sampleSource = new InMemorySource(
|
|
339
|
+
const sampleSource = new InMemorySource("sample", "Sample Events", "#10b981");
|
|
308
340
|
const sampleCalendar = createInMemoryCalendar(sampleSource);
|
|
309
341
|
loadedCalendars.set(sampleCalendar.id, sampleCalendar);
|
|
310
342
|
activeCalendarStore.registerCalendar(sampleCalendar);
|
|
311
|
-
|
|
343
|
+
|
|
312
344
|
// Create sidebar with CalDAV config
|
|
313
345
|
const sidebar = document.createElement("div");
|
|
314
346
|
sidebar.slot = "sidebar";
|
|
@@ -344,7 +376,6 @@ async function waitForMutations(): Promise<void> {
|
|
|
344
376
|
updateActiveCalendarColor();
|
|
345
377
|
})();
|
|
346
378
|
|
|
347
|
-
|
|
348
379
|
// Store for loaded calendars
|
|
349
380
|
const loadedCalendars: Map<string, Calendar> = new Map();
|
|
350
381
|
let config: CalDAVConfigElement;
|
|
@@ -389,7 +420,7 @@ async function createCalDAVCalendars(
|
|
|
389
420
|
username,
|
|
390
421
|
password,
|
|
391
422
|
},
|
|
392
|
-
source.enabled
|
|
423
|
+
source.enabled,
|
|
393
424
|
);
|
|
394
425
|
|
|
395
426
|
// Fetch all calendars and the current user's email from the CalDAV server
|
|
@@ -413,7 +444,11 @@ async function createCalDAVCalendars(
|
|
|
413
444
|
calendarUrl: calInfo.url,
|
|
414
445
|
|
|
415
446
|
async fetchEvents(): Promise<CalendarEvent[]> {
|
|
416
|
-
return caldavSource.fetchEventsForCalendar(
|
|
447
|
+
return caldavSource.fetchEventsForCalendar(
|
|
448
|
+
calInfo.url,
|
|
449
|
+
calInfo.displayName,
|
|
450
|
+
calInfo.color,
|
|
451
|
+
);
|
|
417
452
|
},
|
|
418
453
|
|
|
419
454
|
async createEvent(
|
|
@@ -426,12 +461,15 @@ async function createCalDAVCalendars(
|
|
|
426
461
|
event: CalendarEvent,
|
|
427
462
|
updates: Partial<CalendarEvent>,
|
|
428
463
|
): Promise<CalendarEvent> {
|
|
429
|
-
return caldavSource.updateEvent(event, {
|
|
464
|
+
return caldavSource.updateEvent(event, {
|
|
465
|
+
...updates,
|
|
466
|
+
calendarId: calInfo.url,
|
|
467
|
+
});
|
|
430
468
|
},
|
|
431
469
|
|
|
432
470
|
async deleteEvent(id: string): Promise<void> {
|
|
433
|
-
const eventUrl = `${calInfo.url.replace(/\/$/,
|
|
434
|
-
await caldavSource.request(eventUrl,
|
|
471
|
+
const eventUrl = `${calInfo.url.replace(/\/$/, "")}/${id}.ics`;
|
|
472
|
+
await caldavSource.request(eventUrl, "DELETE", undefined, {});
|
|
435
473
|
},
|
|
436
474
|
});
|
|
437
475
|
}
|
|
@@ -451,7 +489,11 @@ function createICalCalendar(source: CalendarSource): Calendar {
|
|
|
451
489
|
sourceType: "ical",
|
|
452
490
|
|
|
453
491
|
async fetchEvents(): Promise<CalendarEvent[]> {
|
|
454
|
-
const result = await fetchICalEventsWithNotifications(
|
|
492
|
+
const result = await fetchICalEventsWithNotifications(
|
|
493
|
+
source.credentials,
|
|
494
|
+
source.color,
|
|
495
|
+
source.name,
|
|
496
|
+
);
|
|
455
497
|
return result.events;
|
|
456
498
|
},
|
|
457
499
|
// iCal calendars are read-only by default
|
|
@@ -471,7 +513,11 @@ function createTimeseriesJsonCalendar(source: CalendarSource): Calendar {
|
|
|
471
513
|
|
|
472
514
|
async fetchEvents(): Promise<CalendarEvent[]> {
|
|
473
515
|
return fetchTimeseriesJsonEvents(
|
|
474
|
-
source.credentials as {
|
|
516
|
+
source.credentials as {
|
|
517
|
+
url: string;
|
|
518
|
+
timestampField?: string;
|
|
519
|
+
titleField?: string;
|
|
520
|
+
},
|
|
475
521
|
source.color,
|
|
476
522
|
source.name,
|
|
477
523
|
source.id,
|
|
@@ -494,7 +540,7 @@ function createGoogleCalendar(source: CalendarSource): Calendar {
|
|
|
494
540
|
tokenExpiry: source.credentials.tokenExpiry,
|
|
495
541
|
calendarId: calendarId,
|
|
496
542
|
},
|
|
497
|
-
source.enabled
|
|
543
|
+
source.enabled,
|
|
498
544
|
);
|
|
499
545
|
|
|
500
546
|
return {
|
|
@@ -542,7 +588,7 @@ function createInhouseCalendar(source: CalendarSource): Calendar {
|
|
|
542
588
|
unitId: source.credentials.unitId,
|
|
543
589
|
startHour: source.credentials.startHour,
|
|
544
590
|
},
|
|
545
|
-
source.enabled
|
|
591
|
+
source.enabled,
|
|
546
592
|
);
|
|
547
593
|
|
|
548
594
|
return {
|
|
@@ -710,7 +756,9 @@ async function syncCalDAV(force = false) {
|
|
|
710
756
|
}
|
|
711
757
|
} else if (source.type === "timeseries-json") {
|
|
712
758
|
if (!source.credentials?.url) {
|
|
713
|
-
console.warn(
|
|
759
|
+
console.warn(
|
|
760
|
+
`Skipping Timeseries JSON source ${source.name}: missing URL`,
|
|
761
|
+
);
|
|
714
762
|
continue;
|
|
715
763
|
}
|
|
716
764
|
|
|
@@ -721,11 +769,16 @@ async function syncCalDAV(force = false) {
|
|
|
721
769
|
try {
|
|
722
770
|
await sync(calendar, { force });
|
|
723
771
|
} catch (error) {
|
|
724
|
-
console.error(
|
|
772
|
+
console.error(
|
|
773
|
+
`Sync error for Timeseries JSON source ${source.name}:`,
|
|
774
|
+
error,
|
|
775
|
+
);
|
|
725
776
|
}
|
|
726
777
|
} else if (source.type === "google") {
|
|
727
778
|
if (!source.credentials?.accessToken) {
|
|
728
|
-
console.warn(
|
|
779
|
+
console.warn(
|
|
780
|
+
`Skipping Google Calendar source ${source.name}: missing access token`,
|
|
781
|
+
);
|
|
729
782
|
continue;
|
|
730
783
|
}
|
|
731
784
|
|
|
@@ -737,11 +790,19 @@ async function syncCalDAV(force = false) {
|
|
|
737
790
|
try {
|
|
738
791
|
await sync(calendar, { force });
|
|
739
792
|
} catch (error) {
|
|
740
|
-
console.error(
|
|
793
|
+
console.error(
|
|
794
|
+
`Sync error for Google Calendar source ${source.name}:`,
|
|
795
|
+
error,
|
|
796
|
+
);
|
|
741
797
|
}
|
|
742
798
|
} else if (source.type === "inhouse") {
|
|
743
|
-
if (
|
|
744
|
-
|
|
799
|
+
if (
|
|
800
|
+
!source.credentials?.sessionCookie ||
|
|
801
|
+
!source.credentials?.employeeId
|
|
802
|
+
) {
|
|
803
|
+
console.warn(
|
|
804
|
+
`Skipping Inhouse Booking source ${source.name}: missing session cookie or employee ID`,
|
|
805
|
+
);
|
|
745
806
|
continue;
|
|
746
807
|
}
|
|
747
808
|
|
|
@@ -752,7 +813,10 @@ async function syncCalDAV(force = false) {
|
|
|
752
813
|
try {
|
|
753
814
|
await sync(calendar, { force });
|
|
754
815
|
} catch (error) {
|
|
755
|
-
console.error(
|
|
816
|
+
console.error(
|
|
817
|
+
`Sync error for Inhouse Booking source ${source.name}:`,
|
|
818
|
+
error,
|
|
819
|
+
);
|
|
756
820
|
}
|
|
757
821
|
} else {
|
|
758
822
|
console.warn(`Unknown source type for ${source.name}: ${source.type}`);
|
|
@@ -764,7 +828,7 @@ async function syncCalDAV(force = false) {
|
|
|
764
828
|
updateLockedCalendars();
|
|
765
829
|
|
|
766
830
|
// Push calendar list to sidebar
|
|
767
|
-
config.calendars = Array.from(loadedCalendars.values()).map(cal => ({
|
|
831
|
+
config.calendars = Array.from(loadedCalendars.values()).map((cal) => ({
|
|
768
832
|
id: cal.id,
|
|
769
833
|
name: cal.name,
|
|
770
834
|
color: cal.color,
|
|
@@ -867,7 +931,7 @@ function showProjectPickerDialog(
|
|
|
867
931
|
const filtered = filter
|
|
868
932
|
? projects.filter((p) =>
|
|
869
933
|
p.name.toLowerCase().includes(filter.toLowerCase()),
|
|
870
|
-
|
|
934
|
+
)
|
|
871
935
|
: projects;
|
|
872
936
|
for (const project of filtered) {
|
|
873
937
|
const item = document.createElement("button");
|
|
@@ -944,11 +1008,17 @@ calendarElement.addEventListener("create-event", async (e) => {
|
|
|
944
1008
|
lastSynced: new Date(),
|
|
945
1009
|
});
|
|
946
1010
|
try {
|
|
947
|
-
await withMutation(calendar, () =>
|
|
1011
|
+
await withMutation(calendar, () =>
|
|
1012
|
+
calendar.createEvent({ id, title: "New Event", start, end, isAllDay }),
|
|
1013
|
+
);
|
|
948
1014
|
} catch (error) {
|
|
949
1015
|
calendarElement.internal.removeEventOptimistically(id);
|
|
950
1016
|
console.error("Failed to create event:", error);
|
|
951
|
-
queueStatus(
|
|
1017
|
+
queueStatus(
|
|
1018
|
+
`Failed to create event: ${
|
|
1019
|
+
error instanceof Error ? error.message : String(error)
|
|
1020
|
+
}`,
|
|
1021
|
+
);
|
|
952
1022
|
}
|
|
953
1023
|
});
|
|
954
1024
|
|
|
@@ -969,17 +1039,16 @@ calendarElement.addEventListener("update-event", async (e) => {
|
|
|
969
1039
|
return;
|
|
970
1040
|
}
|
|
971
1041
|
|
|
972
|
-
console.log(
|
|
973
|
-
"Updating event:",
|
|
974
|
-
event
|
|
975
|
-
);
|
|
1042
|
+
console.log("Updating event:", event);
|
|
976
1043
|
try {
|
|
977
1044
|
calendarElement.internal.applyEventOptimistically({ ...event, ...updates });
|
|
978
1045
|
await withMutation(calendar, () => calendar.updateEvent(event, updates));
|
|
979
1046
|
|
|
980
1047
|
if ("reminders" in updates) {
|
|
981
1048
|
const eventWithReminders = { ...event, ...updates };
|
|
982
|
-
await notificationScheduler.scheduleEventNotifications(
|
|
1049
|
+
await notificationScheduler.scheduleEventNotifications(
|
|
1050
|
+
eventWithReminders,
|
|
1051
|
+
);
|
|
983
1052
|
|
|
984
1053
|
if (calendarElement.selectedEventForDetail?.id === event.id) {
|
|
985
1054
|
calendarElement.selectedEventForDetail = eventWithReminders;
|
|
@@ -988,7 +1057,11 @@ calendarElement.addEventListener("update-event", async (e) => {
|
|
|
988
1057
|
} catch (error) {
|
|
989
1058
|
calendarElement.internal.applyEventOptimistically(event);
|
|
990
1059
|
console.error("Failed to update event:", error);
|
|
991
|
-
queueStatus(
|
|
1060
|
+
queueStatus(
|
|
1061
|
+
`Failed to update event: ${
|
|
1062
|
+
error instanceof Error ? error.message : String(error)
|
|
1063
|
+
}`,
|
|
1064
|
+
);
|
|
992
1065
|
}
|
|
993
1066
|
});
|
|
994
1067
|
|
|
@@ -1020,9 +1093,7 @@ calendarElement.addEventListener("delete-events", async (e) => {
|
|
|
1020
1093
|
const calendar = findCalendarForEvent(calendarEvents[0]);
|
|
1021
1094
|
|
|
1022
1095
|
if (!calendar) {
|
|
1023
|
-
queueStatus(
|
|
1024
|
-
`Cannot delete events: calendar "${calendarId}" not found.`,
|
|
1025
|
-
);
|
|
1096
|
+
queueStatus(`Cannot delete events: calendar "${calendarId}" not found.`);
|
|
1026
1097
|
continue;
|
|
1027
1098
|
}
|
|
1028
1099
|
|
|
@@ -1050,7 +1121,11 @@ calendarElement.addEventListener("delete-events", async (e) => {
|
|
|
1050
1121
|
calendarElement.internal.applyEventOptimistically(event);
|
|
1051
1122
|
}
|
|
1052
1123
|
console.error("Failed to delete event:", error);
|
|
1053
|
-
queueStatus(
|
|
1124
|
+
queueStatus(
|
|
1125
|
+
`Failed to delete event: ${
|
|
1126
|
+
error instanceof Error ? error.message : String(error)
|
|
1127
|
+
}`,
|
|
1128
|
+
);
|
|
1054
1129
|
}
|
|
1055
1130
|
}
|
|
1056
1131
|
});
|
|
@@ -1068,7 +1143,8 @@ calendarElement.addEventListener("force-sync", async () => {
|
|
|
1068
1143
|
|
|
1069
1144
|
calendarElement.addEventListener("load-notifications", async () => {
|
|
1070
1145
|
try {
|
|
1071
|
-
const notifications =
|
|
1146
|
+
const notifications =
|
|
1147
|
+
await notificationScheduler.getScheduledNotifications();
|
|
1072
1148
|
calendarElement.setScheduledNotifications(notifications);
|
|
1073
1149
|
} catch (error) {
|
|
1074
1150
|
console.error("Failed to load scheduled notifications:", error);
|
|
@@ -1139,7 +1215,6 @@ calendarElement.addEventListener("import-ical", async (e) => {
|
|
|
1139
1215
|
}
|
|
1140
1216
|
});
|
|
1141
1217
|
|
|
1142
|
-
|
|
1143
1218
|
function updateActiveCalendarColor() {
|
|
1144
1219
|
const calendar = activeCalendarStore.getActiveCalendar();
|
|
1145
1220
|
calendarElement.activeCalendarColor = calendar?.color ?? null;
|
|
@@ -1148,8 +1223,8 @@ function updateActiveCalendarColor() {
|
|
|
1148
1223
|
|
|
1149
1224
|
function updateEnabledCalendars() {
|
|
1150
1225
|
const enabledCalendarIdentifiers = Array.from(loadedCalendars.values())
|
|
1151
|
-
.filter(cal => cal.enabled)
|
|
1152
|
-
.flatMap(cal => {
|
|
1226
|
+
.filter((cal) => cal.enabled)
|
|
1227
|
+
.flatMap((cal) => {
|
|
1153
1228
|
// For CalDAV, use the calendar URL; for others, use the source ID
|
|
1154
1229
|
const identifiers = [];
|
|
1155
1230
|
if (cal.calendarUrl) {
|
|
@@ -1165,8 +1240,8 @@ function updateEnabledCalendars() {
|
|
|
1165
1240
|
|
|
1166
1241
|
function updateLockedCalendars() {
|
|
1167
1242
|
const lockedCalendarIdentifiers = Array.from(loadedCalendars.values())
|
|
1168
|
-
.filter(cal => (cal as { locked?: boolean }).locked)
|
|
1169
|
-
.flatMap(cal => {
|
|
1243
|
+
.filter((cal) => (cal as { locked?: boolean }).locked)
|
|
1244
|
+
.flatMap((cal) => {
|
|
1170
1245
|
// For CalDAV, use the calendar URL; for others, use the source ID
|
|
1171
1246
|
const identifiers = [];
|
|
1172
1247
|
if (cal.calendarUrl) {
|