fix: detect tasks created by external CalDAV clients (Thunderbird)

Tasks created in Thunderbird via CalDAV were not visible because
getCalendarTasks() used a strict @type === 'Task' check. Stalwart
may not set @type when converting VTODO from CalDAV to JMAP.

- Use case-insensitive @type matching for server variations
- Add fallback heuristic: detect tasks by presence of 'progress'
  property (exclusive to JSCalendar Task, never on Event objects)
- Normalize @type to 'Task' on detected tasks for consistent
  downstream handling
- Refresh task store on CalendarEvent state changes so tasks
  created externally appear without manual page refresh

Fixes #84
This commit is contained in:
Linus Rath
2026-03-24 14:54:37 +01:00
parent 13010c158d
commit 0c1f182b6b
2 changed files with 24 additions and 3 deletions
+18 -3
View File
@@ -3178,9 +3178,24 @@ export class JMAPClient implements IJMAPClient {
async getCalendarTasks(calendarIds?: string[], targetAccountId?: string): Promise<CalendarTask[]> {
try {
const events = await this.getCalendarEvents(calendarIds, targetAccountId);
return events.filter((e): e is CalendarTask & CalendarEvent =>
(e as unknown as CalendarTask)['@type'] === 'Task'
) as unknown as CalendarTask[];
return events.filter((e) => {
const obj = e as unknown as Record<string, unknown>;
const type = obj['@type'];
// Explicit @type check (case-insensitive to handle server variations)
if (typeof type === 'string' && type.toLowerCase() === 'task') return true;
// Fallback: detect tasks created via CalDAV (e.g. Thunderbird) where @type
// may be missing. The "progress" property is exclusive to JSCalendar Task
// objects and never appears on Event objects.
if (type !== 'Event' && 'progress' in obj && typeof obj.progress === 'string') return true;
return false;
}).map((e) => {
const task = e as unknown as CalendarTask;
// Normalize @type for tasks detected by fallback heuristic
if (task['@type'] !== 'Task') {
(task as unknown as Record<string, unknown>)['@type'] = 'Task';
}
return task;
});
} catch (error) {
console.error('Failed to get calendar tasks:', error);
return [];