mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-25 09:41:04 +01:00
fix(artifacts): heal Starred tab + scope document-star rollback (Greptile)
- onSettled now invalidates the ["artifacts"] list queries too, so an unstarred document leaves the Starred tab immediately instead of lingering until the next natural refetch (the tab is a separate list query from the memberships cache). - Rollback on error re-applies only the failed document's prior star on top of the current cache, instead of restoring a whole snapshot that could clobber a concurrent star mutation's optimistic state. - Add hook test asserting the artifacts list is invalidated on settle. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
19dfe83e56
commit
a9752fc870
2 files changed
+40
-4
No files matched your search
@@ -149,6 +149,32 @@ describe("useDocumentStarMutation", () => {
|
||||
expect(starredIds(queryClient)).toContain("doc-1");
|
||||
});
|
||||
|
||||
it("invalidates the Artifacts list on settle so an unstarred doc leaves the Starred tab", async () => {
|
||||
membershipsApiMock.listMine.mockResolvedValue({
|
||||
...emptyMemberships(),
|
||||
starredDocumentIds: ["doc-1"],
|
||||
documentStarredAt: { "doc-1": new Date().toISOString() },
|
||||
});
|
||||
membershipsApiMock.updateDocument.mockResolvedValue({
|
||||
resourceType: "document",
|
||||
resourceId: "doc-1",
|
||||
state: "left",
|
||||
starredAt: null,
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
|
||||
let handle: HarnessHandle | null = null;
|
||||
const queryClient = await render((h) => { handle = h; });
|
||||
const invalidateSpy = vi.spyOn(queryClient, "invalidateQueries");
|
||||
|
||||
await act(async () => {
|
||||
handle!.mutate({ documentId: "doc-1", documentName: "Launch Brief", starred: false });
|
||||
await flush();
|
||||
});
|
||||
|
||||
expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ["artifacts"] });
|
||||
});
|
||||
|
||||
it("rolls back the optimistic unstar and toasts when the server rejects", async () => {
|
||||
membershipsApiMock.listMine.mockResolvedValue({
|
||||
...emptyMemberships(),
|
||||
|
||||
@@ -259,16 +259,22 @@ export function useDocumentStarMutation(companyId: string | null | undefined) {
|
||||
onMutate: async (variables) => {
|
||||
await queryClient.cancelQueries({ queryKey });
|
||||
const previous = queryClient.getQueryData<ResourceMemberships>(queryKey);
|
||||
// Capture only this document's prior star so a rollback reverts just this
|
||||
// change and leaves any concurrent star mutation's optimistic state intact.
|
||||
const previousStarred = isStarred(previous, "document", variables.documentId);
|
||||
queryClient.setQueryData<ResourceMemberships>(
|
||||
queryKey,
|
||||
applyDocumentStarChange(previous, variables.documentId, variables.starred),
|
||||
);
|
||||
return { previous };
|
||||
return { previousStarred };
|
||||
},
|
||||
onError: (error, variables, context) => {
|
||||
if (context?.previous) {
|
||||
queryClient.setQueryData(queryKey, context.previous);
|
||||
}
|
||||
// Targeted rollback: re-apply this document's prior star on top of the
|
||||
// current cache, so we don't clobber a newer concurrent update.
|
||||
queryClient.setQueryData<ResourceMemberships>(
|
||||
queryKey,
|
||||
(current) => applyDocumentStarChange(current, variables.documentId, context?.previousStarred ?? false),
|
||||
);
|
||||
pushToast({
|
||||
title: `Couldn't ${variables.starred ? "star" : "unstar"} ${variables.documentName}.`,
|
||||
body: error instanceof Error ? error.message : "Try again.",
|
||||
@@ -284,6 +290,10 @@ export function useDocumentStarMutation(companyId: string | null | undefined) {
|
||||
},
|
||||
onSettled: () => {
|
||||
queryClient.invalidateQueries({ queryKey });
|
||||
// The Artifacts "Starred" tab is a separate list query keyed by ["artifacts", ...],
|
||||
// so refresh it too — otherwise an unstarred document lingers in the grid
|
||||
// until the next natural refetch.
|
||||
queryClient.invalidateQueries({ queryKey: ["artifacts"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user