Files
dognation_PT/src/lib/chatSocket.ts
T
sb cc3a0fa836
CI / build (push) Failing after 14m12s
feat: 매칭 플로우 프론트 (진행중·알림·수락/거절·1:1 채팅)
- 매칭 버튼: 신청 후 "진행중" 표시, 무료 소진(429) 안내
- 헤더 알림 벨: 받은 매칭 신청 목록 + 수락/거절
- 실시간 알림: STOMP /topic/users/{id} 구독(신청/수락/거절/만료 토스트)
- 수락 시 1:1 매칭 채팅(MatchChatDialog, /topic/matches/{id})

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 15:04:21 +09:00

103 lines
2.9 KiB
TypeScript

import { Client, type IMessage } from '@stomp/stompjs'
import { tokenStore } from '@/lib/token'
import type { ChatMessage } from '@/api/chat'
import type { MatchNotification } from '@/api/matches'
function wsUrl(): string {
const base = import.meta.env.VITE_API_BASE_URL ?? 'http://localhost:8080'
// http(s) -> ws(s)
return base.replace(/^http/, 'ws') + '/ws'
}
function stompClient(onConnect: (c: Client) => void, onStatus?: (connected: boolean) => void) {
const client = new Client({
brokerURL: wsUrl(),
connectHeaders: { Authorization: `Bearer ${tokenStore.get() ?? ''}` },
reconnectDelay: 3000,
onConnect: () => {
onStatus?.(true)
onConnect(client)
},
onDisconnect: () => onStatus?.(false),
onWebSocketClose: () => onStatus?.(false),
})
return client
}
/**
* 스팟 채팅용 STOMP 클라이언트.
* connect 후 스팟 토픽을 구독하고, send 로 메시지를 전송한다.
*/
export function createSpotChat(
spotId: number,
onMessage: (m: ChatMessage) => void,
onStatus?: (connected: boolean) => void,
) {
const client = new Client({
brokerURL: wsUrl(),
connectHeaders: { Authorization: `Bearer ${tokenStore.get() ?? ''}` },
reconnectDelay: 3000,
onConnect: () => {
onStatus?.(true)
client.subscribe(`/topic/spots/${spotId}`, (msg: IMessage) => {
onMessage(JSON.parse(msg.body) as ChatMessage)
})
},
onDisconnect: () => onStatus?.(false),
onWebSocketClose: () => onStatus?.(false),
})
return {
activate: () => client.activate(),
deactivate: () => client.deactivate(),
send: (content: string) => {
client.publish({
destination: `/app/spots/${spotId}/chat`,
body: JSON.stringify({ content }),
})
},
isConnected: () => client.connected,
}
}
/** 매칭 수락으로 열린 1:1 채팅용 STOMP 클라이언트. */
export function createMatchChat(
matchId: number,
onMessage: (m: ChatMessage) => void,
onStatus?: (connected: boolean) => void,
) {
const client = stompClient((c) => {
c.subscribe(`/topic/matches/${matchId}`, (msg: IMessage) => {
onMessage(JSON.parse(msg.body) as ChatMessage)
})
}, onStatus)
return {
activate: () => client.activate(),
deactivate: () => client.deactivate(),
send: (content: string) => {
client.publish({
destination: `/app/matches/${matchId}/chat`,
body: JSON.stringify({ content }),
})
},
isConnected: () => client.connected,
}
}
/** 로그인 사용자용 매칭 실시간 알림 구독 (/topic/users/{userId}). */
export function createUserNotifications(
userId: number,
onNotification: (n: MatchNotification) => void,
) {
const client = stompClient((c) => {
c.subscribe(`/topic/users/${userId}`, (msg: IMessage) => {
onNotification(JSON.parse(msg.body) as MatchNotification)
})
})
return {
activate: () => client.activate(),
deactivate: () => client.deactivate(),
}
}