fix: return to the event editor with the changes when the recurring-event question is cancelled

This commit is contained in:
Linus Rath
2026-10-28 03:06:41 +01:00
parent 0cc052f48d
commit 9b4ffcd652
2 changed files with 40 additions and 8 deletions
@@ -176,3 +176,27 @@ describe('CalendarApp in the Tasks view', () => {
expect(screen.getByRole('dialog', { name: 'tasks.create' })).toBeInTheDocument();
});
});
const weekly = makeEvent({ recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'weekly', interval: 1 }] as never });
function openEditorOn(title = 'Standup') {
fireEvent.click(screen.getAllByRole('button', { name: new RegExp(title) })[0]);
const card = screen.getByRole('dialog', { name: title });
fireEvent.click(within(card).getByRole('button', { name: 'events.edit' }));
}
describe('CalendarApp recurring event editor', () => {
beforeEach(() => setUp([weekly]));
it('goes back to the editor with the changes when the scope question is cancelled', async () => {
renderApp();
openEditorOn();
fireEvent.change(screen.getByPlaceholderText('form.title'), { target: { value: 'Standup moved' } });
fireEvent.click(screen.getByRole('button', { name: 'form.save' }));
const scope = await screen.findByRole('dialog', { name: 'edit_title' });
fireEvent.click(within(scope).getByRole('button', { name: 'cancel' }));
expect(screen.queryByRole('dialog', { name: 'edit_title' })).toBeNull();
expect(screen.getByPlaceholderText('form.title')).toHaveValue('Standup moved');
expect(store.updateEvent).not.toHaveBeenCalled();
});
});
+16 -8
View File
@@ -101,9 +101,11 @@ import { isEditableEventTarget } from "@/lib/keyboard";
import { isContextMenuOpen } from "@/components/ui/context-menu";
import { useRegisterCommands } from "@/lib/command-registry";
// `fromEditor`: asked on the way out of the event editor, which stays open
// until a scope is picked, so Cancel goes back to it with the changes.
type PendingScopeAction =
| { type: "edit"; event: CalendarEvent; updates: Partial<CalendarEvent>; editor?: EventEditorState; sendScheduling?: boolean }
| { type: "delete"; event: CalendarEvent; sendScheduling?: boolean }
| { type: "edit"; event: CalendarEvent; updates: Partial<CalendarEvent>; editor?: EventEditorState; sendScheduling?: boolean; fromEditor?: boolean }
| { type: "delete"; event: CalendarEvent; sendScheduling?: boolean; fromEditor?: boolean }
| { type: "rsvp"; event: CalendarEvent; participantId: string; status: CalendarParticipant["participationStatus"] };
function isRecurringEvent(event: CalendarEvent): boolean {
@@ -995,9 +997,8 @@ export function CalendarApp({ linkSegments: routeSegments }: CalendarAppProps =
updates: data,
editor,
sendScheduling: sendSchedulingMessages,
fromEditor: true,
});
setShowEventModal(false);
setEditEvent(null);
return;
}
// false: a plugin stopped it and said why; the form stays open.
@@ -1150,9 +1151,8 @@ export function CalendarApp({ linkSegments: routeSegments }: CalendarAppProps =
type: "delete",
event: eventToDelete,
sendScheduling: sendSchedulingMessages || undefined,
fromEditor: showEventModal && editEvent?.id === eventToDelete.id,
});
setShowEventModal(false);
setEditEvent(null);
return;
}
try {
@@ -1160,7 +1160,7 @@ export function CalendarApp({ linkSegments: routeSegments }: CalendarAppProps =
} catch {
toast.error(t("notifications.event_error"));
}
}, [client, deleteEvent, events, editEvent, t]);
}, [client, deleteEvent, events, editEvent, showEventModal, t]);
const submitRsvp = useCallback(async (
eventId: string,
@@ -1184,10 +1184,16 @@ export function CalendarApp({ linkSegments: routeSegments }: CalendarAppProps =
await submitRsvp(event.id, participantId, status, scope === "this" ? "occurrence" : "series");
return;
}
const { type, event, sendScheduling } = pendingScopeAction;
const { type, event, sendScheduling, fromEditor } = pendingScopeAction;
const updates = type === "edit" ? pendingScopeAction.updates : undefined;
const editor = type === "edit" ? pendingScopeAction.editor : undefined;
setPendingScopeAction(null);
// The editor the question came from closes once the change went through.
const closeEditor = () => {
if (!fromEditor) return;
setShowEventModal(false);
setEditEvent(null);
};
// false when a plugin stopped the change (it says why): no "updated".
let done = true;
@@ -1207,6 +1213,7 @@ export function CalendarApp({ linkSegments: routeSegments }: CalendarAppProps =
return;
}
if (!done) return;
closeEditor();
toast.success(t("notifications.event_updated"));
} else {
const deleted = await deleteOccurrence(scope, {
@@ -1220,6 +1227,7 @@ export function CalendarApp({ linkSegments: routeSegments }: CalendarAppProps =
return;
}
if (!done) return;
closeEditor();
toast.success(t("notifications.event_deleted"));
}
try {