Browse Source

Fix WebSocket auth flow and improve header user profile display

- Fix NIP-42 auth race condition: wait for AUTH challenge before authenticating
- Header user profile: avatar fills vertical space, username vertically centered
- Remove username truncation to show full name/npub
- Standardize header height to 3em across all components

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
main
mleku 4 weeks ago
parent
commit
602d563a7c
No known key found for this signature in database
  1. 2
      app/web/src/App.svelte
  2. 48
      app/web/src/Header.svelte
  3. 2
      app/web/src/Sidebar.svelte
  4. 40
      app/web/src/websocket-auth.js

2
app/web/src/App.svelte

@ -3466,7 +3466,7 @@ @@ -3466,7 +3466,7 @@
.main-content {
position: fixed;
left: 200px;
top: 2.5em;
top: 3em;
right: 0;
bottom: 0;
padding: 0;

48
app/web/src/Header.svelte

@ -45,7 +45,7 @@ @@ -45,7 +45,7 @@
<div class="user-avatar-placeholder">👤</div>
{/if}
<span class="user-name">
{userProfile?.name || userPubkey.slice(0, 8) + "..."}
{userProfile?.name || userPubkey}
</span>
</button>
{:else}
@ -64,32 +64,35 @@ @@ -64,32 +64,35 @@
top: 0;
left: 0;
right: 0;
height: 3em;
background: var(--header-bg);
border: 0;
z-index: 1000;
display: flex;
align-items: space-between;
padding: 0.25em;
align-items: stretch;
padding: 0 0.25em;
}
.header-content {
display: flex;
align-items: center;
align-items: stretch;
width: 100%;
padding: 0;
margin: 0;
}
.logo {
height: 2em;
height: 2.5em;
width: auto;
flex-shrink: 0;
align-self: center;
}
.header-title {
flex: 1;
display: flex;
align-items: center;
align-self: center;
}
.app-title {
@ -114,8 +117,8 @@ @@ -114,8 +117,8 @@
.header-buttons {
display: flex;
align-items: center;
height: 100%;
align-items: stretch;
align-self: stretch;
margin-left: auto;
}
@ -124,15 +127,14 @@ @@ -124,15 +127,14 @@
background: transparent;
color: var(--button-text);
border: 0;
height: 100%;
cursor: pointer;
font-size: 1em;
transition: background-color 0.2s;
flex-shrink: 0;
padding: 0.5em;
margin: 0;
display: block;
align-items: center;
display: flex !important;
align-items: center !important;
justify-content: center;
}
@ -144,32 +146,40 @@ @@ -144,32 +146,40 @@
.user-profile-btn {
gap: 0.5em;
justify-content: flex-start;
padding: 0 0.75em;
padding: 0 0.5em;
}
.user-avatar {
width: 1.5em;
height: 1.5em;
height: 2.5em;
width: 2.5em;
border-radius: 50%;
object-fit: cover;
flex-shrink: 0;
align-self: center;
vertical-align: middle;
}
.user-avatar-placeholder {
width: 1.5em;
height: 1.5em;
height: 2.5em;
width: 2.5em;
border-radius: 50%;
background: var(--bg-color);
display: flex;
align-items: center;
justify-content: center;
font-size: 0.8em;
font-size: 1.2em;
flex-shrink: 0;
align-self: center;
}
.user-name {
font-weight: 500;
max-width: 120px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: 1;
align-self: center;
max-width: none !important;
overflow: visible !important;
text-overflow: unset !important;
width: auto !important;
}
</style>

2
app/web/src/Sidebar.svelte

@ -47,7 +47,7 @@ @@ -47,7 +47,7 @@
.sidebar {
position: fixed;
left: 0;
top: 2.5em;
top: 3em;
width: 200px;
bottom: 0;
background: var(--sidebar-bg);

40
app/web/src/websocket-auth.js

@ -175,10 +175,9 @@ export class NostrWebSocketAuth { @@ -175,10 +175,9 @@ export class NostrWebSocketAuth {
const [messageType, eventId, success, reason] = data;
if (messageType === 'OK' && eventId === event.id) {
clearTimeout(timeout);
this.ws.onmessage = originalOnMessage;
if (success) {
clearTimeout(timeout);
this.ws.onmessage = originalOnMessage;
console.log('Event published successfully:', eventId);
resolve({ success: true, eventId, reason });
} else {
@ -186,22 +185,33 @@ export class NostrWebSocketAuth { @@ -186,22 +185,33 @@ export class NostrWebSocketAuth {
// Check if authentication is required
if (reason && reason.includes('auth-required')) {
console.log('Authentication required, attempting to authenticate...');
try {
await this.authenticate();
// Re-send the event after authentication
const retryMessage = ["EVENT", event];
this.ws.send(JSON.stringify(retryMessage));
// Don't resolve yet, wait for the retry response
return;
} catch (authError) {
reject(new Error(`Authentication failed: ${authError.message}`));
return;
}
console.log('Authentication required, waiting for AUTH challenge...');
// Don't restore original handler yet - we need to receive the AUTH challenge
// The AUTH message will be handled by the else branch below
return;
}
clearTimeout(timeout);
this.ws.onmessage = originalOnMessage;
reject(new Error(`Publish failed: ${reason}`));
}
} else if (messageType === 'AUTH') {
// Handle AUTH challenge during publish flow
this.challenge = data[1];
console.log('Received AUTH challenge during publish:', this.challenge);
try {
await this.authenticate();
console.log('Authentication successful, retrying event publish...');
// Re-send the event after authentication
const retryMessage = ["EVENT", event];
this.ws.send(JSON.stringify(retryMessage));
// Don't resolve yet, wait for the retry response
} catch (authError) {
clearTimeout(timeout);
this.ws.onmessage = originalOnMessage;
reject(new Error(`Authentication failed: ${authError.message}`));
}
} else {
// Handle other messages normally
await this.handleMessage(data);

Loading…
Cancel
Save