aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/group-settings.js
diff options
context:
space:
mode:
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.js42
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}>