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:
+18
-3
@@ -3178,9 +3178,24 @@ export class JMAPClient implements IJMAPClient {
|
|||||||
async getCalendarTasks(calendarIds?: string[], targetAccountId?: string): Promise<CalendarTask[]> {
|
async getCalendarTasks(calendarIds?: string[], targetAccountId?: string): Promise<CalendarTask[]> {
|
||||||
try {
|
try {
|
||||||
const events = await this.getCalendarEvents(calendarIds, targetAccountId);
|
const events = await this.getCalendarEvents(calendarIds, targetAccountId);
|
||||||
return events.filter((e): e is CalendarTask & CalendarEvent =>
|
return events.filter((e) => {
|
||||||
(e as unknown as CalendarTask)['@type'] === 'Task'
|
const obj = e as unknown as Record<string, unknown>;
|
||||||
) as unknown as CalendarTask[];
|
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) {
|
} catch (error) {
|
||||||
console.error('Failed to get calendar tasks:', error);
|
console.error('Failed to get calendar tasks:', error);
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
@@ -1102,6 +1102,12 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
|
|||||||
if (dateRange && selectedCalendarIds.length > 0) {
|
if (dateRange && selectedCalendarIds.length > 0) {
|
||||||
calendarStore.fetchEvents(client, dateRange.start, dateRange.end);
|
calendarStore.fetchEvents(client, dateRange.start, dateRange.end);
|
||||||
}
|
}
|
||||||
|
// Refresh tasks when calendar events change (e.g. task created via CalDAV)
|
||||||
|
const { useTaskStore } = await import('./task-store');
|
||||||
|
const taskStore = useTaskStore.getState();
|
||||||
|
if (taskStore.tasks.length > 0 || calendarStore.viewMode === 'tasks') {
|
||||||
|
taskStore.fetchTasks(client);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user