Always apply the server full-width mode after auth (#1130)

Back-port v5 fix 6c3a2cd70 to v6. Adds layoutStore.applyServerMode, which
applies the server's canonical fullWidthMode authoritatively (even when a
local preference exists), and the post-auth path now calls it with the
already-fetched system settings instead of loadFromServer() — which made a
redundant API call and short-circuited on any stale localStorage entry, so
a user's server-side full-width setting was not honored after login/reload.
Exports createLayoutStore and adds a regression test.
This commit is contained in:
rcourtman
2026-06-04 10:02:14 +01:00
parent 2aec933897
commit 6b6684a119
3 changed files with 74 additions and 9 deletions
+4 -1
View File
@@ -279,7 +279,10 @@ export const useAppRuntimeState = () => {
updateSystemSettingsFromResponse(systemSettings);
applyServerThemeIfAllowed(systemSettings.theme);
setHasLoadedServerTheme(true);
layoutStore.loadFromServer();
// Apply the server's canonical full-width mode using the settings we just
// fetched, so it is honored after auth even if a stale localStorage
// preference exists (#1130) — loadFromServer() would short-circuit on it.
layoutStore.applyServerMode(systemSettings.fullWidthMode);
} catch (error) {
logger.error('Failed to load system settings from server', error);
markSystemSettingsLoadedWithDefaults();
@@ -0,0 +1,51 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
vi.mock('@/api/settings', () => ({
SettingsAPI: { getSystemSettings: vi.fn() },
}));
vi.mock('@/utils/logger', () => ({
logger: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() },
}));
import { createLayoutStore } from '@/utils/layout';
import { STORAGE_KEYS } from '@/utils/localStorage';
describe('layout store applyServerMode (#1130)', () => {
beforeEach(() => {
localStorage.clear();
});
it('applies the server full-width value even when a stale local preference exists', () => {
// Stale local preference that loadFromServer() would otherwise honor.
localStorage.setItem(STORAGE_KEYS.FULL_WIDTH_MODE, 'default');
const store = createLayoutStore();
expect(store.isFullWidth()).toBe(false);
store.applyServerMode(true);
expect(store.isFullWidth()).toBe(true);
expect(localStorage.getItem(STORAGE_KEYS.FULL_WIDTH_MODE)).toBe('full-width');
});
it('applies a server default that overrides a local full-width preference', () => {
localStorage.setItem(STORAGE_KEYS.FULL_WIDTH_MODE, 'full-width');
const store = createLayoutStore();
expect(store.isFullWidth()).toBe(true);
store.applyServerMode(false);
expect(store.isFullWidth()).toBe(false);
});
it('leaves the current mode unchanged when the server value is undefined', () => {
localStorage.setItem(STORAGE_KEYS.FULL_WIDTH_MODE, 'full-width');
const store = createLayoutStore();
store.applyServerMode(undefined);
expect(store.isFullWidth()).toBe(true);
});
});
+19 -8
View File
@@ -12,7 +12,7 @@ export type LayoutMode = 'default' | 'full-width';
* Creates a reactive store for layout mode preference
* Syncs with both localStorage (for immediate access) and server (for persistence across updates)
*/
function createLayoutStore() {
export function createLayoutStore() {
const stored = localStorage.getItem(STORAGE_KEYS.FULL_WIDTH_MODE);
const initialMode: LayoutMode = stored === 'full-width' ? 'full-width' : 'default';
@@ -38,6 +38,22 @@ function createLayoutStore() {
const isFullWidth = () => mode() === 'full-width';
/**
* Apply the server's canonical full-width preference (called after auth with
* the already-fetched system settings). Unlike loadFromServer this is
* authoritative: it applies the server value even when a local preference
* exists, so the server setting is honored after login/reload (#1130).
*/
const applyServerMode = (serverFullWidthMode: boolean | undefined) => {
if (serverFullWidthMode !== undefined) {
const serverMode: LayoutMode = serverFullWidthMode ? 'full-width' : 'default';
localStorage.setItem(STORAGE_KEYS.FULL_WIDTH_MODE, serverMode);
setModeInternal(serverMode);
logger.debug('Applied full-width mode from server', { mode: serverMode });
}
setHasLoadedFromServer(true);
};
/**
* Load full-width preference from server (called after auth)
* Only uses server preference if no local preference exists
@@ -50,13 +66,7 @@ function createLayoutStore() {
try {
const settings = await SettingsAPI.getSystemSettings();
if (settings.fullWidthMode !== undefined) {
const serverMode: LayoutMode = settings.fullWidthMode ? 'full-width' : 'default';
localStorage.setItem(STORAGE_KEYS.FULL_WIDTH_MODE, serverMode);
setModeInternal(serverMode);
logger.debug('Loaded full-width mode from server', { mode: serverMode });
}
setHasLoadedFromServer(true);
applyServerMode(settings.fullWidthMode);
} catch (error) {
logger.warn('Failed to load full-width mode from server', error);
}
@@ -67,6 +77,7 @@ function createLayoutStore() {
setMode,
toggle,
isFullWidth,
applyServerMode,
loadFromServer,
};
}