centralized state in the header component

This commit is contained in:
miggymofongo 2024-12-07 16:53:16 -04:00
parent 9fd3d47d25
commit 2dfbc08788
3 changed files with 98 additions and 73 deletions

View file

@ -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`

View file

@ -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;
}

View file

@ -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() {
<table>
${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`
<tr>
<td><h3>${note.date}</h3></td>
@ -264,8 +264,8 @@ async fetchNotes() {
<p>${textContent}</p>
${thumbnailUrl
? html`<img src="${thumbnailUrl}" alt="YouTube thumbnail" style="max-width: 30%; height: 30%;">`
: urlMatch
? html`<img src="${urlMatch[0]}" alt="Note image" style="max-width: 30%; height: 30%;">`
: imgMatch
? html`<img src="${imgMatch}" alt="Note image" style="max-width: 30%; height: 30%;">`
: ''}