Skip to content

Commit 62496a0

Browse files
authored
Merge pull request #219 from boostcampwm-2024/feature-fe-#209
프로필 수정 기능 추가, useCursor에서 프로필 사용하게 수정
2 parents 6fe5225 + ecbf143 commit 62496a0

File tree

6 files changed

+335
-41
lines changed

6 files changed

+335
-41
lines changed

apps/frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"@tanstack/react-query": "^5.59.19",
2525
"@tiptap/extension-collaboration": "^2.9.1",
2626
"@tiptap/extension-collaboration-cursor": "^2.9.1",
27+
"@uiw/react-color": "^2.3.2",
2728
"@xyflow/react": "^12.3.4",
2829
"autoprefixer": "^10.4.20",
2930
"axios": "^1.7.7",
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import logo from "/logo.png?url";
22

3-
export default function LogoBtn() {
3+
interface LogoBtnProps {
4+
onClick?: () => void;
5+
}
6+
export default function LogoBtn({ onClick }: LogoBtnProps) {
47
return (
5-
<div className="h-8 w-8 overflow-clip rounded-md">
8+
<button className="h-8 w-8 overflow-clip rounded-md" onClick={onClick}>
69
<img src={logo} />
7-
</div>
10+
</button>
811
);
912
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { useEffect, useState } from "react";
2+
import Button from "../commons/button";
3+
import { Dialog } from "../commons/dialog";
4+
import { Compact } from "@uiw/react-color";
5+
import useUserStore from "@/store/useUserStore";
6+
7+
type RemoveNoteModalProps = {
8+
isOpen: boolean;
9+
onConfirm: () => void;
10+
onCloseModal: () => void;
11+
};
12+
13+
export default function ProfileModal({
14+
isOpen,
15+
onConfirm,
16+
onCloseModal,
17+
}: RemoveNoteModalProps) {
18+
const { currentUser, setCurrentUser, provider } = useUserStore();
19+
const [hex, setHex] = useState(currentUser.color);
20+
const [isColorPickerOpen, setIsColorPickerOpen] = useState(false);
21+
const [nickname, setNickname] = useState(currentUser.clientId);
22+
23+
useEffect(() => {
24+
setNickname(currentUser.clientId);
25+
setHex(currentUser.color);
26+
}, [currentUser]);
27+
28+
return (
29+
<Dialog isOpen={isOpen} onCloseModal={onCloseModal}>
30+
<div className="flex flex-col items-center gap-4">
31+
<div className="flex flex-col items-center gap-1.5">
32+
<Dialog.Title>프로필 수정</Dialog.Title>
33+
</div>
34+
<div className="flex flex-row items-center gap-3">
35+
<button
36+
className="h-12 w-12 rounded-full border-[1px] border-black"
37+
style={{ backgroundColor: hex }}
38+
onClick={() => setIsColorPickerOpen(!isColorPickerOpen)}
39+
/>
40+
41+
<input
42+
className={
43+
"h-10 rounded-md border-[1px] border-[#eaeaea] p-2 text-sm text-[#141414] outline-none"
44+
}
45+
placeholder="닉네임을 입력하세요"
46+
value={nickname}
47+
onChange={(e) => setNickname(e.target.value)}
48+
/>
49+
</div>
50+
<div className="">
51+
{isColorPickerOpen && (
52+
<Compact color={hex} onChange={(color) => setHex(color.hex)} />
53+
)}
54+
</div>
55+
56+
<div className="flex w-full flex-row justify-between gap-2">
57+
<Button
58+
className="w-full rounded-lg bg-[#171717] text-neutral-100 hover:bg-slate-800"
59+
onClick={() => {
60+
provider.awareness.setLocalStateField("color", hex);
61+
provider.awareness.setLocalStateField("clientId", nickname);
62+
63+
setCurrentUser({
64+
...currentUser,
65+
color: hex,
66+
clientId: nickname,
67+
});
68+
69+
onConfirm();
70+
}}
71+
>
72+
확인
73+
</Button>
74+
<Button
75+
className="w-full rounded-lg bg-[#f4f4f5] text-neutral-700 hover:bg-neutral-200"
76+
onClick={onCloseModal}
77+
>
78+
취소
79+
</Button>
80+
</div>
81+
</div>
82+
<Dialog.CloseButton onCloseModal={onCloseModal} />
83+
</Dialog>
84+
);
85+
}

apps/frontend/src/components/sidebar/TopNav.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,29 @@
11
import VerticalDivider from "@/components/commons/divider/VerticalDivider";
22
import WorkspaceNav from "@/components/WorkspaceNav";
33
import LogoBtn from "@/components/LogoBtn";
4-
4+
import ProfileModal from "./ProfileModal";
55
import workspaceLogo from "@/../public/workspace-logo.svg?url";
6+
import { useState } from "react";
67

78
export default function TopNav() {
9+
const [isModalOpen, setIsModalOpen] = useState(false);
10+
811
return (
912
<div className="flex items-center gap-2">
10-
<LogoBtn />
13+
<LogoBtn
14+
onClick={() => {
15+
setIsModalOpen(true);
16+
}}
17+
/>
18+
<ProfileModal
19+
isOpen={isModalOpen}
20+
onCloseModal={() => {
21+
setIsModalOpen(false);
22+
}}
23+
onConfirm={() => {
24+
setIsModalOpen(false);
25+
}}
26+
/>
1127
<VerticalDivider className="h-3" />
1228
<WorkspaceNav imageUrl={workspaceLogo} workspaceTitle="프로젝트 Web15" />
1329
</div>

apps/frontend/src/hooks/useCursor.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import useUserStore from "@/store/useUserStore";
77
export interface AwarenessState {
88
cursor: XYPosition | null;
99
color: string;
10-
clientId: number;
10+
clientId: string;
1111
}
1212

1313
interface CollaborativeCursorsProps {
@@ -25,6 +25,7 @@ export function useCollaborativeCursors({
2525
new Map(),
2626
);
2727
const { currentUser } = useUserStore();
28+
const { color, clientId } = currentUser;
2829

2930
useEffect(() => {
3031
const wsProvider = new SocketIOProvider(
@@ -46,23 +47,25 @@ export function useCollaborativeCursors({
4647

4748
wsProvider.awareness.setLocalState({
4849
cursor: null,
49-
color: currentUser.color,
50-
clientId: currentUser.clientId,
50+
color,
51+
clientId,
5152
});
5253

5354
wsProvider.awareness.on("change", () => {
5455
const states = new Map(
5556
Array.from(
5657
wsProvider.awareness.getStates() as Map<number, AwarenessState>,
57-
).filter(([, state]) => state.cursor !== null),
58+
).filter(
59+
([, state]) => state.clientId !== clientId && state.cursor !== null,
60+
),
5861
);
5962
setCursors(states);
6063
});
6164

6265
return () => {
6366
wsProvider.destroy();
6467
};
65-
}, [ydoc, roomName]);
68+
}, [ydoc, roomName, color, clientId]);
6669

6770
const updateCursorPosition = useCallback(
6871
(x: number | null, y: number | null) => {
@@ -75,11 +78,11 @@ export function useCollaborativeCursors({
7578

7679
provider.current.awareness.setLocalState({
7780
cursor,
78-
color: currentUser.color,
79-
clientId: currentUser.clientId,
81+
color,
82+
clientId,
8083
});
8184
},
82-
[flowInstance],
85+
[flowInstance, color, clientId],
8386
);
8487

8588
const handleMouseMove = useCallback(

0 commit comments

Comments
 (0)