feat(frontend): add collapsible v1/v2/v4 panels and guild members column

This commit is contained in:
nav
2026-05-14 16:46:38 +00:00
parent 12265e09ec
commit d0bbd4a20f
2 changed files with 90 additions and 8 deletions

View File

@@ -88,11 +88,45 @@ a:hover {
.chat-top-grid {
display: grid;
grid-template-columns: 220px 260px 1fr;
grid-template-columns: 220px 260px 1fr 220px;
gap: 12px;
min-height: 68vh;
}
.chat-top-grid.hide-v1 {
grid-template-columns: 0 260px 1fr 220px;
}
.chat-top-grid.hide-v2 {
grid-template-columns: 220px 0 1fr 220px;
}
.chat-top-grid.hide-v4 {
grid-template-columns: 220px 260px 1fr 0;
}
.chat-top-grid.hide-v1.hide-v2 {
grid-template-columns: 0 0 1fr 220px;
}
.chat-top-grid.hide-v1.hide-v4 {
grid-template-columns: 0 260px 1fr 0;
}
.chat-top-grid.hide-v2.hide-v4 {
grid-template-columns: 220px 0 1fr 0;
}
.chat-top-grid.hide-v1.hide-v2.hide-v4 {
grid-template-columns: 0 0 1fr 0;
}
.chat-top-grid.hide-v1 .col-v1,
.chat-top-grid.hide-v2 .col-v2,
.chat-top-grid.hide-v4 .col-v4 {
display: none;
}
.chat-right {
display: grid;
grid-template-rows: 1fr auto;

View File

@@ -11,12 +11,19 @@ type MessageItem = {
}
type GuildChannel = { id: string; name: string }
type GuildRecord = { id: string; name: string }
type GuildMember = { id: string; userId: string; guildId: string; status: string }
export default function ChatPage() {
const { session, logout } = useAuth()
const [selectedGuildId, setSelectedGuildId] = useState('')
const [selectedChannelId, setSelectedChannelId] = useState('')
const [channels, setChannels] = useState<GuildChannel[]>([])
const [guildDbId, setGuildDbId] = useState('')
const [members, setMembers] = useState<GuildMember[]>([])
const [showGuilds, setShowGuilds] = useState(true)
const [showChannels, setShowChannels] = useState(true)
const [showMembers, setShowMembers] = useState(true)
const [messages, setMessages] = useState<MessageItem[]>([])
const [content, setContent] = useState('')
const [newChannelName, setNewChannelName] = useState('')
@@ -57,7 +64,9 @@ export default function ChatPage() {
if (!guild || !guildToken) return
setError('')
try {
const res = await guildApi().get('/channels')
const res = await guildApi().get('/channels', {
params: guildDbId ? { guildId: guildDbId } : undefined,
})
const list = Array.isArray(res.data) ? (res.data as GuildChannel[]) : []
setChannels(list)
if (!selectedChannelId && list.length) setSelectedChannelId(list[0].id)
@@ -67,6 +76,26 @@ export default function ChatPage() {
}
}
async function loadGuildMetaAndMembers() {
if (!guild || !guildToken) return
setError('')
try {
const guildRes = await guildApi().get('/guilds')
const guildList = Array.isArray(guildRes.data) ? (guildRes.data as GuildRecord[]) : []
const nextGuildDbId = guildList[0]?.id ?? ''
setGuildDbId(nextGuildDbId)
const memberRes = await guildApi().get('/members', {
params: nextGuildDbId ? { guildId: nextGuildDbId } : undefined,
})
const memberList = Array.isArray(memberRes.data) ? (memberRes.data as GuildMember[]) : []
setMembers(memberList)
} catch {
setError('Failed to load guild members')
setMembers([])
}
}
async function pullMessages() {
if (!selectedChannelId || !guild || !guildToken) return
setLoading(true)
@@ -102,7 +131,8 @@ export default function ChatPage() {
if (!guild || !guildToken || !newChannelName.trim()) return
setError('')
try {
const res = await guildApi().post('/channels', { name: newChannelName.trim() })
const payload = guildDbId ? { name: newChannelName.trim(), guildId: guildDbId } : { name: newChannelName.trim() }
const res = await guildApi().post('/channels', payload)
const createdId = res.data?.id as string | undefined
setNewChannelName('')
await loadChannels()
@@ -112,11 +142,15 @@ export default function ChatPage() {
}
}
useEffect(() => {
void loadGuildMetaAndMembers()
}, [selectedGuildId])
useEffect(() => {
void loadChannels()
setMessages([])
setSelectedChannelId('')
}, [selectedGuildId])
}, [selectedGuildId, guildDbId])
useEffect(() => {
void pullMessages()
@@ -145,8 +179,8 @@ export default function ChatPage() {
return (
<section className="chat-layout">
<div className="chat-top-grid">
<div className="panel col-list">
<div className={`chat-top-grid ${!showGuilds ? 'hide-v1' : ''} ${!showChannels ? 'hide-v2' : ''} ${!showMembers ? 'hide-v4' : ''}`}>
<div className="panel col-list col-v1">
<h3>Guilds</h3>
<ul className="list-reset">
{guilds.map((g) => (
@@ -159,7 +193,7 @@ export default function ChatPage() {
</ul>
</div>
<div className="panel col-list">
<div className="panel col-list col-v2">
<h3>Channels</h3>
<div className="row-wrap" style={{ marginBottom: 8 }}>
<input
@@ -181,7 +215,7 @@ export default function ChatPage() {
</ul>
</div>
<div className="chat-right">
<div className="chat-right col-v3">
<div className="panel chat-history">
<h3>Messages</h3>
{loading ? <p className="muted">Loading...</p> : null}
@@ -201,9 +235,23 @@ export default function ChatPage() {
<button className="btn" onClick={sendMessage}>Send</button>
</div>
</div>
<div className="panel col-list col-v4">
<h3>Members</h3>
<ul className="list-reset">
{members.map((m) => (
<li key={m.id}>
<span className="muted">{m.userId}</span>
</li>
))}
</ul>
</div>
</div>
<div className="panel footer-actions">
<button className="btn btn-secondary" onClick={() => setShowGuilds((v) => !v)}>{showGuilds ? 'Hide v1' : 'Show v1'}</button>
<button className="btn btn-secondary" onClick={() => setShowChannels((v) => !v)}>{showChannels ? 'Hide v2' : 'Show v2'}</button>
<button className="btn btn-secondary" onClick={() => setShowMembers((v) => !v)}>{showMembers ? 'Hide v4' : 'Show v4'}</button>
<button className="btn btn-secondary" onClick={() => void logout()}>Logout</button>
<button className="btn btn-secondary" type="button">Settings</button>
</div>