Refactor App.tsx from god component to context-based architecture
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
23
apps/web/src/contexts/bestiary-context.tsx
Normal file
23
apps/web/src/contexts/bestiary-context.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { createContext, type ReactNode, useContext } from "react";
|
||||
import { useBestiary } from "../hooks/use-bestiary.js";
|
||||
|
||||
export type { SearchResult } from "../hooks/use-bestiary.js";
|
||||
|
||||
type BestiaryContextValue = ReturnType<typeof useBestiary>;
|
||||
|
||||
const BestiaryContext = createContext<BestiaryContextValue | null>(null);
|
||||
|
||||
export function BestiaryProvider({ children }: { children: ReactNode }) {
|
||||
const value = useBestiary();
|
||||
return (
|
||||
<BestiaryContext.Provider value={value}>
|
||||
{children}
|
||||
</BestiaryContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useBestiaryContext(): BestiaryContextValue {
|
||||
const ctx = useContext(BestiaryContext);
|
||||
if (!ctx) throw new Error("useBestiaryContext requires BestiaryProvider");
|
||||
return ctx;
|
||||
}
|
||||
21
apps/web/src/contexts/bulk-import-context.tsx
Normal file
21
apps/web/src/contexts/bulk-import-context.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { createContext, type ReactNode, useContext } from "react";
|
||||
import { useBulkImport } from "../hooks/use-bulk-import.js";
|
||||
|
||||
type BulkImportContextValue = ReturnType<typeof useBulkImport>;
|
||||
|
||||
const BulkImportContext = createContext<BulkImportContextValue | null>(null);
|
||||
|
||||
export function BulkImportProvider({ children }: { children: ReactNode }) {
|
||||
const value = useBulkImport();
|
||||
return (
|
||||
<BulkImportContext.Provider value={value}>
|
||||
{children}
|
||||
</BulkImportContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useBulkImportContext(): BulkImportContextValue {
|
||||
const ctx = useContext(BulkImportContext);
|
||||
if (!ctx) throw new Error("useBulkImportContext requires BulkImportProvider");
|
||||
return ctx;
|
||||
}
|
||||
21
apps/web/src/contexts/encounter-context.tsx
Normal file
21
apps/web/src/contexts/encounter-context.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { createContext, type ReactNode, useContext } from "react";
|
||||
import { useEncounter } from "../hooks/use-encounter.js";
|
||||
|
||||
type EncounterContextValue = ReturnType<typeof useEncounter>;
|
||||
|
||||
const EncounterContext = createContext<EncounterContextValue | null>(null);
|
||||
|
||||
export function EncounterProvider({ children }: { children: ReactNode }) {
|
||||
const value = useEncounter();
|
||||
return (
|
||||
<EncounterContext.Provider value={value}>
|
||||
{children}
|
||||
</EncounterContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useEncounterContext(): EncounterContextValue {
|
||||
const ctx = useContext(EncounterContext);
|
||||
if (!ctx) throw new Error("useEncounterContext requires EncounterProvider");
|
||||
return ctx;
|
||||
}
|
||||
7
apps/web/src/contexts/index.ts
Normal file
7
apps/web/src/contexts/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export { BestiaryProvider } from "./bestiary-context.js";
|
||||
export { BulkImportProvider } from "./bulk-import-context.js";
|
||||
export { EncounterProvider } from "./encounter-context.js";
|
||||
export { InitiativeRollsProvider } from "./initiative-rolls-context.js";
|
||||
export { PlayerCharactersProvider } from "./player-characters-context.js";
|
||||
export { SidePanelProvider } from "./side-panel-context.js";
|
||||
export { ThemeProvider } from "./theme-context.js";
|
||||
25
apps/web/src/contexts/initiative-rolls-context.tsx
Normal file
25
apps/web/src/contexts/initiative-rolls-context.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { createContext, type ReactNode, useContext } from "react";
|
||||
import { useInitiativeRolls } from "../hooks/use-initiative-rolls.js";
|
||||
|
||||
type InitiativeRollsContextValue = ReturnType<typeof useInitiativeRolls>;
|
||||
|
||||
const InitiativeRollsContext =
|
||||
createContext<InitiativeRollsContextValue | null>(null);
|
||||
|
||||
export function InitiativeRollsProvider({ children }: { children: ReactNode }) {
|
||||
const value = useInitiativeRolls();
|
||||
return (
|
||||
<InitiativeRollsContext.Provider value={value}>
|
||||
{children}
|
||||
</InitiativeRollsContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useInitiativeRollsContext(): InitiativeRollsContextValue {
|
||||
const ctx = useContext(InitiativeRollsContext);
|
||||
if (!ctx)
|
||||
throw new Error(
|
||||
"useInitiativeRollsContext requires InitiativeRollsProvider",
|
||||
);
|
||||
return ctx;
|
||||
}
|
||||
29
apps/web/src/contexts/player-characters-context.tsx
Normal file
29
apps/web/src/contexts/player-characters-context.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { createContext, type ReactNode, useContext } from "react";
|
||||
import { usePlayerCharacters } from "../hooks/use-player-characters.js";
|
||||
|
||||
type PlayerCharactersContextValue = ReturnType<typeof usePlayerCharacters>;
|
||||
|
||||
const PlayerCharactersContext =
|
||||
createContext<PlayerCharactersContextValue | null>(null);
|
||||
|
||||
export function PlayerCharactersProvider({
|
||||
children,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
}) {
|
||||
const value = usePlayerCharacters();
|
||||
return (
|
||||
<PlayerCharactersContext.Provider value={value}>
|
||||
{children}
|
||||
</PlayerCharactersContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function usePlayerCharactersContext(): PlayerCharactersContextValue {
|
||||
const ctx = useContext(PlayerCharactersContext);
|
||||
if (!ctx)
|
||||
throw new Error(
|
||||
"usePlayerCharactersContext requires PlayerCharactersProvider",
|
||||
);
|
||||
return ctx;
|
||||
}
|
||||
21
apps/web/src/contexts/side-panel-context.tsx
Normal file
21
apps/web/src/contexts/side-panel-context.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { createContext, type ReactNode, useContext } from "react";
|
||||
import { useSidePanelState } from "../hooks/use-side-panel-state.js";
|
||||
|
||||
type SidePanelContextValue = ReturnType<typeof useSidePanelState>;
|
||||
|
||||
const SidePanelContext = createContext<SidePanelContextValue | null>(null);
|
||||
|
||||
export function SidePanelProvider({ children }: { children: ReactNode }) {
|
||||
const value = useSidePanelState();
|
||||
return (
|
||||
<SidePanelContext.Provider value={value}>
|
||||
{children}
|
||||
</SidePanelContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useSidePanelContext(): SidePanelContextValue {
|
||||
const ctx = useContext(SidePanelContext);
|
||||
if (!ctx) throw new Error("useSidePanelContext requires SidePanelProvider");
|
||||
return ctx;
|
||||
}
|
||||
19
apps/web/src/contexts/theme-context.tsx
Normal file
19
apps/web/src/contexts/theme-context.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { createContext, type ReactNode, useContext } from "react";
|
||||
import { useTheme } from "../hooks/use-theme.js";
|
||||
|
||||
type ThemeContextValue = ReturnType<typeof useTheme>;
|
||||
|
||||
const ThemeContext = createContext<ThemeContextValue | null>(null);
|
||||
|
||||
export function ThemeProvider({ children }: { children: ReactNode }) {
|
||||
const value = useTheme();
|
||||
return (
|
||||
<ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useThemeContext(): ThemeContextValue {
|
||||
const ctx = useContext(ThemeContext);
|
||||
if (!ctx) throw new Error("useThemeContext requires ThemeProvider");
|
||||
return ctx;
|
||||
}
|
||||
Reference in New Issue
Block a user