// ========================================== // Chat Surface — Channel Members Panel // ========================================== // Drawer showing channel participants with role management. // Uses Drawer primitive + channels.participants API. import { Drawer } from '../../primitives/drawer.js'; import { UserPicker } from '../../primitives/user-picker.js'; const { html } = window; const { useState, useEffect, useCallback } = hooks; const ROLES = ['owner', 'member', 'observer']; function _initials(name) { if (!name) return '?'; return name.split(/\s+/).map(w => w[0]).join('').toUpperCase().slice(0, 2); } /** * @param {{ open: boolean, channelId: string, onClose: () => void }} props */ export function ChannelMembersPanel({ open, channelId, onClose }) { const [members, setMembers] = useState([]); const [loading, setLoading] = useState(false); const [adding, setAdding] = useState(false); const load = useCallback(async () => { if (!channelId) return; setLoading(true); try { const data = await sw.api.channels.participants(channelId); setMembers(data || []); } catch (e) { sw.toast(e.message, 'error'); } finally { setLoading(false); } }, [channelId]); useEffect(() => { if (open && channelId) load(); }, [open, channelId, load]); async function updateRole(pid, role) { try { await sw.api.channels.updateParticipant(channelId, pid, { role }); sw.toast('Role updated', 'success'); load(); } catch (e) { sw.toast(e.message, 'error'); } } async function remove(pid) { const ok = await sw.confirm('Remove this participant?', true); if (!ok) return; try { await sw.api.channels.removeParticipant(channelId, pid); sw.toast('Participant removed', 'success'); load(); } catch (e) { sw.toast(e.message, 'error'); } } const _onUserSelect = useCallback(async (user) => { setAdding(true); try { await sw.api.channels.addParticipant(channelId, { participant_type: 'user', participant_id: user.id, }); sw.toast('Participant added', 'success'); load(); } catch (e) { sw.toast(e.message, 'error'); } finally { setAdding(false); } }, [channelId, load]); if (!open) return null; return html` <${Drawer} open=${open} side="right" width="340px" title="Members" onClose=${onClose}> ${loading && !members.length ? html`
Loading\u2026
` : html`
${members.map(m => html`
${_initials(m.display_name || m.participant_id)}
${m.display_name || m.participant_id} ${m.participant_type}
`)} ${members.length === 0 && html`
No participants
`}
<${UserPicker} onSelect=${_onUserSelect} placeholder=${adding ? 'Adding\u2026' : 'Add participant\u2026'} />
`} `; }