mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 00:12:39 +00:00
fix: fire task alerts from the start when they are relative to it #1328
This commit is contained in:
@@ -11,11 +11,13 @@ import {
|
||||
alertsToRows,
|
||||
rowsToAlerts,
|
||||
newAlertRow,
|
||||
computeTaskFireTime,
|
||||
} from '../calendar-alerts';
|
||||
import type {
|
||||
CalendarEvent,
|
||||
CalendarEventAlert,
|
||||
Calendar,
|
||||
CalendarTask,
|
||||
} from '@/lib/jmap/types';
|
||||
|
||||
function makeEvent(overrides: Partial<CalendarEvent> = {}): CalendarEvent {
|
||||
@@ -521,3 +523,26 @@ describe('getPendingAlerts', () => {
|
||||
expect(result[0].calendarName).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('computeTaskFireTime (#1328)', () => {
|
||||
const task = (start: string | null, due: string | null) => ({ start, due } as CalendarTask);
|
||||
const offset = (relativeTo?: 'start' | 'end') => ({ '@type': 'OffsetTrigger' as const, offset: '-PT5M', ...(relativeTo ? { relativeTo } : {}) }) as Parameters<typeof computeTaskFireTime>[1];
|
||||
const at = (s: string) => new Date(s).getTime();
|
||||
|
||||
it('counts a start-relative alert from the start', () => {
|
||||
expect(computeTaskFireTime(task('2026-10-23T20:00:00Z', '2026-10-23T20:15:00Z'), offset('start'))).toBe(at('2026-10-23T19:55:00Z'));
|
||||
});
|
||||
|
||||
it('treats a missing relativeTo as start, as Stalwart sends it', () => {
|
||||
expect(computeTaskFireTime(task('2026-10-23T20:00:00Z', '2026-10-23T20:15:00Z'), offset())).toBe(at('2026-10-23T19:55:00Z'));
|
||||
});
|
||||
|
||||
it('counts an end-relative alert from the due time', () => {
|
||||
expect(computeTaskFireTime(task('2026-10-23T20:00:00Z', '2026-10-23T20:15:00Z'), offset('end'))).toBe(at('2026-10-23T20:10:00Z'));
|
||||
});
|
||||
|
||||
it('falls back to the due time when the task has no start', () => {
|
||||
expect(computeTaskFireTime(task(null, '2026-10-23T20:15:00Z'), offset('start'))).toBe(at('2026-10-23T20:10:00Z'));
|
||||
expect(computeTaskFireTime(task(null, null), offset('start'))).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -275,8 +275,11 @@ export function computeTaskFireTime(
|
||||
const offsetMs = parseAlertOffset(trigger.offset);
|
||||
if (offsetMs === null) return null;
|
||||
|
||||
if (!task.due) return null;
|
||||
const baseTime = new Date(task.due).getTime();
|
||||
// RFC 8984 §4.5.2: relativeTo defaults to "start", which for a Task is its
|
||||
// start; "end" is its due. A task without a start counts from its due (#1328).
|
||||
const base = trigger.relativeTo !== 'end' && task.start ? task.start : task.due;
|
||||
if (!base) return null;
|
||||
const baseTime = new Date(base).getTime();
|
||||
if (Number.isNaN(baseTime)) return null;
|
||||
return baseTime + offsetMs;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user