haixunMaster/lib/capabilities/context.tsx

76 lines
1.9 KiB
TypeScript

"use client";
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useState,
type ReactNode,
} from "react";
import type {
CapabilityFeature,
FeatureCapability,
WorkspaceCapabilities,
} from "@/lib/capabilities/types";
import { parseFetchJson } from "@/lib/utils";
interface CapabilitiesContextValue {
loading: boolean;
capabilities: WorkspaceCapabilities | null;
refresh: () => Promise<void>;
isReady: (feature: CapabilityFeature) => boolean;
get: (feature: CapabilityFeature) => FeatureCapability | null;
}
const CapabilitiesContext = createContext<CapabilitiesContextValue | null>(null);
export function CapabilitiesProvider({ children }: { children: ReactNode }) {
const [loading, setLoading] = useState(true);
const [capabilities, setCapabilities] = useState<WorkspaceCapabilities | null>(null);
const refresh = useCallback(async () => {
setLoading(true);
try {
const res = await fetch("/api/capabilities");
const data = await parseFetchJson<{ capabilities?: WorkspaceCapabilities; error?: string }>(
res
);
if (res.ok && data.capabilities) {
setCapabilities(data.capabilities);
} else {
setCapabilities(null);
}
} catch {
setCapabilities(null);
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
refresh();
}, [refresh]);
const value = useMemo<CapabilitiesContextValue>(
() => ({
loading,
capabilities,
refresh,
isReady: (feature) => capabilities?.[feature]?.ready ?? false,
get: (feature) => capabilities?.[feature] ?? null,
}),
[loading, capabilities, refresh]
);
return <CapabilitiesContext.Provider value={value}>{children}</CapabilitiesContext.Provider>;
}
export function useCapabilities() {
const ctx = useContext(CapabilitiesContext);
if (!ctx) {
throw new Error("useCapabilities must be used within CapabilitiesProvider");
}
return ctx;
}