Skip to content

Commit

Permalink
Merge pull request #219 from boostcampwm-2024/feature-fe-#209
Browse files Browse the repository at this point in the history
프로필 수정 기능 추가, useCursor에서 프로필 사용하게 수정
  • Loading branch information
yewonJin authored Nov 20, 2024
2 parents 6fe5225 + ecbf143 commit 62496a0
Show file tree
Hide file tree
Showing 6 changed files with 335 additions and 41 deletions.
1 change: 1 addition & 0 deletions apps/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@tanstack/react-query": "^5.59.19",
"@tiptap/extension-collaboration": "^2.9.1",
"@tiptap/extension-collaboration-cursor": "^2.9.1",
"@uiw/react-color": "^2.3.2",
"@xyflow/react": "^12.3.4",
"autoprefixer": "^10.4.20",
"axios": "^1.7.7",
Expand Down
9 changes: 6 additions & 3 deletions apps/frontend/src/components/LogoBtn.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import logo from "/logo.png?url";

export default function LogoBtn() {
interface LogoBtnProps {
onClick?: () => void;
}
export default function LogoBtn({ onClick }: LogoBtnProps) {
return (
<div className="h-8 w-8 overflow-clip rounded-md">
<button className="h-8 w-8 overflow-clip rounded-md" onClick={onClick}>
<img src={logo} />
</div>
</button>
);
}
85 changes: 85 additions & 0 deletions apps/frontend/src/components/sidebar/ProfileModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { useEffect, useState } from "react";
import Button from "../commons/button";
import { Dialog } from "../commons/dialog";
import { Compact } from "@uiw/react-color";
import useUserStore from "@/store/useUserStore";

type RemoveNoteModalProps = {
isOpen: boolean;
onConfirm: () => void;
onCloseModal: () => void;
};

export default function ProfileModal({
isOpen,
onConfirm,
onCloseModal,
}: RemoveNoteModalProps) {
const { currentUser, setCurrentUser, provider } = useUserStore();
const [hex, setHex] = useState(currentUser.color);
const [isColorPickerOpen, setIsColorPickerOpen] = useState(false);
const [nickname, setNickname] = useState(currentUser.clientId);

useEffect(() => {
setNickname(currentUser.clientId);
setHex(currentUser.color);
}, [currentUser]);

return (
<Dialog isOpen={isOpen} onCloseModal={onCloseModal}>
<div className="flex flex-col items-center gap-4">
<div className="flex flex-col items-center gap-1.5">
<Dialog.Title>프로필 수정</Dialog.Title>
</div>
<div className="flex flex-row items-center gap-3">
<button
className="h-12 w-12 rounded-full border-[1px] border-black"
style={{ backgroundColor: hex }}
onClick={() => setIsColorPickerOpen(!isColorPickerOpen)}
/>

<input
className={
"h-10 rounded-md border-[1px] border-[#eaeaea] p-2 text-sm text-[#141414] outline-none"
}
placeholder="닉네임을 입력하세요"
value={nickname}
onChange={(e) => setNickname(e.target.value)}
/>
</div>
<div className="">
{isColorPickerOpen && (
<Compact color={hex} onChange={(color) => setHex(color.hex)} />
)}
</div>

<div className="flex w-full flex-row justify-between gap-2">
<Button
className="w-full rounded-lg bg-[#171717] text-neutral-100 hover:bg-slate-800"
onClick={() => {
provider.awareness.setLocalStateField("color", hex);
provider.awareness.setLocalStateField("clientId", nickname);

setCurrentUser({
...currentUser,
color: hex,
clientId: nickname,
});

onConfirm();
}}
>
확인
</Button>
<Button
className="w-full rounded-lg bg-[#f4f4f5] text-neutral-700 hover:bg-neutral-200"
onClick={onCloseModal}
>
취소
</Button>
</div>
</div>
<Dialog.CloseButton onCloseModal={onCloseModal} />
</Dialog>
);
}
20 changes: 18 additions & 2 deletions apps/frontend/src/components/sidebar/TopNav.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,29 @@
import VerticalDivider from "@/components/commons/divider/VerticalDivider";
import WorkspaceNav from "@/components/WorkspaceNav";
import LogoBtn from "@/components/LogoBtn";

import ProfileModal from "./ProfileModal";
import workspaceLogo from "@/../public/workspace-logo.svg?url";
import { useState } from "react";

export default function TopNav() {
const [isModalOpen, setIsModalOpen] = useState(false);

return (
<div className="flex items-center gap-2">
<LogoBtn />
<LogoBtn
onClick={() => {
setIsModalOpen(true);
}}
/>
<ProfileModal
isOpen={isModalOpen}
onCloseModal={() => {
setIsModalOpen(false);
}}
onConfirm={() => {
setIsModalOpen(false);
}}
/>
<VerticalDivider className="h-3" />
<WorkspaceNav imageUrl={workspaceLogo} workspaceTitle="프로젝트 Web15" />
</div>
Expand Down
19 changes: 11 additions & 8 deletions apps/frontend/src/hooks/useCursor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import useUserStore from "@/store/useUserStore";
export interface AwarenessState {
cursor: XYPosition | null;
color: string;
clientId: number;
clientId: string;
}

interface CollaborativeCursorsProps {
Expand All @@ -25,6 +25,7 @@ export function useCollaborativeCursors({
new Map(),
);
const { currentUser } = useUserStore();
const { color, clientId } = currentUser;

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

wsProvider.awareness.setLocalState({
cursor: null,
color: currentUser.color,
clientId: currentUser.clientId,
color,
clientId,
});

wsProvider.awareness.on("change", () => {
const states = new Map(
Array.from(
wsProvider.awareness.getStates() as Map<number, AwarenessState>,
).filter(([, state]) => state.cursor !== null),
).filter(
([, state]) => state.clientId !== clientId && state.cursor !== null,
),
);
setCursors(states);
});

return () => {
wsProvider.destroy();
};
}, [ydoc, roomName]);
}, [ydoc, roomName, color, clientId]);

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

provider.current.awareness.setLocalState({
cursor,
color: currentUser.color,
clientId: currentUser.clientId,
color,
clientId,
});
},
[flowInstance],
[flowInstance, color, clientId],
);

const handleMouseMove = useCallback(
Expand Down
Loading

0 comments on commit 62496a0

Please sign in to comment.