diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/group-settings.js')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/group-settings.js | 42 |
1 files changed, 39 insertions, 3 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/group-settings.js b/packages/meshbay-hub/src/meshbay_hub/static/group-settings.js index 29aa7eb..3ffa524 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/group-settings.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/group-settings.js @@ -783,6 +783,27 @@ function GroupSettingsPanel({ groupId, group, token, transportRef, gekRef, useEffect(() => { loadMembers(); }, [loadMembers]); + // Who the node has admitted to this group. The hub counts someone a member + // once they accept the invitation; the node, once they present their code. + // Until then they are not in the group, and are listed as waiting, to the + // owner only. Null when the roster cannot be read: the hub's list is shown. + const [admitted, setAdmitted] = useState(null); + useEffect(() => { + const transport = transportRef.current; + if (!connected || !transport || !transport.connected) { setAdmitted(null); return; } + let cancelled = false; + transport.groupRoster({ fresh: true }) + .then((roster) => { if (!cancelled) setAdmitted(new Set(roster.byAccount.keys())); }) + .catch(() => { if (!cancelled) setAdmitted(null); }); + return () => { cancelled = true; }; + }, [connected, transportRef, members]); + const joined = admitted + ? members.filter(m => m.user_id === adminId || admitted.has(m.user_id)) + : members; + const awaitingCode = admitted + ? members.filter(m => m.user_id !== adminId && !admitted.has(m.user_id)) + : []; + const ownsGroup = Boolean(group && group.is_admin); const loadHosts = useCallback(() => { if (!ownsGroup) return; @@ -1340,7 +1361,7 @@ function GroupSettingsPanel({ groupId, group, token, transportRef, gekRef, </${CollapsibleSection}> `} - <${CollapsibleSection} title=${`${t('group.tab_members')} (${members.length})`}> + <${CollapsibleSection} title=${`${t('group.tab_members')} (${joined.length})`}> <table class="admin-table"> <thead> <tr> @@ -1350,7 +1371,7 @@ function GroupSettingsPanel({ groupId, group, token, transportRef, gekRef, </tr> </thead> <tbody> - ${members.map(m => html` + ${joined.map(m => html` <tr key=${m.user_id}> <td>${m.username}</td> <td> @@ -1372,6 +1393,21 @@ function GroupSettingsPanel({ groupId, group, token, transportRef, gekRef, </td> </tr> `)} + ${isAdmin && awaitingCode.map(m => html` + <tr key=${m.user_id}> + <td>${m.username}</td> + <td><span class="badge">${t('members.awaiting_code')}</span></td> + <td class="admin-actions"> + <button class="admin-btn danger" disabled=${removing === m.user_id} + onClick=${async () => { + if (!await ask(t('members.remove_confirm', { user: m.username }))) return; + removeMember(m); + }}> + ${removing === m.user_id ? '...' : t('members.remove')} + </button> + </td> + </tr> + `)} ${invited.map(m => html` <tr key=${m.user_id}> <td>${m.username}</td> @@ -1391,7 +1427,7 @@ function GroupSettingsPanel({ groupId, group, token, transportRef, gekRef, `)} </tbody> </table> - ${isAdmin && members.length > 1 && html` + ${isAdmin && joined.length > 1 && html` <p class="settings-hint">${t('members.remove_hint')}</p> `} </${CollapsibleSection}> |