Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit 9149b013eb1d32e4d5ed413b1480a0411fe5571b
parent 8c3a13f9857f395be8f40ad646048a6262d2f46f
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Wed, 30 Sep 2026 23:21:19 -0400

deck S6: the On-screen table's reload after a 409 keeps only real edits

loadRows(keep) kept every row's previous draft, edited or not. After a stale
save, "reload saved values" left a row the other writer had changed showing
its OLD value, marked dirty — and the next save sent it, reverting the very
write the stale-token guard exists to protect. A draft is now kept only when
it differs from what the page last read as saved; every other row takes the
newly saved value. Found by onscreen.spec.ts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Diffstat:
Mumtool/components/projects/OnscreenSection.tsx | 14+++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/umtool/components/projects/OnscreenSection.tsx b/umtool/components/projects/OnscreenSection.tsx @@ -421,6 +421,11 @@ export default function OnscreenSection({ const [formDirty, setFormDirty] = useState(false); const [errors, setErrors] = useState<string[]>([]); const [rows, setRows] = useState<Row[]>([]); + // The rows as last saved, for loadRows to tell an edit from an old value. + const rowsRef = useRef<Row[]>([]); + useEffect(() => { + rowsRef.current = rows; + }, [rows]); const [maxChars, setMaxChars] = useState(48); const [drafts, setDrafts] = useState<Record<string, Draft>>({}); const [note, setNote] = useState<string | null>(null); @@ -470,6 +475,11 @@ export default function OnscreenSection({ /** * The table's rows. `keep` keeps every unsaved edit on top of what is now * saved -- the reload after a 409 must not throw away what was typed. + * + * An EDIT is a draft that differs from what this page last read as saved. + * A row nobody touched here takes the newly saved value: kept as its old + * draft it would read as an edit, and the next save would revert the other + * writer's change -- the very write the stale token exists to protect. */ const loadRows = useCallback( async (keep: boolean) => { @@ -479,6 +489,7 @@ export default function OnscreenSection({ setLoadError(String(j.error ?? r.status)); return; } + const before = new Map(rowsRef.current.map((row) => [row.id, draftOf(row.onscreen)])); token.current = j.token; setRows(j.rows); setMaxChars(j.maxChars); @@ -487,7 +498,8 @@ export default function OnscreenSection({ for (const row of j.rows) { const saved = draftOf(row.onscreen); const old = prev[row.id]; - next[row.id] = keep && old ? old : saved; + const was = before.get(row.id); + next[row.id] = keep && old && was && !sameDraft(old, was) ? old : saved; } return next; });