From 54feb244d6355ba4981fb34d2a08ad39e4907f0c Mon Sep 17 00:00:00 2001 From: choigeon96 Date: Mon, 12 Dec 2022 22:45:58 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=EC=B1=84=ED=8C=85=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chat-server/src/common/schemas/chat.schema.ts | 2 +- chat-server/src/socket.gateway.ts | 16 +++-- chat-server/src/socket.service.ts | 18 +++-- client/src/App.tsx | 1 + client/src/assets/icons/index.ts | 3 + client/src/assets/icons/send_icon.svg | 9 +++ client/src/components/Chat/Chat.tsx | 54 +++++++++++++++ .../components/Chat/ChatInput/ChatInput.tsx | 57 ++++++++++++++++ .../src/components/Chat/ChatItem/ChatItem.tsx | 66 +++++++++++++++++++ .../src/components/Chat/ChatList/ChatList.tsx | 38 +++++++++++ .../Chat/ChatRoomSummary/ChatRoomSummary.tsx | 37 +++++++++++ client/src/components/FilterBar/FilterBar.tsx | 4 +- client/src/constants/socketEvents.ts | 6 ++ client/src/constants/styles.ts | 1 + client/src/hooks/queries/useRefreshQuery.ts | 1 - client/src/pages/Chat/ChatPage.tsx | 13 ++++ client/src/pages/index.tsx | 1 + client/src/styles/color.ts | 1 + client/src/types/Chat.ts | 6 ++ 19 files changed, 320 insertions(+), 14 deletions(-) create mode 100644 client/src/assets/icons/send_icon.svg create mode 100644 client/src/components/Chat/Chat.tsx create mode 100644 client/src/components/Chat/ChatInput/ChatInput.tsx create mode 100644 client/src/components/Chat/ChatItem/ChatItem.tsx create mode 100644 client/src/components/Chat/ChatList/ChatList.tsx create mode 100644 client/src/components/Chat/ChatRoomSummary/ChatRoomSummary.tsx create mode 100644 client/src/constants/socketEvents.ts create mode 100644 client/src/constants/styles.ts create mode 100644 client/src/pages/Chat/ChatPage.tsx create mode 100644 client/src/types/Chat.ts diff --git a/chat-server/src/common/schemas/chat.schema.ts b/chat-server/src/common/schemas/chat.schema.ts index 1577e82..746204d 100644 --- a/chat-server/src/common/schemas/chat.schema.ts +++ b/chat-server/src/common/schemas/chat.schema.ts @@ -14,7 +14,7 @@ export class Chat { @Prop() content: string; - @Prop({ type: Date }) + @Prop({ default: new Date() }) createdAt: Date; } diff --git a/chat-server/src/socket.gateway.ts b/chat-server/src/socket.gateway.ts index c771a9c..dc92c16 100644 --- a/chat-server/src/socket.gateway.ts +++ b/chat-server/src/socket.gateway.ts @@ -13,7 +13,8 @@ import { SocketService } from './socket.service'; @WebSocketGateway({ namespace: 'chat', cors: { - origin: ['http://localhost:3000'], + origin: '*', + credential: true, }, }) export class SocketGateway implements OnGatewayDisconnect { @@ -27,9 +28,9 @@ export class SocketGateway implements OnGatewayDisconnect { @ConnectedSocket() socket: Socket, ): Promise { const { recruitId } = data; - socket.join(recruitId); // room에 입장 + socket.join(recruitId.toString()); // room에 입장 await this.socketService.setCacheData(socket.id, data); - const recentMsg = await this.socketService.getRecentMessage(); + const recentMsg = await this.socketService.getRecentMessage(recruitId); socket.emit('server_sent_recent', recentMsg); } @@ -46,11 +47,16 @@ export class SocketGateway implements OnGatewayDisconnect { chat.sender = userId; chat.recruitId = recruitId; chat.content = content; - await this.socketService.saveRecentMessage(chat); - this.server.emit('server_sent', chat); + chat.createdAt = new Date(); + this.server + .in(recruitId.toString()) + .emit('server_sent', await this.socketService.saveRecentMessage(chat)); } async handleDisconnect(@ConnectedSocket() socket: Socket): Promise { await this.socketService.delCacheData(socket.id); } + async handleConnection(@ConnectedSocket() socket: Socket): Promise { + await this.socketService.delCacheData(socket.id); + } } diff --git a/chat-server/src/socket.service.ts b/chat-server/src/socket.service.ts index 9dd3459..2a4f4e6 100644 --- a/chat-server/src/socket.service.ts +++ b/chat-server/src/socket.service.ts @@ -27,15 +27,23 @@ export class SocketService { return this.cacheManager.del(`id:${socketId}`); } - async getRecentMessage() { + async getRecentMessage(recruitId: number) { const response = await this.chatModel - .find() - .sort({ createdAt: -1 }) - .limit(10); + .find({ recruitId }) + .sort({ createdAt: -1 }); + // .limit(10); return response.reverse(); } async saveRecentMessage(chatEntity: Chat) { - this.chatModel.create(chatEntity); + return this.chatModel.create(chatEntity); + } + + async getLatestMessage(recruitId: number) { + const response = await this.chatModel + .find({ recruitId }) + .sort({ createdAt: -1 }) + .limit(1); + return response[0]; } } diff --git a/client/src/App.tsx b/client/src/App.tsx index 952b0c4..f0653b3 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -23,6 +23,7 @@ function App() { } /> + } /> } /> diff --git a/client/src/assets/icons/index.ts b/client/src/assets/icons/index.ts index 1ddbaa8..9616137 100644 --- a/client/src/assets/icons/index.ts +++ b/client/src/assets/icons/index.ts @@ -18,6 +18,8 @@ import CLOCK_ICON from "./clock_icon.svg"; import START_ICON from "./start_icon.svg"; import ARRIVE_ICON from "./arrive_icon.svg"; import MY_POSITION_ICON from "./my_position_icon.svg"; +import SEND_ICON from "./send_icon.svg"; + export { USER_CIRCLE_ICON, ARROW_LEFT_ICON, @@ -39,4 +41,5 @@ export { START_ICON, ARRIVE_ICON, MY_POSITION_ICON, + SEND_ICON, }; diff --git a/client/src/assets/icons/send_icon.svg b/client/src/assets/icons/send_icon.svg new file mode 100644 index 0000000..17c354c --- /dev/null +++ b/client/src/assets/icons/send_icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/client/src/components/Chat/Chat.tsx b/client/src/components/Chat/Chat.tsx new file mode 100644 index 0000000..6f2b367 --- /dev/null +++ b/client/src/components/Chat/Chat.tsx @@ -0,0 +1,54 @@ +import { userState } from "#atoms/userState"; +import { SOCKET_EVENT } from "#constants/socketEvents"; +import { HEADER_HEIGHT } from "#constants/styles"; +import { ChatResponse } from "#types/Chat"; +import { JUSTIFY_CONTENT } from "#types/flexOptions"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { useParams } from "react-router-dom"; +import { useRecoilValue } from "recoil"; +import { io, Socket } from "socket.io-client"; +import styled from "styled-components"; +import { flexColumn } from "styles/flex"; +import ChatInput from "./ChatInput/ChatInput"; +import ChatList from "./ChatList/ChatList"; +import ChatRoomSummary from "./ChatRoomSummary/ChatRoomSummary"; + +const ChatContainer = styled.div` + ${flexColumn({ justifyContent: JUSTIFY_CONTENT.SPACE_BETWEEN })}; + width: 100%; + height: calc(100vh - ${HEADER_HEIGHT}); +`; + +const Chat = () => { + const { userId } = useRecoilValue(userState); + const [chatList, setChatList] = useState([]); + const { id } = useParams(); + const socketRef = useRef(null); + + useEffect(() => { + if (!userId) return; + const socket = io(import.meta.env.VITE_CHAT_URL, { autoConnect: true, reconnection: false }); + socket.emit(SOCKET_EVENT.JOIN, { userId, recruitId: Number(id) }); + socket.on(SOCKET_EVENT.SERVER_SENT_RECENT, setChatList); + socket.on(SOCKET_EVENT.SERVER_SENT, (data) => setChatList((prev) => [...prev, data])); + socketRef.current = socket; + }, [userId]); + + const sendMessage = useCallback( + (content: string) => { + if (!socketRef.current) return; + socketRef.current.emit(SOCKET_EVENT.CLIENT_SENT, { content }); + }, + [socketRef], + ); + + return ( + + + + + + ); +}; + +export default Chat; diff --git a/client/src/components/Chat/ChatInput/ChatInput.tsx b/client/src/components/Chat/ChatInput/ChatInput.tsx new file mode 100644 index 0000000..0ee6745 --- /dev/null +++ b/client/src/components/Chat/ChatInput/ChatInput.tsx @@ -0,0 +1,57 @@ +import { SEND_ICON } from "#assets/icons"; +import { ALIGN_ITEMS } from "#types/flexOptions"; +import { FormEventHandler, useCallback, useRef } from "react"; +import styled from "styled-components"; +import { COLOR } from "styles/color"; +import { flexRow } from "styles/flex"; + +const ChatInputForm = styled.form` + ${flexRow({ alignItems: ALIGN_ITEMS.CENTER })}; + background-color: ${COLOR.F1F4F7}; + width: 100%; + padding: 8px 20px; + input { + border: none; + padding: 10px; + width: 100%; + :focus { + outline: none; + } + } + button { + background-color: ${COLOR.WHITE}; + border: none; + height: 100%; + padding: 10px; + } +`; + +interface ChatInputProps { + sendMessage: (message: string) => void; +} + +const ChatInput = ({ sendMessage }: ChatInputProps) => { + const inputRef = useRef(null); + + const onClickSend = useCallback(() => { + if (!inputRef.current || !inputRef.current.value) return; + sendMessage(inputRef.current.value); + inputRef.current.value = ""; + }, [inputRef]); + + const onSubmitForm: FormEventHandler = useCallback((e) => { + onClickSend(); + e.preventDefault(); + }, []); + + return ( + + + + + ); +}; + +export default ChatInput; diff --git a/client/src/components/Chat/ChatItem/ChatItem.tsx b/client/src/components/Chat/ChatItem/ChatItem.tsx new file mode 100644 index 0000000..216548a --- /dev/null +++ b/client/src/components/Chat/ChatItem/ChatItem.tsx @@ -0,0 +1,66 @@ +import { userState } from "#atoms/userState"; +import { ChatResponse } from "#types/Chat"; +import { ALIGN_ITEMS, JUSTIFY_CONTENT } from "#types/flexOptions"; +import { timeDifference } from "#utils/cardUtils"; +import { useRecoilValue } from "recoil"; +import styled from "styled-components"; +import { COLOR } from "styles/color"; +import { flexColumn, flexRow } from "styles/flex"; +import { fontXSmall } from "styles/font"; + +interface ChatItemProps { + data: ChatResponse; +} + +const ChatItemContainer = styled.div<{ isMine: boolean }>` + width: 100%; + ${flexRow({ alignItems: ALIGN_ITEMS.FLEX_END })}; + justify-content: ${({ isMine }) => (isMine ? JUSTIFY_CONTENT.RIGHT : JUSTIFY_CONTENT.LEFT)}; + flex-direction: ${({ isMine }) => (isMine ? "row-reverse" : "row")}; + margin-top: 15px; + > span { + ${fontXSmall(COLOR.BLACK, 400)} + margin:0 6px; + } +`; + +const ContentWrapper = styled.div<{ isMine: boolean }>` + ${flexColumn({})}; + background-color: ${({ isMine }) => (isMine ? COLOR.PRIMARY : COLOR.F1F4F7)}; + color: ${({ isMine }) => (isMine ? COLOR.WHITE : COLOR.BLACK)}; + padding: 8px 16px; + border-radius: 15px; + font-size: 1.4rem; + max-width: 50%; + span { + white-space: normal; + word-break: break-word; + word-wrap: break-word; + } + span:not(:last-child) { + font-weight: ${700}; + } + span:last-child { + font-weight: ${400}; + } +`; + +const ChatItem = ({ data }: ChatItemProps) => { + const { userId } = useRecoilValue(userState); + const isMine = data.sender === userId; + return ( + + + {userId !== data.sender && {data.sender}} + {data.content} + + + {timeDifference( + new Date(new Date(data.createdAt).getTime() + new Date().getTimezoneOffset() * 60000).toISOString(), + )} + + + ); +}; + +export default ChatItem; diff --git a/client/src/components/Chat/ChatList/ChatList.tsx b/client/src/components/Chat/ChatList/ChatList.tsx new file mode 100644 index 0000000..1c1a8b1 --- /dev/null +++ b/client/src/components/Chat/ChatList/ChatList.tsx @@ -0,0 +1,38 @@ +import { ChatResponse } from "#types/Chat"; +import { useEffect, useRef } from "react"; +import styled from "styled-components"; +import ChatItem from "../ChatItem/ChatItem"; + +const ChatListContainer = styled.div` + padding: 15px; + width: 100%; + height: inherit; + overflow: scroll; + -ms-overflow-style: none; + scrollbar-width: none; + ::-webkit-scrollbar { + display: none; + } +`; + +interface ChatListProps { + data: ChatResponse[]; +} + +const ChatList = ({ data }: ChatListProps) => { + const scrollRef = useRef(null); + + useEffect(() => { + scrollRef.current?.scrollTo(0, scrollRef.current.scrollHeight); + }, [data]); + + return ( + + {data.map((el, idx) => ( + + ))} + + ); +}; + +export default ChatList; diff --git a/client/src/components/Chat/ChatRoomSummary/ChatRoomSummary.tsx b/client/src/components/Chat/ChatRoomSummary/ChatRoomSummary.tsx new file mode 100644 index 0000000..0de8551 --- /dev/null +++ b/client/src/components/Chat/ChatRoomSummary/ChatRoomSummary.tsx @@ -0,0 +1,37 @@ +import useRecruitDetailQuery from "#hooks/queries/useRecruitDetailQuery"; +import styled from "styled-components"; +import { COLOR } from "styles/color"; +import { fontMedium } from "styles/font"; +const SummaryWrapper = styled.div` + width: 100%; + padding: 15px 20px; + background-color: ${COLOR.F1F4F7}; + border-radius: 0 0 16px 16px; + box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.25); + z-index: 30; + h2 { + margin-bottom: 4px; + } + li { + ${fontMedium(COLOR.BLACK, 500)}; + margin-bottom: 2px; + list-style: none; + } +`; + +const ChatRoomSummary = ({ id }: { id: number }) => { + const { data } = useRecruitDetailQuery(id); + + return ( + +

{data?.title}

+
    +
  • 집합 일시: {(data?.startTime ? new Date(data?.startTime) : new Date()).toLocaleString()}
  • +
  • 출발점: {data?.hDong.name}
  • +
  • 총거리: {((data?.pathLength || 0) / 1000).toFixed(2)}km
  • +
+
+ ); +}; + +export default ChatRoomSummary; diff --git a/client/src/components/FilterBar/FilterBar.tsx b/client/src/components/FilterBar/FilterBar.tsx index 392c45a..f4bb746 100644 --- a/client/src/components/FilterBar/FilterBar.tsx +++ b/client/src/components/FilterBar/FilterBar.tsx @@ -10,8 +10,8 @@ const FilterBarWrapper = styled.div` border-bottom: ${`1px solid ${COLOR.BABY_BLUE}`}; ::-webkit-scrollbar { height: 0; - width: 0; /* Remove scrollbar space */ - background: transparent; /* Optional: just make scrollbar invisible */ + width: 0; + background: transparent; } `; diff --git a/client/src/constants/socketEvents.ts b/client/src/constants/socketEvents.ts new file mode 100644 index 0000000..1fc562f --- /dev/null +++ b/client/src/constants/socketEvents.ts @@ -0,0 +1,6 @@ +export enum SOCKET_EVENT { + JOIN = "join", + SERVER_SENT = "server_sent", + CLIENT_SENT = "client_sent", + SERVER_SENT_RECENT = "server_sent_recent", +} diff --git a/client/src/constants/styles.ts b/client/src/constants/styles.ts new file mode 100644 index 0000000..cc35fd5 --- /dev/null +++ b/client/src/constants/styles.ts @@ -0,0 +1 @@ +export const HEADER_HEIGHT = "57px"; diff --git a/client/src/hooks/queries/useRefreshQuery.ts b/client/src/hooks/queries/useRefreshQuery.ts index 50047b2..f8d18ae 100644 --- a/client/src/hooks/queries/useRefreshQuery.ts +++ b/client/src/hooks/queries/useRefreshQuery.ts @@ -29,7 +29,6 @@ const useRefreshQuery = () => { retry: !!(userInfo && userInfo.accessToken), refetchInterval: TIME.ACCESS_TOKEN_EXPIRE_TIME - TIME.MINUTE_IN_SECONDS, refetchOnReconnect: false, - suspense: true, }); }; diff --git a/client/src/pages/Chat/ChatPage.tsx b/client/src/pages/Chat/ChatPage.tsx new file mode 100644 index 0000000..54694f5 --- /dev/null +++ b/client/src/pages/Chat/ChatPage.tsx @@ -0,0 +1,13 @@ +import Chat from "#components/Chat/Chat"; +import Header from "#components/Header/Header"; + +const ChatPage = () => { + return ( + <> +
+ + + ); +}; + +export default ChatPage; diff --git a/client/src/pages/index.tsx b/client/src/pages/index.tsx index a7ef14b..d033bf5 100644 --- a/client/src/pages/index.tsx +++ b/client/src/pages/index.tsx @@ -1,5 +1,6 @@ import { lazy } from "react"; +export const ChatPage = lazy(() => import("#pages/Chat/ChatPage")); export const MainPage = lazy(() => import("#pages/Main/MainPage")); export const MyPage = lazy(() => import("#pages/MyPage/MyPage")); export const SignUp = lazy(() => import("#pages/SignUp/SignUp")); diff --git a/client/src/styles/color.ts b/client/src/styles/color.ts index 7f7ebf5..a923278 100644 --- a/client/src/styles/color.ts +++ b/client/src/styles/color.ts @@ -6,4 +6,5 @@ export enum COLOR { LIGHT_GRAY = "#808080", BABY_BLUE = "#ACB7C7", ORANGE = "#FFB800", + F1F4F7 = "#F1F4F7", } diff --git a/client/src/types/Chat.ts b/client/src/types/Chat.ts new file mode 100644 index 0000000..9fe633e --- /dev/null +++ b/client/src/types/Chat.ts @@ -0,0 +1,6 @@ +export interface ChatResponse { + sender: string; + recruitId: number; + content: string; + createdAt: string; +} From 95525934ab539a8ee33ab1f2305911a3ff7d2fb5 Mon Sep 17 00:00:00 2001 From: choigeon96 Date: Tue, 13 Dec 2022 02:17:03 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20=EC=B1=84=ED=8C=85=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC(=EC=9E=84=EC=8B=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +- client/src/App.tsx | 44 +++++++++---------- client/src/components/Chat/Chat.tsx | 2 +- .../RecruitContent/RecruitContent.styles.ts | 6 +++ .../RecruitContent/RecruitContent.tsx | 10 +++-- .../commons/ChatButton/ChatButton.tsx | 15 +++++++ client/src/hooks/queries/useRefreshQuery.ts | 1 + client/src/index.tsx | 7 ++- package.json | 26 +++++++++++ 9 files changed, 84 insertions(+), 31 deletions(-) create mode 100644 client/src/components/commons/ChatButton/ChatButton.tsx create mode 100644 package.json diff --git a/.gitignore b/.gitignore index 4c98bda..817b15e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ # dump -dump.* \ No newline at end of file +dump.* +package-lock.json +node_modules \ No newline at end of file diff --git a/client/src/App.tsx b/client/src/App.tsx index f0653b3..2117266 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,36 +1,32 @@ import { Route, Routes } from "react-router-dom"; import useRefreshQuery from "#hooks/queries/useRefreshQuery"; import Layout from "#components/Layout/Layout"; -import { Suspense } from "react"; -import Loading from "#components/commons/Loading/Loading"; import * as P from "#pages/index"; function App() { useRefreshQuery(); return ( - }> - - } /> - } /> - } /> - } /> - } /> - } /> - - } /> - } /> - - - } /> - } /> - - - } /> - } /> - - - + + } /> + } /> + } /> + } /> + } /> + } /> + + } /> + } /> + + + } /> + } /> + + + } /> + } /> + + ); } diff --git a/client/src/components/Chat/Chat.tsx b/client/src/components/Chat/Chat.tsx index 6f2b367..e48c859 100644 --- a/client/src/components/Chat/Chat.tsx +++ b/client/src/components/Chat/Chat.tsx @@ -3,7 +3,7 @@ import { SOCKET_EVENT } from "#constants/socketEvents"; import { HEADER_HEIGHT } from "#constants/styles"; import { ChatResponse } from "#types/Chat"; import { JUSTIFY_CONTENT } from "#types/flexOptions"; -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { useParams } from "react-router-dom"; import { useRecoilValue } from "recoil"; import { io, Socket } from "socket.io-client"; diff --git a/client/src/components/RecruitContent/RecruitContent.styles.ts b/client/src/components/RecruitContent/RecruitContent.styles.ts index 1cf0818..aeb9ab2 100644 --- a/client/src/components/RecruitContent/RecruitContent.styles.ts +++ b/client/src/components/RecruitContent/RecruitContent.styles.ts @@ -4,6 +4,7 @@ import { COLOR } from "styles/color"; import { flexColumn, flexRow } from "styles/flex"; export const Title = styled.div` + ${flexRow({ alignItems: ALIGN_ITEMS.CENTER, justifyContent: JUSTIFY_CONTENT.SPACE_BETWEEN })} box-shadow: 0px -4px 4px rgba(0, 0, 0, 0.25); font-size: 2rem; font-weight: bold; @@ -29,3 +30,8 @@ export const Content = styled.div` } } `; + +export const ButtonWrapper = styled.div` + justify-content: center !important; + gap: 8px; +`; diff --git a/client/src/components/RecruitContent/RecruitContent.tsx b/client/src/components/RecruitContent/RecruitContent.tsx index c285439..ee08dc0 100644 --- a/client/src/components/RecruitContent/RecruitContent.tsx +++ b/client/src/components/RecruitContent/RecruitContent.tsx @@ -1,9 +1,10 @@ import Button from "#components/Button/Button"; +import ChatButton from "#components/commons/ChatButton/ChatButton"; import { RecruitDetail } from "#types/RecruitDetail"; import { getPaceFormat } from "#utils/paceUtils"; import { getTimeFormat } from "#utils/stringUtils"; import { useCallback } from "react"; -import { Content, Title } from "./RecruitContent.styles"; +import { ButtonWrapper, Content, Title } from "./RecruitContent.styles"; interface RecruitContentProps { data: RecruitDetail; @@ -27,7 +28,7 @@ const RecruitContent = ({ onClick, data }: RecruitContentProps) => {
총거리 -

{data.pathLength}km

+

{(data.pathLength / 1000).toFixed(2)}km

페이스 @@ -47,7 +48,10 @@ const RecruitContent = ({ onClick, data }: RecruitContentProps) => { {data.currentPpl} / {data.maxPpl}

- + + + {data.isParticipating && } + ); diff --git a/client/src/components/commons/ChatButton/ChatButton.tsx b/client/src/components/commons/ChatButton/ChatButton.tsx new file mode 100644 index 0000000..bca06d5 --- /dev/null +++ b/client/src/components/commons/ChatButton/ChatButton.tsx @@ -0,0 +1,15 @@ +import Button from "#components/Button/Button"; +import { useNavigate, useParams } from "react-router-dom"; +import { COLOR } from "styles/color"; + +const ChatButton = () => { + const { id } = useParams(); + const navigate = useNavigate(); + return ( + + ); +}; + +export default ChatButton; diff --git a/client/src/hooks/queries/useRefreshQuery.ts b/client/src/hooks/queries/useRefreshQuery.ts index f8d18ae..50047b2 100644 --- a/client/src/hooks/queries/useRefreshQuery.ts +++ b/client/src/hooks/queries/useRefreshQuery.ts @@ -29,6 +29,7 @@ const useRefreshQuery = () => { retry: !!(userInfo && userInfo.accessToken), refetchInterval: TIME.ACCESS_TOKEN_EXPIRE_TIME - TIME.MINUTE_IN_SECONDS, refetchOnReconnect: false, + suspense: true, }); }; diff --git a/client/src/index.tsx b/client/src/index.tsx index 61e5104..5a22dc8 100644 --- a/client/src/index.tsx +++ b/client/src/index.tsx @@ -1,10 +1,11 @@ -import React from "react"; +import React, { Suspense } from "react"; import ReactDOM from "react-dom/client"; import "./index.css"; import App from "./App"; import { BrowserRouter } from "react-router-dom"; import { RecoilRoot } from "recoil"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import Loading from "#components/commons/Loading/Loading"; const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement); const queryClient = new QueryClient({ @@ -19,7 +20,9 @@ root.render( - + }> + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..37169c2 --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "name": "runwithme", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "redis": "redis-server", + "dev:api": "cd server && npm run start:dev", + "dev:chat": "cd chat-server && npm run start:dev", + "dev:noti": "cd noti-server && npm run start:dev", + "dev:client": "cd client && npm run start", + "dev": "concurrently \"npm run redis\" \"npm run dev:api\" \"npm run dev:client\" \"npm run dev:noti\" \"npm run dev:chat\"" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/boostcampwm-2022/WEB26.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/boostcampwm-2022/WEB26/issues" + }, + "homepage": "https://github.com/boostcampwm-2022/WEB26#readme", + "devDependencies": { + "concurrently": "^7.6.0" + } +} From b2a7662788a4e48be5de48f05089e68da3e8e60c Mon Sep 17 00:00:00 2001 From: choigeon96 Date: Tue, 13 Dec 2022 03:48:40 +0900 Subject: [PATCH 3/3] fix: clear build error --- client/package-lock.json | 135 +++++++++++++++++++++++++++++++++++++++ client/package.json | 1 + 2 files changed, 136 insertions(+) diff --git a/client/package-lock.json b/client/package-lock.json index 1f3b113..f8bb76a 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -31,6 +31,7 @@ "react-slick": "^0.29.0", "react-spinners": "^0.13.7", "recoil": "^0.7.6", + "socket.io-client": "^4.5.4", "styled-components": "^5.3.6", "web-vitals": "^2.1.4" }, @@ -4338,6 +4339,11 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, "node_modules/@storybook/addon-actions": { "version": "6.5.14", "dev": true, @@ -18163,6 +18169,46 @@ "objectorarray": "^1.0.5" } }, + "node_modules/engine.io-client": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.3.tgz", + "integrity": "sha512-aXPtgF1JS3RuuKcpSrBtimSjYvrbhKW9froICH4s0F3XQWLxsKNxqzG39nnvQZQnva4CMvUK63T7shevxRyYHw==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", + "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.10.0", "dev": true, @@ -31190,6 +31236,32 @@ "node": ">=0.10.0" } }, + "node_modules/socket.io-client": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.4.tgz", + "integrity": "sha512-ZpKteoA06RzkD32IbqILZ+Cnst4xewU7ZYK12aS1mzHftFFjpoMz69IuhP/nL25pJfao/amoPI527KnuhFm01g==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.2.3", + "socket.io-parser": "~4.2.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz", + "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/sockjs": { "version": "0.3.24", "dev": true, @@ -35244,6 +35316,14 @@ "license": "MIT", "peer": true }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "dev": true, @@ -37960,6 +38040,11 @@ "@sinonjs/commons": "^1.7.0" } }, + "@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, "@storybook/addon-actions": { "version": "6.5.14", "dev": true, @@ -47609,6 +47694,31 @@ "objectorarray": "^1.0.5" } }, + "engine.io-client": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.3.tgz", + "integrity": "sha512-aXPtgF1JS3RuuKcpSrBtimSjYvrbhKW9froICH4s0F3XQWLxsKNxqzG39nnvQZQnva4CMvUK63T7shevxRyYHw==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3", + "xmlhttprequest-ssl": "~2.0.0" + }, + "dependencies": { + "ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "requires": {} + } + } + }, + "engine.io-parser": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", + "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==" + }, "enhanced-resolve": { "version": "5.10.0", "dev": true, @@ -56055,6 +56165,26 @@ } } }, + "socket.io-client": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.4.tgz", + "integrity": "sha512-ZpKteoA06RzkD32IbqILZ+Cnst4xewU7ZYK12aS1mzHftFFjpoMz69IuhP/nL25pJfao/amoPI527KnuhFm01g==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.2.3", + "socket.io-parser": "~4.2.1" + } + }, + "socket.io-parser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz", + "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + } + }, "sockjs": { "version": "0.3.24", "dev": true, @@ -58837,6 +58967,11 @@ "dev": true, "peer": true }, + "xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==" + }, "xtend": { "version": "4.0.2", "dev": true diff --git a/client/package.json b/client/package.json index 5a87208..4612c32 100644 --- a/client/package.json +++ b/client/package.json @@ -26,6 +26,7 @@ "react-slick": "^0.29.0", "react-spinners": "^0.13.7", "recoil": "^0.7.6", + "socket.io-client": "^4.5.4", "styled-components": "^5.3.6", "web-vitals": "^2.1.4" },