mirror of
https://github.com/PrimalHQ/primal-web-app.git
synced 2024-10-01 17:31:13 +00:00
Refactor zapps for note
This commit is contained in:
parent
d06c74f466
commit
ac21e06df7
@ -339,12 +339,17 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding-left: 2px;
|
||||
padding-right: 10px;
|
||||
padding-block: 2px;
|
||||
margin: 0;
|
||||
border-radius: 12px;
|
||||
background: var(--devider);
|
||||
width: fit-content;
|
||||
max-width: 100%;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
outline: none;
|
||||
|
||||
.amount {
|
||||
color: var(--text-primary);
|
||||
@ -395,11 +400,16 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding-left: 2px;
|
||||
padding-right: 10px;
|
||||
padding-block: 2px;
|
||||
margin: 0;
|
||||
border-radius: 12px;
|
||||
background: var(--devider);
|
||||
width: fit-content;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
outline: none;
|
||||
|
||||
.amount {
|
||||
color: var(--text-primary);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { A } from '@solidjs/router';
|
||||
import { batch, Component, For, Match, Show, Switch } from 'solid-js';
|
||||
import { batch, Component, createMemo, For, Match, Show, Switch } from 'solid-js';
|
||||
import { PrimalNote, ZapOption } from '../../types/primal';
|
||||
import ParsedNote from '../ParsedNote/ParsedNote';
|
||||
import NoteFooter from './NoteFooter/NoteFooter';
|
||||
@ -18,6 +18,7 @@ import { CustomZapInfo, useAppContext } from '../../contexts/AppContext';
|
||||
import NoteContextTrigger from './NoteContextTrigger';
|
||||
import { date, longDate, veryLongDate } from '../../lib/dates';
|
||||
import { hexToNpub } from '../../lib/keys';
|
||||
import { zapCustomOption } from '../../translations';
|
||||
|
||||
export type NoteFooterState = {
|
||||
likes: number,
|
||||
@ -157,14 +158,31 @@ const Note: Component<{
|
||||
return (likes || 0) + (zaps || 0) + (reposts || 0);
|
||||
};
|
||||
|
||||
const firstZap = () => (threadContext?.topZaps[props.note.post.id] || [])[0];
|
||||
const firstZap = createMemo(() => (threadContext?.topZaps[props.note.post.id] || [])[0]);
|
||||
|
||||
const topZaps = () => {
|
||||
return (threadContext?.topZaps[props.note.post.id] || []).slice(1, 8);
|
||||
const topZaps = createMemo(() => {
|
||||
// return (threadContext?.topZaps[props.note.post.id] || []).slice(1);
|
||||
const zaps = (threadContext?.topZaps[props.note.post.id] || []).slice(1);
|
||||
|
||||
let limit = 0;
|
||||
let digits = 0;
|
||||
|
||||
for (let i=0; i< zaps.length; i++) {
|
||||
const amount = zaps[i].amount || 0;
|
||||
const length = Math.log(amount) * Math.LOG10E + 1 | 0;
|
||||
|
||||
digits += length;
|
||||
|
||||
if (digits > 25 || limit > 6) break;
|
||||
|
||||
limit++;
|
||||
}
|
||||
|
||||
return zaps.slice(0, limit);
|
||||
})
|
||||
|
||||
const zapSender = (zap: TopZap) => {
|
||||
return threadContext?.users.find(u => u.pubkey === zap.sender);
|
||||
return threadContext?.users.find(u => u.pubkey === zap.pubkey);
|
||||
}
|
||||
|
||||
return (
|
||||
@ -223,25 +241,32 @@ const Note: Component<{
|
||||
|
||||
<div class={styles.zapHighlights}>
|
||||
<Show when={firstZap()}>
|
||||
<A class={styles.firstZap} href={`/p/${hexToNpub(firstZap().sender)}`}>
|
||||
<button
|
||||
class={styles.firstZap}
|
||||
onClick={() => openReactionModal('zaps')}
|
||||
>
|
||||
<Avatar user={zapSender(firstZap())} size="micro" />
|
||||
<div class={styles.amount}>
|
||||
{firstZap().amount_sats.toLocaleString()}
|
||||
{firstZap().amount.toLocaleString()}
|
||||
</div>
|
||||
<div class={styles.description}>
|
||||
{firstZap().message}
|
||||
</div>
|
||||
</A>
|
||||
</button>
|
||||
</Show>
|
||||
<div class={styles.topZaps}>
|
||||
<div class={styles.zapList}>
|
||||
<For each={topZaps()}>
|
||||
{zap => (
|
||||
<A class={styles.topZap} href={`/p/${hexToNpub(zap.sender)}`}>
|
||||
<button
|
||||
class={styles.topZap}
|
||||
onClick={() => openReactionModal('zaps')}
|
||||
>
|
||||
<Avatar user={zapSender(zap)} size="micro" />
|
||||
<div class={styles.amount}>
|
||||
{zap.amount_sats.toLocaleString()}
|
||||
{zap.amount.toLocaleString()}
|
||||
</div>
|
||||
</A>
|
||||
</button>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
|
@ -5,11 +5,12 @@ import { Component, createEffect, createSignal, For, onMount, Show } from 'solid
|
||||
import { createStore } from 'solid-js/store';
|
||||
import { APP_ID } from '../../App';
|
||||
import { Kind } from '../../constants';
|
||||
import { useAccountContext } from '../../contexts/AccountContext';
|
||||
import { ReactionStats } from '../../contexts/AppContext';
|
||||
import { hookForDev } from '../../lib/devTools';
|
||||
import { hexToNpub } from '../../lib/keys';
|
||||
import { getEventReactions, getEventZaps } from '../../lib/notes';
|
||||
import { truncateNumber, truncateNumber2 } from '../../lib/notifications';
|
||||
import { getEventQuotes, getEventReactions, getEventZaps } from '../../lib/notes';
|
||||
import { truncateNumber2 } from '../../lib/notifications';
|
||||
import { subscribeTo } from '../../sockets';
|
||||
import { userName } from '../../stores/profile';
|
||||
import { actions as tActions, placeholders as tPlaceholders, reactionsModal } from '../../translations';
|
||||
@ -30,18 +31,21 @@ const ReactionsModal: Component<{
|
||||
}> = (props) => {
|
||||
|
||||
const intl = useIntl();
|
||||
const account = useAccountContext();
|
||||
|
||||
const [selectedTab, setSelectedTab] = createSignal('likes');
|
||||
|
||||
const [likeList, setLikeList] = createStore<any[]>([]);
|
||||
const [zapList, setZapList] = createStore<any[]>([]);
|
||||
const [repostList, setRepostList] = createStore<any[]>([]);
|
||||
// const [quotesList, setQuotesList] = createStore<any[]>([]);
|
||||
|
||||
const [isFetching, setIsFetching] = createSignal(false);
|
||||
|
||||
let loadedLikes = 0;
|
||||
let loadedZaps = 0;
|
||||
let loadedReposts = 0;
|
||||
// let loadedQuotes = 0;
|
||||
|
||||
createEffect(() => {
|
||||
if (props.noteId && props.stats.openOn) {
|
||||
@ -60,6 +64,9 @@ const ReactionsModal: Component<{
|
||||
case 'reposts':
|
||||
loadedReposts === 0 && getReposts();
|
||||
break;
|
||||
// case 'quotes':
|
||||
// loadedQuotes === 0 && getQuotes();
|
||||
// break;
|
||||
}
|
||||
});
|
||||
|
||||
@ -184,7 +191,7 @@ const ReactionsModal: Component<{
|
||||
});
|
||||
|
||||
setIsFetching(() => true);
|
||||
getEventZaps(props.noteId, subId, 20, offset);
|
||||
getEventZaps(props.noteId, account?.publicKey, subId, 20, offset);
|
||||
// getEventReactions(props.noteId, Kind.Zap, subId, offset);
|
||||
};
|
||||
|
||||
@ -225,6 +232,43 @@ const ReactionsModal: Component<{
|
||||
getEventReactions(props.noteId, Kind.Repost, subId, offset);
|
||||
};
|
||||
|
||||
// const getQuotes = (offset = 0) => {
|
||||
// if (!props.noteId) return;
|
||||
|
||||
// const subId = `nr_q_${props.noteId}_${APP_ID}`;
|
||||
|
||||
// const users: any[] = [];
|
||||
|
||||
// const unsub = subscribeTo(subId, (type,_, content) => {
|
||||
// if (type === 'EOSE') {
|
||||
// setQuotesList((reposts) => [...reposts, ...users]);
|
||||
// loadedQuotes = quotesList.length;
|
||||
// setIsFetching(() => false);
|
||||
// unsub();
|
||||
// }
|
||||
|
||||
// if (type === 'EVENT') {
|
||||
// if (content?.kind === Kind.Metadata) {
|
||||
// let user = JSON.parse(content.content);
|
||||
|
||||
// if (!user.displayName || typeof user.displayName === 'string' && user.displayName.trim().length === 0) {
|
||||
// user.displayName = user.display_name;
|
||||
// }
|
||||
// user.pubkey = content.pubkey;
|
||||
// user.npub = hexToNpub(content.pubkey);
|
||||
// user.created_at = content.created_at;
|
||||
|
||||
// users.push(user);
|
||||
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
// setIsFetching(() => true);
|
||||
// getEventQuotes(props.noteId, subId, offset);
|
||||
// };
|
||||
|
||||
const totalCount = () => props.stats.likes + props.stats.quotes + props.stats.reposts + props.stats.zaps;
|
||||
|
||||
return (
|
||||
|
@ -38,15 +38,15 @@ import {
|
||||
} from "../types/primal";
|
||||
import { APP_ID } from "../App";
|
||||
import { useAccountContext } from "./AccountContext";
|
||||
import { setLinkPreviews } from "../lib/notes";
|
||||
import { getEventZaps, setLinkPreviews } from "../lib/notes";
|
||||
import { parseBolt11 } from "../utils";
|
||||
|
||||
export type TopZap = {
|
||||
amount_sats: number,
|
||||
created_at: number,
|
||||
event_id: string,
|
||||
receiver: string,
|
||||
sender: string,
|
||||
zap_receipt_id: string,
|
||||
id: string,
|
||||
amount: number,
|
||||
pubkey: string,
|
||||
message: string,
|
||||
eventId: string,
|
||||
}
|
||||
|
||||
export type ThreadContextStore = {
|
||||
@ -112,6 +112,7 @@ export const ThreadProvider = (props: { children: ContextChildren }) => {
|
||||
clearNotes();
|
||||
updateStore('noteId', noteId)
|
||||
getThread(account?.publicKey, noteId, `thread_${APP_ID}`);
|
||||
getEventZaps(noteId, account?.publicKey, `thread_zapps_${APP_ID}`, 10, 0);
|
||||
updateStore('isFetching', () => true);
|
||||
}
|
||||
|
||||
@ -223,19 +224,50 @@ export const ThreadProvider = (props: { children: ContextChildren }) => {
|
||||
updateStore('page', 'relayHints', (rh) => ({ ...rh, ...hints }));
|
||||
}
|
||||
|
||||
if (content.kind === Kind.EventZapInfo) {
|
||||
const zapInfo = JSON.parse(content.content) as TopZap;
|
||||
|
||||
if (store.topZaps[zapInfo.event_id] === undefined) {
|
||||
updateStore('topZaps', () => ({ [zapInfo.event_id]: [{ ...zapInfo }]}));
|
||||
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 (store.topZaps[eventId] === undefined) {
|
||||
updateStore('topZaps', () => ({ [eventId]: [{ ...zap }]}));
|
||||
return;
|
||||
}
|
||||
|
||||
if (store.topZaps[zapInfo.event_id].find(i => i.zap_receipt_id === zapInfo.zap_receipt_id)) {
|
||||
if (store.topZaps[eventId].find(i => i.id === zap.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateStore('topZaps', zapInfo.event_id, (zs) => [ ...zs, { ...zapInfo }]);
|
||||
updateStore('topZaps', eventId, (zs) => [ ...zs, { ...zap }]);
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
@ -299,6 +331,17 @@ export const ThreadProvider = (props: { children: ContextChildren }) => {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (subId === `thread_zapps_${APP_ID}`) {
|
||||
if (type === 'EOSE') {
|
||||
savePage(store.page);
|
||||
}
|
||||
|
||||
if (type === 'EVENT') {
|
||||
updatePage(content);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onSocketClose = (closeEvent: CloseEvent) => {
|
||||
|
@ -6,6 +6,7 @@ import LinkPreview from "../components/LinkPreview/LinkPreview";
|
||||
import { addrRegex, appleMusicRegex, emojiRegex, hashtagRegex, interpunctionRegex, Kind, linebreakRegex, lnRegex, lnUnifiedRegex, mixCloudRegex, nostrNestsRegex, noteRegex, noteRegexLocal, profileRegex, profileRegexG, soundCloudRegex, spotifyRegex, tagMentionRegex, twitchRegex, urlRegex, urlRegexG, wavlakeRegex, youtubeRegex } from "../constants";
|
||||
import { sendMessage, subscribeTo } from "../sockets";
|
||||
import { MediaSize, NostrRelays, NostrRelaySignedEvent, PrimalNote, SendNoteResult } from "../types/primal";
|
||||
import { npubToHex } from "./keys";
|
||||
import { logError, logInfo, logWarning } from "./logger";
|
||||
import { getMediaUrl as getMediaUrlDefault } from "./media";
|
||||
import { signEvent } from "./nostrAPI";
|
||||
@ -541,18 +542,33 @@ export const triggerImportEvents = (events: NostrRelaySignedEvent[], subId: stri
|
||||
|
||||
|
||||
export const getEventReactions = (eventId: string, kind: number, subid: string, offset = 0) => {
|
||||
const event_id = eventId.startsWith('note1') ? npubToHex(eventId) : eventId;
|
||||
|
||||
sendMessage(JSON.stringify([
|
||||
"REQ",
|
||||
subid,
|
||||
{cache: ["event_actions", { event_id: eventId, kind, limit: 20, offset }]},
|
||||
{cache: ["event_actions", { event_id, kind, limit: 20, offset }]},
|
||||
]));
|
||||
};
|
||||
|
||||
export const getEventQuotes = (eventId: string, subid: string, offset = 0) => {
|
||||
const event_id = eventId.startsWith('note1') ? npubToHex(eventId) : eventId;
|
||||
|
||||
export const getEventZaps = (eventId: string, subid: string, limit: number, offset = 0) => {
|
||||
sendMessage(JSON.stringify([
|
||||
"REQ",
|
||||
subid,
|
||||
{cache: ["event_zaps_by_satszapped", { event_id: eventId, limit, offset }]},
|
||||
{cache: ["note_mentions", { event_id, limit: 20, offset }]},
|
||||
]));
|
||||
};
|
||||
|
||||
export const getEventZaps = (eventId: string, user_pubkey: string | undefined, subid: string, limit: number, offset = 0) => {
|
||||
if (!user_pubkey) return;
|
||||
|
||||
const event_id = eventId.startsWith('note1') ? npubToHex(eventId) : eventId;
|
||||
|
||||
sendMessage(JSON.stringify([
|
||||
"REQ",
|
||||
subid,
|
||||
{cache: ["event_zaps_by_satszapped", { event_id, user_pubkey, limit, offset }]},
|
||||
]));
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user