Skip to content

feat(drafts): save proposals to the server-side SIWE drafts API - #107

Open
douglance wants to merge 1 commit into
siwe/act-as-specsfrom
siwe/drafts
Open

feat(drafts): save proposals to the server-side SIWE drafts API#107
douglance wants to merge 1 commit into
siwe/act-as-specsfrom
siwe/drafts

Conversation

@douglance

Copy link
Copy Markdown
Contributor

Adds server-backed drafts alongside the existing localStorage autosave rather than replacing it.

The two answer different needs: autosave is crash recovery for anyone with a wallet connected, this is a named copy tied to a SIWE session. Swapping the backend would have gated a currently-open flow behind sign-in and cost anonymous users their recovery, so all five localStorage call sites are untouched.

Why the form takes props instead of calling the hooks

CreateProposalForm gains two optional props and no new hooks. The session, query client, and router all live in ProposalDraftLoader. Its existing tests render it with only wagmi mocked — pulling SIWE inside would have forced a QueryClientProvider and a router into them permanently.

The mapping module

lib/drafts/mapping.ts encodes the three ways the two models disagree: governor naming, the title the form doesn't have, and the API's stricter action validation. That last one matters — the form's initial state is one blank action row, which requireAddress rejects, so posting it verbatim would always 400. Blank rows are dropped and anything still invalid is reported before the request.

Query keys

Subject-scoped keys now take string | null | undefined, matching safes, and the hooks gate on skipToken instead of enabled. That narrows the subject inside the queryFn closure, so no cast or placeholder "anon" subject is needed.

Verified: lint clean, tsc clean, 1250 unit tests, production build still prerenders /proposal/new as static.

@vercel

vercel Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tally-zero Ready Ready Preview Aug 27, 2026 6:10pm

Request Review

Adds server-backed drafts alongside the existing localStorage autosave
rather than replacing it. The two answer different needs: autosave is
crash recovery for anyone with a wallet connected, this is a named copy
tied to a SIWE session. Swapping the backend would have gated a
currently-open flow behind sign-in and cost anonymous users their
recovery, so all five localStorage call sites are untouched.

CreateProposalForm gains two optional props and no new hooks. The
session, the query client, and the router all live in
ProposalDraftLoader, which reads `?draft=` and mounts the form on that
draft's contents. Keeping SIWE out of the form is what lets its existing
tests keep rendering it with only wagmi mocked.

lib/drafts/mapping.ts encodes the three ways the two models disagree —
governor naming, the title the form does not have, and the API's stricter
action validation. That last one matters: the form's initial state is one
blank action row, which requireAddress rejects, so posting it verbatim
would always 400. Blank rows are dropped and anything still invalid is
reported before the request rather than as a server error.

Subject-scoped query keys now take `string | null | undefined`, matching
`safes`, and the hooks gate on skipToken instead of `enabled`. That
narrows the subject inside the queryFn closure, so no cast or placeholder
"anon" subject is needed anywhere.

Verified: lint clean, tsc clean, 1250 unit tests, production build still
prerenders /proposal/new as static.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant