mirror of
https://github.com/PrimalHQ/primal-web-app.git
synced 2024-09-30 00:41:09 +00:00
Fix top reads
This commit is contained in:
parent
b1ad4299eb
commit
b4b51a242d
@ -1,4 +1,4 @@
|
||||
.article {
|
||||
.article, .articleShort {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -44,6 +44,10 @@
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 14px;
|
||||
|
||||
&::before {
|
||||
content: '• ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,3 +126,41 @@
|
||||
top: -6px;
|
||||
right: 8px;
|
||||
}
|
||||
|
||||
.articleShort {
|
||||
padding: 0;
|
||||
.header {
|
||||
.userInfo {
|
||||
.userName {
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
.body {
|
||||
.text {
|
||||
.content {
|
||||
.title {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.image {
|
||||
min-width: 100px;
|
||||
img {
|
||||
width: 100px;
|
||||
object-fit: scale-down;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
59
src/components/ArticlePreview/ArticleShort.tsx
Normal file
59
src/components/ArticlePreview/ArticleShort.tsx
Normal file
@ -0,0 +1,59 @@
|
||||
import { A } from '@solidjs/router';
|
||||
import { batch, Component, createEffect, For, JSXElement, Show } from 'solid-js';
|
||||
import { createStore } from 'solid-js/store';
|
||||
import { Portal } from 'solid-js/web';
|
||||
import { useAccountContext } from '../../contexts/AccountContext';
|
||||
import { CustomZapInfo, useAppContext } from '../../contexts/AppContext';
|
||||
import { useThreadContext } from '../../contexts/ThreadContext';
|
||||
import { date, shortDate } from '../../lib/dates';
|
||||
import { hookForDev } from '../../lib/devTools';
|
||||
import { userName } from '../../stores/profile';
|
||||
import { PrimalArticle, ZapOption } from '../../types/primal';
|
||||
import { uuidv4 } from '../../utils';
|
||||
import Avatar from '../Avatar/Avatar';
|
||||
import { NoteReactionsState } from '../Note/Note';
|
||||
import NoteContextTrigger from '../Note/NoteContextTrigger';
|
||||
import ArticleFooter from '../Note/NoteFooter/ArticleFooter';
|
||||
import NoteFooter from '../Note/NoteFooter/NoteFooter';
|
||||
import NoteTopZaps from '../Note/NoteTopZaps';
|
||||
import NoteTopZapsCompact from '../Note/NoteTopZapsCompact';
|
||||
import VerificationCheck from '../VerificationCheck/VerificationCheck';
|
||||
|
||||
import styles from './ArticlePreview.module.scss';
|
||||
|
||||
const ArticlePreview: Component<{
|
||||
id?: string,
|
||||
article: PrimalArticle,
|
||||
}> = (props) => {
|
||||
|
||||
return (
|
||||
<A class={styles.articleShort} href={`/e/${props.article.noteId}`}>
|
||||
<div class={styles.header}>
|
||||
<div class={styles.userInfo}>
|
||||
<Avatar user={props.article.user} size="micro"/>
|
||||
<div class={styles.userName}>{userName(props.article.user)}</div>
|
||||
</div>
|
||||
<div class={styles.time}>
|
||||
{date(props.article.published).label}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class={styles.body}>
|
||||
<div class={styles.text}>
|
||||
<div class={styles.content}>
|
||||
<div class={styles.title}>
|
||||
{props.article.title}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Show when={props.article.image.length > 0}>
|
||||
<div class={styles.image}>
|
||||
<img src={props.article.image} />
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</A>
|
||||
);
|
||||
}
|
||||
|
||||
export default hookForDev(ArticlePreview);
|
@ -60,10 +60,17 @@
|
||||
.headingPicks {
|
||||
@include heading();
|
||||
text-transform: capitalize;
|
||||
height: fit-content;
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
|
||||
.readsSidebar {
|
||||
.section {
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.topic {
|
||||
display: inline-block;
|
||||
background-color: var(--background-input);
|
||||
|
@ -22,6 +22,7 @@ import { getArticleThread, getReadsTopics } from '../../lib/feed';
|
||||
import { fetchArticles } from '../../handleNotes';
|
||||
import { getParametrizedEvent, getParametrizedEvents } from '../../lib/notes';
|
||||
import { decodeIdentifier } from '../../lib/keys';
|
||||
import ArticleShort from '../ArticlePreview/ArticleShort';
|
||||
|
||||
const sidebarOptions = [
|
||||
{
|
||||
@ -121,29 +122,11 @@ const ReadsSidebar: Component< { id?: string } > = (props) => {
|
||||
randomIndices.add(randomIndex);
|
||||
}
|
||||
|
||||
const reads = [ ...randomIndices ].map(i => rec[i])
|
||||
getPEvents(reads)
|
||||
// getRecomendedArticles(reads)
|
||||
}
|
||||
})
|
||||
|
||||
const getPEvents = (ids: string[]) => {
|
||||
const events = ids.reduce<EventCoordinate[]>((acc, id) => {
|
||||
const d = decodeIdentifier(id);
|
||||
|
||||
if (!d.data || d.type !== 'naddr') return acc;
|
||||
|
||||
const { pubkey, identifier, kind } = d.data;
|
||||
|
||||
return [
|
||||
...acc,
|
||||
{ identifier, pubkey, kind },
|
||||
]
|
||||
|
||||
}, []);
|
||||
|
||||
getParametrizedEvents(events, `reads_pe_${APP_ID}`);
|
||||
const reads = [ ...randomIndices ].map(i => rec[i]);
|
||||
|
||||
getRecomendedArticles(reads)
|
||||
}
|
||||
});
|
||||
|
||||
const getRecomendedArticles = async (ids: string[]) => {
|
||||
if (!account?.publicKey) return;
|
||||
@ -156,8 +139,6 @@ const ReadsSidebar: Component< { id?: string } > = (props) => {
|
||||
|
||||
setIsFetching(() => false);
|
||||
|
||||
console.log('ARTICLES: ', articles);
|
||||
|
||||
setTopPicks(() => [...articles]);
|
||||
};
|
||||
|
||||
@ -174,9 +155,11 @@ const ReadsSidebar: Component< { id?: string } > = (props) => {
|
||||
<Loader />
|
||||
}
|
||||
>
|
||||
<div class={styles.section}>
|
||||
<For each={topPicks}>
|
||||
{(note) => <div>{note.title}</div>}
|
||||
{(note) => <ArticleShort article={note} />}
|
||||
</For>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
|
||||
@ -190,9 +173,11 @@ const ReadsSidebar: Component< { id?: string } > = (props) => {
|
||||
<Loader />
|
||||
}
|
||||
>
|
||||
<div class={styles.section}>
|
||||
<For each={topics}>
|
||||
{(topic) => <div class={styles.topic}>{topic}</div>}
|
||||
</For>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
</Show>
|
||||
|
@ -1,12 +1,13 @@
|
||||
import { nip19 } from "nostr-tools";
|
||||
import { Kind } from "./constants";
|
||||
import { getEvents } from "./lib/feed";
|
||||
import { setLinkPreviews } from "./lib/notes";
|
||||
import { decodeIdentifier } from "./lib/keys";
|
||||
import { getParametrizedEvents, setLinkPreviews } from "./lib/notes";
|
||||
import { updateStore, store } from "./services/StoreService";
|
||||
import { subscribeTo } from "./sockets";
|
||||
import { convertToArticles, convertToNotes } from "./stores/note";
|
||||
import { account } from "./translations";
|
||||
import { FeedPage, NostrEventContent, NostrEventType, NostrMentionContent, NostrNoteActionsContent, NostrNoteContent, NostrStatsContent, NostrUserContent, NoteActions, PrimalArticle, PrimalNote, TopZap } from "./types/primal";
|
||||
import { EventCoordinate, FeedPage, NostrEventContent, NostrEventType, NostrMentionContent, NostrNoteActionsContent, NostrNoteContent, NostrStatsContent, NostrUserContent, NoteActions, PrimalArticle, PrimalNote, TopZap } from "./types/primal";
|
||||
import { parseBolt11 } from "./utils";
|
||||
|
||||
export const fetchNotes = (pubkey: string | undefined, noteIds: string[], subId: string) => {
|
||||
@ -197,6 +198,196 @@ export const fetchArticles = (pubkey: string | undefined, noteIds: string[], sub
|
||||
until: 0,
|
||||
}
|
||||
|
||||
const events = noteIds.reduce<EventCoordinate[]>((acc, id) => {
|
||||
const d = decodeIdentifier(id);
|
||||
|
||||
if (!d.data || d.type !== 'naddr') return acc;
|
||||
|
||||
const { pubkey, identifier, kind } = d.data;
|
||||
|
||||
return [
|
||||
...acc,
|
||||
{ identifier, pubkey, kind },
|
||||
]
|
||||
|
||||
}, []);
|
||||
|
||||
let lastNote: PrimalArticle | undefined;
|
||||
|
||||
const unsub = subscribeTo(subId, (type, _, content) => {
|
||||
|
||||
if (type === 'EOSE') {
|
||||
unsub();
|
||||
const notes = convertToArticles(page, page.topZaps);
|
||||
|
||||
resolve(notes);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type === 'EVENT') {
|
||||
if (!content) return;
|
||||
updatePage(content);
|
||||
}
|
||||
});
|
||||
|
||||
getParametrizedEvents(events, subId);
|
||||
// getEvents(pubkey, [...noteIds], subId, true);
|
||||
|
||||
const updatePage = (content: NostrEventContent) => {
|
||||
if (content.kind === Kind.Metadata) {
|
||||
const user = content as NostrUserContent;
|
||||
|
||||
page.users[user.pubkey] = { ...user };
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ([Kind.LongForm, Kind.Repost].includes(content.kind)) {
|
||||
const message = content as NostrNoteContent;
|
||||
|
||||
if (lastNote?.noteId !== nip19.noteEncode(message.id)) {
|
||||
page.messages.push({...message});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (content.kind === Kind.NoteStats) {
|
||||
const statistic = content as NostrStatsContent;
|
||||
const stat = JSON.parse(statistic.content);
|
||||
page.postStats[stat.event_id] = { ...stat };
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (content.kind === Kind.Mentions) {
|
||||
const mentionContent = content as NostrMentionContent;
|
||||
const mention = JSON.parse(mentionContent.content);
|
||||
|
||||
if (!page.mentions) {
|
||||
page.mentions = {};
|
||||
}
|
||||
|
||||
page.mentions[mention.id] = { ...mention };
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (content.kind === Kind.NoteActions) {
|
||||
const noteActionContent = content as NostrNoteActionsContent;
|
||||
const noteActions = JSON.parse(noteActionContent.content) as NoteActions;
|
||||
|
||||
page.noteActions[noteActions.event_id] = { ...noteActions };
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (content.kind === Kind.LinkMetadata) {
|
||||
const metadata = JSON.parse(content.content);
|
||||
|
||||
const data = metadata.resources[0];
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const preview = {
|
||||
url: data.url,
|
||||
title: data.md_title,
|
||||
description: data.md_description,
|
||||
mediaType: data.mimetype,
|
||||
contentType: data.mimetype,
|
||||
images: [data.md_image],
|
||||
favicons: [data.icon_url],
|
||||
};
|
||||
|
||||
setLinkPreviews(() => ({ [data.url]: preview }));
|
||||
return;
|
||||
}
|
||||
|
||||
if (content.kind === Kind.RelayHint) {
|
||||
const hints = JSON.parse(content.content);
|
||||
page.relayHints = { ...page.relayHints, ...hints };
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (content?.kind === Kind.Zap) {
|
||||
const zapTag = content.tags.find(t => t[0] === 'description');
|
||||
|
||||
if (!zapTag) return;
|
||||
|
||||
const zapInfo = JSON.parse(zapTag[1] || '{}');
|
||||
|
||||
let amount = '0';
|
||||
|
||||
let bolt11Tag = content?.tags?.find(t => t[0] === 'bolt11');
|
||||
|
||||
if (bolt11Tag) {
|
||||
try {
|
||||
amount = `${parseBolt11(bolt11Tag[1]) || 0}`;
|
||||
} catch (e) {
|
||||
const amountTag = zapInfo.tags.find((t: string[]) => t[0] === 'amount');
|
||||
|
||||
amount = amountTag ? amountTag[1] : '0';
|
||||
}
|
||||
}
|
||||
|
||||
const eventId = (zapInfo.tags.find((t: string[]) => t[0] === 'e') || [])[1];
|
||||
|
||||
const zap: TopZap = {
|
||||
id: zapInfo.id,
|
||||
amount: parseInt(amount || '0'),
|
||||
pubkey: zapInfo.pubkey,
|
||||
message: zapInfo.content,
|
||||
eventId,
|
||||
};
|
||||
|
||||
if (page.topZaps[eventId] === undefined) {
|
||||
page.topZaps[eventId] = [{ ...zap }];
|
||||
return;
|
||||
}
|
||||
|
||||
if (page.topZaps[eventId].find(i => i.id === zap.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newZaps = [ ...page.topZaps[eventId], { ...zap }].sort((a, b) => b.amount - a.amount);
|
||||
|
||||
page.topZaps[eventId] = [ ...newZaps ];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (content.kind === Kind.NoteQuoteStats) {
|
||||
const quoteStats = JSON.parse(content.content);
|
||||
|
||||
|
||||
// updateStore('quoteCount', () => quoteStats.count || 0);
|
||||
return;
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export const fetchArticleThread = (pubkey: string | undefined, noteIds: string, subId: string) => {
|
||||
return new Promise<PrimalArticle[]>((resolve, reject) => {
|
||||
if (!pubkey) reject('Missing pubkey');
|
||||
|
||||
let page: FeedPage = {
|
||||
users: {},
|
||||
messages: [],
|
||||
postStats: {},
|
||||
mentions: {},
|
||||
noteActions: {},
|
||||
relayHints: {},
|
||||
topZaps: {},
|
||||
since: 0,
|
||||
until: 0,
|
||||
}
|
||||
|
||||
let primaryArticle: PrimalArticle | undefined;
|
||||
|
||||
let lastNote: PrimalArticle | undefined;
|
||||
|
||||
const unsub = subscribeTo(subId, (type, _, content) => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useIntl } from "@cookbook/solid-intl";
|
||||
import { useParams } from "@solidjs/router";
|
||||
import { Component, createEffect, createSignal, For, Show } from "solid-js";
|
||||
import { Component, createEffect, createSignal, For, onMount, Show } from "solid-js";
|
||||
import { createStore } from "solid-js/store";
|
||||
import { APP_ID } from "../App";
|
||||
import { Kind } from "../constants";
|
||||
@ -312,7 +312,7 @@ const Longform: Component< { naddr: string } > = (props) => {
|
||||
getUserProfileInfo(pubkey(), account?.publicKey, subId);
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
onMount(() => {
|
||||
if (naddr() === 'naddr1_test') {
|
||||
|
||||
setNote(() => ({
|
||||
|
Loading…
Reference in New Issue
Block a user