Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
2f2fc18
feat: 질문지 정보에 대한 타입 구현
ShipFriend0516 Nov 28, 2024
48814da
chore: pnpm-lock
ShipFriend0516 Nov 28, 2024
eb9f7c4
feat: 스터디 웹소켓 API 이벤트명 분리
ShipFriend0516 Nov 28, 2024
687c092
feat: 스터디가 진행 중인지 아닌지를 직관적으로 확인할 수 있게 구현
ShipFriend0516 Nov 28, 2024
e7f4f52
feat: 질문 넘기기 API 연동 및 스터디 시작 API 연동
ShipFriend0516 Nov 28, 2024
619af1d
feat: 현재 질문을 표시하고 이전 질문들의 기록을 사이드바에 남기도록 구현
ShipFriend0516 Nov 28, 2024
2ebed38
feat: 스터디 기능 API 핸들러 추가
ShipFriend0516 Nov 28, 2024
64ebd53
feat: 스터디 시작버튼과 중지버튼을 누르면 스터디 시작 및 정지 구현
ShipFriend0516 Nov 28, 2024
1bfb78b
feat: 사이드바 접기 버튼 추가
ShipFriend0516 Nov 28, 2024
b1af3f1
feat: 사이드바를 접을 수 있도록 사이드바를 감싸는 Container 구현
ShipFriend0516 Nov 28, 2024
37f234c
chore: 컴포넌트 큰 단위로 폴더로 구분
ShipFriend0516 Nov 28, 2024
4834f78
refactor: 호스트만 사용할 수 있는 도구와 일반 유저들도 사용할 수 있는 도구 컴포넌트를 분리
ShipFriend0516 Nov 28, 2024
e1c3588
feat: 스터디 시작하면 시작 인디케이터를 띄우기
ShipFriend0516 Nov 28, 2024
dde42e0
feat: 다음 질문, 이전 질문 누르고 2초 후에 활성화 되도록 구현
ShipFriend0516 Nov 28, 2024
6e96862
fix: 질문 번호가 잘못되어있던 오류 수정
ShipFriend0516 Nov 28, 2024
431d9bc
fix: 버튼 프로그레스바가 오버플로우 되던 현상 수정
ShipFriend0516 Nov 28, 2024
3902442
Merge branch 'dev' into feature/study-in-session
ShipFriend0516 Nov 28, 2024
582171d
refactor: study 관련 함수 useStudy 훅으로 분리
ShipFriend0516 Nov 28, 2024
966d140
refactor: uptime 분/초로 표현
ShipFriend0516 Nov 28, 2024
cac93ce
refactor: useStudy -> useSession으로 받아온 함수 사용하도록 수정
ShipFriend0516 Nov 28, 2024
d950de1
refactor: 이전 질문 버튼과 다음 질문 버튼이 질문지의 양 끝단에서 비활성화되도록 구현
ShipFriend0516 Nov 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion frontend/src/components/session/SessionHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { RoomMetadata } from "@hooks/type/session";
import { useEffect, useState } from "react";

interface SessionHeaderProps {
roomMetadata: RoomMetadata | null;
Expand All @@ -9,6 +10,19 @@ const SessionHeader = ({
participantsCount,
roomMetadata,
}: SessionHeaderProps) => {
const [uptime, setUptime] = useState<number>(0);
const SECOND = 1000;
useEffect(() => {
if (!roomMetadata?.inProgress) return;
const interval = setInterval(() => {
setUptime((prev) => prev + 1);
}, SECOND);

return () => {
clearInterval(interval);
};
}, [roomMetadata?.inProgress]);

return (
<div
className={
Expand All @@ -17,7 +31,6 @@ const SessionHeader = ({
>
{roomMetadata?.title ? (
<>
{" "}
<span className={"bg-green-500 rounded-md px-2 py-0.5 text-white"}>
{roomMetadata?.category}
</span>
Expand All @@ -26,6 +39,24 @@ const SessionHeader = ({
{roomMetadata &&
`(${participantsCount} / ${roomMetadata.maxParticipants})`}
</span>
{roomMetadata.inProgress ? (
<span className={"inline-flex items-center ml-4"}>
<span
className={
"inline-block mr-2 rounded-full w-4 h-4 bg-red-700 animate-pulse shadow-red-500"
}
></span>
<span>스터디 진행 중</span>
<span className={"ml-2 w-16"}>
{Math.floor(uptime / 60)}분 {uptime % 60}초
</span>
</span>
) : (
<span>
<span className={"w-3 h-3 bg-gray-300 "}></span>
스터디 시작 전
</span>
)}
</>
) : (
<h1>아직 세션에 참가하지 않았습니다.</h1>
Expand Down
141 changes: 0 additions & 141 deletions frontend/src/components/session/SessionSidebar.tsx

This file was deleted.

177 changes: 177 additions & 0 deletions frontend/src/components/session/Sidebar/SessionSidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import { FaClipboardList, FaFolder } from "react-icons/fa";
import { FaUserGroup } from "react-icons/fa6";
import useModalStore from "@stores/useModalStore.ts";
import Modal from "../../common/Modal";
import { useNavigate } from "react-router-dom";
import { Socket } from "socket.io-client";
import useToast from "@hooks/useToast.ts";
import { TbCrown } from "react-icons/tb";
import { SESSION_EMIT_EVENT } from "@/constants/WebSocket/SessionEvent.ts";
import { Question } from "@hooks/type/session";

interface ParticipantsData {
nickname: string;
isHost: boolean;
}

interface Props {
socket: Socket | null;
questionList: Question[];
currentIndex: number;
participants: ParticipantsData[];
roomId: string | undefined; // TODO: sessionId가 입력되지 않았을 때(undefined) 처리 필요
isHost: boolean;
}

const SessionSidebar = ({
socket,
questionList,
currentIndex,
participants,
roomId,
isHost,
}: Props) => {
const { openModal } = useModalStore();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

모달은 modalStore가 아닌 useModal로 modal 인스턴스를 가져와서 사용하면 될거 같습니다. 이전 PR에 사용법을 적어두었습니다. #258
이 PR 참고하시면 좋을거 같아요!

const navigate = useNavigate();
const toast = useToast();

const existHandler = () => {
socket?.emit(SESSION_EMIT_EVENT.LEAVE, { roomId });
toast.success("메인 화면으로 이동합니다.");
navigate("/sessions");
};

const destroyAndExitHandler = () => {
socket?.off(SESSION_EMIT_EVENT.FINISH);
socket?.emit(SESSION_EMIT_EVENT.FINISH, { roomId });
toast.success("메인 화면으로 이동합니다.");
navigate("/sessions");
};

const HostModalData = {
title: "세션을 종료할까요?",
subtitle: "세션을 종료하면 참가자들이 모두 나가게 됩니다.",
leftButton: "방장 양도 후 종료",
rightButton: "세션 종료",
type: "red",
onLeftClick: existHandler,
onRightClick: destroyAndExitHandler,
};

const ParticipantModalData = {
title: "지금 나가면 다시 들어올 수 없어요!",
subtitle: "정말 종료하시겠어요?",
leftButton: "취소하기",
rightButton: "종료하기",
type: "red",
onLeftClick: () => {},
onRightClick: existHandler,
};

return (
<div
className={
"flex flex-grow px-4 gap-2 items-stretch w-[440px] bg-white shrink-0"
}
>
<div className={"flex flex-col gap-4 flex-grow justify-between "}>
<div className={"flex flex-col gap-4 "}>
<div className={"flex flex-col gap-2 pt-6"}>
<h2 className={"inline-flex gap-1 items-center text-semibold-m"}>
<FaClipboardList />
현재 질문
</h2>
<div
className={
"border border-accent-gray p-2 bg-transparent rounded-xl "
}
>
{currentIndex >= 0 ? (
<p>
<span className={"text-bold-s"}>
Q{questionList[currentIndex].index + 1}.{" "}
</span>
{questionList[currentIndex].content}
</p>
) : (
<p>질문 로딩 중...</p>
)}
</div>
</div>
<div className={"flex flex-col gap-2 mt-4"}>
<h2 className={"inline-flex gap-1 items-center text-semibold-m"}>
<FaUserGroup />
참가자
</h2>
<ul>
{participants.map((participant, index) => (
<li key={index} className={"flex items-center gap-2"}>
<span className={"w-4 h-4 bg-point-2 rounded-full"} />
<span>{participant.nickname}</span>
<span className={"text-yellow-400"}>
{participant.isHost && <TbCrown />}
</span>
</li>
))}
</ul>
</div>
<div className={"flex flex-col gap-2 mt-4"}>
<h2 className={"inline-flex gap-1 items-center text-semibold-m"}>
<FaFolder />
이전 질문
</h2>
<ul>
{currentIndex <= 0 && (
<li className={"text-medium-s"}>
여기에 이전 질문이 기록됩니다.
</li>
)}
{questionList.map((question, index) => {
if (index < currentIndex)
return (
<li key={question.id}>
Q{index + 1}. {question.content}
</li>
);
})}
</ul>
</div>
</div>
<div className={"h-16 items-center flex w-full"}>
<button
className={"w-full bg-red-500 text-white rounded-md py-2"}
onClick={() => {
openModal();
}}
>
종료하기
</button>
</div>
</div>

<Modal
title={isHost ? HostModalData.title : ParticipantModalData.title}
subtitle={
isHost ? HostModalData.subtitle : ParticipantModalData.subtitle
}
leftButton={
isHost ? HostModalData.leftButton : ParticipantModalData.leftButton
}
rightButton={
isHost ? HostModalData.rightButton : ParticipantModalData.rightButton
}
type={"red"}
onLeftClick={
isHost ? HostModalData.onLeftClick : ParticipantModalData.onLeftClick
}
onRightClick={
isHost
? HostModalData.onRightClick
: ParticipantModalData.onRightClick
}
/>
</div>
);
};

export default SessionSidebar;
Loading