수락된 매칭 채팅을 닫으면 재진입 불가하던 문제 대응. 헤더 forum 아이콘 → 진행중(수락) 매칭 목록 → 탭하면 채팅 다시 열림. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,15 @@ export interface ReceivedMatch {
|
|||||||
expiresAt: string | null
|
expiresAt: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 진행중(수락된) 매칭 — 채팅 재진입 목록용. */
|
||||||
|
export interface OngoingMatch {
|
||||||
|
matchId: number
|
||||||
|
otherDogId: number
|
||||||
|
otherDogName: string
|
||||||
|
otherDogBreed: string | null
|
||||||
|
respondedAt: string | null
|
||||||
|
}
|
||||||
|
|
||||||
/** STOMP /topic/users/{userId} 로 오는 매칭 알림. */
|
/** STOMP /topic/users/{userId} 로 오는 매칭 알림. */
|
||||||
export interface MatchNotification {
|
export interface MatchNotification {
|
||||||
type: 'NEW_REQUEST' | 'ACCEPTED' | 'REJECTED' | 'EXPIRED' | 'FEEDBACK_REQUEST'
|
type: 'NEW_REQUEST' | 'ACCEPTED' | 'REJECTED' | 'EXPIRED' | 'FEEDBACK_REQUEST'
|
||||||
@@ -35,4 +44,5 @@ export const matchesApi = {
|
|||||||
reject: (id: number) => http.post<Match>(`/api/matches/${id}/reject`),
|
reject: (id: number) => http.post<Match>(`/api/matches/${id}/reject`),
|
||||||
received: (dogId: number) => http.get<ReceivedMatch[]>(`/api/matches/received/${dogId}`),
|
received: (dogId: number) => http.get<ReceivedMatch[]>(`/api/matches/received/${dogId}`),
|
||||||
sent: (dogId: number) => http.get<Match[]>(`/api/matches/sent/${dogId}`),
|
sent: (dogId: number) => http.get<Match[]>(`/api/matches/sent/${dogId}`),
|
||||||
|
ongoing: (dogId: number) => http.get<OngoingMatch[]>(`/api/matches/ongoing/${dogId}`),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,37 @@
|
|||||||
</q-menu>
|
</q-menu>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
|
|
||||||
|
<!-- 진행중 매칭 채팅 (닫아도 다시 열기) -->
|
||||||
|
<q-btn flat round dense icon="forum" class="q-mr-xs">
|
||||||
|
<q-badge v-if="matching.ongoing.length" color="teal" floating>
|
||||||
|
{{ matching.ongoing.length }}
|
||||||
|
</q-badge>
|
||||||
|
<q-menu anchor="bottom right" self="top right">
|
||||||
|
<q-list style="min-width: 260px">
|
||||||
|
<q-item-label header>진행중 채팅</q-item-label>
|
||||||
|
<q-item v-if="!matching.ongoing.length">
|
||||||
|
<q-item-section class="text-grey-6">진행중인 매칭이 없어요.</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item
|
||||||
|
v-for="o in matching.ongoing"
|
||||||
|
:key="o.matchId"
|
||||||
|
clickable
|
||||||
|
v-close-popup
|
||||||
|
@click="openMatchChat(o.matchId)"
|
||||||
|
>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-avatar color="primary" text-color="white" icon="favorite" />
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label class="text-weight-medium">{{ o.otherDogName }}</q-item-label>
|
||||||
|
<q-item-label caption>{{ o.otherDogBreed || '견종 미상' }} · 대화 열기</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section side><q-icon name="chat" color="primary" /></q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</q-menu>
|
||||||
|
</q-btn>
|
||||||
|
|
||||||
<q-btn flat round dense icon="account_circle">
|
<q-btn flat round dense icon="account_circle">
|
||||||
<q-menu>
|
<q-menu>
|
||||||
<q-list style="min-width: 160px">
|
<q-list style="min-width: 160px">
|
||||||
@@ -128,6 +159,12 @@ function openFeedback(id: number) {
|
|||||||
feedbackOpen.value = true
|
feedbackOpen.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 진행중 채팅 목록에서 특정 매칭 채팅 다시 열기. */
|
||||||
|
function openMatchChat(matchId: number) {
|
||||||
|
matchChatId.value = matchId
|
||||||
|
matchChatOpen.value = true
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await dogs.loadMyDogs()
|
await dogs.loadMyDogs()
|
||||||
if (auth.member && dogs.currentDogId != null) {
|
if (auth.member && dogs.currentDogId != null) {
|
||||||
|
|||||||
+16
-2
@@ -1,7 +1,12 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { Notify } from 'quasar'
|
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'
|
import { createUserNotifications } from '@/lib/chatSocket'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13,6 +18,7 @@ import { createUserNotifications } from '@/lib/chatSocket'
|
|||||||
*/
|
*/
|
||||||
export const useMatchingStore = defineStore('matching', () => {
|
export const useMatchingStore = defineStore('matching', () => {
|
||||||
const received = ref<ReceivedMatch[]>([])
|
const received = ref<ReceivedMatch[]>([])
|
||||||
|
const ongoing = ref<OngoingMatch[]>([])
|
||||||
const sentPendingTargetIds = ref<Set<number>>(new Set())
|
const sentPendingTargetIds = ref<Set<number>>(new Set())
|
||||||
const openChatMatchId = ref<number | null>(null)
|
const openChatMatchId = ref<number | null>(null)
|
||||||
/** 산책 후 피드백(찰떡지수) 팝업을 띄울 매칭 id — 알림 수신 시 세팅. */
|
/** 산책 후 피드백(찰떡지수) 팝업을 띄울 매칭 id — 알림 수신 시 세팅. */
|
||||||
@@ -24,8 +30,13 @@ export const useMatchingStore = defineStore('matching', () => {
|
|||||||
async function refresh(dogId: number): Promise<void> {
|
async function refresh(dogId: number): Promise<void> {
|
||||||
myDogId = dogId
|
myDogId = dogId
|
||||||
try {
|
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
|
received.value = rec
|
||||||
|
ongoing.value = ong
|
||||||
sentPendingTargetIds.value = new Set(snt.map((m) => m.targetDogId))
|
sentPendingTargetIds.value = new Set(snt.map((m) => m.targetDogId))
|
||||||
} catch {
|
} catch {
|
||||||
// 무시 (미로그인/네트워크)
|
// 무시 (미로그인/네트워크)
|
||||||
@@ -44,6 +55,7 @@ export const useMatchingStore = defineStore('matching', () => {
|
|||||||
socket?.deactivate()
|
socket?.deactivate()
|
||||||
socket = null
|
socket = null
|
||||||
received.value = []
|
received.value = []
|
||||||
|
ongoing.value = []
|
||||||
sentPendingTargetIds.value = new Set()
|
sentPendingTargetIds.value = new Set()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,6 +81,7 @@ export const useMatchingStore = defineStore('matching', () => {
|
|||||||
await matchesApi.accept(id)
|
await matchesApi.accept(id)
|
||||||
received.value = received.value.filter((m) => m.id !== id)
|
received.value = received.value.filter((m) => m.id !== id)
|
||||||
openChatMatchId.value = id // 수락자도 바로 채팅 열기
|
openChatMatchId.value = id // 수락자도 바로 채팅 열기
|
||||||
|
if (myDogId != null) void refresh(myDogId) // 진행중 목록에 반영
|
||||||
}
|
}
|
||||||
|
|
||||||
async function reject(id: number): Promise<void> {
|
async function reject(id: number): Promise<void> {
|
||||||
@@ -89,6 +102,7 @@ export const useMatchingStore = defineStore('matching', () => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
received,
|
received,
|
||||||
|
ongoing,
|
||||||
sentPendingTargetIds,
|
sentPendingTargetIds,
|
||||||
openChatMatchId,
|
openChatMatchId,
|
||||||
feedbackMatchId,
|
feedbackMatchId,
|
||||||
|
|||||||
Reference in New Issue
Block a user