feat(frontend): add api client and socket client wrappers with runtime config
This commit is contained in:
@@ -1,8 +1,86 @@
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { getApiClient } from '../lib/api-client'
|
||||
import { disconnectSocket, getSocketClient } from '../lib/socket-client'
|
||||
|
||||
type MessageItem = {
|
||||
messageId: string
|
||||
seq: number
|
||||
content: string
|
||||
isDeleted?: boolean
|
||||
}
|
||||
|
||||
export default function ChatPage() {
|
||||
const [channelId, setChannelId] = useState('')
|
||||
const [content, setContent] = useState('')
|
||||
const [messages, setMessages] = useState<MessageItem[]>([])
|
||||
const [socketState, setSocketState] = useState<'offline' | 'online'>('offline')
|
||||
|
||||
const socket = useMemo(() => getSocketClient('frontend-user'), [])
|
||||
|
||||
useEffect(() => {
|
||||
socket.on('connect', () => setSocketState('online'))
|
||||
socket.on('disconnect', () => setSocketState('offline'))
|
||||
socket.on('message.created', (m: MessageItem) => setMessages((prev) => [...prev, m]))
|
||||
socket.on('message.updated', (m: MessageItem) =>
|
||||
setMessages((prev) => prev.map((x) => (x.messageId === m.messageId ? m : x))),
|
||||
)
|
||||
socket.on('message.deleted', (m: MessageItem) =>
|
||||
setMessages((prev) =>
|
||||
prev.map((x) => (x.messageId === m.messageId ? { ...x, isDeleted: true, content: '[deleted]' } : x)),
|
||||
),
|
||||
)
|
||||
socket.connect()
|
||||
|
||||
return () => {
|
||||
socket.removeAllListeners()
|
||||
disconnectSocket()
|
||||
}
|
||||
}, [socket])
|
||||
|
||||
async function pullMessages() {
|
||||
if (!channelId) return
|
||||
const res = await getApiClient().get(`/channels/${channelId}/messages`, {
|
||||
params: { seq_from: 1, seq_to: 999999, limit: 100 },
|
||||
})
|
||||
setMessages(res.data.items ?? [])
|
||||
}
|
||||
|
||||
async function sendMessage() {
|
||||
if (!channelId || !content.trim()) return
|
||||
await getApiClient().post(`/channels/${channelId}/messages`, {
|
||||
content,
|
||||
authorUserId: 'frontend-user',
|
||||
})
|
||||
setContent('')
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!channelId || !socket.connected) return
|
||||
socket.emit('join_channel', { channelId })
|
||||
return () => {
|
||||
socket.emit('leave_channel', { channelId })
|
||||
}
|
||||
}, [channelId, socket, socketState])
|
||||
|
||||
return (
|
||||
<section>
|
||||
<h2>聊天</h2>
|
||||
<p>下一步接入消息拉取/发送/编辑/删除与实时订阅。</p>
|
||||
<p>Socket: {socketState}</p>
|
||||
<div style={{ display: 'flex', gap: 8, marginBottom: 8 }}>
|
||||
<input value={channelId} onChange={(e) => setChannelId(e.target.value)} placeholder="Channel ID" />
|
||||
<button onClick={pullMessages}>拉取</button>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 8, marginBottom: 8 }}>
|
||||
<input value={content} onChange={(e) => setContent(e.target.value)} placeholder="输入消息" />
|
||||
<button onClick={sendMessage}>发送</button>
|
||||
</div>
|
||||
<ul>
|
||||
{messages.map((m) => (
|
||||
<li key={m.messageId}>
|
||||
#{m.seq} {m.content}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user