You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
182 lines
6.4 KiB
TypeScript
182 lines
6.4 KiB
TypeScript
import {Event, nip19} from 'nostr-tools';
|
|
import {Children, elem, elemArticle, parseTextContent} from './utils/dom';
|
|
import {dateTime, formatTime} from './utils/time';
|
|
import {/*validatePow,*/ sortByCreatedAt} from './events';
|
|
import {setViewElem} from './view';
|
|
import {config} from './settings';
|
|
import {getReactions, getReactionContents} from './reactions';
|
|
import {openWriteInput} from './write';
|
|
// import {linkPreview} from './media';
|
|
import {getMetadata} from './profiles';
|
|
import {EventWithNip19, replyList} from './notes';
|
|
import {parseJSON} from './media';
|
|
|
|
setInterval(() => {
|
|
document.querySelectorAll('time[datetime]').forEach((timeElem: HTMLTimeElement) => {
|
|
timeElem.textContent = formatTime(new Date(timeElem.dateTime));
|
|
});
|
|
}, 10000);
|
|
|
|
export const createTextNote = (
|
|
evt: EventWithNip19,
|
|
relay: string,
|
|
) => {
|
|
const {img, name, userName} = getMetadata(evt.pubkey);
|
|
const replies = replyList.filter(({replyTo}) => replyTo === evt.id)
|
|
.sort(sortByCreatedAt)
|
|
.reverse();
|
|
// const isLongContent = evt.content.trimRight().length > 280;
|
|
// const content = isLongContent ? evt.content.slice(0, 280) : evt.content;
|
|
const reactions = getReactions(evt.id);
|
|
const didReact = reactions.length && !!reactions.find(reaction => reaction.pubkey === config.pubkey);
|
|
const [content, {firstLink}] = parseTextContent(evt.content);
|
|
const time = new Date(evt.created_at * 1000);
|
|
const buttons = elem('div', {className: 'buttons'}, [
|
|
elem('button', {name: 'reply', type: 'button'}, [
|
|
elem('img', {height: 24, width: 24, src: '/assets/comment.svg'})
|
|
]),
|
|
elem('button', {name: 'star', type: 'button'}, [
|
|
elem('img', {
|
|
alt: didReact ? '✭' : '✩', // ♥
|
|
height: 24, width: 24,
|
|
src: `/assets/${didReact ? 'star-fill' : 'star'}.svg`,
|
|
title: getReactionContents(evt.id).join(' '),
|
|
}),
|
|
elem('small', {data: {reactions: ''}}, reactions.length || ''),
|
|
]),
|
|
]);
|
|
if (localStorage.getItem('reply_to') === evt.id) {
|
|
openWriteInput(buttons, evt.id);
|
|
}
|
|
const replyFeed: Array<HTMLElement> = replies[0] ? replies.map(e => setViewElem(e.id, createTextNote(e, relay))) : [];
|
|
return elemArticle([
|
|
elem('a', {className: 'mbox-img', href: `/${evt.nip19.npub}`, tabIndex: -1}, img),
|
|
elem('div', {className: 'mbox-body'}, [
|
|
elem('header', {
|
|
className: 'mbox-header',
|
|
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.content}`
|
|
}, [
|
|
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('div', {className: 'mbox-content'/* data: isLongContent ? {append: evt.content.slice(280)} : null*/}, content /*[
|
|
...content,
|
|
(firstLink && validatePow(evt)) ? linkPreview(firstLink, evt.id, relay) : null,
|
|
]*/),
|
|
buttons,
|
|
]),
|
|
...(replies[0] ? [elem('div', {className: 'mbox-replies'}, replyFeed)] : []),
|
|
], {
|
|
className: replies.length ? 'mbox-has-replies' : '',
|
|
data: {kind: evt.kind, id: evt.id, pubkey: evt.pubkey, relay}
|
|
});
|
|
};
|
|
|
|
type EventWithContent = Omit<Event, 'content'> & {
|
|
content: Children
|
|
}
|
|
|
|
export const renderUpdateContact = (
|
|
evt: EventWithContent,
|
|
relay: string,
|
|
) => {
|
|
const {img, name, userName} = getMetadata(evt.pubkey);
|
|
const time = new Date(evt.created_at * 1000);
|
|
return elemArticle([
|
|
elem('div', {className: 'mbox-img'}, img),
|
|
elem('div', {className: 'mbox-body'}, [
|
|
elem('header', {className: 'mbox-header'}, [
|
|
elem('span', {
|
|
className: `mbox-username${name ? ' mbox-kind0-name' : ''}`,
|
|
data: {profile: evt.pubkey},
|
|
}, name || userName),
|
|
' ',
|
|
elem('span', {data: {contacts: evt.id}, title: time.toISOString()}, evt.content),
|
|
]),
|
|
]),
|
|
], {
|
|
className: 'mbox-updated-contact',
|
|
data: {kind: evt.kind, id: evt.id, pubkey: evt.pubkey, relay}
|
|
}
|
|
);
|
|
};
|
|
|
|
export const renderRecommendServer = (evt: Event, relay: string) => {
|
|
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)}, [
|
|
elem('header', {className: 'mbox-header'}, [
|
|
elem('small', {}, [
|
|
elem('strong', {}, userName)
|
|
]),
|
|
]),
|
|
` recommends server: ${evt.content}`,
|
|
]);
|
|
return elemArticle([
|
|
elem('div', {className: 'mbox-img'}, [img]), body
|
|
], {
|
|
className: 'mbox-recommend-server',
|
|
data: {kind: evt.kind, id: evt.id, pubkey: evt.pubkey}
|
|
});
|
|
};
|
|
|
|
export const renderEventDetails = (evt: Event, relay: string) => {
|
|
const {img, name, userName} = getMetadata(evt.pubkey);
|
|
const npub = nip19.npubEncode(evt.pubkey);
|
|
|
|
let content = (![1, 7].includes(evt.kind) && evt.content !== '') ? parseJSON(evt.content) : (evt.content || '<empty>');
|
|
switch (typeof content) {
|
|
case 'object':
|
|
content = JSON.stringify(content, null, 2);
|
|
break;
|
|
default:
|
|
content = `${content}`;
|
|
}
|
|
const body = elem('div', {className: 'mbox-body'}, [
|
|
elem('header', {className: 'mbox-header'}, [
|
|
elem('a', {
|
|
className: `mbox-username${name ? ' mbox-kind0-name' : ''}`,
|
|
data: {profile: evt.pubkey},
|
|
href: `/${npub}`,
|
|
}, name || userName),
|
|
]),
|
|
elem('dl', {}, [
|
|
elem('dt', {}, 'npub'),
|
|
elem('dd', {}, npub),
|
|
elem('dt', {}, 'created at'),
|
|
elem('dd', {}, dateTime.format(evt.created_at * 1000)),
|
|
elem('dt', {}, 'relay'),
|
|
elem('dd', {}, relay),
|
|
]),
|
|
elem('h2', {}, 'Event'),
|
|
elem('dl', {}, [
|
|
elem('dt', {}, 'id'),
|
|
elem('dd', {}, evt.id),
|
|
elem('dt', {}, 'kind'),
|
|
elem('dd', {}, evt.kind),
|
|
elem('dt', {}, 'pubkey'),
|
|
elem('dd', {}, evt.pubkey),
|
|
elem('dt', {}, 'tags count'),
|
|
elem('dd', {}, evt.tags.length),
|
|
elem('dt', {}, 'tags'),
|
|
elem('dd', {}, JSON.stringify(evt.tags)),
|
|
elem('dt', {}, 'content'),
|
|
elem('dd', {}, elem('pre', {}, content as string)),
|
|
]),
|
|
]);
|
|
return elemArticle([
|
|
elem('a', {className: 'mbox-img', href: `/${npub}`, tabIndex: -1}, img),
|
|
body,
|
|
], {
|
|
className: 'mbox-plain-event',
|
|
data: {kind: evt.kind, id: evt.id, pubkey: evt.pubkey}
|
|
});
|
|
};
|