Skip to content

Commit 005507a

Browse files
authored
Merge pull request #77 from SPWwj/br-collab-fix
Br collab fix
2 parents c0c3fc2 + 853a725 commit 005507a

File tree

10 files changed

+223
-47
lines changed

10 files changed

+223
-47
lines changed

backend/user-service/services/tokenService.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { UnauthorisedError } from "../utils/httpErrors.js";
77
class TokenService {
88
static generateAccessToken(user) {
99
const accessToken = jwt.sign(
10-
{ id: user.id, isAdmin: user.isAdmin },
10+
{ id: user.id, isAdmin: user.isAdmin, username: user.username },
1111
jwtConfig.accessTokenSecret,
1212
jwtConfig.accessTokenOptions
1313
);
@@ -16,7 +16,7 @@ class TokenService {
1616

1717
static generateRefreshToken(user) {
1818
const refreshToken = jwt.sign(
19-
{ id: user.id, jti: uuidv4() },
19+
{ id: user.id, jti: uuidv4(), username: user.username },
2020
jwtConfig.refreshTokenSecret,
2121
jwtConfig.refreshTokenOptions
2222
);

frontend/src/domain/context/CollaborationContext.tsx

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { createContext, useState, useContext, ReactNode, useEffect, useMemo } from "react";
1+
import React, { createContext, useState, useContext, ReactNode, useEffect, useMemo, useCallback } from "react";
22
import * as Y from "yjs";
33
import * as monaco from "monaco-editor";
44
import { MonacoBinding } from "y-monaco";
@@ -20,6 +20,7 @@ interface CollaborationContextType {
2020
execResult: CodeExecResult | null;
2121
setRoomId: (roomId: string) => void;
2222
connectedUsers: string[];
23+
disconnect: () => void;
2324
}
2425

2526
const CollaborationContext = createContext<CollaborationContextType | undefined>(undefined);
@@ -42,12 +43,10 @@ export const CollaborationProvider: React.FC<{ children: ReactNode }> = ({ child
4243
const [execResult, setExecResult] = useState<CodeExecResult | null>(null);
4344
const [isExecuting, setIsExecuting] = useState<boolean>(false);
4445
const [connectedUsers, setConnectedUsers] = useState<string[]>([]);
45-
4646
const [editor, setEditor] = useState<monaco.editor.IStandaloneCodeEditor | null>(null);
4747
const [provider, setProvider] = useState<WebsocketProvider | null>(null);
4848
const [binding, setBinding] = useState<MonacoBinding | null>(null);
4949

50-
// Connect to the server-managed Yjs document when roomId is set
5150
useEffect(() => {
5251
if (roomId == null) {
5352
console.log(`RoomId is null!`)
@@ -70,11 +69,8 @@ export const CollaborationProvider: React.FC<{ children: ReactNode }> = ({ child
7069
};
7170
}, [roomId]);
7271

73-
// Set up Monaco editor with server-managed Yjs document
74-
useEffect(() => {
75-
if (provider == null || editor == null || editor.getModel() == null) {
76-
return;
77-
}
72+
// Set up Monaco editor with server-managed Yjs document useEffect(() => {
73+
if (!provider || !editor?.getModel()) return;
7874

7975
// Access the server-managed ydoc via provider.doc
8076
const ytext = provider.doc.getText("monaco");
@@ -88,12 +84,12 @@ export const CollaborationProvider: React.FC<{ children: ReactNode }> = ({ child
8884
if (key === SELECTED_LANGUAGE) {
8985
const language: Language = ymap.get(SELECTED_LANGUAGE) as Language;
9086
setSelectedLanguage(language);
91-
const model = editor.getModel();
92-
monaco.editor.setModelLanguage(model!, language.language);
87+
monaco.editor.setModelLanguage(editor.getModel()!, language.language);
9388
}
9489
});
9590
});
9691

92+
9793
// Initialize editor language from the shared Yjs map
9894
const language = ymap.get(SELECTED_LANGUAGE) as Language;
9995
const model = editor.getModel();
@@ -108,14 +104,13 @@ export const CollaborationProvider: React.FC<{ children: ReactNode }> = ({ child
108104
}, []);
109105

110106
const initialiseLanguages = async () => {
111-
// Initialise language dropdown
112107
const allLanguages = monaco.languages.getLanguages();
113108
const pistonLanguageVersions = await PistonClient.getLanguageVersions();
114109
setLanguages(
115110
allLanguages
116111
.filter((lang) => pistonLanguageVersions.some((pistonLang: any) => pistonLang.language === lang.id))
117112
.map((lang) => ({
118-
alias: lang.aliases && lang.aliases.length > 0 ? lang.aliases[0] : lang.id,
113+
alias: lang.aliases?.[0] || lang.id,
119114
language: lang.id,
120115
version: pistonLanguageVersions.find((pistonLang: any) => pistonLang.language === lang.id)?.version
121116
}))
@@ -131,11 +126,9 @@ export const CollaborationProvider: React.FC<{ children: ReactNode }> = ({ child
131126
try {
132127
setIsExecuting(true);
133128
const sourceCode = editor?.getValue();
134-
if (!sourceCode) {
135-
// TODO
136-
return;
137-
}
138-
const output: CodeExecResult = await PistonClient.executeCode(selectedLanguage, sourceCode);
129+
if (!sourceCode) return;
130+
131+
const output = await PistonClient.executeCode(selectedLanguage, sourceCode);
139132
setExecResult(output);
140133
} catch (e) {
141134
toast.error("There was an issue running the code");
@@ -159,7 +152,8 @@ export const CollaborationProvider: React.FC<{ children: ReactNode }> = ({ child
159152
handleExecuteCode,
160153
isExecuting,
161154
execResult,
162-
connectedUsers
155+
connectedUsers,
156+
disconnect
163157
}}
164158
>
165159
{children}
Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
11
.submitButton {
2-
background-color: #ffa500;
2+
background-color: #ffa500 !important;
3+
color: black !important;
4+
font-weight: bold !important;
35
}
46

57
.runButton {
6-
background-color: black;
7-
color: white;
8+
background-color: #1a1a1a !important;
9+
color: white !important;
10+
}
11+
12+
.runButton:hover {
13+
background-color: #333 !important;
14+
}
15+
16+
.submitButton:hover {
17+
background-color: #ffb52e !important;
818
}
919

1020
button {
1121
width: fit-content;
12-
}
22+
}

frontend/src/presentation/components/CodeEditor/CodeEditor.tsx

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import React, { useState } from "react";
1+
import React, { useState, useEffect, useRef } from "react";
22
import styles from "./CodeEditor.module.css";
3-
import Editor, { Monaco } from "@monaco-editor/react";
3+
import Editor from "@monaco-editor/react";
44
import { Button, Spin } from "antd";
55
import { useCollaboration } from "domain/context/CollaborationContext";
66
import * as monaco from "monaco-editor";
@@ -15,22 +15,47 @@ interface CodeEditorProps {
1515
const CodeEditor: React.FC<CodeEditorProps> = ({ roomId }) => {
1616
const { onEditorIsMounted, isExecuting, setRoomId, connectedUsers } = useCollaboration();
1717
const [theme, setTheme] = useState("vs-light");
18+
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(null);
19+
const containerRef = useRef<HTMLDivElement>(null);
20+
21+
useEffect(() => {
22+
let resizeObserver: ResizeObserver | null = null;
23+
let resizeTimeout: NodeJS.Timeout;
24+
25+
if (containerRef.current && editorRef.current) {
26+
resizeObserver = new ResizeObserver((entries) => {
27+
// Debounce resize events
28+
clearTimeout(resizeTimeout);
29+
resizeTimeout = setTimeout(() => {
30+
if (editorRef.current) {
31+
editorRef.current.layout();
32+
}
33+
}, 100);
34+
});
35+
36+
resizeObserver.observe(containerRef.current);
37+
}
38+
39+
return () => {
40+
if (resizeObserver) {
41+
resizeObserver.disconnect();
42+
}
43+
clearTimeout(resizeTimeout);
44+
};
45+
}, []);
1846

1947
const handleEditorDidMount = (editor: monaco.editor.IStandaloneCodeEditor) => {
48+
editorRef.current = editor;
2049
onEditorIsMounted(editor);
2150
setRoomId(roomId);
2251
};
2352

2453
const handleToggleTheme = () => {
25-
if (theme === "vs-light") {
26-
setTheme("vs-dark");
27-
} else {
28-
setTheme("vs-light");
29-
}
54+
setTheme(theme === "vs-light" ? "vs-dark" : "vs-light");
3055
};
3156

3257
return (
33-
<div className={styles.container}>
58+
<div className={styles.container} ref={containerRef}>
3459
<div className={styles.toolbar}>
3560
<div className={styles.toolbarLeft}>
3661
<LanguageSelector />
@@ -69,7 +94,7 @@ const CodeEditor: React.FC<CodeEditorProps> = ({ roomId }) => {
6994
options={{
7095
minimap: { enabled: false },
7196
scrollbar: { verticalScrollbarSize: 4 },
72-
formatOnPaste: true
97+
formatOnPaste: true,
7398
}}
7499
/>
75100
</div>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
.toggleContainer {
2+
display: flex;
3+
align-items: center;
4+
justify-content: space-between;
5+
padding: 12px 16px;
6+
}
7+
8+
.toggleButton {
9+
display: inline-flex;
10+
align-items: center;
11+
gap: 8px;
12+
padding: 8px 16px;
13+
border-radius: 8px;
14+
font-size: 14px;
15+
font-weight: 500;
16+
color: #333;
17+
background: transparent;
18+
border: none;
19+
cursor: pointer;
20+
transition: all 0.2s ease;
21+
}
22+
23+
.toggleButton:hover {
24+
background-color: #f5f5f5;
25+
}
26+
27+
.icon {
28+
width: 16px;
29+
height: 16px;
30+
}
31+
32+
.status {
33+
padding: 4px 8px;
34+
border-radius: 12px;
35+
font-size: 12px;
36+
font-weight: 500;
37+
}
38+
39+
.chatMode {
40+
background-color: #e8f0fe;
41+
color: #1a73e8;
42+
}
43+
44+
.questionMode {
45+
background-color: #f3e8ff;
46+
color: #7e22ce;
47+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// ToggleButton.tsx
2+
import React from "react";
3+
import styles from "./ToggleButton.module.css";
4+
5+
const ToggleButton = ({ showChat, onClick }: { showChat: boolean; onClick: () => void }) => {
6+
return (
7+
<div className={styles.toggleContainer}>
8+
<button className={styles.toggleButton} onClick={onClick}>
9+
<span className={styles.iconWrapper}>
10+
{showChat ? (
11+
<span className={`${styles.icon} ${styles.bookIcon}`}>📖</span>
12+
) : (
13+
<span className={`${styles.icon} ${styles.chatIcon}`}>💬</span>
14+
)}
15+
</span>
16+
<span>{showChat ? "View Question" : "Open Chat"}</span>
17+
</button>
18+
<div className={`${styles.status} ${showChat ? styles.chatMode : styles.questionMode}`}>
19+
{showChat ? "Chat Mode" : "Question Mode"}
20+
</div>
21+
</div>
22+
);
23+
};
24+
25+
export default ToggleButton;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
.chatFrameWrapper {
2+
height: 100%;
3+
width: 100%;
4+
display: flex;
5+
flex-direction: column;
6+
background: #ffffff;
7+
border-radius: 8px;
8+
overflow: hidden;
9+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
10+
}
11+
12+
.chatFrame {
13+
width: 100%;
14+
flex: 1;
15+
border: none;
16+
background: transparent;
17+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// ChatFrame.tsx
2+
import React from "react";
3+
import styles from "./ChatFrame.module.css";
4+
5+
interface ChatFrameProps {
6+
roomId: string;
7+
}
8+
9+
const ChatFrame: React.FC<ChatFrameProps> = ({ roomId }) => {
10+
const chatUrl = `http://localhost:5173/Assistant`;
11+
12+
return (
13+
<div className={styles.chatFrameWrapper}>
14+
<iframe
15+
src={chatUrl}
16+
className={styles.chatFrame}
17+
title="Chat Embed"
18+
allow="camera; microphone; fullscreen; display-capture"
19+
/>
20+
</div>
21+
);
22+
};
23+
24+
export default ChatFrame;

frontend/src/presentation/pages/CollaborationRoomPage.module.css

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.container {
2-
height: 100%;
2+
height: 100vh;
33
width: 100%;
44
overflow-x: hidden;
55
display: flex;
@@ -19,10 +19,11 @@
1919
min-height: 0;
2020
}
2121

22-
.questionContainer {
23-
flex-shrink: 0;
22+
.questionContent {
23+
height: 100%;
24+
display: flex;
25+
flex-direction: column;
2426
}
25-
2627
.verticalSeparator,
2728
.horizontalSeparator {
2829
transition: background-color 0.1s ease;
@@ -38,6 +39,7 @@
3839
cursor: col-resize;
3940
width: 8px;
4041
}
42+
4143
.horizontalSeparator {
4244
cursor: row-resize;
4345
height: 6px;
@@ -46,4 +48,4 @@
4648
.outputContainer {
4749
flex-shrink: 0;
4850
overflow-y: auto;
49-
}
51+
}

0 commit comments

Comments
 (0)