diff --git a/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte b/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte index 5a8771195b..7e95de4f40 100644 --- a/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte +++ b/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte @@ -737,19 +737,34 @@ async function handleToggleEnabled(nEnabled: boolean) { const previousEnabled = enabled - enabled = nEnabled - if (!trigger?.draftConfig) { - const ok = await withForkConflictRetry( - (force) => - ScheduleService.setScheduleEnabled({ - path: initialPath, - workspace: wsId ?? '', - requestBody: { enabled: nEnabled, force } - }), - 'schedule' - ) + const writesBackend = !trigger?.draftConfig + const togglePath = initialPath + const setEnabled = (v: boolean) => { + // The drawer is reused: a revert landing after it moved to another + // schedule would fold this one's value into that one's baseline. + if (initialPath !== togglePath) return + enabled = v + if (writesBackend) draftSync.patchBaseline({ enabled: v }) + } + setEnabled(nEnabled) + if (writesBackend) { + let ok: boolean + try { + ok = await withForkConflictRetry( + (force) => + ScheduleService.setScheduleEnabled({ + path: initialPath, + workspace: wsId ?? '', + requestBody: { enabled: nEnabled, force } + }), + 'schedule' + ) + } catch (err) { + setEnabled(previousEnabled) + throw err + } if (!ok) { - enabled = previousEnabled + setEnabled(previousEnabled) return } sendUserToast(`${nEnabled ? 'enabled' : 'disabled'} schedule ${initialPath}`) diff --git a/frontend/src/lib/components/triggers/useTriggerDraftSync.svelte.ts b/frontend/src/lib/components/triggers/useTriggerDraftSync.svelte.ts index 98d38ac25f..abe2e287bd 100644 --- a/frontend/src/lib/components/triggers/useTriggerDraftSync.svelte.ts +++ b/frontend/src/lib/components/triggers/useTriggerDraftSync.svelte.ts @@ -88,6 +88,13 @@ export interface TriggerDraftSync { * the form back to the now-stale draft. */ discard(path: string, fallback: Cfg | undefined): void + /** + * Fold fields the editor wrote to the backend on its own (the enabled/mode + * toggle) into the baseline, so they don't read as unsaved changes. Call it + * in the same tick as the form write: a later call leaves the persist-effect + * time to save the toggle as a draft. + */ + patchBaseline(patch: Cfg): void } /** @@ -301,6 +308,15 @@ export function useTriggerDraftSync(opts: TriggerDraftSyncOptions): TriggerDraft openedOnDraft = false await opts.applyCfg(deployedCfg) }, - discard + discard, + patchBaseline(patch: Cfg) { + const deployed = opts.deployed() + if (deployed == null) return + // The deployed object too, so `resetToDeployed` restores the new value; + // `settledBaseline` because it shadows the deployed object and is the + // reactive side (an editor's `deployed` may be a plain `let`). + Object.assign(deployed, patch) + settledBaseline = { ...snapshotCfg(settledBaseline ?? deployed), ...patch } + } } }