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(), } }