Skip to content

Commit

Permalink
Merge pull request #293 from boostcampwm-2024/dev-fe
Browse files Browse the repository at this point in the history
[MERGE] dev-fe to dev
  • Loading branch information
hoeeeeeh authored Dec 30, 2024
2 parents da5f390 + 9b4971f commit ed7b0ee
Show file tree
Hide file tree
Showing 61 changed files with 1,441 additions and 610 deletions.
7 changes: 2 additions & 5 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { ThemeProvider } from 'styled-components';
import { theme } from './styles/theme';
import { MainPage, ClientPage, HostPage } from './pages';
import { ClientPage, ErrorPage, HostPage, MainPage, ReplayPage } from './pages';
import { QueryClientProvider } from '@tanstack/react-query';
import { queryClient } from '@apis/index';
import withUserId from '@hocs/withUserId';
import ReplayPage from '@pages/ReplayPage';

function AppComponent() {
return (
Expand All @@ -19,12 +18,10 @@ function AppComponent() {
>
<Routes>
<Route path="/" element={<MainPage />} />
<Route path="/live" element={<ClientPage />} />
<Route path="/live/:id" element={<ClientPage />} />
<Route path="/replay" element={<ReplayPage />} />
<Route path="/replay/:id" element={<ReplayPage />} />
<Route path="/host" element={<HostPage />} />
<Route path="/host/:id" element={<HostPage />} />
<Route path="*" element={<ErrorPage />} />
</Routes>
</Router>
</ThemeProvider>
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/apis/checkLiveExist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { AxiosResponse } from 'axios';
import { fetchInstance } from '.';
import { LiveExistenceResponse } from '@type/live';

export const checkLiveExist = async ({ liveId }: { liveId: string }): Promise<LiveExistenceResponse> => {
const response: AxiosResponse<LiveExistenceResponse> = await fetchInstance().get('/streams/existence', {
params: {
sessionKey: liveId
}
});

return response.data;
};
13 changes: 13 additions & 0 deletions frontend/src/apis/checkReplayExist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { AxiosResponse } from 'axios';
import { fetchInstance } from '.';
import { ReplayExistenceResponse } from '@type/replay';

export const checkReplayExist = async ({ videoId }: { videoId: string }): Promise<ReplayExistenceResponse> => {
const response: AxiosResponse<ReplayExistenceResponse> = await fetchInstance().get('/replay/existence', {
params: {
videoId
}
});

return response.data;
};
17 changes: 17 additions & 0 deletions frontend/src/apis/fetchChatRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { AxiosResponse } from 'axios';
import { fetchInstance } from '.';

export type ChatRuleResponse = {
notice: string;
channelName: string;
};

export const fetchChatRule = async ({ sessionKey }: { sessionKey: string }): Promise<ChatRuleResponse> => {
const response: AxiosResponse<ChatRuleResponse> = await fetchInstance().get('/streams/notice', {
params: {
sessionKey
}
});

return response.data;
};
25 changes: 13 additions & 12 deletions frontend/src/apis/fetchLive.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { AxiosResponse } from 'axios';
import { fetchInstance } from '.';
import { ClientLive } from '@type/live';
import { ClientLiveResponse } from '@type/live';

type ClientLiveResponse = {
info: ClientLive;
};

export const fetchLive = async ({ liveId }: { liveId: string }): Promise<ClientLive> => {
const response: AxiosResponse<ClientLiveResponse> = await fetchInstance().get('/streams/live', {
params: {
liveId
export const fetchLive = async ({ liveId }: { liveId: string }): Promise<ClientLiveResponse> => {
try {
const response: AxiosResponse = await fetchInstance().get('/streams/live', {
params: { liveId }
});
return response.data;
} catch (error: any) {
if (error.response && error.response.status === 400) {
console.log('error', error);
throw error;
}
});

return response.data.info;
throw error;
}
};
27 changes: 15 additions & 12 deletions frontend/src/apis/fetchReplay.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { AxiosResponse } from 'axios';
import { fetchInstance } from '.';
import { ReplayStream } from '@type/replay';
import { ClientReplayResponse } from '@type/replay';

type ReplayStreamResponse = {
info: ReplayStream;
};

export const fetchReplay = async ({ videoId }: { videoId: string }): Promise<ReplayStream> => {
const response: AxiosResponse<ReplayStreamResponse> = await fetchInstance().get('/replay/video', {
params: {
videoId
export const fetchReplay = async ({ videoId }: { videoId: string }): Promise<ClientReplayResponse> => {
try {
const response: AxiosResponse = await fetchInstance().get('/replay/video', {
params: {
videoId
}
});
return response.data;
} catch (error: any) {
if (error.response && error.response.status === 400) {
console.log('error', error);
throw error;
}
});

return response.data.info;
throw error;
}
};
10 changes: 10 additions & 0 deletions frontend/src/apis/queries/chat/useFetchChatRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { useQuery } from '@tanstack/react-query';
import { ChatRuleResponse, fetchChatRule } from '@apis/fetchChatRule';

export const useFetchChatRule = ({ sessionKey }: { sessionKey: string }) => {
return useQuery<ChatRuleResponse, Error>({
queryKey: ['chatRule'],
queryFn: () => fetchChatRule({ sessionKey }),
refetchOnWindowFocus: false
});
};
13 changes: 13 additions & 0 deletions frontend/src/apis/queries/client/useCheckLiveExist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useQuery } from '@tanstack/react-query';

import { checkLiveExist } from '@apis/checkLiveExist';
import { LiveExistenceResponse } from '@type/live';

export const useCheckLiveExist = ({ liveId }: { liveId: string }) => {
return useQuery<LiveExistenceResponse, Error>({
queryKey: ['checkLiveExist'],
queryFn: () => checkLiveExist({ liveId }),
refetchOnWindowFocus: false,
initialData: { existed: true }
});
};
12 changes: 7 additions & 5 deletions frontend/src/apis/queries/client/useFetchLive.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { useQuery } from '@tanstack/react-query';

import { fetchLive } from '@apis/fetchLive';
import { ClientLive } from '@type/live';
import { ClientLive, ClientLiveResponse } from '@type/live';

export const useClientLive = ({ liveId }: { liveId: string }) => {
return useQuery<ClientLive, Error>({
return useQuery<ClientLiveResponse, Error>({
queryKey: ['clientLive'],
queryFn: () => fetchLive({ liveId: liveId }),
refetchOnWindowFocus: false
queryFn: () => fetchLive({ liveId }),
refetchOnWindowFocus: false,
initialData: { info: {} as ClientLive },
throwOnError: true,
retry: 0,
});
};
2 changes: 1 addition & 1 deletion frontend/src/apis/queries/main/useFetchMainLive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ export const useMainLive = () => {
return useSuspenseQuery<MainLive[], Error>({
queryKey: ['mainLive'],
queryFn: fetchMainLive,
refetchOnWindowFocus: false,
refetchOnWindowFocus: false
});
};
13 changes: 13 additions & 0 deletions frontend/src/apis/queries/replay/useCheckReplayExist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useQuery } from '@tanstack/react-query';

import { checkReplayExist } from '@apis/checkReplayExist';
import { ReplayExistenceResponse } from '@type/replay';

export const useCheckReplayExist = ({ videoId }: { videoId: string }) => {
return useQuery<ReplayExistenceResponse, Error>({
queryKey: ['checkReplayExist'],
queryFn: () => checkReplayExist({ videoId }),
refetchOnWindowFocus: false,
initialData: { existed: true }
});
};
11 changes: 7 additions & 4 deletions frontend/src/apis/queries/replay/useFetchReplay.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { useQuery } from '@tanstack/react-query';

import { fetchReplay } from '@apis/fetchReplay';
import { ReplayStream } from '@type/replay';
import { ClientReplayResponse, ReplayStream } from '@type/replay';

export const useClientReplay = ({ videoId }: { videoId: string }) => {
return useQuery<ReplayStream, Error>({
return useQuery<ClientReplayResponse, Error>({
queryKey: ['clientReplay'],
queryFn: () => fetchReplay({ videoId: videoId }),
refetchOnWindowFocus: false
queryFn: () => fetchReplay({ videoId }),
refetchOnWindowFocus: false,
initialData: { info: {} as ReplayStream },
throwOnError: true,
retry: 0
});
};
3 changes: 3 additions & 0 deletions frontend/src/assets/icons/user-block.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/src/assets/icons/warning_icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 17 additions & 15 deletions frontend/src/components/chat/ChatInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import SpeechBubbleIcon from '@assets/icons/speech-bubble.svg';
import QuestionIcon from '@assets/icons/question.svg';
import SpeakerIcon from '@assets/icons/speaker.svg';
import SendIcon from '@assets/icons/send.svg';
import { useRef, useEffect, useState, ChangeEvent, KeyboardEvent, memo } from 'react';
import { useRef, useEffect, useState, KeyboardEvent, memo } from 'react';
import { CHATTING_SOCKET_SEND_EVENT, CHATTING_TYPES } from '@constants/chat';
import { ChattingTypes } from '@type/chat';
import { ChattingSendTypes } from '@type/chat';
import { getStoredId } from '@utils/id';
import { UserType } from '@type/user';

Expand All @@ -17,11 +17,10 @@ interface ChatInputProps {

const INITIAL_TEXTAREA_HEIGHT = 20;

export const ChatInput = ({ worker, userType, roomId }: ChatInputProps) => {
const ChatInput = ({ worker, userType, roomId }: ChatInputProps) => {
const [hasInput, setHasInput] = useState(false);
const [isFocused, setIsFocused] = useState(false);
const [msgType, setMsgType] = useState<ChattingTypes>(CHATTING_TYPES.NORMAL);
const [message, setMessage] = useState('');
const [msgType, setMsgType] = useState<ChattingSendTypes>(CHATTING_TYPES.NORMAL);
const textareaRef = useRef<HTMLTextAreaElement | null>(null);

const userId = getStoredId();
Expand All @@ -38,8 +37,9 @@ export const ChatInput = ({ worker, userType, roomId }: ChatInputProps) => {
};

const handleMessageSend = () => {
if (!worker || !message.trim()) return;
if (!worker || !textareaRef.current || !textareaRef.current.value.trim()) return;

const message = textareaRef.current.value.trim();
const eventMap = {
[CHATTING_TYPES.NORMAL]: CHATTING_SOCKET_SEND_EVENT.NORMAL,
[CHATTING_TYPES.QUESTION]: CHATTING_SOCKET_SEND_EVENT.QUESTION,
Expand All @@ -58,19 +58,21 @@ export const ChatInput = ({ worker, userType, roomId }: ChatInputProps) => {
});

resetTextareaHeight();
setMessage('');
textareaRef.current.value = '';
setHasInput(false);
};

const handleInputChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
const inputValue = e.target.value;
const handleInputChange = () => {
if (!textareaRef.current) return;

if (inputValue.length > 150) {
const value = textareaRef.current.value;

if (value.length > 150) {
textareaRef.current.value = value.slice(0, 150);
return;
}

setMessage(e.target.value);
setHasInput(e.target.value.length > 0);
setHasInput(value.length > 0);
};

const resetTextareaHeight = () => {
Expand Down Expand Up @@ -146,11 +148,10 @@ export const ChatInput = ({ worker, userType, roomId }: ChatInputProps) => {

<ChatInputArea
ref={textareaRef}
value={message}
onChange={handleInputChange}
placeholder={`${
msgType === CHATTING_TYPES.NORMAL ? '채팅을' : msgType === CHATTING_TYPES.QUESTION ? '질문을' : '공지를'
} 입력해주세요`}
onInput={handleInputChange}
onBlur={handleBlur}
onFocus={handleFocus}
onKeyDown={handleKeyDown}
Expand Down Expand Up @@ -185,6 +186,7 @@ const ChatInputWrapper = styled.div<{ $hasInput: boolean; $isFocused: boolean }>
const ChatInputArea = styled.textarea`
width: 100%;
min-height: 20px;
max-height: 40px;
scrollbar-width: none;
resize: none;
border: none;
Expand All @@ -193,7 +195,7 @@ const ChatInputArea = styled.textarea`
${({ theme }) => theme.tokenTypographys['display-medium16']};
background-color: transparent;
white-space: normal;
line-height: 20px;
line-height: 23px;
`;

const InputBtn = styled.button`
Expand Down
Loading

0 comments on commit ed7b0ee

Please sign in to comment.