forked from nostr/nostrweb
profiles: improve profile view
- add about to profile header - set document title to profile name - refactor how profile metadata
parent
3a1bf6f56f
commit
cd7dfa3f19
|
@ -1,7 +1,7 @@
|
||||||
import { elem } from './utils/dom';
|
import { elem } from './utils/dom';
|
||||||
import { getNoxyUrl } from './utils/url';
|
import { getNoxyUrl } from './utils/url';
|
||||||
|
|
||||||
export const parseContent = (content: string): unknown => {
|
export const parseJSON = (content: string): unknown => {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(content);
|
return JSON.parse(content);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
|
|
216
src/profiles.ts
216
src/profiles.ts
|
@ -1,59 +1,75 @@
|
||||||
import {Event} from 'nostr-tools';
|
import {Event} from 'nostr-tools';
|
||||||
import {elem, elemCanvas} from './utils/dom';
|
import {elem, elemCanvas, parseTextContent} from './utils/dom';
|
||||||
import {getHost, getNoxyUrl} from './utils/url';
|
import {getNoxyUrl} from './utils/url';
|
||||||
import {getViewContent, getViewElem} from './view';
|
import {getViewContent, getViewElem} from './view';
|
||||||
// import {validatePow} from './events';
|
import {parseJSON} from './media';
|
||||||
import {parseContent} from './media';
|
import {sortByCreatedAt} from './events';
|
||||||
|
|
||||||
type Metadata = {
|
|
||||||
name?: string;
|
|
||||||
about?: string;
|
|
||||||
picture?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Profile = {
|
type Profile = {
|
||||||
metadata: {
|
name: string;
|
||||||
[relay: string]: Metadata;
|
about?: string;
|
||||||
};
|
|
||||||
name?: string;
|
|
||||||
picture?: string;
|
picture?: string;
|
||||||
pubkey: string;
|
website?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const transformMetadata = (data: unknown): Profile | undefined => {
|
||||||
|
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
||||||
|
console.warn('expected nip-01 JSON object with user info, but got something funny', data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const hasNameString = 'name' in data && typeof data.name === 'string';
|
||||||
|
const hasAboutString = 'about' in data && typeof data.about === 'string';
|
||||||
|
const hasPictureString = 'picture' in data && typeof data.picture === 'string';
|
||||||
|
// custom
|
||||||
|
const hasDisplayName = 'display_name' in data && typeof data.display_name === 'string';
|
||||||
|
const hasWebsite = 'website' in data && typeof data.website === 'string';
|
||||||
|
|
||||||
|
if (!hasNameString && !hasAboutString && !hasPictureString && !hasDisplayName) {
|
||||||
|
console.warn('expected basic nip-01 user info (name, about, picture) but nothing found', data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const name = (
|
||||||
|
hasNameString && data.name as string
|
||||||
|
|| hasDisplayName && data.display_name as string
|
||||||
|
|| ''
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
...(hasAboutString && {about: data.about as string}),
|
||||||
|
...(hasPictureString && {picture: data.picture as string}),
|
||||||
|
...(hasWebsite && {hasWebsite: data.website as string})
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const userList: Array<Profile> = [];
|
const profileMap: {
|
||||||
// const tempContactList = {};
|
[pubkey: string]: Profile
|
||||||
|
} = {};
|
||||||
|
|
||||||
const setMetadata = (
|
const metadataList: Array<Event> = [];
|
||||||
evt: Event,
|
|
||||||
relay: string,
|
export const handleMetadata = (evt: Event, relay: string) => {
|
||||||
metadata: Metadata,
|
if (metadataList.find(({id}) => id === evt.id)) {
|
||||||
) => {
|
return;
|
||||||
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,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
const contactEventList = metadataList.filter(({pubkey}) => pubkey === evt.pubkey);
|
||||||
|
metadataList.push(evt);
|
||||||
|
contactEventList.push(evt);
|
||||||
|
contactEventList.sort(sortByCreatedAt);
|
||||||
|
|
||||||
// store the first seen name (for now) as main user.name
|
if (contactEventList.some(({created_at}) => created_at > evt.created_at) ) {
|
||||||
if (!user.name && metadata.name) {
|
// evt is older
|
||||||
user.name = metadata.name;
|
return;
|
||||||
}
|
}
|
||||||
|
const content = parseJSON(evt.content);
|
||||||
|
const metadata = transformMetadata(content);
|
||||||
|
if (!metadata) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
profileMap[evt.pubkey] = metadata;
|
||||||
|
|
||||||
// use the first seen profile pic (for now), pics from different relays are not supported yet
|
if (metadata.picture) {
|
||||||
if (!user.picture && metadata.picture) {
|
|
||||||
const imgUrl = getNoxyUrl('data', metadata.picture, evt.id, relay);
|
const imgUrl = getNoxyUrl('data', metadata.picture, evt.id, relay);
|
||||||
if (imgUrl) {
|
if (imgUrl) {
|
||||||
user.picture = imgUrl.href;
|
|
||||||
|
|
||||||
// update profile images that used some nip-13 work
|
// update profile images that used some nip-13 work
|
||||||
// if (imgUrl.href && validatePow(evt)) {
|
// if (imgUrl.href && validatePow(evt)) {
|
||||||
// document.body
|
// document.body
|
||||||
|
@ -62,118 +78,52 @@ const setMetadata = (
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (metadata.name) {
|
||||||
// update profile names
|
// update profile names
|
||||||
const name = user.metadata[relay].name || user.name || '';
|
|
||||||
if (name) {
|
|
||||||
document.body
|
document.body
|
||||||
// TODO: this should not depend on specific DOM structure, move pubkey info on username element
|
.querySelectorAll(`[data-profile="${evt.pubkey}"]`)
|
||||||
.querySelectorAll(`[data-pubkey="${evt.pubkey}"] > .mbox-body > header .mbox-username:not(.mbox-kind0-name)`)
|
|
||||||
.forEach((username: HTMLElement) => {
|
.forEach((username: HTMLElement) => {
|
||||||
username.textContent = name;
|
username.textContent = metadata.name;
|
||||||
username.classList.add('mbox-kind0-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) => {
|
export const getMetadata = (pubkey: string) => {
|
||||||
const content = parseContent(evt.content);
|
const user = profileMap[pubkey];
|
||||||
if (!content || typeof content !== 'object' || Array.isArray(content)) {
|
const name = user?.name;
|
||||||
console.warn('expected nip-01 JSON object with user info, but got something funny', evt);
|
const userName = name || pubkey.slice(0, 8);
|
||||||
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 getProfile = (pubkey: string) => userList.find(user => user.pubkey === pubkey);
|
|
||||||
|
|
||||||
export const getMetadata = (evt: Event, relay: string) => {
|
|
||||||
const host = getHost(relay);
|
|
||||||
const user = getProfile(evt.pubkey);
|
|
||||||
// const userImg = user?.picture;
|
// 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', {
|
const img = /* (userImg && validatePow(evt)) ? elem('img', {
|
||||||
alt: `${userName} ${host}`,
|
alt: `${userName} ${host}`,
|
||||||
loading: 'lazy',
|
loading: 'lazy',
|
||||||
src: userImg,
|
src: userImg,
|
||||||
title: `${userName} on ${host} ${userAbout}`,
|
title: `${userName} on ${host} ${userAbout}`,
|
||||||
}) : */ elemCanvas(evt.pubkey);
|
}) : */ elemCanvas(pubkey);
|
||||||
const time = new Date(evt.created_at * 1000);
|
return {img, name, userName};
|
||||||
return {host, img, name, time, userName};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* export function handleContactList(evt, relay) {
|
export const renderProfile = (pubkey: string) => {
|
||||||
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}});
|
|
||||||
// }
|
|
||||||
|
|
||||||
export const renderProfile = (id: string) => {
|
|
||||||
const content = getViewContent();
|
const content = getViewContent();
|
||||||
const header = getViewElem(id);
|
const header = getViewElem(pubkey);
|
||||||
if (!content || !header) {
|
const metadata = profileMap[pubkey];
|
||||||
|
if (!content || !header || !metadata) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const profile = getProfile(id);
|
if (metadata.name) {
|
||||||
if (profile && profile.name) {
|
|
||||||
const h1 = header.querySelector('h1');
|
const h1 = header.querySelector('h1');
|
||||||
if (h1) {
|
if (h1) {
|
||||||
h1.textContent = profile.name;
|
h1.textContent = metadata.name;
|
||||||
|
document.title = metadata.name;
|
||||||
} else {
|
} else {
|
||||||
header.append(elem('h1', {}, profile.name));
|
header.append(elem('h1', {}, metadata.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (metadata.about) {
|
||||||
|
const detail = getViewElem(`detail-${pubkey}`);
|
||||||
|
if (!detail.children.length) {
|
||||||
|
const [content] = parseTextContent(metadata.about);
|
||||||
|
detail?.append(...content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -228,7 +228,7 @@ profileForm.addEventListener('submit', async (e) => {
|
||||||
return console.error(error, relay);
|
return console.error(error, relay);
|
||||||
}
|
}
|
||||||
console.info(`publish request sent to ${relay}`);
|
console.info(`publish request sent to ${relay}`);
|
||||||
profileStatus.textContent = 'profile metadata successfully published';
|
profileStatus.textContent = 'profile successfully published';
|
||||||
profileStatus.hidden = false;
|
profileStatus.hidden = false;
|
||||||
profileSubmit.disabled = true;
|
profileSubmit.disabled = true;
|
||||||
});
|
});
|
||||||
|
|
|
@ -140,6 +140,10 @@ nav a {
|
||||||
padding-left: var(--extra-space);
|
padding-left: var(--extra-space);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hero p {
|
||||||
|
padding-left: var(--extra-space);
|
||||||
|
}
|
||||||
|
|
||||||
.hero small {
|
.hero small {
|
||||||
color: var(--color-accent);
|
color: var(--color-accent);
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
|
@ -150,9 +154,13 @@ nav a {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
@media (min-width: 64ch) {
|
@media (min-width: 54ch) {
|
||||||
.hero small {
|
.hero small {
|
||||||
padding-left: var(--extra-space);
|
padding-left: var(--extra-space);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hero footer {
|
||||||
|
padding-left: var(--extra-space);
|
||||||
|
}
|
||||||
|
|
27
src/ui.ts
27
src/ui.ts
|
@ -9,6 +9,7 @@ import {openWriteInput} from './write';
|
||||||
// import {linkPreview} from './media';
|
// import {linkPreview} from './media';
|
||||||
import {getMetadata} from './profiles';
|
import {getMetadata} from './profiles';
|
||||||
import {EventWithNip19, replyList} from './notes';
|
import {EventWithNip19, replyList} from './notes';
|
||||||
|
import {parseJSON} from './media';
|
||||||
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
document.querySelectorAll('time[datetime]').forEach((timeElem: HTMLTimeElement) => {
|
document.querySelectorAll('time[datetime]').forEach((timeElem: HTMLTimeElement) => {
|
||||||
|
@ -20,7 +21,7 @@ export const createTextNote = (
|
||||||
evt: EventWithNip19,
|
evt: EventWithNip19,
|
||||||
relay: string,
|
relay: string,
|
||||||
) => {
|
) => {
|
||||||
const {host, img, name, time, userName} = getMetadata(evt, relay);
|
const {img, name, userName} = getMetadata(evt.pubkey);
|
||||||
const replies = replyList.filter(({replyTo}) => replyTo === evt.id)
|
const replies = replyList.filter(({replyTo}) => replyTo === evt.id)
|
||||||
.sort(sortByCreatedAt)
|
.sort(sortByCreatedAt)
|
||||||
.reverse();
|
.reverse();
|
||||||
|
@ -29,6 +30,7 @@ export const createTextNote = (
|
||||||
const reactions = getReactions(evt.id);
|
const reactions = getReactions(evt.id);
|
||||||
const didReact = reactions.length && !!reactions.find(reaction => reaction.pubkey === config.pubkey);
|
const didReact = reactions.length && !!reactions.find(reaction => reaction.pubkey === config.pubkey);
|
||||||
const [content, {firstLink}] = parseTextContent(evt.content);
|
const [content, {firstLink}] = parseTextContent(evt.content);
|
||||||
|
const time = new Date(evt.created_at * 1000);
|
||||||
const buttons = elem('div', {className: 'buttons'}, [
|
const buttons = elem('div', {className: 'buttons'}, [
|
||||||
elem('button', {name: 'reply', type: 'button'}, [
|
elem('button', {name: 'reply', type: 'button'}, [
|
||||||
elem('img', {height: 24, width: 24, src: '/assets/comment.svg'})
|
elem('img', {height: 24, width: 24, src: '/assets/comment.svg'})
|
||||||
|
@ -48,15 +50,19 @@ export const createTextNote = (
|
||||||
}
|
}
|
||||||
const replyFeed: Array<HTMLElement> = replies[0] ? replies.map(e => setViewElem(e.id, createTextNote(e, relay))) : [];
|
const replyFeed: Array<HTMLElement> = replies[0] ? replies.map(e => setViewElem(e.id, createTextNote(e, relay))) : [];
|
||||||
return elemArticle([
|
return elemArticle([
|
||||||
elem('a', {className: 'mbox-img', href: `/${evt.nip19.npub}`}, img),
|
elem('a', {className: 'mbox-img', href: `/${evt.nip19.npub}`, tabIndex: -1}, img),
|
||||||
elem('div', {className: 'mbox-body'}, [
|
elem('div', {className: 'mbox-body'}, [
|
||||||
elem('header', {
|
elem('header', {
|
||||||
className: 'mbox-header',
|
className: 'mbox-header',
|
||||||
title: `User: ${userName}\n${time}\n\nUser pubkey: ${evt.pubkey}\n\nRelay: ${host}\n\nEvent-id: ${evt.id}
|
title: `User: ${userName}\n${time}\n\nUser pubkey: ${evt.pubkey}\n\nRelay: ${relay}\n\nEvent-id: ${evt.id}
|
||||||
${evt.tags.length ? `\nTags ${JSON.stringify(evt.tags)}\n` : ''}
|
${evt.tags.length ? `\nTags ${JSON.stringify(evt.tags)}\n` : ''}
|
||||||
${evt.content}`
|
${evt.content}`
|
||||||
}, [
|
}, [
|
||||||
elem('a', {className: `mbox-username${name ? ' mbox-kind0-name' : ''}`, href: `/${evt.nip19.npub}`}, name || userName),
|
elem('a', {
|
||||||
|
className: `mbox-username${name ? ' mbox-kind0-name' : ''}`,
|
||||||
|
data: {profile: evt.pubkey},
|
||||||
|
href: `/${evt.nip19.npub}`,
|
||||||
|
}, name || userName),
|
||||||
' ',
|
' ',
|
||||||
elem('a', {href: `/${evt.nip19.note}`}, elem('time', {dateTime: time.toISOString()}, formatTime(time))),
|
elem('a', {href: `/${evt.nip19.note}`}, elem('time', {dateTime: time.toISOString()}, formatTime(time))),
|
||||||
]),
|
]),
|
||||||
|
@ -67,11 +73,15 @@ export const createTextNote = (
|
||||||
buttons,
|
buttons,
|
||||||
]),
|
]),
|
||||||
...(replies[0] ? [elem('div', {className: 'mbox-replies'}, replyFeed)] : []),
|
...(replies[0] ? [elem('div', {className: 'mbox-replies'}, replyFeed)] : []),
|
||||||
], {className: replies.length ? 'mbox-has-replies' : '', data: {id: evt.id, pubkey: evt.pubkey, relay}});
|
], {
|
||||||
|
className: replies.length ? 'mbox-has-replies' : '',
|
||||||
|
data: {kind: evt.kind, id: evt.id, pubkey: evt.pubkey, relay}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const renderRecommendServer = (evt: Event, relay: string) => {
|
export const renderRecommendServer = (evt: Event, relay: string) => {
|
||||||
const {img, name, time, userName} = getMetadata(evt, relay);
|
const {img, userName} = getMetadata(evt.pubkey);
|
||||||
|
const time = new Date(evt.created_at * 1000);
|
||||||
const body = elem('div', {className: 'mbox-body', title: dateTime.format(time)}, [
|
const body = elem('div', {className: 'mbox-body', title: dateTime.format(time)}, [
|
||||||
elem('header', {className: 'mbox-header'}, [
|
elem('header', {className: 'mbox-header'}, [
|
||||||
elem('small', {}, [
|
elem('small', {}, [
|
||||||
|
@ -82,5 +92,8 @@ export const renderRecommendServer = (evt: Event, relay: string) => {
|
||||||
]);
|
]);
|
||||||
return elemArticle([
|
return elemArticle([
|
||||||
elem('div', {className: 'mbox-img'}, [img]), body
|
elem('div', {className: 'mbox-img'}, [img]), body
|
||||||
], {className: 'mbox-recommend-server', data: {id: evt.id, pubkey: evt.pubkey}});
|
], {
|
||||||
|
className: 'mbox-recommend-server',
|
||||||
|
data: {kind: evt.kind, id: evt.id, pubkey: evt.pubkey}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
31
src/view.ts
31
src/view.ts
|
@ -1,3 +1,4 @@
|
||||||
|
import {nip19} from 'nostr-tools';
|
||||||
import {elem} from './utils/dom';
|
import {elem} from './utils/dom';
|
||||||
|
|
||||||
type ViewOptions = {
|
type ViewOptions = {
|
||||||
|
@ -49,19 +50,25 @@ export const setViewElem = (id: string, node: HTMLElement) => {
|
||||||
|
|
||||||
const mainContainer = document.querySelector('main') as HTMLElement;
|
const mainContainer = document.querySelector('main') as HTMLElement;
|
||||||
|
|
||||||
const createContainer = (
|
const renderView = (options: ViewOptions) => {
|
||||||
route: string,
|
|
||||||
options: ViewOptions,
|
|
||||||
) => {
|
|
||||||
const content = elem('div', {className: 'content'});
|
const content = elem('div', {className: 'content'});
|
||||||
const dom: DOMMap = {};
|
const dom: DOMMap = {};
|
||||||
switch (options.type) {
|
switch (options.type) {
|
||||||
case 'profile':
|
case 'profile':
|
||||||
const header = elem('header', {className: 'hero'},
|
const pubkey = options.id;
|
||||||
elem('small', {}, route)
|
const detail = elem('p', {data: {'profileDetails': pubkey}});
|
||||||
);
|
dom[`detail-${pubkey}`] = detail;
|
||||||
dom[options.id] = header;
|
const header = elem('header', {className: 'hero'}, [
|
||||||
|
elem('small', {}, nip19.npubEncode(pubkey)),
|
||||||
|
elem('h1', {}, pubkey),
|
||||||
|
detail,
|
||||||
|
elem('footer', {}, [
|
||||||
|
elem('span', {data:{following: pubkey}})
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
dom[pubkey] = header;
|
||||||
content.append(header);
|
content.append(header);
|
||||||
|
document.title = pubkey;
|
||||||
break;
|
break;
|
||||||
case 'note':
|
case 'note':
|
||||||
break;
|
break;
|
||||||
|
@ -69,6 +76,14 @@ const createContainer = (
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const view = elem('section', {className: 'view'}, [content]);
|
const view = elem('section', {className: 'view'}, [content]);
|
||||||
|
return {content, dom, view};
|
||||||
|
};
|
||||||
|
|
||||||
|
const createContainer = (
|
||||||
|
route: string,
|
||||||
|
options: ViewOptions,
|
||||||
|
) => {
|
||||||
|
const {content, dom, view} = renderView(options);
|
||||||
const container = {id: route, options, view, content, dom};
|
const container = {id: route, options, view, content, dom};
|
||||||
mainContainer.append(view);
|
mainContainer.append(view);
|
||||||
containers.push(container);
|
containers.push(container);
|
||||||
|
|
Loading…
Reference in New Issue