import {Event} from 'nostr-tools'; import {elem, elemCanvas} from './utils/dom'; import {getHost, getNoxyUrl} from './utils/url'; import {validatePow} from './events'; import {parseContent} from './media'; type Metadata = { name?: string; about?: string; picture?: string; }; type Profile = { metadata: { [relay: string]: Metadata; }; name?: string; picture?: string; pubkey: string; }; const userList: Array = []; // const tempContactList = {}; const setMetadata = ( evt: Event, relay: string, metadata: Metadata, ) => { let user = userList.find(u => u.pubkey === evt.pubkey); if (!user) { user = { metadata: {[relay]: metadata}, pubkey: evt.pubkey, }; userList.push(user); } else { user.metadata[relay] = { ...user.metadata[relay], // timestamp: evt.created_at, ...metadata, }; } // store the first seen name (for now) as main user.name if (!user.name && metadata.name) { user.name = metadata.name; } // use the first seen profile pic (for now), pics from different relays are not supported yet if (!user.picture && metadata.picture) { const imgUrl = getNoxyUrl('data', metadata.picture, evt.id, relay); if (imgUrl) { user.picture = imgUrl.href; // update profile images that used some nip-13 work if (imgUrl.href && validatePow(evt)) { document.body .querySelectorAll(`canvas[data-pubkey="${evt.pubkey}"]`) .forEach(canvas => canvas.parentNode?.replaceChild(elem('img', {src: imgUrl.href}), canvas)); } } } // update profile names const name = user.metadata[relay].name || user.name || ''; if (name) { document.body .querySelectorAll(`[data-pubkey="${evt.pubkey}"] .mbox-username:not(.mbox-kind0-name)`) .forEach((username: HTMLElement) => { username.textContent = name; username.classList.add('mbox-kind0-name'); }); } // if (tempContactList[relay]) { // const updates = tempContactList[relay].filter(update => update.pubkey === evt.pubkey); // if (updates) { // console.log('TODO: add contact list (kind 3)', updates); // } // } }; export const handleMetadata = (evt: Event, relay: string) => { const content = parseContent(evt.content); if (!content || typeof content !== 'object' || Array.isArray(content)) { console.warn('expected nip-01 JSON object with user info, but got something funny', evt); return; } const hasNameString = 'name' in content && typeof content.name === 'string'; const hasAboutString = 'about' in content && typeof content.about === 'string'; const hasPictureString = 'picture' in content && typeof content.picture === 'string'; // custom const hasDisplayName = 'display_name' in content && typeof content.display_name === 'string'; if (!hasNameString && !hasAboutString && !hasPictureString && !hasDisplayName) { console.warn('expected basic nip-01 user info (name, about, picture) but nothing found', evt); return; } const metadata: Metadata = { ...(hasNameString && {name: content.name as string} || hasDisplayName && {name: content.display_name as string}), ...(hasAboutString && {about: content.about as string}), ...(hasPictureString && {picture: content.picture as string}), }; setMetadata(evt, relay, metadata); }; export const getMetadata = (evt: Event, relay: string) => { const host = getHost(relay); const user = userList.find(user => user.pubkey === evt.pubkey); const userImg = user?.picture; const name = user?.metadata[relay]?.name || user?.name; const userName = name || evt.pubkey.slice(0, 8); const userAbout = user?.metadata[relay]?.about || ''; const img = (userImg && validatePow(evt)) ? elem('img', { alt: `${userName} ${host}`, loading: 'lazy', src: userImg, title: `${userName} on ${host} ${userAbout}`, }) : elemCanvas(evt.pubkey); const time = new Date(evt.created_at * 1000); return {host, img, name, time, userName}; }; /* export function handleContactList(evt, relay) { if (getViewElem(evt.id)) { return; } const art = renderUpdateContact(evt, relay); if (textNoteList.length < 2) { getViewContent().append(art); return; } const closestTextNotes = textNoteList.sort(sortEventCreatedAt(evt.created_at)); getViewElem(closestTextNotes[0].id).after(art); setViewElem(evt.id, art); // const user = userList.find(u => u.pupkey === evt.pubkey); // if (user) { // console.log(`TODO: add contact list for ${evt.pubkey.slice(0, 8)} on ${relay}`, evt.tags); // } else { // tempContactList[relay] = tempContactList[relay] // ? [...tempContactList[relay], evt] // : [evt]; // } } */ // function renderUpdateContact(evt, relay) { // const {img, time, userName} = getMetadata(evt, relay); // const body = elem('div', {className: 'mbox-body', title: dateTime.format(time)}, [ // elem('header', {className: 'mbox-header'}, [ // elem('small', {}, []), // ]), // elem('pre', {title: JSON.stringify(evt.content)}, [ // elem('strong', {}, userName), // ' updated contacts: ', // JSON.stringify(evt.tags), // ]), // ]); // return renderArticle([img, body], {className: 'mbox-updated-contact', data: {id: evt.id, pubkey: evt.pubkey, relay}}); // }