feat: persist composer drafts in memory across navigation #155
Reference in New Issue
Block a user
Delete Branch "userAdityaa/flotilla:branch-persist-drafts"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Description
Implement in-memory draft support for all compose areas (rooms, DMs, threads, goals, classifieds, event/comment forms). Calendar event date fields are intentionally not drafted.
Solution
Fixes #150
How to Test
Looks pretty good, I think if we can start each component by reconciling initialValues and draft, it will remove a lot of complexity. Also, go ahead and add this to the new polls form as well.
@@ -40,2 +41,4 @@const {url, h, header, initialValues}: Props = $props()const draftKey = !initialValues ? `calendar:${url}:${h ?? ""}` : undefinedconst draft = draftKey ? getDraft(draftKey) : {}Let's actually do the opposite — if a user closes an edit form and re-opens it the current values should overwrite their changes. On the other hand, when creating a new event the draft doesn't conflict with anything and so it should stick around. Same thing for classifieds, goals, etc.
In fact, this will make things a lot simpler, since we can do something like
if (!h) { initialValues = {...intialValues, ...getDraft(calendar:${url}:${h})} }, cleaning up later code significantly.@@ -95,17 +99,37 @@pushToast({message: "Your event has been saved!"})publishThunk({event, relays: [url]})if (draftKey) clearDraft(draftKey)Go ahead and accept undefined in
clearDraftand move the conditional inside just to keep callers cleaner@@ -65,6 +65,7 @@const {pubkeys, info}: Props = $props()const draftKey = `dm:${[...pubkeys].sort().join(":")}`Use
$chat.idfrom the line below this one@@ -22,1 +24,3 @@const {content, disabled = false, onEscape, onEditPrevious, onSubmit}: Props = $props()const {content, disabled = false, draftKey, onEscape, onEditPrevious, onSubmit}: Props = $props()const draft = draftKey && !content ? getDraft(draftKey) : {}Howabout removing the content conditional and changing line 75 to
content ?? draft.content? Just makes things a little simpler.Completed all the refactoring changes.
Quick summary
Added draft/initialValues reconciliation pattern to
CalendarEventFormandClassifiedFormso drafts only persist for new items, not edits. Updated draftKey logic in Chat to use$chat.id, and simplified content conditionals in ChatCompose/RoomCompose.Sorry, could you clarify what you mean by “new polls” here?
Sure, there's a new component
PollCreate.sveltethat could use the same treatment.Looks like my local branch was behind my fork. I’ve synced it now and will push the changes shortly.
Pushed the changes including
PollCreate.svelte. It’s now ready for another round of review.@@ -41,0 +44,4 @@const draft = getDraft(draftKey)if (!initialValues) {initialValues = draft as unknown as typeof initialValues}In order to get rid of this coercion and ensure that get/set are dealing with the same data, maybe add a
DraftKeyutility:This will also clean up a lot of the stuff in other components like
let content = $state((draft.content as string | undefined) ?? ""), which would simply becomelet content = $state(draft?.content ?? "")@@ -109,0 +129,4 @@title,location,})})This is missing start/end
@@ -129,0 +139,4 @@let topics = $state(uniq(removeUndefined((initialValues?.topics ?? []).map(normalizeTopic))))$effect(() => {setDraft(draftKey, {...getDraft(draftKey), title, price, currency, topics, status})This is missing
images@@ -87,0 +100,4 @@let amount = $state((draft.amount as number | undefined) ?? 1000)$effect(() => {setDraft(draftKey, {...getDraft(draftKey), content, amount})destructuring shouldn't be necessary since we already set content/amount based on draft. For consistency, go ahead and add initialValues to props here too and follow the same fallback pattern.
Apologies for missing the
imagesandstart/endpoints earlier. All the feedback has now been addressed.@@ -41,0 +48,4 @@location?: stringstart?: numberend?: number}This is redundant with Props, just use
contentin both cases correctly typed.@@ -101,2 +119,2 @@const content = initialValues?.content || ""const editor = makeEditor({url, submit, uploading, content})const onChange = (json: unknown) => {draftKey.update({editorContent: json})Instead of update, let's just use set and make all the initialValues properties non-optional
@@ -103,0 +124,4 @@submit,uploading,onChange,content: draft?.editorContent ?? initialValues?.content ?? "",This is redundant if we use
contentfor both initialValues and draft.@@ -34,0 +37,4 @@pollType?: PollTypeendsAt?: numberoptions?: DraftOption[]}>(`poll:${url}:${h ?? ""}`)Define the types at the top level so they're named
Hey @hodlbod, all the feedback has been addressed, and it's ready for review.
c84cdc076dto1b1b1be34e