Skip to content

Commit eb2e243

Browse files
authored
Merge pull request #779 from devsoc-unsw/refactor/websocket-handler-init-timing
Refactor/websocket handler init timing
2 parents c840547 + f480177 commit eb2e243

File tree

4 files changed

+126
-61
lines changed

4 files changed

+126
-61
lines changed

client/src/Services/socketClient.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ class SocketClient {
1515

1616
private setupDefaultEvents() {
1717
this.socket.on('connect', () => {
18-
console.log('Socket handler Built / Connected!');
18+
console.log('Socket Connected!');
1919
});
20-
this.socket.on('disconnect', () => {
21-
console.log('Socket handler Removed / Disconnected!');
20+
21+
this.socket.off('disconnect', () => {
22+
console.log('Socket Disconnected!');
2223
});
2324

2425
// TODO: This section leaves for debugging purpose
@@ -37,6 +38,7 @@ class SocketClient {
3738
this.socket = io(URL, { path: '/dapi' });
3839
this.setupDefaultEvents();
3940
this.socket.connect();
41+
console.log('created a new socket client!');
4042
}
4143

4244
setupEventHandlers(handlers: ServerToClientEvent) {
@@ -82,11 +84,27 @@ class SocketClient {
8284
}
8385

8486
interface SocketStore {
85-
socketClient: SocketClient;
87+
socketClient: SocketClient | null;
88+
initialise: () => void;
89+
disconnect: () => void;
8690
}
8791

88-
const useSocketClientStore = create<SocketStore>(() => ({
89-
socketClient: new SocketClient(),
92+
const useSocketClientStore = create<SocketStore>((set, get) => ({
93+
socketClient: null,
94+
initialise: () => {
95+
if (!get().socketClient) {
96+
console.log('intialising socket client');
97+
set({ socketClient: new SocketClient() });
98+
}
99+
},
100+
disconnect: () => {
101+
const client = get().socketClient;
102+
if (client) {
103+
client.socket.close();
104+
console.log('closing socket client ');
105+
set({ socketClient: null });
106+
}
107+
},
90108
}));
91109

92110
export default useSocketClientStore;

client/src/Services/useSocketCommunication.ts

Lines changed: 81 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -21,107 +21,136 @@ export const useSocketCommunication = () => {
2121
resetConsoleChunks,
2222
appendConsoleChunks,
2323
} = useGlobalStore();
24+
2425
const { setActive, clearFrontendState } = useFrontendStateStore();
2526
const { socketClient } = useSocketClientStore();
26-
const { setToastMessage: setMessage } = useToastStateStore();
27+
const { setToastMessage } = useToastStateStore();
2728
const queue = useQueue();
2829

30+
// Setup socket event handlers on mount
2931
useEffect(() => {
30-
const eventHandler = buildSocketEventHandler({
32+
console.log('usesocketcom is mounted');
33+
34+
if (!socketClient) {
35+
return;
36+
}
37+
38+
const handlers = buildSocketEventHandler({
3139
setActive,
3240
updateNextFrame,
3341
updateTypeDeclaration,
3442
appendConsoleChunks,
3543
updateCurrFocusedTab,
36-
setMessage,
44+
setMessage: setToastMessage,
3745
});
3846

39-
socketClient.setupEventHandlers(eventHandler);
40-
}, []);
47+
socketClient.setupEventHandlers(handlers);
48+
}, [socketClient]);
4149

50+
// reset entire debuggin session
4251
const resetDebugSession = useCallback(() => {
4352
updateNextFrame(INITIAL_BACKEND_STATE);
4453
clearFrontendState();
4554
setActive(false);
4655
clearTypeDeclarations();
4756
clearUserAnnotation();
4857
resetConsoleChunks();
49-
}, []);
58+
}, [
59+
updateNextFrame,
60+
clearFrontendState,
61+
setActive,
62+
clearTypeDeclarations,
63+
clearUserAnnotation,
64+
resetConsoleChunks,
65+
]);
5066

67+
// error handler for sending code
68+
const handleSendCodeError = () => {
69+
setToastMessage({
70+
content: 'No file being selected',
71+
colorTheme: 'warning',
72+
durationMs: DEFAULT_MESSAGE_DURATION,
73+
});
74+
};
75+
76+
// send code to backend to start debugging session
5177
const sendCode = useCallback(() => {
78+
if (!socketClient) return;
79+
5280
resetDebugSession();
81+
5382
const { fileSystem, currFocusFilePath } = useUserFsStateStore.getState();
5483
const file = fileSystem.getFileFromPath(currFocusFilePath);
5584

5685
if (!file || file.path === 'root') {
57-
setMessage({
58-
content: 'No file being selected.',
59-
colorTheme: 'warning',
60-
durationMs: DEFAULT_MESSAGE_DURATION,
61-
});
86+
handleSendCodeError();
6287
return;
6388
}
89+
6490
socketClient.serverAction.initializeDebugSession(file.data);
65-
}, [socketClient]);
91+
}, [socketClient, resetDebugSession]);
6692

67-
const executeNextWithRetry = useCallback(() => {
68-
const addEventListenerWithTimeout = (
69-
listener: (state: BackendState | null) => void,
70-
timeout: number
71-
) => {
72-
let resolved = false;
73-
74-
const wrappedListener = (state: BackendState) => {
75-
if (!resolved) {
76-
resolved = true;
77-
listener(state);
78-
socketClient.socket.off('sendBackendStateToUser', wrappedListener);
79-
}
80-
};
81-
82-
socketClient.socket.on('sendBackendStateToUser', wrappedListener);
83-
84-
setTimeout(() => {
85-
if (!resolved) {
86-
resolved = true;
87-
listener(null);
88-
socketClient.socket.off('sendBackendStateToUser', wrappedListener);
89-
}
90-
}, timeout);
93+
// add event listener with timeout
94+
const addEventListenerWithTimeout = (
95+
listener: (state: BackendState | null) => void,
96+
timeout: number
97+
) => {
98+
if (!socketClient) return;
99+
100+
let resolved = false;
101+
102+
const wrappedListener = (state: BackendState) => {
103+
if (!resolved) {
104+
resolved = true;
105+
listener(state);
106+
socketClient.socket.off('sendBackendStateToUser', wrappedListener);
107+
}
91108
};
92109

110+
socketClient.socket.on('sendBackendStateToUser', wrappedListener);
111+
112+
setTimeout(() => {
113+
if (!resolved) {
114+
resolved = true;
115+
listener(null);
116+
socketClient.socket.off('sendBackendStateToUser', wrappedListener);
117+
}
118+
}, timeout);
119+
};
120+
121+
// step debugger and wait for backend to respond
122+
const executeNextWithRetry = useCallback(() => {
123+
if (!socketClient) return Promise.resolve(false);
124+
93125
return queue(() => {
94126
return new Promise<boolean>((resolve) => {
95127
const handleBackendState = (state: BackendState | null) => {
96-
if (state) {
97-
resolve(true); // Resolve as success
98-
} else {
99-
resolve(false); // Resolve as failure due to timeout
100-
}
128+
resolve(!!state);
101129
};
102130

103-
// Add the event listener with a timeout
104131
addEventListenerWithTimeout(handleBackendState, 5000);
105132
socketClient.serverAction.executeNext();
106133
});
107134
});
108-
}, [socketClient]);
135+
}, [socketClient, queue]);
109136

137+
// to call multiple next states in bulk
110138
const bulkSendNextStates = useCallback(
111139
async (count: number) => {
112-
const results = await Promise.all(Array.from({ length: count }, executeNextWithRetry));
113-
const successfulCount = results.filter((result) => result).length;
114-
return successfulCount;
140+
const results = await Promise.all(
141+
Array.from({ length: count }, () => executeNextWithRetry())
142+
);
143+
return results.filter(Boolean).length;
115144
},
116145
[executeNextWithRetry]
117146
);
118147

119148
return {
120-
resetConsoleChunks,
121-
appendConsoleChunks,
122-
sendCode,
123-
getNextState: executeNextWithRetry,
124-
bulkSendNextStates,
125-
resetDebugSession,
149+
resetConsoleChunks, // Clear console logs
150+
appendConsoleChunks, // Append logs
151+
sendCode, // Start session with file
152+
getNextState: executeNextWithRetry, // Step once
153+
bulkSendNextStates, // Step multiple times
154+
resetDebugSession, // Full reset
126155
};
127156
};

client/src/visualiser-debugger/Component/Console/useCursor.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@ function useCursor(
1111
// eslint-disable-next-line @typescript-eslint/no-unused-vars
1212
isCompiled: boolean
1313
) {
14-
const socket = useSocketClientStore((state) => state.socketClient);
14+
const socketClient = useSocketClientStore((state) => state.socketClient); // i dont understand whats happening and need to clarify
1515
const appendConsoleChunk = useGlobalStore((state) => state.appendConsoleChunks);
1616
const setActive = useFrontendStateStore((state) => state.setActive);
17-
const { socketClient } = useSocketClientStore();
1817

1918
const [shifts, setShifts] = useState(0);
2019
const [paused, setPaused] = useState(true);
@@ -65,7 +64,8 @@ function useCursor(
6564
// TODO: Ensure that it's okay to send the PREFIX to the backend
6665
// because if we remove the PREFIX, we can't tell which command
6766
// is input while the program is running and while the program is not running
68-
socket.serverAction.sendStdin(content);
67+
if (!socketClient) return;
68+
socketClient.serverAction.sendStdin(content);
6969
appendConsoleChunk(`${content}\n`);
7070
clearInput();
7171
scrollToBottom();
@@ -82,6 +82,7 @@ function useCursor(
8282
default:
8383
break;
8484
}
85+
if (!socketClient) return;
8586

8687
if (isCtrlPressed && key === 'd') {
8788
socketClient.serverAction.sendEOF();

client/src/visualiser-debugger/DevelopmentMode.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Tabs, Tab } from 'components/Tabs';
66
import Console from 'visualiser-debugger/Component/Console/Console';
77
import Joyride from 'react-joyride';
88
import DynamicTabs from 'components/TabResize/DynamicTabs';
9+
import useSocketClientStore from 'Services/socketClient';
910
import { ThemeProvider as MuiThemeProvider } from '@mui/material';
1011
import DevelopmentModeNavbar from '../components/Navbars/DevelopmentModeNavbar';
1112
import Configuration from './Component/Configuration/Configuration';
@@ -35,6 +36,9 @@ const DevelopmentModeContent = () => {
3536
}
3637
};
3738

39+
const initialise = useSocketClientStore((state) => state.initialise);
40+
const disconnect = useSocketClientStore((state) => state.disconnect);
41+
3842
// Onboarding Code
3943
useEffect(() => {
4044
if (onboardingCurrFile) {
@@ -45,6 +49,19 @@ const DevelopmentModeContent = () => {
4549
}
4650
}, [onboardingCurrFile]);
4751

52+
// initialising socket cycle
53+
useEffect(() => {
54+
// intialise socket Connection
55+
console.log('DevelopmentMode is mounted');
56+
initialise();
57+
58+
// disconnect socket connection
59+
return () => {
60+
console.log('DevelopmentMode is unmounted');
61+
disconnect();
62+
};
63+
}, [initialise, disconnect]);
64+
4865
const handleClickStart = (event: React.MouseEvent<HTMLElement>) => {
4966
event.preventDefault();
5067
resetRootPaths();

0 commit comments

Comments
 (0)