Instant refresh top zaps

This commit is contained in:
Bojan Mojsilovic 2024-04-23 13:48:48 +02:00
parent 26d24179e2
commit b0091ccfe1
4 changed files with 61 additions and 19 deletions

View File

@ -130,9 +130,7 @@ const CustomZap: Component<{
const handleZap = (success = false) => { const handleZap = (success = false) => {
if (success) { if (success) {
setTimeout(() => { props.onSuccess(selectedValue());
props.onSuccess(selectedValue());
}, 2_000)
return; return;
} }

View File

@ -18,7 +18,9 @@ import { CustomZapInfo, useAppContext } from '../../contexts/AppContext';
import NoteContextTrigger from './NoteContextTrigger'; import NoteContextTrigger from './NoteContextTrigger';
import { date, longDate, veryLongDate } from '../../lib/dates'; import { date, longDate, veryLongDate } from '../../lib/dates';
import { hexToNpub } from '../../lib/keys'; import { hexToNpub } from '../../lib/keys';
import { zapCustomOption } from '../../translations'; import { thread, zapCustomOption } from '../../translations';
import { useAccountContext } from '../../contexts/AccountContext';
import { uuidv4 } from '../../utils';
export type NoteFooterState = { export type NoteFooterState = {
likes: number, likes: number,
@ -37,6 +39,7 @@ export type NoteFooterState = {
hideZapIcon: boolean, hideZapIcon: boolean,
moreZapsAvailable: boolean, moreZapsAvailable: boolean,
isRepostMenuVisible: boolean, isRepostMenuVisible: boolean,
topZaps: TopZap[],
}; };
const Note: Component<{ const Note: Component<{
@ -49,6 +52,7 @@ const Note: Component<{
const threadContext = useThreadContext(); const threadContext = useThreadContext();
const app = useAppContext(); const app = useAppContext();
const account = useAccountContext();
const intl = useIntl(); const intl = useIntl();
const noteType = () => props.noteType || 'feed'; const noteType = () => props.noteType || 'feed';
@ -76,6 +80,7 @@ const Note: Component<{
hideZapIcon: false, hideZapIcon: false,
moreZapsAvailable: false, moreZapsAvailable: false,
isRepostMenuVisible: false, isRepostMenuVisible: false,
topZaps: [],
}); });
let noteContextMenu: HTMLDivElement | undefined; let noteContextMenu: HTMLDivElement | undefined;
@ -94,6 +99,27 @@ const Note: Component<{
const onSuccessZap = (zapOption: ZapOption) => { const onSuccessZap = (zapOption: ZapOption) => {
app?.actions.closeCustomZapModal(); app?.actions.closeCustomZapModal();
app?.actions.resetCustomZap(); app?.actions.resetCustomZap();
const pubkey = account?.publicKey;
if (!pubkey) return;
const oldZaps = [ ...reactionsState.topZaps ];
const newZap = {
amount: zapOption.amount || 0,
message: zapOption.message || '',
pubkey,
eventId: props.note.post.id,
id: uuidv4() as string,
};
if (!threadContext?.users.find((u) => u.pubkey === pubkey)) {
threadContext?.actions.fetchUsers([pubkey])
}
const zaps = [ ...oldZaps, { ...newZap }].sort((a, b) => b.amount - a.amount);
batch(() => { batch(() => {
updateReactionsState('zapCount', (z) => z + 1); updateReactionsState('zapCount', (z) => z + 1);
// updateFooterState('satsZapped', (z) => z + (zapOption.amount || 0)); // updateFooterState('satsZapped', (z) => z + (zapOption.amount || 0));
@ -102,8 +128,8 @@ const Note: Component<{
updateReactionsState('showZapAnim', () => false); updateReactionsState('showZapAnim', () => false);
updateReactionsState('hideZapIcon', () => false); updateReactionsState('hideZapIcon', () => false);
updateReactionsState('zapped', () => true); updateReactionsState('zapped', () => true);
updateReactionsState('topZaps', () => [...zaps]);
}); });
threadContext?.actions.fetchTopZaps(props.note.post.id);
}; };
const onFailZap = (zapOption: ZapOption) => { const onFailZap = (zapOption: ZapOption) => {
@ -169,12 +195,10 @@ const Note: Component<{
return (likes || 0) + (zapCount || 0) + (reposts || 0); return (likes || 0) + (zapCount || 0) + (reposts || 0);
}; };
const topZaps = () => threadContext?.topZaps[props.note.post.id] || []; const firstZap = createMemo(() => reactionsState.topZaps[0]);
const firstZap = createMemo(() => topZaps()[0]);
const restZaps = createMemo(() => { const restZaps = createMemo(() => {
const zaps = topZaps().slice(1); const zaps = reactionsState.topZaps.slice(1);
let limit = 0; let limit = 0;
let digits = 0; let digits = 0;
@ -200,11 +224,15 @@ const Note: Component<{
} }
return rest; return rest;
}) });
const zapSender = (zap: TopZap) => { const zapSender = (zap: TopZap) => {
return threadContext?.users.find(u => u.pubkey === zap.pubkey); return threadContext?.users.find(u => u.pubkey === zap.pubkey);
} };
createEffect(() => {
updateReactionsState('topZaps', () => [ ...(threadContext?.topZaps[props.note.post.id] || []) ]);
});
return ( return (
<Switch> <Switch>
@ -260,7 +288,7 @@ const Note: Component<{
<ParsedNote note={props.note} width={Math.min(574, window.innerWidth)} /> <ParsedNote note={props.note} width={Math.min(574, window.innerWidth)} />
</div> </div>
<div class={`${styles.zapHighlights} ${topZaps().length < 4 ? styles.onlyFew : ''}`}> <div class={`${styles.zapHighlights} ${reactionsState.topZaps.length < 4 ? styles.onlyFew : ''}`}>
<Show when={firstZap()}> <Show when={firstZap()}>
<button <button
class={styles.firstZap} class={styles.firstZap}

View File

@ -279,13 +279,11 @@ const NoteFooter: Component<{
props.updateState('isZapping', () => false); props.updateState('isZapping', () => false);
if (success) { if (success) {
setTimeout(() => { props.customZapInfo.onSuccess({
props.customZapInfo.onSuccess({ emoji,
emoji, amount,
amount, message,
message, });
});
}, 2_000);
return; return;
} }

View File

@ -40,6 +40,7 @@ import { APP_ID } from "../App";
import { useAccountContext } from "./AccountContext"; import { useAccountContext } from "./AccountContext";
import { getEventZaps, setLinkPreviews } from "../lib/notes"; import { getEventZaps, setLinkPreviews } from "../lib/notes";
import { parseBolt11 } from "../utils"; import { parseBolt11 } from "../utils";
import { getUserProfiles } from "../lib/profile";
export type TopZap = { export type TopZap = {
id: string, id: string,
@ -69,6 +70,7 @@ export type ThreadContextStore = {
savePage: (page: FeedPage) => void, savePage: (page: FeedPage) => void,
setPrimaryNote: (context: PrimalNote | undefined) => void, setPrimaryNote: (context: PrimalNote | undefined) => void,
fetchTopZaps: (noteId: string) => void, fetchTopZaps: (noteId: string) => void,
fetchUsers: (pubkeys: string[]) => void,
} }
} }
@ -292,6 +294,10 @@ export const ThreadProvider = (props: { children: ContextChildren }) => {
getEventZaps(noteId, account?.publicKey, `thread_zapps_${APP_ID}`, 10, 0); getEventZaps(noteId, account?.publicKey, `thread_zapps_${APP_ID}`, 10, 0);
}; };
const fetchUsers = (pubkeys: string[]) => {
getUserProfiles(pubkeys, `thread_pk_${APP_ID}`);
};
// SOCKET HANDLERS ------------------------------ // SOCKET HANDLERS ------------------------------
const onMessage = (event: MessageEvent) => { const onMessage = (event: MessageEvent) => {
@ -351,6 +357,17 @@ export const ThreadProvider = (props: { children: ContextChildren }) => {
return; return;
} }
} }
if (subId === `thread_pk_${APP_ID}`) {
if (type === 'EOSE') {
savePage(store.page);
}
if (type === 'EVENT') {
updatePage(content);
return;
}
}
}; };
const onSocketClose = (closeEvent: CloseEvent) => { const onSocketClose = (closeEvent: CloseEvent) => {
@ -420,6 +437,7 @@ export const ThreadProvider = (props: { children: ContextChildren }) => {
savePage, savePage,
setPrimaryNote, setPrimaryNote,
fetchTopZaps, fetchTopZaps,
fetchUsers,
}, },
}); });