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 { .chat-top-grid {
display: grid; display: grid;
grid-template-columns: 220px 260px 1fr; grid-template-columns: 220px 260px 1fr 220px;
gap: 12px; gap: 12px;
min-height: 68vh; 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 { .chat-right {
display: grid; display: grid;
grid-template-rows: 1fr auto; grid-template-rows: 1fr auto;

View File

@@ -11,12 +11,19 @@ type MessageItem = {
} }
type GuildChannel = { id: string; name: string } 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() { export default function ChatPage() {
const { session, logout } = useAuth() const { session, logout } = useAuth()
const [selectedGuildId, setSelectedGuildId] = useState('') const [selectedGuildId, setSelectedGuildId] = useState('')
const [selectedChannelId, setSelectedChannelId] = useState('') const [selectedChannelId, setSelectedChannelId] = useState('')
const [channels, setChannels] = useState<GuildChannel[]>([]) 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 [messages, setMessages] = useState<MessageItem[]>([])
const [content, setContent] = useState('') const [content, setContent] = useState('')
const [newChannelName, setNewChannelName] = useState('') const [newChannelName, setNewChannelName] = useState('')
@@ -57,7 +64,9 @@ export default function ChatPage() {
if (!guild || !guildToken) return if (!guild || !guildToken) return
setError('') setError('')
try { 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[]) : [] const list = Array.isArray(res.data) ? (res.data as GuildChannel[]) : []
setChannels(list) setChannels(list)
if (!selectedChannelId && list.length) setSelectedChannelId(list[0].id) 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() { async function pullMessages() {
if (!selectedChannelId || !guild || !guildToken) return if (!selectedChannelId || !guild || !guildToken) return
setLoading(true) setLoading(true)
@@ -102,7 +131,8 @@ export default function ChatPage() {
if (!guild || !guildToken || !newChannelName.trim()) return if (!guild || !guildToken || !newChannelName.trim()) return
setError('') setError('')
try { 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 const createdId = res.data?.id as string | undefined
setNewChannelName('') setNewChannelName('')
await loadChannels() await loadChannels()
@@ -112,11 +142,15 @@ export default function ChatPage() {
} }
} }
useEffect(() => {
void loadGuildMetaAndMembers()
}, [selectedGuildId])
useEffect(() => { useEffect(() => {
void loadChannels() void loadChannels()
setMessages([]) setMessages([])
setSelectedChannelId('') setSelectedChannelId('')
}, [selectedGuildId]) }, [selectedGuildId, guildDbId])
useEffect(() => { useEffect(() => {
void pullMessages() void pullMessages()
@@ -145,8 +179,8 @@ export default function ChatPage() {
return ( return (
<section className="chat-layout"> <section className="chat-layout">
<div className="chat-top-grid"> <div className={`chat-top-grid ${!showGuilds ? 'hide-v1' : ''} ${!showChannels ? 'hide-v2' : ''} ${!showMembers ? 'hide-v4' : ''}`}>
<div className="panel col-list"> <div className="panel col-list col-v1">
<h3>Guilds</h3> <h3>Guilds</h3>
<ul className="list-reset"> <ul className="list-reset">
{guilds.map((g) => ( {guilds.map((g) => (
@@ -159,7 +193,7 @@ export default function ChatPage() {
</ul> </ul>
</div> </div>
<div className="panel col-list"> <div className="panel col-list col-v2">
<h3>Channels</h3> <h3>Channels</h3>
<div className="row-wrap" style={{ marginBottom: 8 }}> <div className="row-wrap" style={{ marginBottom: 8 }}>
<input <input
@@ -181,7 +215,7 @@ export default function ChatPage() {
</ul> </ul>
</div> </div>
<div className="chat-right"> <div className="chat-right col-v3">
<div className="panel chat-history"> <div className="panel chat-history">
<h3>Messages</h3> <h3>Messages</h3>
{loading ? <p className="muted">Loading...</p> : null} {loading ? <p className="muted">Loading...</p> : null}
@@ -201,9 +235,23 @@ export default function ChatPage() {
<button className="btn" onClick={sendMessage}>Send</button> <button className="btn" onClick={sendMessage}>Send</button>
</div> </div>
</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>
<div className="panel footer-actions"> <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" onClick={() => void logout()}>Logout</button>
<button className="btn btn-secondary" type="button">Settings</button> <button className="btn btn-secondary" type="button">Settings</button>
</div> </div>