mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-02 08:07:11 +00:00
fix: treat CalDAV tasks marked COMPLETED, NEEDS_ACTION or canceled as such
Tasks created over CalDAV can come back with their iCalendar STATUS spelling or as `canceled` (stalwartlabs/calcard#20). The app compared against the JSCalendar values only, so a completed CalDAV task counted as pending, still raised its reminder, and a tap on it completed it instead of reopening it. - normalize the known spellings when events are read, like webmail's `normalizeCalendarTask` (5f1d2795), and keep unknown values as they are - reopen a cancelled task on tap, since the tasks sheet shows it ticked
This commit is contained in:
@@ -209,6 +209,55 @@ describe('calendar operations', () => {
|
||||
const [event] = await getEvents(['ev1']);
|
||||
expect(event.recurrenceRules).toEqual([{ frequency: 'weekly', interval: 2 }]);
|
||||
});
|
||||
|
||||
it('normalizes the CalDAV spellings of task progress (calcard#20)', async () => {
|
||||
const list = [
|
||||
{ id: 't1', progress: 'COMPLETED' },
|
||||
{ id: 't2', progress: 'NEEDS_ACTION' },
|
||||
{ id: 't3', progress: 'IN_PROCESS' },
|
||||
{ id: 't4', progress: 'canceled' },
|
||||
{ id: 't5', progress: 'CANCELLED' },
|
||||
{ id: 't6', progress: 'needs-action' },
|
||||
];
|
||||
mockRequest.mockResolvedValue({
|
||||
methodResponses: [['CalendarEvent/get', { list }, '0']],
|
||||
});
|
||||
|
||||
const result = await getEvents(list.map((t) => t.id));
|
||||
expect(result.map((t) => t.progress)).toEqual([
|
||||
'completed',
|
||||
'needs-action',
|
||||
'in-process',
|
||||
'cancelled',
|
||||
'cancelled',
|
||||
'needs-action',
|
||||
]);
|
||||
});
|
||||
|
||||
it('leaves unknown and non-string task progress untouched', async () => {
|
||||
const list = [
|
||||
{ id: 't1', progress: 'constructor' },
|
||||
{ id: 't2', progress: '__proto__' },
|
||||
{ id: 't3', progress: 'failed' },
|
||||
{ id: 't4', progress: 42 },
|
||||
{ id: 't5', progress: null },
|
||||
{ id: 'ev1', title: 'Event' },
|
||||
];
|
||||
mockRequest.mockResolvedValue({
|
||||
methodResponses: [['CalendarEvent/get', { list }, '0']],
|
||||
});
|
||||
|
||||
const result = await getEvents(list.map((t) => t.id));
|
||||
expect(result.map((t) => t.progress as unknown)).toEqual([
|
||||
'constructor',
|
||||
'__proto__',
|
||||
'failed',
|
||||
42,
|
||||
null,
|
||||
undefined,
|
||||
]);
|
||||
expect(result[5]).not.toHaveProperty('progress');
|
||||
});
|
||||
});
|
||||
|
||||
describe('createEvent', () => {
|
||||
|
||||
+27
-1
@@ -147,6 +147,32 @@ export function normalizeRecurrenceProperties<T extends Partial<CalendarEvent>>(
|
||||
return result;
|
||||
}
|
||||
|
||||
const TASK_PROGRESS_NORMALIZATION = new Map<string, string>([
|
||||
['needs-action', 'needs-action'],
|
||||
['needs_action', 'needs-action'],
|
||||
['in-process', 'in-process'],
|
||||
['in_process', 'in-process'],
|
||||
['completed', 'completed'],
|
||||
['cancelled', 'cancelled'],
|
||||
['canceled', 'cancelled'],
|
||||
]);
|
||||
|
||||
/**
|
||||
* CalDAV-created tasks can come back with their iCalendar STATUS spelling
|
||||
* (`COMPLETED`, `NEEDS_ACTION`) or as `canceled` (stalwartlabs/calcard#20).
|
||||
* Map the known spellings onto the JSCalendar values the task filters, the
|
||||
* checkbox and the reminders compare against; leave unknown values alone.
|
||||
* Mirrors webmail's normalizeCalendarTask.
|
||||
*/
|
||||
export function normalizeTaskProgress<T extends Partial<CalendarEvent>>(event: T): T {
|
||||
const progress = (event as { progress?: unknown }).progress;
|
||||
if (typeof progress !== 'string') return event;
|
||||
const normalized = TASK_PROGRESS_NORMALIZATION.get(progress.toLowerCase());
|
||||
return normalized === undefined || normalized === progress
|
||||
? event
|
||||
: { ...event, progress: normalized };
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the client's plural recurrence arrays to the singular JSCalendar 2.0
|
||||
* properties Stalwart expects on write, dropping null rule fields. An empty
|
||||
@@ -357,7 +383,7 @@ export async function getEvents(ids: string[], accountId?: string): Promise<Cale
|
||||
USING,
|
||||
);
|
||||
const list = methodResult<{ list: CalendarEvent[] }>(res).list ?? [];
|
||||
all.push(...list.map(normalizeRecurrenceProperties));
|
||||
all.push(...list.map((e) => normalizeTaskProgress(normalizeRecurrenceProperties(e))));
|
||||
}
|
||||
return all;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user