diff --git a/G11_Report.pdf b/G11_Report.pdf new file mode 100644 index 00000000..67f2d61b Binary files /dev/null and b/G11_Report.pdf differ diff --git a/frontend/src/components/room/code-editor.tsx b/frontend/src/components/room/code-editor.tsx index 539e54ec..0368a5c5 100644 --- a/frontend/src/components/room/code-editor.tsx +++ b/frontend/src/components/room/code-editor.tsx @@ -82,10 +82,23 @@ export default function CodeEditor({ const editorMount: OnMount = (editorL: editor.IStandaloneCodeEditor) => { setMonacoInstance(editorL); + monacoInstance?.onDidChangeCursorPosition((e) => { + if (onCursorChange === undefined) return; + const cursor = monacoInstance + .getModel()! + .getOffsetAt(monacoInstance.getPosition()!); + onCursorChange(cursor); + }); + // allow for range selection - since the above event prevents highlighting + monacoInstance?.onDidChangeCursorSelection((e) => { + if (onCursorChange === undefined) return; + const cursor = monacoInstance + .getModel()! + .getOffsetAt(monacoInstance.getPosition()!); + onCursorChange(cursor); + }); }; - const [previousText, setPreviousText] = React.useState(text); - const setCursorPosition = React.useCallback( (cursor: number) => { if (!monacoInstance) return; @@ -96,48 +109,40 @@ export default function CodeEditor({ [monacoInstance] ); - const updateCursorPosition = (prevText: any, newText: any) => { - if (!monacoInstance) return; - if (!cursor) return; - if (prevText.slice(0, cursor) !== newText.slice(0, cursor)) { - return true; - } - return false; - }; + // function updateCursorPosition( + // prevText: string, + // currText: string, + // cursor: number + // ) { + // if (!monacoInstance) return; + // if (!cursor) return; + // if (prevText.slice(0, cursor) !== currText.slice(0, cursor)) { + // return true; + // } else { + // return false; + // } + // } React.useEffect(() => { if (cursor !== undefined) { + setCursorPosition(cursor); console.log(cursor); - if (updateCursorPosition(previousText, text)) { - setCursorPosition(cursor + 1); - } else { - setCursorPosition(cursor); - } } - - monacoInstance?.onDidChangeCursorPosition((e) => { - if (onCursorChange === undefined) return; - const cursor = monacoInstance - .getModel()! - .getOffsetAt(monacoInstance.getPosition()!); - onCursorChange(cursor); - }); - }, [text, cursor, setCursorPosition, monacoInstance, onCursorChange]); + }, [text, cursor, monacoInstance]); const editorOnChange = React.useCallback( (value: string | undefined) => { if (!monacoInstance) return; if (value === undefined) return; - if (onCursorChange === undefined) return; if (monacoInstance.getPosition()) { + if (onCursorChange === undefined) return; const cursor = monacoInstance .getModel()! .getOffsetAt(monacoInstance.getPosition()!); onCursorChange(cursor); } onChange(value); - setPreviousText(value); }, [onChange, onCursorChange, monacoInstance] ); diff --git a/frontend/src/components/room/video-room.tsx b/frontend/src/components/room/video-room.tsx index e9b24c4b..6ca66321 100644 --- a/frontend/src/components/room/video-room.tsx +++ b/frontend/src/components/room/video-room.tsx @@ -106,6 +106,11 @@ const VideoRoom: React.FC = ({ room, className }) => { room?.localParticipant.videoTracks.forEach(publication => { if (publication.track) { publication.track.enable(!isCameraOn); + if (isCameraOn) { + setTimeout(publication => publication.track.stop(), 1000, publication); + } else { + publication.track.restart(); + } setIsCameraOn(!isCameraOn); } }); diff --git a/frontend/src/hooks/useCollaboration.tsx b/frontend/src/hooks/useCollaboration.tsx index 0bb3fded..34db244d 100644 --- a/frontend/src/hooks/useCollaboration.tsx +++ b/frontend/src/hooks/useCollaboration.tsx @@ -36,15 +36,14 @@ const useCollaboration = ({ }: UseCollaborationProps) => { const [socket, setSocket] = useState(null); const [text, setText] = useState("#Write your solution here"); - const [cursor, setCursor] = useState( - "#Write your solution here".length - ); + const [cursor, setCursor] = useState(text.length); const [room, setRoom] = useState(null); // twilio room const [questionId, setQuestionId] = useState(""); const textRef = useRef(text); const cursorRef = useRef(cursor); const prevCursorRef = useRef(cursor); const prevTextRef = useRef(text); + const [prevText, setPrevText] = useState(""); const awaitingAck = useRef(false); // ack from sending update const awaitingSync = useRef(false); // synced with server const twilioTokenRef = useRef(""); @@ -103,6 +102,7 @@ const useCollaboration = ({ console.log("Connected to Room"); room.localParticipant.videoTracks.forEach((publication) => { publication.track.disable(); + publication.track.stop(); }); room.localParticipant.audioTracks.forEach((publication) => { publication.track.disable(); @@ -110,7 +110,10 @@ const useCollaboration = ({ setRoom(room); }) .catch((err) => { - toast.error("An error occured when starting video: " + (err as Error).message); + toast.error( + "An error occured when starting video: " + + (err as Error).message + ); console.log(err, token, userId, roomId); }); }); @@ -139,16 +142,30 @@ const useCollaboration = ({ textRef.current = text; prevTextRef.current = text; setText(text); + if (cursor && cursor > -1) { - // console.log("Update cursor to " + cursor); - cursorRef.current = cursor; - setCursor(cursor); - } else { - cursorRef.current = prevCursorRef.current; - cursor = prevCursorRef.current; - // console.log("Update cursor to " + prevCursorRef.current); - setCursor(prevCursorRef.current); + if (prevText.slice(0, cursor) !== text.slice(0, cursor)) { + console.log("+1 " + cursor); + setCursor(cursor + 1); + } else { + console.log("normal " + cursor); + + setCursor(cursor); + } } + + // if (cursor && cursor > -1) { + // console.log("Update cursor to " + cursor); + // cursorRef.current = cursor; + // setCursor(cursor); + // } else { + // cursorRef.current = prevCursorRef.current; + // cursor = prevCursorRef.current; + // console.log("Update cursor to " + prevCursorRef.current); + // setCursor(prevCursorRef.current); + // } + setPrevText(text); + awaitingSync.current = false; } ); @@ -191,7 +208,7 @@ const useCollaboration = ({ prevTextRef.current = textRef.current; - // console.log(textOp); + console.log(textOp); const textOperationSet: TextOperationSetWithCursor = { version: vers, @@ -199,9 +216,13 @@ const useCollaboration = ({ cursor: cursorRef.current, }; - socket.emit(SocketEvents.ROOM_UPDATE, textOperationSet, () => { + socket.on("ACK", () => { awaitingAck.current = false; }); + + socket.emit(SocketEvents.ROOM_UPDATE, textOperationSet, () => { + awaitingAck.current = false; // may not work on prod + }); }, [text, socket]); const disconnect = () => { diff --git a/services/collaboration-service/src/routes/room.ts b/services/collaboration-service/src/routes/room.ts index 86fe2293..ee324362 100644 --- a/services/collaboration-service/src/routes/room.ts +++ b/services/collaboration-service/src/routes/room.ts @@ -194,6 +194,7 @@ function initSocketListeners(io: Server, socket: Socket, room_id: string) { roomUpdate(io, socket, room_id, text); } ackCallback(); + socket.emit("ACK"); }); } );