fix(composer): guard websocket and recorder race on chat switch
Some checks failed
CI / test (push) Failing after 1m32s

This commit is contained in:
2026-03-08 20:42:19 +03:00
parent 20f31cd15e
commit f0582bf4ab
2 changed files with 20 additions and 6 deletions

View File

@@ -264,8 +264,7 @@ export function MessageComposer() {
useEffect(() => {
const prev = prevActiveChatIdRef.current;
if (prev && prev !== activeChatId) {
const ws = getWs();
ws?.send(JSON.stringify({ event: "typing_stop", payload: { chat_id: prev } }));
sendWsEvent("typing_stop", { chat_id: prev });
typingActiveRef.current = false;
if (typingStopTimerRef.current !== null) {
window.clearTimeout(typingStopTimerRef.current);
@@ -327,6 +326,18 @@ export function MessageComposer() {
return wsRef.current;
}
function sendWsEvent(eventName: string, payload: Record<string, unknown>) {
const ws = getWs();
if (!ws || ws.readyState !== WebSocket.OPEN) {
return;
}
try {
ws.send(JSON.stringify({ event: eventName, payload }));
} catch {
return;
}
}
function sendRealtimeChatEvent(
eventName:
| "typing_start"
@@ -339,8 +350,7 @@ export function MessageComposer() {
if (!activeChatId) {
return;
}
const ws = getWs();
ws?.send(JSON.stringify({ event: eventName, payload: { chat_id: activeChatId } }));
sendWsEvent(eventName, { chat_id: activeChatId });
}
function emitTypingStopIfActive() {
@@ -711,7 +721,11 @@ export function MessageComposer() {
pointerCancelArmedRef.current = false;
setDragHint("idle");
if (recorderRef.current && recorderRef.current.state !== "inactive") {
recorderRef.current.stop();
try {
recorderRef.current.stop();
} catch {
// ignore recorder lifecycle race during quick chat switches/unmount
}
}
recorderRef.current = null;
setRecordingState("idle");