mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 00:12:39 +00:00
fix: keep every calendar hidden after reload when the user hid them all
This commit is contained in:
@@ -70,6 +70,7 @@ export function snapshotAccount(accountId: string): void {
|
||||
calendars: [...calendarState.calendars],
|
||||
events: [...calendarState.events],
|
||||
selectedCalendarIds: [...calendarState.selectedCalendarIds],
|
||||
calendarSelectionChosen: calendarState.calendarSelectionChosen,
|
||||
viewMode: calendarState.viewMode,
|
||||
supportsCalendar: calendarState.supportsCalendar,
|
||||
},
|
||||
|
||||
@@ -35,7 +35,7 @@ const own = [cal({ id: 'a', accountId: 'me' }), cal({ id: 'b', accountId: 'me' }
|
||||
|
||||
describe('calendar selection after a fetch', () => {
|
||||
beforeEach(() => {
|
||||
useCalendarStore.setState({ calendars: [], selectedCalendarIds: [], error: null });
|
||||
useCalendarStore.setState({ calendars: [], selectedCalendarIds: [], calendarSelectionChosen: false, error: null });
|
||||
});
|
||||
|
||||
it('leaves the list and the selection alone when the fetch fails', async () => {
|
||||
@@ -106,6 +106,48 @@ describe('calendar selection after a fetch', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('a selection the user made', () => {
|
||||
beforeEach(() => {
|
||||
useCalendarStore.setState({ calendars: own, selectedCalendarIds: ['a', 'b'], calendarSelectionChosen: false, error: null });
|
||||
});
|
||||
|
||||
it('stays empty after the user hid every calendar', async () => {
|
||||
useCalendarStore.getState().toggleCalendarVisibility('a');
|
||||
useCalendarStore.getState().toggleCalendarVisibility('b');
|
||||
|
||||
await useCalendarStore.getState().fetchCalendars(clientReturning({ calendars: own }));
|
||||
|
||||
expect(useCalendarStore.getState().selectedCalendarIds).toEqual([]);
|
||||
});
|
||||
|
||||
it('keeps only the birthday calendar when the user hid every other one', async () => {
|
||||
useCalendarStore.setState({ selectedCalendarIds: ['a', 'b', BIRTHDAY_CALENDAR_ID] });
|
||||
useCalendarStore.getState().toggleCalendarVisibility('a');
|
||||
useCalendarStore.getState().toggleCalendarVisibility('b');
|
||||
|
||||
await useCalendarStore.getState().fetchCalendars(clientReturning({ calendars: own }));
|
||||
|
||||
expect(useCalendarStore.getState().selectedCalendarIds).toEqual([BIRTHDAY_CALENDAR_ID]);
|
||||
});
|
||||
|
||||
it('falls back to all when the calendars it named were removed', async () => {
|
||||
useCalendarStore.getState().toggleCalendarVisibility('b');
|
||||
|
||||
await useCalendarStore.getState().fetchCalendars(clientReturning({ calendars: [cal({ id: 'c', accountId: 'me' })] }));
|
||||
|
||||
expect(useCalendarStore.getState().selectedCalendarIds).toEqual(['c']);
|
||||
});
|
||||
|
||||
it('does not count showing the birthday calendar as a choice', async () => {
|
||||
useCalendarStore.setState({ selectedCalendarIds: [] });
|
||||
useCalendarStore.getState().toggleCalendarVisibility(BIRTHDAY_CALENDAR_ID);
|
||||
|
||||
await useCalendarStore.getState().fetchCalendars(clientReturning({ calendars: own }));
|
||||
|
||||
expect(useCalendarStore.getState().selectedCalendarIds).toEqual(['a', 'b', BIRTHDAY_CALENDAR_ID]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('reconcileSelectedIds with unloaded accounts', () => {
|
||||
it('keeps ids under an unloaded prefix verbatim instead of remapping them by raw id', () => {
|
||||
const calendars = [cal({ id: 'A@h::b', originalId: 'b', localAccountId: 'A@h' })];
|
||||
|
||||
@@ -527,6 +527,8 @@ export function reconcileSelectedIds(
|
||||
* default when no real calendar is left in it. The virtual birthday calendar
|
||||
* always survives reconciliation, so it must not count - counting it pinned
|
||||
* the selection to "birthdays only" once the real calendars had dropped out.
|
||||
* A selection the user emptied by hiding every calendar (`chosen`) stays
|
||||
* empty; one that named calendars which are gone now falls back.
|
||||
*/
|
||||
function selectionAfterFetch(
|
||||
selectedCalendarIds: string[],
|
||||
@@ -534,9 +536,11 @@ function selectionAfterFetch(
|
||||
defaultSelected: string[],
|
||||
unloadedPrefixes?: string[],
|
||||
rawOwner?: string,
|
||||
chosen = false,
|
||||
): string[] {
|
||||
const reconciled = reconcileSelectedIds(selectedCalendarIds, calendars, unloadedPrefixes, rawOwner);
|
||||
if (reconciled.some((id) => id !== BIRTHDAY_CALENDAR_ID)) return reconciled;
|
||||
if (chosen && !selectedCalendarIds.some((id) => id !== BIRTHDAY_CALENDAR_ID)) return reconciled;
|
||||
return reconciled.includes(BIRTHDAY_CALENDAR_ID) ? [...defaultSelected, BIRTHDAY_CALENDAR_ID] : defaultSelected;
|
||||
}
|
||||
|
||||
@@ -770,6 +774,8 @@ interface CalendarStore {
|
||||
selectedDate: Date;
|
||||
viewMode: CalendarViewMode;
|
||||
selectedCalendarIds: string[];
|
||||
/** The user picked the visible calendars by hand: an empty pick hides them all. */
|
||||
calendarSelectionChosen: boolean;
|
||||
selectedEventId: string | null;
|
||||
isLoading: boolean;
|
||||
isLoadingEvents: boolean;
|
||||
@@ -857,6 +863,7 @@ const initialState = {
|
||||
events: [],
|
||||
selectedDate: displayNow(),
|
||||
selectedCalendarIds: [] as string[],
|
||||
calendarSelectionChosen: false,
|
||||
selectedEventId: null as string | null,
|
||||
isLoading: false,
|
||||
isLoadingEvents: false,
|
||||
@@ -928,6 +935,8 @@ export const useCalendarStore = create<CalendarStore>()(
|
||||
defaultSelected,
|
||||
// Shared calendars' ids are `<accountId>:<id>` (getAllCalendars).
|
||||
failedAccountIds.map((accountId) => `${accountId}:`),
|
||||
undefined,
|
||||
get().calendarSelectionChosen,
|
||||
),
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -1014,6 +1023,7 @@ export const useCalendarStore = create<CalendarStore>()(
|
||||
calendars.map(c => c.id),
|
||||
unloadedPrefixes,
|
||||
activeLocalAccountId ?? undefined,
|
||||
get().calendarSelectionChosen,
|
||||
),
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -1653,6 +1663,8 @@ export const useCalendarStore = create<CalendarStore>()(
|
||||
selectedCalendarIds: visible
|
||||
? [...state.selectedCalendarIds, calendarId]
|
||||
: state.selectedCalendarIds.filter(id => id !== calendarId),
|
||||
// Showing birthdays is a setting, not a pick of calendars.
|
||||
calendarSelectionChosen: state.calendarSelectionChosen || calendarId !== BIRTHDAY_CALENDAR_ID,
|
||||
}));
|
||||
void calendarHooks.onCalendarVisibilityToggle.emit({ calendarId, visible });
|
||||
},
|
||||
@@ -2060,6 +2072,7 @@ export const useCalendarStore = create<CalendarStore>()(
|
||||
},
|
||||
partialize: (state) => ({
|
||||
selectedCalendarIds: state.selectedCalendarIds,
|
||||
calendarSelectionChosen: state.calendarSelectionChosen,
|
||||
viewMode: state.viewMode,
|
||||
icalSubscriptions: state.icalSubscriptions,
|
||||
deletedSubscriptionIds: state.deletedSubscriptionIds,
|
||||
|
||||
Reference in New Issue
Block a user