Move auth and timezone hooks into dedicated hook files
This commit is contained in:
@@ -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.
|
**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:**
|
**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.
|
- 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.
|
- 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.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Navigate, useLocation } from "react-router-dom";
|
import { Navigate, useLocation } from "react-router-dom";
|
||||||
import { useAuth } from "../providers/AuthProvider";
|
import { useAuth } from "../hooks/useAuth";
|
||||||
|
|
||||||
interface RequireAuthProps {
|
interface RequireAuthProps {
|
||||||
/** The protected page content to render when authenticated. */
|
/** The protected page content to render when authenticated. */
|
||||||
|
|||||||
15
frontend/src/hooks/useAuth.ts
Normal file
15
frontend/src/hooks/useAuth.ts
Normal file
@@ -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 `<AuthProvider>`.
|
||||||
|
*/
|
||||||
|
export function useAuth(): AuthContextValue {
|
||||||
|
const ctx = useContext(AuthContext);
|
||||||
|
if (ctx === null) {
|
||||||
|
throw new Error("useAuth must be used within <AuthProvider>.");
|
||||||
|
}
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
15
frontend/src/hooks/useTimezone.ts
Normal file
15
frontend/src/hooks/useTimezone.ts
Normal file
@@ -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 `<TimezoneProvider>`.
|
||||||
|
*/
|
||||||
|
export function useTimezone(): string {
|
||||||
|
const context = useContext(TimezoneContext);
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error("useTimezone must be used within <TimezoneProvider>.");
|
||||||
|
}
|
||||||
|
return context.timezone;
|
||||||
|
}
|
||||||
@@ -30,7 +30,7 @@ import {
|
|||||||
NavigationRegular,
|
NavigationRegular,
|
||||||
} from "@fluentui/react-icons";
|
} from "@fluentui/react-icons";
|
||||||
import { NavLink, Outlet, useNavigate } from "react-router-dom";
|
import { NavLink, Outlet, useNavigate } from "react-router-dom";
|
||||||
import { useAuth } from "../providers/AuthProvider";
|
import { useAuth } from "../hooks/useAuth";
|
||||||
import { useServerStatus } from "../hooks/useServerStatus";
|
import { useServerStatus } from "../hooks/useServerStatus";
|
||||||
import { useBlocklistStatus } from "../hooks/useBlocklist";
|
import { useBlocklistStatus } from "../hooks/useBlocklist";
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import {
|
|||||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||||
import type { ChangeEvent, FormEvent } from "react";
|
import type { ChangeEvent, FormEvent } from "react";
|
||||||
import { ApiError } from "../api/client";
|
import { ApiError } from "../api/client";
|
||||||
import { useAuth } from "../providers/AuthProvider";
|
import { useAuth } from "../hooks/useAuth";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Styles
|
// Styles
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
import {
|
import {
|
||||||
createContext,
|
createContext,
|
||||||
useCallback,
|
useCallback,
|
||||||
useContext,
|
|
||||||
useMemo,
|
useMemo,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
@@ -25,7 +24,7 @@ interface AuthState {
|
|||||||
expiresAt: string | null;
|
expiresAt: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AuthContextValue {
|
export interface AuthContextValue {
|
||||||
/** `true` when a valid session token is held in state. */
|
/** `true` when a valid session token is held in state. */
|
||||||
isAuthenticated: boolean;
|
isAuthenticated: boolean;
|
||||||
/**
|
/**
|
||||||
@@ -41,7 +40,7 @@ interface AuthContextValue {
|
|||||||
// Context
|
// Context
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
const AuthContext = createContext<AuthContextValue | null>(null);
|
export const AuthContext = createContext<AuthContextValue | null>(null);
|
||||||
|
|
||||||
const SESSION_KEY = "bangui_token";
|
const SESSION_KEY = "bangui_token";
|
||||||
const SESSION_EXPIRES_KEY = "bangui_expires_at";
|
const SESSION_EXPIRES_KEY = "bangui_expires_at";
|
||||||
@@ -97,22 +96,3 @@ export function AuthProvider({
|
|||||||
|
|
||||||
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Hook
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Access authentication state and actions.
|
|
||||||
*
|
|
||||||
* Must be called inside a component rendered within `<AuthProvider>`.
|
|
||||||
*
|
|
||||||
* @throws {Error} When called outside of `<AuthProvider>`.
|
|
||||||
*/
|
|
||||||
export function useAuth(): AuthContextValue {
|
|
||||||
const ctx = useContext(AuthContext);
|
|
||||||
if (ctx === null) {
|
|
||||||
throw new Error("useAuth must be used within <AuthProvider>.");
|
|
||||||
}
|
|
||||||
return ctx;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -9,19 +9,19 @@
|
|||||||
* always receive a safe fallback.
|
* always receive a safe fallback.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createContext, useContext, useMemo } from "react";
|
import { createContext, useMemo } from "react";
|
||||||
import { useTimezoneData } from "../hooks/useTimezoneData";
|
import { useTimezoneData } from "../hooks/useTimezoneData";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Context definition
|
// Context definition
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
interface TimezoneContextValue {
|
export interface TimezoneContextValue {
|
||||||
/** IANA timezone string, e.g. ``"Europe/Berlin"`` or ``"UTC"``. */
|
/** IANA timezone string, e.g. ``"Europe/Berlin"`` or ``"UTC"``. */
|
||||||
timezone: string;
|
timezone: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TimezoneContext = createContext<TimezoneContextValue>({ timezone: "UTC" });
|
export const TimezoneContext = createContext<TimezoneContextValue | undefined>(undefined);
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Provider
|
// Provider
|
||||||
@@ -53,24 +53,3 @@ export function TimezoneProvider({
|
|||||||
<TimezoneContext.Provider value={value}>{children}</TimezoneContext.Provider>
|
<TimezoneContext.Provider value={value}>{children}</TimezoneContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user