fix: save every ui pref key changed in one burst so dashboard reset sticks

This commit is contained in:
LukeGus committed 2026-11-04 19:07:02 -06:00
1 parent 19bdd589f3
commit 4aead47049
3 files changed
+41 -2

No files matched your search

+1
View File
@@ -29,6 +29,7 @@
- The Secure your account setup step no longer shows up empty when no passkey or authenticator plugin is on
- An installed plugin update older than the copy in a newer image is removed at startup instead of logging an error every boot
- Canceling a download to the desktop app right as it starts no longer leaves a partial file behind
- Changing two interface settings at once, like resetting the dashboard, now saves both instead of only the last one
## 26.11.0
+16 -2
View File
@@ -179,10 +179,24 @@ export function UiPreferencesProvider({
const merged = { ...pendingPatch.current };
for (const [key, value] of Object.entries(patch)) {
if (key === "overrides" && value && typeof value === "object") {
merged.overrides = {
// Two levels deep like the server, so two keys set in one area
// in the same burst both get sent.
const areas = {
...((merged.overrides as Record<string, unknown>) ?? {}),
...(value as Record<string, unknown>),
};
for (const [area, keys] of Object.entries(
value as Record<string, unknown>,
)) {
const prev = areas[area];
areas[area] =
keys &&
typeof keys === "object" &&
prev &&
typeof prev === "object"
? { ...prev, ...keys }
: keys;
}
merged.overrides = areas;
} else if (key === "onboarding" && value && typeof value === "object") {
const prev = (merged.onboarding ?? {}) as Record<string, unknown>;
const next = value as Record<string, unknown>;
@@ -185,6 +185,30 @@ describe("UiPreferencesProvider", () => {
expect(result.current?.preferences.overrides).toEqual({});
});
it("sends every key set in one area within a burst", async () => {
vi.useFakeTimers();
api.getUserPreferences.mockResolvedValue({ storageMode: "cloud" });
api.getUiPreferences.mockResolvedValue({
...defaultUiPreferences(),
overrides: { "plugin:x": { grid: ["a"], slots: ["b"] } },
});
const { result } = renderHook(() => useUiPreferencesContext(), { wrapper });
await vi.waitFor(() => expect(result.current?.loaded).toBe(true));
act(() => {
result.current?.setPluginOverride("x", "grid", null);
result.current?.setPluginOverride("x", "slots", null);
});
await act(async () => {
await vi.advanceTimersByTimeAsync(1000);
});
expect(api.saveUiPreferences).toHaveBeenCalledWith({
overrides: { "plugin:x": { grid: null, slots: null } },
});
});
it("prunes an area once its last override is cleared", async () => {
api.getUiPreferences.mockResolvedValue({
...defaultUiPreferences(),