centralized state in the header component
This commit is contained in:
parent
9fd3d47d25
commit
2dfbc08788
3 changed files with 98 additions and 73 deletions
|
@ -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();
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
try {
|
||||
const relay = await Relay.connect('wss://notes.miguelalmodo.com');
|
||||
console.log(`connected to ${relay.url}`);
|
||||
|
||||
// subscribe to my profile metadata from the relay
|
||||
const sub = relay.subscribe([
|
||||
const sub = relay.subscribe(
|
||||
[{ kinds: [0], authors: [pubkey] }],
|
||||
{
|
||||
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 });
|
||||
|
||||
this.dispatchUserStateChanged();
|
||||
},
|
||||
oneose: () => {
|
||||
sub.close();
|
||||
oneose: () => sub.close(),
|
||||
}
|
||||
});
|
||||
} catch (error: any) {
|
||||
);
|
||||
} 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,19 +134,26 @@ 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', {
|
||||
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
|
||||
}));
|
||||
composed: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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%;">`
|
||||
: ''}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue