Files
Psychotoxical-psysonic/src/store/authStoreHelpers.ts
T
Maxim Isaev 0f5ece6d03 feat(settings): library card grid max columns in Appearance (4–12, default 6)
Persist libraryGridMaxColumns, wire useCardGridMetrics and card grid layout,
add i18n and settings search index; document performance hint in copy.
2026-05-15 02:19:20 +03:00

72 lines
2.7 KiB
TypeScript

import { clampStoredLoudnessPreAnalysisAttenuationRefDb } from '../utils/audio/loudnessPreAnalysisSlider';
import {
DEFAULT_LOUDNESS_PRE_ANALYSIS_ATTENUATION_DB,
DEFAULT_LIBRARY_GRID_MAX_COLUMNS,
LIBRARY_GRID_MAX_COLUMNS_MAX,
LIBRARY_GRID_MAX_COLUMNS_MIN,
LOUDNESS_LUFS_PRESETS,
MIX_MIN_RATING_FILTER_MAX_STARS,
RANDOM_MIX_SIZE_OPTIONS,
} from './authStoreDefaults';
import type { LoudnessLufsPreset } from './authStoreTypes';
export function generateId(): string {
return Date.now().toString(36) + Math.random().toString(36).slice(2);
}
export function sanitizeLoudnessLufsPreset(v: unknown, fallback: LoudnessLufsPreset): LoudnessLufsPreset {
return (LOUDNESS_LUFS_PRESETS as readonly number[]).includes(v as number)
? (v as LoudnessLufsPreset)
: fallback;
}
export function sanitizeLoudnessPreAnalysisFromStorage(v: unknown): number {
const n = typeof v === 'number' ? v : Number(v);
if (!Number.isFinite(n)) return DEFAULT_LOUDNESS_PRE_ANALYSIS_ATTENUATION_DB;
return clampStoredLoudnessPreAnalysisAttenuationRefDb(n);
}
export function clampMixFilterMinStars(v: unknown): number {
const n = typeof v === 'number' ? v : Number(v);
if (!Number.isFinite(n)) return 0;
return Math.max(0, Math.min(MIX_MIN_RATING_FILTER_MAX_STARS, Math.round(n)));
}
export function clampRandomMixSize(v: number): number {
if (!Number.isFinite(v)) return 50;
// Snap to the nearest allowed option so a tampered persisted value can't break the picker.
let nearest = RANDOM_MIX_SIZE_OPTIONS[0];
let bestDelta = Math.abs(v - nearest);
for (const opt of RANDOM_MIX_SIZE_OPTIONS) {
const d = Math.abs(v - opt);
if (d < bestDelta) { nearest = opt; bestDelta = d; }
}
return nearest;
}
/** Persisted max columns for library card grids (albums, artists, playlists, …). */
export function clampLibraryGridMaxColumns(v: unknown): number {
const n = typeof v === 'number' ? v : Number(v);
if (!Number.isFinite(n)) return DEFAULT_LIBRARY_GRID_MAX_COLUMNS;
return Math.max(LIBRARY_GRID_MAX_COLUMNS_MIN, Math.min(LIBRARY_GRID_MAX_COLUMNS_MAX, Math.round(n)));
}
export function clampSkipStarThreshold(v: number): number {
if (!Number.isFinite(v)) return 3;
return Math.max(1, Math.min(99, Math.round(v)));
}
export function skipStarCountStorageKey(serverId: string | null | undefined, trackId: string): string {
return `${serverId ?? ''}\u001f${trackId}`;
}
export function sanitizeSkipStarCounts(raw: unknown): Record<string, number> {
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return {};
const next: Record<string, number> = {};
for (const [k, v] of Object.entries(raw as Record<string, unknown>)) {
const n = Number(v);
if (Number.isFinite(n) && n > 0) next[k] = Math.min(Math.floor(n), 1_000_000);
}
return next;
}