Persist post drafts to localStorage#2
Open
mozzius wants to merge 5 commits into
Open
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
mirror the editor's working state (title, body, tags, path, format and
in-post image blob refs) into localStorage so nothing typed is lost before
it reaches the PDS. drafts are debounced, flushed on pagehide, restored on
load, and cleared on save/delete. a status pill ("draft saved locally" vs
"saved to your PDS") and a restore banner with a discard action make it clear
whether what you see is only cached locally or actually committed.
image blob refs round-trip through lexToJson/jsonToLex so restored uploads
still save correctly (plain JSON.stringify mangles the CID).
also consolidates the editor's ~13 useState fields into a single useReducer
(postEditorReducer.ts) with named seed/restore/revert/clear actions.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
931351f to
d65af73
Compare
on a fresh load the persist effect ran before the record finished loading, when the form was still empty — so isDirty was false and the !isDirty branch cleared the draft from localStorage a beat before the seeding effect could restore it. gate persistence behind a `hydrated` flag set once the initial seed/restore has run. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
surfaces the existing discardDraft logic as an always-available control, shown whenever there are unsaved changes. confirms first, then clears the local draft and reverts to the published version (or empties a new post). the restore banner's "discard draft" link now routes through the same confirm. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
isDirty diffs the body against seededBodyRef, a baseline set at load and never moved on save — so after saving an existing post the body always read as dirty, and the persist effect kept re-writing the draft we'd just cleared. on save we now rebaseline seededBodyRef to the saved body and bump a baselineTick so the isDirty memo (which can't depend on a ref) recomputes — clearing the dirty state right away for body edits. a debounced write also bails if the draft was cleared underneath it (flushRef nulled), so a pending write can't resurrect a just-saved draft. metadata-only edits settle on the background refetch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
mounts the real PostEditor with the real query hooks, reducer, drafts module and jsdom localStorage; mocks only the network layer (repo.ts), auth, CodeMirror and analytics. covers the regressions we hit: draft persists on type, restores after a reload, saving clears the dirty state + draft (and doesn't resurrect it), discard reverts, and new-post drafts key by publication. verified the suite has teeth — backing out the save-rebaseline fix or the hydration gate each fails its corresponding test. sets up vitest jsdom env (per-file), testing-library, and a plausible stub alias (the package ships only a `module` field the test resolver can't load). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Mirrors everything you type in the post editor into
localStorageso nothing is lost before it reaches the PDS, with UI that makes it unambiguous whether what you see is only cached locally or actually saved.src/lib/drafts.ts(new) — DID-scoped keys (sh:draft:<did>:<rkey>, or…:new:<pub>for unsaved posts), a versionedPostDraft, andload/save/clear. In-post image blob refs round-trip throughlexToJson/jsonToLexso restored uploads still save correctly (plainJSON.stringifymangles the CID into"[object Object]").PostEditor.tsx— restores a draft on load (keeping the server body as the dirty baseline, so "dirty" correctly means local-only), debounced auto-persist (~600ms) with apagehideflush, and clear-on-save/delete.● Draft saved locally · 2m ago(accent dot) vs✓ Saved to your PDS(muted check) vsSaving….Also in here
Consolidates the editor's ~13 scattered
useStates into a singleuseReducer(postEditorReducer.ts) with namedseed/restoreDraft/revert/draftCleared/markSaved/markCleanactions. This is interleaved with the draft work inPostEditor.tsx, so it couldn't be split into a separate commit without a broken intermediate.Known tradeoffs
Fileis not persisted (not serializable); the cover picker resets on reload.Testing
pnpm typecheck✓pnpm test✓ (86 passing)pnpm format:check✓Manual: type → reload (draft restored, banner shown) → save (pill flips to "Saved to your PDS", key cleared); upload image → reload → publish (image survives); discard reverts to server.
🤖 Generated with Claude Code