Skip to content

Commit 526e42e

Browse files
committed
PEER-234: Add avatar outlines and dc button to toolbar
Signed-off-by: SeeuSim <[email protected]>
1 parent 6a86ff6 commit 526e42e

File tree

3 files changed

+71
-38
lines changed

3 files changed

+71
-38
lines changed

frontend/src/components/blocks/interview/editor.tsx

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import {
1313
} from '@/components/ui/select';
1414
import { getTheme, type IEditorTheme, languages, themeOptions } from '@/lib/editor/extensions';
1515
import { useCollab } from '@/lib/hooks/use-collab';
16+
import { Button } from '@/components/ui/button';
17+
import { ChevronLeftIcon } from '@radix-ui/react-icons';
1618

1719
const EXTENSION_HEIGHT = 250;
1820
const MIN_EDITOR_HEIGHT = 350;
@@ -24,43 +26,65 @@ type EditorProps = {
2426
export const Editor = ({ room }: EditorProps) => {
2527
const { height } = useWindowSize();
2628
const [theme, setTheme] = useState<IEditorTheme>('vscodeDark');
27-
const { editorRef, extensions, language, setLanguage, code, setCode } = useCollab(room);
29+
const { editorRef, extensions, language, setLanguage, code, setCode, members } = useCollab(room);
2830
const themePreset = useMemo(() => {
2931
return getTheme(theme);
3032
}, [theme]);
3133

3234
return (
3335
<div className='flex flex-col gap-4 p-4'>
34-
<div className='flex gap-4'>
35-
<div className='flex flex-col gap-2'>
36-
<Label>Language</Label>
37-
<Select value={language} onValueChange={(val) => setLanguage(val as LanguageName)}>
38-
<SelectTrigger className='max-w-[150px]'>
39-
<SelectValue />
40-
</SelectTrigger>
41-
<SelectContent>
42-
{languages.map((lang, idx) => (
43-
<SelectItem value={lang} key={idx}>
44-
{lang}
45-
</SelectItem>
46-
))}
47-
</SelectContent>
48-
</Select>
36+
<div className='flex w-full justify-between gap-4'>
37+
<div className='flex gap-4'>
38+
<div className='flex flex-col gap-2'>
39+
<Label>Language</Label>
40+
<Select value={language} onValueChange={(val) => setLanguage(val as LanguageName)}>
41+
<SelectTrigger className='max-w-[150px]'>
42+
<SelectValue />
43+
</SelectTrigger>
44+
<SelectContent>
45+
{languages.map((lang, idx) => (
46+
<SelectItem value={lang} key={idx}>
47+
{lang}
48+
</SelectItem>
49+
))}
50+
</SelectContent>
51+
</Select>
52+
</div>
53+
<div className='flex flex-col gap-2'>
54+
<Label>Theme</Label>
55+
<Select value={theme} onValueChange={(val) => setTheme(val as IEditorTheme)}>
56+
<SelectTrigger className='max-w-[150px]'>
57+
<SelectValue />
58+
</SelectTrigger>
59+
<SelectContent>
60+
{themeOptions.map((theme, idx) => (
61+
<SelectItem value={theme} key={idx}>
62+
{theme}
63+
</SelectItem>
64+
))}
65+
</SelectContent>
66+
</Select>
67+
</div>
4968
</div>
50-
<div className='flex flex-col gap-2'>
51-
<Label>Theme</Label>
52-
<Select value={theme} onValueChange={(val) => setTheme(val as IEditorTheme)}>
53-
<SelectTrigger className='max-w-[150px]'>
54-
<SelectValue />
55-
</SelectTrigger>
56-
<SelectContent>
57-
{themeOptions.map((theme, idx) => (
58-
<SelectItem value={theme} key={idx}>
59-
{theme}
60-
</SelectItem>
61-
))}
62-
</SelectContent>
63-
</Select>
69+
<div className='flex items-center gap-2'>
70+
<div className='flex gap-1 font-mono text-xs'>
71+
{/* TODO: Get user avatar and display */}
72+
{members.map((member, index) => (
73+
<div
74+
className='grid size-8 place-items-center text-clip rounded-full border p-1 text-xs'
75+
style={{
76+
borderColor: member.color,
77+
}}
78+
key={index}
79+
>
80+
<span>{member.name}</span>
81+
</div>
82+
))}
83+
</div>
84+
<Button variant='destructive' size='sm' className='group flex items-center !px-2 !py-1'>
85+
<ChevronLeftIcon className='transition-transform duration-200 ease-in-out group-hover:-translate-x-px' />
86+
<span>Disconnect</span>
87+
</Button>
6488
</div>
6589
</div>
6690
<div className='border-border w-full !overflow-clip rounded-lg border shadow-sm'>

frontend/src/lib/hooks/use-collab.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { LanguageName } from '@uiw/codemirror-extensions-langs';
22
import type { Extension, ReactCodeMirrorRef } from '@uiw/react-codemirror';
3-
import { useEffect, useMemo, useRef, useState } from 'react';
3+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
44
import { yCollab } from 'y-codemirror.next';
55
import { WebsocketProvider } from 'y-websocket';
66
import * as Y from 'yjs';
@@ -24,12 +24,18 @@ const getRandomColor = () => {
2424
return usercolors[Math.floor(Math.random() * usercolors.length)];
2525
};
2626

27+
type IYjsUserState = { user: { name: string; userId: string; color: string; colorLight: string } };
28+
2729
// TODO: Test if collab logic works
2830
export const useCollab = (roomId: string) => {
2931
const [code, setCode] = useState('');
3032
const editorRef = useRef<ReactCodeMirrorRef>(null);
3133
const [extensions, setExtensions] = useState<Array<Extension>>(baseExtensions);
3234
const [language, setLanguage] = useState<LanguageName>('python');
35+
36+
const [members, _setMembers] = useState<Array<IYjsUserState['user']>>([]);
37+
const setMembers = useCallback(_setMembers, [members]);
38+
3339
const langExtension = useMemo(() => {
3440
return getLanguage(language);
3541
}, [language]);
@@ -56,19 +62,20 @@ export const useCollab = (roomId: string) => {
5662
console.log(`Connection Status: ${ev?.status}`);
5763
});
5864

59-
// TODO: Update state store, then display avatars
6065
awareness.on('update', () => {
61-
console.log(awareness.getStates().values()); // Array of name, color, light
66+
const members = awareness.getStates().values() as MapIterator<IYjsUserState>;
67+
setMembers(Array.from(members).map((v) => v.user));
6268
});
6369

6470
const { color, light } = getRandomColor();
65-
// TODO: Get user name
66-
// TODO: Set user ID
67-
awareness.setLocalStateField('user', {
71+
// TODO: Get user name, ID
72+
const userState: IYjsUserState['user'] = {
6873
name: `Anon`,
69-
color: color,
74+
userId: '',
75+
color,
7076
colorLight: light,
71-
});
77+
};
78+
awareness.setLocalStateField('user', userState);
7279

7380
const collabExt = yCollab(ytext, awareness, { undoManager });
7481
setCode(ytext.toString());
@@ -86,5 +93,6 @@ export const useCollab = (roomId: string) => {
8693
setLanguage,
8794
code,
8895
setCode,
96+
members,
8997
};
9098
};

frontend/vite.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export default defineConfig(({ mode }) => {
4646
},
4747
'/collab-ws': {
4848
target: `${env.VITE_COLLAB_SERVICE.replace('http', 'ws')}`,
49+
rewrite: (path) => path.replace(/\/collab-ws/, ''),
4950
ws: true,
5051
},
5152
},

0 commit comments

Comments
 (0)