fix: improve CalDAV task detection for external clients (Thunderbird) #84
Two issues prevented tasks created in Thunderbird (or other CalDAV
clients) from appearing in the task view:
1. percentComplete was not in CALENDAR_TASK_PROPERTIES, so it was
never requested from the server and the heuristic check for it
was always false (dead code).
2. The hasTaskFields heuristic used strict value checks:
- 'progress' in obj && typeof obj.progress === 'string'
→ fails when Stalwart returns progress: null instead of the
RFC 8984 default "needs-action"
- 'due' in obj && obj.due != null
→ fails when Stalwart includes due: null for tasks without a
DUE date (key present, value null)
RFC 8984 §5.2 defines due, progress and percentComplete as Task-only
properties — a VEVENT will never include them in a JMAP response.
Checking for key presence alone (even when null) is therefore a
reliable discriminator, regardless of the actual value.
This commit is contained in:
+8
-3
@@ -189,6 +189,7 @@ const CALENDAR_TASK_PROPERTIES = [
|
|||||||
'useDefaultAlerts',
|
'useDefaultAlerts',
|
||||||
'alerts',
|
'alerts',
|
||||||
'relatedTo',
|
'relatedTo',
|
||||||
|
'percentComplete', // Task-only per RFC 8984 §5.2.4 — used in detection heuristic
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -3974,9 +3975,13 @@ export class JMAPClient implements IJMAPClient {
|
|||||||
const isExplicitTask = typeof type === 'string' && type.toLowerCase() === 'task';
|
const isExplicitTask = typeof type === 'string' && type.toLowerCase() === 'task';
|
||||||
// CalDAV-created tasks (e.g. Thunderbird) may lack @type or have @type
|
// CalDAV-created tasks (e.g. Thunderbird) may lack @type or have @type
|
||||||
// set to something other than 'Event'. Detect them by the presence of
|
// set to something other than 'Event'. Detect them by the presence of
|
||||||
// task-specific fields: progress, due, or percentComplete.
|
// task-specific keys (due, progress, percentComplete), which RFC 8984 §5.2
|
||||||
const hasTaskFields = ('progress' in obj && typeof obj.progress === 'string')
|
// defines as Task-only — a VEVENT will never include them in the response.
|
||||||
|| ('due' in obj && obj.due != null)
|
// We check for key presence (even if null) because Stalwart may return null
|
||||||
|
// instead of the RFC defaults (e.g. progress default is "needs-action").
|
||||||
|
// @see https://www.rfc-editor.org/rfc/rfc8984#section-5.2
|
||||||
|
const hasTaskFields = ('due' in obj)
|
||||||
|
|| ('progress' in obj)
|
||||||
|| ('percentComplete' in obj);
|
|| ('percentComplete' in obj);
|
||||||
const isCalDavTask = type !== 'Event' && hasTaskFields;
|
const isCalDavTask = type !== 'Event' && hasTaskFields;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user