From 2dfbc08788b17796111ce947975c2bd02c38d15b Mon Sep 17 00:00:00 2001 From: miggymofongo Date: Sat, 7 Dec 2024 16:53:16 -0400 Subject: [PATCH] centralized state in the header component --- src/components/header.ts | 99 ++++++++++++++++++++-------------------- src/pages/app-home.ts | 44 ++++++++++++++---- src/pages/note-wall.ts | 28 ++++++------ 3 files changed, 98 insertions(+), 73 deletions(-) diff --git a/src/components/header.ts b/src/components/header.ts index 497969c..355abaa 100644 --- a/src/components/header.ts +++ b/src/components/header.ts @@ -63,62 +63,56 @@ export class AppHeader extends LitElement { } } `; - + /** the connected callback method one of the lifecycle methods that runs once the element is added + * to the dom. there is no disconnectedCallback because the header is never removed from the DOM. + * i centralize state in this component because it's where the sign in and out button resides. + * + * + */ connectedCallback(): void { super.connectedCallback(); - //is there a stored pubkey in localStorage already? + // Check for stored pubkey and fetch metadata if available const storedPubkey = localStorage.getItem('pubkey'); if (storedPubkey) { - this.isSignedIn = false; + this.isSignedIn = true; this.fetchProfileMetadata(storedPubkey); } else { - // Show guest view by default - this.displayGuestView(); - }} + this.displayGuestView(); + } + } displayGuestView() { - // Set initial values for guest view + // this method displays a guest view when the user is signed out this.nostrAddy = ''; this.bio = 'Welcome, guest! Please sign in with a browser extension to view your profile.'; - this.profilePic = '/public/assets/img/default_pfp.png'; // Could be a placeholder image for guests + this.profilePic = '/public/assets/img/default_pfp.png'; // a placeholder profile picture this.isSignedIn = false; + + this.dispatchUserStateChanged(); } async fetchProfileMetadata(pubkey: string) { - const relay = await Relay.connect('wss://notes.miguelalmodo.com'); - console.log(`connected to ${relay.url}`); + try { + const relay = await Relay.connect('wss://notes.miguelalmodo.com'); + const sub = relay.subscribe( + [{ kinds: [0], authors: [pubkey] }], + { + onevent: (event) => { + const profileData = JSON.parse(event.content); + this.profilePic = profileData.picture || '/assets/img/default_pfp.png'; + this.nostrAddy = profileData.nip05 || 'no addy available'; + this.bio = profileData.about || 'bio not available'; - // subscribe to my profile metadata from the relay - const sub = relay.subscribe([ - { - kinds: [0], // profile metadata - authors: [pubkey], //will insert the pubkey of the user who signed in - } - ], { - onevent: (event) => { - const profileData = JSON.parse(event.content); - this.profilePic = profileData.picture || '/assets/img/default_pfp.png'; - this.nostrAddy = profileData.nip05 || 'no addy available'; - this.bio = profileData.about || 'bio not available'; - - this.dispatchEvent(new CustomEvent('profile-updated' , { - detail: { nostrAddy: this.nostrAddy, bio: this.bio, profilePic: this.profilePic }, - bubbles: true, - composed: true - })); - console.log('Profile updated event dispatched:', { nostrAddy: this.nostrAddy, bio: this.bio, profilePic: this.profilePic }); - - }, - oneose: () => { - sub.close(); - } - }); - } catch (error: any) { + this.dispatchUserStateChanged(); + }, + oneose: () => sub.close(), + } + ); + } catch (error) { console.error('Failed to fetch profile metadata:', error); - this.profilePic = '/assets/img/default_pfp.png'; - this.nostrAddy = 'failed to fetch address'; - this.bio = 'failed to fetch profile' + this.displayGuestView(); + } } async signInWithNostr() { @@ -140,20 +134,27 @@ export class AppHeader extends LitElement { } } + /* this sign out method clears the public key from local storage and sets the profile view + back to guest view */ signOut() { - // clear pubkey from localStorage and reset to guest view localStorage.removeItem('pubkey'); this.displayGuestView(); + } - this.isSignedIn = false; - - console.log('signed out') - - this.dispatchEvent( new CustomEvent('user-signed-out', { - bubbles: true, - composed: true - })); - } + dispatchUserStateChanged() { + this.dispatchEvent( + new CustomEvent('user-state-changed', { + detail: { + isSignedIn: this.isSignedIn, + nostrAddy: this.nostrAddy, + bio: this.bio, + profilePic: this.profilePic, + }, + bubbles: true, + composed: true, + }) + ); + } render() { return html` diff --git a/src/pages/app-home.ts b/src/pages/app-home.ts index 26a0bdf..20e5253 100644 --- a/src/pages/app-home.ts +++ b/src/pages/app-home.ts @@ -117,18 +117,18 @@ Profile Picture Container connectedCallback(): void { super.connectedCallback(); - window.addEventListener('profile-updated', this.updateProfileFromEvent.bind(this)); - this.addEventListener('user-signed-out', this.handlesSignOut); - const storedPubkey = localStorage.getItem('pubkey'); - if (storedPubkey) { - // manually fetch profile metadata on mount - this.nostrAddy = 'loading...'; - this.bio = 'loading profile...'; - this.profilePic = '/assets/img/default_pfp.png'; - } + + this.addEventListener('user-state-changed', (event: Event) => { + const detail = (event as CustomEvent).detail; + this.nostrAddy = detail.nostrAddy; + this.bio = detail.bio; + this.profilePic = detail.profilePic; + this.isSignedIn = detail.isSignedIn; + }); } + disconnectedCallback(): void { super.disconnectedCallback(); window.removeEventListener('profile-updated', this.updateProfileFromEvent.bind(this)); @@ -154,12 +154,36 @@ Profile Picture Container this.requestUpdate(); } + async fetchAndDisplayProfile(pubkey: string) { + this.nostrAddy = pubkey; + this.bio = 'Loading profile info...'; + this.profilePic = '/assets/img/loading_pfp.png'; + + try { + // Fetch profile metadata from a relay + const relay = await Relay.connect('wss://notes.miguelalmodo.com'); // Example URL + const sub = relay.subscribe([{ kinds: [0], authors: [pubkey] }], { + onevent: (event) => { + const profileData = JSON.parse(event.content); + this.nostrAddy = profileData.nip05 || 'No address available'; + this.bio = profileData.about || 'No bio available'; + this.profilePic = profileData.picture || '/assets/img/default_pfp.png'; + this.requestUpdate(); + }, + oneose: () => sub.close(), + }); + } catch (error) { + console.error('Error fetching profile:', error); + this.displayGuestView(); + } + } + displayGuestView() { // Set initial values for guest view this.nostrAddy = ''; this.bio = 'Welcome, guest! Please sign in with a browser extension to view your profile.'; - this.profilePic = ''; // Could be a placeholder image for guests + this.profilePic = '/assets/img/default_pfp.png'; // Could be a placeholder image for guests this.isSignedIn = false; } diff --git a/src/pages/note-wall.ts b/src/pages/note-wall.ts index 4845c3e..748cb1e 100644 --- a/src/pages/note-wall.ts +++ b/src/pages/note-wall.ts @@ -160,6 +160,7 @@ export class NoteWall extends LitElement { this.fetchNotes(), ]); + console.log('initial notes:', this.notes); } @@ -192,17 +193,12 @@ async fetchNotes() { } ], { onevent: async (event) => { - /* // Translate the note content to the user's language - const userLang = this.getUserLang(); - // const translatedContent = await this.translateText(event.content, userLang); - - this.notes.push({ - content: translatedContent, + console.log('Event received:', event); + this.notes = [...this.notes, { + content: event.content, date: new Date(event.created_at * 1000).toLocaleDateString(), - }); */ - + }]; this.requestUpdate(); - console.log(event); }, oneose: () => { sub.close(); @@ -243,20 +239,24 @@ async fetchNotes() { ${this.notes.map(note => { - // Extract URL from note content + // extract URL from note content const urlMatch = note.content.match(/https?:\/\/[^\s]+/); const textContent = note.content.replace(urlMatch?.[0] || '', '').trim(); - // Check for YouTube links and extract video ID + // Check for yt links and extract video ID const youtubeRegex = /(?:https?:\/\/(?:www\.)?youtube\.com\/watch\?v=|https?:\/\/youtu\.be\/)([a-zA-Z0-9_-]{11})/; const youtubeMatch = urlMatch?.[0].match(youtubeRegex); const youtubeVideoId = youtubeMatch ? youtubeMatch[1] : null; - // Construct YouTube thumbnail URL if applicable + // build yt thumbnail URL if applicable const thumbnailUrl = youtubeVideoId ? `https://img.youtube.com/vi/${youtubeVideoId}/maxresdefault.jpg` : null; + //check for image links + const imgRegex = /(https?:\/\/[^\s]+\.(?:jpg|jpeg|png|gif))/i; + const imgMatch = urlMatch?.find(url => imgRegex.test(url)); + return html` @@ -264,8 +264,8 @@ async fetchNotes() {

${textContent}

${thumbnailUrl ? html`YouTube thumbnail` - : urlMatch - ? html`Note image` + : imgMatch + ? html`Note image` : ''}

${note.date}