mirror of
https://github.com/kennethnym/aris.git
synced 2026-03-20 17:11:17 +00:00
Compare commits
2 Commits
851e6af951
...
feat/calda
| Author | SHA1 | Date | |
|---|---|---|---|
| bb09de2da6 | |||
| e220ccc392 |
@@ -479,4 +479,29 @@ describe("computeSignals", () => {
|
||||
})
|
||||
expect(computeSignals(event2h1m, now).urgency).toBe(0.5)
|
||||
})
|
||||
|
||||
test("cancelled events get urgency 0.1 regardless of timing", () => {
|
||||
const event = makeEvent({
|
||||
status: "cancelled",
|
||||
startDate: new Date("2026-01-15T12:20:00Z"), // would be 0.9 if not cancelled
|
||||
})
|
||||
const signals = computeSignals(event, now)
|
||||
expect(signals.urgency).toBe(0.1)
|
||||
expect(signals.timeRelevance).toBe(TimeRelevance.Ambient)
|
||||
})
|
||||
|
||||
test("uses timezone for 'later today' boundary", () => {
|
||||
// now = 2026-01-15T12:00:00Z = 2026-01-15T21:00:00 JST (UTC+9)
|
||||
// event at 2026-01-15T15:30:00Z = 2026-01-16T00:30:00 JST — next day in JST
|
||||
const event = makeEvent({
|
||||
startDate: new Date("2026-01-15T15:30:00Z"),
|
||||
})
|
||||
|
||||
// Without timezone: UTC day ends at 2026-01-16T00:00:00Z, event is before that → "later today"
|
||||
expect(computeSignals(event, now).urgency).toBe(0.5)
|
||||
|
||||
// With Asia/Tokyo: local day ends at 2026-01-15T15:00:00Z (midnight Jan 16 JST),
|
||||
// event is after that → "future days"
|
||||
expect(computeSignals(event, now, "Asia/Tokyo").urgency).toBe(0.2)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,6 +4,7 @@ import { TimeRelevance, UnknownActionError } from "@aris/core"
|
||||
import { DAVClient } from "tsdav"
|
||||
|
||||
import type { CalDavDAVClient, CalDavEventData, CalDavFeedItem } from "./types.ts"
|
||||
import { CalDavEventStatus } from "./types.ts"
|
||||
|
||||
import { CalDavCalendarKey, type CalendarContext } from "./calendar-context.ts"
|
||||
import { parseICalEvents } from "./ical-parser.ts"
|
||||
@@ -75,6 +76,7 @@ export class CalDavSource implements FeedSource<CalDavFeedItem> {
|
||||
private readonly injectedClient: CalDavDAVClient | null
|
||||
private clientPromise: Promise<CalDavDAVClient> | null = null
|
||||
private cachedEvents: { time: Date; events: CalDavEventData[] } | null = null
|
||||
private pendingFetch: { time: Date; promise: Promise<CalDavEventData[]> } | null = null
|
||||
|
||||
constructor(options: CalDavSourceOptions) {
|
||||
this.options = options
|
||||
@@ -105,9 +107,10 @@ export class CalDavSource implements FeedSource<CalDavFeedItem> {
|
||||
}
|
||||
|
||||
const now = context.time
|
||||
const inProgress = events.filter((e) => !e.isAllDay && e.startDate <= now && e.endDate > now)
|
||||
const active = events.filter((e) => e.status !== CalDavEventStatus.Cancelled)
|
||||
const inProgress = active.filter((e) => !e.isAllDay && e.startDate <= now && e.endDate > now)
|
||||
|
||||
const upcoming = events
|
||||
const upcoming = active
|
||||
.filter((e) => !e.isAllDay && e.startDate > now)
|
||||
.sort((a, b) => a.startDate.getTime() - b.startDate.getTime())
|
||||
|
||||
@@ -124,14 +127,30 @@ export class CalDavSource implements FeedSource<CalDavFeedItem> {
|
||||
async fetchItems(context: Context): Promise<CalDavFeedItem[]> {
|
||||
const now = context.time
|
||||
const events = await this.fetchEvents(context)
|
||||
return events.map((event) => createFeedItem(event, now))
|
||||
return events.map((event) => createFeedItem(event, now, this.timeZone))
|
||||
}
|
||||
|
||||
private async fetchEvents(context: Context): Promise<CalDavEventData[]> {
|
||||
private fetchEvents(context: Context): Promise<CalDavEventData[]> {
|
||||
if (this.cachedEvents && this.cachedEvents.time === context.time) {
|
||||
return this.cachedEvents.events
|
||||
return Promise.resolve(this.cachedEvents.events)
|
||||
}
|
||||
|
||||
// Deduplicate concurrent fetches for the same context.time reference
|
||||
if (this.pendingFetch && this.pendingFetch.time === context.time) {
|
||||
return this.pendingFetch.promise
|
||||
}
|
||||
|
||||
const promise = this.doFetchEvents(context).finally(() => {
|
||||
if (this.pendingFetch?.promise === promise) {
|
||||
this.pendingFetch = null
|
||||
}
|
||||
})
|
||||
|
||||
this.pendingFetch = { time: context.time, promise }
|
||||
return promise
|
||||
}
|
||||
|
||||
private async doFetchEvents(context: Context): Promise<CalDavEventData[]> {
|
||||
const client = await this.connectClient()
|
||||
const calendars = await client.fetchCalendars()
|
||||
|
||||
@@ -273,7 +292,15 @@ function startOfDay(date: Date, timeZone?: string): Date {
|
||||
return new Date(Date.UTC(year, month - 1, day) - offsetMs)
|
||||
}
|
||||
|
||||
export function computeSignals(event: CalDavEventData, now: Date): FeedItemSignals {
|
||||
export function computeSignals(
|
||||
event: CalDavEventData,
|
||||
now: Date,
|
||||
timeZone?: string,
|
||||
): FeedItemSignals {
|
||||
if (event.status === CalDavEventStatus.Cancelled) {
|
||||
return { urgency: 0.1, timeRelevance: TimeRelevance.Ambient }
|
||||
}
|
||||
|
||||
if (event.isAllDay) {
|
||||
return { urgency: 0.3, timeRelevance: TimeRelevance.Ambient }
|
||||
}
|
||||
@@ -298,11 +325,9 @@ export function computeSignals(event: CalDavEventData, now: Date): FeedItemSigna
|
||||
return { urgency: 0.7, timeRelevance: TimeRelevance.Upcoming }
|
||||
}
|
||||
|
||||
// Later today
|
||||
const startOfDay = new Date(now)
|
||||
startOfDay.setUTCHours(0, 0, 0, 0)
|
||||
const endOfDay = new Date(startOfDay)
|
||||
endOfDay.setUTCDate(endOfDay.getUTCDate() + 1)
|
||||
// Later today (using local day boundary when timeZone is set)
|
||||
const todayStart = startOfDay(now, timeZone)
|
||||
const endOfDay = new Date(todayStart.getTime() + 24 * 60 * 60 * 1000)
|
||||
|
||||
if (event.startDate.getTime() < endOfDay.getTime()) {
|
||||
return { urgency: 0.5, timeRelevance: TimeRelevance.Upcoming }
|
||||
@@ -312,12 +337,12 @@ export function computeSignals(event: CalDavEventData, now: Date): FeedItemSigna
|
||||
return { urgency: 0.2, timeRelevance: TimeRelevance.Ambient }
|
||||
}
|
||||
|
||||
function createFeedItem(event: CalDavEventData, now: Date): CalDavFeedItem {
|
||||
function createFeedItem(event: CalDavEventData, now: Date, timeZone?: string): CalDavFeedItem {
|
||||
return {
|
||||
id: `caldav-event-${event.uid}${event.recurrenceId ? `-${event.recurrenceId}` : ""}`,
|
||||
type: "caldav-event",
|
||||
timestamp: now,
|
||||
data: event,
|
||||
signals: computeSignals(event, now),
|
||||
signals: computeSignals(event, now, timeZone),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user