Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat] 비디오에 nickname 표시 #317

Merged
merged 13 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions apps/media/src/mediasoup/mediasoup.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export class MediasoupService implements OnModuleInit {
if (room.hasPeer(socketId)) {
throw new WsException(ErrorMessage.PEER_ALREADY_EXISTS_IN_ROOM);
}

room.addPeer(socketId, nickname);

return room.getRouter().rtpCapabilities;
Expand Down
3 changes: 2 additions & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
"react-hook-form": "^7.53.2",
"socket.io-client": "^4.8.1",
"tailwind-merge": "^2.5.4",
"zod": "^3.23.8"
"zod": "^3.23.8",
"zustand": "^5.0.1"
},
"devDependencies": {
"@chromatic-com/storybook": "^3.2.2",
Expand Down
24 changes: 10 additions & 14 deletions apps/web/src/components/common/Header/User.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
/* eslint-disable react-refresh/only-export-components */
import { Link } from '@tanstack/react-router';
import axios from 'axios';
import { Provider } from '@repo/types';

import UserProfileOfMeDialog from '@/components/user/UserProfileOfMeDialog';
import { useUserProfileOfMe } from '@/hooks/api/user';
import useAuthInfo from '@/hooks/useAuthInfo';
import useModal from '@/hooks/useModal';

import Avatar from '../Avatar';
Expand All @@ -18,26 +17,23 @@ export const LOGIN_TYPE: Record<Provider, string> = {
};

function User() {
const { data, error } = useUserProfileOfMe();
const { isOpen, onOpen, onClose } = useModal();

const isUnauthorized = axios.isAxiosError(error) && error.response?.status === 401;
const { isLoading, isAuthenticated, authInfo } = useAuthInfo();

const loginType = data?.provider && LOGIN_TYPE[data.provider];
const { isOpen, onOpen, onClose } = useModal();

const AuthorizedContent = () => (
<>
<section className="flex cursor-pointer items-center gap-2" onClick={onOpen}>
<Avatar size="xs" src={data?.profileImageUrl} />
<span className="text-body1 text-alt">{data?.nickname}</span>
<Avatar size="xs" src={authInfo?.profileImageUrl} />
<span className="text-body1 text-alt">{authInfo?.nickname}</span>
</section>
{isOpen && data && loginType && (
{isOpen && authInfo && (
<UserProfileOfMeDialog
onClose={onClose}
isOpen={isOpen}
profileImageUrl={data.profileImageUrl}
nickname={data.nickname}
loginType={loginType}
profileImageUrl={authInfo.profileImageUrl}
nickname={authInfo.nickname}
loginType={LOGIN_TYPE[authInfo.provider]}
/>
)}
</>
Expand All @@ -53,7 +49,7 @@ function User() {

return (
<aside className="flex gap-3">
{isUnauthorized ? <UnauthorizedContent /> : <AuthorizedContent />}
{isAuthenticated && !isLoading ? <AuthorizedContent /> : <UnauthorizedContent />}
</aside>
);
}
Expand Down
1 change: 0 additions & 1 deletion apps/web/src/components/common/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ function Header() {
대시보드
</Link>
</nav>
{/* TODO: User 로그인시 핸들링 */}
<User />
</header>
);
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/dashboard/DashboardTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const DASHBOARD_ROUTES = {

function DashboardTab() {
const navigate = useNavigate();
const isOpenedMatch = useMatch({ from: '/dashboard/open', shouldThrow: false });
const isOpenedMatch = useMatch({ from: '/_authenticated/dashboard/open', shouldThrow: false });
const selectedTab = isOpenedMatch ? 'OPENED' : 'APPLIED';

const DASHBOARD_TAB_DATA: TabData<keyof typeof DASHBOARD_TAB>[] = [
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/components/live/StreamView/List/Pinned.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ function PinnedGrid({
stream={pinnedVideoStreamData.stream}
paused={pinnedVideoStreamData.paused}
isMicOn={getAudioMutedState(pinnedVideoStreamData)}
nickname={pinnedVideoStreamData.nickname}
/>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ function SubVideoGrid({
avatarSize="sm"
paused={streamData.paused}
isMicOn={getAudioMutedState(streamData)}
nickname={streamData.nickname}
/>
</div>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ function VideoGrid({ videoStreamData, onVideoClick, getAudioMutedState }: VideoG
stream={streamData.stream}
paused={streamData.paused}
isMicOn={getAudioMutedState(streamData)}
nickname={streamData.nickname}
/>
</div>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface VideoPlayerProps {
isMicOn?: boolean;
avatarSize?: 'sm' | 'md' | 'lg';
mediaType?: 'video' | 'screen';
nickname: string;
}

function VideoPlayer({
Expand All @@ -33,8 +34,8 @@ function VideoPlayer({
mediaType = 'video',
isMicOn = false,
avatarSize = 'md',
nickname,
}: VideoPlayerProps) {
const NAME = '김티클'; // TODO: 이름 받아오기
const [isLoading, setIsLoading] = useState(true);
const videoRef = useRef<HTMLVideoElement>(null);

Expand Down Expand Up @@ -65,7 +66,7 @@ function VideoPlayer({
</div>
)}
<div className="absolute bottom-3 left-3 z-10">
<Badge>{NAME}</Badge>
<Badge>{nickname}</Badge>
</div>
</>
)}
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/components/live/StreamView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface StreamData {
kind: types.MediaKind;
stream: MediaStream | null;
paused: boolean;
nickname: string;
}

const StreamView = () => {
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/ticle/detail/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useNavigate, useParams } from '@tanstack/react-router';

Check warning on line 1 in apps/web/src/components/ticle/detail/index.tsx

View workflow job for this annotation

GitHub Actions / check

'useNavigate' is defined but never used

import CalendarIc from '@/assets/icons/calendar.svg?react';
import ClockIc from '@/assets/icons/clock.svg?react';
Expand All @@ -11,7 +11,7 @@
import { formatDateTimeRange } from '@/utils/date';

function Detail() {
const { ticleId } = useParams({ from: '/ticle/$ticleId' });
const { ticleId } = useParams({ from: '/_authenticated/ticle/$ticleId' });
const { data } = useTicle(ticleId);
const { mutate } = useApplyTicle();

Expand Down
5 changes: 2 additions & 3 deletions apps/web/src/hooks/mediasoup/useMediasoup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,10 @@ const useMediasoup = () => {
resumeRemoteStream(producerId);
});

socket.on(SOCKET_EVENTS.newProducer, ({ peerId, producerId, kind, paused }) => {
socket.on(SOCKET_EVENTS.newProducer, ({ peerId, producerId, kind, paused, nickname }) => {
if (socket.id === peerId) return;

// TODO: nickname 추가
consume({ producerId, kind, peerId, paused, nickname: '변경' });
consume({ producerId, kind, peerId, paused, nickname });
});
};

Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/hooks/mediasoup/useProducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ interface UseProducerProps {
}

const useProducer = ({ socketRef, transportsRef }: UseProducerProps) => {
const { ticleId: roomId } = useParams({ from: '/live/$ticleId' });
const { ticleId: roomId } = useParams({ from: '/_authenticated/live/$ticleId' });

const producersRef = useRef<{ [key in MediaTypes]: types.Producer | null }>({
video: null,
Expand Down
11 changes: 9 additions & 2 deletions apps/web/src/hooks/mediasoup/useRemoteStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,19 @@ import { client, SOCKET_EVENTS } from '@repo/mediasoup';
import { useMediasoupState } from '@/contexts/mediasoup/context';

const useRemoteStream = () => {
const { ticleId } = useParams({ from: '/live/$ticleId' });
const { ticleId } = useParams({ from: '/_authenticated/live/$ticleId' });
const { socketRef, transportsRef, deviceRef } = useMediasoupState();

const [videoStreams, setVideoStreams] = useState<client.RemoteStream[]>([]);
const [audioStreams, setAudioStreams] = useState<client.RemoteStream[]>([]);

const consume = async ({ producerId, peerId, kind, paused }: client.CreateProducerRes) => {
const consume = async ({
producerId,
peerId,
kind,
paused,
nickname,
}: client.CreateProducerRes) => {
const socket = socketRef.current;
const device = deviceRef.current;
const { recvTransport } = transportsRef.current;
Expand All @@ -36,6 +42,7 @@ const useRemoteStream = () => {
consumer,
socketId: peerId,
kind,
nickname,
stream,
paused,
};
Expand Down
18 changes: 11 additions & 7 deletions apps/web/src/hooks/mediasoup/useRoom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,30 @@ import type { client } from '@repo/mediasoup';
import { SOCKET_EVENTS } from '@repo/mediasoup';

import { useMediasoupState } from '@/contexts/mediasoup/context';
import useAuthStore from '@/stores/useAuthStore';

interface JoinRoomRes {
rtpCapabilities: client.RtpCapabilities;
}
const useRoom = () => {
const { socketRef } = useMediasoupState();
const { ticleId: roomId } = useParams({ from: '/live/$ticleId' });
const { ticleId: roomId } = useParams({ from: '/_authenticated/live/$ticleId' });
const nickname = useAuthStore.getState().authInfo?.nickname;

const createRoom = async () => {
const socket = socketRef.current;

if (!socket) return;

const data = { roomId };

return new Promise<client.RtpCapabilities>((resolve) => {
socket.emit(SOCKET_EVENTS.createRoom, data, () => {
socket.emit(SOCKET_EVENTS.joinRoom, data, ({ rtpCapabilities }: JoinRoomRes) => {
resolve(rtpCapabilities);
});
socket.emit(SOCKET_EVENTS.createRoom, { roomId }, () => {
socket.emit(
SOCKET_EVENTS.joinRoom,
{ roomId, nickname },
({ rtpCapabilities }: JoinRoomRes) => {
resolve(rtpCapabilities);
}
);
});
});
};
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/hooks/mediasoup/useTransport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface TransportRef {
}

const useTransport = (socketRef: MutableRefObject<Socket | null>) => {
const { ticleId: roomId } = useParams({ from: '/live/$ticleId' });
const { ticleId: roomId } = useParams({ from: '/_authenticated/live/$ticleId' });

const transportsRef = useRef<TransportRef>({
sendTransport: null,
Expand Down
40 changes: 40 additions & 0 deletions apps/web/src/hooks/useAuthInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useEffect } from 'react';

import useAuthStore, { AuthInfo } from '@/stores/useAuthStore';

import { useUserProfileOfMe } from './api/user';

interface AuthInfoReturn {
isLoading: boolean;
isAuthenticated: boolean;
authInfo: AuthInfo | null;
}

const useAuthInfo = (): AuthInfoReturn => {
const { data: user, isError } = useUserProfileOfMe();
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
const authInfo = useAuthStore((state) => state.authInfo);
const setAuthInfo = useAuthStore((state) => state.setAuthInfo);
const clearAuthState = useAuthStore((state) => state.clearAuthState);

useEffect(() => {
if (!user) {
clearAuthState();
return;
}

setAuthInfo({
nickname: user.nickname,
profileImageUrl: user.profileImageUrl,
provider: user.provider,
});
}, [user, isError, setAuthInfo, clearAuthState]);

return {
isLoading: !user && !isError,
isAuthenticated,
authInfo,
};
};

export default useAuthInfo;
6 changes: 5 additions & 1 deletion apps/web/src/hooks/usePagination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useEffect, useMemo, useState } from 'react';
import { StreamData } from '@/components/live/StreamView';
import { useLocalStreamState } from '@/contexts/localStream/context';
import { useRemoteStreamState } from '@/contexts/remoteStream/context';
import useAuthStore from '@/stores/useAuthStore';

interface PaginationParams {
itemsPerPage: number;
Expand All @@ -11,6 +12,7 @@ interface PaginationParams {
const usePagination = ({ itemsPerPage }: PaginationParams) => {
const { video, screen } = useLocalStreamState();
const { videoStreams } = useRemoteStreamState();
const nickname = useAuthStore.getState().authInfo?.nickname;

const [currentPage, setCurrentPage] = useState(0);

Expand All @@ -23,6 +25,7 @@ const usePagination = ({ itemsPerPage }: PaginationParams) => {
kind: 'video',
stream: screen.stream,
paused: false,
nickname: nickname ?? '',
});
}

Expand All @@ -32,14 +35,15 @@ const usePagination = ({ itemsPerPage }: PaginationParams) => {
kind: 'video',
stream: video.stream,
paused: video.paused,
nickname: nickname ?? '',
});
}

const startIdx = currentPage * itemsPerPage;
const endIdx = startIdx + itemsPerPage;

return totalItems.slice(startIdx, endIdx);
}, [videoStreams, currentPage, itemsPerPage, video, screen]);
}, [videoStreams, currentPage, itemsPerPage, video, screen, nickname]);

const onNextPage = () => {
setCurrentPage((prev) => Math.min(totalPages - 1, prev + 1));
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/hooks/usePinnedVideo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ const usePinnedVideo = () => {
kind: 'video',
stream: pinnedStream.stream,
paused: pinnedStream.paused,
nickname: pinnedVideoStreamData.nickname,
});
}, [video, screen, audio]);

Expand Down
Loading
Loading