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 {
|
.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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user