feat(frontend): add collapsible v1/v2/v4 panels and guild members column
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user