feat(characters): add catalog boundary and capabilities
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { CHARACTERS } from "@/data/constants/character";
|
||||
import {
|
||||
CharacterCatalogProvider,
|
||||
useCharacterCatalog,
|
||||
} from "@/providers/character-catalog-provider";
|
||||
|
||||
describe("CharacterCatalogProvider", () => {
|
||||
it("provides the complete local character catalog", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<CharacterCatalogProvider>
|
||||
<CatalogProbe />
|
||||
</CharacterCatalogProvider>,
|
||||
);
|
||||
|
||||
expect(html).toContain("elio,maya,nayeli");
|
||||
});
|
||||
|
||||
it("accepts a serializable catalog snapshot", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<CharacterCatalogProvider characters={[CHARACTERS[1]]}>
|
||||
<CatalogProbe />
|
||||
</CharacterCatalogProvider>,
|
||||
);
|
||||
|
||||
expect(html).toContain("maya");
|
||||
expect(html).not.toContain("elio");
|
||||
});
|
||||
});
|
||||
|
||||
function CatalogProbe() {
|
||||
const catalog = useCharacterCatalog();
|
||||
return <span>{catalog.characters.map((character) => character.slug).join(",")}</span>;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
createContext,
|
||||
type ReactNode,
|
||||
useContext,
|
||||
useMemo,
|
||||
} from "react";
|
||||
|
||||
import {
|
||||
CHARACTERS,
|
||||
createCharacterCatalog,
|
||||
type CharacterCatalog,
|
||||
type CharacterProfile,
|
||||
} from "@/data/constants/character";
|
||||
|
||||
const CharacterCatalogContext = createContext<CharacterCatalog | null>(null);
|
||||
|
||||
export interface CharacterCatalogProviderProps {
|
||||
children: ReactNode;
|
||||
characters?: readonly CharacterProfile[];
|
||||
}
|
||||
|
||||
export function CharacterCatalogProvider({
|
||||
children,
|
||||
characters = CHARACTERS,
|
||||
}: CharacterCatalogProviderProps) {
|
||||
const catalog = useMemo(
|
||||
() => createCharacterCatalog(characters),
|
||||
[characters],
|
||||
);
|
||||
return (
|
||||
<CharacterCatalogContext.Provider value={catalog}>
|
||||
{children}
|
||||
</CharacterCatalogContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useCharacterCatalog(): CharacterCatalog {
|
||||
const catalog = useContext(CharacterCatalogContext);
|
||||
if (!catalog) {
|
||||
throw new Error(
|
||||
"useCharacterCatalog must be used within CharacterCatalogProvider.",
|
||||
);
|
||||
}
|
||||
return catalog;
|
||||
}
|
||||
@@ -3,12 +3,13 @@
|
||||
/**
|
||||
* 根级 Client Providers 包装
|
||||
*
|
||||
* 这里只保留跨路由的 Auth、User、消息预览和 viewport Provider。Chat、
|
||||
* Payment、PrivateRoom 状态机由对应路由 layout 按需挂载。
|
||||
* 这里只保留跨路由的 Character Catalog、Auth、User、消息预览和 viewport
|
||||
* Provider。Chat、Payment、PrivateRoom 状态机由对应路由 layout 按需挂载。
|
||||
*/
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
import { CharacterCatalogProvider } from "@/providers/character-catalog-provider";
|
||||
import { SplashLatestMessageProvider } from "@/providers/splash-latest-message-provider";
|
||||
import { ViewportCssVarsProvider } from "@/providers/viewport-css-vars-provider";
|
||||
import { AppNavigationGuard } from "@/router/app-navigation-guard";
|
||||
@@ -24,19 +25,21 @@ export interface RootProvidersProps {
|
||||
|
||||
export function RootProviders({ children }: RootProvidersProps) {
|
||||
return (
|
||||
<ViewportCssVarsProvider>
|
||||
<AuthProvider>
|
||||
<AuthStatusChecker />
|
||||
<OAuthSessionSync />
|
||||
<AppNavigationGuard />
|
||||
<SplashLatestMessageProvider>
|
||||
<UserProvider>
|
||||
<UserAuthSync />
|
||||
{children}
|
||||
<div id="toast-portal" />
|
||||
</UserProvider>
|
||||
</SplashLatestMessageProvider>
|
||||
</AuthProvider>
|
||||
</ViewportCssVarsProvider>
|
||||
<CharacterCatalogProvider>
|
||||
<ViewportCssVarsProvider>
|
||||
<AuthProvider>
|
||||
<AuthStatusChecker />
|
||||
<OAuthSessionSync />
|
||||
<AppNavigationGuard />
|
||||
<SplashLatestMessageProvider>
|
||||
<UserProvider>
|
||||
<UserAuthSync />
|
||||
{children}
|
||||
<div id="toast-portal" />
|
||||
</UserProvider>
|
||||
</SplashLatestMessageProvider>
|
||||
</AuthProvider>
|
||||
</ViewportCssVarsProvider>
|
||||
</CharacterCatalogProvider>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user