diff --git a/locales/rn/de.json b/locales/rn/de.json index 8193bd8f..2391303f 100644 --- a/locales/rn/de.json +++ b/locales/rn/de.json @@ -616,6 +616,9 @@ "your_server": "Ihrem Server" } }, + "notifications": { + "vacation_filters_off": "Abwesenheits-Einstellungen gespeichert, aber Ihre Filter sind ausgeschaltet. Speichern Sie noch einmal, um sie wieder einzuschalten." + }, "offline": { "banner": "Sie sind offline", "discarded_changes": "{count, plural, one {# Offline-Änderung wurde} other {# Offline-Änderungen wurden}} verworfen, weil sich der Mailserver geändert hat", diff --git a/locales/rn/en.json b/locales/rn/en.json index 84ffef24..a3e7c40c 100644 --- a/locales/rn/en.json +++ b/locales/rn/en.json @@ -661,6 +661,9 @@ "your_server": "your server" } }, + "notifications": { + "vacation_filters_off": "Out of office settings saved, but your filters are switched off. Save again to turn them back on." + }, "offline": { "banner": "You are offline", "discarded_changes": "{count, plural, one {# offline change was} other {# offline changes were}} discarded because the mail server changed", diff --git a/src/components/settings/VacationSettings.tsx b/src/components/settings/VacationSettings.tsx index 60aef49e..3b8ae54c 100644 --- a/src/components/settings/VacationSettings.tsx +++ b/src/components/settings/VacationSettings.tsx @@ -192,7 +192,9 @@ export function VacationSettings() { Alert.alert(t('notifications.vacation_saved', 'Vacation responder settings saved')); } catch (err) { Alert.alert( - err instanceof VacationFiltersError + err instanceof VacationFiltersError && err.filtersOff + ? t('notifications.vacation_filters_off', 'Out of office settings saved, but your filters are switched off. Save again to turn them back on.') + : err instanceof VacationFiltersError ? t('notifications.vacation_filters_save_failed', 'Out of office settings saved, but the forwarding and reply recipients could not be saved') : t('notifications.vacation_save_failed', 'Failed to save vacation responder settings'), err instanceof Error ? err.message : undefined, diff --git a/src/stores/__tests__/vacation-store.test.ts b/src/stores/__tests__/vacation-store.test.ts index 8044e013..31ef0a8c 100644 --- a/src/stores/__tests__/vacation-store.test.ts +++ b/src/stores/__tests__/vacation-store.test.ts @@ -21,7 +21,7 @@ vi.mock('../filter-store', () => ({ import * as vacation from '../../api/vacation'; import * as sieve from '../../api/sieve'; import * as filters from '../filter-store'; -import { useVacationStore } from '../vacation-store'; +import { useVacationStore, VacationFiltersError } from '../vacation-store'; const api = vi.mocked(vacation); const sieveApi = vi.mocked(sieve); @@ -164,6 +164,21 @@ describe('vacation-store and the filters script (webmail 198a3c0d)', () => { ); }); + // Turning the auto-reply on activates Stalwart's vacation script, which + // switches the filters off until the filters script includes it. + it('reports filters left off when the auto-reply was turned on', async () => { + sieveApi.isSieveSupported.mockReturnValue(true); + filterStore.syncVacationWithFilters.mockRejectedValueOnce(new Error('boom')); + const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}); + + const saved = useVacationStore.getState().save({ isEnabled: true }); + await expect(saved).rejects.toBeInstanceOf(VacationFiltersError); + await expect(saved).rejects.toMatchObject({ filtersOff: true }); + expect(api.setVacationResponse).toHaveBeenCalledWith({ isEnabled: true }, undefined); + expect(useVacationStore.getState()).toMatchObject({ isEnabled: true, isSaving: false }); + warn.mockRestore(); + }); + it('reports a forwarding that could not be saved', async () => { sieveApi.isSieveSupported.mockReturnValue(true); filterStore.syncVacationWithFilters.mockRejectedValueOnce(new Error('boom')); diff --git a/src/stores/vacation-store.ts b/src/stores/vacation-store.ts index 4b73a1b6..ff7c549b 100644 --- a/src/stores/vacation-store.ts +++ b/src/stores/vacation-store.ts @@ -19,9 +19,17 @@ export type VacationForwardSettings = Pick((set, get) => ({ let filtersError: VacationFiltersError | null = null; let filtersState: Partial = {}; const needsSync = vacationUpdates.isEnabled !== undefined || forwardSettings !== undefined || audience !== undefined; + // Turning the auto-reply on activates Stalwart's vacation script, which + // switches the filters off until the sync includes it in them again. + const turnedOn = vacationUpdates.isEnabled === true; if (needsSync && isSieveSupported(accountId)) { let synced = false; try { @@ -180,9 +191,9 @@ export const useVacationStore = create((set, get) => ({ console.warn('[vacation] Failed to keep filters active next to the vacation response:', err); // Forwarding that is or was on may now not run, or still run; an // auto-reply meant for some senders may go to everyone, or the other - // way round. - if (forwardSettings?.enabled || state.forward?.enabled || audience || state.audience) { - filtersError = new VacationFiltersError(err); + // way round; the filters may be off. + if (turnedOn || forwardSettings?.enabled || state.forward?.enabled || audience || state.audience) { + filtersError = new VacationFiltersError(err, turnedOn); } } // What the filters script holds now, and whether it runs. After a