From 6c3046c8dc075ec7007cc6bbbd407167449eebf2 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sat, 31 Oct 2026 04:07:12 +0100 Subject: [PATCH] fix: keep the text typed in the Sieve editor when the script refreshes (bulwarkmail/webmail#1162) --- src/components/filters/SieveEditorSheet.tsx | 10 +++++++--- src/lib/sieve/__tests__/editor-seed.test.ts | 11 +++++++++++ src/lib/sieve/editor-seed.ts | 8 ++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 src/lib/sieve/__tests__/editor-seed.test.ts create mode 100644 src/lib/sieve/editor-seed.ts diff --git a/src/components/filters/SieveEditorSheet.tsx b/src/components/filters/SieveEditorSheet.tsx index a62e1445..97337e49 100644 --- a/src/components/filters/SieveEditorSheet.tsx +++ b/src/components/filters/SieveEditorSheet.tsx @@ -11,6 +11,7 @@ import Button from '../Button'; import { useLocaleStore } from '../../stores/locale-store'; import { SafeAreaModal } from '../SafeAreaModal'; import { MODAL_ORIENTATIONS } from '../../lib/modal-orientations'; +import { seedsOnRender } from '../../lib/sieve/editor-seed'; interface SieveEditorSheetProps { visible: boolean; @@ -30,14 +31,17 @@ export function SieveEditorSheet({ visible, content, onSave, onClose, onValidate const [validationResult, setValidationResult] = useState<{ isValid: boolean; errors?: string[] } | null>(null); const [showSaveWarning, setShowSaveWarning] = useState(false); - // Re-seed the editor whenever it's (re)opened with fresh content. Keying the - // editor on `content` while closed avoids a stale draft on the next open. + // Seed the editor when it opens, with the script as it is then. A new + // `content` while it is open (a store refresh) must not wipe what was + // typed (#1162). + const wasVisible = React.useRef(false); React.useEffect(() => { - if (visible) { + if (seedsOnRender(wasVisible.current, visible)) { setScript(content); setValidationResult(null); setShowSaveWarning(false); } + wasVisible.current = visible; }, [visible, content]); const lineCount = script.split('\n').length; diff --git a/src/lib/sieve/__tests__/editor-seed.test.ts b/src/lib/sieve/__tests__/editor-seed.test.ts new file mode 100644 index 00000000..66c7652f --- /dev/null +++ b/src/lib/sieve/__tests__/editor-seed.test.ts @@ -0,0 +1,11 @@ +import { describe, expect, it } from 'vitest'; +import { seedsOnRender } from '../editor-seed'; + +describe('seedsOnRender', () => { + it('seeds the Sieve editor when it opens, not when the script is refreshed while it is open (#1162)', () => { + expect(seedsOnRender(false, true)).toBe(true); + expect(seedsOnRender(true, true)).toBe(false); + expect(seedsOnRender(true, false)).toBe(false); + expect(seedsOnRender(false, false)).toBe(false); + }); +}); diff --git a/src/lib/sieve/editor-seed.ts b/src/lib/sieve/editor-seed.ts new file mode 100644 index 00000000..86f20276 --- /dev/null +++ b/src/lib/sieve/editor-seed.ts @@ -0,0 +1,8 @@ +/** + * Whether an editor sheet takes its content afresh: only when it opens. A + * new `content` while it is open (a store refresh) must not replace what the + * user typed (#1162). + */ +export function seedsOnRender(wasVisible: boolean, visible: boolean): boolean { + return visible && !wasVisible; +}