fix(drafts): preserve the user-typed draft_path on reload of draft-only items

The flow / app / raw-app editors all dropped the saved `draft_path`
back to the URL's `u/{user}/draft_{uuid}` slot the moment the user
reloaded a draft-only edit page: the route sourced the Path widget's
initial path from `page.params.path` instead of the previously-saved
`draft_path`, and the first user edit then mirrored that URL path
back into the autosaved draft — silently overwriting the friendly
name in both the row and the editor.

- Flow route: after computing `effectiveFlow`, override `flowInitialPath`
  with `effectiveFlow.draft_path` when set.
- App route: pass `newPath={(app.value as any)?.draft_path ?? app.path}`
  through to `AppEditor`; AppEditorHeader's `newEditedPath` default now
  prefers a non-empty `newPath` over the random `<adj>_app` seed (the
  `newApp && !newPath` branch keeps the `/apps/add` friendly auto-name).
- Raw-app route: surface `savedRawAppDraft.draft_path` onto `backendApp`
  so the `extractRawApp` path seeds `newPath` with the friendly name.

Reload + a subsequent edit now leaves `draft_path` intact for all three
kinds; verified end-to-end via the `/drafts/get_draft/...` endpoint.
This commit is contained in:
Diego Imbert
2026-06-08 01:03:48 +02:00
parent 47c45edd80
commit 3290b80817
4 changed files with 25 additions and 3 deletions
@@ -141,7 +141,9 @@
* rename detection still works. */
let newEditedPath = $state(
untrack(() =>
newApp ? userPathPrefix($userStore?.username) + random_adj() + '_app' : (newPath ?? '')
newApp && !newPath
? userPathPrefix($userStore?.username) + random_adj() + '_app'
: (newPath ?? '')
)
)
let deployedValue: Value | undefined = $state(undefined) // Value to diff against
@@ -345,7 +345,7 @@
on:restore={onRestore}
summary={app.summary}
app={app.value}
newPath={app.path}
newPath={(app.value as any)?.draft_path ?? app.path}
path={page.params.path ?? ''}
policy={app.policy}
bind:savedApp
@@ -210,7 +210,11 @@
summary = app.summary
// lastVersion = app.version
policy = app.policy
newPath = app.path
// Reload of a draft-only (or rename-in-progress) raw app: prefer
// the previously-typed `draft_path` so the topbar shows the
// pending friendly name instead of the autogenerated
// `u/{user}/draft_{uuid}` URL slot.
newPath = (app as any).draft_path ?? app.path
}
/** Increments per `loadApp` call. Stale loads (e.g. when picker
@@ -321,8 +325,15 @@
summary?: string
policy?: any
custom_path?: string
draft_path?: string
}
| undefined
// Surface the saved `draft_path` on `backendApp` so the
// extract-from-backend path below seeds `newPath` with the
// friendly name instead of the URL `draft_{uuid}` slot.
if (savedRawAppDraft?.draft_path) {
;(backendApp as any).draft_path = savedRawAppDraft.draft_path
}
if (backendApp.no_deployed) {
backendApp.value = {
files: savedRawAppDraft?.files ?? {},
@@ -294,6 +294,15 @@
? ({ ...deployedFlow, ...draftFromBackend } as Flow)
: (deployedFlow as Flow)
savedFlow = structuredClone($state.snapshot(effectiveFlow)) as Flow
// Reload of a draft-only (or rename-in-progress) flow: surface
// the previously-typed `draft_path` to the Path widget so the
// topbar shows the user's pending name instead of the
// autogenerated `u/{user}/draft_{uuid}` URL slot. Without this
// the widget seeds from `page.params.path`, the user's first
// edit clobbers `draft.draft_path` back to the URL path, and
// the home list silently loses the friendly name.
const renderedDraftPath = (effectiveFlow as any).draft_path as string | undefined
if (renderedDraftPath) flowInitialPath = renderedDraftPath
const localDraft = flowHandle.draft
const previousMeta = flowHandle.meta