mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-21 00:02:30 +00:00
fix: a routed discard names where it landed, a superseded drawer opening is dropped, and a loaded draft keeps its base in every editor
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
c80f3f5293
commit
493448b2ce
@@ -321,8 +321,8 @@ pub struct SaveDraftResponse {
|
||||
/// On `saved`: when the change was applied (client remembers it as the
|
||||
/// next `last_sync`). On `conflict`: the existing row's `created_at`.
|
||||
pub current_timestamp: chrono::DateTime<chrono::Utc>,
|
||||
/// `saved` upserts only: where the draft is. Differs from the URL path when
|
||||
/// the item had moved away from it; the editor follows it there.
|
||||
/// `saved` only: where the write landed. Differs from the URL path when the item
|
||||
/// had moved away from it; the editor follows it there.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub path: Option<String>,
|
||||
}
|
||||
@@ -538,7 +538,9 @@ async fn update_draft(
|
||||
)
|
||||
.fetch_optional(&db)
|
||||
.await?
|
||||
.map(|ts| (ts, None))
|
||||
// Named for the same reason an upsert is: the editor that discarded is still on
|
||||
// the path the item left, and reloading there would land on nothing.
|
||||
.map(|ts| (ts, moved_to.clone()))
|
||||
};
|
||||
|
||||
if let Some((ts, path)) = applied {
|
||||
|
||||
@@ -1163,17 +1163,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
/** Bumped per drawer opening: the fetches below are awaited, so a reopen (or a
|
||||
* path change) while they run must not have the older one land last. */
|
||||
let diffOpening = 0
|
||||
|
||||
export async function openDiffDrawer() {
|
||||
const opening = ++diffOpening
|
||||
if (!savedFlow) return
|
||||
await syncWithDeployed()
|
||||
const currentDraftTriggers = structuredClone(triggersState.getDraftTriggersSnapshot())
|
||||
diffDrawer?.openDrawer()
|
||||
const currentFlow = flowStore.val
|
||||
const versions = await deployedVersionOptions()
|
||||
if (opening !== diffOpening) return
|
||||
diffDrawer?.setDiff({
|
||||
mode: 'normal',
|
||||
deployed: deployedValue ?? savedFlow,
|
||||
deployedLabel,
|
||||
versions: await deployedVersionOptions(),
|
||||
versions,
|
||||
onTakeLatest,
|
||||
draftBase: draftBaseVersion,
|
||||
deployedHead: deployedVersionShown != null ? String(deployedVersionShown) : undefined,
|
||||
|
||||
@@ -870,7 +870,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
/** Bumped per drawer opening: the fetches below are awaited, so a reopen (or a
|
||||
* path change) while they run must not have the older one land last. */
|
||||
let diffOpening = 0
|
||||
|
||||
export async function openDiffDrawer() {
|
||||
const opening = ++diffOpening
|
||||
if (!savedScript) {
|
||||
return
|
||||
}
|
||||
@@ -893,11 +898,13 @@
|
||||
|
||||
diffDrawer?.openDrawer()
|
||||
const headHash = (deployed as { hash?: string } | undefined)?.hash
|
||||
const versions = await deployedVersionOptions(headHash)
|
||||
if (opening !== diffOpening) return
|
||||
diffDrawer?.setDiff({
|
||||
mode: 'normal',
|
||||
deployed,
|
||||
deployedLabel: deployedVersionLabel(deployed),
|
||||
versions: await deployedVersionOptions(headHash),
|
||||
versions,
|
||||
onTakeLatest,
|
||||
draftBase: draftBaseHash,
|
||||
deployedHead: headHash,
|
||||
|
||||
@@ -450,7 +450,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
/** Bumped per drawer opening: the fetches below are awaited, so a reopen (or a
|
||||
* path change) while they run must not have the older one land last. */
|
||||
let diffOpening = 0
|
||||
|
||||
export async function openDiffDrawer() {
|
||||
const opening = ++diffOpening
|
||||
if (!savedApp) {
|
||||
return
|
||||
}
|
||||
@@ -459,10 +464,12 @@
|
||||
await syncWithDeployed()
|
||||
|
||||
diffDrawer?.openDrawer()
|
||||
const versions = await deployedVersionOptions()
|
||||
if (opening !== diffOpening) return
|
||||
diffDrawer?.setDiff({
|
||||
mode: 'normal',
|
||||
deployed: deployedValue ?? stripRawAppDiffNoise(savedApp),
|
||||
versions: await deployedVersionOptions(),
|
||||
versions,
|
||||
onTakeLatest,
|
||||
draftBase: draftBaseVersion,
|
||||
deployedHead: deployedVersionShown != null ? String(deployedVersionShown) : undefined,
|
||||
|
||||
@@ -340,9 +340,10 @@ async function postSave(opts: UserDraftDbSyncerSaveOpts): Promise<void> {
|
||||
// cached state the same way an upsert does. Listener errors must never
|
||||
// make a committed save read as failed.
|
||||
notifyAnySaved({ workspace: opts.workspace, itemKind: opts.itemKind, path: opts.path })
|
||||
// The item had moved and the save landed where its drafts went. Last,
|
||||
// The item had moved and the write landed where its drafts went — a discard
|
||||
// included, since the editor that sent it is on a path the item has left. Last,
|
||||
// so the editor that reacts (by leaving this path) sees a settled key.
|
||||
if (opts.value !== null && resp.path && resp.path !== opts.path) {
|
||||
if (resp.path && resp.path !== opts.path) {
|
||||
const listeners = relocationListeners.get(key)
|
||||
if (listeners) for (const l of [...listeners]) l(resp.path)
|
||||
}
|
||||
|
||||
@@ -307,6 +307,11 @@
|
||||
}
|
||||
if (pendingLoad) {
|
||||
backendApp = { ...backendApp, value: pendingLoad.value as App } as typeof backendApp
|
||||
// Their draft's base, not ours: the prompt and the deploy guard read it, and
|
||||
// deploying their content on our base would claim a version it never forked
|
||||
// from. See /scripts/edit.
|
||||
const theirs = (pendingLoad.value as App)?.parent_version
|
||||
draftBaseVersion = theirs != null ? String(theirs) : undefined
|
||||
if (hasOwnDraft) {
|
||||
// AppEditor `migrateApp`s the value in place on mount (see its
|
||||
// `migratedDeployedBaseline`), so the draft cell settles to the
|
||||
@@ -334,7 +339,8 @@
|
||||
// draft value. An existing own draft already carries it (preserved by the
|
||||
// value swap above). `parent_version` is in DRAFT_COMPARE_IGNORED_FIELDS, so it
|
||||
// never trips the autosave no-op / "unsaved changes" comparison.
|
||||
if (!hasOwnDraft && !backendApp.no_deployed && backendApp.value) {
|
||||
// Not after loading a teammate's draft either: that value carries their base.
|
||||
if (!hasOwnDraft && !pendingLoad && !backendApp.no_deployed && backendApp.value) {
|
||||
const versions = (backendApp as { versions?: number[] }).versions
|
||||
const head = Array.isArray(versions) ? versions[versions.length - 1] : undefined
|
||||
if (head != null) (backendApp.value as App).parent_version = head
|
||||
|
||||
@@ -404,6 +404,11 @@
|
||||
? ({ ...deployedFlow, ...(pendingLoad.value as object) } as Flow)
|
||||
: effectiveFlow
|
||||
flow = flowToRender
|
||||
if (pendingLoad) {
|
||||
// Their draft's base, not ours; see /scripts/edit.
|
||||
const theirs = (pendingLoad.value as { version_id?: number })?.version_id
|
||||
draftBaseVersion = theirs != null ? String(theirs) : undefined
|
||||
}
|
||||
if (pendingLoad && hasOwnDraft) {
|
||||
OtherUserDraftLoad.beginOverlay({
|
||||
workspace: $workspaceStore!,
|
||||
|
||||
@@ -389,6 +389,10 @@
|
||||
...(pendingLoad.value as object),
|
||||
parent_hash: (pendingLoad.value as { parent_hash?: string })?.parent_hash ?? parentHash
|
||||
} as EditableScript
|
||||
// Their draft's base, not ours: the prompt and the deploy guard read these,
|
||||
// and deploying their content on our base would claim a version it never
|
||||
// forked from.
|
||||
draftBaseHash = loadedValue.parent_hash
|
||||
if (hasOwnDraft) {
|
||||
OtherUserDraftLoad.beginOverlay({
|
||||
workspace: $workspaceStore!,
|
||||
|
||||
Reference in New Issue
Block a user