From 2c37aac91d5f059cf39dddd4f81e05864c386700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=B1=EC=A3=BC?= Date: Sun, 12 Apr 2026 19:26:39 +0900 Subject: [PATCH 1/7] =?UTF-8?q?feat:=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20=EB=B0=8F=20=EC=B6=94=EA=B0=80=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 4 + src/apis/chat/entity.ts | 52 ++++++ src/apis/chat/index.ts | 28 +++ src/apis/chat/mutations.ts | 7 + src/apis/chat/queries.ts | 16 +- src/assets/svg/check_color.svg | 3 + .../Header/components/ChatAddHeader.tsx | 23 +++ .../Header/components/ChatListHeader.tsx | 6 +- .../Header/components/ChatSearchHeader.tsx | 18 ++ src/pages/Chat/AddChatRoom.tsx | 137 ++++++++++++++ src/pages/Chat/ChatSearch.tsx | 169 ++++++++++++++++++ src/pages/Chat/hooks/useChat.ts | 4 + src/pages/Chat/hooks/useChatMutations.ts | 11 ++ src/pages/Chat/index.tsx | 2 +- 14 files changed, 475 insertions(+), 5 deletions(-) create mode 100644 src/assets/svg/check_color.svg create mode 100644 src/components/layout/Header/components/ChatAddHeader.tsx create mode 100644 src/components/layout/Header/components/ChatSearchHeader.tsx create mode 100644 src/pages/Chat/AddChatRoom.tsx create mode 100644 src/pages/Chat/ChatSearch.tsx diff --git a/src/App.tsx b/src/App.tsx index 124be5c..913d595 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -17,6 +17,8 @@ const StudentIdStep = lazy(() => import('./pages/Auth/SignUp/StudentIdStep')); const TermStep = lazy(() => import('./pages/Auth/SignUp/TermStep')); const UniversityStep = lazy(() => import('./pages/Auth/SignUp/UniversityStep')); const ChatListPage = lazy(() => import('./pages/Chat')); +const ChatSearch = lazy(() => import('./pages/Chat/ChatSearch')); +const ChatAdd = lazy(() => import('./pages/Chat/AddChatRoom')); const ChatRoom = lazy(() => import('./pages/Chat/ChatRoom')); const ApplicationPage = lazy(() => import('./pages/Club/Application')); const ApplyCompletePage = lazy(() => import('./pages/Club/Application/applyCompletePage')); @@ -120,6 +122,8 @@ function App() { } /> } /> } /> + } /> + } /> } /> diff --git a/src/apis/chat/entity.ts b/src/apis/chat/entity.ts index f6a3f82..93abee9 100644 --- a/src/apis/chat/entity.ts +++ b/src/apis/chat/entity.ts @@ -1,6 +1,7 @@ import type { PaginationParams, PaginationResponse } from '../common/pagination'; export type ChatType = 'DIRECT' | 'CLUB_GROUP' | 'GROUP' | 'INQUIRY'; +export type SortBy = 'CLUB' | 'NAME'; export interface Room { roomId: number; @@ -45,3 +46,54 @@ export interface ChatMessagesResponse extends PaginationResponse { export interface CreateChatRoomResponse { chatRoomId: number; } + +export interface Messages { + roomId: number; + chatType: ChatType; + roomName: string; + roomImageUrl: string; + matchedMessage: string; + matchedMessageSentAt: string; +} + +export interface RoomMatched extends PaginationResponse { + rooms?: Room[]; +} + +export interface MessageMatched extends PaginationResponse { + messages?: Messages[]; +} + +export interface MatchedRequestParams extends PaginationParams { + keyword: string; +} + +export interface MatchResponse { + roomMatches?: RoomMatched; + messageMatches?: MessageMatched; +} + +export interface User { + userId: number; + name: string; + imageUrl: string; + studentNumber: string; +} + +export interface Section { + clubId: number; + clubName: string; + users: User[]; +} + +export interface InvitableFriendRequestParams extends PaginationParams { + query: string; + sortBy: SortBy; +} + +export interface InvitableFriend extends PaginationResponse { + sortBy: SortBy; + grouped: boolean; + users?: User[]; + sections?: Section[]; +} diff --git a/src/apis/chat/index.ts b/src/apis/chat/index.ts index 8158c3e..5eae7fe 100644 --- a/src/apis/chat/index.ts +++ b/src/apis/chat/index.ts @@ -6,6 +6,10 @@ import type { ChatRoomsResponse, CreateChatRoomResponse, SendChatMessageRequest, + MatchResponse, + MatchedRequestParams, + InvitableFriendRequestParams, + InvitableFriend, } from './entity'; export const getChatRooms = async () => { @@ -23,6 +27,14 @@ export const postChatRooms = async (userId: number) => { return response; }; +export const postChatRoomsGroup = async (userIds: number[]) => { + const response = await apiClient.post('chats/rooms/group', { + body: { userIds }, + requiresAuth: true, + }); + return response; +}; + export const postChatMessage = async ({ chatRoomId, content }: SendChatMessageRequest) => { return apiClient.post(`chats/rooms/${chatRoomId}/messages`, { body: { content }, @@ -64,3 +76,19 @@ export const deleteChatRoom = async (chatRoomId: number) => { }); return response; }; + +export const getSearchChat = async ({ ...query }: MatchedRequestParams) => { + const response = await apiClient.get(`chats/rooms/search`, { + params: query, + requiresAuth: true, + }); + return response; +}; + +export const getInvitableFriends = async ({ ...query }: InvitableFriendRequestParams) => { + const response = await apiClient.get('chats/rooms/invitables', { + params: query, + requiresAuth: true, + }); + return response; +}; diff --git a/src/apis/chat/mutations.ts b/src/apis/chat/mutations.ts index 49885d1..f162b6a 100644 --- a/src/apis/chat/mutations.ts +++ b/src/apis/chat/mutations.ts @@ -1,6 +1,7 @@ import { mutationOptions } from '@tanstack/react-query'; import { patchChatRoomName, + postChatRoomsGroup, postAdminChatRoom, postChatMessage, postChatMute, @@ -11,6 +12,7 @@ import { export const chatMutationKeys = { createRoom: () => ['chat', 'createRoom'] as const, createAdminRoom: () => ['chat', 'createAdminRoom'] as const, + createRoomGroup: () => ['chat', 'createRoomGroup'] as const, sendMessage: () => ['chat', 'sendMessage'] as const, toggleMute: (chatRoomId?: number) => ['chat', 'toggleMute', chatRoomId ?? 'unknown'] as const, updateRoomName: () => ['chat', 'updateRoomName'] as const, @@ -28,6 +30,11 @@ export const chatMutations = { mutationKey: chatMutationKeys.createAdminRoom(), mutationFn: postAdminChatRoom, }), + createRoomGroup: () => + mutationOptions({ + mutationKey: chatMutationKeys.createRoomGroup(), + mutationFn: postChatRoomsGroup, + }), sendMessage: () => mutationOptions({ mutationKey: chatMutationKeys.sendMessage(), diff --git a/src/apis/chat/queries.ts b/src/apis/chat/queries.ts index 2ac95de..831dd34 100644 --- a/src/apis/chat/queries.ts +++ b/src/apis/chat/queries.ts @@ -1,12 +1,14 @@ import { infiniteQueryOptions, queryOptions } from '@tanstack/react-query'; -import type { ChatMessagesResponse } from './entity'; -import { getChatMessages, getChatRooms } from '.'; +import type { ChatMessagesResponse, SortBy } from './entity'; +import { getChatMessages, getChatRooms, getSearchChat, getInvitableFriends } from '.'; export const chatQueryKeys = { all: ['chat'] as const, rooms: () => [...chatQueryKeys.all, 'rooms'] as const, messages: (chatRoomId: number) => [...chatQueryKeys.all, 'messages', chatRoomId] as const, disabledMessages: () => [...chatQueryKeys.all, 'messages', 'disabled'] as const, + search: (keyword: string) => [...chatQueryKeys.all, 'search', keyword], + invite: (query: string, sortBy: SortBy) => [...chatQueryKeys.all, 'invite', query, sortBy], }; export const chatQueries = { @@ -29,4 +31,14 @@ export const chatQueries = { lastPage.currentPage < lastPage.totalPage ? lastPage.currentPage + 1 : undefined, enabled: Boolean(chatRoomId), }), + search: (keyword: string) => + queryOptions({ + queryKey: chatQueryKeys.search(keyword), + queryFn: () => getSearchChat({ keyword, page: 1, limit: 20 }), + }), + invite: (query: string, sortBy: SortBy) => + queryOptions({ + queryKey: chatQueryKeys.invite(query, sortBy), + queryFn: () => getInvitableFriends({ query, sortBy, page: 1, limit: 20 }), + }), }; diff --git a/src/assets/svg/check_color.svg b/src/assets/svg/check_color.svg new file mode 100644 index 0000000..27b5d41 --- /dev/null +++ b/src/assets/svg/check_color.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/layout/Header/components/ChatAddHeader.tsx b/src/components/layout/Header/components/ChatAddHeader.tsx new file mode 100644 index 0000000..a7a2992 --- /dev/null +++ b/src/components/layout/Header/components/ChatAddHeader.tsx @@ -0,0 +1,23 @@ +import BackTitleHeader from '@/components/layout/Header/components/BackTitleHeader'; + +interface ChatListHeaderProps { + title: string; + onConfirm: () => void; +} + +export default function ChatAddHeader({ title, onConfirm }: ChatListHeaderProps) { + const ringSlot = ( + + ); + + return ( + + ); +} diff --git a/src/components/layout/Header/components/ChatListHeader.tsx b/src/components/layout/Header/components/ChatListHeader.tsx index 7f299a9..3999559 100644 --- a/src/components/layout/Header/components/ChatListHeader.tsx +++ b/src/components/layout/Header/components/ChatListHeader.tsx @@ -1,4 +1,5 @@ import type { Ref } from 'react'; +import { useNavigate } from 'react-router-dom'; import AddCircle from '@/assets/svg/add_circle.svg'; import Search from '@/assets/svg/big-search-icon.svg'; import BackTitleHeader from '@/components/layout/Header/components/BackTitleHeader'; @@ -9,12 +10,13 @@ interface ChatListHeaderProps { } export default function ChatListHeader({ title, headerRef }: ChatListHeaderProps) { + const navigate = useNavigate(); const rightSlot = (
- -
diff --git a/src/components/layout/Header/components/ChatSearchHeader.tsx b/src/components/layout/Header/components/ChatSearchHeader.tsx new file mode 100644 index 0000000..d433552 --- /dev/null +++ b/src/components/layout/Header/components/ChatSearchHeader.tsx @@ -0,0 +1,18 @@ +import type { Ref } from 'react'; +import BackTitleHeader from '@/components/layout/Header/components/BackTitleHeader'; + +interface ChatListHeaderProps { + title: string; + headerRef?: Ref; +} + +export default function ChatSearchHeader({ title, headerRef }: ChatListHeaderProps) { + return ( + + ); +} diff --git a/src/pages/Chat/AddChatRoom.tsx b/src/pages/Chat/AddChatRoom.tsx new file mode 100644 index 0000000..c2f1206 --- /dev/null +++ b/src/pages/Chat/AddChatRoom.tsx @@ -0,0 +1,137 @@ +import { Fragment, useState } from 'react'; +import { useQuery } from '@tanstack/react-query'; +import { useNavigate } from 'react-router-dom'; +import type { User, Section, SortBy } from '@/apis/chat/entity'; +import { chatQueries } from '@/apis/chat/queries'; +import Search from '@/assets/svg/big-search-icon.svg'; +import Check from '@/assets/svg/check_color.svg'; +import Dropdown from '@/components/common/Dropdown'; +import ChatAddHeader from '@/components/layout/Header/components/ChatAddHeader'; +import useDebouncedCallback from '@/utils/hooks/useDebounce'; +import useChat from './hooks/useChat'; + +type UserListProps = { + onToggle: (userId: number) => void; + selectedUserIds: Set; +}; + +function InvitableSectionList({ clubName, users, onToggle, selectedUserIds }: Section & UserListProps) { + return ( +
+ {clubName} + {users.map((user) => ( + +
onToggle(user.userId)}> + 프로필 +
+ + {user.name} ({user.studentNumber}) + +
+ {selectedUserIds.has(user.userId) && } +
+
+ ))} +
+ ); +} + +function InvitableUserList({ userId, name, imageUrl, studentNumber, onToggle, selectedUserIds }: User & UserListProps) { + return ( +
onToggle(userId)}> +
+ 프로필 +
+ + {name} ({studentNumber}) + +
+ {selectedUserIds.has(userId) && } +
+
+ ); +} + +export default function AddChatRoom() { + const title = '채팅방 추가'; + const [keyword, setKeyword] = useState(''); + const [debouncedQuery, setDebouncedQuery] = useState(''); + const [sortBy, setSortBy] = useState('CLUB'); + const [selectedUserIds, setSelectedUserIds] = useState>(new Set()); + + const SORT_OPTIONS = [ + { value: 'CLUB', label: '동아리' }, + { value: 'NAME', label: '이름' }, + ] as const; + + const navigate = useNavigate(); + const { createRoomGroup } = useChat(); + const { data } = useQuery({ + ...chatQueries.invite(debouncedQuery, sortBy), + }); + + const updateDebouncedQuery = useDebouncedCallback((value: string) => { + const trimmed = value.trim(); + setDebouncedQuery(trimmed); + }, 300); + const handleChange = (value: string) => { + setKeyword(value); + updateDebouncedQuery(value); + }; + const toggleUser = (userId: number) => { + setSelectedUserIds((prev) => { + const next = new Set(prev); + if (next.has(userId)) { + next.delete(userId); + } else { + next.add(userId); + } + return next; + }); + }; + + return ( +
+ { + const result = await createRoomGroup(Array.from(selectedUserIds)); + navigate(`/chats/${result.chatRoomId}`); + }} + /> + +
+
+ 친구 선택({data?.currentCount}) + setSortBy(value)} + /> +
+ {data?.sortBy === 'CLUB' + ? data?.sections?.map((section) => ( + + )) + : data?.users?.map((user) => ( + + ))} +
+
+ ); +} diff --git a/src/pages/Chat/ChatSearch.tsx b/src/pages/Chat/ChatSearch.tsx new file mode 100644 index 0000000..452c782 --- /dev/null +++ b/src/pages/Chat/ChatSearch.tsx @@ -0,0 +1,169 @@ +import { useState, Fragment } from 'react'; +import { useQuery } from '@tanstack/react-query'; +import { Link } from 'react-router-dom'; +import type { Messages, Room } from '@/apis/chat/entity'; +import { chatQueries } from '@/apis/chat/queries'; +import BellOffIcon from '@/assets/svg/bell-off.svg'; +import Search from '@/assets/svg/big-search-icon.svg'; +import PersonIcon from '@/assets/svg/person.svg'; +import ChatSearchHeader from '@/components/layout/Header/components/ChatSearchHeader'; +import useDebouncedCallback from '@/utils/hooks/useDebounce'; + +const DEFAULT_LAST_MESSAGE = '원하는 채팅을 찾아보세요'; + +const formatTime = (timeString: string) => { + const timeMatch = timeString.match(/(\d{1,2}):(\d{2})/); + + if (!timeMatch) { + return ''; + } + + const hour = Number(timeMatch[1]); + const minute = Number(timeMatch[2]); + const period = hour < 12 ? '오전' : '오후'; + const displayHour = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour; + + return `${period} ${displayHour}:${String(minute).padStart(2, '0')}`; +}; + +function ChatRoomAvatar({ roomImageUrl }: Pick) { + if (roomImageUrl) { + return ( + + ); + } + + return ( + + ); +} + +function RoomListItem({ room }: { room: Room }) { + const hasUnreadMessage = room.unreadCount > 0; + const previewMessage = room.lastMessage?.trim() || DEFAULT_LAST_MESSAGE; + + return ( + + +
+
+
+ {room.roomName} + {room.isMuted && } +
+ {room.lastSentAt && ( + + {formatTime(room.lastSentAt)} + + )} +
+
+

+ {previewMessage} +

+ {hasUnreadMessage && ( + + + + )} +
+
+ + ); +} + +function MessageListItem({ message, keyword }: { message: Messages; keyword: string }) { + const parts = message.matchedMessage.split(keyword); + return ( + + +
+
+
+ {message.roomName} +
+ {message.matchedMessageSentAt && ( + + {formatTime(message.matchedMessageSentAt)} + + )} +
+
+

+ {parts.map((part, index) => ( + + {part} + {index < parts.length - 1 && {keyword}} + + ))} +

+
+
+ + ); +} + +export default function ChatSearch() { + const [keyword, setKeyword] = useState(''); + const [debouncedQuery, setDebouncedQuery] = useState(''); + + const updateDebouncedQuery = useDebouncedCallback((value: string) => { + const trimmed = value.trim(); + setDebouncedQuery(trimmed); + }, 300); + + const handleChange = (value: string) => { + setKeyword(value); + updateDebouncedQuery(value); + }; + + const { data } = useQuery({ + ...chatQueries.search(debouncedQuery), + enabled: !!debouncedQuery, + }); + + const title = '채팅방 검색'; + return ( +
+ + + {debouncedQuery && ( +
+ {data?.roomMatches?.rooms?.map((room) => ( + + ))} + {data?.messageMatches?.messages?.map((message, index) => ( + + ))} +
+ )} +
+ ); +} diff --git a/src/pages/Chat/hooks/useChat.ts b/src/pages/Chat/hooks/useChat.ts index caa2151..da15325 100644 --- a/src/pages/Chat/hooks/useChat.ts +++ b/src/pages/Chat/hooks/useChat.ts @@ -3,6 +3,7 @@ import { chatQueries } from '@/apis/chat/queries'; import { clubQueries } from '@/apis/club/queries'; import { useCreateChatRoomMutation, + useCreateChatRoomGroupMutation, useSendChatMessageMutation, useToggleChatMuteMutation, useUpdateChatRoomNameMutation, @@ -17,6 +18,8 @@ const useChat = (chatRoomId?: number) => { const createChatRoomMutation = useCreateChatRoomMutation(); + const createRoomGroupMutation = useCreateChatRoomGroupMutation(); + const { data: chatMessagesData, fetchNextPage, @@ -46,6 +49,7 @@ const useChat = (chatRoomId?: number) => { return { chatRoomList, createChatRoom: createChatRoomMutation.mutateAsync, + createRoomGroup: createRoomGroupMutation.mutateAsync, isCreatingChatRoom: createChatRoomMutation.isPending, chatMessages: allMessages, fetchNextPage, diff --git a/src/pages/Chat/hooks/useChatMutations.ts b/src/pages/Chat/hooks/useChatMutations.ts index 821b8be..ee5d8cc 100644 --- a/src/pages/Chat/hooks/useChatMutations.ts +++ b/src/pages/Chat/hooks/useChatMutations.ts @@ -13,6 +13,17 @@ export const useCreateChatRoomMutation = () => { }); }; +export const useCreateChatRoomGroupMutation = () => { + const queryClient = useQueryClient(); + + return useMutation({ + ...chatMutations.createRoomGroup(), + onSuccess: async () => { + await queryClient.invalidateQueries({ queryKey: chatQueryKeys.rooms() }); + }, + }); +}; + export const useSendChatMessageMutation = () => { const queryClient = useQueryClient(); diff --git a/src/pages/Chat/index.tsx b/src/pages/Chat/index.tsx index fdc3abf..1b81f7c 100644 --- a/src/pages/Chat/index.tsx +++ b/src/pages/Chat/index.tsx @@ -224,7 +224,7 @@ function ChatListPage() { } setChangeRoomName(null); }; - + console.log(rooms); const contextMenuItems = (room: Room) => [ { label: '채팅방 이름 변경', From b1654378dff7e7268dcf45d3c047b5105f44450d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=B1=EC=A3=BC?= Date: Sun, 12 Apr 2026 22:09:47 +0900 Subject: [PATCH 2/7] =?UTF-8?q?fix:=20=EC=BD=94=EB=93=9C=EB=9E=98=EB=B9=97?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=EC=82=AC=ED=95=AD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/chat/entity.ts | 2 +- src/apis/chat/queries.ts | 4 +- .../Header/components/ChatRoomMoreHeader.tsx | 42 +++++++++++ src/pages/Chat/AddChatRoom.tsx | 20 +++-- src/pages/Chat/ChatRoomMore.tsx | 73 +++++++++++++++++++ src/pages/Chat/ChatSearch.tsx | 33 +++++---- src/pages/Chat/index.tsx | 2 +- 7 files changed, 150 insertions(+), 26 deletions(-) create mode 100644 src/components/layout/Header/components/ChatRoomMoreHeader.tsx create mode 100644 src/pages/Chat/ChatRoomMore.tsx diff --git a/src/apis/chat/entity.ts b/src/apis/chat/entity.ts index 93abee9..13d71cc 100644 --- a/src/apis/chat/entity.ts +++ b/src/apis/chat/entity.ts @@ -1,4 +1,4 @@ -import type { PaginationParams, PaginationResponse } from '../common/pagination'; +import type { PaginationParams, PaginationResponse } from '@/apis/common/pagination'; export type ChatType = 'DIRECT' | 'CLUB_GROUP' | 'GROUP' | 'INQUIRY'; export type SortBy = 'CLUB' | 'NAME'; diff --git a/src/apis/chat/queries.ts b/src/apis/chat/queries.ts index 831dd34..7e9218d 100644 --- a/src/apis/chat/queries.ts +++ b/src/apis/chat/queries.ts @@ -1,6 +1,6 @@ import { infiniteQueryOptions, queryOptions } from '@tanstack/react-query'; -import type { ChatMessagesResponse, SortBy } from './entity'; -import { getChatMessages, getChatRooms, getSearchChat, getInvitableFriends } from '.'; +import { getChatMessages, getChatRooms, getSearchChat, getInvitableFriends } from '@/apis/chat'; +import type { ChatMessagesResponse, SortBy } from '@/apis/chat/entity'; export const chatQueryKeys = { all: ['chat'] as const, diff --git a/src/components/layout/Header/components/ChatRoomMoreHeader.tsx b/src/components/layout/Header/components/ChatRoomMoreHeader.tsx new file mode 100644 index 0000000..a5af9a1 --- /dev/null +++ b/src/components/layout/Header/components/ChatRoomMoreHeader.tsx @@ -0,0 +1,42 @@ +import type { Ref } from 'react'; +import { useParams } from 'react-router-dom'; +import useChat from '@/pages/Chat/hooks/useChat'; +import BackTitleHeader from './BackTitleHeader'; + +export default function ChatRoomMoreHeader({ headerRef }: { headerRef?: Ref }) { + const { chatRoomId } = useParams(); + const numericRoomId = Number(chatRoomId); + + const { chatRoomList, toggleMute, isTogglingMute } = useChat(numericRoomId); + const chatRoom = chatRoomList.rooms.find((room) => room.roomId === numericRoomId); + const isMuted = chatRoom?.isMuted ?? false; + + const muteToggle = ( +
+ {isMuted == true ? '알림 켜기' : '알림 끄기'} + +
+ ); + + return ( + + ); +} diff --git a/src/pages/Chat/AddChatRoom.tsx b/src/pages/Chat/AddChatRoom.tsx index c2f1206..ee3d409 100644 --- a/src/pages/Chat/AddChatRoom.tsx +++ b/src/pages/Chat/AddChatRoom.tsx @@ -1,5 +1,5 @@ import { Fragment, useState } from 'react'; -import { useQuery } from '@tanstack/react-query'; +import { useSuspenseQuery } from '@tanstack/react-query'; import { useNavigate } from 'react-router-dom'; import type { User, Section, SortBy } from '@/apis/chat/entity'; import { chatQueries } from '@/apis/chat/queries'; @@ -7,8 +7,10 @@ import Search from '@/assets/svg/big-search-icon.svg'; import Check from '@/assets/svg/check_color.svg'; import Dropdown from '@/components/common/Dropdown'; import ChatAddHeader from '@/components/layout/Header/components/ChatAddHeader'; +import useChat from '@/pages/Chat/hooks/useChat'; import useDebouncedCallback from '@/utils/hooks/useDebounce'; -import useChat from './hooks/useChat'; +import { isApiError } from '@/utils/ts/error/apiError'; +import { isServerErrorStatus, redirectToServerErrorPage } from '@/utils/ts/error/errorRedirect'; type UserListProps = { onToggle: (userId: number) => void; @@ -66,7 +68,7 @@ export default function AddChatRoom() { const navigate = useNavigate(); const { createRoomGroup } = useChat(); - const { data } = useQuery({ + const { data } = useSuspenseQuery({ ...chatQueries.invite(debouncedQuery, sortBy), }); @@ -95,8 +97,14 @@ export default function AddChatRoom() { { - const result = await createRoomGroup(Array.from(selectedUserIds)); - navigate(`/chats/${result.chatRoomId}`); + try { + const result = await createRoomGroup(Array.from(selectedUserIds)); + navigate(`/chats/${result.chatRoomId}`); + } catch (error) { + if (isApiError(error) && isServerErrorStatus(error.status)) { + redirectToServerErrorPage(); + } + } }} />