Skip to content

Commit 347ee63

Browse files
committed
Co-authored-by: jujupro <[email protected]>
2 parents 2cdf68f + 3714b08 commit 347ee63

File tree

9 files changed

+180
-67
lines changed

9 files changed

+180
-67
lines changed

app/src/components/bottom/CodePreview.tsx

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import 'ace-builds/src-noconflict/mode-javascript';
44
import 'ace-builds/src-noconflict/theme-dracula';
55
import 'ace-builds/src-noconflict/theme-terminal';
66

7-
import * as esbuild from 'esbuild-wasm';
8-
97
import React, { useContext, useEffect, useRef, useState } from 'react';
108
import {
119
codePreviewInput,
@@ -19,22 +17,14 @@ import { RootState } from '../../redux/store';
1917
import { fetchPlugin } from '../../plugins/fetch-plugin';
2018
import { unpkgPathPlugin } from '../../plugins/unpkg-path-plugin';
2119
import useResizeObserver from '../../tree/useResizeObserver';
20+
import { initializeEsbuild } from '../../helperFunctions/esbuildService';
2221

2322
const CodePreview: React.FC<{
2423
theme: string | null;
2524
setTheme: any | null;
2625
}> = ({ theme, setTheme }) => {
2726
const ref = useRef<any>();
2827

29-
/**
30-
* Starts the Web Assembly service.
31-
*/
32-
const startService = async () => {
33-
ref.current = await esbuild.initialize({
34-
worker: true,
35-
wasmURL: 'https://unpkg.com/[email protected]/esbuild.wasm'
36-
});
37-
};
3828
const dispatch = useDispatch();
3929

4030
const wrapper = useRef();
@@ -49,7 +39,8 @@ const CodePreview: React.FC<{
4939
const [input, setInput] = useState('');
5040

5141
useEffect(() => {
52-
startService();
42+
//Starts the Web Assembly service
43+
initializeEsbuild();
5344
}, []);
5445

5546
useEffect(() => {
@@ -61,6 +52,13 @@ const CodePreview: React.FC<{
6152
dispatch(codePreviewInput(currentComponent.code));
6253
}, [currentComponent, state.components]);
6354

55+
useEffect(() => {
56+
console.log('CodePreview Mounted');
57+
return () => {
58+
console.log('CodePreview Unmounted');
59+
};
60+
}, []);
61+
6462
/**
6563
* Handler thats listens to changes in code editor
6664
* @param {string} data - Code entered by the user

app/src/components/left/HTMLPanel.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,11 @@ const HTMLPanel = (props): JSX.Element => {
144144
};
145145

146146
const handleCreateElement = useCallback((e) => {
147-
if (e.key === 'Enter' && e.target.tagName !== 'TEXTAREA') {
147+
if (
148+
e.key === 'Enter' &&
149+
e.target.tagName !== 'TEXTAREA' &&
150+
e.target.id !== 'filled-hidden-label-small'
151+
) {
148152
e.preventDefault();
149153
document.getElementById('submitButton').click();
150154
}

app/src/components/left/RoomsContainer.tsx

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
changeFocus,
1616
deleteChild,
1717
changePosition,
18+
resetState,
1819
updateStateUsed,
1920
updateUseContext,
2021
updateCss,
@@ -100,9 +101,9 @@ const RoomsContainer = () => {
100101

101102
// update user list when there's a change: new join or leave the room
102103
socket.on('updateUserList', (newUserList) => {
103-
console.log('user list received from server');
104+
//console.log('user list received from server');
104105
dispatch(setUserList(newUserList));
105-
console.log('userList:', userList);
106+
// console.log('userList:', userList);
106107
});
107108

108109
socket.on('child data from server', (childData: object) => {
@@ -152,7 +153,6 @@ const RoomsContainer = () => {
152153
//edge case: if userList is not empty, reset it to empty array
153154
if (userList.length !== 0) dispatch(setUserList([]));
154155
handleUserEnteredRoom(roomCode);
155-
156156
dispatch(setRoomCode(roomCode)); //?
157157
dispatch(setUserJoined(true)); //setting joined room to true for rendering leave room button
158158
}
@@ -161,6 +161,7 @@ const RoomsContainer = () => {
161161
let socket = getSocket();
162162
if (socket) {
163163
socket.disconnect(); //disconnecting socket from server
164+
console.log(socket);
164165
socket = null;
165166
console.log('user leaves the room');
166167
}
@@ -169,6 +170,7 @@ const RoomsContainer = () => {
169170
dispatch(setUserName(''));
170171
dispatch(setUserList([]));
171172
dispatch(setUserJoined(false)); //setting joined to false so join room UI appear
173+
dispatch(resetState(''));
172174
}
173175

174176
//checking empty input field (not including spaces)
@@ -178,7 +180,20 @@ const RoomsContainer = () => {
178180
return userName.length === 0 || roomCode.length === 0;
179181
}
180182

181-
const userColors = ['#FC00BD', '#D0FC00', '#00DBFC', '#FD98B8', '#FCAA00', '#9267FF'];
183+
const handleKeyDown = (e) => {
184+
if (e.key === 'Enter' && e.target.id === 'filled-hidden-label-small') {
185+
e.preventDefault();
186+
joinRoom();
187+
}
188+
};
189+
const userColors = [
190+
'#FC00BD',
191+
'#D0FC00',
192+
'#00DBFC',
193+
'#FD98B8',
194+
'#FCAA00',
195+
'#9267FF'
196+
];
182197

183198
return (
184199
<div>
@@ -248,7 +263,7 @@ const RoomsContainer = () => {
248263
primary={`${index + 1}. ${
249264
index === 0 ? `${user} (host)` : user
250265
}`}
251-
style={{color: userColors[userList.indexOf(user)]}}
266+
style={{ color: userColors[userList.indexOf(user)] }}
252267
/>
253268
</ListItem>
254269
))}
@@ -290,6 +305,8 @@ const RoomsContainer = () => {
290305
value={roomCode}
291306
placeholder="Input Room Number"
292307
onChange={(e) => dispatch(setRoomCode(e.target.value))}
308+
className="enterRoomInput"
309+
onKeyDown={handleKeyDown}
293310
/>
294311
<Button
295312
variant="contained"

app/src/components/main/Canvas.tsx

Lines changed: 113 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ function Canvas(props: {}): JSX.Element {
2828
console.log('canvas is rendered');
2929

3030
const [remoteCursors, setRemoteCursors] = useState([]);
31+
const [toggleSwitch, setToggleSwitch] = useState(true);
3132

3233
const debounceSetPosition = debounce((newX, newY) => {
33-
//emit socket event every 500ms when cursor moves
34+
//emit socket event every 300ms when cursor moves
3435
if (userList.length > 1)
3536
emitEvent('cursorData', roomCode, { x: newX, y: newY, userName });
3637
}, 300);
@@ -39,52 +40,99 @@ function Canvas(props: {}): JSX.Element {
3940
debounceSetPosition(e.clientX, e.clientY);
4041
};
4142

42-
const socket = getSocket();
43-
if (socket) {
44-
socket.on('remote cursor data from server', (remoteData) => {
45-
setRemoteCursors((prevState) => {
46-
//check if received cursor data is from an existing user in the room
47-
const cursorIdx = prevState.findIndex(
48-
(cursor) => cursor.remoteUserName === remoteData.userName
49-
);
50-
51-
//[{x,y,remoteUserName, isVisible}, {...}, {...}]
52-
//existing user
53-
if (cursorIdx >= 0) {
54-
//check if cursor position has changed
55-
if (
56-
prevState[cursorIdx].x !== remoteData.x ||
57-
prevState[cursorIdx].y !== remoteData.y
58-
) {
59-
//update existing user's cursor position
60-
const updatedCursors = [...prevState];
61-
updatedCursors[cursorIdx] = {
62-
...prevState[cursorIdx],
63-
x: remoteData.x,
64-
y: remoteData.y
65-
};
66-
return updatedCursors;
67-
} else {
68-
//return previous state if no change
69-
return prevState;
70-
}
43+
const handleCursorDataFromServer = (remoteData) => {
44+
setRemoteCursors((prevState) => {
45+
//check if received cursor data is from an existing user in the room
46+
const cursorIdx = prevState.findIndex(
47+
(cursor) => cursor.remoteUserName === remoteData.userName
48+
);
49+
//[{x,y,remoteUserName, isVisible}, {...}, {...}]
50+
//existing user
51+
if (cursorIdx >= 0) {
52+
//check if cursor position has changed
53+
if (
54+
prevState[cursorIdx].x !== remoteData.x ||
55+
prevState[cursorIdx].y !== remoteData.y
56+
) {
57+
//update existing user's cursor position
58+
const updatedCursors = [...prevState];
59+
updatedCursors[cursorIdx] = {
60+
...prevState[cursorIdx],
61+
x: remoteData.x,
62+
y: remoteData.y
63+
};
64+
return updatedCursors;
7165
} else {
72-
//new user: add new user's cursor
73-
return [
74-
...prevState,
75-
{
76-
x: remoteData.x,
77-
y: remoteData.y,
78-
remoteUserName: remoteData.userName,
79-
isVisible: true
80-
}
81-
];
66+
//return previous state if no change
67+
return prevState;
8268
}
83-
});
69+
} else {
70+
//new user: add new user's cursor
71+
return [
72+
...prevState,
73+
{
74+
x: remoteData.x,
75+
y: remoteData.y,
76+
remoteUserName: remoteData.userName,
77+
isVisible: true
78+
}
79+
];
80+
}
8481
});
85-
}
82+
};
83+
const handleToggleSwitch = () => {
84+
setToggleSwitch(!toggleSwitch);
85+
//checks the state before it's updated so need to check the opposite condition
86+
if (toggleSwitch) {
87+
//turn off
88+
socket.off('remote cursor data from server');
89+
//make remote cursor invisible
90+
setRemoteCursors((prevState) => {
91+
const newState = prevState.map((cursor) => ({
92+
...cursor,
93+
isVisible: false
94+
}));
95+
return newState;
96+
});
97+
} else {
98+
//turn on
99+
socket.on('remote cursor data from server', (remoteData) =>
100+
handleCursorDataFromServer(remoteData)
101+
);
102+
//make remote cursor visible
103+
setRemoteCursors((prevState) =>
104+
prevState.map((cursor) => ({
105+
...cursor,
106+
isVisible: true
107+
}))
108+
);
109+
}
110+
};
86111

87-
//--------------------------------
112+
console.log('Toggle Switch:', toggleSwitch);
113+
114+
const socket = getSocket();
115+
//wrap the socket event listener in useEffect with dependency array as [socket], so the the effect will run only when: 1. After the initial rendering of the component 2. Every time the socket instance changes(connect, disconnect)
116+
useEffect(() => {
117+
console.log(
118+
'socket inside useEffect:',
119+
socket ? 'connected' : 'not connected'
120+
);
121+
122+
if (socket) {
123+
console.log('------setting up socket.on event listener-------');
124+
socket.on('remote cursor data from server', (remoteData) =>
125+
handleCursorDataFromServer(remoteData)
126+
);
127+
}
128+
129+
return () => {
130+
console.log('clean up cursor event listener after canvas unmount');
131+
if (socket) socket.off('remote cursor data from server');
132+
};
133+
}, [socket]);
134+
135+
//-----------------
88136

89137
// find the current component based on the canvasFocus component ID in the state
90138
const currentComponent: Component = state.components.find(
@@ -240,7 +288,14 @@ function Canvas(props: {}): JSX.Element {
240288
currentComponent.style
241289
);
242290

243-
const userColors = ['#FC00BD', '#D0FC00', '#00DBFC', '#FD98B8', '#FCAA00', '#9267FF'];
291+
const userColors = [
292+
'#FC00BD',
293+
'#D0FC00',
294+
'#00DBFC',
295+
'#FD98B8',
296+
'#FCAA00',
297+
'#9267FF'
298+
];
244299

245300
return (
246301
<div
@@ -273,6 +328,20 @@ function Canvas(props: {}): JSX.Element {
273328
</div>
274329
)
275330
)}
331+
332+
<label
333+
className="switch"
334+
style={{
335+
position: 'relative',
336+
display: 'inline-block',
337+
width: '60px',
338+
height: '34px'
339+
}}
340+
>
341+
<button className="btn-toggle" onClick={handleToggleSwitch}>
342+
On/Off
343+
</button>
344+
</label>
276345
</div>
277346
);
278347
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import * as esbuild from 'esbuild-wasm';
2+
3+
/* Singleton pattern for initializing esbuild */
4+
/* This ensures that esbuild is only initialized once, regardless of how many times CodePreview is mounted and unmounted */
5+
let isEsbuildInitialized = false;
6+
7+
export const initializeEsbuild = async () => {
8+
if (!isEsbuildInitialized) {
9+
await esbuild.initialize({
10+
worker: true,
11+
wasmURL: 'https://unpkg.com/[email protected]/esbuild.wasm'
12+
});
13+
isEsbuildInitialized = true;
14+
}
15+
};

app/src/helperFunctions/socket.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ import config from '../../../config';
1616
let socket = null;
1717

1818
export const initializeSocket = () => {
19-
if (socket) socket.disconnect();
19+
if (socket) socket.connect();
2020
if (!socket) {
2121
socket = io(config.API_BASE_URL, { transports: ['websocket'] });
2222
console.log('A user connected');
23+
console.log('socket:', socket);
2324
}
2425
};
2526

app/src/redux/reducers/slice/userSlice.ts

Whitespace-only changes.

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)