fix: deduplicate online user count by username
Same user with multiple tabs/connections (different peer IDs) was counted multiple times. Now deduplicates by username, keeping the most recently seen entry per user for badge count and panel display. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
55771eb26e
commit
7fec0cb699
|
|
@ -485,6 +485,19 @@ export class RStackCollabOverlay extends HTMLElement {
|
|||
}
|
||||
}
|
||||
|
||||
/** Deduplicate peers by username, keeping the most recently seen entry per user. */
|
||||
#uniquePeers(): PeerState[] {
|
||||
const byName = new Map<string, PeerState>();
|
||||
for (const peer of this.#peers.values()) {
|
||||
const key = peer.username.toLowerCase();
|
||||
const existing = byName.get(key);
|
||||
if (!existing || peer.lastSeen > existing.lastSeen) {
|
||||
byName.set(key, peer);
|
||||
}
|
||||
}
|
||||
return Array.from(byName.values());
|
||||
}
|
||||
|
||||
// ── Color assignment ──
|
||||
|
||||
#colorForPeer(peerId: string): string {
|
||||
|
|
@ -536,7 +549,8 @@ export class RStackCollabOverlay extends HTMLElement {
|
|||
const countEl = this.#shadow.getElementById('panel-count');
|
||||
if (!list) return;
|
||||
|
||||
const onlineCount = this.#peers.size + 1;
|
||||
const uniquePeers = this.#uniquePeers();
|
||||
const onlineCount = uniquePeers.length + 1;
|
||||
if (countEl) countEl.textContent = this.#connState === 'connected' ? `${onlineCount} online` : '\u2014';
|
||||
|
||||
const fragments: string[] = [];
|
||||
|
|
@ -566,9 +580,9 @@ export class RStackCollabOverlay extends HTMLElement {
|
|||
</div>
|
||||
`);
|
||||
|
||||
// Remote peer rows
|
||||
// Remote peer rows (deduplicated by username)
|
||||
const isCanvas = this.#moduleId === 'rspace';
|
||||
for (const [pid, peer] of this.#peers) {
|
||||
for (const peer of uniquePeers) {
|
||||
const ctxParts: string[] = [];
|
||||
if (peer.module) ctxParts.push(peer.module);
|
||||
if (peer.context) ctxParts.push(peer.context);
|
||||
|
|
@ -580,15 +594,15 @@ export class RStackCollabOverlay extends HTMLElement {
|
|||
<span class="name">${this.#escHtml(peer.username)}</span>
|
||||
${ctxStr ? `<span class="peer-context">${this.#escHtml(ctxStr)}</span>` : ''}
|
||||
</div>
|
||||
${isCanvas ? `<button class="actions-btn" data-pid="${this.#escHtml(pid)}">></button>` : ''}
|
||||
${isCanvas ? `<button class="actions-btn" data-pid="${this.#escHtml(peer.peerId)}">></button>` : ''}
|
||||
</div>
|
||||
`);
|
||||
// Expanded actions for canvas
|
||||
if (isCanvas && this.#openActionsId === pid) {
|
||||
if (isCanvas && this.#openActionsId === peer.peerId) {
|
||||
fragments.push(`
|
||||
<div class="people-actions">
|
||||
<button data-action="navigate" data-pid="${this.#escHtml(pid)}">Navigate to</button>
|
||||
<button data-action="ping" data-pid="${this.#escHtml(pid)}">Ping to join you</button>
|
||||
<button data-action="navigate" data-pid="${this.#escHtml(peer.peerId)}">Navigate to</button>
|
||||
<button data-action="ping" data-pid="${this.#escHtml(peer.peerId)}">Ping to join you</button>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
|
|
@ -707,9 +721,10 @@ export class RStackCollabOverlay extends HTMLElement {
|
|||
return;
|
||||
}
|
||||
|
||||
const count = this.#peers.size + 1; // +1 for self
|
||||
const uniquePeers = this.#uniquePeers();
|
||||
const count = uniquePeers.length + 1; // +1 for self
|
||||
|
||||
const dots = Array.from(this.#peers.values())
|
||||
const dots = uniquePeers
|
||||
.slice(0, 5) // show max 5 dots
|
||||
.map(p => `<span class="dot" style="background:${p.color}"></span>`)
|
||||
.join('');
|
||||
|
|
|
|||
Loading…
Reference in New Issue