forked from GitHubMirrors/silverbullet-icalendar
switch to ical.js - v4.2
This commit is contained in:
2
PLUG.md
2
PLUG.md
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: Library/sstent/icalendar
|
name: Library/sstent/icalendar
|
||||||
version: "0.4.1"
|
version: "0.4.2"
|
||||||
tags: meta/library
|
tags: meta/library
|
||||||
files:
|
files:
|
||||||
- icalendar.plug.js
|
- icalendar.plug.js
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "icalendar-plug",
|
"name": "icalendar-plug",
|
||||||
"version": "0.4.1",
|
"version": "0.4.2",
|
||||||
"nodeModulesDir": "auto",
|
"nodeModulesDir": "auto",
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"sync-version": "deno run -A scripts/sync-version.ts",
|
"sync-version": "deno run -A scripts/sync-version.ts",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: icalendar
|
name: icalendar
|
||||||
version: 0.4.1
|
version: 0.4.2
|
||||||
author: sstent
|
author: sstent
|
||||||
index: icalendar.ts
|
index: icalendar.ts
|
||||||
# Legacy SilverBullet permission name
|
# Legacy SilverBullet permission name
|
||||||
|
|||||||
74
icalendar.ts
74
icalendar.ts
@@ -65,16 +65,45 @@ async function sha256Hash(str: string): Promise<string> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts Date to local time string (browser's timezone)
|
* Converts UTC Date to a specific timezone string
|
||||||
|
* Uses Intl.DateTimeFormat to properly handle timezone conversion
|
||||||
*/
|
*/
|
||||||
export function localDateString(date: Date): string {
|
export function dateToTimezoneString(date: Date, timezone: string = "America/Los_Angeles"): string {
|
||||||
const pad = (n: number) => String(n).padStart(2, "0");
|
try {
|
||||||
return date.getFullYear() + "-" +
|
// Get date components in the target timezone
|
||||||
pad(date.getMonth() + 1) + "-" +
|
const formatter = new Intl.DateTimeFormat('en-US', {
|
||||||
pad(date.getDate()) + "T" +
|
timeZone: timezone,
|
||||||
pad(date.getHours()) + ":" +
|
year: 'numeric',
|
||||||
pad(date.getMinutes()) + ":" +
|
month: '2-digit',
|
||||||
pad(date.getSeconds());
|
day: '2-digit',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const parts = formatter.formatToParts(date);
|
||||||
|
const values: Record<string, string> = {};
|
||||||
|
|
||||||
|
for (const part of parts) {
|
||||||
|
if (part.type !== 'literal') {
|
||||||
|
values[part.type] = part.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format as ISO-like string: YYYY-MM-DDTHH:MM:SS
|
||||||
|
return `${values.year}-${values.month}-${values.day}T${values.hour}:${values.minute}:${values.second}`;
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`[iCalendar] Error converting to timezone ${timezone}:`, err);
|
||||||
|
// Fallback to UTC
|
||||||
|
const pad = (n: number) => String(n).padStart(2, "0");
|
||||||
|
return date.getUTCFullYear() + "-" +
|
||||||
|
pad(date.getUTCMonth() + 1) + "-" +
|
||||||
|
pad(date.getUTCDate()) + "T" +
|
||||||
|
pad(date.getUTCHours()) + ":" +
|
||||||
|
pad(date.getUTCMinutes()) + ":" +
|
||||||
|
pad(date.getUTCSeconds());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -122,13 +151,15 @@ function convertDatesToStrings<T>(obj: T): any {
|
|||||||
// Configuration Functions
|
// Configuration Functions
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
async function getSources(): Promise<{ sources: any[], syncWindowDays: number }> {
|
async function getSources(): Promise<{ sources: any[], syncWindowDays: number, displayTimezone: string }> {
|
||||||
try {
|
try {
|
||||||
const rawConfig = await config.get("icalendar", { sources: [] }) as any;
|
const rawConfig = await config.get("icalendar", { sources: [] }) as any;
|
||||||
console.log("[iCalendar] Raw config retrieved:", JSON.stringify(rawConfig));
|
console.log("[iCalendar] Raw config retrieved:", JSON.stringify(rawConfig));
|
||||||
|
|
||||||
let sources = rawConfig.sources || [];
|
let sources = rawConfig.sources || [];
|
||||||
const syncWindowDays = rawConfig.syncWindowDays || 365;
|
const syncWindowDays = rawConfig.syncWindowDays || 365;
|
||||||
|
// Get user's display timezone, default to America/Los_Angeles (PST)
|
||||||
|
const displayTimezone = rawConfig.displayTimezone || "America/Los_Angeles";
|
||||||
|
|
||||||
if (sources && typeof sources === "object" && !Array.isArray(sources)) {
|
if (sources && typeof sources === "object" && !Array.isArray(sources)) {
|
||||||
const sourceArray = [];
|
const sourceArray = [];
|
||||||
@@ -140,10 +171,10 @@ async function getSources(): Promise<{ sources: any[], syncWindowDays: number }>
|
|||||||
sources = sourceArray;
|
sources = sourceArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { sources, syncWindowDays };
|
return { sources, syncWindowDays, displayTimezone };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("[iCalendar] Error in getSources:", e);
|
console.error("[iCalendar] Error in getSources:", e);
|
||||||
return { sources: [], syncWindowDays: 365 };
|
return { sources: [], syncWindowDays: 365, displayTimezone: "America/Los_Angeles" };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,7 +244,7 @@ async function resolveEventEnd(icsEvent: any): Promise<Date | null> {
|
|||||||
/**
|
/**
|
||||||
* Expands recurring events into individual occurrences.
|
* Expands recurring events into individual occurrences.
|
||||||
*/
|
*/
|
||||||
export function expandRecurrences(icsEvent: any, windowDays = 365, now = new Date()): any[] {
|
export function expandRecurrences(icsEvent: any, windowDays = 365, displayTimezone = "America/Los_Angeles", now = new Date()): any[] {
|
||||||
const rruleStr = icsEvent.rrule || (icsEvent as any).recurrenceRule;
|
const rruleStr = icsEvent.rrule || (icsEvent as any).recurrenceRule;
|
||||||
if (!rruleStr) return [icsEvent];
|
if (!rruleStr) return [icsEvent];
|
||||||
|
|
||||||
@@ -268,9 +299,9 @@ export function expandRecurrences(icsEvent: any, windowDays = 365, now = new Dat
|
|||||||
return {
|
return {
|
||||||
...icsEvent,
|
...icsEvent,
|
||||||
start: occurrenceDate.toISOString(),
|
start: occurrenceDate.toISOString(),
|
||||||
startLocal: localDateString(occurrenceDate),
|
startLocal: dateToTimezoneString(occurrenceDate, displayTimezone),
|
||||||
end: endDate ? endDate.toISOString() : undefined,
|
end: endDate ? endDate.toISOString() : undefined,
|
||||||
endLocal: endDate ? localDateString(endDate) : undefined,
|
endLocal: endDate ? dateToTimezoneString(endDate, displayTimezone) : undefined,
|
||||||
recurrent: true,
|
recurrent: true,
|
||||||
rrule: undefined,
|
rrule: undefined,
|
||||||
};
|
};
|
||||||
@@ -283,7 +314,7 @@ export function expandRecurrences(icsEvent: any, windowDays = 365, now = new Dat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchAndParseCalendar(source: any, windowDays = 365): Promise<any[]> {
|
async function fetchAndParseCalendar(source: any, windowDays = 365, displayTimezone = "America/Los_Angeles"): Promise<any[]> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(source.url);
|
const response = await fetch(source.url);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -314,9 +345,9 @@ async function fetchAndParseCalendar(source: any, windowDays = 365): Promise<any
|
|||||||
name: icsEvent.summary || "Untitled Event",
|
name: icsEvent.summary || "Untitled Event",
|
||||||
// Store both UTC (for sorting/comparison) and local (for display)
|
// Store both UTC (for sorting/comparison) and local (for display)
|
||||||
start: startDateUTC.toISOString(),
|
start: startDateUTC.toISOString(),
|
||||||
startLocal: localDateString(startDateUTC),
|
startLocal: dateToTimezoneString(startDateUTC, displayTimezone),
|
||||||
end: endDateUTC ? endDateUTC.toISOString() : undefined,
|
end: endDateUTC ? endDateUTC.toISOString() : undefined,
|
||||||
endLocal: endDateUTC ? localDateString(endDateUTC) : undefined,
|
endLocal: endDateUTC ? dateToTimezoneString(endDateUTC, displayTimezone) : undefined,
|
||||||
tag: "ical-event",
|
tag: "ical-event",
|
||||||
sourceName: source.name,
|
sourceName: source.name,
|
||||||
timezone: rawTz
|
timezone: rawTz
|
||||||
@@ -326,7 +357,7 @@ async function fetchAndParseCalendar(source: any, windowDays = 365): Promise<any
|
|||||||
baseEvent.description = `(Warning: Unknown timezone "${rawTz}") ${baseEvent.description || ""}`;
|
baseEvent.description = `(Warning: Unknown timezone "${rawTz}") ${baseEvent.description || ""}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const expanded = expandRecurrences(baseEvent, windowDays);
|
const expanded = expandRecurrences(baseEvent, windowDays, displayTimezone);
|
||||||
for (const occurrence of expanded) {
|
for (const occurrence of expanded) {
|
||||||
// Use summary in key to avoid collisions
|
// Use summary in key to avoid collisions
|
||||||
const uniqueKey = `${occurrence.start}${occurrence.uid || ''}${occurrence.summary || ''}`;
|
const uniqueKey = `${occurrence.start}${occurrence.uid || ''}${occurrence.summary || ''}`;
|
||||||
@@ -343,13 +374,14 @@ async function fetchAndParseCalendar(source: any, windowDays = 365): Promise<any
|
|||||||
|
|
||||||
export async function syncCalendars() {
|
export async function syncCalendars() {
|
||||||
try {
|
try {
|
||||||
const { sources, syncWindowDays } = await getSources();
|
const { sources, syncWindowDays, displayTimezone } = await getSources();
|
||||||
if (sources.length === 0) return;
|
if (sources.length === 0) return;
|
||||||
|
|
||||||
|
console.log(`[iCalendar] Using display timezone: ${displayTimezone}`);
|
||||||
await editor.flashNotification("Syncing calendars...", "info");
|
await editor.flashNotification("Syncing calendars...", "info");
|
||||||
const allEvents: any[] = [];
|
const allEvents: any[] = [];
|
||||||
for (const source of sources) {
|
for (const source of sources) {
|
||||||
const events = await fetchAndParseCalendar(source, syncWindowDays);
|
const events = await fetchAndParseCalendar(source, syncWindowDays, displayTimezone);
|
||||||
allEvents.push(...events);
|
allEvents.push(...events);
|
||||||
}
|
}
|
||||||
await index.indexObjects("$icalendar", allEvents);
|
await index.indexObjects("$icalendar", allEvents);
|
||||||
|
|||||||
Reference in New Issue
Block a user