Files
lanspread/crates/lanspread-tauri-deno-ts/src/hooks/useSettings.ts
T
ddidderr ebeee2d90a fix(settings): name descending size sort explicitly
The library sort setting used `size` for largest-first sorting while the
ascending option used `sizeAsc`. That made the pair asymmetric and left the
current settings model carrying a legacy-looking key.

Rename the current descending key to `sizeDesc` in the type, menu, and sort
logic. Stored `size` values are normalized to `sizeDesc` on read, so existing
users keep the same largest-first behavior while new writes use the explicit
key.

Test Plan:
- deno task build
- RUSTC_WRAPPER= CARGO_BUILD_RUSTC_WRAPPER= just build
- git diff --check

Refs: local review feedback
2026-05-19 21:28:40 +02:00

127 lines
4.0 KiB
TypeScript

import { useCallback, useEffect, useState } from 'react';
import { load } from '@tauri-apps/plugin-store';
import { GameFilter, GameSort } from '../lib/types';
import { SETTINGS_FILE, SETTINGS_FILE_OPTIONS, UI_SETTINGS_KEY } from '../lib/store';
export type Density = 'compact' | 'normal' | 'large';
export type CoverAspect = 'box' | 'square' | 'banner';
export type BackgroundStyle = 'flat' | 'gradient' | 'animated';
export interface UISettings {
accent: string;
bg: BackgroundStyle;
density: Density;
aspect: CoverAspect;
sort: GameSort;
filter: GameFilter;
}
type StoredGameSort = GameSort | 'size';
type StoredUISettings = Partial<Omit<UISettings, 'sort'> & { sort: StoredGameSort }>;
export const ACCENT_OPTIONS = [
{ value: '#3b82f6', label: 'Blue' },
{ value: '#22d3ee', label: 'Cyan' },
{ value: '#a855f7', label: 'Violet' },
{ value: '#22c55e', label: 'Green' },
{ value: '#f59e0b', label: 'Amber' },
{ value: '#ef4444', label: 'Red' },
] as const;
export const BG_OPTIONS: ReadonlyArray<{ value: BackgroundStyle; label: string }> = [
{ value: 'flat', label: 'Flat' },
{ value: 'gradient', label: 'Gradient' },
{ value: 'animated', label: 'Animated' },
];
export const DENSITY_OPTIONS: ReadonlyArray<{ value: Density; label: string }> = [
{ value: 'compact', label: 'Compact' },
{ value: 'normal', label: 'Normal' },
{ value: 'large', label: 'Large' },
];
export const ASPECT_OPTIONS: ReadonlyArray<{ value: CoverAspect; label: string }> = [
{ value: 'box', label: 'Box-art' },
{ value: 'square', label: 'Square' },
{ value: 'banner', label: 'Banner' },
];
export const DEFAULT_SETTINGS: UISettings = {
accent: '#3b82f6',
bg: 'gradient',
density: 'normal',
aspect: 'square',
sort: 'status',
filter: 'local',
};
const sanitize = (raw: StoredUISettings | undefined): UISettings => ({
accent: raw?.accent ?? DEFAULT_SETTINGS.accent,
bg: raw?.bg ?? DEFAULT_SETTINGS.bg,
density: raw?.density ?? DEFAULT_SETTINGS.density,
aspect: raw?.aspect ?? DEFAULT_SETTINGS.aspect,
sort: sanitizeSort(raw?.sort),
filter: raw?.filter ?? DEFAULT_SETTINGS.filter,
});
const sanitizeSort = (sort: StoredGameSort | undefined): GameSort => (
sort === 'size' ? 'sizeDesc' : sort ?? DEFAULT_SETTINGS.sort
);
export interface UseSettings {
settings: UISettings;
set: <K extends keyof UISettings>(key: K, value: UISettings[K]) => void;
ready: boolean;
}
/**
* Loads UI preferences from the Tauri persistent store once on mount and
* writes every change back through it. Components only see a synchronous
* `settings` snapshot; persistence is fire-and-forget.
*/
export const useSettings = (): UseSettings => {
const [settings, setSettings] = useState<UISettings>(DEFAULT_SETTINGS);
const [ready, setReady] = useState(false);
useEffect(() => {
let cancelled = false;
const init = async () => {
try {
const store = await load(SETTINGS_FILE, SETTINGS_FILE_OPTIONS);
const saved = await store.get<StoredUISettings>(UI_SETTINGS_KEY);
if (!cancelled) {
setSettings(sanitize(saved ?? undefined));
}
} catch (err) {
console.error('Failed to load UI settings:', err);
} finally {
if (!cancelled) setReady(true);
}
};
void init();
return () => {
cancelled = true;
};
}, []);
const set = useCallback(<K extends keyof UISettings>(key: K, value: UISettings[K]) => {
setSettings(prev => {
const next = { ...prev, [key]: value };
void persist(next);
return next;
});
}, []);
return { settings, set, ready };
};
const persist = async (settings: UISettings): Promise<void> => {
try {
const store = await load(SETTINGS_FILE, SETTINGS_FILE_OPTIONS);
await store.set(UI_SETTINGS_KEY, settings);
} catch (err) {
console.error('Failed to persist UI settings:', err);
}
};