diff --git a/lite/build.js b/lite/build.js index 4cac4e3..2764fa5 100644 --- a/lite/build.js +++ b/lite/build.js @@ -675,7 +675,7 @@ class FloatingTab { appearance: { style: 'pill', // 'pill', 'square', 'circle' theme: 'auto', // 'auto', 'light', 'dark' - icon: '[LOGIN]', + icon: '', text: 'Login', iconOnly: false }, @@ -685,11 +685,14 @@ class FloatingTab { autoSlide: true, persistent: false }, + getUserInfo: false, + getUserRelay: [], ...options }; this.isAuthenticated = false; this.userInfo = null; + this.userProfile = null; this.container = null; this.isVisible = false; @@ -790,11 +793,24 @@ class FloatingTab { } } - _handleAuth(authData) { + async _handleAuth(authData) { console.log('FloatingTab: Handling authentication:', authData); this.isAuthenticated = true; this.userInfo = authData; + // Fetch user profile if enabled and we have a pubkey + if (this.options.getUserInfo && authData.pubkey) { + console.log('FloatingTab: Fetching user profile for:', authData.pubkey); + try { + const profile = await this._fetchUserProfile(authData.pubkey); + this.userProfile = profile; + console.log('FloatingTab: User profile fetched:', profile); + } catch (error) { + console.warn('FloatingTab: Failed to fetch user profile:', error); + this.userProfile = null; + } + } + if (this.options.behavior.hideWhenAuthenticated) { this.hide(); } else { @@ -806,6 +822,7 @@ class FloatingTab { console.log('FloatingTab: Handling logout'); this.isAuthenticated = false; this.userInfo = null; + this.userProfile = null; if (this.options.behavior.hideWhenAuthenticated) { this.show(); @@ -873,18 +890,29 @@ class FloatingTab { // Update content if (this.isAuthenticated && this.options.behavior.showUserInfo) { - const display = this.userInfo?.pubkey ? - (this.options.appearance.iconOnly ? - '[USER]' : - \`[USER] \${this.userInfo.pubkey.slice(0, 6)}...\`) : - (this.options.appearance.iconOnly ? '[AUTH]' : '[AUTH] Logged In'); + let display; + + // Use profile name if available, otherwise fall back to pubkey + if (this.userProfile?.name || this.userProfile?.display_name) { + const userName = this.userProfile.name || this.userProfile.display_name; + display = this.options.appearance.iconOnly + ? userName.slice(0, 8) + : userName; + } else if (this.userInfo?.pubkey) { + // Fallback to pubkey display + display = this.options.appearance.iconOnly + ? this.userInfo.pubkey.slice(0, 6) + : \`\${this.userInfo.pubkey.slice(0, 6)}...\`; + } else { + display = this.options.appearance.iconOnly ? 'User' : 'Authenticated'; + } this.container.textContent = display; this.container.className = 'nl-floating-tab nl-floating-tab--logged-in'; } else { const display = this.options.appearance.iconOnly ? this.options.appearance.icon : - \`\${this.options.appearance.icon} \${this.options.appearance.text}\`; + (this.options.appearance.icon ? \`\${this.options.appearance.icon} \${this.options.appearance.text}\` : this.options.appearance.text); this.container.textContent = display; this.container.className = 'nl-floating-tab nl-floating-tab--logged-out'; @@ -915,6 +943,62 @@ class FloatingTab { } } + async _fetchUserProfile(pubkey) { + if (!this.options.getUserInfo) { + console.log('FloatingTab: getUserInfo disabled, skipping profile fetch'); + return null; + } + + // Determine which relays to use + const relays = this.options.getUserRelay.length > 0 + ? this.options.getUserRelay + : (this.modal?.options?.relays || ['wss://relay.damus.io', 'wss://nos.lol']); + + console.log('FloatingTab: Fetching profile from relays:', relays); + + try { + // Create a SimplePool instance for querying + const pool = new window.NostrTools.nip46.SimplePool(); + + // Query for kind 0 (user metadata) events + const events = await pool.querySync(relays, { + kinds: [0], + authors: [pubkey], + limit: 1 + }, { timeout: 5000 }); + + console.log('FloatingTab: Profile query returned', events.length, 'events'); + + if (events.length === 0) { + console.log('FloatingTab: No profile events found'); + return null; + } + + // Get the most recent event + const latestEvent = events.sort((a, b) => b.created_at - a.created_at)[0]; + + try { + const profile = JSON.parse(latestEvent.content); + console.log('FloatingTab: Parsed profile:', profile); + + // Return relevant profile fields + return { + name: profile.name || null, + display_name: profile.display_name || null, + about: profile.about || null, + picture: profile.picture || null, + nip05: profile.nip05 || null + }; + } catch (parseError) { + console.warn('FloatingTab: Failed to parse profile JSON:', parseError); + return null; + } + } catch (error) { + console.error('FloatingTab: Profile fetch error:', error); + return null; + } + } + _position() { if (!this.container) return; @@ -1076,7 +1160,7 @@ class NostrLite { appearance: { style: 'pill', theme: 'auto', - icon: '[LOGIN]', + icon: '', text: 'Login', iconOnly: false }, @@ -1085,7 +1169,9 @@ class NostrLite { showUserInfo: true, autoSlide: true, persistent: false - } + }, + getUserInfo: false, + getUserRelay: [] }, ...options }; diff --git a/lite/nostr-lite.js b/lite/nostr-lite.js index 578ff5c..2717e91 100644 --- a/lite/nostr-lite.js +++ b/lite/nostr-lite.js @@ -8,7 +8,7 @@ * Two-file architecture: * 1. Load nostr.bundle.js (official nostr-tools bundle) * 2. Load nostr-lite.js (this file - NOSTR_LOGIN_LITE library with CSS-only themes) - * Generated on: 2025-09-15T18:15:24.222Z + * Generated on: 2025-09-15T18:50:50.789Z */ // Verify dependencies are loaded @@ -1076,26 +1076,6 @@ class Modal { button.style.background = 'var(--nl-secondary-color)'; }; - const iconDiv = document.createElement('div'); - // Replace emoji icons with text-based ones - const iconMap = { - '🔌': '[EXT]', - '🔑': '[KEY]', - '🌐': '[NET]', - '👁️': '[VIEW]', - '📱': '[SMS]' - }; - iconDiv.textContent = iconMap[option.icon] || option.icon; - iconDiv.style.cssText = ` - font-size: 16px; - font-weight: bold; - margin-right: 16px; - width: 50px; - text-align: center; - color: var(--nl-primary-color); - font-family: var(--nl-font-family, 'Courier New', monospace); - `; - const contentDiv = document.createElement('div'); contentDiv.style.cssText = 'flex: 1; text-align: left;'; @@ -1119,7 +1099,6 @@ class Modal { contentDiv.appendChild(titleDiv); contentDiv.appendChild(descDiv); - button.appendChild(iconDiv); button.appendChild(contentDiv); this.modalBody.appendChild(button); }); @@ -2144,7 +2123,7 @@ class FloatingTab { appearance: { style: 'pill', // 'pill', 'square', 'circle' theme: 'auto', // 'auto', 'light', 'dark' - icon: '[LOGIN]', + icon: '', text: 'Login', iconOnly: false }, @@ -2154,11 +2133,14 @@ class FloatingTab { autoSlide: true, persistent: false }, + getUserInfo: false, + getUserRelay: [], ...options }; this.isAuthenticated = false; this.userInfo = null; + this.userProfile = null; this.container = null; this.isVisible = false; @@ -2259,11 +2241,24 @@ class FloatingTab { } } - _handleAuth(authData) { + async _handleAuth(authData) { console.log('FloatingTab: Handling authentication:', authData); this.isAuthenticated = true; this.userInfo = authData; + // Fetch user profile if enabled and we have a pubkey + if (this.options.getUserInfo && authData.pubkey) { + console.log('FloatingTab: Fetching user profile for:', authData.pubkey); + try { + const profile = await this._fetchUserProfile(authData.pubkey); + this.userProfile = profile; + console.log('FloatingTab: User profile fetched:', profile); + } catch (error) { + console.warn('FloatingTab: Failed to fetch user profile:', error); + this.userProfile = null; + } + } + if (this.options.behavior.hideWhenAuthenticated) { this.hide(); } else { @@ -2275,6 +2270,7 @@ class FloatingTab { console.log('FloatingTab: Handling logout'); this.isAuthenticated = false; this.userInfo = null; + this.userProfile = null; if (this.options.behavior.hideWhenAuthenticated) { this.show(); @@ -2342,18 +2338,29 @@ class FloatingTab { // Update content if (this.isAuthenticated && this.options.behavior.showUserInfo) { - const display = this.userInfo?.pubkey ? - (this.options.appearance.iconOnly ? - '[USER]' : - `[USER] ${this.userInfo.pubkey.slice(0, 6)}...`) : - (this.options.appearance.iconOnly ? '[AUTH]' : '[AUTH] Logged In'); + let display; + + // Use profile name if available, otherwise fall back to pubkey + if (this.userProfile?.name || this.userProfile?.display_name) { + const userName = this.userProfile.name || this.userProfile.display_name; + display = this.options.appearance.iconOnly + ? userName.slice(0, 8) + : userName; + } else if (this.userInfo?.pubkey) { + // Fallback to pubkey display + display = this.options.appearance.iconOnly + ? this.userInfo.pubkey.slice(0, 6) + : `${this.userInfo.pubkey.slice(0, 6)}...`; + } else { + display = this.options.appearance.iconOnly ? 'User' : 'Authenticated'; + } this.container.textContent = display; this.container.className = 'nl-floating-tab nl-floating-tab--logged-in'; } else { const display = this.options.appearance.iconOnly ? this.options.appearance.icon : - `${this.options.appearance.icon} ${this.options.appearance.text}`; + (this.options.appearance.icon ? `${this.options.appearance.icon} ${this.options.appearance.text}` : this.options.appearance.text); this.container.textContent = display; this.container.className = 'nl-floating-tab nl-floating-tab--logged-out'; @@ -2384,6 +2391,62 @@ class FloatingTab { } } + async _fetchUserProfile(pubkey) { + if (!this.options.getUserInfo) { + console.log('FloatingTab: getUserInfo disabled, skipping profile fetch'); + return null; + } + + // Determine which relays to use + const relays = this.options.getUserRelay.length > 0 + ? this.options.getUserRelay + : (this.modal?.options?.relays || ['wss://relay.damus.io', 'wss://nos.lol']); + + console.log('FloatingTab: Fetching profile from relays:', relays); + + try { + // Create a SimplePool instance for querying + const pool = new window.NostrTools.nip46.SimplePool(); + + // Query for kind 0 (user metadata) events + const events = await pool.querySync(relays, { + kinds: [0], + authors: [pubkey], + limit: 1 + }, { timeout: 5000 }); + + console.log('FloatingTab: Profile query returned', events.length, 'events'); + + if (events.length === 0) { + console.log('FloatingTab: No profile events found'); + return null; + } + + // Get the most recent event + const latestEvent = events.sort((a, b) => b.created_at - a.created_at)[0]; + + try { + const profile = JSON.parse(latestEvent.content); + console.log('FloatingTab: Parsed profile:', profile); + + // Return relevant profile fields + return { + name: profile.name || null, + display_name: profile.display_name || null, + about: profile.about || null, + picture: profile.picture || null, + nip05: profile.nip05 || null + }; + } catch (parseError) { + console.warn('FloatingTab: Failed to parse profile JSON:', parseError); + return null; + } + } catch (error) { + console.error('FloatingTab: Profile fetch error:', error); + return null; + } + } + _position() { if (!this.container) return; @@ -2545,7 +2608,7 @@ class NostrLite { appearance: { style: 'pill', theme: 'auto', - icon: '[LOGIN]', + icon: '', text: 'Login', iconOnly: false }, @@ -2554,7 +2617,9 @@ class NostrLite { showUserInfo: true, autoSlide: true, persistent: false - } + }, + getUserInfo: false, + getUserRelay: [] }, ...options }; diff --git a/lite/ui/modal.js b/lite/ui/modal.js index 0628d8c..8177b5e 100644 --- a/lite/ui/modal.js +++ b/lite/ui/modal.js @@ -280,26 +280,6 @@ class Modal { button.style.background = 'var(--nl-secondary-color)'; }; - const iconDiv = document.createElement('div'); - // Replace emoji icons with text-based ones - const iconMap = { - '🔌': '[EXT]', - '🔑': '[KEY]', - '🌐': '[NET]', - '👁️': '[VIEW]', - '📱': '[SMS]' - }; - iconDiv.textContent = iconMap[option.icon] || option.icon; - iconDiv.style.cssText = ` - font-size: 16px; - font-weight: bold; - margin-right: 16px; - width: 50px; - text-align: center; - color: var(--nl-primary-color); - font-family: var(--nl-font-family, 'Courier New', monospace); - `; - const contentDiv = document.createElement('div'); contentDiv.style.cssText = 'flex: 1; text-align: left;'; @@ -323,7 +303,6 @@ class Modal { contentDiv.appendChild(titleDiv); contentDiv.appendChild(descDiv); - button.appendChild(iconDiv); button.appendChild(contentDiv); this.modalBody.appendChild(button); });