feat: chat — ⋮ room menu, typing indicator, online presence, read receipts
- RoomRow: ⋮ button (hover) for pin/unpin context menu - MessageBubble: right-click context menu with emoji reactions + edit/delete - Typing indicator: debounced setTyping, animated TypingDots, polling getTyping every 2s - Online presence: heartbeat setPresence every 30s, green dot on avatars/header - Read receipts: ✓/✓✓ for own messages in direct rooms via otherUserLastRead - Migration 075: chat_presence + chat_typing tables - Context menus clamped to viewport bounds Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -364,6 +364,14 @@ export const api = {
|
||||
req<ChatSearchResult[]>('GET', `/api/hotels/${slug}/chat/search?q=${encodeURIComponent(q)}`),
|
||||
notify: (slug: string, text: string, systemName?: string) =>
|
||||
req<ChatMessage>('POST', `/api/hotels/${slug}/chat/notify`, { text, system_name: systemName }),
|
||||
setPresence: (slug: string) =>
|
||||
req<{ ok: boolean }>('POST', `/api/hotels/${slug}/chat/presence`, {}),
|
||||
getPresence: (slug: string) =>
|
||||
req<string[]>('GET', `/api/hotels/${slug}/chat/presence`),
|
||||
setTyping: (slug: string, roomId: string, typing: boolean) =>
|
||||
req<{ ok: boolean }>('POST', `/api/hotels/${slug}/chat/rooms/${roomId}/typing`, { typing }),
|
||||
getTyping: (slug: string, roomId: string) =>
|
||||
req<string[]>('GET', `/api/hotels/${slug}/chat/rooms/${roomId}/typing`),
|
||||
},
|
||||
|
||||
// ── Workstations ──────────────────────────────────────────────────────────
|
||||
@@ -1333,6 +1341,7 @@ export interface ChatRoom {
|
||||
lastSender: string | null
|
||||
otherUserName: string | null
|
||||
otherUserId: string | null
|
||||
otherUserLastRead: string | null
|
||||
}
|
||||
|
||||
export interface ChatReaction {
|
||||
|
||||
Reference in New Issue
Block a user