Skip to content

Commit 2c24c44

Browse files
authored
fix: resolve chat input disabled issue by handling backend ready even… (#888)
2 parents 9c024a4 + ad831c6 commit 2c24c44

1 file changed

Lines changed: 138 additions & 56 deletions

File tree

src/hooks/useInstallationSetup.ts

Lines changed: 138 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { useEffect, useRef, useCallback } from 'react';
2-
import { useInstallationStore } from '@/store/installationStore';
3-
import { useAuthStore } from '@/store/authStore';
1+
import { useEffect, useRef, useCallback } from "react";
2+
import { useInstallationStore } from "@/store/installationStore";
3+
import { useAuthStore } from "@/store/authStore";
44

55
/**
66
* Hook that sets up Electron IPC listeners and handles installation state synchronization
@@ -12,78 +12,110 @@ export const useInstallationSetup = () => {
1212
const hasCheckedOnMount = useRef(false);
1313
const installationCompleted = useRef(false);
1414
const backendReady = useRef(false);
15-
const startInstallation = useInstallationStore(state => state.startInstallation);
16-
const performInstallation = useInstallationStore(state => state.performInstallation);
17-
const addLog = useInstallationStore(state => state.addLog);
18-
const setSuccess = useInstallationStore(state => state.setSuccess);
19-
const setError = useInstallationStore(state => state.setError);
20-
const setBackendError = useInstallationStore(state => state.setBackendError);
21-
const setWaitingBackend = useInstallationStore(state => state.setWaitingBackend);
22-
const needsBackendRestart = useInstallationStore(state => state.needsBackendRestart);
23-
const setNeedsBackendRestart = useInstallationStore(state => state.setNeedsBackendRestart);
15+
const startInstallation = useInstallationStore(
16+
(state) => state.startInstallation
17+
);
18+
const performInstallation = useInstallationStore(
19+
(state) => state.performInstallation
20+
);
21+
const addLog = useInstallationStore((state) => state.addLog);
22+
const setSuccess = useInstallationStore((state) => state.setSuccess);
23+
const setError = useInstallationStore((state) => state.setError);
24+
const setBackendError = useInstallationStore(
25+
(state) => state.setBackendError
26+
);
27+
const setWaitingBackend = useInstallationStore(
28+
(state) => state.setWaitingBackend
29+
);
30+
const needsBackendRestart = useInstallationStore(
31+
(state) => state.needsBackendRestart
32+
);
33+
const setNeedsBackendRestart = useInstallationStore(
34+
(state) => state.setNeedsBackendRestart
35+
);
2436

2537
// Shared function to poll backend status
2638
const startBackendPolling = useCallback(() => {
27-
console.log('[useInstallationSetup] Starting backend polling');
39+
console.log("[useInstallationSetup] Starting backend polling");
2840

2941
// Immediately check backend status once
3042
const checkBackendStatus = async () => {
3143
try {
3244
const backendPort = await window.electronAPI.getBackendPort();
3345
if (backendPort && backendPort > 0) {
34-
console.log('[useInstallationSetup] Backend immediately detected on port:', backendPort);
46+
console.log(
47+
"[useInstallationSetup] Backend immediately detected on port:",
48+
backendPort
49+
);
3550

3651
// Verify backend is actually responding
37-
const response = await fetch(`http://localhost:${backendPort}/health`).catch(() => null);
52+
const response = await fetch(
53+
`http://localhost:${backendPort}/health`
54+
).catch(() => null);
3855
if (response && response.ok) {
39-
console.log('[useInstallationSetup] Backend health check passed immediately');
56+
console.log(
57+
"[useInstallationSetup] Backend health check passed immediately"
58+
);
4059
backendReady.current = true;
4160
setSuccess();
42-
setInitState('done');
61+
setInitState("done");
4362
setNeedsBackendRestart(false);
4463
return true; // Backend is ready, no need to poll
4564
}
4665
}
4766
} catch (error) {
48-
console.log('[useInstallationSetup] Initial backend check failed:', error);
67+
console.log(
68+
"[useInstallationSetup] Initial backend check failed:",
69+
error
70+
);
4971
}
5072
return false; // Backend not ready, need to poll
5173
};
5274

5375
// Check immediately, then start polling if needed
5476
checkBackendStatus().then((isReady) => {
5577
if (isReady) {
56-
console.log('[useInstallationSetup] Backend already ready, skipping polling');
78+
console.log(
79+
"[useInstallationSetup] Backend already ready, skipping polling"
80+
);
5781
return;
5882
}
5983

60-
console.log('[useInstallationSetup] Backend not ready, starting polling');
84+
console.log("[useInstallationSetup] Backend not ready, starting polling");
6185

6286
// Poll backend status every 2 seconds to ensure we catch when it's ready
6387
// This is a fallback in case the backend-ready event is missed
6488
const pollInterval = setInterval(async () => {
6589
try {
6690
const backendPort = await window.electronAPI.getBackendPort();
6791
if (backendPort && backendPort > 0) {
68-
console.log('[useInstallationSetup] Backend poll detected ready on port:', backendPort);
92+
console.log(
93+
"[useInstallationSetup] Backend poll detected ready on port:",
94+
backendPort
95+
);
6996

7097
// Verify backend is actually responding
71-
const response = await fetch(`http://localhost:${backendPort}/health`).catch(() => null);
98+
const response = await fetch(
99+
`http://localhost:${backendPort}/health`
100+
).catch(() => null);
72101
if (response && response.ok) {
73-
console.log('[useInstallationSetup] Backend health check passed');
102+
console.log("[useInstallationSetup] Backend health check passed");
74103
clearInterval(pollInterval);
75104

76105
if (!backendReady.current) {
77106
backendReady.current = true;
78107
setSuccess();
79-
setInitState('done');
108+
setInitState("done");
80109
// Clear the flag after backend is ready
81110
setNeedsBackendRestart(false);
82111
}
83112
}
84113
}
85114
} catch (error) {
86-
console.log('[useInstallationSetup] Backend poll check failed:', error);
115+
console.log(
116+
"[useInstallationSetup] Backend poll check failed:",
117+
error
118+
);
87119
}
88120
}, 2000);
89121

@@ -98,7 +130,9 @@ export const useInstallationSetup = () => {
98130
useEffect(() => {
99131
// When user logs in after logout, needsBackendRestart will be true
100132
if (needsBackendRestart && email !== null) {
101-
console.log('[useInstallationSetup] Detected login after logout, waiting for backend restart');
133+
console.log(
134+
"[useInstallationSetup] Detected login after logout, waiting for backend restart"
135+
);
102136

103137
// For account switching, tools are already installed, only backend needs restart
104138
// So we mark installation as completed and only wait for backend
@@ -113,7 +147,6 @@ export const useInstallationSetup = () => {
113147
}
114148
}, [needsBackendRestart, email, setWaitingBackend, startBackendPolling]);
115149

116-
117150
useEffect(() => {
118151
if (hasCheckedOnMount.current) {
119152
return;
@@ -127,56 +160,74 @@ export const useInstallationSetup = () => {
127160

128161
if (result.success) {
129162
if (result.isInstalled) {
130-
console.log('[useInstallationSetup] Tools already installed, waiting for backend');
163+
console.log(
164+
"[useInstallationSetup] Tools already installed, waiting for backend"
165+
);
131166
installationCompleted.current = true;
132167
setWaitingBackend();
133168

134169
// Start polling for backend when tools are already installed
135170
startBackendPolling();
136171
}
137172

138-
if (initState !== 'done') {
173+
if (initState !== "done") {
139174
if (!result.isInstalled && initState === "permissions") {
140-
console.log('[useInstallationSetup] Tools not installed and initState is permissions, setting to carousel');
175+
console.log(
176+
"[useInstallationSetup] Tools not installed and initState is permissions, setting to carousel"
177+
);
141178
setInitState("carousel");
142179
}
143180
}
144181
}
145182
return result;
146183
} catch (error) {
147-
console.error("[useInstallationSetup] Tool installation check failed:", error);
184+
console.error(
185+
"[useInstallationSetup] Tool installation check failed:",
186+
error
187+
);
148188
return { success: false, error };
149189
}
150190
};
151191

152-
const checkBackendStatus = async(toolResult?: any) => {
192+
const checkBackendStatus = async (toolResult?: any) => {
153193
try {
154-
const installationStatus = await window.electronAPI.getInstallationStatus();
194+
const installationStatus =
195+
await window.electronAPI.getInstallationStatus();
155196

156197
if (installationStatus.success && installationStatus.isInstalling) {
157198
startInstallation();
158199
}
159200
} catch (err) {
160-
console.error('[useInstallationSetup] Failed to check installation status:', err);
201+
console.error(
202+
"[useInstallationSetup] Failed to check installation status:",
203+
err
204+
);
161205
}
162-
}
206+
};
163207

164208
const runInitialChecks = async () => {
165209
const toolResult = await checkToolInstalled();
166210
await checkBackendStatus(toolResult);
167211
};
168212

169213
runInitialChecks();
170-
// eslint-disable-next-line react-hooks/exhaustive-deps
214+
// eslint-disable-next-line react-hooks/exhaustive-deps
171215
}, []);
172216

173217
useEffect(() => {
174218
const checkAndSetDone = () => {
175-
console.log('[useInstallationSetup] Checking readiness - Installation:', installationCompleted.current, 'Backend:', backendReady.current);
219+
console.log(
220+
"[useInstallationSetup] Checking readiness - Installation:",
221+
installationCompleted.current,
222+
"Backend:",
223+
backendReady.current
224+
);
176225

177226
if (installationCompleted.current && backendReady.current) {
178-
console.log('[useInstallationSetup] Both installation and backend are ready, setting initState to done');
179-
setInitState('done');
227+
console.log(
228+
"[useInstallationSetup] Both installation and backend are ready, setting initState to done"
229+
);
230+
setInitState("done");
180231
}
181232
};
182233

@@ -188,39 +239,63 @@ export const useInstallationSetup = () => {
188239

189240
const handleInstallLog = (data: { type: string; data: string }) => {
190241
addLog({
191-
type: data.type as 'stdout' | 'stderr',
242+
type: data.type as "stdout" | "stderr",
192243
data: data.data,
193244
timestamp: new Date(),
194245
});
195246
};
196247

197-
const handleInstallComplete = (data: { success: boolean; code?: number; error?: string }) => {
198-
console.log('[useInstallationSetup] Installation complete event received:', data);
248+
const handleInstallComplete = (data: {
249+
success: boolean;
250+
code?: number;
251+
error?: string;
252+
}) => {
253+
console.log(
254+
"[useInstallationSetup] Installation complete event received:",
255+
data
256+
);
199257

200258
if (data.success) {
201259
installationCompleted.current = true;
202-
console.log('[useInstallationSetup] Installation marked as completed');
260+
console.log("[useInstallationSetup] Installation marked as completed");
203261

204262
// setSuccess() will be called in handleBackendReady to prevent premature state change
205263
checkAndSetDone();
206264
} else {
207-
setError(data.error || 'Installation failed');
265+
setError(data.error || "Installation failed");
208266
}
209267
};
210268

211-
const handleBackendReady = (data: { success: boolean; port?: number; error?: string }) => {
212-
console.log('[useInstallationSetup] Backend ready event received:', data);
269+
const handleBackendReady = (data: {
270+
success: boolean;
271+
port?: number;
272+
error?: string;
273+
}) => {
274+
console.log("[useInstallationSetup] Backend ready event received:", data);
213275

214276
if (data.success && data.port) {
215-
console.log(`[useInstallationSetup] Backend is ready on port ${data.port}`);
277+
console.log(
278+
`[useInstallationSetup] Backend is ready on port ${data.port}`
279+
);
216280
backendReady.current = true;
217-
console.log('[useInstallationSetup] Backend marked as ready');
281+
// If backend is ready, installation must be complete (or satisfied enough)
282+
// This handles race condition where install-complete event is missed or skipped
283+
if (!installationCompleted.current) {
284+
console.log(
285+
"[useInstallationSetup] Backend ready implies installation complete - setting flag"
286+
);
287+
installationCompleted.current = true;
288+
}
289+
console.log("[useInstallationSetup] Backend marked as ready");
218290

219291
setSuccess();
220292
checkAndSetDone();
221293
} else {
222-
console.error('[useInstallationSetup] Backend failed to start:', data.error);
223-
setBackendError(data.error || 'Backend startup failed');
294+
console.error(
295+
"[useInstallationSetup] Backend failed to start:",
296+
data.error
297+
);
298+
setBackendError(data.error || "Backend startup failed");
224299
}
225300
};
226301

@@ -230,10 +305,17 @@ export const useInstallationSetup = () => {
230305
window.electronAPI.onBackendReady(handleBackendReady);
231306

232307
return () => {
233-
window.electronAPI.removeAllListeners('install-dependencies-start');
234-
window.electronAPI.removeAllListeners('install-dependencies-log');
235-
window.electronAPI.removeAllListeners('install-dependencies-complete');
236-
window.electronAPI.removeAllListeners('backend-ready');
308+
window.electronAPI.removeAllListeners("install-dependencies-start");
309+
window.electronAPI.removeAllListeners("install-dependencies-log");
310+
window.electronAPI.removeAllListeners("install-dependencies-complete");
311+
window.electronAPI.removeAllListeners("backend-ready");
237312
};
238-
}, [startInstallation, addLog, setSuccess, setError, setBackendError, setInitState]);
239-
};
313+
}, [
314+
startInstallation,
315+
addLog,
316+
setSuccess,
317+
setError,
318+
setBackendError,
319+
setInitState,
320+
]);
321+
};

0 commit comments

Comments
 (0)