128 lines
4.5 KiB
TypeScript
128 lines
4.5 KiB
TypeScript
import type { SettingsMenuMode } from "@/lib/settings/settings-menu-mode";
|
|
import type { Theme } from "@/lib/settings/types";
|
|
import type { UserSettingsState } from "@/lib/state/slices/settings/types";
|
|
import type { UserSettingsUpdatePayload } from "@/lib/types/http";
|
|
|
|
export type AppearanceState = {
|
|
theme: Theme;
|
|
settingsMenuMode: SettingsMenuMode;
|
|
richOutputAnimationsEnabled: boolean;
|
|
startupPage: UserSettingsState["startupPage"];
|
|
changesPanelLayout: UserSettingsState["changesPanelLayout"];
|
|
appStatusBarEnabled: boolean;
|
|
sidebarHoverEnabled: boolean;
|
|
sidebarHoverDelayMs: string;
|
|
showMetrics: boolean;
|
|
simplifiedMetrics: boolean;
|
|
};
|
|
|
|
export function createAppearanceSavedState(
|
|
theme: Theme,
|
|
settingsMenuMode: SettingsMenuMode,
|
|
richOutputAnimationsEnabled: boolean,
|
|
userSettings: Pick<
|
|
UserSettingsState,
|
|
| "appStatusBarEnabled"
|
|
| "changesPanelLayout"
|
|
| "startupPage"
|
|
| "systemMetricsDisplay"
|
|
| "sidebarHoverEnabled"
|
|
| "sidebarHoverDelayMs"
|
|
>,
|
|
): AppearanceState {
|
|
return {
|
|
theme,
|
|
sidebarHoverEnabled: userSettings.sidebarHoverEnabled,
|
|
sidebarHoverDelayMs: String(userSettings.sidebarHoverDelayMs),
|
|
appStatusBarEnabled: userSettings.appStatusBarEnabled,
|
|
// Per-device, but drafted and saved with account settings under one control.
|
|
settingsMenuMode,
|
|
richOutputAnimationsEnabled,
|
|
changesPanelLayout: userSettings.changesPanelLayout,
|
|
startupPage: userSettings.startupPage,
|
|
showMetrics: userSettings.systemMetricsDisplay.showInTopbar,
|
|
simplifiedMetrics: userSettings.systemMetricsDisplay.simplified,
|
|
};
|
|
}
|
|
|
|
export function buildAppearanceUserSettingsPatch(
|
|
submitted: AppearanceState,
|
|
saved: AppearanceState,
|
|
): UserSettingsUpdatePayload {
|
|
const delay = parseSidebarHoverDelay(submitted.sidebarHoverDelayMs);
|
|
// i18n-exempt: unreachable invariant guard; the contributor presents localized validation.
|
|
if (delay === null) throw new Error("Invalid sidebar hover delay");
|
|
const patch: UserSettingsUpdatePayload = {};
|
|
if (submitted.sidebarHoverEnabled !== saved.sidebarHoverEnabled) {
|
|
patch.sidebar_hover_enabled = submitted.sidebarHoverEnabled;
|
|
}
|
|
if (delay !== Number(saved.sidebarHoverDelayMs)) {
|
|
patch.sidebar_hover_delay_ms = delay;
|
|
}
|
|
if (submitted.startupPage !== saved.startupPage) {
|
|
patch.startup_page = submitted.startupPage;
|
|
}
|
|
if (submitted.changesPanelLayout !== saved.changesPanelLayout) {
|
|
patch.changes_panel_layout = submitted.changesPanelLayout;
|
|
}
|
|
if (submitted.appStatusBarEnabled !== saved.appStatusBarEnabled) {
|
|
patch.app_status_bar_enabled = submitted.appStatusBarEnabled;
|
|
}
|
|
const metrics: NonNullable<UserSettingsUpdatePayload["system_metrics_display"]> = {};
|
|
if (submitted.showMetrics !== saved.showMetrics) {
|
|
metrics.show_in_topbar = submitted.showMetrics;
|
|
}
|
|
if (submitted.simplifiedMetrics !== saved.simplifiedMetrics) {
|
|
metrics.simplified = submitted.simplifiedMetrics;
|
|
}
|
|
if (Object.keys(metrics).length > 0) {
|
|
patch.system_metrics_display = metrics;
|
|
}
|
|
return patch;
|
|
}
|
|
|
|
export function rebaseAppearanceDraft(
|
|
draft: AppearanceState,
|
|
baseline: AppearanceState,
|
|
nextSaved: AppearanceState,
|
|
preserveDraftFields: ReadonlySet<keyof AppearanceState> = new Set(),
|
|
): AppearanceState {
|
|
const rebase = <Field extends keyof AppearanceState>(field: Field): AppearanceState[Field] =>
|
|
preserveDraftFields.has(field) || draft[field] !== baseline[field]
|
|
? draft[field]
|
|
: nextSaved[field];
|
|
return {
|
|
theme: rebase("theme"),
|
|
sidebarHoverEnabled: rebase("sidebarHoverEnabled"),
|
|
sidebarHoverDelayMs: rebase("sidebarHoverDelayMs"),
|
|
settingsMenuMode: rebase("settingsMenuMode"),
|
|
richOutputAnimationsEnabled: rebase("richOutputAnimationsEnabled"),
|
|
startupPage: rebase("startupPage"),
|
|
changesPanelLayout: rebase("changesPanelLayout"),
|
|
appStatusBarEnabled: rebase("appStatusBarEnabled"),
|
|
showMetrics: rebase("showMetrics"),
|
|
simplifiedMetrics: rebase("simplifiedMetrics"),
|
|
};
|
|
}
|
|
|
|
export function appearanceRevision(state: AppearanceState): string {
|
|
return JSON.stringify([
|
|
state.theme,
|
|
state.sidebarHoverEnabled,
|
|
state.sidebarHoverDelayMs,
|
|
state.settingsMenuMode,
|
|
state.richOutputAnimationsEnabled,
|
|
state.startupPage,
|
|
state.changesPanelLayout,
|
|
state.appStatusBarEnabled,
|
|
state.showMetrics,
|
|
state.simplifiedMetrics,
|
|
]);
|
|
}
|
|
|
|
export function parseSidebarHoverDelay(value: string): number | null {
|
|
if (!/^\d+$/.test(value)) return null;
|
|
const delay = Number(value);
|
|
return Number.isInteger(delay) && delay >= 0 && delay <= 5000 ? delay : null;
|
|
}
|