diff --git a/src/App.tsx b/src/App.tsx index 201d514..e514911 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -45,6 +45,7 @@ import LoginModal from "./pages/LoginPage/LoginModal"; import InviteSpace from "./pages/SpacePage/InviteSpace"; import HomePageMemberPage from "./pages/HomePage/HomePageMember"; import HomePageProfile from "./pages/HomePage/HomePageProfile"; +import SpecialVoiceRoom from "./pages/VoiceRoomPage/SpecialVoiceRoom"; // will we need constant path in later..? // const PATH = { @@ -121,6 +122,7 @@ function App() { { path: "/createvoiceroom", element: , hasBottomBar: false }, { path: "/joinvoiceroom", element: , hasBottombar: false }, { path: "/editvoiceroom", element: , hasBottombar: false }, + { path: "/specialvoiceroom", element: , hasBottombar: false }, ]; const routes_children_board = [ diff --git a/src/apis/voiceroomApi.ts b/src/apis/voiceroomApi.ts index de2bd5c..4c83b56 100644 --- a/src/apis/voiceroomApi.ts +++ b/src/apis/voiceroomApi.ts @@ -1,16 +1,37 @@ -import { VrList } from "@/pages/VoiceRoomPage/VoiceRoomListPage"; +import { VrList, participantInfo } from "@/pages/VoiceRoomPage/VoiceRoomListPage"; import { createRequestOptionsJSON, RequestOptions, createRequestOptionsJSON_AUTH, + fetchApi, } from "@/apis/_createRequestOptions"; import { updateRoom } from "@/pages/VoiceRoomPage/EditVoiceRoomPage"; +import { UserInfo } from "@livekit/components-react"; + +interface VoiceRoomParticipantInfoResponseType { + code: number; + status: number; + message: string; + result: { + participantInfoList: participantInfo[]; + }; +} const fetchVrApi = async (url: string, options: RequestOptions) => { const response = await fetch(url, options).catch((err) => console.error(err)); return response; }; +// API 함수 정의 +export const VrParticipantApi = async (spaceId: number, vrId: number) => { + const requestOptions = createRequestOptionsJSON_AUTH("GET"); + + if (!requestOptions) return null; + + const url = `${import.meta.env.VITE_API_BACK_URL}/space/${spaceId}/voiceRoom/${vrId}/participant`; + return await fetchApi(url, requestOptions); +}; + export const VrListApi = async ( spaceID: number, setVRList: React.Dispatch>, @@ -20,12 +41,21 @@ export const VrListApi = async ( return null; } const response = await fetchVrApi( - `${import.meta.env.VITE_API_BACK_URL}/space/${spaceID}/voiceRoom`, + `${import.meta.env.VITE_API_BACK_URL}/space/${spaceID}/voiceRoom?showParticipant=true`, requestOptions, ); if (response) { response.json().then((data) => { - setVRList(data.result.voiceRoomList); + const _temp: VrList[] = data.result.voiceRoomList; + // _temp.participantInfoList = + // _temp.map((value, index) => + // VrParticipantApi(spaceID, value.id).then((res) => { + // if (res?.result.participantInfoList !== undefined) { + // value.participantInfoList = res?.result.participantInfoList; + // } + // }), + // ); + setVRList(_temp); }); } else { setVRList([]); diff --git a/src/pages/SpacePage/AddSpacePage.tsx b/src/pages/SpacePage/AddSpacePage.tsx index a796a04..5b5953e 100644 --- a/src/pages/SpacePage/AddSpacePage.tsx +++ b/src/pages/SpacePage/AddSpacePage.tsx @@ -71,7 +71,10 @@ const AddSpacePage = () => { const handleCreateSpace = () => { createSpaceApi(spacename, spaceImg).then((data) => { - navigate(`/space/${data?.result.spaceId}`); + if (data?.result.spaceId !== undefined) { + localStorage.setItem("spaceId", data?.result.spaceId.toString()); + navigate(`/space/${data?.result.spaceId}`); + } }); }; diff --git a/src/pages/VoiceRoomPage/SpecialVoiceRoom.tsx b/src/pages/VoiceRoomPage/SpecialVoiceRoom.tsx new file mode 100644 index 0000000..4af86dd --- /dev/null +++ b/src/pages/VoiceRoomPage/SpecialVoiceRoom.tsx @@ -0,0 +1,104 @@ +import TopBarText from "@/components/TopBarText"; +import { LeftEnum } from "@/components/TopBarText"; +import plus from "@/assets/VoiceRoom/icon_plus.svg"; +import back from "@/assets/icon_back.svg"; +import setting from "@/assets/VoiceRoom/icon_setting.svg"; + +import { useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import * as s from "@/pages/VoiceRoomPage/VoiceRoomListPage.styled"; +import * as sty from "@/components/TopBarText.styled"; + +import { + CarouselLayout, + ControlBar, + FocusLayout, + FocusLayoutContainer, + FocusToggle, + GridLayout, + LiveKitRoom, + ParticipantTile, + RoomAudioRenderer, + useTracks, +} from "@livekit/components-react"; +import "@livekit/components-styles"; +import { Room, Track } from "livekit-client"; +import { sleep } from "livekit-client/dist/src/room/utils"; + +const SpecialVoiceRoom = () => { + const navigate = useNavigate(); + const [room] = useState(new Room()); + const [token, setToken] = useState(""); + const [isConnected, setIsConnected] = useState(false); + const [connect, setConnect] = useState(false); + + const handleDisconnect = () => { + setIsConnected(false); + setConnect(false); + navigate("/voiceroom"); + }; + + useEffect(() => { + const temp = localStorage.getItem("VrToken"); + if (temp) { + const temp2 = temp.substring(7); + setToken(temp2); + } else { + setToken(""); + } + }, []); + return ( + <> + { + setIsConnected(true); + console.log("connect!"); + }} + onDisconnected={handleDisconnect} + > + {/* The RoomAudioRenderer takes care of room-wide audio for you. */} + + + {isConnected && } + {/* Controls for the user to start/stop audio, video, and screen + share tracks and to leave the room. */} + + + ); +}; +function MyVideoConference() { + // `useTracks` returns all camera and screen share tracks. If a user + // joins without a published camera track, a placeholder track is returned. + + const tracks = useTracks( + [ + { source: Track.Source.Camera, withPlaceholder: false }, + { source: Track.Source.ScreenShare, withPlaceholder: false }, + ], + { onlySubscribed: false }, + ); + const track2 = tracks.find((trackRef) => trackRef.source === "screen_share"); + return track2 ? ( + + {/* The GridLayout accepts zero or one child. The child is used + as a template to render all passed in tracks. */} + { + nextMode(); + }} + > + + ) : ( + <> + ); +} +export default SpecialVoiceRoom; diff --git a/src/pages/VoiceRoomPage/VoiceRoomListPage.styled.ts b/src/pages/VoiceRoomPage/VoiceRoomListPage.styled.ts index b50087d..d8e15a7 100644 --- a/src/pages/VoiceRoomPage/VoiceRoomListPage.styled.ts +++ b/src/pages/VoiceRoomPage/VoiceRoomListPage.styled.ts @@ -12,7 +12,7 @@ export const DropdownDiv = styled.div` color: var(--Foundation-Gray-white, #fff); margin: 0rem 0rem 0.5rem 1rem; padding: 1rem; - width: 7.5rem; + width: 12rem; /* text/Regular 14pt */ font-family: Freesentation; diff --git a/src/pages/VoiceRoomPage/VoiceRoomListPage.tsx b/src/pages/VoiceRoomPage/VoiceRoomListPage.tsx index 22b137b..bb80851 100644 --- a/src/pages/VoiceRoomPage/VoiceRoomListPage.tsx +++ b/src/pages/VoiceRoomPage/VoiceRoomListPage.tsx @@ -26,7 +26,7 @@ export type VrList = { const VoiceRoomPortal = ({ vrList }: { vrList: VrList }) => { const navigate = useNavigate(); return ( -
+
{ navigate("/joinvoiceroom", { state: vrList }); @@ -34,12 +34,17 @@ const VoiceRoomPortal = ({ vrList }: { vrList: VrList }) => { > {vrList.name} - 대화 중인 스페이서 {vrList.numParticipant}명 + + 대화 중인 스페이서 {vrList.numParticipant}명 + {vrList.numParticipant === 0 ? ( <> ) : ( - ; + {vrList.participantInfoList !== null && + vrList.participantInfoList.map((value, index) => ( + + ))} )} @@ -51,20 +56,24 @@ const VoiceRoomListPage = () => { const [vrList, setVrList] = useState([]); const [activeVrList, setActiveVrList] = useState([]); const [inactiveVrList, setInActiveVrList] = useState([]); + const [isLoading, setIsLoading] = useState(false); const filterVrList = () => { + let tmp1: VrList[] = []; + let tmp2: VrList[] = []; vrList?.map((value, index) => { { - value.active - ? setActiveVrList((activeVrList) => [...(activeVrList || []), value]) - : setInActiveVrList((inactiveVrList) => [...(inactiveVrList || []), value]); + value.active ? (tmp1 = [...tmp1, value]) : (tmp2 = [...tmp2, value]); } }); + setActiveVrList([...tmp1]); + setInActiveVrList([...tmp2]); }; useEffect(() => { const spaceId = localStorage.getItem("spaceId"); if (spaceId !== null) { + setIsLoading(true); VrListApi(Number.parseInt(spaceId), setVrList); } //space ID @@ -74,12 +83,20 @@ const VoiceRoomListPage = () => { filterVrList(); }, [vrList]); + useEffect(() => { + if (inactiveVrList?.length !== 0 || activeVrList?.length !== 0) { + setIsLoading(false); + } + }, [activeVrList, inactiveVrList]); + const navigate = useNavigate(); const onClickInActiveVrRoom = (vrInfo: VrList) => { navigate("/joinvoiceroom", { state: vrInfo }); }; - return ( + return isLoading ? ( + <>{isLoading} + ) : ( <> { navigate("/editvoiceroom"); }} /> -
- 활동 중인 보이스룸 +
+ 활동 중인 보이스룸 {activeVrList?.length == 0 ? ( - 요청한 정산이 없어요! + 활동 중인 보이스룸이 없어요! ) : ( -
- {activeVrList?.map((value, index) => { - return ; - })} -
+ <> + {activeVrList?.map((value, index) => ( + + ))} + )} - 아무도 없어요! + + {" "} + 아무도 없어요!{" "} + {inactiveVrList?.length == 0 ? ( - 요청한 정산이 없어요! + 조용한 보이스룸이 없어요! ) : (
{inactiveVrList?.map((value, index) => { diff --git a/src/pages/VoiceRoomPage/VoiceRoomPage.tsx b/src/pages/VoiceRoomPage/VoiceRoomPage.tsx index a1d8985..04c8221 100644 --- a/src/pages/VoiceRoomPage/VoiceRoomPage.tsx +++ b/src/pages/VoiceRoomPage/VoiceRoomPage.tsx @@ -10,7 +10,11 @@ import * as s from "@/pages/VoiceRoomPage/VoiceRoomListPage.styled"; import * as sty from "@/components/TopBarText.styled"; import { + CarouselLayout, ControlBar, + FocusLayout, + FocusLayoutContainer, + FocusToggle, GridLayout, LiveKitRoom, ParticipantTile, @@ -18,7 +22,7 @@ import { useTracks, } from "@livekit/components-react"; import "@livekit/components-styles"; -import { Track } from "livekit-client"; +import { Room, Track } from "livekit-client"; const VoiceRoomPage = ({ VoiceRoomName, @@ -28,7 +32,17 @@ const VoiceRoomPage = ({ setJoin: React.Dispatch>; }) => { const navigate = useNavigate(); + const [room] = useState(new Room()); const [token, setToken] = useState(""); + const [isConnected, setIsConnected] = useState(false); + const [connect, setConnect] = useState(false); + + const handleDisconnect = () => { + setIsConnected(false); + setConnect(false); + navigate("/voiceroom"); + }; + useEffect(() => { const temp = localStorage.getItem("VrToken"); if (temp) { @@ -55,35 +69,41 @@ const VoiceRoomPage = ({ -
- - - {/* The RoomAudioRenderer takes care of room-wide audio for you. */} - - {/* Controls for the user to start/stop audio, video, and screen + { + setIsConnected(true); + console.log("connect!"); + }} + onDisconnected={handleDisconnect} + > + {/* The RoomAudioRenderer takes care of room-wide audio for you. */} + + + {isConnected && } + + {/* Controls for the user to start/stop audio, video, and screen share tracks and to leave the room. */} - - -
+
); }; function MyVideoConference() { // `useTracks` returns all camera and screen share tracks. If a user // joins without a published camera track, a placeholder track is returned. - const [mode, setMode] = useState(0); + const [mode, setMode] = useState(true); + const navigator = useNavigate(); const nextMode = () => { - setMode((mode + 1) % 3); + navigator("/specialvoiceroom"); }; + useEffect(() => { + // let track2 = tracks.find((trackRef) => trackRef.source === "screen_share"); + }, [mode]); const tracks = useTracks( [ { source: Track.Source.Camera, withPlaceholder: true }, @@ -92,10 +112,10 @@ function MyVideoConference() { { onlySubscribed: false }, ); return ( - + {/* The GridLayout accepts zero or one child. The child is used as a template to render all passed in tracks. */} - + ); } diff --git a/src/pages/VoiceRoomPage/VoiceRoomUser.tsx b/src/pages/VoiceRoomPage/VoiceRoomUser.tsx index 3e5ce12..259abc1 100644 --- a/src/pages/VoiceRoomPage/VoiceRoomUser.tsx +++ b/src/pages/VoiceRoomPage/VoiceRoomUser.tsx @@ -2,14 +2,19 @@ import * as s from "@/pages/VoiceRoomPage/VoiceRoomUser.styled"; import micON from "@/assets/VoiceRoom/icon_microphone_ON.svg"; import micOFF from "@/assets/VoiceRoom/icon_microphone_OFF.svg"; import { participantInfo } from "@/pages/VoiceRoomPage/VoiceRoomListPage"; +import { getUserDefaultImageURL } from "@/utils/getUserDefaultImageURL"; -export const VoiceRoomUser = ({ props }: { props: participantInfo[] }) => { +export const VoiceRoomUser = ({ props }: { props: participantInfo }) => { + const userId = Number.parseInt(localStorage.getItem("userId")!); + console.log(props); return ( - {/* +
{props.name}
- */} +
);