返回 JoyAI-Echo
WorkplaceProvider.tsx
根目录 / echo_longvideo / Director_Agent / webui / src / providers / WorkplaceProvider.tsx
1 import {
2 createContext,
3 useCallback,
4 useContext,
5 useMemo,
6 useState,
7 type ReactNode,
8 } from "react";
9
10 import {
11 useWorkplaceEditor,
12 type UseWorkplaceEditorResult,
13 } from "@/hooks/workplace-editor/useWorkplaceEditor";
14 import { useWorkplace, type UseWorkplaceResult } from "@/hooks/useWorkplace";
15
16 /**
17 * Per-session workplace subscription. A single Provider call avoids duplicate
18 * HTTP polling and WebSocket handlers when both chat and panel need workplace.
19 *
20 * `composerSendBlocked` reflects right-side textarea focus (not dirty state):
21 * while the user edits in WorkplacePanel, the left chat send button is disabled.
22 */
23 type WorkplaceContextValue = UseWorkplaceResult & {
24 editor: UseWorkplaceEditorResult;
25 composerSendBlocked: boolean;
26 setAuxTextEditing: (active: boolean) => void;
27 };
28
29 const WorkplaceContext = createContext<WorkplaceContextValue | null>(null);
30
31 export function WorkplaceProvider({
32 sessionKey,
33 children,
34 }: {
35 sessionKey: string | null;
36 children: ReactNode;
37 }) {
38 const workplaceResult = useWorkplace(sessionKey);
39 const editor = useWorkplaceEditor(workplaceResult.workplace, {
40 saveStory: workplaceResult.saveStory,
41 saveStoryProfile: workplaceResult.saveStoryProfile,
42 confirmStory: workplaceResult.confirmStory,
43 startGeneration: workplaceResult.startGeneration,
44 startMerge: workplaceResult.startMerge,
45 splitShot: (shotId, payload) => workplaceResult.splitShot(shotId, payload),
46 mergeShotUp: workplaceResult.mergeShotUp,
47 deleteShot: workplaceResult.deleteShot,
48 });
49
50 const [auxEditingCount, setAuxEditingCount] = useState(0);
51 const setAuxTextEditing = useCallback((active: boolean) => {
52 setAuxEditingCount((count) => Math.max(0, active ? count + 1 : count - 1));
53 }, []);
54
55 const composerSendBlocked =
56 editor.isFieldFocused ||
57 auxEditingCount > 0 ||
58 editor.storyDirty ||
59 editor.beatsDirty;
60
61 const value = useMemo(
62 () => ({
63 ...workplaceResult,
64 editor,
65 composerSendBlocked,
66 setAuxTextEditing,
67 }),
68 [workplaceResult, editor, composerSendBlocked, setAuxTextEditing],
69 );
70
71 return (
72 <WorkplaceContext.Provider value={value}>
73 {children}
74 </WorkplaceContext.Provider>
75 );
76 }
77
78 export function useWorkplaceContext(): WorkplaceContextValue {
79 const ctx = useContext(WorkplaceContext);
80 if (!ctx) {
81 throw new Error(
82 "useWorkplaceContext must be used within a WorkplaceProvider",
83 );
84 }
85 return ctx;
86 }
87
87 lines Plain Text