mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-31 14:16:39 +01:00
fix(ui): supersede startup jobs when newer edits are admitted
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
8f58be556a
commit
b257ae2014
2 files changed
+29
No files matched your search
@@ -120,6 +120,32 @@ describe("useDurableAutosave", () => {
|
||||
expect(store.getSnapshot().pendingWrites).toBe(0);
|
||||
});
|
||||
|
||||
it.each(["same-target", "different-target"])("preserves newer completed saves across first identity resolution (%s)", async target => {
|
||||
let server = "Original";
|
||||
const client = new QueryClient({ defaultOptions: { queries: { staleTime: Infinity, retry: false, enabled: false } } });
|
||||
const oldSave = vi.fn().mockRejectedValueOnce(outage()).mockImplementation(async value => { server = value; });
|
||||
const show = (sourceId: string, targetKey: string, save: (value: string) => Promise<unknown>) => act(() => {
|
||||
root.render(<QueryClientProvider client={client}><ConnectivityProvider store={store}>
|
||||
<Harness sourceId={sourceId} targetKey={targetKey} save={save} />
|
||||
</ConnectivityProvider></QueryClientProvider>);
|
||||
});
|
||||
show("old-startup-editor", "task-a:title", oldSave);
|
||||
await act(async () => { expect(await current.run("Older title")).toEqual({ kind: "waiting" }); });
|
||||
act(() => { root.unmount(); root = createRoot(container); });
|
||||
act(() => client.setQueryData(queryKeys.auth.session, { user: { id: "user-1" } }));
|
||||
const newSave = vi.fn(async value => { if (target === "same-target") server = value; });
|
||||
show("new-known-editor", target === "same-target" ? "task-a:title" : "task-b:title", newSave);
|
||||
await act(async () => { expect(await current.run("Newer title")).toEqual({ kind: "saved" }); });
|
||||
expect(newSave).toHaveBeenCalledExactlyOnceWith("Newer title");
|
||||
// The newer save has finished and released its registry entry before the
|
||||
// detached startup job gets its first chance to adopt the account.
|
||||
await advance(30_000);
|
||||
expect(server).toBe(target === "same-target" ? "Newer title" : "Older title");
|
||||
expect(oldSave).toHaveBeenCalledTimes(target === "same-target" ? 1 : 2);
|
||||
expect(store.getSnapshot().pendingWrites).toBe(0);
|
||||
client.clear();
|
||||
});
|
||||
|
||||
it("keeps a detached edit when a reopened editor resets only its local state and storage is unavailable", async () => {
|
||||
const storage = vi.spyOn(Storage.prototype, "setItem").mockImplementation(() => { throw new Error("Storage unavailable"); });
|
||||
try {
|
||||
|
||||
@@ -273,6 +273,9 @@ export function useDurableAutosave<T>(options: DurableAutosaveOptions<T>): Durab
|
||||
const current = optionsRef.current;
|
||||
const target = current.targetKey ?? current.sourceId;
|
||||
const admittedOwner = ownerRef.current ?? readOwner();
|
||||
// A newer known-account edit supersedes an older startup job even if this
|
||||
// save finishes before that job can adopt the account's registry key.
|
||||
if (admittedOwner !== undefined) registry.delete(JSON.stringify([undefined, target]));
|
||||
const job: Job = { token: Symbol(), registryKey: JSON.stringify([admittedOwner, target]), resends: 0, id: ++sequence.current, target, owner: admittedOwner, account: client ? bindAccount(client) : null, draft,
|
||||
save: overrides.save ?? current.save, matchesServer: overrides.matchesServer ?? current.matchesServer,
|
||||
onRetryFailed: overrides.onRetryFailed, storageKey: current.storageKey ?? null, action: current.action ?? "save" };
|
||||
|
||||
Reference in new issue
Block a user