Consolidate frontend storage keys into constants module

- Move magic strings from AuthProvider, MainLayout, and ThemeProvider to
  frontend/src/utils/constants.ts
- Add STORAGE_KEY_AUTHENTICATED, STORAGE_KEY_SIDEBAR_COLLAPSED, and
  STORAGE_KEY_THEME constants with JSDoc descriptions
- Update all three files to import and use centralized keys
- Prevents key drift and typo regressions across the frontend

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-04-28 09:48:28 +02:00
parent 72c4a0ed04
commit 252204ed97
5 changed files with 29 additions and 31 deletions

View File

@@ -36,6 +36,7 @@ import { useAuth } from "../hooks/useAuth";
import { useServerStatus } from "../hooks/useServerStatus";
import { useBlocklistStatus } from "../hooks/useBlocklistStatus";
import { useThemeMode } from "../providers/ThemeProvider";
import { STORAGE_KEY_SIDEBAR_COLLAPSED } from "../utils/constants";
// ---------------------------------------------------------------------------
// Styles
@@ -43,7 +44,6 @@ import { useThemeMode } from "../providers/ThemeProvider";
const SIDEBAR_FULL = "240px";
const SIDEBAR_COLLAPSED = "48px";
const SIDEBAR_COLLAPSED_STORAGE_KEY = "bangui_sidebar_collapsed";
const useStyles = makeStyles({
root: {
@@ -237,7 +237,7 @@ export function MainLayout(): React.JSX.Element {
const readSavedCollapsed = (): boolean => {
try {
const savedValue = localStorage.getItem(SIDEBAR_COLLAPSED_STORAGE_KEY);
const savedValue = localStorage.getItem(STORAGE_KEY_SIDEBAR_COLLAPSED);
if (savedValue === "true") {
return true;
}
@@ -259,14 +259,14 @@ export function MainLayout(): React.JSX.Element {
useEffect(() => {
try {
localStorage.setItem(SIDEBAR_COLLAPSED_STORAGE_KEY, String(collapsed));
localStorage.setItem(STORAGE_KEY_SIDEBAR_COLLAPSED, String(collapsed));
} catch {
// Local storage may be unavailable in some environments.
}
}, [collapsed]);
useEffect(() => {
const savedValue = localStorage.getItem(SIDEBAR_COLLAPSED_STORAGE_KEY);
const savedValue = localStorage.getItem(STORAGE_KEY_SIDEBAR_COLLAPSED);
if (savedValue !== null) {
return undefined;
}

View File

@@ -44,6 +44,7 @@ import { useNavigate } from "react-router-dom";
import * as authApi from "../api/auth";
import { setUnauthorizedHandler } from "../api/client";
import { setAuthErrorHandler } from "../utils/fetchError";
import { STORAGE_KEY_AUTHENTICATED } from "../utils/constants";
import { SessionValidationLoading } from "../components/SessionValidationLoading";
import { useSessionValidation } from "../hooks/useSessionValidation";
@@ -65,8 +66,6 @@ export interface AuthContextValue {
export const AuthContext = createContext<AuthContextValue | null>(null);
const IS_AUTHENTICATED_KEY = "bangui_authenticated";
// ---------------------------------------------------------------------------
// Provider
// ---------------------------------------------------------------------------
@@ -87,14 +86,14 @@ export function AuthProvider({
children: React.ReactNode;
}): React.JSX.Element {
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(() => {
const stored = sessionStorage.getItem(IS_AUTHENTICATED_KEY);
const stored = sessionStorage.getItem(STORAGE_KEY_AUTHENTICATED);
return stored === "true";
});
const [isValidating, setIsValidating] = useState<boolean>(isAuthenticated);
const navigate = useNavigate();
const handleSessionExpired = useCallback((): void => {
sessionStorage.removeItem(IS_AUTHENTICATED_KEY);
sessionStorage.removeItem(STORAGE_KEY_AUTHENTICATED);
setIsAuthenticated(false);
navigate("/login", { replace: true });
}, [navigate]);
@@ -136,7 +135,7 @@ export function AuthProvider({
const login = useCallback(async (password: string): Promise<void> => {
await authApi.login(password);
sessionStorage.setItem(IS_AUTHENTICATED_KEY, "true");
sessionStorage.setItem(STORAGE_KEY_AUTHENTICATED, "true");
setIsAuthenticated(true);
}, []);
@@ -145,7 +144,7 @@ export function AuthProvider({
await authApi.logout();
} finally {
// Always clear local state even if the API call fails (e.g. expired session).
sessionStorage.removeItem(IS_AUTHENTICATED_KEY);
sessionStorage.removeItem(STORAGE_KEY_AUTHENTICATED);
setIsAuthenticated(false);
}
}, []);

View File

@@ -1,5 +1,6 @@
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
import type { ReactNode } from "react";
import { STORAGE_KEY_THEME } from "../utils/constants";
type ThemeMode = "light" | "dark";
@@ -10,12 +11,11 @@ interface ThemeProviderContext {
hasExplicitPreference: boolean;
}
const THEME_STORAGE_KEY = "bangui_theme";
const ThemeModeContext = createContext<ThemeProviderContext | null>(null);
function readSavedTheme(): ThemeMode | null {
try {
const value = localStorage.getItem(THEME_STORAGE_KEY);
const value = localStorage.getItem(STORAGE_KEY_THEME);
if (value === "dark" || value === "light") {
return value;
}
@@ -40,7 +40,7 @@ export function ThemeProvider({ children }: ThemeProviderProps): React.JSX.Eleme
const setColorMode = useCallback((mode: ThemeMode): void => {
try {
localStorage.setItem(THEME_STORAGE_KEY, mode);
localStorage.setItem(STORAGE_KEY_THEME, mode);
} catch {
// Ignore storage failures.
}

View File

@@ -3,6 +3,10 @@
*/
import type { BanOriginFilter, TimeRange } from "../types/ban";
// ---------------------------------------------------------------------------
// UI Constants
// ---------------------------------------------------------------------------
export const BAN_ORIGIN_FILTER_LABELS: Record<BanOriginFilter, string> = {
all: "All",
blocklist: "Blocklist",
@@ -21,3 +25,16 @@ export const BAN_PAGE_SIZE = 100 as const;
/** Items per page for the history table. */
export const HISTORY_PAGE_SIZE = 50 as const;
// ---------------------------------------------------------------------------
// Storage Keys
// ---------------------------------------------------------------------------
/** SessionStorage key for authentication state persistence. */
export const STORAGE_KEY_AUTHENTICATED = "bangui_authenticated" as const;
/** LocalStorage key for sidebar collapsed state. */
export const STORAGE_KEY_SIDEBAR_COLLAPSED = "bangui_sidebar_collapsed" as const;
/** LocalStorage key for theme preference. */
export const STORAGE_KEY_THEME = "bangui_theme" as const;