Skip to content

Commit 3568c39

Browse files
authored
Merge pull request #736 from RohitR311/client-notifs
feat: enable client-side notifications for all runs
2 parents ca29967 + fa0d153 commit 3568c39

File tree

3 files changed

+116
-45
lines changed

3 files changed

+116
-45
lines changed

server/src/server.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,10 +160,27 @@ app.use((req, res, next) => {
160160
next();
161161
});
162162

163+
io.of('/queued-run').on('connection', (socket) => {
164+
const userId = socket.handshake.query.userId as string;
165+
166+
if (userId) {
167+
socket.join(`user-${userId}`);
168+
logger.log('info', `Client joined queued-run namespace for user: ${userId}, socket: ${socket.id}`);
169+
170+
socket.on('disconnect', () => {
171+
logger.log('info', `Client disconnected from queued-run namespace: ${socket.id}`);
172+
});
173+
} else {
174+
logger.log('warn', `Client connected to queued-run namespace without userId: ${socket.id}`);
175+
socket.disconnect();
176+
}
177+
});
178+
163179
setInterval(() => {
164180
processQueuedRuns();
165181
}, 5000);
166182

183+
167184
server.listen(SERVER_PORT, '0.0.0.0', async () => {
168185
try {
169186
await connectDB();

src/context/socket.tsx

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
1-
import React, { createContext, useCallback, useContext, useMemo, useState } from 'react';
1+
import React, { createContext, useCallback, useContext, useState, useRef, useEffect } from 'react';
22
import { io, Socket } from 'socket.io-client';
33
import { apiUrl } from "../apiConfig";
44

55
const SERVER_ENDPOINT = apiUrl;
66

77
interface SocketState {
88
socket: Socket | null;
9+
queueSocket: Socket | null;
910
id: string;
1011
setId: (id: string) => void;
12+
connectToQueueSocket: (userId: string, onRunCompleted?: (data: any) => void) => void;
13+
disconnectQueueSocket: () => void;
1114
};
1215

1316
class SocketStore implements Partial<SocketState> {
14-
socket = null;
17+
socket: Socket | null = null;
18+
queueSocket: Socket | null = null;
1519
id = '';
1620
};
1721

@@ -22,7 +26,9 @@ export const useSocketStore = () => useContext(socketStoreContext);
2226

2327
export const SocketProvider = ({ children }: { children: JSX.Element }) => {
2428
const [socket, setSocket] = useState<Socket | null>(socketStore.socket);
29+
const [queueSocket, setQueueSocket] = useState<Socket | null>(socketStore.queueSocket);
2530
const [id, setActiveId] = useState<string>(socketStore.id);
31+
const runCompletedCallbackRef = useRef<((data: any) => void) | null>(null);
2632

2733
const setId = useCallback((id: string) => {
2834
// the socket client connection is recomputed whenever id changes -> the new browser has been initialized
@@ -39,12 +45,70 @@ export const SocketProvider = ({ children }: { children: JSX.Element }) => {
3945
setActiveId(id);
4046
}, [setSocket]);
4147

48+
const connectToQueueSocket = useCallback((userId: string, onRunCompleted?: (data: any) => void) => {
49+
runCompletedCallbackRef.current = onRunCompleted || null;
50+
51+
const newQueueSocket = io(`${SERVER_ENDPOINT}/queued-run`, {
52+
transports: ["websocket"],
53+
rejectUnauthorized: false,
54+
query: { userId }
55+
});
56+
57+
newQueueSocket.on('connect', () => {
58+
console.log('Queue socket connected for user:', userId);
59+
});
60+
61+
newQueueSocket.on('connect_error', (error) => {
62+
console.log('Queue socket connection error:', error);
63+
});
64+
65+
newQueueSocket.on('run-completed', (completionData) => {
66+
console.log('Run completed event received:', completionData);
67+
if (runCompletedCallbackRef.current) {
68+
runCompletedCallbackRef.current(completionData);
69+
}
70+
});
71+
72+
setQueueSocket(currentSocket => {
73+
if (currentSocket) {
74+
currentSocket.disconnect();
75+
}
76+
return newQueueSocket;
77+
});
78+
79+
socketStore.queueSocket = newQueueSocket;
80+
}, []);
81+
82+
const disconnectQueueSocket = useCallback(() => {
83+
setQueueSocket(currentSocket => {
84+
if (currentSocket) {
85+
currentSocket.disconnect();
86+
}
87+
return null;
88+
});
89+
90+
socketStore.queueSocket = null;
91+
runCompletedCallbackRef.current = null;
92+
}, []);
93+
94+
// Cleanup on unmount
95+
useEffect(() => {
96+
return () => {
97+
if (queueSocket) {
98+
queueSocket.disconnect();
99+
}
100+
};
101+
}, [queueSocket]);
102+
42103
return (
43104
<socketStoreContext.Provider
44105
value={{
45106
socket,
107+
queueSocket,
46108
id,
47109
setId,
110+
connectToQueueSocket,
111+
disconnectQueueSocket,
48112
}}
49113
>
50114
{children}

src/pages/MainPage.tsx

Lines changed: 33 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { ScheduleSettings } from "../components/robot/ScheduleSettings";
1515
import { apiUrl } from "../apiConfig";
1616
import { useNavigate } from 'react-router-dom';
1717
import { AuthContext } from '../context/auth';
18+
import { useSocketStore } from '../context/socket';
1819

1920
interface MainPageProps {
2021
handleEditRecording: (id: string, fileName: string) => void;
@@ -54,6 +55,8 @@ export const MainPage = ({ handleEditRecording, initialContent }: MainPageProps)
5455
const { state } = useContext(AuthContext);
5556
const { user } = state;
5657

58+
const { connectToQueueSocket, disconnectQueueSocket } = useSocketStore();
59+
5760
const abortRunHandler = (runId: string, robotName: string, browserId: string) => {
5861
notify('info', t('main_page.notifications.abort_initiated', { name: robotName }));
5962

@@ -138,50 +141,7 @@ export const MainPage = ({ handleEditRecording, initialContent }: MainPageProps)
138141
navigate(`/runs/${robotMetaId}/run/${runId}`);
139142

140143
if (queued) {
141-
console.log('Creating queue socket for queued run:', runId);
142-
143144
setQueuedRuns(prev => new Set([...prev, runId]));
144-
145-
const queueSocket = io(`${apiUrl}/queued-run`, {
146-
transports: ["websocket"],
147-
rejectUnauthorized: false,
148-
query: { userId: user?.id }
149-
});
150-
151-
queueSocket.on('connect', () => {
152-
console.log('Queue socket connected for user:', user?.id);
153-
});
154-
155-
queueSocket.on('connect_error', (error) => {
156-
console.log('Queue socket connection error:', error);
157-
});
158-
159-
queueSocket.on('run-completed', (completionData) => {
160-
if (completionData.runId === runId) {
161-
setRunningRecordingName('');
162-
setCurrentInterpretationLog('');
163-
setRerenderRuns(true);
164-
165-
setQueuedRuns(prev => {
166-
const newSet = new Set(prev);
167-
newSet.delete(runId);
168-
return newSet;
169-
});
170-
171-
const robotName = completionData.robotName || runningRecordingName;
172-
173-
if (completionData.status === 'success') {
174-
notify('success', t('main_page.notifications.interpretation_success', { name: robotName }));
175-
} else {
176-
notify('error', t('main_page.notifications.interpretation_failed', { name: robotName }));
177-
}
178-
179-
queueSocket.disconnect();
180-
}
181-
});
182-
183-
setSockets(sockets => [...sockets, queueSocket]);
184-
185145
notify('info', `Run queued: ${runningRecordingName}`);
186146
} else {
187147
const socket = io(`${apiUrl}/${browserId}`, {
@@ -245,6 +205,36 @@ export const MainPage = ({ handleEditRecording, initialContent }: MainPageProps)
245205
return message === 'success';
246206
}
247207

208+
useEffect(() => {
209+
if (user?.id) {
210+
const handleRunCompleted = (completionData: any) => {
211+
setRerenderRuns(true);
212+
213+
if (queuedRuns.has(completionData.runId)) {
214+
setQueuedRuns(prev => {
215+
const newSet = new Set(prev);
216+
newSet.delete(completionData.runId);
217+
return newSet;
218+
});
219+
}
220+
221+
const robotName = completionData.robotName || 'Unknown Robot';
222+
223+
if (completionData.status === 'success') {
224+
notify('success', t('main_page.notifications.interpretation_success', { name: robotName }));
225+
} else {
226+
notify('error', t('main_page.notifications.interpretation_failed', { name: robotName }));
227+
}
228+
};
229+
230+
connectToQueueSocket(user.id, handleRunCompleted);
231+
232+
return () => {
233+
disconnectQueueSocket();
234+
};
235+
}
236+
}, [user?.id, connectToQueueSocket, disconnectQueueSocket, t, setRerenderRuns, queuedRuns, setQueuedRuns]);
237+
248238
const DisplayContent = () => {
249239
switch (content) {
250240
case 'robots':

0 commit comments

Comments
 (0)