Skip to content

Commit 3647203

Browse files
committed
fix: websocket
1 parent df6c23a commit 3647203

File tree

2 files changed

+138
-112
lines changed

2 files changed

+138
-112
lines changed

src/components/fm.tsx

Lines changed: 90 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,15 @@ const arraysEqual = (a: Uint8Array, b: Uint8Array) => {
6363
const FMComponent: React.FC<FMProps & JSX.IntrinsicElements["div"]> = ({ wsUrl, ...props }) => {
6464
const { t } = useTranslation();
6565
const fmRef = useRef<HTMLDivElement>(null);
66+
const wsRef = useRef<WebSocket | null>(null);
67+
68+
useEffect(() => {
69+
return () => {
70+
if (wsRef.current) {
71+
wsRef.current.close();
72+
}
73+
};
74+
}, []);
6675

6776
const [dOpen, setdOpen] = useState(false);
6877
const [uOpen, setuOpen] = useState(false);
@@ -146,99 +155,104 @@ const FMComponent: React.FC<FMProps & JSX.IntrinsicElements["div"]> = ({ wsUrl,
146155

147156
worker.onmessage = async (event: MessageEvent<FMWorkerData>) => {
148157
switch (event.data.type) {
149-
case FMWorkerOpcode.Error: {
150-
console.error('Error from worker', event.data.error);
151-
break;
152-
}
153-
case FMWorkerOpcode.Progress: {
154-
handleReady.current = true;
155-
break;
156-
}
157-
case FMWorkerOpcode.Result: {
158-
handleReady.current = false;
159-
160-
if (event.data.blob && event.data.fileName) {
161-
const url = URL.createObjectURL(event.data.blob);
162-
const anchor = document.createElement('a');
163-
anchor.href = url;
164-
anchor.download = event.data.fileName;
165-
anchor.click();
166-
URL.revokeObjectURL(url);
158+
case FMWorkerOpcode.Error: {
159+
console.error('Error from worker', event.data.error);
160+
break;
161+
}
162+
case FMWorkerOpcode.Progress: {
163+
handleReady.current = true;
164+
break;
167165
}
166+
case FMWorkerOpcode.Result: {
167+
handleReady.current = false;
168+
169+
if (event.data.blob && event.data.fileName) {
170+
const url = URL.createObjectURL(event.data.blob);
171+
const anchor = document.createElement('a');
172+
anchor.href = url;
173+
anchor.download = event.data.fileName;
174+
anchor.click();
175+
URL.revokeObjectURL(url);
176+
}
168177

169-
firstChunk.current = true;
170-
if (dOpen) setdOpen(false);
171-
break;
172-
}
178+
firstChunk.current = true;
179+
if (dOpen) setdOpen(false);
180+
break;
181+
}
173182
}
174183
}
175184

176185
const [currentPath, setPath] = useState('');
177186
useEffect(() => {
178-
listFile();
179-
}, [currentPath])
187+
if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
188+
listFile();
189+
}
190+
}, [wsRef.current, currentPath])
180191

181-
const ws = new WebSocket(wsUrl);
182-
ws.binaryType = 'arraybuffer';
183-
ws.onopen = () => {
184-
listFile();
185-
}
186-
ws.onclose = (e) => {
187-
console.log('WebSocket connection closed:', e);
188-
}
189-
ws.onerror = (e) => {
190-
console.error(e);
191-
toast("Websocket" + " " + t("Error"), {
192-
description: t("Results.UnExpectedError"),
193-
})
194-
}
195-
ws.onmessage = async (e) => {
196-
try {
197-
const buf: ArrayBufferLike = e.data;
198-
199-
if (firstChunk.current) {
200-
const identifier = new Uint8Array(buf, 0, 4);
201-
if (arraysEqual(identifier, FMIdentifier.file)) {
202-
worker.postMessage({ operation: 1, arrayBuffer: buf, fileName: currentBasename.current });
203-
firstChunk.current = false;
204-
} else if (arraysEqual(identifier, FMIdentifier.fileName)) {
205-
const { path, fmList } = await fm.parseFMList(buf);
206-
setPath(path);
207-
setFMEntries(fmList);
208-
} else if (arraysEqual(identifier, FMIdentifier.error)) {
209-
const errBytes = buf.slice(4);
210-
const errMsg = new TextDecoder('utf-8').decode(errBytes);
211-
throw new Error(errMsg);
212-
} else if (arraysEqual(identifier, FMIdentifier.complete)) {
213-
// Upload completed
214-
if (uOpen) setuOpen(false);
215-
listFile();
192+
useEffect(() => {
193+
const ws = new WebSocket(wsUrl);
194+
wsRef.current = ws;
195+
ws.binaryType = 'arraybuffer';
196+
ws.onopen = () => {
197+
listFile();
198+
}
199+
ws.onclose = (e) => {
200+
console.log('WebSocket connection closed:', e);
201+
}
202+
ws.onerror = (e) => {
203+
console.error(e);
204+
toast("Websocket" + " " + t("Error"), {
205+
description: t("Results.UnExpectedError"),
206+
})
207+
}
208+
ws.onmessage = async (e) => {
209+
try {
210+
const buf: ArrayBufferLike = e.data;
211+
212+
if (firstChunk.current) {
213+
const identifier = new Uint8Array(buf, 0, 4);
214+
if (arraysEqual(identifier, FMIdentifier.file)) {
215+
worker.postMessage({ operation: 1, arrayBuffer: buf, fileName: currentBasename.current });
216+
firstChunk.current = false;
217+
} else if (arraysEqual(identifier, FMIdentifier.fileName)) {
218+
const { path, fmList } = await fm.parseFMList(buf);
219+
setPath(path);
220+
setFMEntries(fmList);
221+
} else if (arraysEqual(identifier, FMIdentifier.error)) {
222+
const errBytes = buf.slice(4);
223+
const errMsg = new TextDecoder('utf-8').decode(errBytes);
224+
throw new Error(errMsg);
225+
} else if (arraysEqual(identifier, FMIdentifier.complete)) {
226+
// Upload completed
227+
if (uOpen) setuOpen(false);
228+
listFile();
229+
} else {
230+
throw new Error(t("Results.UnknownIdentifier"));
231+
}
216232
} else {
217-
throw new Error(t("Results.UnknownIdentifier"));
233+
await waitForHandleReady();
234+
worker.postMessage({ operation: 2, arrayBuffer: buf, fileName: currentBasename.current });
218235
}
219-
} else {
220-
await waitForHandleReady();
221-
worker.postMessage({ operation: 2, arrayBuffer: buf, fileName: currentBasename.current });
236+
} catch (error) {
237+
console.error('Error processing received data:', error);
238+
toast("FM" + " " + t("Error"), {
239+
description: t("Results.UnExpectedError"),
240+
})
241+
if (dOpen) setdOpen(false);
242+
if (uOpen) setuOpen(false);
222243
}
223-
} catch (error) {
224-
console.error('Error processing received data:', error);
225-
toast("FM" + " " + t("Error"), {
226-
description: t("Results.UnExpectedError"),
227-
})
228-
if (dOpen) setdOpen(false);
229-
if (uOpen) setuOpen(false);
230244
}
231-
}
245+
}, [wsUrl])
232246

233-
const listFile = () => {
247+
let listFile = () => {
234248
const prefix = new Int8Array([FMOpcode.List]);
235249
const pathMsg = new TextEncoder().encode(currentPath);
236250

237251
const msg = new Int8Array(prefix.length + pathMsg.length);
238252
msg.set(prefix);
239253
msg.set(pathMsg, prefix.length);
240254

241-
ws.send(msg);
255+
wsRef.current?.send(msg);
242256
}
243257

244258
const downloadFile = (basename: string) => {
@@ -250,7 +264,7 @@ const FMComponent: React.FC<FMProps & JSX.IntrinsicElements["div"]> = ({ wsUrl,
250264
msg.set(prefix);
251265
msg.set(filePathMessage, prefix.length);
252266

253-
ws.send(msg);
267+
wsRef.current?.send(msg);
254268
}
255269

256270
const uploadFile = async (file: File) => {
@@ -259,13 +273,13 @@ const FMComponent: React.FC<FMProps & JSX.IntrinsicElements["div"]> = ({ wsUrl,
259273

260274
// Send header
261275
const header = fm.buildUploadHeader({ path: currentPath, file: file });
262-
ws.send(header);
276+
wsRef.current?.send(header);
263277

264278
// Send data chunks
265279
while (offset < file.size) {
266280
const chunk = file.slice(offset, offset + chunkSize);
267281
const arrayBuffer = await fm.readFileAsArrayBuffer(chunk);
268-
if (arrayBuffer) ws.send(arrayBuffer);
282+
if (arrayBuffer) wsRef.current?.send(arrayBuffer);
269283
offset += chunkSize;
270284
}
271285
}

src/components/terminal.tsx

Lines changed: 48 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,34 @@ interface XtermProps {
2828

2929
const XtermComponent: React.FC<XtermProps & JSX.IntrinsicElements["div"]> = ({ wsUrl, setClose, ...props }) => {
3030
const terminalRef = useRef<HTMLDivElement>(null);
31+
const wsRef = useRef<WebSocket | null>(null);
3132

32-
const ws = new WebSocket(wsUrl);
33-
ws.binaryType = "arraybuffer";
34-
ws.onopen = () => {
35-
onResize();
36-
}
37-
ws.onclose = () => {
38-
terminal.dispose();
39-
setClose(true);
40-
}
41-
ws.onerror = (e) => {
42-
console.error(e);
43-
toast("Websocket error", {
44-
description: "View console for details.",
45-
})
46-
}
33+
useEffect(() => {
34+
return () => {
35+
if (wsRef.current) {
36+
wsRef.current.close();
37+
}
38+
};
39+
}, []);
40+
41+
useEffect(() => {
42+
const ws = new WebSocket(wsUrl);
43+
wsRef.current = ws;
44+
ws.binaryType = "arraybuffer";
45+
ws.onopen = () => {
46+
onResize();
47+
}
48+
ws.onclose = () => {
49+
terminal.dispose();
50+
setClose(true);
51+
}
52+
ws.onerror = (e) => {
53+
console.error(e);
54+
toast("Websocket error", {
55+
description: "View console for details.",
56+
})
57+
}
58+
}, [wsUrl]);
4759

4860
const terminal = useRef(
4961
new Terminal({
@@ -73,7 +85,7 @@ const XtermComponent: React.FC<XtermProps & JSX.IntrinsicElements["div"]> = ({ w
7385
msg.set(prefix);
7486
msg.set(resizeMessage, prefix.length);
7587

76-
ws.send(msg);
88+
wsRef.current?.send(msg);
7789
}
7890
};
7991

@@ -92,20 +104,19 @@ const XtermComponent: React.FC<XtermProps & JSX.IntrinsicElements["div"]> = ({ w
92104
};
93105

94106
useEffect(() => {
95-
if (!ws || !terminalRef.current) return;
96-
97-
const attachAddon = new AttachAddon(ws);
107+
if (!wsRef.current || !terminalRef.current) return;
108+
const attachAddon = new AttachAddon(wsRef.current);
98109
terminal.loadAddon(attachAddon);
99110
terminal.loadAddon(fitAddon);
100111
terminal.open(terminalRef.current);
101112
window.addEventListener('resize', onResize);
102113
return () => {
103114
window.removeEventListener('resize', onResize);
104-
if (ws) {
105-
ws.close();
115+
if (wsRef.current) {
116+
wsRef.current.close();
106117
}
107118
};
108-
}, [ws, terminal]);
119+
}, [wsRef.current, terminal]);
109120

110121
return <div ref={terminalRef} {...props} />;
111122
};
@@ -116,21 +127,22 @@ export const TerminalPage = () => {
116127

117128
const { id } = useParams<{ id: string }>();
118129

119-
useEffect(() => {
120-
const fetchTerminal = async () => {
121-
if (id && !terminal) {
122-
try {
123-
const createdTerminal = await createTerminal(Number(id));
124-
setTerminal(createdTerminal);
125-
} catch (e) {
126-
toast("Terminal API Error", {
127-
description: "View console for details.",
128-
})
129-
console.error("fetch error", e);
130-
return;
131-
}
130+
const fetchTerminal = async () => {
131+
if (id && !terminal) {
132+
try {
133+
const createdTerminal = await createTerminal(Number(id));
134+
setTerminal(createdTerminal);
135+
} catch (e) {
136+
toast("Terminal API Error", {
137+
description: "View console for details.",
138+
})
139+
console.error("fetch error", e);
140+
return;
132141
}
133-
};
142+
}
143+
}
144+
145+
useEffect(() => {
134146
fetchTerminal();
135147
}, [id]);
136148

0 commit comments

Comments
 (0)