Skip to content

Commit fc46cda

Browse files
committed
feat(project-context): add projectLoading state and enhance initial data loading logic
1 parent bc13608 commit fc46cda

File tree

3 files changed

+1290
-740
lines changed

3 files changed

+1290
-740
lines changed

frontend/src/components/chat/code-engine/code-engine.tsx

+184-35
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { TreeItem, TreeItemIndex } from 'react-complex-tree';
66
import { ProjectContext } from './project-context';
77
import CodeTab from './tabs/code-tab';
88
import PreviewTab from './tabs/preview-tab';
9-
import ConsoleTab from './tabs/preview-tab';
9+
import ConsoleTab from './tabs/console-tab';
1010
import ResponsiveToolbar from './responsive-toolbar';
1111
import SaveChangesBar from './save-changes-bar';
1212

@@ -19,14 +19,14 @@ export function CodeEngine({
1919
isProjectReady?: boolean;
2020
projectId?: string;
2121
}) {
22-
// Initialize state and context
23-
const { curProject, filePath } = useContext(ProjectContext);
24-
console.log('filepath', filePath);
25-
console.log('chatid', chatId);
26-
console.log('projectid', projectId);
22+
const { curProject, projectLoading, pollChatProject } =
23+
useContext(ProjectContext);
24+
const [localProject, setLocalProject] = useState(null);
25+
const [isLoading, setIsLoading] = useState(true);
26+
const [filePath, setFilePath] = useState<string | null>(null);
2727
const [saving, setSaving] = useState(false);
28-
const [preCode, setPrecode] = useState('// some comment');
29-
const [newCode, setCode] = useState('// some comment');
28+
const [preCode, setPrecode] = useState('// Loading...');
29+
const [newCode, setCode] = useState('// Loading...');
3030
const [activeTab, setActiveTab] = useState<'preview' | 'code' | 'console'>(
3131
'code'
3232
);
@@ -35,81 +35,193 @@ export function CodeEngine({
3535
Record<TreeItemIndex, TreeItem<any>>
3636
>({});
3737

38-
// Reference to the editor instance
3938
const editorRef = useRef(null);
39+
const projectPathRef = useRef(null);
40+
41+
// Poll for project if needed using chatId
42+
useEffect(() => {
43+
if (!curProject && chatId && !projectLoading) {
44+
const loadProjectFromChat = async () => {
45+
try {
46+
setIsLoading(true);
47+
const project = await pollChatProject(chatId);
48+
if (project) {
49+
setLocalProject(project);
50+
}
51+
} catch (error) {
52+
console.error('Failed to load project from chat:', error);
53+
} finally {
54+
setIsLoading(false);
55+
}
56+
};
57+
58+
loadProjectFromChat();
59+
} else {
60+
setIsLoading(projectLoading);
61+
}
62+
}, [chatId, curProject, projectLoading, pollChatProject]);
63+
64+
// Use either curProject from context or locally polled project
65+
const activeProject = curProject || localProject;
66+
67+
// Update projectPathRef when project changes
68+
useEffect(() => {
69+
if (activeProject?.projectPath) {
70+
projectPathRef.current = activeProject.projectPath;
71+
}
72+
}, [activeProject]);
4073

41-
// Fetch file structure function (only for code tab)
4274
async function fetchFiles() {
43-
if (!curProject?.projectPath) {
75+
const projectPath = activeProject?.projectPath || projectPathRef.current;
76+
if (!projectPath) {
4477
return;
4578
}
4679

4780
try {
4881
setIsFileStructureLoading(true);
49-
const response = await fetch(
50-
`/api/project?path=${curProject.projectPath}`
51-
);
82+
const response = await fetch(`/api/project?path=${projectPath}`);
83+
if (!response.ok) {
84+
throw new Error(`Failed to fetch file structure: ${response.status}`);
85+
}
5286
const data = await response.json();
53-
setFileStructureData(data.res || {});
87+
if (data && data.res) {
88+
setFileStructureData(data.res);
89+
} else {
90+
console.warn('Empty or invalid file structure data received');
91+
}
5492
} catch (error) {
5593
console.error('Error fetching file structure:', error);
5694
} finally {
5795
setIsFileStructureLoading(false);
5896
}
5997
}
6098

61-
// Effect: Fetch file structure when needed for code tab
99+
// Effect for loading file structure when project is ready
62100
useEffect(() => {
101+
const shouldFetchFiles =
102+
isProjectReady &&
103+
(activeProject?.projectPath || projectPathRef.current) &&
104+
Object.keys(fileStructureData).length === 0 &&
105+
!isFileStructureLoading;
106+
107+
if (shouldFetchFiles) {
108+
fetchFiles();
109+
}
110+
}, [
111+
isProjectReady,
112+
activeProject,
113+
isFileStructureLoading,
114+
fileStructureData,
115+
]);
116+
117+
// Effect for selecting default file once structure is loaded
118+
useEffect(() => {
119+
if (
120+
!isFileStructureLoading &&
121+
Object.keys(fileStructureData).length > 0 &&
122+
!filePath
123+
) {
124+
selectDefaultFile();
125+
}
126+
}, [isFileStructureLoading, fileStructureData, filePath]);
127+
128+
// Retry mechanism for fetching files if needed
129+
useEffect(() => {
130+
let retryTimeout;
131+
63132
if (
64-
activeTab === 'code' &&
133+
isProjectReady &&
134+
activeProject?.projectPath &&
65135
Object.keys(fileStructureData).length === 0 &&
66-
curProject?.projectPath
136+
!isFileStructureLoading
67137
) {
68-
fetchFiles();
138+
retryTimeout = setTimeout(() => {
139+
console.log('Retrying file structure fetch...');
140+
fetchFiles();
141+
}, 3000);
69142
}
70-
}, [activeTab, fileStructureData, curProject?.projectPath]);
71143

72-
// Reset code to previous state
144+
return () => {
145+
if (retryTimeout) clearTimeout(retryTimeout);
146+
};
147+
}, [
148+
isProjectReady,
149+
activeProject,
150+
fileStructureData,
151+
isFileStructureLoading,
152+
]);
153+
154+
function selectDefaultFile() {
155+
const defaultFiles = [
156+
'src/App.tsx',
157+
'src/App.js',
158+
'src/index.tsx',
159+
'src/index.js',
160+
'app/page.tsx',
161+
'pages/index.tsx',
162+
'index.html',
163+
'README.md',
164+
];
165+
166+
for (const defaultFile of defaultFiles) {
167+
if (fileStructureData[`root/${defaultFile}`]) {
168+
setFilePath(defaultFile);
169+
return;
170+
}
171+
}
172+
173+
const firstFile = Object.entries(fileStructureData).find(
174+
([key, item]) =>
175+
key.startsWith('root/') && !item.isFolder && key !== 'root/'
176+
);
177+
178+
if (firstFile) {
179+
setFilePath(firstFile[0].replace('root/', ''));
180+
}
181+
}
182+
73183
const handleReset = () => {
74184
setCode(preCode);
75185
editorRef.current?.setValue(preCode);
76186
setSaving(false);
77187
};
78188

79-
// Update file content on the server
80189
const updateCode = async (value) => {
81-
if (!curProject) return;
190+
const projectPath = activeProject?.projectPath || projectPathRef.current;
191+
if (!projectPath || !filePath) return;
82192

83193
try {
84194
const response = await fetch('/api/file', {
85195
method: 'POST',
86196
credentials: 'include',
87197
headers: { 'Content-Type': 'application/json' },
88198
body: JSON.stringify({
89-
filePath: `${curProject.projectPath}/${filePath}`,
199+
filePath: `${projectPath}/${filePath}`,
90200
newContent: JSON.stringify(value),
91201
}),
92202
});
203+
204+
if (!response.ok) {
205+
throw new Error(`Failed to update file: ${response.status}`);
206+
}
207+
93208
await response.json();
94209
} catch (error) {
95-
console.error(error);
210+
console.error('Error updating file:', error);
96211
}
97212
};
98213

99-
// Save the new code and update the previous state
100214
const handleSave = () => {
101215
setSaving(false);
102216
setPrecode(newCode);
103217
updateCode(newCode);
104218
};
105219

106-
// Track unsaved changes
107220
const updateSavingStatus = (value) => {
108221
setCode(value);
109222
setSaving(true);
110223
};
111224

112-
// Render appropriate content based on active tab
113225
const renderTabContent = () => {
114226
switch (activeTab) {
115227
case 'code':
@@ -120,6 +232,8 @@ export function CodeEngine({
120232
newCode={newCode}
121233
isFileStructureLoading={isFileStructureLoading}
122234
updateSavingStatus={updateSavingStatus}
235+
filePath={filePath}
236+
setFilePath={setFilePath}
123237
/>
124238
);
125239
case 'preview':
@@ -131,20 +245,54 @@ export function CodeEngine({
131245
}
132246
};
133247

134-
// Render the CodeEngine layout
248+
useEffect(() => {
249+
async function getCode() {
250+
const projectPath = activeProject?.projectPath || projectPathRef.current;
251+
if (!projectPath || !filePath) return;
252+
253+
const file_node = fileStructureData[`root/${filePath}`];
254+
if (!file_node) return;
255+
256+
const isFolder = file_node.isFolder;
257+
if (isFolder) return;
258+
259+
try {
260+
const res = await fetch(
261+
`/api/file?path=${encodeURIComponent(`${projectPath}/${filePath}`)}`
262+
);
263+
264+
if (!res.ok) {
265+
throw new Error(`Failed to fetch file content: ${res.status}`);
266+
}
267+
268+
const data = await res.json();
269+
setCode(data.content);
270+
setPrecode(data.content);
271+
} catch (error) {
272+
console.error('Error loading file content:', error);
273+
}
274+
}
275+
276+
getCode();
277+
}, [filePath, activeProject, fileStructureData]);
278+
279+
// Determine if we're truly ready to render
280+
const showLoader =
281+
!isProjectReady ||
282+
isLoading ||
283+
(!activeProject?.projectPath && !projectPathRef.current && !localProject);
284+
135285
return (
136286
<div className="rounded-lg border shadow-sm overflow-scroll h-full">
137-
{/* Header Bar */}
138287
<ResponsiveToolbar
139-
isLoading={!isProjectReady}
288+
isLoading={showLoader}
140289
activeTab={activeTab}
141290
setActiveTab={setActiveTab}
142291
/>
143292

144-
{/* Main Content Area with Loading */}
145293
<div className="relative h-[calc(100vh-48px-4rem)]">
146294
<AnimatePresence>
147-
{!isProjectReady && (
295+
{showLoader && (
148296
<motion.div
149297
key="loader"
150298
initial={{ opacity: 0 }}
@@ -154,15 +302,16 @@ export function CodeEngine({
154302
>
155303
<Loader className="w-8 h-8 text-primary animate-spin" />
156304
<p className="text-sm text-muted-foreground">
157-
Initializing project...
305+
{projectLoading
306+
? 'Loading project...'
307+
: 'Initializing project...'}
158308
</p>
159309
</motion.div>
160310
)}
161311
</AnimatePresence>
162312

163313
<div className="flex h-full">{renderTabContent()}</div>
164314

165-
{/* Save Changes Bar */}
166315
{saving && <SaveChangesBar onSave={handleSave} onReset={handleReset} />}
167316
</div>
168317
</div>

0 commit comments

Comments
 (0)