Skip to content

serve: dedup the optimistic-write scaffold in web/src/store.ts #27

Description

@andreiRS

Follow-up from the #13#26 integration review (quality-only, non-blocking; deferred deliberately because the web layer has no test net — UI testing is browser-only/locked — so a blind refactor carries regression risk).

What

The optimistic / pending / reconcile machinery in web/src/store.ts grew across #19/#20/#21/#22/#23 and now has parallel near-copies:

  1. Post-dating-by-sha is copy-pasted 3× (move, create, edit reconcile blocks in applySnapshot):

    const postDates = board.head != null && (p.issuedHead == null || board.head.sha !== p.issuedHead.sha);

    → extract postDatesIssued(head, issuedHead): boolean.

  2. The three pending-write bodies (move, createTask, editTask) repeat the same scaffold: capture issuedHead, optimistic apply, set the pending entry, clearDelayTimer + an identical 200ms setTimeout that flips showPending.
    → extract a startPendingTimer(key, set, mapName) (or similar) helper for the delay-gate.

The #23 slice already unified the failure path (handleWriteFailure); this is the remaining issue-path + reconcile-loop duplication.

Out of scope

Leave the per-type landed checks as-is — their semantics genuinely differ (lane membership for move, uuid-in-board for create, confirm-and-drop for edit). This is a DRY pass on the shared scaffold only.

Verification

No automated UI tests exist (locked: browser-only). After the refactor, browser-verify move/create/edit happy + failure paths on the single-origin server, and gate on tsc --noEmit (root + web) + web build.

Metadata

Metadata

Assignees

No one assigned

    Labels

    serve-boardVisual web board (tasks serve) v1

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions