Skip to content

Commit bae95ec

Browse files
Roll back failed folder icon saves
Clear only the matching optimistic icon when persistence fails so later persisted or synced values stay visible.
1 parent bfe40b6 commit bae95ec

3 files changed

Lines changed: 36 additions & 0 deletions

File tree

apps/desktop/src/folders/folder-editor.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ export function FolderEditor({ folderPath }: { folderPath: string }) {
4040
const persistedIcons = useFolderIcons();
4141
const iconOverrides = useFolderSelection((state) => state.iconOverrides);
4242
const setIconOverride = useFolderSelection((state) => state.setIconOverride);
43+
const clearIconOverride = useFolderSelection(
44+
(state) => state.clearIconOverride,
45+
);
4346
const rekeyIconOverride = useFolderSelection(
4447
(state) => state.rekeyIconOverride,
4548
);
@@ -101,6 +104,7 @@ export function FolderEditor({ folderPath }: { folderPath: string }) {
101104
onChange={(nextIcon) => {
102105
setIconOverride(folderPath, nextIcon);
103106
void updateFolderIcon(folderPath, nextIcon).catch((error) => {
107+
clearIconOverride(folderPath, nextIcon);
104108
console.error("[folder-editor] failed to update icon", error);
105109
});
106110
}}

apps/desktop/src/folders/index.test.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,4 +281,27 @@ describe("Folders workspace", () => {
281281
color: "#9ca3af",
282282
});
283283
});
284+
285+
it("clears an optimistic folder icon when saving fails", async () => {
286+
mocks.folders = ["Work"];
287+
mocks.updateFolderIcon.mockRejectedValue(new Error("unavailable"));
288+
const consoleError = vi
289+
.spyOn(console, "error")
290+
.mockImplementation(() => {});
291+
292+
render(<FoldersWorkspace />);
293+
294+
fireEvent.click(screen.getByRole("button", { name: "Choose folder icon" }));
295+
fireEvent.click(screen.getByRole("button", { name: "target" }));
296+
297+
await waitFor(() => {
298+
expect(mocks.updateFolderIcon).toHaveBeenCalledWith("Work", {
299+
type: "icon",
300+
value: "target",
301+
color: "#9ca3af",
302+
});
303+
expect(useFolderSelection.getState().iconOverrides.Work).toBeUndefined();
304+
});
305+
consoleError.mockRestore();
306+
});
284307
});

apps/desktop/src/folders/selection.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export const useFolderSelection = create<{
77
iconOverrides: Record<string, TemplateIcon>;
88
setSelectedPath: (path: string | null) => void;
99
setIconOverride: (path: string, icon: TemplateIcon) => void;
10+
clearIconOverride: (path: string, icon: TemplateIcon) => void;
1011
rekeyIconOverride: (fromPath: string, toPath: string) => void;
1112
}>((set) => ({
1213
selectedPath: null,
@@ -16,6 +17,14 @@ export const useFolderSelection = create<{
1617
set((state) => ({
1718
iconOverrides: { ...state.iconOverrides, [path]: icon },
1819
})),
20+
clearIconOverride: (path, icon) =>
21+
set((state) => {
22+
if (state.iconOverrides[path] !== icon) {
23+
return state;
24+
}
25+
const { [path]: _, ...rest } = state.iconOverrides;
26+
return { iconOverrides: rest };
27+
}),
1928
rekeyIconOverride: (fromPath, toPath) =>
2029
set((state) => {
2130
if (fromPath === toPath || !state.iconOverrides[fromPath]) {

0 commit comments

Comments
 (0)