From 5fee017fe0985da0a346215b70047a5675fc5013 Mon Sep 17 00:00:00 2001 From: ion Date: Tue, 13 Jan 2026 13:16:51 +0900 Subject: [PATCH 01/73] =?UTF-8?q?feat/#115:=201:1=20=EC=B1=84=ED=8C=85?= =?UTF-8?q?=EB=B0=A9=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20api=20?= =?UTF-8?q?=EB=B0=8F=20type=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/api/chatApi.ts | 11 +++++++++++ TinyBite/api/urls.ts | 6 ++++++ TinyBite/types/chat.types.ts | 24 ++++++++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/TinyBite/api/chatApi.ts b/TinyBite/api/chatApi.ts index 4584a70..e5279b0 100644 --- a/TinyBite/api/chatApi.ts +++ b/TinyBite/api/chatApi.ts @@ -3,6 +3,7 @@ import { GetChatMessagesParams, GetChatMessagesResponse, OneToOneChatCardSchema, + OneToOneChatDetailSchema, } from "@/types/chat.types"; import { privateAxios } from "./axios"; import { ENDPOINT } from "./urls"; @@ -36,3 +37,13 @@ export const getOnetoOneRoomList = async () => { ); return res.data.data; }; + +/** + * 1:1 채팅방 디테일 조회 + */ +export const getOnetoOneRoomDetail = async (chatroomId: number) => { + const res = await privateAxios.get>( + ENDPOINT.CHAT_ROOM.DETAIL.ONE_TO_ONE(chatroomId) + ); + return res.data.data; +}; diff --git a/TinyBite/api/urls.ts b/TinyBite/api/urls.ts index e1100ea..fd5c3b3 100644 --- a/TinyBite/api/urls.ts +++ b/TinyBite/api/urls.ts @@ -46,7 +46,13 @@ export const ENDPOINT = { WS_SEND: "/publish/send", }, CHAT_ROOM: { + // 채팅방 리스트 조회 ONE_TO_ONE: "/api/v1/chatroom/one-to-one", GROUP: "/api/v1/chatroom/group", + // 채팅방 정보 조회 + DETAIL: { + ONE_TO_ONE: (chatroomId: number) => + `/api/v1/chatroom/one-to-one/${chatroomId}`, + }, }, }; diff --git a/TinyBite/types/chat.types.ts b/TinyBite/types/chat.types.ts index ca4dd21..07bf47f 100644 --- a/TinyBite/types/chat.types.ts +++ b/TinyBite/types/chat.types.ts @@ -3,6 +3,11 @@ */ export type RoomType = "ONE_TO_ONE" | "Group"; +/** + * 채팅방 참여자 타입 + */ +export type participantType = "HOST" | "PARTICIPANT"; + // ============================================ /** @@ -47,6 +52,25 @@ export interface OneToOneChatCardSchema { unreadMessageCnt: number; } +/** + * 1:1 채팅방 내부 detail 스키마 + */ +export interface OneToOneChatDetailSchema { + chatRoomId: number; + participantType: participantType; + participantStatus: OneToOneChatStatusType; + partyTitle: string; + targetName: string; + + // HOST에게 포함되는 정보 + targetProfileImage?: string; + targetLocation?: string; + + // PARTICIPANT에게 포함되는 정보 + // participantStatus가 APPROVED일 때만 한함 + groupChatRoomId?: number; +} + // ============================================ /** From 14dc73a2e8381385e3b173d742972a5b6af3c35f Mon Sep 17 00:00:00 2001 From: ion Date: Wed, 14 Jan 2026 12:59:24 +0900 Subject: [PATCH 02/73] =?UTF-8?q?fix/#115:=20=EB=A1=9C=EB=94=A9=20?= =?UTF-8?q?=EB=B0=8F=20=EC=97=90=EB=9F=AC=20=EC=B2=98=EB=A6=AC=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20distanceKm=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/app/party-detail/[id].tsx | 4 ++-- TinyBite/types/party.types.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/TinyBite/app/party-detail/[id].tsx b/TinyBite/app/party-detail/[id].tsx index 17d7c32..7f9f1a1 100644 --- a/TinyBite/app/party-detail/[id].tsx +++ b/TinyBite/app/party-detail/[id].tsx @@ -98,7 +98,7 @@ export default function PartyDetailScreen() { }, [isSuccess, partyDetail, setInitialPartyInfo]); // 로딩 중일 때 - if (isLoading) { + if (isLoading || !partyDetail) { return ( @@ -107,7 +107,7 @@ export default function PartyDetailScreen() { } // 에러 발생 시 - if (error || !partyDetail) { + if (error) { return ( Date: Wed, 14 Jan 2026 13:02:18 +0900 Subject: [PATCH 03/73] =?UTF-8?q?fix/#115:=20=EB=A1=9C=EB=94=A9=20?= =?UTF-8?q?=EB=B0=8F=20=EC=97=90=EB=9F=AC=20=EC=B2=98=EB=A6=AC=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/app/party-detail/[id].tsx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/TinyBite/app/party-detail/[id].tsx b/TinyBite/app/party-detail/[id].tsx index 7f9f1a1..cb46d56 100644 --- a/TinyBite/app/party-detail/[id].tsx +++ b/TinyBite/app/party-detail/[id].tsx @@ -97,17 +97,8 @@ export default function PartyDetailScreen() { } }, [isSuccess, partyDetail, setInitialPartyInfo]); - // 로딩 중일 때 - if (isLoading || !partyDetail) { - return ( - - - - ); - } - // 에러 발생 시 - if (error) { + if (error && !isLoading) { return ( + + + ); + } + return ( <> {/* 상태바 스타일: 헤더가 나타나면 dark, 아니면 light */} From 9f107565a02ad43b8d54c3a506baf22be1ed382f Mon Sep 17 00:00:00 2001 From: ion Date: Wed, 14 Jan 2026 13:50:29 +0900 Subject: [PATCH 04/73] =?UTF-8?q?feat/#115:=20=EC=9D=BC=EB=8C=80=EC=9D=BC?= =?UTF-8?q?=20=EC=B1=84=ED=8C=85=EB=B0=A9=20=EC=83=81=EC=84=B8=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/hooks/queries/useChatRoom.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/TinyBite/hooks/queries/useChatRoom.ts b/TinyBite/hooks/queries/useChatRoom.ts index 9c64b45..3e32ffc 100644 --- a/TinyBite/hooks/queries/useChatRoom.ts +++ b/TinyBite/hooks/queries/useChatRoom.ts @@ -1,4 +1,4 @@ -import { getOnetoOneRoomList } from "@/api/chatApi"; +import { getOnetoOneRoomDetail, getOnetoOneRoomList } from "@/api/chatApi"; import { useQuery } from "@tanstack/react-query"; export const useGetOnetoOneRoomListQuery = () => { @@ -8,3 +8,10 @@ export const useGetOnetoOneRoomListQuery = () => { staleTime: 0, }); }; + +export const useGetOnetoOneRoomDetailQuery = (chatroomId: number) => { + return useQuery({ + queryKey: ["getOnetoOneRoomDetail", chatroomId], + queryFn: () => getOnetoOneRoomDetail(chatroomId), + }); +}; From d6a5ff580c901f5669e5b0014c243f0e7fc87429 Mon Sep 17 00:00:00 2001 From: ion Date: Wed, 14 Jan 2026 14:02:25 +0900 Subject: [PATCH 05/73] =?UTF-8?q?chore/#115:=20=EC=B1=84=ED=8C=85=EB=B0=A9?= =?UTF-8?q?=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20=EC=8B=9C=20?= =?UTF-8?q?=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20=ED=8C=8C=EB=9D=BC?= =?UTF-8?q?=EB=AF=B8=ED=84=B0=20=EC=A3=BC=EC=84=9D=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/components/chat/ChatCard.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/TinyBite/components/chat/ChatCard.tsx b/TinyBite/components/chat/ChatCard.tsx index b584fb4..af12001 100644 --- a/TinyBite/components/chat/ChatCard.tsx +++ b/TinyBite/components/chat/ChatCard.tsx @@ -33,9 +33,9 @@ const ChatItem = ({ item }: ChatItemProps) => { params: { id: item.chatRoomId, roomType: item.roomType, - status: item.status, - partyTitle: item.partyTitle, - targetName: item.targetName, + // status: item.status, + // partyTitle: item.partyTitle, + // targetName: item.targetName, }, }); }; From c415015bda6bfe7cdb02722be4b975e517160143 Mon Sep 17 00:00:00 2001 From: ion Date: Wed, 14 Jan 2026 14:03:01 +0900 Subject: [PATCH 06/73] =?UTF-8?q?feat/#115:=201:1=20=EC=B1=84=ED=8C=85?= =?UTF-8?q?=EB=B0=A9=20=EB=94=94=ED=85=8C=EC=9D=BC=20=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EB=B0=8F=20=EB=A1=9C=EB=94=A9/=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EC=B2=98=EB=A6=AC=20=EB=A1=9C=EC=A7=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/components/ChatRoomHeader.tsx | 15 +++-- TinyBite/components/layout/ChatRoomLayout.tsx | 67 ++++++++++++++++++- 2 files changed, 76 insertions(+), 6 deletions(-) diff --git a/TinyBite/components/ChatRoomHeader.tsx b/TinyBite/components/ChatRoomHeader.tsx index 359d609..2c1a927 100644 --- a/TinyBite/components/ChatRoomHeader.tsx +++ b/TinyBite/components/ChatRoomHeader.tsx @@ -2,6 +2,7 @@ import OneOnOneChatStatusTag from "@/components/chat/OneOnOneChatStatusTag"; import { colors } from "@/styles/colors"; import { textStyles } from "@/styles/typography/textStyles"; import { + OneToOneChatDetailSchema, OneToOneChatStatusType, PartyStatusType, RoomType, @@ -14,7 +15,11 @@ import PartyStatusTag from "./chat/PartyStatusTag"; const CHEVRON_LEFT_ICON = require("@/assets/images/chevron/chevron-left-36-gray.png"); const MEMBER_COUNT = require("@/assets/images/chat/member-count.png"); -const ChatRoomHeader = () => { +interface ChatRoomHeaderProps { + roomDetail: OneToOneChatDetailSchema; +} + +const ChatRoomHeader = ({ roomDetail }: ChatRoomHeaderProps) => { const { roomType } = useLocalSearchParams<{ roomType: RoomType; }>(); @@ -34,14 +39,16 @@ const ChatRoomHeader = () => { numberOfLines={1} ellipsizeMode="tail" > - .임시 타켓 이름. + {roomDetail.targetName} - .임시 파티 제목. + {roomDetail.partyTitle} ) : ( diff --git a/TinyBite/components/layout/ChatRoomLayout.tsx b/TinyBite/components/layout/ChatRoomLayout.tsx index 784f85e..cec1f64 100644 --- a/TinyBite/components/layout/ChatRoomLayout.tsx +++ b/TinyBite/components/layout/ChatRoomLayout.tsx @@ -1,9 +1,11 @@ import ChatInputBox from "@/components/chat/ChatInputBox"; import ChatRoomHeader from "@/components/ChatRoomHeader"; +import { useGetOnetoOneRoomDetailQuery } from "@/hooks/queries/useChatRoom"; import { colors } from "@/styles/colors"; +import { useLocalSearchParams, useRouter } from "expo-router"; import { StatusBar } from "expo-status-bar"; import { ReactNode, useState } from "react"; -import { StyleSheet, View } from "react-native"; +import { ActivityIndicator, Alert, StyleSheet, View } from "react-native"; import { KeyboardAvoidingView } from "react-native-keyboard-controller"; import { SafeAreaView } from "react-native-safe-area-context"; import ChatBottomPanel from "../chat/ChatBottomPanel"; @@ -19,17 +21,78 @@ const ChatRoomLayout = ({ onSendText, onSendImage, }: ChatRoomLayoutProps) => { + const { id: chatRoomId, roomType } = useLocalSearchParams<{ + id: string; + roomType: string; + }>(); + const router = useRouter(); const [isPanelVisible, setIsPanelVisible] = useState(false); + // 1:1 채팅방 디테일 정보 관리 + const { + data: oneToOneData, + isLoading: oneToOneIsLoading, + isError: oneToOneIsError, + } = useGetOnetoOneRoomDetailQuery(parseInt(chatRoomId)); + + // 통합된 데이터 사용 + // const roomDetailData = roomType === 'ONE_TO_ONE' ? oneToOneData : groupData; + // const roomDetailIsLoading = oneToOneIsLoading || groupIsLoading; + // const roomDetailIsError = oneToOneIsError || groupIsError; + const roomDetailData = roomType === "ONE_TO_ONE" ? oneToOneData : null; + const roomDetailIsLoading = oneToOneIsLoading || null; + const roomDetailIsError = oneToOneIsError || null; + const togglePanel = () => { setIsPanelVisible((prev) => !prev); }; + if (roomDetailIsLoading) { + return ( + + + + ); + } + + if (roomDetailIsError) { + Alert.alert( + "오류", + "채팅방 정보를 불러올 수 없습니다.", + [ + { + text: "확인", + onPress: () => router.back(), + }, + ], + { cancelable: false } + ); + return ( + + + + ); + } + + if (!roomDetailData) { + return null; + } + return ( <> - + Date: Wed, 14 Jan 2026 14:03:34 +0900 Subject: [PATCH 07/73] =?UTF-8?q?feat/#115:=201:1=20=EC=B1=84=ED=8C=85?= =?UTF-8?q?=EB=B0=A9=20=EC=83=81=EC=84=B8=20=EC=A1=B0=ED=9A=8C=20=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=20=EC=98=B5=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/components/layout/ChatRoomLayout.tsx | 4 +++- TinyBite/hooks/queries/useChatRoom.ts | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/TinyBite/components/layout/ChatRoomLayout.tsx b/TinyBite/components/layout/ChatRoomLayout.tsx index cec1f64..49f2b33 100644 --- a/TinyBite/components/layout/ChatRoomLayout.tsx +++ b/TinyBite/components/layout/ChatRoomLayout.tsx @@ -33,7 +33,9 @@ const ChatRoomLayout = ({ data: oneToOneData, isLoading: oneToOneIsLoading, isError: oneToOneIsError, - } = useGetOnetoOneRoomDetailQuery(parseInt(chatRoomId)); + } = useGetOnetoOneRoomDetailQuery(parseInt(chatRoomId), { + enabled: roomType === "ONE_TO_ONE", + }); // 통합된 데이터 사용 // const roomDetailData = roomType === 'ONE_TO_ONE' ? oneToOneData : groupData; diff --git a/TinyBite/hooks/queries/useChatRoom.ts b/TinyBite/hooks/queries/useChatRoom.ts index 3e32ffc..598e7b8 100644 --- a/TinyBite/hooks/queries/useChatRoom.ts +++ b/TinyBite/hooks/queries/useChatRoom.ts @@ -9,9 +9,13 @@ export const useGetOnetoOneRoomListQuery = () => { }); }; -export const useGetOnetoOneRoomDetailQuery = (chatroomId: number) => { +export const useGetOnetoOneRoomDetailQuery = ( + chatroomId: number, + options?: { enabled?: boolean } +) => { return useQuery({ queryKey: ["getOnetoOneRoomDetail", chatroomId], queryFn: () => getOnetoOneRoomDetail(chatroomId), + ...options, }); }; From c6534b1b15e0ce293cae7268ae1d4e085ae6cdbb Mon Sep 17 00:00:00 2001 From: ion Date: Wed, 14 Jan 2026 14:23:57 +0900 Subject: [PATCH 08/73] =?UTF-8?q?fix/#115:=20=EC=B1=84=ED=8C=85=EB=B0=A9?= =?UTF-8?q?=20=ED=83=80=EC=9E=85=20=EC=83=81=EC=88=98=EC=9D=98=20=EB=8C=80?= =?UTF-8?q?=EC=86=8C=EB=AC=B8=EC=9E=90=20=EC=9D=BC=EA=B4=80=EC=84=B1=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/types/chat.types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TinyBite/types/chat.types.ts b/TinyBite/types/chat.types.ts index 07bf47f..646b636 100644 --- a/TinyBite/types/chat.types.ts +++ b/TinyBite/types/chat.types.ts @@ -1,7 +1,7 @@ /** * 채팅방 타입 */ -export type RoomType = "ONE_TO_ONE" | "Group"; +export type RoomType = "ONE_TO_ONE" | "GROUP"; /** * 채팅방 참여자 타입 From ea8a535f0d94176ce629f20fdf57f30f6e2a91cf Mon Sep 17 00:00:00 2001 From: ion Date: Wed, 14 Jan 2026 14:24:17 +0900 Subject: [PATCH 09/73] =?UTF-8?q?fix/#115:=20=EC=B1=84=ED=8C=85=EB=B0=A9?= =?UTF-8?q?=20=ED=83=80=EC=9E=85=EC=9D=98=20roomType=EC=9D=84=20=EB=AC=B8?= =?UTF-8?q?=EC=9E=90=EC=97=B4=EC=97=90=EC=84=9C=20RoomType=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/components/layout/ChatRoomLayout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TinyBite/components/layout/ChatRoomLayout.tsx b/TinyBite/components/layout/ChatRoomLayout.tsx index 49f2b33..8612669 100644 --- a/TinyBite/components/layout/ChatRoomLayout.tsx +++ b/TinyBite/components/layout/ChatRoomLayout.tsx @@ -23,7 +23,7 @@ const ChatRoomLayout = ({ }: ChatRoomLayoutProps) => { const { id: chatRoomId, roomType } = useLocalSearchParams<{ id: string; - roomType: string; + roomType: RoomType; }>(); const router = useRouter(); const [isPanelVisible, setIsPanelVisible] = useState(false); From 279638e688735cc73ac4a5c067c3dd72f6178fdf Mon Sep 17 00:00:00 2001 From: ion Date: Wed, 14 Jan 2026 14:24:36 +0900 Subject: [PATCH 10/73] =?UTF-8?q?feat/#115:=201:1=20=EC=B1=84=ED=8C=85?= =?UTF-8?q?=EB=B0=A9=20=EC=83=81=ED=83=9C=20=ED=95=B8=EB=93=A4=EB=9F=AC=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/chat/ChatRoomStatusHandler.tsx | 76 +++++++++++++++++++ TinyBite/components/layout/ChatRoomLayout.tsx | 21 ++--- 2 files changed, 82 insertions(+), 15 deletions(-) create mode 100644 TinyBite/components/chat/ChatRoomStatusHandler.tsx diff --git a/TinyBite/components/chat/ChatRoomStatusHandler.tsx b/TinyBite/components/chat/ChatRoomStatusHandler.tsx new file mode 100644 index 0000000..4d1d247 --- /dev/null +++ b/TinyBite/components/chat/ChatRoomStatusHandler.tsx @@ -0,0 +1,76 @@ +import { OneToOneChatDetailSchema, RoomType } from "@/types/chat.types"; +import { ChatJoinRequestCard } from "./host/ChatJoinRequestCard"; +import { ChatJoinAcceptedCard } from "./participant/ChatJoinAcceptedCard"; +import { ChatJoinPendingCard } from "./participant/ChatJoinPendingCard"; + +// type participantType = 'HOST' | 'PARTICIPANT'; +// type OneToOneChatStatusType = 'PENDING' | 'REJECTED' | 'APPROVED' | 'REQUESTED' | 'ENDED'; + +interface ChatRoomStatusHandlerProps { + roomType: RoomType; + chatDetail: OneToOneChatDetailSchema; +} + +const ChatRoomStatusHandler = ({ + roomType, + chatDetail, +}: ChatRoomStatusHandlerProps) => { + // ONE_TO_ONE이 아니면 아무것도 렌더링하지 않음 + if (roomType !== "ONE_TO_ONE") { + return null; + } + + const { participantType, participantStatus } = chatDetail; + + // HOST 분기 + if (participantType === "HOST") { + if (participantStatus === "REQUESTED") { + // 1:1 파티장 - 수락, 거절 + return ( + console.log("승인")} + onReject={() => console.log("거절")} + /> + ); + } + + if (participantStatus === "REJECTED" || participantStatus === "APPROVED") { + return null; + } + + // 예상치 못한 status + alert(`HOST에게 예상치 못한 status: ${participantStatus}`); + return null; + } + + // PARTICIPANT 분기 + if (participantType === "PARTICIPANT") { + if (participantStatus === "PENDING") { + // 1:1 참여자 - 대기 + return ; + } + + if (participantStatus === "APPROVED") { + // 1:1 참여자 - 수락됨 + return ; + } + + if (participantStatus === "REJECTED") { + return null; + } + + // 예상치 못한 status + alert(`PARTICIPANT에게 예상치 못한 status: ${participantStatus}`); + return null; + } + + // 예상치 못한 participantType + alert(`예상치 못한 participantType: ${participantType}`); + return null; +}; + +export default ChatRoomStatusHandler; diff --git a/TinyBite/components/layout/ChatRoomLayout.tsx b/TinyBite/components/layout/ChatRoomLayout.tsx index 8612669..3fe7185 100644 --- a/TinyBite/components/layout/ChatRoomLayout.tsx +++ b/TinyBite/components/layout/ChatRoomLayout.tsx @@ -2,6 +2,7 @@ import ChatInputBox from "@/components/chat/ChatInputBox"; import ChatRoomHeader from "@/components/ChatRoomHeader"; import { useGetOnetoOneRoomDetailQuery } from "@/hooks/queries/useChatRoom"; import { colors } from "@/styles/colors"; +import { RoomType } from "@/types/chat.types"; import { useLocalSearchParams, useRouter } from "expo-router"; import { StatusBar } from "expo-status-bar"; import { ReactNode, useState } from "react"; @@ -9,6 +10,7 @@ import { ActivityIndicator, Alert, StyleSheet, View } from "react-native"; import { KeyboardAvoidingView } from "react-native-keyboard-controller"; import { SafeAreaView } from "react-native-safe-area-context"; import ChatBottomPanel from "../chat/ChatBottomPanel"; +import ChatRoomStatusHandler from "../chat/ChatRoomStatusHandler"; interface ChatRoomLayoutProps { children: ReactNode; @@ -101,21 +103,10 @@ const ChatRoomLayout = ({ keyboardVerticalOffset={0} > - {/* 1:1 파티장 - 수락, 거절 */} - {/* console.log("승인")} - onReject={() => console.log("거절")} - /> */} - - {/* 1:1 참여자 - 대기 */} - {/* */} - - {/* 1:1 참여자 - 수락됨 */} - {/* */} + {/* group 파티장 - 정산하기 */} {/* */} From 3d231fad556270c46da3a73c4d6422a6857d1b61 Mon Sep 17 00:00:00 2001 From: ion Date: Wed, 14 Jan 2026 14:29:24 +0900 Subject: [PATCH 11/73] =?UTF-8?q?fix/#115:=20=EC=B0=B8=EC=97=AC=EC=A4=91?= =?UTF-8?q?=EC=9D=B8=20=ED=8C=8C=ED=8B=B0=20=ED=95=84=ED=84=B0=EC=9D=98=20?= =?UTF-8?q?roomType=20=EB=8C=80=EC=86=8C=EB=AC=B8=EC=9E=90=20=EC=9D=BC?= =?UTF-8?q?=EA=B4=80=EC=84=B1=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/app/(tabs)/chat.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TinyBite/app/(tabs)/chat.tsx b/TinyBite/app/(tabs)/chat.tsx index 2a4f8b2..1dc5a14 100644 --- a/TinyBite/app/(tabs)/chat.tsx +++ b/TinyBite/app/(tabs)/chat.tsx @@ -49,7 +49,7 @@ export default function ChatScreen() { (item) => { if (selectedFilter === "전체") return true; if (selectedFilter === "1:1 채팅") return item.roomType === "ONE_TO_ONE"; - if (selectedFilter === "참여중인 파티") return item.roomType === "Group"; + if (selectedFilter === "참여중인 파티") return item.roomType === "GROUP"; return true; } ); From f914a1b5ca96da9af65dc17e168ee699d03a57a9 Mon Sep 17 00:00:00 2001 From: ion Date: Wed, 14 Jan 2026 15:50:24 +0900 Subject: [PATCH 12/73] =?UTF-8?q?fix/#115:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=EC=97=90=EC=84=9C=20=ED=8C=8C=ED=8B=B0=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=A1=B0=ED=9A=8C=20api=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C=EC=8B=9C=20=EA=B1=B0=EB=A6=AC=20=ED=8C=8C?= =?UTF-8?q?=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/api/partyApi.ts | 24 ++++++++++++++++++---- TinyBite/components/mypage/MyPartyList.tsx | 15 +++++++++++--- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/TinyBite/api/partyApi.ts b/TinyBite/api/partyApi.ts index 49bb15e..29a9503 100644 --- a/TinyBite/api/partyApi.ts +++ b/TinyBite/api/partyApi.ts @@ -166,9 +166,17 @@ export const patchParty = async ({ /* 참여중인 파티 리스트 조회 API * @returns 참여중인 파티 리스트 */ -export const getActiveParties = async (): Promise => { +export const getActiveParties = async ( + latitude: number | undefined, + longitude: number | undefined +): Promise => { try { - const res = await privateAxios.get(ENDPOINT.USER.ACTIVE_PARTIES); + const res = await privateAxios.get(ENDPOINT.USER.ACTIVE_PARTIES, { + params: { + latitude, + longitude, + }, + }); return res.data || []; } catch (error) { console.error("참여중인 파티 리스트 로딩 실패:", error); @@ -180,9 +188,17 @@ export const getActiveParties = async (): Promise => { * 호스팅 중인 파티 리스트 조회 API * @returns 호스팅 중인 파티 리스트 */ -export const getHostingParties = async (): Promise => { +export const getHostingParties = async ( + latitude: number | undefined, + longitude: number | undefined +): Promise => { try { - const res = await privateAxios.get(ENDPOINT.USER.HOSTING_PARTIES); + const res = await privateAxios.get(ENDPOINT.USER.HOSTING_PARTIES, { + params: { + latitude, + longitude, + }, + }); return res.data || []; } catch (error) { console.error("호스팅 중인 파티 리스트 로딩 실패:", error); diff --git a/TinyBite/components/mypage/MyPartyList.tsx b/TinyBite/components/mypage/MyPartyList.tsx index 8c2b719..7fd48d5 100644 --- a/TinyBite/components/mypage/MyPartyList.tsx +++ b/TinyBite/components/mypage/MyPartyList.tsx @@ -1,11 +1,12 @@ import { getActiveParties, getHostingParties } from "@/api/partyApi"; import MainCard from "@/components/main/MainCard"; +import { useUserCoords } from "@/hooks/useUserCoords"; import { colors } from "@/styles/colors"; import { textStyles } from "@/styles/typography/textStyles"; import { PartyItem } from "@/types/party.types"; import { useQuery } from "@tanstack/react-query"; import { useRouter } from "expo-router"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { Pressable, ScrollView, StyleSheet, Text, View } from "react-native"; type TabType = "active" | "hosting"; @@ -18,13 +19,21 @@ type TabType = "active" | "hosting"; const MyPartyList = () => { const router = useRouter(); const [activeTab, setActiveTab] = useState("active"); + const { coords, refresh: fetchCoords } = useUserCoords(); + + // 컴포넌트 마운트 시 위치 정보 가져오기 + useEffect(() => { + if (!coords) { + fetchCoords(); + } + }, []); // 참여중인 파티 리스트 조회 const { data: activeParties = [], isLoading: isLoadingActive } = useQuery< PartyItem[] >({ queryKey: ["getActiveParties"], - queryFn: getActiveParties, + queryFn:() => getActiveParties(coords?.latitude, coords?.longitude), }); // 호스팅 중인 파티 리스트 조회 @@ -32,7 +41,7 @@ const MyPartyList = () => { PartyItem[] >({ queryKey: ["getHostingParties"], - queryFn: getHostingParties, + queryFn:() => getHostingParties(coords?.latitude, coords?.longitude), enabled: activeTab === "hosting", }); From 3756ca93bb36503a7614ef80ac1cf3a76148373f Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 00:24:57 +0900 Subject: [PATCH 13/73] =?UTF-8?q?feat/#115:=201:1=20=EC=B1=84=ED=8C=85?= =?UTF-8?q?=EB=B0=A9=20=EC=83=81=EC=84=B8=20=EC=8A=A4=ED=82=A4=EB=A7=88?= =?UTF-8?q?=EC=97=90=20participantId=20=EB=B0=8F=20partyId=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/types/chat.types.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TinyBite/types/chat.types.ts b/TinyBite/types/chat.types.ts index 646b636..f5c475a 100644 --- a/TinyBite/types/chat.types.ts +++ b/TinyBite/types/chat.types.ts @@ -57,8 +57,10 @@ export interface OneToOneChatCardSchema { */ export interface OneToOneChatDetailSchema { chatRoomId: number; + participantId: number; participantType: participantType; participantStatus: OneToOneChatStatusType; + partyId: number; partyTitle: string; targetName: string; From 32b58e5e9b24d397240b262c0112631739f25766 Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 00:54:10 +0900 Subject: [PATCH 14/73] =?UTF-8?q?feat/#115:=20=ED=8C=8C=ED=8B=B0=20?= =?UTF-8?q?=EC=B0=B8=EC=97=AC=20=EC=8A=B9=EC=9D=B8=20=EB=B0=8F=20=EA=B1=B0?= =?UTF-8?q?=EC=A0=88=20API=20=EC=B6=94=EA=B0=80=EC=99=80=20=ED=9B=85=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/api/partyApi.ts | 26 +++++++++++++++++ TinyBite/api/urls.ts | 6 ++++ TinyBite/hooks/mutations/useChat.ts | 45 +++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 TinyBite/hooks/mutations/useChat.ts diff --git a/TinyBite/api/partyApi.ts b/TinyBite/api/partyApi.ts index 29a9503..dade7bc 100644 --- a/TinyBite/api/partyApi.ts +++ b/TinyBite/api/partyApi.ts @@ -286,3 +286,29 @@ export const postRequestJoinParty = async (partyId: number) => { return res.data.data; }; + +/** + * 파티 참여 승인 + */ +export const postApproveJoinParty = async ( + partyId: number, + participantId: number +) => { + const res = await privateAxios.post>( + ENDPOINT.PARTY.PARTICIPANTS.APPROVE(partyId, participantId) + ); + console.log("postApproveJoinParty >>", res); +}; + +/** + * 파티 참여 거절 + */ +export const postRejectJoinParty = async ( + partyId: number, + participantId: number +) => { + const res = await privateAxios.post>( + ENDPOINT.PARTY.PARTICIPANTS.REJECT(partyId, participantId) + ); + console.log("postRejectJoinParty >>", res); +}; diff --git a/TinyBite/api/urls.ts b/TinyBite/api/urls.ts index fd5c3b3..f1d2adb 100644 --- a/TinyBite/api/urls.ts +++ b/TinyBite/api/urls.ts @@ -28,6 +28,12 @@ export const ENDPOINT = { SEARCH_LOG: "/api/parties/search/log", SEARCH_LOG_DELETE: (keyword: string) => `/api/parties/search/log/${keyword}`, + PARTICIPANTS: { + APPROVE: (partyId: number, participantId: number) => + `/api/parties/${partyId}/participants/${participantId}/approve`, + REJECT: (partyId: number, participantId: number) => + `/api/parties/${partyId}/participants/${participantId}/reject`, + }, }, FILE: { UPLOAD_FILE: "api/v1/file/upload", diff --git a/TinyBite/hooks/mutations/useChat.ts b/TinyBite/hooks/mutations/useChat.ts new file mode 100644 index 0000000..fca2805 --- /dev/null +++ b/TinyBite/hooks/mutations/useChat.ts @@ -0,0 +1,45 @@ +import { postApproveJoinParty, postRejectJoinParty } from "@/api/partyApi"; +import { useMutation } from "@tanstack/react-query"; +import Toast from "react-native-toast-message"; + +export const useApproveJoinPartyMutation = ( + partyId: number, + participantId: number +) => { + return useMutation({ + mutationFn: () => postApproveJoinParty(partyId, participantId), + onSuccess: (data) => { + return data; + }, + onError: () => { + Toast.show({ + type: "basicToast", + props: { text: "파티 참여 승인에 실패했습니다. 다시 시도해주세요." }, + position: "bottom", + bottomOffset: 133, + visibilityTime: 2000, + }); + }, + }); +}; + +export const useRejectJoinPartyMutation = ( + partyId: number, + participantId: number +) => { + return useMutation({ + mutationFn: () => postRejectJoinParty(partyId, participantId), + onSuccess: (data) => { + return data; + }, + onError: () => { + Toast.show({ + type: "basicToast", + props: { text: "파티 참여 거절에 실패했습니다. 다시 시도해주세요." }, + position: "bottom", + bottomOffset: 133, + visibilityTime: 2000, + }); + }, + }); +}; From e19fe8568cfd3b9f7e46f41ce8aa68595f095eff Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 01:57:37 +0900 Subject: [PATCH 15/73] =?UTF-8?q?fix/#115:=20=EC=B1=84=ED=8C=85=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=EC=97=90=EC=84=9C=20=EC=82=AC=EC=9A=A9=EC=9E=90=20ID?= =?UTF-8?q?=20=EA=B8=B0=EB=B0=98=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EA=B5=AC?= =?UTF-8?q?=EB=B6=84=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80=20=EB=B0=8F?= =?UTF-8?q?=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20isMine=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/app/chat/[id].tsx | 16 +++++++++------- TinyBite/components/chat/message/ChatMessage.tsx | 7 ++++--- TinyBite/types/chat.types.ts | 4 ++-- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/TinyBite/app/chat/[id].tsx b/TinyBite/app/chat/[id].tsx index 3d67a59..b2194ae 100644 --- a/TinyBite/app/chat/[id].tsx +++ b/TinyBite/app/chat/[id].tsx @@ -1,5 +1,5 @@ import { getPrevMessage } from "@/api/chatApi"; -import ChatMessageComponent from "@/components/chat/message/ChatMessage"; +import ChatMessage from "@/components/chat/message/ChatMessage"; import ChatRoomLayout from "@/components/layout/ChatRoomLayout"; import { useChatMessages } from "@/hooks/useChatMessages"; import { websocketClient } from "@/lib/websocket/websocketClient"; @@ -8,7 +8,7 @@ import { useQuery } from "@tanstack/react-query"; import { useLocalSearchParams } from "expo-router"; import * as SecureStore from "expo-secure-store"; import { useEffect } from "react"; -import { FlatList } from "react-native"; +import { FlatList, Text } from "react-native"; export default function ChatRoomScreen() { const { id: chatRoomId } = useLocalSearchParams<{ @@ -16,8 +16,6 @@ export default function ChatRoomScreen() { }>(); const user = useAuthStore((state) => state.user); - const userId = user?.userId; - const nickname = user?.nickname; const { data, isLoading, error, refetch } = useQuery({ queryKey: ["chatMessages", chatRoomId], @@ -48,10 +46,12 @@ export default function ChatRoomScreen() { useChatMessages({ chatRoomId: chatRoomId ? parseInt(chatRoomId) : 0, initialMessages: data?.messages, - userId: userId || 0, - nickname: nickname || "", + userId: user?.userId || 0, + nickname: user?.nickname || "", }); + if (!user) return Loading...; + return ( item.messageId.toString()} - renderItem={({ item }) => } + renderItem={({ item }) => ( + + )} /> ); diff --git a/TinyBite/components/chat/message/ChatMessage.tsx b/TinyBite/components/chat/message/ChatMessage.tsx index 6237e44..d67c72e 100644 --- a/TinyBite/components/chat/message/ChatMessage.tsx +++ b/TinyBite/components/chat/message/ChatMessage.tsx @@ -8,9 +8,10 @@ import { OutgoingMessage } from "./OutgoingMessage"; interface ChatMessageProps { message: ChatMessageSchema; + userId: number; } -const ChatMessage = ({ message }: ChatMessageProps) => { +const ChatMessage = ({ message, userId }: ChatMessageProps) => { switch (message.messageType) { case "DATE": return ; @@ -19,14 +20,14 @@ const ChatMessage = ({ message }: ChatMessageProps) => { return ; case "TEXT": - return message.isMine ? ( + return message.senderId === userId ? ( ) : ( ); case "IMAGE": - return message.isMine ? ( + return message.senderId === userId ? ( ) : ( diff --git a/TinyBite/types/chat.types.ts b/TinyBite/types/chat.types.ts index f5c475a..4abeb7b 100644 --- a/TinyBite/types/chat.types.ts +++ b/TinyBite/types/chat.types.ts @@ -157,7 +157,7 @@ export interface TextMessage extends BaseMessage { senderId: number; nickname: string; - isMine: boolean; + // isMine: boolean; text: string; } @@ -172,7 +172,7 @@ export interface ImageUrlMessage extends BaseMessage { senderId: number; nickname: string; - isMine: boolean; + // isMine: boolean; imageUrl: string; } From f8225ee3c6565c903cee296691f2dae6de8950e0 Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 01:57:50 +0900 Subject: [PATCH 16/73] =?UTF-8?q?chore/#115:=20postApproveJoinParty=20?= =?UTF-8?q?=EB=B0=8F=20postRejectJoinParty=EC=97=90=EC=84=9C=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=EC=9D=91=EB=8B=B5=20=EB=B3=80?= =?UTF-8?q?=EC=88=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/api/partyApi.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/TinyBite/api/partyApi.ts b/TinyBite/api/partyApi.ts index dade7bc..f61d3ca 100644 --- a/TinyBite/api/partyApi.ts +++ b/TinyBite/api/partyApi.ts @@ -294,10 +294,9 @@ export const postApproveJoinParty = async ( partyId: number, participantId: number ) => { - const res = await privateAxios.post>( + await privateAxios.post>( ENDPOINT.PARTY.PARTICIPANTS.APPROVE(partyId, participantId) ); - console.log("postApproveJoinParty >>", res); }; /** @@ -307,8 +306,7 @@ export const postRejectJoinParty = async ( partyId: number, participantId: number ) => { - const res = await privateAxios.post>( + await privateAxios.post>( ENDPOINT.PARTY.PARTICIPANTS.REJECT(partyId, participantId) ); - console.log("postRejectJoinParty >>", res); }; From 1c4d0025042e6aa25ff3afa2500e4e418d74ac0e Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 01:58:03 +0900 Subject: [PATCH 17/73] =?UTF-8?q?feat/#115:=201:1=20=EC=B1=84=ED=8C=85?= =?UTF-8?q?=EB=B0=A9=EC=97=90=EC=84=9C=20=ED=8C=8C=ED=8B=B0=20=EC=B0=B8?= =?UTF-8?q?=EC=97=AC=20=EC=8A=B9=EC=9D=B8=20=EB=B0=8F=20=EA=B1=B0=EC=A0=88?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/chat/ChatRoomStatusHandler.tsx | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/TinyBite/components/chat/ChatRoomStatusHandler.tsx b/TinyBite/components/chat/ChatRoomStatusHandler.tsx index 4d1d247..477a5eb 100644 --- a/TinyBite/components/chat/ChatRoomStatusHandler.tsx +++ b/TinyBite/components/chat/ChatRoomStatusHandler.tsx @@ -1,3 +1,7 @@ +import { + useApproveJoinPartyMutation, + useRejectJoinPartyMutation, +} from "@/hooks/mutations/useChat"; import { OneToOneChatDetailSchema, RoomType } from "@/types/chat.types"; import { ChatJoinRequestCard } from "./host/ChatJoinRequestCard"; import { ChatJoinAcceptedCard } from "./participant/ChatJoinAcceptedCard"; @@ -15,13 +19,22 @@ const ChatRoomStatusHandler = ({ roomType, chatDetail, }: ChatRoomStatusHandlerProps) => { + const { mutate: approveMutate } = useApproveJoinPartyMutation( + chatDetail.partyId, + chatDetail.participantId + ); + const { mutate: rejectMutate } = useRejectJoinPartyMutation( + chatDetail.partyId, + chatDetail.participantId + ); + + const { participantType, participantStatus } = chatDetail; + // ONE_TO_ONE이 아니면 아무것도 렌더링하지 않음 if (roomType !== "ONE_TO_ONE") { return null; } - const { participantType, participantStatus } = chatDetail; - // HOST 분기 if (participantType === "HOST") { if (participantStatus === "REQUESTED") { @@ -32,8 +45,8 @@ const ChatRoomStatusHandler = ({ nickname={chatDetail.targetName} location={chatDetail.targetLocation || ""} message="파티에 참여하고 싶어요!" - onApprove={() => console.log("승인")} - onReject={() => console.log("거절")} + onApprove={approveMutate} + onReject={rejectMutate} /> ); } From 7bc2963c22fc12ff7a2bd5cc7e4c09921b1a5428 Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 02:31:21 +0900 Subject: [PATCH 18/73] =?UTF-8?q?feat/#115:=20=EC=B1=84=ED=8C=85=EB=B0=A9?= =?UTF-8?q?=20=EC=B0=B8=EC=97=AC=20=EC=8A=B9=EC=9D=B8=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B1=B0=EC=A0=88=20=ED=9B=84=201=EB=8C=801=20=EC=B1=84?= =?UTF-8?q?=ED=8C=85=EB=B0=A9=20=EC=A0=95=EB=B3=B4=20=EA=B0=B1=EC=8B=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/chat/ChatRoomStatusHandler.tsx | 6 +++-- TinyBite/hooks/mutations/useChat.ts | 24 +++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/TinyBite/components/chat/ChatRoomStatusHandler.tsx b/TinyBite/components/chat/ChatRoomStatusHandler.tsx index 477a5eb..05ce1e2 100644 --- a/TinyBite/components/chat/ChatRoomStatusHandler.tsx +++ b/TinyBite/components/chat/ChatRoomStatusHandler.tsx @@ -21,11 +21,13 @@ const ChatRoomStatusHandler = ({ }: ChatRoomStatusHandlerProps) => { const { mutate: approveMutate } = useApproveJoinPartyMutation( chatDetail.partyId, - chatDetail.participantId + chatDetail.participantId, + chatDetail.chatRoomId ); const { mutate: rejectMutate } = useRejectJoinPartyMutation( chatDetail.partyId, - chatDetail.participantId + chatDetail.participantId, + chatDetail.chatRoomId ); const { participantType, participantStatus } = chatDetail; diff --git a/TinyBite/hooks/mutations/useChat.ts b/TinyBite/hooks/mutations/useChat.ts index fca2805..d0d6309 100644 --- a/TinyBite/hooks/mutations/useChat.ts +++ b/TinyBite/hooks/mutations/useChat.ts @@ -1,15 +1,20 @@ import { postApproveJoinParty, postRejectJoinParty } from "@/api/partyApi"; -import { useMutation } from "@tanstack/react-query"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; import Toast from "react-native-toast-message"; export const useApproveJoinPartyMutation = ( partyId: number, - participantId: number + participantId: number, + chatroomId: number ) => { + const queryClient = useQueryClient(); + return useMutation({ mutationFn: () => postApproveJoinParty(partyId, participantId), - onSuccess: (data) => { - return data; + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ["getOnetoOneRoomDetail", chatroomId], + }); }, onError: () => { Toast.show({ @@ -25,12 +30,17 @@ export const useApproveJoinPartyMutation = ( export const useRejectJoinPartyMutation = ( partyId: number, - participantId: number + participantId: number, + chatroomId: number ) => { + const queryClient = useQueryClient(); + return useMutation({ mutationFn: () => postRejectJoinParty(partyId, participantId), - onSuccess: (data) => { - return data; + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ["getOnetoOneRoomDetail", chatroomId], + }); }, onError: () => { Toast.show({ From de051d721bb7ed1eccaa7bfc24453b96d1fdcf16 Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 02:32:34 +0900 Subject: [PATCH 19/73] =?UTF-8?q?chore/#115:=20=EB=B2=84=EC=A0=84=200.5.0?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=EB=B0=8F=20lockfileVersion=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/app.config.js | 2 +- TinyBite/package-lock.json | 6 +++--- TinyBite/package.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/TinyBite/app.config.js b/TinyBite/app.config.js index 0ae0624..2b08ecf 100644 --- a/TinyBite/app.config.js +++ b/TinyBite/app.config.js @@ -4,7 +4,7 @@ module.exports = { name: "한입만", slug: "TinyBite", owner: "tinybite-2025", - version: "0.4.0", + version: "0.5.0", orientation: "portrait", icon: "./assets/images/icon.png", scheme: "tinybite", diff --git a/TinyBite/package-lock.json b/TinyBite/package-lock.json index 3bdb5be..6351fb2 100644 --- a/TinyBite/package-lock.json +++ b/TinyBite/package-lock.json @@ -1,12 +1,12 @@ { "name": "tinybite", - "version": "0.4.0", - "lockfileVersion": 4, + "version": "0.5.0", + "lockfileVersion": 5, "requires": true, "packages": { "": { "name": "tinybite", - "version": "0.4.0", + "version": "0.5.0", "dependencies": { "@expo/vector-icons": "^15.0.3", "@react-native-google-signin/google-signin": "^16.0.0", diff --git a/TinyBite/package.json b/TinyBite/package.json index d52296a..3288e4e 100644 --- a/TinyBite/package.json +++ b/TinyBite/package.json @@ -1,7 +1,7 @@ { "name": "tinybite", "main": "expo-router/entry", - "version": "0.4.0", + "version": "0.5.0", "scripts": { "start": "expo start", "reset-project": "node ./scripts/reset-project.js", From 1c3df5b217a469b9d5f058aae9dcc236155797ec Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 03:34:02 +0900 Subject: [PATCH 20/73] =?UTF-8?q?fix/#115:=20401=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=EC=97=90=EC=84=9C=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20URL=20=EC=B2=B4=ED=81=AC=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/api/axios.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/TinyBite/api/axios.ts b/TinyBite/api/axios.ts index 3fb85d4..13cd289 100644 --- a/TinyBite/api/axios.ts +++ b/TinyBite/api/axios.ts @@ -90,11 +90,7 @@ privateAxios.interceptors.response.use( const originalRequest = error.config; // 401 에러이고 재시도하지 않은 요청인지 확인 - if ( - error.response?.status === 401 && - !originalRequest._retry && - !originalRequest.url?.includes("/refresh") - ) { + if (error.response?.status === 401 && !originalRequest._retry) { if (isRefreshing) { return new Promise((resolve, reject) => { failedQueue.push({ resolve, reject }); From e0c78af35589320a5a58d30d86e405edce13e742 Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 11:39:30 +0900 Subject: [PATCH 21/73] =?UTF-8?q?fix/#115:=20=EA=B8=B0=EB=B3=B8=20?= =?UTF-8?q?=ED=94=BD=EC=97=85=20=EC=9E=A5=EC=86=8C=EB=A5=BC=20=EB=B9=88=20?= =?UTF-8?q?=EB=AC=B8=EC=9E=90=EC=97=B4=EB=A1=9C=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/stores/creatingPartyStore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TinyBite/stores/creatingPartyStore.ts b/TinyBite/stores/creatingPartyStore.ts index 18a1fee..7a29633 100644 --- a/TinyBite/stores/creatingPartyStore.ts +++ b/TinyBite/stores/creatingPartyStore.ts @@ -131,7 +131,7 @@ export const useCreatingPartyStore = create((set, get) => ({ totalAmount: "", numberOfPeople: 2, pickUpLocation: { - place: "중구 명동", + place: "", pickupLatitude: 37.55103512680912, pickupLongitude: 126.9254146711746, }, From aa43ac04c10bbcfbc931fd0c51b93718b5227609 Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 12:56:58 +0900 Subject: [PATCH 22/73] =?UTF-8?q?feat/#115:=201:1=20=EC=B1=84=ED=8C=85?= =?UTF-8?q?=EB=B0=A9=20=EC=88=98=EB=9D=BD=20=EC=8B=9C=20=EA=B7=B8=EB=A3=B9?= =?UTF-8?q?=20=EC=B1=84=ED=8C=85=EB=B0=A9=20ID=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EB=B0=8F=20=EB=84=A4=EB=B9=84=EA=B2=8C=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/chat/ChatRoomStatusHandler.tsx | 5 ++++- .../chat/participant/ChatJoinAcceptedCard.tsx | 18 +++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/TinyBite/components/chat/ChatRoomStatusHandler.tsx b/TinyBite/components/chat/ChatRoomStatusHandler.tsx index 05ce1e2..ec68af2 100644 --- a/TinyBite/components/chat/ChatRoomStatusHandler.tsx +++ b/TinyBite/components/chat/ChatRoomStatusHandler.tsx @@ -71,7 +71,10 @@ const ChatRoomStatusHandler = ({ if (participantStatus === "APPROVED") { // 1:1 참여자 - 수락됨 - return ; + if (chatDetail.groupChatRoomId === undefined) { + return null; + } + return ; } if (participantStatus === "REJECTED") { diff --git a/TinyBite/components/chat/participant/ChatJoinAcceptedCard.tsx b/TinyBite/components/chat/participant/ChatJoinAcceptedCard.tsx index 2e30fe9..ffff83f 100644 --- a/TinyBite/components/chat/participant/ChatJoinAcceptedCard.tsx +++ b/TinyBite/components/chat/participant/ChatJoinAcceptedCard.tsx @@ -1,13 +1,29 @@ import { colors } from "@/styles/colors"; import { textStyles } from "@/styles/typography/textStyles"; +import { router } from "expo-router"; import React from "react"; import { Image, StyleSheet, Text, TouchableOpacity, View } from "react-native"; const GROUP_ICON = require("@/assets/images/chat/group-icon.png"); -export const ChatJoinAcceptedCard = () => { +interface ChatJoinAcceptedCardProps { + chatRoomId: number +} + +export const ChatJoinAcceptedCard = ({chatRoomId} : ChatJoinAcceptedCardProps) => { + const handleNavigateToChatRoom = () => { console.log("파티 채팅방 가기"); + router.dismissTo({ + pathname: "/chat/[id]", + params: { + id: chatRoomId, + roomType: "GROUP", + // status: item.status, + // partyTitle: item.partyTitle, + // targetName: item.targetName, + }, + }) }; return ( From dd037e95fb951c1dfec39e2cbc453cb962a5f075 Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 13:12:03 +0900 Subject: [PATCH 23/73] =?UTF-8?q?feat/#115:=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EC=B1=84=ED=8C=85=20=EC=83=81=ED=83=9C=20=EB=B0=8F=20=EC=B9=B4?= =?UTF-8?q?=EB=93=9C=20=EC=8A=A4=ED=82=A4=EB=A7=88=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/types/chat.types.ts | 64 +++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/TinyBite/types/chat.types.ts b/TinyBite/types/chat.types.ts index 4abeb7b..3299ae5 100644 --- a/TinyBite/types/chat.types.ts +++ b/TinyBite/types/chat.types.ts @@ -8,6 +8,11 @@ export type RoomType = "ONE_TO_ONE" | "GROUP"; */ export type participantType = "HOST" | "PARTICIPANT"; +/** + * 파티 카테고리 타입 + */ +export type PartyCategoryType = "delivery" | "grocery" | "essentials"; + // ============================================ /** @@ -76,37 +81,50 @@ export interface OneToOneChatDetailSchema { // ============================================ /** - * 참여중인 파티 상태 태그 타입 + * 그룹 채팅 상태 (백엔드 기준) */ -export type PartyStatusType = "모집 중" | "진행 중" | "파티 종료"; +export type GroupChatStatusType = + | "RECRUITING" + | "COMPLETED" + | "CLOSED" + | "CANCELLED"; /** - * 파티 카테고리 타입 + * 그룹 채팅 상태 : 한글 매핑 객체 (UI 전용) */ -export type PartyCategoryType = "delivery" | "grocery" | "essentials"; +export const GroupChatStatusLabelMap: Record = { + RECRUITING: "모집 중", + COMPLETED: "진행 중", + CLOSED: "파티 종료", + CANCELLED: "파티 취소", +}; /** - * 채팅 아이템 인터페이스 (공통 + 선택적 필드) + * 그룹 채팅 카드 스키마 */ -export type ChatItemType = { - // 1. 공통 필드 (어떤 채팅이든 무조건 있음) - id: string; - name?: string; - lastMessage: string; +export interface GroupChatCardSchema { + chatRoomId: number; roomType: RoomType; - timestamp: string; - unreadCount?: number; - status: OneToOneChatStatusType | PartyStatusType | null; - partyTitle?: string; - - // 2. 1:1 채팅 전용 - opponentProfileImage?: any; //추후에 프로필 이미지 추가 시 url로 수정 - myProfileImage?: any; //추후에 프로필 이미지 추가 시 url로 수정 - - // 3. 파티 채팅 전용 - partyImage?: any; //추후에 파티 이미지 추가 시 url로 수정 - memberCount?: number; - category?: PartyCategoryType; + recentTime: string; // "2026-01-10T23:40:00" + partyTitle: string; + partyImage: string; + partyStatus: OneToOneChatStatusType; + recentMessage: string; + unreadMessageCnt: number; + currentParticipantCnt: number; +} + +/** + * 채팅방 내부 detail 스키마 + */ +export type GroupChatDetailSchema = { + groupChatRoomId: number; + partyId: number; + partyTitle: string; + status: GroupChatStatusType; + currentParticipantCnt: number; + maxParticipantCnt: number; + formattedPartyCnt: string; }; // ============================================ From cc2eb78115ca2fddb2c205cb9f20de033afb8218 Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 13:12:52 +0900 Subject: [PATCH 24/73] =?UTF-8?q?feat/#115:=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EC=B1=84=ED=8C=85=EB=B0=A9=20=EC=97=94=EB=93=9C=ED=8F=AC?= =?UTF-8?q?=EC=9D=B8=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/api/urls.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/TinyBite/api/urls.ts b/TinyBite/api/urls.ts index f1d2adb..0404298 100644 --- a/TinyBite/api/urls.ts +++ b/TinyBite/api/urls.ts @@ -59,6 +59,7 @@ export const ENDPOINT = { DETAIL: { ONE_TO_ONE: (chatroomId: number) => `/api/v1/chatroom/one-to-one/${chatroomId}`, + GROUP: (chatroomId: number) => `/api/v1/chatroom/group/${chatroomId}`, }, }, }; From 074f02bc35c25db773e7debdb1ef20ba3ecd943d Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 13:15:21 +0900 Subject: [PATCH 25/73] =?UTF-8?q?feat/#115:=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EC=B1=84=ED=8C=85=EB=B0=A9=20=EB=AA=A9=EB=A1=9D=20=EB=B0=8F=20?= =?UTF-8?q?=EB=94=94=ED=85=8C=EC=9D=BC=20=EC=A1=B0=ED=9A=8C=20api=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/api/chatApi.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/TinyBite/api/chatApi.ts b/TinyBite/api/chatApi.ts index e5279b0..44e05d7 100644 --- a/TinyBite/api/chatApi.ts +++ b/TinyBite/api/chatApi.ts @@ -2,6 +2,8 @@ import { ApiSuccess } from "@/types/api.types"; import { GetChatMessagesParams, GetChatMessagesResponse, + GroupChatCardSchema, + GroupChatDetailSchema, OneToOneChatCardSchema, OneToOneChatDetailSchema, } from "@/types/chat.types"; @@ -47,3 +49,23 @@ export const getOnetoOneRoomDetail = async (chatroomId: number) => { ); return res.data.data; }; + +/** + * 그룹 채팅방 목록 조회 + */ +export const getGroupRoomList = async () => { + const res = await privateAxios.get>( + ENDPOINT.CHAT_ROOM.GROUP + ); + return res.data.data; +}; + +/** + * 그룹 채팅방 디테일 조회 + */ +export const getGroupRoomDetail = async (chatroomId: number) => { + const res = await privateAxios.get>( + ENDPOINT.CHAT_ROOM.DETAIL.GROUP(chatroomId) + ); + return res.data.data; +}; From 94735fc5b98e49d5c9d8a0a3ca44e7beecc6ab29 Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 13:16:12 +0900 Subject: [PATCH 26/73] =?UTF-8?q?feat/#115:=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EC=B1=84=ED=8C=85=EB=B0=A9=20=EB=AA=A9=EB=A1=9D=20=EB=B0=8F=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/hooks/queries/useChatRoom.ts | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/TinyBite/hooks/queries/useChatRoom.ts b/TinyBite/hooks/queries/useChatRoom.ts index 598e7b8..def5658 100644 --- a/TinyBite/hooks/queries/useChatRoom.ts +++ b/TinyBite/hooks/queries/useChatRoom.ts @@ -1,4 +1,9 @@ -import { getOnetoOneRoomDetail, getOnetoOneRoomList } from "@/api/chatApi"; +import { + getGroupRoomDetail, + getGroupRoomList, + getOnetoOneRoomDetail, + getOnetoOneRoomList, +} from "@/api/chatApi"; import { useQuery } from "@tanstack/react-query"; export const useGetOnetoOneRoomListQuery = () => { @@ -19,3 +24,22 @@ export const useGetOnetoOneRoomDetailQuery = ( ...options, }); }; + +export const useGetGroupRoomListQuery = () => { + return useQuery({ + queryKey: ["useGetGroupRoomListQuery"], + queryFn: getGroupRoomList, + staleTime: 0, + }); +}; + +export const useGetGroupRoomDetailQuery = ( + chatroomId: number, + options?: { enabled?: boolean } +) => { + return useQuery({ + queryKey: ["useGetGroupRoomDetailQuery", chatroomId], + queryFn: () => getGroupRoomDetail(chatroomId), + ...options, + }); +}; From 87b652977d84a0d67d3732b5ace762acacc7d2b7 Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 13:22:17 +0900 Subject: [PATCH 27/73] =?UTF-8?q?refactor/#115:=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20=EA=B5=AC=EC=A1=B0=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/app/(tabs)/chat.tsx | 31 +++++++++++++------------------ TinyBite/types/chat.types.ts | 5 +++++ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/TinyBite/app/(tabs)/chat.tsx b/TinyBite/app/(tabs)/chat.tsx index 1dc5a14..f0435bc 100644 --- a/TinyBite/app/(tabs)/chat.tsx +++ b/TinyBite/app/(tabs)/chat.tsx @@ -2,7 +2,7 @@ import ChatCard from "@/components/chat/ChatCard"; import { useGetOnetoOneRoomListQuery } from "@/hooks/queries/useChatRoom"; import { colors } from "@/styles/colors"; import { textStyles } from "@/styles/typography/textStyles"; -import { OneToOneChatCardSchema } from "@/types/chat.types"; +import { FilterTab, OneToOneChatCardSchema } from "@/types/chat.types"; import { useFocusEffect } from "expo-router"; import { useCallback, useState } from "react"; import { @@ -15,10 +15,20 @@ import { } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; +// 필터 탭 목록 +const filters: FilterTab[] = ["전체", "참여중인 파티", "1:1 채팅"]; + +/** + * 채팅 아이템 렌더링 함수 + */ +const renderChatItem = ({ item }: { item: OneToOneChatCardSchema }) => { + return ; +}; + /** - * 필터 탭 타입 정의 + * 아이템 구분선 컴포넌트 (80% 너비) */ -type FilterTab = "전체" | "참여중인 파티" | "1:1 채팅"; +const ItemSeparator = () => ; /** * 채팅 화면 컴포넌트 @@ -39,9 +49,6 @@ export default function ChatScreen() { // 선택된 필터 탭 상태 const [selectedFilter, setSelectedFilter] = useState("전체"); - // 필터 탭 목록 - const filters: FilterTab[] = ["전체", "참여중인 파티", "1:1 채팅"]; - /** * 필터에 맞는 데이터 필터링 */ @@ -61,18 +68,6 @@ export default function ChatScreen() { // ...item, // })); - /** - * 채팅 아이템 렌더링 함수 - */ - const renderChatItem = ({ item }: { item: OneToOneChatCardSchema }) => { - return ; - }; - - /** - * 아이템 구분선 컴포넌트 (80% 너비) - */ - const ItemSeparator = () => ; - return ( {/* 상단 SafeArea (메인 색상 배경) */} diff --git a/TinyBite/types/chat.types.ts b/TinyBite/types/chat.types.ts index 3299ae5..5c21a72 100644 --- a/TinyBite/types/chat.types.ts +++ b/TinyBite/types/chat.types.ts @@ -1,3 +1,8 @@ +/** + * 필터 탭 타입 정의 + */ +export type FilterTab = "전체" | "참여중인 파티" | "1:1 채팅"; + /** * 채팅방 타입 */ From a99294a7cd13fc0c9b906e931886173e372484b9 Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 14:11:01 +0900 Subject: [PATCH 28/73] =?UTF-8?q?feat/#115:=201:1=20=EB=B0=8F=20=EA=B7=B8?= =?UTF-8?q?=EB=A3=B9=20=EC=B1=84=ED=8C=85=20=EC=95=84=EC=9D=B4=ED=85=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=B2=98=EB=A6=AC=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/components/chat/ChatCardImage.tsx | 69 ++++++++++++---------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/TinyBite/components/chat/ChatCardImage.tsx b/TinyBite/components/chat/ChatCardImage.tsx index 6b7d1b9..6ce4101 100644 --- a/TinyBite/components/chat/ChatCardImage.tsx +++ b/TinyBite/components/chat/ChatCardImage.tsx @@ -1,18 +1,21 @@ import { colors } from "@/styles/colors"; -import { OneToOneChatCardSchema, PartyCategoryType } from "@/types/chat.types"; +import { + GroupChatCardSchema, + OneToOneChatCardSchema, +} from "@/types/chat.types"; import { Image, StyleSheet, View } from "react-native"; /** * 카테고리별 아이콘 매핑 */ -const categoryIcons: Record = { - delivery: require("@/assets/images/main/category/delivery.png"), - grocery: require("@/assets/images/main/category/grocery.png"), - essentials: require("@/assets/images/main/category/essentials.png"), -}; +// const categoryIcons: Record = { +// delivery: require("@/assets/images/main/category/delivery.png"), +// grocery: require("@/assets/images/main/category/grocery.png"), +// essentials: require("@/assets/images/main/category/essentials.png"), +// }; interface ChatItemImageProps { - item: OneToOneChatCardSchema; + item: OneToOneChatCardSchema | GroupChatCardSchema; } /** @@ -24,13 +27,14 @@ const ChatItemImage = ({ item }: ChatItemImageProps) => { const isOneOnOne = item.roomType === "ONE_TO_ONE"; if (isOneOnOne) { + const oneToOneItem = item as OneToOneChatCardSchema; return ( {/* 상대방 프로필 이미지 (왼쪽) */} - {item.targetProfileImage ? ( + {oneToOneItem.targetProfileImage ? ( @@ -40,9 +44,9 @@ const ChatItemImage = ({ item }: ChatItemImageProps) => { {/* 내 프로필 이미지 (오른쪽, 겹침) */} - {item.myProfileImage ? ( + {oneToOneItem.myProfileImage ? ( @@ -54,27 +58,28 @@ const ChatItemImage = ({ item }: ChatItemImageProps) => { ); } - // return ( - // - // {item.partyImage ? ( - // - // ) : ( - // - // {item.category && categoryIcons[item.category] ? ( - // - // ) : null} - // - // )} - // - // ); + const groupItem = item as GroupChatCardSchema; + return ( + + {groupItem.partyImage ? ( + + ) : ( + + {/* {groupItem.category && categoryIcons[groupItem.category] ? ( + + ) : null} */} + + )} + + ); }; export default ChatItemImage; From 10368821026baac6a90f08029833ac301a3544a3 Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 14:12:04 +0900 Subject: [PATCH 29/73] =?UTF-8?q?feat/#115:=201:1=20=EB=B0=8F=20=EA=B7=B8?= =?UTF-8?q?=EB=A3=B9=20=EC=B1=84=ED=8C=85=EB=B0=A9=20=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=ED=86=B5=ED=95=A9=20=EC=A1=B0=ED=9A=8C=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/app/(tabs)/chat.tsx | 53 ++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/TinyBite/app/(tabs)/chat.tsx b/TinyBite/app/(tabs)/chat.tsx index f0435bc..b1b6b32 100644 --- a/TinyBite/app/(tabs)/chat.tsx +++ b/TinyBite/app/(tabs)/chat.tsx @@ -1,10 +1,16 @@ import ChatCard from "@/components/chat/ChatCard"; -import { useGetOnetoOneRoomListQuery } from "@/hooks/queries/useChatRoom"; +import { + useGetGroupRoomListQuery, + useGetOnetoOneRoomListQuery, +} from "@/hooks/queries/useChatRoom"; import { colors } from "@/styles/colors"; import { textStyles } from "@/styles/typography/textStyles"; -import { FilterTab, OneToOneChatCardSchema } from "@/types/chat.types"; -import { useFocusEffect } from "expo-router"; -import { useCallback, useState } from "react"; +import { + FilterTab, + GroupChatCardSchema, + OneToOneChatCardSchema, +} from "@/types/chat.types"; +import { useEffect, useState } from "react"; import { FlatList, Image, @@ -21,7 +27,11 @@ const filters: FilterTab[] = ["전체", "참여중인 파티", "1:1 채팅"]; /** * 채팅 아이템 렌더링 함수 */ -const renderChatItem = ({ item }: { item: OneToOneChatCardSchema }) => { +const renderChatItem = ({ + item, +}: { + item: OneToOneChatCardSchema | GroupChatCardSchema; +}) => { return ; }; @@ -37,29 +47,32 @@ const ItemSeparator = () => ; * - 채팅 리스트: 사용자별 채팅 아이템 표시 */ export default function ChatScreen() { - const { data: onetoOneRoomList = [], refetch } = - useGetOnetoOneRoomListQuery(); + const [selectedFilter, setSelectedFilter] = useState("전체"); + const [chatRoomList, setChatRoomList] = useState< + (OneToOneChatCardSchema | GroupChatCardSchema)[] + >([]); - useFocusEffect( - useCallback(() => { - refetch(); - }, [refetch]) - ); + const { data: groupRoomList, isLoading: groupIsLoading } = + useGetGroupRoomListQuery(); + const { data: oneToOneRoomList, isLoading: oneToOneIsLoading } = + useGetOnetoOneRoomListQuery(); - // 선택된 필터 탭 상태 - const [selectedFilter, setSelectedFilter] = useState("전체"); + useEffect(() => { + if (!groupIsLoading && !oneToOneIsLoading) { + const merged = [...(groupRoomList ?? []), ...(oneToOneRoomList ?? [])]; + setChatRoomList(merged); + } + }, [groupIsLoading, oneToOneIsLoading, groupRoomList, oneToOneRoomList]); /** * 필터에 맞는 데이터 필터링 */ - const filteredData: OneToOneChatCardSchema[] = onetoOneRoomList.filter( - (item) => { + const filteredData: (OneToOneChatCardSchema | GroupChatCardSchema)[] = + chatRoomList.filter((item) => { if (selectedFilter === "전체") return true; - if (selectedFilter === "1:1 채팅") return item.roomType === "ONE_TO_ONE"; if (selectedFilter === "참여중인 파티") return item.roomType === "GROUP"; - return true; - } - ); + if (selectedFilter === "1:1 채팅") return item.roomType === "ONE_TO_ONE"; + }); // .map((item) => ({ // id: item.chatRoomId, // lastMessage: item.recentMessage, From 5f900e1cee6c99155421f6eddcce385d13186d64 Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 14:12:22 +0900 Subject: [PATCH 30/73] =?UTF-8?q?feat/#115:=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EC=B1=84=ED=8C=85=20=EC=95=84=EC=9D=B4=ED=85=9C=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/components/chat/ChatCard.tsx | 43 +++++++++++++++------------ 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/TinyBite/components/chat/ChatCard.tsx b/TinyBite/components/chat/ChatCard.tsx index af12001..6b205c2 100644 --- a/TinyBite/components/chat/ChatCard.tsx +++ b/TinyBite/components/chat/ChatCard.tsx @@ -1,18 +1,19 @@ import { colors } from "@/styles/colors"; import { textStyles } from "@/styles/typography/textStyles"; import { + GroupChatCardSchema, + GroupChatStatusType, OneToOneChatCardSchema, OneToOneChatStatusType, - PartyStatusType, } from "@/types/chat.types"; import { useRouter } from "expo-router"; -import { StyleSheet, Text, TouchableOpacity, View } from "react-native"; +import { Image, StyleSheet, Text, TouchableOpacity, View } from "react-native"; import ChatCardImage from "./ChatCardImage"; import OneOnOneChatStatusTag from "./OneOnOneChatStatusTag"; import PartyStatusTag from "./PartyStatusTag"; interface ChatItemProps { - item: OneToOneChatCardSchema; + item: OneToOneChatCardSchema | GroupChatCardSchema; } /** @@ -27,6 +28,9 @@ const ChatItem = ({ item }: ChatItemProps) => { // 채팅 타입 확인 (1:1 채팅인지 파티 채팅인지) const isOneOnOne = item.roomType === "ONE_TO_ONE"; + const oneToOneItem = item as OneToOneChatCardSchema; + const groupItem = item as GroupChatCardSchema; + const handleChatPress = () => { router.navigate({ pathname: "/chat/[id]", @@ -57,7 +61,7 @@ const ChatItem = ({ item }: ChatItemProps) => { ellipsizeMode="tail" > {/* 1:1 채팅은 사용자 이름, 파티 채팅은 파티 제목 표시 */} - {isOneOnOne ? item.targetName : item.partyTitle} + {isOneOnOne ? oneToOneItem.targetName : groupItem.partyTitle} {item.recentTime} @@ -88,28 +92,29 @@ const ChatItem = ({ item }: ChatItemProps) => { {/* 태그 영역: 상태 태그 + 파티 제목(1:1) / 인원수(파티) */} {/* 상태 태그: RoomType에 따라 적절한 태그 컴포넌트 사용 */} - {item.status && - (isOneOnOne ? ( - // 1:1 채팅 상태 태그 (승인 대기, 승인 거절, 승인 완료, 승인 요청, 파티 종료) - - ) : ( - // 파티 채팅 상태 태그 (모집 중, 진행 중, 파티 종료) - - ))} + {isOneOnOne ? ( + // 1:1 채팅 상태 태그 (승인 대기, 승인 거절, 승인 완료, 승인 요청, 파티 종료) + + ) : ( + // 파티 채팅 상태 태그 (모집 중, 진행 중, 파티 종료) + + )} {/* 1:1 채팅일 때 파티 제목 표시 */} - {isOneOnOne && item.partyTitle && ( + {isOneOnOne && ( - {item.partyTitle} + {oneToOneItem.partyTitle} )} {/* 파티 채팅일 때 인원수 표시 */} - {/* {!isOneOnOne && item.memberCount !== undefined && ( + {!isOneOnOne && ( { resizeMode="contain" /> - {item.memberCount} + {groupItem.currentParticipantCnt} - )} */} + )} From 7b3bcffd2d26996b23adadc3213be7da83f113c7 Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 14:13:39 +0900 Subject: [PATCH 31/73] =?UTF-8?q?chore/#115:=20=EB=B2=84=EC=A0=84=200.6.0?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/app.config.js | 2 +- TinyBite/package-lock.json | 6 +++--- TinyBite/package.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/TinyBite/app.config.js b/TinyBite/app.config.js index 2b08ecf..2ecf113 100644 --- a/TinyBite/app.config.js +++ b/TinyBite/app.config.js @@ -4,7 +4,7 @@ module.exports = { name: "한입만", slug: "TinyBite", owner: "tinybite-2025", - version: "0.5.0", + version: "0.6.0", orientation: "portrait", icon: "./assets/images/icon.png", scheme: "tinybite", diff --git a/TinyBite/package-lock.json b/TinyBite/package-lock.json index 6351fb2..abca0a5 100644 --- a/TinyBite/package-lock.json +++ b/TinyBite/package-lock.json @@ -1,12 +1,12 @@ { "name": "tinybite", - "version": "0.5.0", - "lockfileVersion": 5, + "version": "0.6.0", + "lockfileVersion": 6, "requires": true, "packages": { "": { "name": "tinybite", - "version": "0.5.0", + "version": "0.6.0", "dependencies": { "@expo/vector-icons": "^15.0.3", "@react-native-google-signin/google-signin": "^16.0.0", diff --git a/TinyBite/package.json b/TinyBite/package.json index 3288e4e..d332b7c 100644 --- a/TinyBite/package.json +++ b/TinyBite/package.json @@ -1,7 +1,7 @@ { "name": "tinybite", "main": "expo-router/entry", - "version": "0.5.0", + "version": "0.6.0", "scripts": { "start": "expo start", "reset-project": "node ./scripts/reset-project.js", From d1daad7686618ecae758c8cf0eb85d3ae2444975 Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 23:03:02 +0900 Subject: [PATCH 32/73] =?UTF-8?q?fix/#115:=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EC=B1=84=ED=8C=85=20=EC=83=81=ED=83=9C=20=ED=83=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=EC=9D=98=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=ED=83=80=EC=9E=85=20=EB=B0=8F=20=EC=8A=A4=ED=83=80?= =?UTF-8?q?=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/components/chat/PartyStatusTag.tsx | 25 +++++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/TinyBite/components/chat/PartyStatusTag.tsx b/TinyBite/components/chat/PartyStatusTag.tsx index 47f84e2..f1a827b 100644 --- a/TinyBite/components/chat/PartyStatusTag.tsx +++ b/TinyBite/components/chat/PartyStatusTag.tsx @@ -1,10 +1,13 @@ import { colors } from "@/styles/colors"; import { textStyles } from "@/styles/typography/textStyles"; -import { PartyStatusType } from "@/types/chat.types"; +import { + GroupChatStatusLabelMap, + GroupChatStatusType, +} from "@/types/chat.types"; import { StyleSheet, Text, View } from "react-native"; export interface PartyStatusTagProps { - status: PartyStatusType; + status: GroupChatStatusType; } /** @@ -16,21 +19,29 @@ export interface PartyStatusTagProps { const PartyStatusTag = ({ status }: PartyStatusTagProps) => { // 상태별 스타일을 객체로 정의 const statusStyles = { - "모집 중": { + RECRUITING: { + // 모집 중 container: styles.statusTagRecruiting, text: styles.statusTagTextRecruiting, }, - "진행 중": { + COMPLETED: { + // 진행 중 container: styles.statusTagOngoing, text: styles.statusTagTextOngoing, }, - "파티 종료": { + CLOSED: { + // 파티 종료 + container: styles.statusTagEnded, + text: styles.statusTagTextEnded, + }, + CANCELLED: { + // ?? container: styles.statusTagEnded, text: styles.statusTagTextEnded, }, }; - const currentStyle = statusStyles[status] || statusStyles["모집 중"]; + const currentStyle = statusStyles[status] || statusStyles["RECRUITING"]; return ( @@ -41,7 +52,7 @@ const PartyStatusTag = ({ status }: PartyStatusTagProps) => { currentStyle.text, ]} > - {status} + {GroupChatStatusLabelMap[status]} ); From cd658250931da0a98d267f4ce3fdfee1000bf839 Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 23:08:33 +0900 Subject: [PATCH 33/73] =?UTF-8?q?feat/#115:=20=ED=8C=8C=ED=8B=B0=20?= =?UTF-8?q?=EC=B7=A8=EC=86=8C=20=EC=83=81=ED=83=9C=20=ED=83=9C=EA=B7=B8=20?= =?UTF-8?q?=EB=B0=8F=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/components/chat/PartyStatusTag.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/TinyBite/components/chat/PartyStatusTag.tsx b/TinyBite/components/chat/PartyStatusTag.tsx index f1a827b..90f2c71 100644 --- a/TinyBite/components/chat/PartyStatusTag.tsx +++ b/TinyBite/components/chat/PartyStatusTag.tsx @@ -35,9 +35,9 @@ const PartyStatusTag = ({ status }: PartyStatusTagProps) => { text: styles.statusTagTextEnded, }, CANCELLED: { - // ?? - container: styles.statusTagEnded, - text: styles.statusTagTextEnded, + // 파티 취소 + container: styles.statusTagCancelled, + text: styles.statusTagTextCancelled, }, }; @@ -79,6 +79,10 @@ const styles = StyleSheet.create({ statusTagEnded: { backgroundColor: colors.gray[4], }, + // 파티 취소 상태 태그 + statusTagCancelled: { + backgroundColor: colors.red[1], + }, // 상태 태그 텍스트 기본 스타일 statusTagText: {}, // 모집 중 상태 태그 텍스트 @@ -93,4 +97,8 @@ const styles = StyleSheet.create({ statusTagTextEnded: { color: colors.gray[1], }, + // 파티 취소 상태 태그 텍스트 + statusTagTextCancelled: { + color: colors.red[1], + }, }); From f1e52daf36522f422037bb534a62b04f55b20372 Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 23:15:01 +0900 Subject: [PATCH 34/73] =?UTF-8?q?refactor/#115:=201:1=20=EC=B1=84=ED=8C=85?= =?UTF-8?q?=EB=B0=A9=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B0=84=EC=86=8C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/components/layout/ChatRoomLayout.tsx | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/TinyBite/components/layout/ChatRoomLayout.tsx b/TinyBite/components/layout/ChatRoomLayout.tsx index 3fe7185..a0f10c3 100644 --- a/TinyBite/components/layout/ChatRoomLayout.tsx +++ b/TinyBite/components/layout/ChatRoomLayout.tsx @@ -31,11 +31,7 @@ const ChatRoomLayout = ({ const [isPanelVisible, setIsPanelVisible] = useState(false); // 1:1 채팅방 디테일 정보 관리 - const { - data: oneToOneData, - isLoading: oneToOneIsLoading, - isError: oneToOneIsError, - } = useGetOnetoOneRoomDetailQuery(parseInt(chatRoomId), { + const oneToOneQuery = useGetOnetoOneRoomDetailQuery(parseInt(chatRoomId), { enabled: roomType === "ONE_TO_ONE", }); @@ -43,9 +39,9 @@ const ChatRoomLayout = ({ // const roomDetailData = roomType === 'ONE_TO_ONE' ? oneToOneData : groupData; // const roomDetailIsLoading = oneToOneIsLoading || groupIsLoading; // const roomDetailIsError = oneToOneIsError || groupIsError; - const roomDetailData = roomType === "ONE_TO_ONE" ? oneToOneData : null; - const roomDetailIsLoading = oneToOneIsLoading || null; - const roomDetailIsError = oneToOneIsError || null; + const roomDetailData = roomType === "ONE_TO_ONE" ? oneToOneQuery.data : null; + const roomDetailIsLoading = oneToOneQuery.isLoading || null; + const roomDetailIsError = oneToOneQuery.isError || null; const togglePanel = () => { setIsPanelVisible((prev) => !prev); From aa09b2bb4d84ee4ec134cd6581dc1210319f59ae Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 23:16:14 +0900 Subject: [PATCH 35/73] =?UTF-8?q?feat/#115:=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EC=B1=84=ED=8C=85=EB=B0=A9=20=EB=94=94=ED=85=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/components/layout/ChatRoomLayout.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/TinyBite/components/layout/ChatRoomLayout.tsx b/TinyBite/components/layout/ChatRoomLayout.tsx index a0f10c3..74f7580 100644 --- a/TinyBite/components/layout/ChatRoomLayout.tsx +++ b/TinyBite/components/layout/ChatRoomLayout.tsx @@ -1,6 +1,9 @@ import ChatInputBox from "@/components/chat/ChatInputBox"; import ChatRoomHeader from "@/components/ChatRoomHeader"; -import { useGetOnetoOneRoomDetailQuery } from "@/hooks/queries/useChatRoom"; +import { + useGetGroupRoomDetailQuery, + useGetOnetoOneRoomDetailQuery, +} from "@/hooks/queries/useChatRoom"; import { colors } from "@/styles/colors"; import { RoomType } from "@/types/chat.types"; import { useLocalSearchParams, useRouter } from "expo-router"; @@ -34,6 +37,10 @@ const ChatRoomLayout = ({ const oneToOneQuery = useGetOnetoOneRoomDetailQuery(parseInt(chatRoomId), { enabled: roomType === "ONE_TO_ONE", }); + // 그룹 채팅방 디테일 정보 관리 + const GroupQuery = useGetGroupRoomDetailQuery(parseInt(chatRoomId), { + enabled: roomType === "GROUP", + }); // 통합된 데이터 사용 // const roomDetailData = roomType === 'ONE_TO_ONE' ? oneToOneData : groupData; From 0e645f3c439bbaa1b32f5741646f77f516b44021 Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 23:31:17 +0900 Subject: [PATCH 36/73] =?UTF-8?q?feat/#115:=201:1=20=EB=B0=8F=20=EA=B7=B8?= =?UTF-8?q?=EB=A3=B9=20=EC=B1=84=ED=8C=85=EB=B0=A9=20=EC=A0=95=EB=B3=B4?= =?UTF-8?q?=EC=97=90=20roomType=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/types/chat.types.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/TinyBite/types/chat.types.ts b/TinyBite/types/chat.types.ts index 5c21a72..cdefc1f 100644 --- a/TinyBite/types/chat.types.ts +++ b/TinyBite/types/chat.types.ts @@ -67,6 +67,7 @@ export interface OneToOneChatCardSchema { */ export interface OneToOneChatDetailSchema { chatRoomId: number; + roomType: RoomType; participantId: number; participantType: participantType; participantStatus: OneToOneChatStatusType; @@ -113,7 +114,7 @@ export interface GroupChatCardSchema { recentTime: string; // "2026-01-10T23:40:00" partyTitle: string; partyImage: string; - partyStatus: OneToOneChatStatusType; + partyStatus: GroupChatStatusType; recentMessage: string; unreadMessageCnt: number; currentParticipantCnt: number; @@ -124,6 +125,7 @@ export interface GroupChatCardSchema { */ export type GroupChatDetailSchema = { groupChatRoomId: number; + roomType: RoomType; partyId: number; partyTitle: string; status: GroupChatStatusType; From 23c0b6e0d4bdfdc6d78ed410b00aabf6348a20fa Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 23:40:25 +0900 Subject: [PATCH 37/73] =?UTF-8?q?feat/#115:=201:1=20=EB=B0=8F=20=EA=B7=B8?= =?UTF-8?q?=EB=A3=B9=20=EC=B1=84=ED=8C=85=EB=B0=A9=20=EC=83=81=EC=84=B8=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=EC=97=90=20roomType=20=EA=B3=A0=EC=A0=95=20?= =?UTF-8?q?=EA=B0=92=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/types/chat.types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TinyBite/types/chat.types.ts b/TinyBite/types/chat.types.ts index cdefc1f..63d4f43 100644 --- a/TinyBite/types/chat.types.ts +++ b/TinyBite/types/chat.types.ts @@ -67,7 +67,7 @@ export interface OneToOneChatCardSchema { */ export interface OneToOneChatDetailSchema { chatRoomId: number; - roomType: RoomType; + roomType: "ONE_TO_ONE"; participantId: number; participantType: participantType; participantStatus: OneToOneChatStatusType; @@ -125,7 +125,7 @@ export interface GroupChatCardSchema { */ export type GroupChatDetailSchema = { groupChatRoomId: number; - roomType: RoomType; + roomType: "GROUP"; partyId: number; partyTitle: string; status: GroupChatStatusType; From 11a55437c2c28ea797143261958f2a4bfb52ba20 Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 23:40:47 +0900 Subject: [PATCH 38/73] =?UTF-8?q?feat/#115:=201:1=20=EB=B0=8F=20=EA=B7=B8?= =?UTF-8?q?=EB=A3=B9=20=EC=B1=84=ED=8C=85=EB=B0=A9=20=EC=A0=95=EB=B3=B4?= =?UTF-8?q?=EC=97=90=20roomDetail=20=ED=83=80=EC=9E=85=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9=20=EB=B0=8F=20=EC=83=81=ED=83=9C=20=ED=91=9C=EC=8B=9C?= =?UTF-8?q?=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/components/ChatRoomHeader.tsx | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/TinyBite/components/ChatRoomHeader.tsx b/TinyBite/components/ChatRoomHeader.tsx index 2c1a927..ff42ae6 100644 --- a/TinyBite/components/ChatRoomHeader.tsx +++ b/TinyBite/components/ChatRoomHeader.tsx @@ -2,12 +2,12 @@ import OneOnOneChatStatusTag from "@/components/chat/OneOnOneChatStatusTag"; import { colors } from "@/styles/colors"; import { textStyles } from "@/styles/typography/textStyles"; import { + GroupChatDetailSchema, + GroupChatStatusType, OneToOneChatDetailSchema, OneToOneChatStatusType, - PartyStatusType, - RoomType, } from "@/types/chat.types"; -import { router, useLocalSearchParams } from "expo-router"; +import { router } from "expo-router"; import { Image, StyleSheet, Text, TouchableOpacity, View } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; import PartyStatusTag from "./chat/PartyStatusTag"; @@ -16,13 +16,11 @@ const CHEVRON_LEFT_ICON = require("@/assets/images/chevron/chevron-left-36-gray. const MEMBER_COUNT = require("@/assets/images/chat/member-count.png"); interface ChatRoomHeaderProps { - roomDetail: OneToOneChatDetailSchema; + roomDetail: OneToOneChatDetailSchema | GroupChatDetailSchema; } const ChatRoomHeader = ({ roomDetail }: ChatRoomHeaderProps) => { - const { roomType } = useLocalSearchParams<{ - roomType: RoomType; - }>(); + // const data = roomDetail.roomType === 'ONE_TO_ONE' ? roomDetail as OneToOneChatDetailSchema : roomDetail as GroupChatDetailSchema; return ( @@ -31,7 +29,7 @@ const ChatRoomHeader = ({ roomDetail }: ChatRoomHeaderProps) => { - {roomType === "ONE_TO_ONE" ? ( + {roomDetail.roomType === "ONE_TO_ONE" ? ( { numberOfLines={1} ellipsizeMode="tail" > - .임시 파티 제목. + {roomDetail.partyTitle} - .임시 명수. + {roomDetail.currentParticipantCnt} From a052483e118cf27bab1263fd0b619b9cab6c2481 Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 23:51:35 +0900 Subject: [PATCH 39/73] =?UTF-8?q?refacto/#115r:=201:1=20=EC=B1=84=ED=8C=85?= =?UTF-8?q?=EB=B0=A9=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/components/layout/ChatRoomLayout.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TinyBite/components/layout/ChatRoomLayout.tsx b/TinyBite/components/layout/ChatRoomLayout.tsx index 74f7580..81f8565 100644 --- a/TinyBite/components/layout/ChatRoomLayout.tsx +++ b/TinyBite/components/layout/ChatRoomLayout.tsx @@ -44,8 +44,8 @@ const ChatRoomLayout = ({ // 통합된 데이터 사용 // const roomDetailData = roomType === 'ONE_TO_ONE' ? oneToOneData : groupData; - // const roomDetailIsLoading = oneToOneIsLoading || groupIsLoading; - // const roomDetailIsError = oneToOneIsError || groupIsError; + // const roomDetailIsLoading = oneToOneQuery.isLoading || groupIsLoading; + // const roomDetailIsError = oneToOneQuery.isError || groupIsError; const roomDetailData = roomType === "ONE_TO_ONE" ? oneToOneQuery.data : null; const roomDetailIsLoading = oneToOneQuery.isLoading || null; const roomDetailIsError = oneToOneQuery.isError || null; From d70e02b87647c2e9c3ecb88faac3f1b499b0edef Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 23:51:48 +0900 Subject: [PATCH 40/73] =?UTF-8?q?feat/#115:=201:1=20=EC=B1=84=ED=8C=85?= =?UTF-8?q?=EB=B0=A9=20=EC=B0=B8=EC=97=AC=20=EC=8A=B9=EC=9D=B8=20=EB=B0=8F?= =?UTF-8?q?=20=EA=B1=B0=EC=A0=88=20=EB=A1=9C=EC=A7=81=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=EB=A5=BC=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=EC=A0=81=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/hooks/mutations/useChat.ts | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/TinyBite/hooks/mutations/useChat.ts b/TinyBite/hooks/mutations/useChat.ts index d0d6309..d37e858 100644 --- a/TinyBite/hooks/mutations/useChat.ts +++ b/TinyBite/hooks/mutations/useChat.ts @@ -3,14 +3,20 @@ import { useMutation, useQueryClient } from "@tanstack/react-query"; import Toast from "react-native-toast-message"; export const useApproveJoinPartyMutation = ( - partyId: number, - participantId: number, - chatroomId: number + partyId?: number, + participantId?: number, + chatroomId?: number ) => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: () => postApproveJoinParty(partyId, participantId), + mutationFn: async () => { + if (!partyId || !participantId) { + throw new Error("필수 파라미터 누락: approve join party"); + } + + return postApproveJoinParty(partyId, participantId); + }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["getOnetoOneRoomDetail", chatroomId], @@ -29,14 +35,20 @@ export const useApproveJoinPartyMutation = ( }; export const useRejectJoinPartyMutation = ( - partyId: number, - participantId: number, - chatroomId: number + partyId?: number, + participantId?: number, + chatroomId?: number ) => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: () => postRejectJoinParty(partyId, participantId), + mutationFn: async () => { + if (!partyId || !participantId) { + throw new Error("필수 파라미터 누락: approve join party"); + } + + return postRejectJoinParty(partyId, participantId); + }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["getOnetoOneRoomDetail", chatroomId], From 6030dd28dd5fd2e08d5df1a72cb40ec2cd8da5a6 Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 23:52:16 +0900 Subject: [PATCH 41/73] =?UTF-8?q?feat/#115:=201:1=20=EC=B1=84=ED=8C=85?= =?UTF-8?q?=EB=B0=A9=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=EA=B7=B8=EB=A3=B9?= =?UTF-8?q?=20=EC=B1=84=ED=8C=85=EB=B0=A9=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/components/layout/ChatRoomLayout.tsx | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/TinyBite/components/layout/ChatRoomLayout.tsx b/TinyBite/components/layout/ChatRoomLayout.tsx index 81f8565..35150fb 100644 --- a/TinyBite/components/layout/ChatRoomLayout.tsx +++ b/TinyBite/components/layout/ChatRoomLayout.tsx @@ -43,12 +43,10 @@ const ChatRoomLayout = ({ }); // 통합된 데이터 사용 - // const roomDetailData = roomType === 'ONE_TO_ONE' ? oneToOneData : groupData; - // const roomDetailIsLoading = oneToOneQuery.isLoading || groupIsLoading; - // const roomDetailIsError = oneToOneQuery.isError || groupIsError; - const roomDetailData = roomType === "ONE_TO_ONE" ? oneToOneQuery.data : null; - const roomDetailIsLoading = oneToOneQuery.isLoading || null; - const roomDetailIsError = oneToOneQuery.isError || null; + const roomDetailData = + roomType === "ONE_TO_ONE" ? oneToOneQuery.data : GroupQuery.data; + const roomDetailIsLoading = oneToOneQuery.isLoading || GroupQuery.data; + const roomDetailIsError = oneToOneQuery.isError || GroupQuery.data; const togglePanel = () => { setIsPanelVisible((prev) => !prev); @@ -106,10 +104,7 @@ const ChatRoomLayout = ({ keyboardVerticalOffset={0} > - + {/* group 파티장 - 정산하기 */} {/* */} From c3f083dd9220a09f1cee26ca06ce821bdada27ba Mon Sep 17 00:00:00 2001 From: ion Date: Thu, 15 Jan 2026 23:52:35 +0900 Subject: [PATCH 42/73] =?UTF-8?q?feat/#115:=201:1=20=EC=B1=84=ED=8C=85?= =?UTF-8?q?=EB=B0=A9=20=EC=A0=95=EB=B3=B4=20=EC=B2=98=EB=A6=AC=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/chat/ChatRoomStatusHandler.tsx | 51 +++++++++++-------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/TinyBite/components/chat/ChatRoomStatusHandler.tsx b/TinyBite/components/chat/ChatRoomStatusHandler.tsx index ec68af2..d236e0a 100644 --- a/TinyBite/components/chat/ChatRoomStatusHandler.tsx +++ b/TinyBite/components/chat/ChatRoomStatusHandler.tsx @@ -2,41 +2,52 @@ import { useApproveJoinPartyMutation, useRejectJoinPartyMutation, } from "@/hooks/mutations/useChat"; -import { OneToOneChatDetailSchema, RoomType } from "@/types/chat.types"; +import { + GroupChatDetailSchema, + OneToOneChatDetailSchema, +} from "@/types/chat.types"; import { ChatJoinRequestCard } from "./host/ChatJoinRequestCard"; import { ChatJoinAcceptedCard } from "./participant/ChatJoinAcceptedCard"; import { ChatJoinPendingCard } from "./participant/ChatJoinPendingCard"; // type participantType = 'HOST' | 'PARTICIPANT'; // type OneToOneChatStatusType = 'PENDING' | 'REJECTED' | 'APPROVED' | 'REQUESTED' | 'ENDED'; +// type GroupChatStatusType = "RECRUITING" | "COMPLETED" | "CLOSED" | "CANCELLED"; interface ChatRoomStatusHandlerProps { - roomType: RoomType; - chatDetail: OneToOneChatDetailSchema; + chatDetail: OneToOneChatDetailSchema | GroupChatDetailSchema; } -const ChatRoomStatusHandler = ({ - roomType, - chatDetail, -}: ChatRoomStatusHandlerProps) => { - const { mutate: approveMutate } = useApproveJoinPartyMutation( - chatDetail.partyId, - chatDetail.participantId, - chatDetail.chatRoomId - ); - const { mutate: rejectMutate } = useRejectJoinPartyMutation( - chatDetail.partyId, - chatDetail.participantId, - chatDetail.chatRoomId +const ChatRoomStatusHandler = ({ chatDetail }: ChatRoomStatusHandlerProps) => { + const approveMutation = useApproveJoinPartyMutation( + chatDetail.roomType === "ONE_TO_ONE" ? chatDetail.partyId : undefined, + chatDetail.roomType === "ONE_TO_ONE" ? chatDetail.participantId : undefined, + chatDetail.roomType === "ONE_TO_ONE" ? chatDetail.chatRoomId : undefined ); - const { participantType, participantStatus } = chatDetail; + const rejectMutation = useRejectJoinPartyMutation( + chatDetail.roomType === "ONE_TO_ONE" ? chatDetail.partyId : undefined, + chatDetail.roomType === "ONE_TO_ONE" ? chatDetail.participantId : undefined, + chatDetail.roomType === "ONE_TO_ONE" ? chatDetail.chatRoomId : undefined + ); // ONE_TO_ONE이 아니면 아무것도 렌더링하지 않음 - if (roomType !== "ONE_TO_ONE") { + if (chatDetail.roomType !== "ONE_TO_ONE") { return null; } + const { participantType, participantStatus } = chatDetail; + + const handleApprove = () => { + if (chatDetail.roomType !== "ONE_TO_ONE") return; + approveMutation.mutate(); + }; + + const handleReject = () => { + if (chatDetail.roomType !== "ONE_TO_ONE") return; + rejectMutation.mutate(); + }; + // HOST 분기 if (participantType === "HOST") { if (participantStatus === "REQUESTED") { @@ -47,8 +58,8 @@ const ChatRoomStatusHandler = ({ nickname={chatDetail.targetName} location={chatDetail.targetLocation || ""} message="파티에 참여하고 싶어요!" - onApprove={approveMutate} - onReject={rejectMutate} + onApprove={handleApprove} + onReject={handleReject} /> ); } From b908b9862583246ef7ee30bb9ee032bb85534ea9 Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 00:09:49 +0900 Subject: [PATCH 43/73] =?UTF-8?q?feat/#115:=201:1=20=EB=B0=8F=20=EA=B7=B8?= =?UTF-8?q?=EB=A3=B9=20=EC=B1=84=ED=8C=85=EB=B0=A9=20=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EB=A1=9C=EC=A7=81=20=EA=B0=9C=EC=84=A0=20?= =?UTF-8?q?=EB=B0=8F=20=EC=BD=94=EB=93=9C=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/components/layout/ChatRoomLayout.tsx | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/TinyBite/components/layout/ChatRoomLayout.tsx b/TinyBite/components/layout/ChatRoomLayout.tsx index 35150fb..f06314f 100644 --- a/TinyBite/components/layout/ChatRoomLayout.tsx +++ b/TinyBite/components/layout/ChatRoomLayout.tsx @@ -30,23 +30,26 @@ const ChatRoomLayout = ({ id: string; roomType: RoomType; }>(); + const numericChatRoomId = Number(chatRoomId); const router = useRouter(); const [isPanelVisible, setIsPanelVisible] = useState(false); - // 1:1 채팅방 디테일 정보 관리 - const oneToOneQuery = useGetOnetoOneRoomDetailQuery(parseInt(chatRoomId), { + const oneToOneQuery = useGetOnetoOneRoomDetailQuery(numericChatRoomId, { enabled: roomType === "ONE_TO_ONE", }); - // 그룹 채팅방 디테일 정보 관리 - const GroupQuery = useGetGroupRoomDetailQuery(parseInt(chatRoomId), { + const groupQuery = useGetGroupRoomDetailQuery(numericChatRoomId, { enabled: roomType === "GROUP", }); - // 통합된 데이터 사용 - const roomDetailData = - roomType === "ONE_TO_ONE" ? oneToOneQuery.data : GroupQuery.data; - const roomDetailIsLoading = oneToOneQuery.isLoading || GroupQuery.data; - const roomDetailIsError = oneToOneQuery.isError || GroupQuery.data; + const isOneToOne = roomType === "ONE_TO_ONE"; + + const roomDetailData = isOneToOne ? oneToOneQuery.data : groupQuery.data; + const roomDetailIsLoading = isOneToOne + ? oneToOneQuery.isLoading + : groupQuery.isLoading; + const roomDetailIsError = isOneToOne + ? oneToOneQuery.isError + : groupQuery.isError; const togglePanel = () => { setIsPanelVisible((prev) => !prev); From 33a6878930f961263cdca04d4fd6ae75efb364bf Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 00:16:59 +0900 Subject: [PATCH 44/73] =?UTF-8?q?feat/#115:=20=ED=8C=8C=ED=8B=B0=20?= =?UTF-8?q?=EC=9D=B8=EC=9B=90=20=EB=AA=A8=EC=A7=91=20=EC=99=84=EB=A3=8C=20?= =?UTF-8?q?=EB=B0=8F=20=EC=A2=85=EB=A3=8C=20=EB=A1=9C=EC=A7=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/api/partyApi.ts | 13 +++++++++++++ TinyBite/api/urls.ts | 2 ++ 2 files changed, 15 insertions(+) diff --git a/TinyBite/api/partyApi.ts b/TinyBite/api/partyApi.ts index f61d3ca..67c55dc 100644 --- a/TinyBite/api/partyApi.ts +++ b/TinyBite/api/partyApi.ts @@ -310,3 +310,16 @@ export const postRejectJoinParty = async ( ENDPOINT.PARTY.PARTICIPANTS.REJECT(partyId, participantId) ); }; + +/** + * 파티 인원 모집 완료 + */ +export const postCompleteParty = async (partyId: number) => { + await privateAxios.post>(ENDPOINT.PARTY.COMPLETE(partyId)); +}; +/** + * 파티 종료 + */ +export const postSettleParty = async (partyId: number) => { + await privateAxios.post>(ENDPOINT.PARTY.SETTLE(partyId)); +}; diff --git a/TinyBite/api/urls.ts b/TinyBite/api/urls.ts index 0404298..b42ca5b 100644 --- a/TinyBite/api/urls.ts +++ b/TinyBite/api/urls.ts @@ -34,6 +34,8 @@ export const ENDPOINT = { REJECT: (partyId: number, participantId: number) => `/api/parties/${partyId}/participants/${participantId}/reject`, }, + COMPLETE: (partyId: number) => `/api/parties/${partyId}/complete`, + SETTLE: (partyId: number) => `/api/parties/${partyId}/settle`, }, FILE: { UPLOAD_FILE: "api/v1/file/upload", From ed3c55ddb8b80e071db17d276bd1aa0a9456d54b Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 00:19:16 +0900 Subject: [PATCH 45/73] =?UTF-8?q?feat/#115:=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=ED=8C=8C=ED=8B=B0=20=EC=99=84=EB=A3=8C=20?= =?UTF-8?q?=EB=B0=8F=20=EC=A0=95=EC=82=B0=20=EB=A1=9C=EC=A7=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/hooks/mutations/useChat.ts | 54 ++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/TinyBite/hooks/mutations/useChat.ts b/TinyBite/hooks/mutations/useChat.ts index d37e858..4199696 100644 --- a/TinyBite/hooks/mutations/useChat.ts +++ b/TinyBite/hooks/mutations/useChat.ts @@ -1,4 +1,9 @@ -import { postApproveJoinParty, postRejectJoinParty } from "@/api/partyApi"; +import { + postApproveJoinParty, + postCompleteParty, + postRejectJoinParty, + postSettleParty, +} from "@/api/partyApi"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import Toast from "react-native-toast-message"; @@ -65,3 +70,50 @@ export const useRejectJoinPartyMutation = ( }, }); }; + +export const useCompletePartyMutation = ( + partyId: number, + chatroomId: number +) => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: () => postCompleteParty(partyId), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ["getOnetoOneRoomDetail", chatroomId], + }); + }, + onError: (error) => { + Toast.show({ + type: "basicToast", + props: { text: error.message }, + position: "bottom", + bottomOffset: 133, + visibilityTime: 2000, + }); + }, + }); +}; + +export const useSettlePartyMutation = (partyId: number, chatroomId: number) => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: () => postSettleParty(partyId), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ["getOnetoOneRoomDetail", chatroomId], + }); + }, + onError: (error) => { + Toast.show({ + type: "basicToast", + props: { text: error.message }, + position: "bottom", + bottomOffset: 133, + visibilityTime: 2000, + }); + }, + }); +}; From 601b8d9120797e65eefe349310f470bab8074816 Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 00:21:27 +0900 Subject: [PATCH 46/73] =?UTF-8?q?fix/#115:=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EC=B1=84=ED=8C=85=EB=B0=A9=20=EC=99=84=EB=A3=8C=20=EB=B0=8F=20?= =?UTF-8?q?=EC=A0=95=EC=82=B0=20=EB=A1=9C=EC=A7=81=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=ED=95=84=EC=88=98=20=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/hooks/mutations/useChat.ts | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/TinyBite/hooks/mutations/useChat.ts b/TinyBite/hooks/mutations/useChat.ts index 4199696..9f03240 100644 --- a/TinyBite/hooks/mutations/useChat.ts +++ b/TinyBite/hooks/mutations/useChat.ts @@ -72,13 +72,19 @@ export const useRejectJoinPartyMutation = ( }; export const useCompletePartyMutation = ( - partyId: number, - chatroomId: number + partyId?: number, + chatroomId?: number ) => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: () => postCompleteParty(partyId), + mutationFn: async () => { + if (!partyId) { + throw new Error("필수 파라미터 누락: approve join party"); + } + + return postCompleteParty(partyId); + }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["getOnetoOneRoomDetail", chatroomId], @@ -96,11 +102,20 @@ export const useCompletePartyMutation = ( }); }; -export const useSettlePartyMutation = (partyId: number, chatroomId: number) => { +export const useSettlePartyMutation = ( + partyId?: number, + chatroomId?: number +) => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: () => postSettleParty(partyId), + mutationFn: async () => { + if (!partyId) { + throw new Error("필수 파라미터 누락: approve join party"); + } + + return postSettleParty(partyId); + }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["getOnetoOneRoomDetail", chatroomId], From cd17977b54d0bc3ae2f7afe4b6d509a0962e7025 Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 00:22:18 +0900 Subject: [PATCH 47/73] =?UTF-8?q?feat/#115:=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EC=B1=84=ED=8C=85=20=EC=99=84=EB=A3=8C=20=EB=B0=8F=20=EC=A0=95?= =?UTF-8?q?=EC=82=B0=20=EB=AE=A4=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/components/chat/ChatRoomStatusHandler.tsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/TinyBite/components/chat/ChatRoomStatusHandler.tsx b/TinyBite/components/chat/ChatRoomStatusHandler.tsx index d236e0a..9393da1 100644 --- a/TinyBite/components/chat/ChatRoomStatusHandler.tsx +++ b/TinyBite/components/chat/ChatRoomStatusHandler.tsx @@ -1,6 +1,8 @@ import { useApproveJoinPartyMutation, + useCompletePartyMutation, useRejectJoinPartyMutation, + useSettlePartyMutation, } from "@/hooks/mutations/useChat"; import { GroupChatDetailSchema, @@ -31,6 +33,16 @@ const ChatRoomStatusHandler = ({ chatDetail }: ChatRoomStatusHandlerProps) => { chatDetail.roomType === "ONE_TO_ONE" ? chatDetail.chatRoomId : undefined ); + const completeMutation = useCompletePartyMutation( + chatDetail.roomType === "GROUP" ? chatDetail.partyId : undefined, + chatDetail.roomType === "GROUP" ? chatDetail.groupChatRoomId : undefined + ); + + const settleMutation = useSettlePartyMutation( + chatDetail.roomType === "GROUP" ? chatDetail.partyId : undefined, + chatDetail.roomType === "GROUP" ? chatDetail.groupChatRoomId : undefined + ); + // ONE_TO_ONE이 아니면 아무것도 렌더링하지 않음 if (chatDetail.roomType !== "ONE_TO_ONE") { return null; From 4dff15c3169334d805641c7cb5dd7918b811cc3c Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 00:36:03 +0900 Subject: [PATCH 48/73] =?UTF-8?q?feat/#115:=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EC=B1=84=ED=8C=85=20=EC=83=81=EC=84=B8=20=EC=8A=A4=ED=82=A4?= =?UTF-8?q?=EB=A7=88=EC=97=90=20=EC=B0=B8=EC=97=AC=EC=9E=90=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/types/chat.types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/TinyBite/types/chat.types.ts b/TinyBite/types/chat.types.ts index 63d4f43..117500a 100644 --- a/TinyBite/types/chat.types.ts +++ b/TinyBite/types/chat.types.ts @@ -128,6 +128,7 @@ export type GroupChatDetailSchema = { roomType: "GROUP"; partyId: number; partyTitle: string; + participantType: participantType; status: GroupChatStatusType; currentParticipantCnt: number; maxParticipantCnt: number; From ad094528cbb92d9ff8c7f5b7f569c61e6a1b067c Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 00:36:30 +0900 Subject: [PATCH 49/73] =?UTF-8?q?fix/#115:=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EC=B1=84=ED=8C=85=20=EC=83=81=ED=83=9C=20=ED=83=80=EC=9E=85?= =?UTF-8?q?=EC=9D=84=20GroupChatStatusType=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EB=B0=8F=20=EA=B4=80=EB=A0=A8=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../participant/ChatPartyProgressCard.tsx | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/TinyBite/components/chat/participant/ChatPartyProgressCard.tsx b/TinyBite/components/chat/participant/ChatPartyProgressCard.tsx index b948ee8..a188b26 100644 --- a/TinyBite/components/chat/participant/ChatPartyProgressCard.tsx +++ b/TinyBite/components/chat/participant/ChatPartyProgressCard.tsx @@ -1,5 +1,6 @@ import { colors } from "@/styles/colors"; import { textStyles } from "@/styles/typography/textStyles"; +import { GroupChatStatusType } from "@/types/chat.types"; import React, { useEffect } from "react"; import { Image, StyleSheet, Text, View } from "react-native"; import Animated, { @@ -13,10 +14,8 @@ const MEGAPHONE_ICON = require("@/assets/images/chat/notice.png"); const COIN_ICON = require("@/assets/images/chat/coin.png"); const BRAND_ICON = require("@/assets/images/chat/brand-logo-32.png"); -type PartyStatus = "모집 중" | "진행 중" | "파티 종료"; - interface ChatPartyProgressCardProps { - status: PartyStatus; + status: GroupChatStatusType; currentMembers: number; maxMembers: number; } @@ -29,13 +28,13 @@ const ChatPartyProgressCard = ({ const progress = useSharedValue(0); // 상태에 따른 진행도 설정 - const getProgressValue = (status: PartyStatus) => { + const getProgressValue = (status: GroupChatStatusType) => { switch (status) { - case "모집 중": + case "RECRUITING": return 33.33; - case "진행 중": + case "COMPLETED": return 66.66; - case "파티 종료": + case "CLOSED": return 100; default: return 0; @@ -43,23 +42,23 @@ const ChatPartyProgressCard = ({ }; // 상태에 따른 아이콘 및 텍스트 설정 - const getStatusConfig = (status: PartyStatus) => { + const getStatusConfig = (status: GroupChatStatusType) => { switch (status) { - case "모집 중": + case "RECRUITING": return { icon: MEGAPHONE_ICON, title: "파티 참여 완료!", description: "인원을 모집 중입니다.", activeStep: 2, }; - case "진행 중": + case "COMPLETED": return { icon: COIN_ICON, title: "인원 모집 완료!", description: "파티장이 정산을 준비하고 있어요.", activeStep: 3, }; - case "파티 종료": + case "CLOSED": return { icon: BRAND_ICON, title: "수령 완료!", From 0b0477331c608d023e0745119ecbb2d50472c39e Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 00:36:36 +0900 Subject: [PATCH 50/73] =?UTF-8?q?feat/#115:=20=EC=9D=BC=EB=8C=80=EC=9D=BC?= =?UTF-8?q?=20=EC=B1=84=ED=8C=85=EB=B0=A9=EC=97=90=EC=84=9C=20=EA=B7=B8?= =?UTF-8?q?=EB=A3=B9=20=EC=B1=84=ED=8C=85=20=EC=83=81=ED=83=9C=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20=EB=A0=8C=EB=8D=94=EB=A7=81=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/chat/ChatRoomStatusHandler.tsx | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/TinyBite/components/chat/ChatRoomStatusHandler.tsx b/TinyBite/components/chat/ChatRoomStatusHandler.tsx index 9393da1..7279c02 100644 --- a/TinyBite/components/chat/ChatRoomStatusHandler.tsx +++ b/TinyBite/components/chat/ChatRoomStatusHandler.tsx @@ -1,16 +1,17 @@ import { useApproveJoinPartyMutation, - useCompletePartyMutation, useRejectJoinPartyMutation, - useSettlePartyMutation, } from "@/hooks/mutations/useChat"; import { GroupChatDetailSchema, OneToOneChatDetailSchema, } from "@/types/chat.types"; import { ChatJoinRequestCard } from "./host/ChatJoinRequestCard"; +import ChatPartyClosureCard from "./host/ChatPartyClosureCard"; +import { ChatRecruitmentCloseCard } from "./host/ChatRecruitmentCloseCard"; import { ChatJoinAcceptedCard } from "./participant/ChatJoinAcceptedCard"; import { ChatJoinPendingCard } from "./participant/ChatJoinPendingCard"; +import ChatPartyProgressCard from "./participant/ChatPartyProgressCard"; // type participantType = 'HOST' | 'PARTICIPANT'; // type OneToOneChatStatusType = 'PENDING' | 'REJECTED' | 'APPROVED' | 'REQUESTED' | 'ENDED'; @@ -33,21 +34,44 @@ const ChatRoomStatusHandler = ({ chatDetail }: ChatRoomStatusHandlerProps) => { chatDetail.roomType === "ONE_TO_ONE" ? chatDetail.chatRoomId : undefined ); - const completeMutation = useCompletePartyMutation( - chatDetail.roomType === "GROUP" ? chatDetail.partyId : undefined, - chatDetail.roomType === "GROUP" ? chatDetail.groupChatRoomId : undefined - ); + // GROUP인 경우 + if (chatDetail.roomType === "GROUP") { + // HOST 분기 + if (chatDetail.participantType === "HOST") { + if (chatDetail.status === "RECRUITING") { + return ; + } - const settleMutation = useSettlePartyMutation( - chatDetail.roomType === "GROUP" ? chatDetail.partyId : undefined, - chatDetail.roomType === "GROUP" ? chatDetail.groupChatRoomId : undefined - ); + if (chatDetail.status === "COMPLETED") { + return ; + } + + if (chatDetail.status === "CLOSED" || chatDetail.status === "CANCELLED") { + return null; + } + + // 예상치 못한 status + alert(`HOST에게 예상치 못한 status: ${chatDetail.status}`); + return null; + } + + // PARTICIPANT 분기 + if (chatDetail.participantType === "PARTICIPANT") { + return ( + + ); + } - // ONE_TO_ONE이 아니면 아무것도 렌더링하지 않음 - if (chatDetail.roomType !== "ONE_TO_ONE") { + // 예상치 못한 participantType + alert(`예상치 못한 participantType: ${chatDetail.participantType}`); return null; } + // ONE_TO_ONE인 경우 const { participantType, participantStatus } = chatDetail; const handleApprove = () => { From 3e580f7b12abca177b3c27ca3d14f9b87d9711ca Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 00:42:59 +0900 Subject: [PATCH 51/73] =?UTF-8?q?chore/#115:=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EC=B1=84=ED=8C=85=20=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/components/layout/ChatRoomLayout.tsx | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/TinyBite/components/layout/ChatRoomLayout.tsx b/TinyBite/components/layout/ChatRoomLayout.tsx index f06314f..19940d5 100644 --- a/TinyBite/components/layout/ChatRoomLayout.tsx +++ b/TinyBite/components/layout/ChatRoomLayout.tsx @@ -108,20 +108,6 @@ const ChatRoomLayout = ({ > - - {/* group 파티장 - 정산하기 */} - {/* */} - - {/* group 파티장 - 모집 완료 */} - {/* */} - - {/* group 참여자 - 진행상황 */} - {/* */} - {children} From 489367b27aa812e12b906231b3de6fc4ff12dcb0 Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 00:54:40 +0900 Subject: [PATCH 52/73] =?UTF-8?q?feat/#115:=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EC=B1=84=ED=8C=85=EB=B0=A9=EC=97=90=EC=84=9C=20=ED=8C=8C?= =?UTF-8?q?=ED=8B=B0=20=EC=83=81=ED=83=9C=EC=97=90=20=EB=94=B0=EB=A5=B8=20?= =?UTF-8?q?=EB=AA=A8=EC=A7=91=20=EC=B9=B4=EB=93=9C=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=ED=8C=8C?= =?UTF-8?q?=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/chat/ChatRoomStatusHandler.tsx | 9 ++++- .../chat/host/ChatRecruitmentCloseCard.tsx | 38 +++++++++++++++---- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/TinyBite/components/chat/ChatRoomStatusHandler.tsx b/TinyBite/components/chat/ChatRoomStatusHandler.tsx index 7279c02..2266b26 100644 --- a/TinyBite/components/chat/ChatRoomStatusHandler.tsx +++ b/TinyBite/components/chat/ChatRoomStatusHandler.tsx @@ -39,7 +39,14 @@ const ChatRoomStatusHandler = ({ chatDetail }: ChatRoomStatusHandlerProps) => { // HOST 분기 if (chatDetail.participantType === "HOST") { if (chatDetail.status === "RECRUITING") { - return ; + return ( + + ); } if (chatDetail.status === "COMPLETED") { diff --git a/TinyBite/components/chat/host/ChatRecruitmentCloseCard.tsx b/TinyBite/components/chat/host/ChatRecruitmentCloseCard.tsx index 4770bde..dfb792e 100644 --- a/TinyBite/components/chat/host/ChatRecruitmentCloseCard.tsx +++ b/TinyBite/components/chat/host/ChatRecruitmentCloseCard.tsx @@ -1,17 +1,44 @@ import ConfirmModal from "@/components/ConfirmModal"; +import { useCompletePartyMutation } from "@/hooks/mutations/useChat"; import { colors } from "@/styles/colors"; import { textStyles } from "@/styles/typography/textStyles"; +import { useQueryClient } from "@tanstack/react-query"; import React, { useState } from "react"; import { Image, StyleSheet, Text, TouchableOpacity, View } from "react-native"; const GROUP_ICON = require("@/assets/images/chat/group-icon.png"); const FIRESORKS_ICON = require("@/assets/images/chat/fireworks.png"); -export const ChatRecruitmentCloseCard = () => { +interface ChatRecruitmentCloseCardProps { + currentMembers: number; + maxMembers: number; + partyId: number; + groupChatRoomId: number; +} + +export const ChatRecruitmentCloseCard = ({ + currentMembers, + maxMembers, + partyId, + groupChatRoomId, +}: ChatRecruitmentCloseCardProps) => { + const queryClient = useQueryClient(); const [showModal, setShowModal] = useState(false); + const completeMutation = useCompletePartyMutation(partyId, groupChatRoomId); + + const handleCompleteParty = async () => { + await completeMutation.mutateAsync(); + if (completeMutation.isSuccess) { + setShowModal(true); + } + }; + const handleNavigateToChatRoom = () => { - console.log("파티 채팅방 가기"); + setShowModal(false); + queryClient.invalidateQueries({ + queryKey: ["useGetGroupRoomDetailQuery", groupChatRoomId], + }); }; return ( @@ -23,15 +50,12 @@ export const ChatRecruitmentCloseCard = () => { - 모집 중 (3/3명) + 모집 중 ({currentMembers}/{maxMembers}명) {/* 하단 버튼 영역 */} - setShowModal(true)} - > + 모집을 마감하고 정산하기 From d9b0176f023bf3bebbfc51eb3cfcfeb9b7fcba78 Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 00:57:54 +0900 Subject: [PATCH 53/73] =?UTF-8?q?feat/#115:=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EC=B1=84=ED=8C=85=EB=B0=A9=EC=97=90=EC=84=9C=20=ED=8C=8C?= =?UTF-8?q?=ED=8B=B0=20=EC=A2=85=EB=A3=8C=20=EB=A1=9C=EC=A7=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20=EA=B4=80=EB=A0=A8=20=ED=9B=85=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/chat/ChatRoomStatusHandler.tsx | 7 +++++- .../chat/host/ChatPartyClosureCard.tsx | 24 ++++++++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/TinyBite/components/chat/ChatRoomStatusHandler.tsx b/TinyBite/components/chat/ChatRoomStatusHandler.tsx index 2266b26..9a54c4e 100644 --- a/TinyBite/components/chat/ChatRoomStatusHandler.tsx +++ b/TinyBite/components/chat/ChatRoomStatusHandler.tsx @@ -50,7 +50,12 @@ const ChatRoomStatusHandler = ({ chatDetail }: ChatRoomStatusHandlerProps) => { } if (chatDetail.status === "COMPLETED") { - return ; + return ( + + ); } if (chatDetail.status === "CLOSED" || chatDetail.status === "CANCELLED") { diff --git a/TinyBite/components/chat/host/ChatPartyClosureCard.tsx b/TinyBite/components/chat/host/ChatPartyClosureCard.tsx index 98794bd..3f6a8db 100644 --- a/TinyBite/components/chat/host/ChatPartyClosureCard.tsx +++ b/TinyBite/components/chat/host/ChatPartyClosureCard.tsx @@ -1,17 +1,35 @@ import ConfirmModal from "@/components/ConfirmModal"; +import { useSettlePartyMutation } from "@/hooks/mutations/useChat"; import { colors } from "@/styles/colors"; import { textStyles } from "@/styles/typography/textStyles"; +import { useQueryClient } from "@tanstack/react-query"; import React, { useState } from "react"; import { Image, StyleSheet, Text, TouchableOpacity, View } from "react-native"; const GROUP_ICON = require("@/assets/images/chat/group-icon.png"); const BRAND_ICON = require("@/assets/images/chat/brand-logo-50.png"); -const ChatPartyClosureCard = () => { +interface ChatPartyClosureCardProps { + partyId: number; + groupChatRoomId: number; +} + +const ChatPartyClosureCard = ({ + partyId, + groupChatRoomId, +}: ChatPartyClosureCardProps) => { + const queryClient = useQueryClient(); const [showModal, setShowModal] = useState(false); - const handleCloseChatRoom = () => { - console.log("종료하기"); + const settleMutation = useSettlePartyMutation(partyId, groupChatRoomId); + + const handleCloseChatRoom = async () => { + await settleMutation.mutateAsync(); + if (settleMutation.isSuccess) { + queryClient.invalidateQueries({ + queryKey: ["useGetGroupRoomDetailQuery", groupChatRoomId], + }); + } }; return ( From dc2a98b45dbdda1304e7b70539c53b678f2cc960 Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 09:08:15 +0900 Subject: [PATCH 54/73] =?UTF-8?q?fix/#115:=20=ED=8C=8C=ED=8B=B0=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C=20=EB=A1=9C=EC=A7=81=EC=9D=84=20PATCH=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=EB=A1=9C=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=B0=8F=20=EC=9D=BC=EB=8C=80=EC=9D=BC,=20=EA=B7=B8=EB=A3=B9?= =?UTF-8?q?=20=EC=B1=84=ED=8C=85=EB=B0=A9=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BF=BC=EB=A6=AC=EC=97=90=20refetchOnWindowFocus=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/api/partyApi.ts | 3 ++- TinyBite/hooks/queries/useChatRoom.ts | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/TinyBite/api/partyApi.ts b/TinyBite/api/partyApi.ts index 67c55dc..32d0e22 100644 --- a/TinyBite/api/partyApi.ts +++ b/TinyBite/api/partyApi.ts @@ -315,8 +315,9 @@ export const postRejectJoinParty = async ( * 파티 인원 모집 완료 */ export const postCompleteParty = async (partyId: number) => { - await privateAxios.post>(ENDPOINT.PARTY.COMPLETE(partyId)); + await privateAxios.patch>(ENDPOINT.PARTY.COMPLETE(partyId)); }; + /** * 파티 종료 */ diff --git a/TinyBite/hooks/queries/useChatRoom.ts b/TinyBite/hooks/queries/useChatRoom.ts index def5658..828c9e5 100644 --- a/TinyBite/hooks/queries/useChatRoom.ts +++ b/TinyBite/hooks/queries/useChatRoom.ts @@ -11,6 +11,7 @@ export const useGetOnetoOneRoomListQuery = () => { queryKey: ["getOnetoOneRoomList"], queryFn: getOnetoOneRoomList, staleTime: 0, + refetchOnWindowFocus: true, }); }; @@ -30,6 +31,7 @@ export const useGetGroupRoomListQuery = () => { queryKey: ["useGetGroupRoomListQuery"], queryFn: getGroupRoomList, staleTime: 0, + refetchOnWindowFocus: true, }); }; From d345fabc04dc2c9211751afd54972a3c14dcda92 Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 09:08:43 +0900 Subject: [PATCH 55/73] =?UTF-8?q?feat/#115:=20=EC=B1=84=ED=8C=85=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=EC=97=90=EC=84=9C=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EB=B0=8F=201:1=20=EC=B1=84=ED=8C=85=EB=B0=A9=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EC=A1=B0=ED=9A=8C=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=ED=95=84=ED=84=B0=EB=A7=81=20?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/app/(tabs)/chat.tsx | 37 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/TinyBite/app/(tabs)/chat.tsx b/TinyBite/app/(tabs)/chat.tsx index b1b6b32..25e81bb 100644 --- a/TinyBite/app/(tabs)/chat.tsx +++ b/TinyBite/app/(tabs)/chat.tsx @@ -10,7 +10,8 @@ import { GroupChatCardSchema, OneToOneChatCardSchema, } from "@/types/chat.types"; -import { useEffect, useState } from "react"; +import { useFocusEffect } from "expo-router"; +import { useCallback, useMemo, useState } from "react"; import { FlatList, Image, @@ -48,38 +49,30 @@ const ItemSeparator = () => ; */ export default function ChatScreen() { const [selectedFilter, setSelectedFilter] = useState("전체"); - const [chatRoomList, setChatRoomList] = useState< - (OneToOneChatCardSchema | GroupChatCardSchema)[] - >([]); - const { data: groupRoomList, isLoading: groupIsLoading } = - useGetGroupRoomListQuery(); - const { data: oneToOneRoomList, isLoading: oneToOneIsLoading } = - useGetOnetoOneRoomListQuery(); + const groupQuery = useGetGroupRoomListQuery(); + const oneToOneQuery = useGetOnetoOneRoomListQuery(); - useEffect(() => { - if (!groupIsLoading && !oneToOneIsLoading) { - const merged = [...(groupRoomList ?? []), ...(oneToOneRoomList ?? [])]; - setChatRoomList(merged); - } - }, [groupIsLoading, oneToOneIsLoading, groupRoomList, oneToOneRoomList]); + useFocusEffect( + useCallback(() => { + groupQuery.refetch(); + oneToOneQuery.refetch(); + }, []) + ); + + const mergedData = useMemo(() => { + return [...(groupQuery.data ?? []), ...(oneToOneQuery.data ?? [])]; + }, [groupQuery.data, oneToOneQuery.data]); /** * 필터에 맞는 데이터 필터링 */ const filteredData: (OneToOneChatCardSchema | GroupChatCardSchema)[] = - chatRoomList.filter((item) => { + mergedData.filter((item) => { if (selectedFilter === "전체") return true; if (selectedFilter === "참여중인 파티") return item.roomType === "GROUP"; if (selectedFilter === "1:1 채팅") return item.roomType === "ONE_TO_ONE"; }); - // .map((item) => ({ - // id: item.chatRoomId, - // lastMessage: item.recentMessage, - // timestamp: item.recentTime, - // targetName: item.partyTitle || "", // Add missing required property - // ...item, - // })); return ( From a45362be04bf896a08f3e90502bd6183e933d512 Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 09:20:09 +0900 Subject: [PATCH 56/73] =?UTF-8?q?fix/#115:=20axios=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=EC=97=90=EC=84=9C=20withCredentials=20=EC=A0=9C=EA=B1=B0=20?= =?UTF-8?q?=EB=B0=8F=20401/403=20=EC=97=90=EB=9F=AC=20=EC=B2=98=EB=A6=AC?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/api/axios.ts | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/TinyBite/api/axios.ts b/TinyBite/api/axios.ts index 13cd289..95c596e 100644 --- a/TinyBite/api/axios.ts +++ b/TinyBite/api/axios.ts @@ -6,7 +6,6 @@ import * as SecureStore from "expo-secure-store"; // 인증 필요 x export const publicAxios = axios.create({ baseURL: BASE_URL, - withCredentials: true, }); publicAxios.interceptors.request.use( @@ -29,7 +28,6 @@ publicAxios.interceptors.request.use( // 인증 필요 o export const privateAxios = axios.create({ baseURL: BASE_URL, - withCredentials: true, }); privateAxios.interceptors.request.use( @@ -89,14 +87,21 @@ privateAxios.interceptors.response.use( async (error) => { const originalRequest = error.config; - // 401 에러이고 재시도하지 않은 요청인지 확인 - if (error.response?.status === 401 && !originalRequest._retry) { + // 401 | 403 에러이고 재시도하지 않은 요청인지 확인 + if ( + (error.response?.status === 401 || error.response?.status === 403) && + !originalRequest._retry + ) { if (isRefreshing) { return new Promise((resolve, reject) => { failedQueue.push({ resolve, reject }); }) .then((token) => { originalRequest.headers.Authorization = `Bearer ${token}`; + if (originalRequest.url?.includes(ENDPOINT.AUTH.REFRESH)) { + useAuthStore.getState().logout(); + return Promise.reject(error); + } return privateAxios(originalRequest); }) .catch((err) => { @@ -110,6 +115,12 @@ privateAxios.interceptors.response.use( try { const refreshToken = await SecureStore.getItemAsync("refreshToken"); + + if (!refreshToken) { + useAuthStore.getState().logout(); + return Promise.reject(new Error("No refresh token")); + } + const res = await publicAxios.post(ENDPOINT.AUTH.REFRESH, { refreshToken: refreshToken, }); @@ -125,6 +136,10 @@ privateAxios.interceptors.response.use( const newAccessToken = await SecureStore.getItemAsync("accessToken"); processQueue(null, newAccessToken); originalRequest.headers.Authorization = `Bearer ${newAccessToken}`; + if (originalRequest.url?.includes(ENDPOINT.AUTH.REFRESH)) { + useAuthStore.getState().logout(); + return Promise.reject(error); + } return privateAxios(originalRequest); } catch (refreshError) { processQueue(refreshError, null); From e08beeaa479b584a927483fcaebccb10cf56c942 Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 09:23:28 +0900 Subject: [PATCH 57/73] =?UTF-8?q?chore/#115:=20=EB=B2=84=EC=A0=84=200.7.0?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/app.config.js | 2 +- TinyBite/package-lock.json | 6 +++--- TinyBite/package.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/TinyBite/app.config.js b/TinyBite/app.config.js index 2ecf113..23895e2 100644 --- a/TinyBite/app.config.js +++ b/TinyBite/app.config.js @@ -4,7 +4,7 @@ module.exports = { name: "한입만", slug: "TinyBite", owner: "tinybite-2025", - version: "0.6.0", + version: "0.7.0", orientation: "portrait", icon: "./assets/images/icon.png", scheme: "tinybite", diff --git a/TinyBite/package-lock.json b/TinyBite/package-lock.json index abca0a5..18347a3 100644 --- a/TinyBite/package-lock.json +++ b/TinyBite/package-lock.json @@ -1,12 +1,12 @@ { "name": "tinybite", - "version": "0.6.0", - "lockfileVersion": 6, + "version": "0.7.0", + "lockfileVersion": 7, "requires": true, "packages": { "": { "name": "tinybite", - "version": "0.6.0", + "version": "0.7.0", "dependencies": { "@expo/vector-icons": "^15.0.3", "@react-native-google-signin/google-signin": "^16.0.0", diff --git a/TinyBite/package.json b/TinyBite/package.json index d332b7c..aa769d7 100644 --- a/TinyBite/package.json +++ b/TinyBite/package.json @@ -1,7 +1,7 @@ { "name": "tinybite", "main": "expo-router/entry", - "version": "0.6.0", + "version": "0.7.0", "scripts": { "start": "expo start", "reset-project": "node ./scripts/reset-project.js", From b99ae276efec8bf91441ba8bd203dd158a48ee62 Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 11:46:45 +0900 Subject: [PATCH 58/73] =?UTF-8?q?feat/#115:=20=ED=8C=8C=ED=8B=B0=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EC=84=B1=EA=B3=B5=20=EC=8B=9C=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=EC=97=90=EA=B2=8C=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/app/party/create/[type].tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/TinyBite/app/party/create/[type].tsx b/TinyBite/app/party/create/[type].tsx index 4d13d9b..b124c43 100644 --- a/TinyBite/app/party/create/[type].tsx +++ b/TinyBite/app/party/create/[type].tsx @@ -90,6 +90,13 @@ export default function PartyCreateScreen() { onSuccess: (data) => { resetCreateParty(); router.replace("/(tabs)"); + Toast.show({ + type: "basicToast", + props: { text: "파티가 생성되었습니다." }, + position: "bottom", + bottomOffset: 133, + visibilityTime: 2000, + }); }, onError: (error: AxiosError) => { if (error.response?.data) { From b793b6100ccb508bccab9bfdd499feed63970857 Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 11:58:23 +0900 Subject: [PATCH 59/73] =?UTF-8?q?fix/#115:=20=EC=B1=84=ED=8C=85=20?= =?UTF-8?q?=EC=95=84=EC=9D=B4=ED=85=9C=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 프로필 이미지 정렬 및 사용자 이름 최대 너비 조정 --- TinyBite/components/chat/ChatCard.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/TinyBite/components/chat/ChatCard.tsx b/TinyBite/components/chat/ChatCard.tsx index 6b205c2..2a8cb07 100644 --- a/TinyBite/components/chat/ChatCard.tsx +++ b/TinyBite/components/chat/ChatCard.tsx @@ -145,6 +145,7 @@ const styles = StyleSheet.create({ // 프로필 이미지 컨테이너 profileContainer: { marginRight: 16, + justifyContent: "center", }, // 채팅 컨텐츠 영역 chatContent: { @@ -155,11 +156,12 @@ const styles = StyleSheet.create({ flexDirection: "row", justifyContent: "space-between", alignItems: "center", + gap: 4, }, // 사용자 이름 / 파티 제목 userName: { + flexShrink: 1, color: colors.black, - maxWidth: "80%", }, // 타임스탬프 timestamp: { From 6d3aa3976d10e33ffbc5f05a059597c1399525bb Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 11:59:13 +0900 Subject: [PATCH 60/73] =?UTF-8?q?fix/#115:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20'=EC=B0=B8=EC=97=AC=EC=A4=91=EC=9D=B8=20?= =?UTF-8?q?=ED=8C=8C=ED=8B=B0'=20=EA=B1=B0=EB=A6=AC=20=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/components/mypage/MyPartyList.tsx | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/TinyBite/components/mypage/MyPartyList.tsx b/TinyBite/components/mypage/MyPartyList.tsx index 7fd48d5..ddca98e 100644 --- a/TinyBite/components/mypage/MyPartyList.tsx +++ b/TinyBite/components/mypage/MyPartyList.tsx @@ -19,21 +19,22 @@ type TabType = "active" | "hosting"; const MyPartyList = () => { const router = useRouter(); const [activeTab, setActiveTab] = useState("active"); - const { coords, refresh: fetchCoords } = useUserCoords(); - - // 컴포넌트 마운트 시 위치 정보 가져오기 - useEffect(() => { - if (!coords) { - fetchCoords(); - } - }, []); + const { coords, refresh: fetchCoords } = useUserCoords(); + + // 컴포넌트 마운트 시 위치 정보 가져오기 + useEffect(() => { + if (!coords) { + fetchCoords(); + } + }, []); // 참여중인 파티 리스트 조회 const { data: activeParties = [], isLoading: isLoadingActive } = useQuery< PartyItem[] >({ queryKey: ["getActiveParties"], - queryFn:() => getActiveParties(coords?.latitude, coords?.longitude), + queryFn: () => getActiveParties(coords?.latitude, coords?.longitude), + enabled: activeTab === "active", }); // 호스팅 중인 파티 리스트 조회 @@ -41,7 +42,7 @@ const MyPartyList = () => { PartyItem[] >({ queryKey: ["getHostingParties"], - queryFn:() => getHostingParties(coords?.latitude, coords?.longitude), + queryFn: () => getHostingParties(coords?.latitude, coords?.longitude), enabled: activeTab === "hosting", }); From f81f999bc12928c1b68debafbe47d9bff710df12 Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 12:00:57 +0900 Subject: [PATCH 61/73] =?UTF-8?q?feat/#115:=20=EC=B1=84=ED=8C=85=EB=B0=A9?= =?UTF-8?q?=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=EC=BF=BC=EB=A6=AC=20?= =?UTF-8?q?=EB=AC=B4=ED=9A=A8=ED=99=94=20=EB=B0=A9=EC=8B=9D=20=ED=86=B5?= =?UTF-8?q?=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/host/ChatPartyClosureCard.tsx | 7 +++-- .../chat/host/ChatRecruitmentCloseCard.tsx | 7 +++-- TinyBite/hooks/mutations/useChat.ts | 28 +++++++++++-------- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/TinyBite/components/chat/host/ChatPartyClosureCard.tsx b/TinyBite/components/chat/host/ChatPartyClosureCard.tsx index 3f6a8db..30f5497 100644 --- a/TinyBite/components/chat/host/ChatPartyClosureCard.tsx +++ b/TinyBite/components/chat/host/ChatPartyClosureCard.tsx @@ -26,9 +26,10 @@ const ChatPartyClosureCard = ({ const handleCloseChatRoom = async () => { await settleMutation.mutateAsync(); if (settleMutation.isSuccess) { - queryClient.invalidateQueries({ - queryKey: ["useGetGroupRoomDetailQuery", groupChatRoomId], - }); + // queryClient.invalidateQueries({ + // queryKey: ["useGetGroupRoomDetailQuery", groupChatRoomId], + // }); + queryClient.invalidateQueries(); } }; diff --git a/TinyBite/components/chat/host/ChatRecruitmentCloseCard.tsx b/TinyBite/components/chat/host/ChatRecruitmentCloseCard.tsx index dfb792e..64d9ab8 100644 --- a/TinyBite/components/chat/host/ChatRecruitmentCloseCard.tsx +++ b/TinyBite/components/chat/host/ChatRecruitmentCloseCard.tsx @@ -36,9 +36,10 @@ export const ChatRecruitmentCloseCard = ({ const handleNavigateToChatRoom = () => { setShowModal(false); - queryClient.invalidateQueries({ - queryKey: ["useGetGroupRoomDetailQuery", groupChatRoomId], - }); + // queryClient.invalidateQueries({ + // queryKey: ["useGetGroupRoomDetailQuery", groupChatRoomId], + // }); + queryClient.invalidateQueries(); }; return ( diff --git a/TinyBite/hooks/mutations/useChat.ts b/TinyBite/hooks/mutations/useChat.ts index 9f03240..6fff127 100644 --- a/TinyBite/hooks/mutations/useChat.ts +++ b/TinyBite/hooks/mutations/useChat.ts @@ -23,9 +23,10 @@ export const useApproveJoinPartyMutation = ( return postApproveJoinParty(partyId, participantId); }, onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: ["getOnetoOneRoomDetail", chatroomId], - }); + // queryClient.invalidateQueries({ + // queryKey: ["getOnetoOneRoomDetail", chatroomId], + // }); + queryClient.invalidateQueries(); }, onError: () => { Toast.show({ @@ -55,9 +56,10 @@ export const useRejectJoinPartyMutation = ( return postRejectJoinParty(partyId, participantId); }, onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: ["getOnetoOneRoomDetail", chatroomId], - }); + // queryClient.invalidateQueries({ + // queryKey: ["getOnetoOneRoomDetail", chatroomId], + // }); + queryClient.invalidateQueries(); }, onError: () => { Toast.show({ @@ -86,9 +88,10 @@ export const useCompletePartyMutation = ( return postCompleteParty(partyId); }, onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: ["getOnetoOneRoomDetail", chatroomId], - }); + // queryClient.invalidateQueries({ + // queryKey: ["useGetGroupRoomDetailQuery", chatroomId], + // }); + queryClient.invalidateQueries(); }, onError: (error) => { Toast.show({ @@ -117,9 +120,10 @@ export const useSettlePartyMutation = ( return postSettleParty(partyId); }, onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: ["getOnetoOneRoomDetail", chatroomId], - }); + // queryClient.invalidateQueries({ + // queryKey: ["useGetGroupRoomDetailQuery", chatroomId], + // }); + queryClient.invalidateQueries(); }, onError: (error) => { Toast.show({ From b48e838b886aa6f8fd4359e2ae8e2eda135dbdc3 Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 12:01:14 +0900 Subject: [PATCH 62/73] =?UTF-8?q?fix(style)/#115:=20=EC=B1=84=ED=8C=85=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0=20-=20=EA=B5=AC=EB=B6=84=EC=84=A0=20=EC=97=AC?= =?UTF-8?q?=EB=B0=B1=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/app/(tabs)/chat.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TinyBite/app/(tabs)/chat.tsx b/TinyBite/app/(tabs)/chat.tsx index 25e81bb..0143fe1 100644 --- a/TinyBite/app/(tabs)/chat.tsx +++ b/TinyBite/app/(tabs)/chat.tsx @@ -157,6 +157,7 @@ const styles = StyleSheet.create({ flex: 1, backgroundColor: colors.background, }, + // 상단 SafeArea 배경색 (메인 색상) safeAreaTop: { backgroundColor: colors.main, @@ -225,8 +226,7 @@ const styles = StyleSheet.create({ separator: { height: 1, backgroundColor: colors.gray[4], - marginLeft: "10%", - marginRight: "10%", + marginHorizontal: 20, }, // 빈 상태 컨테이너 emptyContainer: { From c8b4b3371ba23dabee4e453d75f0097b167bd61c Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 12:01:41 +0900 Subject: [PATCH 63/73] =?UTF-8?q?feat/#115:=20=ED=8C=8C=ED=8B=B0=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EB=B0=8F=20=EC=88=98=EC=A0=95=20=EC=8A=A4?= =?UTF-8?q?=ED=86=A0=EC=96=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/stores/creatingPartyStore.ts | 30 +++++------- TinyBite/stores/editPartyStore.ts | 68 +++++++++++++++++---------- 2 files changed, 54 insertions(+), 44 deletions(-) diff --git a/TinyBite/stores/creatingPartyStore.ts b/TinyBite/stores/creatingPartyStore.ts index 7a29633..671f48d 100644 --- a/TinyBite/stores/creatingPartyStore.ts +++ b/TinyBite/stores/creatingPartyStore.ts @@ -20,7 +20,7 @@ export interface CreatingPartyState { partyTitle: string; totalAmount: string; numberOfPeople: number; - pickUpLocation: PickupLocation; + pickUpLocation: PickupLocation | null; detailedDescription: string; productLink: string; addPhoto: (uri: string, mimeType: string, fileName: string) => void; @@ -29,7 +29,7 @@ export interface CreatingPartyState { setPartyTitle: (title: string) => void; setTotalAmount: (amount: string) => void; setNumberOfPeople: (number: number) => void; - setPickUpLocation: (location: PickupLocation) => void; + setPickUpLocation: (location: PickupLocation | null) => void; setDetailedDescription: (description: string) => void; setProductLink: (link: string) => void; resetCreateParty: () => void; @@ -42,11 +42,7 @@ export const useCreatingPartyStore = create((set, get) => ({ partyTitle: "", totalAmount: "", numberOfPeople: 2, - pickUpLocation: { - place: "", - pickupLatitude: 0, - pickupLongitude: 0, - }, + pickUpLocation: null, detailedDescription: "", productLink: "", addPhoto: (uri: string, mimeType: string, fileName: string) => { @@ -103,13 +99,15 @@ export const useCreatingPartyStore = create((set, get) => ({ numberOfPeople: number, })); }, - setPickUpLocation: (location: PickupLocation) => { + setPickUpLocation: (location: PickupLocation | null) => { set(() => ({ - pickUpLocation: { - place: location.place, - pickupLatitude: location.pickupLatitude, - pickupLongitude: location.pickupLongitude, - }, + pickUpLocation: location + ? { + place: location.place, + pickupLatitude: location.pickupLatitude, + pickupLongitude: location.pickupLongitude, + } + : null, })); }, setDetailedDescription: (description: string) => { @@ -130,11 +128,7 @@ export const useCreatingPartyStore = create((set, get) => ({ partyTitle: "", totalAmount: "", numberOfPeople: 2, - pickUpLocation: { - place: "", - pickupLatitude: 37.55103512680912, - pickupLongitude: 126.9254146711746, - }, + pickUpLocation: null, detailedDescription: "", productLink: "", })); diff --git a/TinyBite/stores/editPartyStore.ts b/TinyBite/stores/editPartyStore.ts index c62d6eb..9dd828d 100644 --- a/TinyBite/stores/editPartyStore.ts +++ b/TinyBite/stores/editPartyStore.ts @@ -8,6 +8,8 @@ export interface PhotoUrl { } export interface editPartyState { + originalInfo: PartyDetail | null; + seq: number; partyId: number; photos: (Photo | PhotoUrl)[]; @@ -26,9 +28,7 @@ export interface editPartyState { }; pickupLocation: { isEdited: boolean; - place: string; - pickupLatitude: number; - pickupLongitude: number; + value: PickupLocation | null; }; description: string; productLink: { @@ -36,20 +36,23 @@ export interface editPartyState { value: string; }; - setInitialPartyInfo: (info: PartyDetail) => void; + setOriginalInfo: (info: PartyDetail) => void; + setInitialPartyInfo: () => void; addPhoto: (uri: string, mimeType: string, fileName: string) => void; deletePhoto: (id: number) => void; setRepresentativePhoto: (id: number) => void; setPartyTitle: (title: string) => void; setTotalAmount: (amount: string) => void; - setPickUpLocation: (location: PickupLocation) => void; + setPickUpLocation: (location: PickupLocation | null) => void; setDetailedDescription: (description: string) => void; setProductLink: (link: string) => void; setMaxParticipants: (number: number) => void; resetEditParty: () => void; } -export const useEditPartyStore = create((set) => ({ +export const useEditPartyStore = create((set, get) => ({ + originalInfo: null, + seq: 0, partyId: 0, photos: [], @@ -68,9 +71,7 @@ export const useEditPartyStore = create((set) => ({ }, pickupLocation: { isEdited: false, - place: "", - pickupLatitude: 0, - pickupLongitude: 0, + value: null, }, description: "", productLink: { @@ -78,7 +79,16 @@ export const useEditPartyStore = create((set) => ({ value: "", }, - setInitialPartyInfo: (info: PartyDetail) => + setOriginalInfo: (info: PartyDetail) => + set({ + originalInfo: info, + }), + setInitialPartyInfo: () => { + const info = get().originalInfo; + if (!info) { + return; + } + set({ seq: info.images?.length || 0, partyId: info.partyId, @@ -101,16 +111,19 @@ export const useEditPartyStore = create((set) => ({ }, pickupLocation: { isEdited: false, - place: info.pickupLocation.place, - pickupLatitude: info.pickupLocation.pickupLatitude, - pickupLongitude: info.pickupLocation.pickupLongitude, + value: { + place: info.pickupLocation.place, + pickupLatitude: info.pickupLocation.pickupLatitude, + pickupLongitude: info.pickupLocation.pickupLongitude, + }, }, description: info.description || "", productLink: { isEdited: false, value: info.productLink?.url || "", }, - }), + }); + }, setPartyTitle: (title: string) => { set(() => ({ title: { @@ -166,16 +179,19 @@ export const useEditPartyStore = create((set) => ({ }, })); }, - setPickUpLocation: (location: PickupLocation) => { + setPickUpLocation: (location: PickupLocation | null) => set(() => ({ - pickupLocation: { - isEdited: true, - place: location.place, - pickupLatitude: location.pickupLatitude, - pickupLongitude: location.pickupLongitude, - }, - })); - }, + pickupLocation: location + ? { + isEdited: true, + value: { + place: location.place, + pickupLatitude: location.pickupLatitude, + pickupLongitude: location.pickupLongitude, + }, + } + : { isEdited: true, value: null }, + })), setDetailedDescription: (description: string) => { set(() => ({ description: description, @@ -199,6 +215,8 @@ export const useEditPartyStore = create((set) => ({ }, resetEditParty: () => set({ + originalInfo: null, + seq: 0, partyId: 0, photos: [], @@ -217,9 +235,7 @@ export const useEditPartyStore = create((set) => ({ }, pickupLocation: { isEdited: false, - place: "", - pickupLatitude: 37.569, - pickupLongitude: 126.991, + value: null, }, description: "", productLink: { From f7b34f368a0015a757bd9f9bc112a53933f067f6 Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 12:03:01 +0900 Subject: [PATCH 64/73] =?UTF-8?q?feat/#115:=20=EC=B1=84=ED=8C=85=EB=B0=A9?= =?UTF-8?q?=20=EC=A0=95=EB=B3=B4=20=EC=B2=98=EB=A6=AC=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=ED=95=84=EC=88=98=20?= =?UTF-8?q?=EA=B0=92=20=EA=B2=80=EC=A6=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/app/party-detail/[id].tsx | 8 ++-- TinyBite/app/party/create/[type].tsx | 21 ++++++++--- TinyBite/app/party/edit/[type].tsx | 52 ++++++++++++++++++++++---- TinyBite/app/search/location/index.tsx | 16 +++++--- 4 files changed, 73 insertions(+), 24 deletions(-) diff --git a/TinyBite/app/party-detail/[id].tsx b/TinyBite/app/party-detail/[id].tsx index cb46d56..295aee5 100644 --- a/TinyBite/app/party-detail/[id].tsx +++ b/TinyBite/app/party-detail/[id].tsx @@ -43,9 +43,7 @@ export default function PartyDetailScreen() { const queryClient = useQueryClient(); const { id } = useLocalSearchParams<{ id: string }>(); const partyId = id ? parseInt(id, 10) : 0; - const setInitialPartyInfo = useEditPartyStore( - (state) => state.setInitialPartyInfo - ); + const setOriginalInfo = useEditPartyStore((state) => state.setOriginalInfo); // 현재 로그인한 사용자 정보 조회 const { user } = useAuthStore(); @@ -93,9 +91,9 @@ export default function PartyDetailScreen() { // 데이터를 성공적으로 받아왔을 시 Store 업데이트 (렌더링 중 업데이트 방지) useEffect(() => { if (isSuccess && partyDetail) { - setInitialPartyInfo(partyDetail); + setOriginalInfo(partyDetail); } - }, [isSuccess, partyDetail, setInitialPartyInfo]); + }, [isSuccess, partyDetail, setOriginalInfo]); // 에러 발생 시 if (error && !isLoading) { diff --git a/TinyBite/app/party/create/[type].tsx b/TinyBite/app/party/create/[type].tsx index b124c43..707febe 100644 --- a/TinyBite/app/party/create/[type].tsx +++ b/TinyBite/app/party/create/[type].tsx @@ -134,6 +134,17 @@ export default function PartyCreateScreen() { }; const showCorrectLinkToast = () => { + if (!isValid) { + Toast.show({ + type: "basicToast", + props: { text: "필수 값을 채워주세요." }, + position: "bottom", + bottomOffset: 133, + visibilityTime: 2000, + }); + return; + } + Toast.show({ type: "basicToast", props: { text: "올바른 URL 형식으로 입력해주세요." }, @@ -161,9 +172,9 @@ export default function PartyCreateScreen() { totalPrice: Number(totalAmount), maxParticipants: numberOfPeople, pickupLocation: { - place: pickUpLocation.place, - pickupLatitude: pickUpLocation.pickupLatitude, - pickupLongitude: pickUpLocation.pickupLongitude, + place: pickUpLocation!.place, + pickupLatitude: pickUpLocation!.pickupLatitude, + pickupLongitude: pickUpLocation!.pickupLongitude, }, ...(photoStringList && { images: photoStringList }), ...(productLink && { productLink }), @@ -177,7 +188,7 @@ export default function PartyCreateScreen() { <> - + diff --git a/TinyBite/app/party/edit/[type].tsx b/TinyBite/app/party/edit/[type].tsx index af0c884..540af0a 100644 --- a/TinyBite/app/party/edit/[type].tsx +++ b/TinyBite/app/party/edit/[type].tsx @@ -16,6 +16,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query"; import { AxiosError } from "axios"; import { router, useLocalSearchParams } from "expo-router"; import { StatusBar } from "expo-status-bar"; +import { useEffect } from "react"; import { FlatList, Pressable, StyleSheet, View } from "react-native"; import { KeyboardAwareScrollView } from "react-native-keyboard-controller"; import { SafeAreaView } from "react-native-safe-area-context"; @@ -58,6 +59,7 @@ export default function PartyCreateScreen() { pickupLocation, description, productLink, + setInitialPartyInfo, setPartyTitle, setTotalAmount, setPickUpLocation, @@ -74,6 +76,7 @@ export default function PartyCreateScreen() { pickupLocation: state.pickupLocation, description: state.description, productLink: state.productLink, + setInitialPartyInfo: state.setInitialPartyInfo, setPartyTitle: state.setPartyTitle, setTotalAmount: state.setTotalAmount, setPickUpLocation: state.setPickUpLocation, @@ -121,6 +124,10 @@ export default function PartyCreateScreen() { }, }); + useEffect(() => { + setInitialPartyInfo(); + }, [setInitialPartyInfo]); + const renderItem = ({ item }: { item: Photo | PhotoUrl }) => { return ; }; @@ -138,7 +145,30 @@ export default function PartyCreateScreen() { } }; + const isValid = (): boolean => { + if ( + title.value && + totalPrice.value && + maxParticipants.value && + pickupLocation.value + ) { + return true; + } + return false; + }; + const onClickEditParty = async () => { + if (!isValid) { + Toast.show({ + type: "basicToast", + props: { text: "필수 값을 채워주세요." }, + position: "bottom", + bottomOffset: 133, + visibilityTime: 2000, + }); + return; + } + if (productLink.isEdited && !isValidLink(productLink.value)) { Toast.show({ type: "basicToast", @@ -179,11 +209,6 @@ export default function PartyCreateScreen() { // 필수 필드 images: allImageUrls, description: description, - pickupLocation: { - place: pickupLocation.place, - pickupLatitude: pickupLocation.pickupLatitude, - pickupLongitude: pickupLocation.pickupLongitude, - }, }; if (title.isEdited) { @@ -198,6 +223,13 @@ export default function PartyCreateScreen() { if (productLink.isEdited) { body.productLink = productLink.value; } + if (pickupLocation.isEdited && pickupLocation.value) { + body.pickupLocation = { + place: pickupLocation.value.place, + pickupLatitude: pickupLocation.value.pickupLatitude, + pickupLongitude: pickupLocation.value.pickupLongitude, + }; + } await EditPartyMutation.mutateAsync({ partyId: partyId, @@ -212,7 +244,7 @@ export default function PartyCreateScreen() { <> - + @@ -305,7 +337,11 @@ export default function PartyCreateScreen() { )} - + diff --git a/TinyBite/app/search/location/index.tsx b/TinyBite/app/search/location/index.tsx index e3210bc..c44e00a 100644 --- a/TinyBite/app/search/location/index.tsx +++ b/TinyBite/app/search/location/index.tsx @@ -77,12 +77,16 @@ export default function PartyPlaceSearch() { }; const onPressDone = () => { - const locationData: PickupLocation = { - place: selectedItem?.place_name!, - pickupLatitude: parseFloat(selectedItem?.y!), - pickupLongitude: parseFloat(selectedItem?.x!), - }; - setPickUpLocation(locationData); + if (selectedItem) { + const locationData: PickupLocation = { + place: selectedItem.place_name, + pickupLatitude: parseFloat(selectedItem.y), + pickupLongitude: parseFloat(selectedItem.x), + }; + setPickUpLocation(locationData); + } else { + setPickUpLocation(null); + } router.back(); }; From 9f99af6ee28b716faf0f32a7b00b97df1381bed2 Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 12:03:08 +0900 Subject: [PATCH 65/73] =?UTF-8?q?feat/#115:=20CreatePartyPageHeader?= =?UTF-8?q?=EC=97=90=EC=84=9C=20action=20prop=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20=EB=B2=84=ED=8A=BC=20=ED=81=B4=EB=A6=AD=20=EC=8B=9C?= =?UTF-8?q?=20=EB=8F=99=EC=9E=91=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/components/CreatePartyPageHeader.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/TinyBite/components/CreatePartyPageHeader.tsx b/TinyBite/components/CreatePartyPageHeader.tsx index 0485ef5..a8f38e0 100644 --- a/TinyBite/components/CreatePartyPageHeader.tsx +++ b/TinyBite/components/CreatePartyPageHeader.tsx @@ -8,13 +8,22 @@ const CHEVRON_LEFT_ICON = require("@/assets/images/chevron/chevron-left-36-gray. interface CreatePartyPageHeaderProps { title: string; + action?: () => void; } -const CreatePartyPageHeader = ({ title }: CreatePartyPageHeaderProps) => { +const CreatePartyPageHeader = ({ + title, + action, +}: CreatePartyPageHeaderProps) => { return ( - router.back()}> + { + action?.(); + router.back(); + }} + > From 4c77a68b263aba5c984dff92fb02dc8bcbe0cbd3 Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 12:39:49 +0900 Subject: [PATCH 66/73] =?UTF-8?q?fix:=20=ED=8C=8C=ED=8B=B0=20=EC=88=98?= =?UTF-8?q?=EC=A0=95,=20=EC=9E=A5=EC=86=8C=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EB=B0=8F=20=EB=9D=BC=EC=9A=B0=ED=8C=85/=EB=84=A4=EB=B9=84?= =?UTF-8?q?=EA=B2=8C=EC=9D=B4=EC=85=98=20=ED=9D=90=EB=A6=84=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 파티 수정 화면 및 사진 업로드/검증 로직 버그 수정 - 카카오 API 기반 장소 검색 컴포넌트 동작 오류 수정 - 파티 검색 무한 스크롤 및 최근 검색어 처리 개선 - (app) prefix 라우팅 누락 및 경로 오류 수정 - 로그인/회원가입 이동 및 뒤로가기 네비게이션 문제 수정 - 전반적인 네비게이션 및 상태 관리 로직 정리 --- TinyBite/app/{ => (app)}/(mypage)/edit.tsx | 0 .../app/{ => (app)}/(mypage)/neighborhood.tsx | 0 .../app/{ => (app)}/(mypage)/notification.tsx | 0 TinyBite/app/{ => (app)}/(mypage)/setting.tsx | 0 TinyBite/app/{ => (app)}/(tabs)/_layout.tsx | 0 TinyBite/app/{ => (app)}/(tabs)/chat.tsx | 0 TinyBite/app/{ => (app)}/(tabs)/index.tsx | 0 TinyBite/app/{ => (app)}/(tabs)/mypage.tsx | 0 TinyBite/app/{ => (app)}/camera.tsx | 0 TinyBite/app/{ => (app)}/chat/[id].tsx | 0 TinyBite/app/{ => (app)}/gallery-preview.tsx | 0 .../app/{ => (app)}/party-detail/[id].tsx | 0 .../app/{ => (app)}/party/create/[type].tsx | 0 .../app/{ => (app)}/party/edit/[type].tsx | 0 .../app/{ => (app)}/search/location/index.tsx | 0 TinyBite/app/{ => (app)}/search/search.tsx | 2 +- TinyBite/app/(auth)/login/login.tsx | 22 +++++++++++++++++-- TinyBite/app/(auth)/signup/complete.tsx | 2 +- TinyBite/app/(auth)/signup/region.tsx | 2 +- TinyBite/app/_layout.tsx | 9 ++++++-- TinyBite/app/index.tsx | 2 +- TinyBite/components/chat/ChatBottomPanel.tsx | 6 ++--- TinyBite/components/chat/ChatCard.tsx | 2 +- .../chat/participant/ChatJoinAcceptedCard.tsx | 11 +++++----- .../components/main/FloatingMenuOverlay.tsx | 6 ++--- TinyBite/components/main/MainHeader.tsx | 2 +- TinyBite/components/main/PartyList.tsx | 2 +- TinyBite/components/mypage/MyPartyList.tsx | 2 +- .../components/search/SearchResultList.tsx | 2 +- TinyBite/hooks/mutations/useParty.ts | 2 +- TinyBite/stores/authStore.ts | 2 +- 31 files changed, 50 insertions(+), 26 deletions(-) rename TinyBite/app/{ => (app)}/(mypage)/edit.tsx (100%) rename TinyBite/app/{ => (app)}/(mypage)/neighborhood.tsx (100%) rename TinyBite/app/{ => (app)}/(mypage)/notification.tsx (100%) rename TinyBite/app/{ => (app)}/(mypage)/setting.tsx (100%) rename TinyBite/app/{ => (app)}/(tabs)/_layout.tsx (100%) rename TinyBite/app/{ => (app)}/(tabs)/chat.tsx (100%) rename TinyBite/app/{ => (app)}/(tabs)/index.tsx (100%) rename TinyBite/app/{ => (app)}/(tabs)/mypage.tsx (100%) rename TinyBite/app/{ => (app)}/camera.tsx (100%) rename TinyBite/app/{ => (app)}/chat/[id].tsx (100%) rename TinyBite/app/{ => (app)}/gallery-preview.tsx (100%) rename TinyBite/app/{ => (app)}/party-detail/[id].tsx (100%) rename TinyBite/app/{ => (app)}/party/create/[type].tsx (100%) rename TinyBite/app/{ => (app)}/party/edit/[type].tsx (100%) rename TinyBite/app/{ => (app)}/search/location/index.tsx (100%) rename TinyBite/app/{ => (app)}/search/search.tsx (98%) diff --git a/TinyBite/app/(mypage)/edit.tsx b/TinyBite/app/(app)/(mypage)/edit.tsx similarity index 100% rename from TinyBite/app/(mypage)/edit.tsx rename to TinyBite/app/(app)/(mypage)/edit.tsx diff --git a/TinyBite/app/(mypage)/neighborhood.tsx b/TinyBite/app/(app)/(mypage)/neighborhood.tsx similarity index 100% rename from TinyBite/app/(mypage)/neighborhood.tsx rename to TinyBite/app/(app)/(mypage)/neighborhood.tsx diff --git a/TinyBite/app/(mypage)/notification.tsx b/TinyBite/app/(app)/(mypage)/notification.tsx similarity index 100% rename from TinyBite/app/(mypage)/notification.tsx rename to TinyBite/app/(app)/(mypage)/notification.tsx diff --git a/TinyBite/app/(mypage)/setting.tsx b/TinyBite/app/(app)/(mypage)/setting.tsx similarity index 100% rename from TinyBite/app/(mypage)/setting.tsx rename to TinyBite/app/(app)/(mypage)/setting.tsx diff --git a/TinyBite/app/(tabs)/_layout.tsx b/TinyBite/app/(app)/(tabs)/_layout.tsx similarity index 100% rename from TinyBite/app/(tabs)/_layout.tsx rename to TinyBite/app/(app)/(tabs)/_layout.tsx diff --git a/TinyBite/app/(tabs)/chat.tsx b/TinyBite/app/(app)/(tabs)/chat.tsx similarity index 100% rename from TinyBite/app/(tabs)/chat.tsx rename to TinyBite/app/(app)/(tabs)/chat.tsx diff --git a/TinyBite/app/(tabs)/index.tsx b/TinyBite/app/(app)/(tabs)/index.tsx similarity index 100% rename from TinyBite/app/(tabs)/index.tsx rename to TinyBite/app/(app)/(tabs)/index.tsx diff --git a/TinyBite/app/(tabs)/mypage.tsx b/TinyBite/app/(app)/(tabs)/mypage.tsx similarity index 100% rename from TinyBite/app/(tabs)/mypage.tsx rename to TinyBite/app/(app)/(tabs)/mypage.tsx diff --git a/TinyBite/app/camera.tsx b/TinyBite/app/(app)/camera.tsx similarity index 100% rename from TinyBite/app/camera.tsx rename to TinyBite/app/(app)/camera.tsx diff --git a/TinyBite/app/chat/[id].tsx b/TinyBite/app/(app)/chat/[id].tsx similarity index 100% rename from TinyBite/app/chat/[id].tsx rename to TinyBite/app/(app)/chat/[id].tsx diff --git a/TinyBite/app/gallery-preview.tsx b/TinyBite/app/(app)/gallery-preview.tsx similarity index 100% rename from TinyBite/app/gallery-preview.tsx rename to TinyBite/app/(app)/gallery-preview.tsx diff --git a/TinyBite/app/party-detail/[id].tsx b/TinyBite/app/(app)/party-detail/[id].tsx similarity index 100% rename from TinyBite/app/party-detail/[id].tsx rename to TinyBite/app/(app)/party-detail/[id].tsx diff --git a/TinyBite/app/party/create/[type].tsx b/TinyBite/app/(app)/party/create/[type].tsx similarity index 100% rename from TinyBite/app/party/create/[type].tsx rename to TinyBite/app/(app)/party/create/[type].tsx diff --git a/TinyBite/app/party/edit/[type].tsx b/TinyBite/app/(app)/party/edit/[type].tsx similarity index 100% rename from TinyBite/app/party/edit/[type].tsx rename to TinyBite/app/(app)/party/edit/[type].tsx diff --git a/TinyBite/app/search/location/index.tsx b/TinyBite/app/(app)/search/location/index.tsx similarity index 100% rename from TinyBite/app/search/location/index.tsx rename to TinyBite/app/(app)/search/location/index.tsx diff --git a/TinyBite/app/search/search.tsx b/TinyBite/app/(app)/search/search.tsx similarity index 98% rename from TinyBite/app/search/search.tsx rename to TinyBite/app/(app)/search/search.tsx index 25de3eb..77457e2 100644 --- a/TinyBite/app/search/search.tsx +++ b/TinyBite/app/(app)/search/search.tsx @@ -7,7 +7,7 @@ import SearchResultList from "@/components/search/SearchResultList"; import { useUserCoords } from "@/hooks/useUserCoords"; import { usePartyStore } from "@/stores/partyStore"; import { colors } from "@/styles/colors"; -import { PartyItem } from "@/types/party"; +import { PartyItem } from "@/types/party.types"; import { useInfiniteQuery, useQuery } from "@tanstack/react-query"; import { useRouter } from "expo-router"; import { StatusBar } from "expo-status-bar"; diff --git a/TinyBite/app/(auth)/login/login.tsx b/TinyBite/app/(auth)/login/login.tsx index 984a506..ddc0250 100644 --- a/TinyBite/app/(auth)/login/login.tsx +++ b/TinyBite/app/(auth)/login/login.tsx @@ -7,10 +7,12 @@ import { ApiError } from "@/types/api.types"; import { getErrorMessage } from "@/utils/getErrorMessage"; import { useMutation } from "@tanstack/react-query"; import { AxiosError } from "axios"; -import { useRouter } from "expo-router"; +import { useFocusEffect, useRouter } from "expo-router"; import * as SecureStore from "expo-secure-store"; import { StatusBar } from "expo-status-bar"; +import { useCallback } from "react"; import { + BackHandler, Image, Platform, StyleSheet, @@ -36,7 +38,7 @@ export default function LoginScreen() { if (data.signup) { login(data); await SecureStore.deleteItemAsync("googleIdToken"); - router.dismissTo("/(tabs)"); + router.dismissTo("/(app)/(tabs)"); } else { router.push("/(auth)/signup/terms"); } @@ -57,6 +59,22 @@ export default function LoginScreen() { }, }); + useFocusEffect( + useCallback(() => { + const onBackPress = () => { + BackHandler.exitApp(); // 앱 종료 + return true; // 기본 동작 막기 + }; + + const subscription = BackHandler.addEventListener( + "hardwareBackPress", + onBackPress + ); + + return () => subscription.remove(); + }, []) + ); + const handleGoogleLogin = async () => { await signOut(); diff --git a/TinyBite/app/(auth)/signup/complete.tsx b/TinyBite/app/(auth)/signup/complete.tsx index a14445d..50c0179 100644 --- a/TinyBite/app/(auth)/signup/complete.tsx +++ b/TinyBite/app/(auth)/signup/complete.tsx @@ -24,7 +24,7 @@ export default function CompleteScreen() { {/* 다음 버튼 */} router.replace("/(tabs)")} + onPress={() => router.replace("/(app)/(tabs)")} > 내 동네 파티 목록 확인하기 diff --git a/TinyBite/app/(auth)/signup/region.tsx b/TinyBite/app/(auth)/signup/region.tsx index a93a8c1..cb92a46 100644 --- a/TinyBite/app/(auth)/signup/region.tsx +++ b/TinyBite/app/(auth)/signup/region.tsx @@ -1,5 +1,5 @@ import { getLocationName, postSignupGoogle } from "@/api/authApi"; -import { PlaceItem } from "@/app/search/location"; +import { PlaceItem } from "@/app/(app)/search/location"; import PaginationIndecatorHeader from "@/components/PaginationIndecatorHeader"; import { useUserCoords } from "@/hooks/useUserCoords"; import { useAuthStore } from "@/stores/authStore"; diff --git a/TinyBite/app/_layout.tsx b/TinyBite/app/_layout.tsx index 432c2e5..8059f23 100644 --- a/TinyBite/app/_layout.tsx +++ b/TinyBite/app/_layout.tsx @@ -12,8 +12,13 @@ export default function RootLayout() { - - + + + + + + + diff --git a/TinyBite/app/index.tsx b/TinyBite/app/index.tsx index a5b2792..26404d6 100644 --- a/TinyBite/app/index.tsx +++ b/TinyBite/app/index.tsx @@ -68,7 +68,7 @@ export default function Index() { // 5. 유저 있음 → 저장 후 메인 if (user) { setUser(user); - router.replace("/(tabs)"); + router.replace("/(app)/(tabs)"); } }; diff --git a/TinyBite/components/chat/ChatBottomPanel.tsx b/TinyBite/components/chat/ChatBottomPanel.tsx index a39f60a..3fca61b 100644 --- a/TinyBite/components/chat/ChatBottomPanel.tsx +++ b/TinyBite/components/chat/ChatBottomPanel.tsx @@ -59,7 +59,7 @@ const ChatBottomPanel = ({ // 미리보기 화면으로 이동 router.push({ - pathname: "/gallery-preview", + pathname: "/(app)/gallery-preview", params: { uri: selectedImage.uri, fileName: fileName, @@ -81,7 +81,7 @@ const ChatBottomPanel = ({ // 카메라 화면으로 이동 setIsPanelVisible(false); - router.push("/camera"); + router.push("/(app)/camera"); }; useEffect(() => { @@ -134,7 +134,7 @@ const ChatBottomPanel = ({ const { granted } = await requestPermission(); setShowCameraModal(false); if (granted) { - router.push("/camera"); + router.push("/(app)/camera"); } }} /> diff --git a/TinyBite/components/chat/ChatCard.tsx b/TinyBite/components/chat/ChatCard.tsx index 2a8cb07..52c834a 100644 --- a/TinyBite/components/chat/ChatCard.tsx +++ b/TinyBite/components/chat/ChatCard.tsx @@ -33,7 +33,7 @@ const ChatItem = ({ item }: ChatItemProps) => { const handleChatPress = () => { router.navigate({ - pathname: "/chat/[id]", + pathname: "/(app)/chat/[id]", params: { id: item.chatRoomId, roomType: item.roomType, diff --git a/TinyBite/components/chat/participant/ChatJoinAcceptedCard.tsx b/TinyBite/components/chat/participant/ChatJoinAcceptedCard.tsx index ffff83f..255f042 100644 --- a/TinyBite/components/chat/participant/ChatJoinAcceptedCard.tsx +++ b/TinyBite/components/chat/participant/ChatJoinAcceptedCard.tsx @@ -7,15 +7,16 @@ import { Image, StyleSheet, Text, TouchableOpacity, View } from "react-native"; const GROUP_ICON = require("@/assets/images/chat/group-icon.png"); interface ChatJoinAcceptedCardProps { - chatRoomId: number + chatRoomId: number; } -export const ChatJoinAcceptedCard = ({chatRoomId} : ChatJoinAcceptedCardProps) => { - +export const ChatJoinAcceptedCard = ({ + chatRoomId, +}: ChatJoinAcceptedCardProps) => { const handleNavigateToChatRoom = () => { console.log("파티 채팅방 가기"); router.dismissTo({ - pathname: "/chat/[id]", + pathname: "/(app)/chat/[id]", params: { id: chatRoomId, roomType: "GROUP", @@ -23,7 +24,7 @@ export const ChatJoinAcceptedCard = ({chatRoomId} : ChatJoinAcceptedCardProps) = // partyTitle: item.partyTitle, // targetName: item.targetName, }, - }) + }); }; return ( diff --git a/TinyBite/components/main/FloatingMenuOverlay.tsx b/TinyBite/components/main/FloatingMenuOverlay.tsx index 7f15fe6..c485048 100644 --- a/TinyBite/components/main/FloatingMenuOverlay.tsx +++ b/TinyBite/components/main/FloatingMenuOverlay.tsx @@ -48,7 +48,7 @@ const FloatingMenuOverlay = ({ onPress={() => { setIsMenuOpen(false); router.navigate({ - pathname: "/party/create/[type]", + pathname: "/(app)/party/create/[type]", params: { type: "DELIVERY" }, }); }} @@ -69,7 +69,7 @@ const FloatingMenuOverlay = ({ onPress={() => { setIsMenuOpen(false); router.navigate({ - pathname: "/party/create/[type]", + pathname: "/(app)/party/create/[type]", params: { type: "HOUSEHOLD" }, }); }} @@ -90,7 +90,7 @@ const FloatingMenuOverlay = ({ onPress={() => { setIsMenuOpen(false); router.navigate({ - pathname: "/party/create/[type]", + pathname: "/(app)/party/create/[type]", params: { type: "GROCERY" }, }); }} diff --git a/TinyBite/components/main/MainHeader.tsx b/TinyBite/components/main/MainHeader.tsx index 5b3859e..1182311 100644 --- a/TinyBite/components/main/MainHeader.tsx +++ b/TinyBite/components/main/MainHeader.tsx @@ -78,7 +78,7 @@ const MainHeader = ({}: MainHeaderProps = {}) => { {location} - router.push("/search/search")}> + router.push("/(app)/search/search")}> { item={item} onPress={() => router.push({ - pathname: "/party-detail/[id]" as any, + pathname: "/(app)/party-detail/[id]" as any, params: { id: item.partyId.toString() }, }) } diff --git a/TinyBite/components/mypage/MyPartyList.tsx b/TinyBite/components/mypage/MyPartyList.tsx index ddca98e..3ab0b2f 100644 --- a/TinyBite/components/mypage/MyPartyList.tsx +++ b/TinyBite/components/mypage/MyPartyList.tsx @@ -122,7 +122,7 @@ const MyPartyList = () => { containerStyle={styles.mypageCard} onPress={() => router.push({ - pathname: "/party-detail/[id]" as any, + pathname: "/(app)/party-detail/[id]" as any, params: { id: item.partyId.toString() }, }) } diff --git a/TinyBite/components/search/SearchResultList.tsx b/TinyBite/components/search/SearchResultList.tsx index d29c203..34230e9 100644 --- a/TinyBite/components/search/SearchResultList.tsx +++ b/TinyBite/components/search/SearchResultList.tsx @@ -1,7 +1,7 @@ import MainCard from "@/components/main/MainCard"; import { colors } from "@/styles/colors"; import { textStyles } from "@/styles/typography/textStyles"; -import { PartyItem } from "@/types/party"; +import { PartyItem } from "@/types/party.types"; import { FlatList, StyleSheet, Text, View } from "react-native"; interface SearchResultListProps { diff --git a/TinyBite/hooks/mutations/useParty.ts b/TinyBite/hooks/mutations/useParty.ts index ca5b5ca..146e879 100644 --- a/TinyBite/hooks/mutations/useParty.ts +++ b/TinyBite/hooks/mutations/useParty.ts @@ -15,7 +15,7 @@ export const useRequestJoinParty = () => { onSuccess: (data) => { queryClient.invalidateQueries({ queryKey: ["getOnetoOneRoomList"] }); router.push({ - pathname: "/chat/[id]", + pathname: "/(app)/chat/[id]", params: { id: data, roomType: "ONE_TO_ONE", diff --git a/TinyBite/stores/authStore.ts b/TinyBite/stores/authStore.ts index e9ce557..1360f88 100644 --- a/TinyBite/stores/authStore.ts +++ b/TinyBite/stores/authStore.ts @@ -35,6 +35,6 @@ export const useAuthStore = create((set, get) => ({ set({ user: null, isAuthenticated: false }); - router.replace("/login/login"); + router.replace("/(auth)/login/login"); }, })); From 33cd0a2dff202e60ec552b97c5f657f398a471d5 Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 12:51:39 +0900 Subject: [PATCH 67/73] =?UTF-8?q?fix/#115:=201:1=20=EC=B1=84=ED=8C=85=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=95=84=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=ED=8C=8C=EC=8B=B1=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/components/chat/ChatCardImage.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/TinyBite/components/chat/ChatCardImage.tsx b/TinyBite/components/chat/ChatCardImage.tsx index 6ce4101..2ddcedc 100644 --- a/TinyBite/components/chat/ChatCardImage.tsx +++ b/TinyBite/components/chat/ChatCardImage.tsx @@ -3,6 +3,7 @@ import { GroupChatCardSchema, OneToOneChatCardSchema, } from "@/types/chat.types"; +import { parseProfileImage } from "@/utils/parseProfileImage"; import { Image, StyleSheet, View } from "react-native"; /** @@ -28,13 +29,16 @@ const ChatItemImage = ({ item }: ChatItemImageProps) => { if (isOneOnOne) { const oneToOneItem = item as OneToOneChatCardSchema; + return ( {/* 상대방 프로필 이미지 (왼쪽) */} {oneToOneItem.targetProfileImage ? ( @@ -46,7 +50,9 @@ const ChatItemImage = ({ item }: ChatItemImageProps) => { {oneToOneItem.myProfileImage ? ( From 0b48b448db4bb40b74e2e59e897fb0ce298af15f Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 12:53:51 +0900 Subject: [PATCH 68/73] =?UTF-8?q?chore/#115:=20=EB=B2=84=EC=A0=84=200.8.0?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/app.config.js | 2 +- TinyBite/package-lock.json | 6 +++--- TinyBite/package.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/TinyBite/app.config.js b/TinyBite/app.config.js index 030750e..5a3b7d0 100644 --- a/TinyBite/app.config.js +++ b/TinyBite/app.config.js @@ -4,7 +4,7 @@ module.exports = { name: "한입만", slug: "TinyBite", owner: "tinybite-2025", - version: "0.7.0", + version: "0.8.0", orientation: "portrait", icon: "./assets/images/icon.png", scheme: "tinybite", diff --git a/TinyBite/package-lock.json b/TinyBite/package-lock.json index 84399ce..dec9083 100644 --- a/TinyBite/package-lock.json +++ b/TinyBite/package-lock.json @@ -1,12 +1,12 @@ { "name": "tinybite", - "version": "0.7.0", - "lockfileVersion": 7, + "version": "0.8.0", + "lockfileVersion": 8, "requires": true, "packages": { "": { "name": "tinybite", - "version": "0.7.0", + "version": "0.8.0", "dependencies": { "@expo/vector-icons": "^15.0.3", "@react-native-google-signin/google-signin": "^16.0.0", diff --git a/TinyBite/package.json b/TinyBite/package.json index 1589abb..be3991b 100644 --- a/TinyBite/package.json +++ b/TinyBite/package.json @@ -1,7 +1,7 @@ { "name": "tinybite", "main": "expo-router/entry", - "version": "0.7.0", + "version": "0.8.0", "scripts": { "start": "expo start", "reset-project": "node ./scripts/reset-project.js", From e8ac912ec4e894360e4bcd26c73e2bbc0341081f Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 12:59:09 +0900 Subject: [PATCH 69/73] =?UTF-8?q?fix/#115:=20=EB=9D=BC=EC=9A=B0=ED=84=B0?= =?UTF-8?q?=20=EA=B2=BD=EB=A1=9C=EB=A5=BC=20=EC=88=98=EC=A0=95=ED=95=98?= =?UTF-8?q?=EC=97=AC=20=EC=98=AC=EB=B0=94=EB=A5=B8=20=ED=83=AD=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=A6=AC=EB=94=94=EB=A0=89=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/app/(app)/party-detail/[id].tsx | 2 +- TinyBite/app/(app)/party/create/[type].tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/TinyBite/app/(app)/party-detail/[id].tsx b/TinyBite/app/(app)/party-detail/[id].tsx index 295aee5..3ecf442 100644 --- a/TinyBite/app/(app)/party-detail/[id].tsx +++ b/TinyBite/app/(app)/party-detail/[id].tsx @@ -156,7 +156,7 @@ export default function PartyDetailScreen() { queryClient.invalidateQueries({ queryKey: ["getHostingParties"], }); - router.replace("/(tabs)"); + router.replace("/(app)/(tabs)"); return true; } catch (error: any) { // 400(이미 참여자가 있음 / 권한 없음) 상태 코드인 경우 에러 로그 출력하지 않음 diff --git a/TinyBite/app/(app)/party/create/[type].tsx b/TinyBite/app/(app)/party/create/[type].tsx index 707febe..d2dfae8 100644 --- a/TinyBite/app/(app)/party/create/[type].tsx +++ b/TinyBite/app/(app)/party/create/[type].tsx @@ -89,7 +89,7 @@ export default function PartyCreateScreen() { mutationFn: postCreateParty, onSuccess: (data) => { resetCreateParty(); - router.replace("/(tabs)"); + router.replace("/(app)/(tabs)"); Toast.show({ type: "basicToast", props: { text: "파티가 생성되었습니다." }, From 1336901343a1bd915bb5c812cf4a772bdf02c797 Mon Sep 17 00:00:00 2001 From: ion Date: Fri, 16 Jan 2026 13:00:05 +0900 Subject: [PATCH 70/73] =?UTF-8?q?chore/#115:=20=EB=B2=84=EC=A0=84=200.9.0?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/app.config.js | 2 +- TinyBite/package-lock.json | 6 +++--- TinyBite/package.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/TinyBite/app.config.js b/TinyBite/app.config.js index 5a3b7d0..34234f7 100644 --- a/TinyBite/app.config.js +++ b/TinyBite/app.config.js @@ -4,7 +4,7 @@ module.exports = { name: "한입만", slug: "TinyBite", owner: "tinybite-2025", - version: "0.8.0", + version: "0.9.0", orientation: "portrait", icon: "./assets/images/icon.png", scheme: "tinybite", diff --git a/TinyBite/package-lock.json b/TinyBite/package-lock.json index dec9083..6939d2c 100644 --- a/TinyBite/package-lock.json +++ b/TinyBite/package-lock.json @@ -1,12 +1,12 @@ { "name": "tinybite", - "version": "0.8.0", - "lockfileVersion": 8, + "version": "0.9.0", + "lockfileVersion": 9, "requires": true, "packages": { "": { "name": "tinybite", - "version": "0.8.0", + "version": "0.9.0", "dependencies": { "@expo/vector-icons": "^15.0.3", "@react-native-google-signin/google-signin": "^16.0.0", diff --git a/TinyBite/package.json b/TinyBite/package.json index be3991b..853a9af 100644 --- a/TinyBite/package.json +++ b/TinyBite/package.json @@ -1,7 +1,7 @@ { "name": "tinybite", "main": "expo-router/entry", - "version": "0.8.0", + "version": "0.9.0", "scripts": { "start": "expo start", "reset-project": "node ./scripts/reset-project.js", From 698273e0652bea0a137a18c0e84c3a927d5c571d Mon Sep 17 00:00:00 2001 From: ParkSeohyeon Date: Fri, 16 Jan 2026 21:24:40 +0900 Subject: [PATCH 71/73] =?UTF-8?q?chore/#115:=20=EC=83=81=EC=84=B8=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20CTA=20=EB=B2=84=ED=8A=BC=20=EB=AC=B8=EA=B5=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/app/(app)/party-detail/[id].tsx | 1 - TinyBite/components/main/party-detail/PartyDetailCTA.tsx | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/TinyBite/app/(app)/party-detail/[id].tsx b/TinyBite/app/(app)/party-detail/[id].tsx index 3ecf442..ce324ed 100644 --- a/TinyBite/app/(app)/party-detail/[id].tsx +++ b/TinyBite/app/(app)/party-detail/[id].tsx @@ -223,7 +223,6 @@ export default function PartyDetailScreen() { detailPartyId={partyId} isClosed={partyDetail?.isClosed} isParticipating={partyDetail?.isParticipating} - pricePerPerson={partyDetail?.pricePerPerson} /> diff --git a/TinyBite/components/main/party-detail/PartyDetailCTA.tsx b/TinyBite/components/main/party-detail/PartyDetailCTA.tsx index 4bef8ec..19e76aa 100644 --- a/TinyBite/components/main/party-detail/PartyDetailCTA.tsx +++ b/TinyBite/components/main/party-detail/PartyDetailCTA.tsx @@ -8,14 +8,12 @@ interface PartyDetailCTAProps { detailPartyId: number; isClosed?: boolean; isParticipating?: boolean; - pricePerPerson?: number; } const PartyDetailCTA = ({ detailPartyId, isClosed, isParticipating, - pricePerPerson, }: PartyDetailCTAProps) => { const { mutate } = useRequestJoinParty(); @@ -27,7 +25,7 @@ const PartyDetailCTA = ({ return "채팅방으로 이동"; } if (pricePerPerson != null) { - return `${pricePerPerson.toLocaleString()}원으로 참여하기`; + return "채팅으로 참여하기"; } return "로딩 중..."; }; From c7129e9a4004c51e52fb3f3b464f44a69e05d8dc Mon Sep 17 00:00:00 2001 From: ParkSeohyeon Date: Fri, 16 Jan 2026 21:52:45 +0900 Subject: [PATCH 72/73] =?UTF-8?q?fix/#115:=20CTA=20=EC=9E=98=EB=AA=BB?= =?UTF-8?q?=EB=90=9C=20=EB=B6=84=EA=B8=B0=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/components/main/party-detail/PartyDetailCTA.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/TinyBite/components/main/party-detail/PartyDetailCTA.tsx b/TinyBite/components/main/party-detail/PartyDetailCTA.tsx index 19e76aa..18c50a6 100644 --- a/TinyBite/components/main/party-detail/PartyDetailCTA.tsx +++ b/TinyBite/components/main/party-detail/PartyDetailCTA.tsx @@ -24,10 +24,7 @@ const PartyDetailCTA = ({ if (isParticipating) { return "채팅방으로 이동"; } - if (pricePerPerson != null) { - return "채팅으로 참여하기"; - } - return "로딩 중..."; + return "채팅으로 참여하기"; }; const handleGoToChatPress = () => { From b04ac28401c1702d1d1e2b858e0d61cb2f84958b Mon Sep 17 00:00:00 2001 From: ParkSeohyeon Date: Fri, 16 Jan 2026 22:30:22 +0900 Subject: [PATCH 73/73] =?UTF-8?q?feat/#115:=20groupChatRoomId=20=EA=B0=92?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TinyBite/app/(app)/party-detail/[id].tsx | 1 + .../main/party-detail/PartyDetailCTA.tsx | 17 ++++++++++++++++- TinyBite/types/party.types.ts | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/TinyBite/app/(app)/party-detail/[id].tsx b/TinyBite/app/(app)/party-detail/[id].tsx index ce324ed..55499f6 100644 --- a/TinyBite/app/(app)/party-detail/[id].tsx +++ b/TinyBite/app/(app)/party-detail/[id].tsx @@ -223,6 +223,7 @@ export default function PartyDetailScreen() { detailPartyId={partyId} isClosed={partyDetail?.isClosed} isParticipating={partyDetail?.isParticipating} + groupChatRoomId={partyDetail?.groupChatRoomId} /> diff --git a/TinyBite/components/main/party-detail/PartyDetailCTA.tsx b/TinyBite/components/main/party-detail/PartyDetailCTA.tsx index 18c50a6..edbb63e 100644 --- a/TinyBite/components/main/party-detail/PartyDetailCTA.tsx +++ b/TinyBite/components/main/party-detail/PartyDetailCTA.tsx @@ -1,6 +1,7 @@ import { useRequestJoinParty } from "@/hooks/mutations/useParty"; import { colors } from "@/styles/colors"; import { textStyles } from "@/styles/typography/textStyles"; +import { router } from "expo-router"; import { StyleSheet, Text, TouchableOpacity } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; @@ -8,12 +9,14 @@ interface PartyDetailCTAProps { detailPartyId: number; isClosed?: boolean; isParticipating?: boolean; + groupChatRoomId?: number; } const PartyDetailCTA = ({ detailPartyId, isClosed, isParticipating, + groupChatRoomId, }: PartyDetailCTAProps) => { const { mutate } = useRequestJoinParty(); @@ -28,7 +31,19 @@ const PartyDetailCTA = ({ }; const handleGoToChatPress = () => { - mutate(detailPartyId); + //groupChatRoomId가 있으면 그룹 채팅방으로 이동 + if (groupChatRoomId) { + router.push({ + pathname: "/(app)/chat/[id]", + params: { + id: groupChatRoomId.toString(), + roomType: "GROUP", + }, + }); + } else { + // 참여 요청 + mutate(detailPartyId); + } }; return ( diff --git a/TinyBite/types/party.types.ts b/TinyBite/types/party.types.ts index 9e8b2b3..41c0f22 100644 --- a/TinyBite/types/party.types.ts +++ b/TinyBite/types/party.types.ts @@ -121,6 +121,7 @@ export type PartyDetail = { images: string[]; isClosed: boolean; isParticipating: boolean; + groupChatRoomId?: number; // 참여중일 때만 포함됨 }; /**