Files
dognation_PT/src/stores/matching.ts
T

122 lines
3.4 KiB
TypeScript
Raw Normal View History

import { defineStore } from 'pinia'
import { ref } from 'vue'
import { Notify } from 'quasar'
import { matchesApi, type MatchNotification, type ReceivedMatch } from '@/api/matches'
import { createUserNotifications } from '@/lib/chatSocket'
/**
* 매칭 상태/알림 스토어.
* - 로그인 시 connect(userId, myDogId): 실시간 알림 구독 + 받은/보낸 매칭 로드
* - received: 내 개가 받은 대기중 신청 (수락/거절 대상)
* - sentPendingTargetIds: 내가 신청한 진행중 대상 dogId (버튼 "진행중" 표시)
* - openChatMatchId: 수락된 매칭 → 1:1 채팅 열기 신호
*/
export const useMatchingStore = defineStore('matching', () => {
const received = ref<ReceivedMatch[]>([])
const sentPendingTargetIds = ref<Set<number>>(new Set())
const openChatMatchId = ref<number | null>(null)
let socket: ReturnType<typeof createUserNotifications> | null = null
let myDogId: number | null = null
async function refresh(dogId: number): Promise<void> {
myDogId = dogId
try {
const [rec, snt] = await Promise.all([matchesApi.received(dogId), matchesApi.sent(dogId)])
received.value = rec
sentPendingTargetIds.value = new Set(snt.map((m) => m.targetDogId))
} catch {
// 무시 (미로그인/네트워크)
}
}
function connect(userId: number, dogId: number): void {
disconnect()
myDogId = dogId
void refresh(dogId)
socket = createUserNotifications(userId, onNotification)
socket.activate()
}
function disconnect(): void {
socket?.deactivate()
socket = null
received.value = []
sentPendingTargetIds.value = new Set()
}
function onNotification(n: MatchNotification): void {
Notify.create({
message: n.message,
caption: n.title,
color: colorFor(n.type),
icon: iconFor(n.type),
position: 'top',
timeout: 4000,
})
if (n.type === 'ACCEPTED') {
openChatMatchId.value = n.matchId // 신청자: 수락됨 → 채팅 열기
}
if (myDogId != null) void refresh(myDogId) // 목록/진행중 동기화
}
async function accept(id: number): Promise<void> {
await matchesApi.accept(id)
received.value = received.value.filter((m) => m.id !== id)
openChatMatchId.value = id // 수락자도 바로 채팅 열기
}
async function reject(id: number): Promise<void> {
await matchesApi.reject(id)
received.value = received.value.filter((m) => m.id !== id)
}
/** 매칭 신청 직후 낙관적으로 진행중 표시. */
function markSent(targetDogId: number): void {
const next = new Set(sentPendingTargetIds.value)
next.add(targetDogId)
sentPendingTargetIds.value = next
}
function isPending(targetDogId: number): boolean {
return sentPendingTargetIds.value.has(targetDogId)
}
return {
received,
sentPendingTargetIds,
openChatMatchId,
connect,
disconnect,
refresh,
accept,
reject,
markSent,
isPending,
}
})
function colorFor(type: MatchNotification['type']): string {
switch (type) {
case 'ACCEPTED':
return 'positive'
case 'REJECTED':
case 'EXPIRED':
return 'grey-8'
default:
return 'primary'
}
}
function iconFor(type: MatchNotification['type']): string {
switch (type) {
case 'NEW_REQUEST':
return 'favorite'
case 'ACCEPTED':
return 'celebration'
case 'EXPIRED':
return 'schedule'
default:
return 'heart_broken'
}
}