diff --git a/src/components/Note/Note.module.scss b/src/components/Note/Note.module.scss
index 1ed9251..160f386 100644
--- a/src/components/Note/Note.module.scss
+++ b/src/components/Note/Note.module.scss
@@ -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);
diff --git a/src/components/Note/Note.tsx b/src/components/Note/Note.tsx
index 3f612da..3932902 100644
--- a/src/components/Note/Note.tsx
+++ b/src/components/Note/Note.tsx
@@ -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<{
-
+
+
diff --git a/src/components/ReactionsModal/ReactionsModal.tsx b/src/components/ReactionsModal/ReactionsModal.tsx
index 32c5280..9a63998 100644
--- a/src/components/ReactionsModal/ReactionsModal.tsx
+++ b/src/components/ReactionsModal/ReactionsModal.tsx
@@ -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
([]);
const [zapList, setZapList] = createStore([]);
const [repostList, setRepostList] = createStore([]);
+ // const [quotesList, setQuotesList] = createStore([]);
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 (
diff --git a/src/contexts/ThreadContext.tsx b/src/contexts/ThreadContext.tsx
index 3971ed9..841ce83 100644
--- a/src/contexts/ThreadContext.tsx
+++ b/src/contexts/ThreadContext.tsx
@@ -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) => {
diff --git a/src/lib/notes.tsx b/src/lib/notes.tsx
index aeae4fb..bb2a7d7 100644
--- a/src/lib/notes.tsx
+++ b/src/lib/notes.tsx
@@ -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 }]},
]));
};