fix(rnotes): resolve anonymous identity in comments, suggestions, and cursors

Session was stored as { claims: { username, sub } } but getSessionInfo()
read sess.username (top-level) instead of sess.claims.username — always
falling back to "Anonymous". Fixed in all 3 locations: folk-notes-app,
comment-panel, collab-presence.

Also fixed awareness race: Yjs provider broadcast initial awareness before
user field was set, creating a ghost "anonymous" peer. Now identity is set
on awareness before provider connects, and the duplicate set is removed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-03-28 16:34:08 -07:00
parent 99fa59c8df
commit e8d88cd10e
3 changed files with 17 additions and 13 deletions

View File

@ -87,9 +87,10 @@ class NotesCommentPanel extends HTMLElement {
private getSessionInfo(): { authorName: string; authorId: string } { private getSessionInfo(): { authorName: string; authorId: string } {
try { try {
const sess = JSON.parse(localStorage.getItem('encryptid_session') || '{}'); const sess = JSON.parse(localStorage.getItem('encryptid_session') || '{}');
const c = sess?.claims;
return { return {
authorName: sess?.username || sess?.displayName || 'Anonymous', authorName: c?.username || c?.displayName || sess?.username || 'Anonymous',
authorId: sess?.userId || sess?.sub || 'anon', authorId: c?.sub || sess?.userId || 'anon',
}; };
} catch { } catch {
return { authorName: 'Anonymous', authorId: 'anon' }; return { authorName: 'Anonymous', authorId: 'anon' };

View File

@ -1306,9 +1306,17 @@ Gear: EUR 400 (10%)</code></pre><p><em>Maya is tracking expenses in rF
// IndexedDB persistence for offline // IndexedDB persistence for offline
this.yIndexedDb = new IndexeddbPersistence(roomName, this.ydoc); this.yIndexedDb = new IndexeddbPersistence(roomName, this.ydoc);
// Set awareness identity BEFORE connecting provider (avoids anonymous ghost)
const sessionForAwareness = this.getSessionInfo();
// Connect Yjs provider over rSpace WebSocket // Connect Yjs provider over rSpace WebSocket
if (runtime?.isInitialized) { if (runtime?.isInitialized) {
this.yjsProvider = new RSpaceYjsProvider(note.id, this.ydoc, runtime); this.yjsProvider = new RSpaceYjsProvider(note.id, this.ydoc, runtime);
// Pre-set user so the first awareness broadcast has the correct name
this.yjsProvider.awareness.setLocalStateField('user', {
name: sessionForAwareness.username || 'Anonymous',
color: this.userColor(sessionForAwareness.userId || 'anon'),
});
} }
// Content migration: if Y.Doc fragment is empty and Automerge has content // Content migration: if Y.Doc fragment is empty and Automerge has content
@ -1377,13 +1385,6 @@ Gear: EUR 400 (10%)</code></pre><p><em>Maya is tracking expenses in rF
); );
this.editor.registerPlugin(cursorPlugin); this.editor.registerPlugin(cursorPlugin);
// Set local user state on awareness
const session = this.getSessionInfo();
this.yjsProvider.awareness.setLocalStateField('user', {
name: session.username || 'Anonymous',
color: this.userColor(session.userId || 'anon'),
});
// Update collab status bar when peers change // Update collab status bar when peers change
this.yjsProvider.awareness.on('update', () => { this.yjsProvider.awareness.on('update', () => {
this.updatePeersIndicator(); this.updatePeersIndicator();
@ -1479,9 +1480,10 @@ Gear: EUR 400 (10%)</code></pre><p><em>Maya is tracking expenses in rF
private getSessionInfo(): { username: string; userId: string } { private getSessionInfo(): { username: string; userId: string } {
try { try {
const sess = JSON.parse(localStorage.getItem('encryptid_session') || '{}'); const sess = JSON.parse(localStorage.getItem('encryptid_session') || '{}');
const c = sess?.claims;
return { return {
username: sess?.username || sess?.displayName || 'Anonymous', username: c?.username || c?.displayName || sess?.username || 'Anonymous',
userId: sess?.userId || sess?.sub || 'anon', userId: c?.sub || sess?.userId || 'anon',
}; };
} catch { } catch {
return { username: 'Anonymous', userId: 'anon' }; return { username: 'Anonymous', userId: 'anon' };

View File

@ -12,9 +12,10 @@
function getSessionInfo(): { username: string; userId: string } { function getSessionInfo(): { username: string; userId: string } {
try { try {
const sess = JSON.parse(localStorage.getItem('encryptid_session') || '{}'); const sess = JSON.parse(localStorage.getItem('encryptid_session') || '{}');
const c = sess?.claims;
return { return {
username: sess?.username || sess?.displayName || 'Anonymous', username: c?.username || c?.displayName || sess?.username || 'Anonymous',
userId: sess?.userId || sess?.sub || 'anon', userId: c?.sub || sess?.userId || 'anon',
}; };
} catch { } catch {
return { username: 'Anonymous', userId: 'anon' }; return { username: 'Anonymous', userId: 'anon' };