diff --git a/src/App.tsx b/src/App.tsx index 75583cb..b2feab3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,123 +1,19 @@ -import { useEffect, useMemo, useState } from 'react' -import axios from 'axios' -import { io, Socket } from 'socket.io-client' -import './App.css' - -type Health = { ok: boolean; service: string; database?: string } -type MessageItem = { - messageId: string - seq: number - content: string - createdAt: string - isDeleted?: boolean -} - -const API_BASE = import.meta.env.VITE_GUILD_API_BASE ?? 'http://localhost:7002/api' -const SOCKET_BASE = import.meta.env.VITE_GUILD_SOCKET_BASE ?? 'http://localhost:7002/realtime' -const API_KEY = import.meta.env.VITE_API_KEY ?? 'change-me-api-key' - -function App() { - const [health, setHealth] = useState(null) - const [channelId, setChannelId] = useState('') - const [messages, setMessages] = useState([]) - const [content, setContent] = useState('') - const [socketState, setSocketState] = useState<'offline' | 'online'>('offline') - - const client = useMemo( - () => - axios.create({ - baseURL: API_BASE, - headers: { 'x-api-key': API_KEY }, - }), - [], - ) - - useEffect(() => { - client.get('/healthz').then((res) => setHealth(res.data)).catch(() => setHealth(null)) - }, [client]) - - useEffect(() => { - if (!channelId) return - - const socket: Socket = io(SOCKET_BASE, { - transports: ['websocket'], - auth: { apiKey: API_KEY, userId: 'frontend-user' }, - extraHeaders: { 'x-api-key': API_KEY }, - }) - - socket.on('connect', () => { - setSocketState('online') - socket.emit('join_channel', { channelId }) - }) - - socket.on('disconnect', () => setSocketState('offline')) - socket.on('message.created', (m) => setMessages((prev) => [...prev, m])) - socket.on('message.updated', (m) => - setMessages((prev) => prev.map((x) => (x.messageId === m.messageId ? m : x))), - ) - socket.on('message.deleted', (m) => - setMessages((prev) => - prev.map((x) => (x.messageId === m.messageId ? { ...x, isDeleted: true, content: '[deleted]' } : x)), - ), - ) - - return () => { - socket.emit('leave_channel', { channelId }) - socket.disconnect() - setSocketState('offline') - } - }, [channelId]) - - async function pullMessages() { - if (!channelId) return - const res = await client.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 client.post(`/channels/${channelId}/messages`, { - content, - authorUserId: 'frontend-user', - }) - setContent('') - } +import { Navigate, Route, Routes } from 'react-router-dom' +import AppLayout from './layouts/AppLayout' +import ChatPage from './pages/ChatPage' +import LoginPage from './pages/LoginPage' +import WorkspacePage from './pages/WorkspacePage' +export default function App() { return ( -
-

Fabric Frontend

-

Health: {health ? `${health.service} (${health.database ?? 'ok'})` : 'unavailable'}

-

Socket: {socketState}

- -
- setChannelId(e.target.value)} - placeholder="Channel ID" - /> - -
- -
- setContent(e.target.value)} - placeholder="输入消息" - /> - -
- -
    - {messages.map((m) => ( -
  • - #{m.seq} {m.content} -
  • - ))} -
-
+ + }> + } /> + } /> + } /> + } /> + + } /> + ) } - -export default App diff --git a/src/layouts/AppLayout.tsx b/src/layouts/AppLayout.tsx new file mode 100644 index 0000000..a7142e3 --- /dev/null +++ b/src/layouts/AppLayout.tsx @@ -0,0 +1,19 @@ +import { Link, Outlet } from 'react-router-dom' + +export default function AppLayout() { + return ( +
+ +
+ +
+
+ ) +} diff --git a/src/main.tsx b/src/main.tsx index bef5202..ade9d64 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,10 +1,13 @@ import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' +import { BrowserRouter } from 'react-router-dom' import './index.css' import App from './App.tsx' createRoot(document.getElementById('root')!).render( - + + + , ) diff --git a/src/pages/ChatPage.tsx b/src/pages/ChatPage.tsx new file mode 100644 index 0000000..61e825e --- /dev/null +++ b/src/pages/ChatPage.tsx @@ -0,0 +1,8 @@ +export default function ChatPage() { + return ( +
+

聊天

+

下一步接入消息拉取/发送/编辑/删除与实时订阅。

+
+ ) +} diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx new file mode 100644 index 0000000..262f3e3 --- /dev/null +++ b/src/pages/LoginPage.tsx @@ -0,0 +1,8 @@ +export default function LoginPage() { + return ( +
+

登录

+

下一步接入 Center 登录与 token 管理。

+
+ ) +} diff --git a/src/pages/WorkspacePage.tsx b/src/pages/WorkspacePage.tsx new file mode 100644 index 0000000..a9a6e26 --- /dev/null +++ b/src/pages/WorkspacePage.tsx @@ -0,0 +1,8 @@ +export default function WorkspacePage() { + return ( +
+

工作台

+

这里会展示 Guild/Channel 概览与会话入口。

+
+ ) +}