From befd49fd29c9733ed3261398e8b8e103d6060cf1 Mon Sep 17 00:00:00 2001 From: sb Date: Sun, 12 Jul 2026 14:27:44 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=97=A4=EB=8D=94=EC=97=90=20=EC=A7=84?= =?UTF-8?q?=ED=96=89=EC=A4=91=20=EC=B1=84=ED=8C=85=20=EB=AA=A9=EB=A1=9D=20?= =?UTF-8?q?=E2=80=94=20=EB=8B=AB=EC=9D=80=20=EB=A7=A4=EC=B9=AD=20=EC=B1=84?= =?UTF-8?q?=ED=8C=85=20=EB=8B=A4=EC=8B=9C=20=EC=97=B4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 수락된 매칭 채팅을 닫으면 재진입 불가하던 문제 대응. 헤더 forum 아이콘 → 진행중(수락) 매칭 목록 → 탭하면 채팅 다시 열림. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/api/matches.ts | 10 ++++++++++ src/layouts/MainLayout.vue | 37 +++++++++++++++++++++++++++++++++++++ src/stores/matching.ts | 18 ++++++++++++++++-- 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/api/matches.ts b/src/api/matches.ts index f98c797..93d4dc3 100644 --- a/src/api/matches.ts +++ b/src/api/matches.ts @@ -20,6 +20,15 @@ export interface ReceivedMatch { expiresAt: string | null } +/** 진행중(수락된) 매칭 — 채팅 재진입 목록용. */ +export interface OngoingMatch { + matchId: number + otherDogId: number + otherDogName: string + otherDogBreed: string | null + respondedAt: string | null +} + /** STOMP /topic/users/{userId} 로 오는 매칭 알림. */ export interface MatchNotification { type: 'NEW_REQUEST' | 'ACCEPTED' | 'REJECTED' | 'EXPIRED' | 'FEEDBACK_REQUEST' @@ -35,4 +44,5 @@ export const matchesApi = { reject: (id: number) => http.post(`/api/matches/${id}/reject`), received: (dogId: number) => http.get(`/api/matches/received/${dogId}`), sent: (dogId: number) => http.get(`/api/matches/sent/${dogId}`), + ongoing: (dogId: number) => http.get(`/api/matches/ongoing/${dogId}`), } diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue index 6b0c4ae..94dfd0f 100644 --- a/src/layouts/MainLayout.vue +++ b/src/layouts/MainLayout.vue @@ -39,6 +39,37 @@ + + + + {{ matching.ongoing.length }} + + + + 진행중 채팅 + + 진행중인 매칭이 없어요. + + + + + + + {{ o.otherDogName }} + {{ o.otherDogBreed || '견종 미상' }} · 대화 열기 + + + + + + + @@ -128,6 +159,12 @@ function openFeedback(id: number) { feedbackOpen.value = true } +/** 진행중 채팅 목록에서 특정 매칭 채팅 다시 열기. */ +function openMatchChat(matchId: number) { + matchChatId.value = matchId + matchChatOpen.value = true +} + onMounted(async () => { await dogs.loadMyDogs() if (auth.member && dogs.currentDogId != null) { diff --git a/src/stores/matching.ts b/src/stores/matching.ts index 6d39202..56cef89 100644 --- a/src/stores/matching.ts +++ b/src/stores/matching.ts @@ -1,7 +1,12 @@ import { defineStore } from 'pinia' import { ref } from 'vue' import { Notify } from 'quasar' -import { matchesApi, type MatchNotification, type ReceivedMatch } from '@/api/matches' +import { + matchesApi, + type MatchNotification, + type OngoingMatch, + type ReceivedMatch, +} from '@/api/matches' import { createUserNotifications } from '@/lib/chatSocket' /** @@ -13,6 +18,7 @@ import { createUserNotifications } from '@/lib/chatSocket' */ export const useMatchingStore = defineStore('matching', () => { const received = ref([]) + const ongoing = ref([]) const sentPendingTargetIds = ref>(new Set()) const openChatMatchId = ref(null) /** 산책 후 피드백(찰떡지수) 팝업을 띄울 매칭 id — 알림 수신 시 세팅. */ @@ -24,8 +30,13 @@ export const useMatchingStore = defineStore('matching', () => { async function refresh(dogId: number): Promise { myDogId = dogId try { - const [rec, snt] = await Promise.all([matchesApi.received(dogId), matchesApi.sent(dogId)]) + const [rec, snt, ong] = await Promise.all([ + matchesApi.received(dogId), + matchesApi.sent(dogId), + matchesApi.ongoing(dogId).catch(() => [] as OngoingMatch[]), + ]) received.value = rec + ongoing.value = ong sentPendingTargetIds.value = new Set(snt.map((m) => m.targetDogId)) } catch { // 무시 (미로그인/네트워크) @@ -44,6 +55,7 @@ export const useMatchingStore = defineStore('matching', () => { socket?.deactivate() socket = null received.value = [] + ongoing.value = [] sentPendingTargetIds.value = new Set() } @@ -69,6 +81,7 @@ export const useMatchingStore = defineStore('matching', () => { await matchesApi.accept(id) received.value = received.value.filter((m) => m.id !== id) openChatMatchId.value = id // 수락자도 바로 채팅 열기 + if (myDogId != null) void refresh(myDogId) // 진행중 목록에 반영 } async function reject(id: number): Promise { @@ -89,6 +102,7 @@ export const useMatchingStore = defineStore('matching', () => { return { received, + ongoing, sentPendingTargetIds, openChatMatchId, feedbackMatchId,