Basic longform render

This commit is contained in:
Bojan Mojsilovic 2024-05-09 14:28:38 +02:00
parent 4c7de1e307
commit 6a2ce12501
6 changed files with 1494 additions and 5 deletions

1196
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -38,6 +38,7 @@
"qr-code-styling": "^1.6.0-rc.1",
"sass": "1.67.0",
"solid-js": "1.7.11",
"solid-markdown": "^2.0.1",
"solid-transition-group": "0.2.3"
}
}

View File

@ -43,6 +43,7 @@ const Moderation = lazy(() => import('./pages/Settings/Moderation'));
const Menu = lazy(() => import('./pages/Settings/Menu'));
const Landing = lazy(() => import('./pages/Landing'));
const AppDownloadQr = lazy(() => import('./pages/appDownloadQr'));
const Longform = lazy(() => import('./pages/Longform'));
const Terms = lazy(() => import('./pages/Terms'));
const Privacy = lazy(() => import('./pages/Privacy'));
@ -111,6 +112,7 @@ const Router: Component = () => {
<Route path="/home" component={Home} />
<Route path="/thread/:postId" component={Thread} />
<Route path="/e/:postId" component={Thread} />
<Route path="/l/:naddr" component={Longform} />
<Route path="/explore/:scope?/:timeframe?" component={Explore} />
<Route path="/messages/:sender?" component={Messages} />
<Route path="/notifications" component={Notifications} />

View File

@ -0,0 +1,84 @@
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px;
border-bottom: 1px solid var(--devider);
.author {
display: flex;
justify-content: flex-start;
align-items: center;
gap: 6px;
.userName {
color: var(--text-primary);
font-size: 14px;
font-weight: 700;
}
}
.time {
color: var(--text-tertiary);
font-size: 14px;
font-weight: 700;
}
}
.longform {
display: flex;
flex-direction: column;
gap: 20px;
position: relative;
margin-block: 48px;
margin-inline: 20px;
.title {
color: var(--text-primary);
font-size: 28px;
font-weight: 700;
}
.summary {
color: var(--text-secondary);
font-size: 18px;
font-weight: 700;
}
.image {
width: 100%;
object-fit: contain;
border-radius: 12px;
overflow: hidden;
}
.content {
* {
color: var(--text-primary);
}
p, li {
font-size: 18px;
font-weight: 500;
}
a {
color: var(--accent-links);
}
}
.tags {
width: 100%;
.tag {
display: inline-block;
color: var(--text-secondary);
font-size: 12px;
font-weight: 300;
background-color: var(--background-input);
padding: 2px 8px;
border-radius: 8px;
width: fit-content;
margin: 4px;
}
}
}

207
src/pages/Longform.tsx Normal file
View File

@ -0,0 +1,207 @@
import { useIntl } from "@cookbook/solid-intl";
import { useParams } from "@solidjs/router";
import { Component, createEffect, createSignal, For, Show } from "solid-js";
import { createStore } from "solid-js/store";
import { APP_ID } from "../App";
import { Kind } from "../constants";
import { useAccountContext } from "../contexts/AccountContext";
import { decodeIdentifier, hexToNpub } from "../lib/keys";
import { getParametrizedEvent } from "../lib/notes";
import { subscribeTo } from "../sockets";
import { SolidMarkdown } from "solid-markdown";
import styles from './Longform.module.scss';
import Loader from "../components/Loader/Loader";
import { NostrUserContent, PrimalUser } from "../types/primal";
import { getUserProfileInfo } from "../lib/profile";
import { convertToUser, userName } from "../stores/profile";
import Avatar from "../components/Avatar/Avatar";
import { date, longDate, shortDate, veryLongDate } from "../lib/dates";
export type LongFormData = {
title: string,
summary: string,
image: string,
tags: string[],
published: number,
content: string,
author: string,
};
const emptyLongNote = {
title: '',
summary: '',
image: '',
tags: [],
published: 0,
content: '',
author: '',
}
const Longform: Component = () => {
const account = useAccountContext();
const params = useParams();
const intl = useIntl();
const [note, setNote] = createStore<LongFormData>({...emptyLongNote});
const [pubkey, setPubkey] = createSignal<string>('');
// @ts-ignore
const [author, setAuthor] = createStore<PrimalUser>()
createEffect(() => {
if (!pubkey()) {
return;
}
const naddr = params.naddr;
const subId = `author_${naddr}_${APP_ID}`;
const unsub = subscribeTo(subId, (type, subId, content) =>{
if (type === 'EOSE') {
unsub();
return;
}
if (type === 'EVENT') {
if (!content) {
return;
}
if(content.kind === Kind.Metadata) {
const userContent = content as NostrUserContent;
const user = convertToUser(userContent);
setAuthor(() => ({ ...user }));
}
}
})
getUserProfileInfo(pubkey(), account?.publicKey, subId);
});
createEffect(() => {
const naddr = params.naddr;
if (typeof naddr === 'string' && naddr.startsWith('naddr')) {
const decoded = decodeIdentifier(naddr);
const { pubkey, identifier, kind } = decoded.data;
const subId = `naddr_${naddr}_${APP_ID}`;
const unsub = subscribeTo(subId, (type, subId, content) =>{
if (type === 'EOSE') {
unsub();
return;
}
if (type === 'EVENT') {
if (!content) {
return;
}
if(content.kind === Kind.LongForm) {
setPubkey(() => content.pubkey);
let n: LongFormData = {
title: '',
summary: '',
image: '',
tags: [],
published: content.created_at || 0,
content: content.content,
author: content.pubkey,
}
content.tags.forEach(tag => {
switch (tag[0]) {
case 't':
n.tags.push(tag[1]);
break;
case 'title':
n.title = tag[1];
break;
case 'summary':
n.summary = tag[1];
break;
case 'image':
n.image = tag[1];
break;
case 'published':
n.published = parseInt(tag[1]);
break;
case 'content':
n.content = tag[1];
break;
case 'author':
n.author = tag[1];
break;
default:
break;
}
});
setNote(() => ({...n}));
}
}
});
getParametrizedEvent(pubkey, identifier, kind, subId);
}
})
return (
<>
<div class={styles.header}>
<div class={styles.author}>
<Show when={author}>
<Avatar user={author} size="xs" />
<div class={styles.userName}>
{userName(author)}
</div>
</Show>
</div>
<div class={styles.time}>
{shortDate(note.published)}
</div>
</div>
<div class={styles.longform}>
<Show
when={note.content.length > 0}
fallback={<Loader />}
>
<div class={styles.title}>
{note.title}
</div>
<div class={styles.summary}>
{note.summary}
</div>
<div class={styles.tags}>
<For each={note.tags}>
{tag => (
<div class={styles.tag}>
{tag}
</div>
)}
</For>
</div>
<img class={styles.image} src={note.image} />
<div class={styles.content}>
<SolidMarkdown
children={note.content || ''}
/>
</div>
</Show>
</div>
</>);
}
export default Longform;

View File

@ -68,10 +68,13 @@ export const userName = (user: PrimalUser | undefined) => {
if (!user) {
return '';
}
const npub = user.npub || hexToNpub(user.pubkey) || '';
const name = user.display_name ||
user.displayName ||
user.name ||
truncateNpub(user.npub);
truncateNpub(npub);
return name ?
name :
@ -82,10 +85,12 @@ export const authorName = (user: PrimalUser | undefined) => {
if (!user) {
return '';
}
const npub = user.npub || hexToNpub(user.pubkey) || '';
const name = user.display_name ||
user.displayName ||
user.name ||
truncateNpub(user.npub);
truncateNpub(npub);
return name ?
name :