From 82b87b3e3284e3d7ecab72edc7b1f339ad06ed17 Mon Sep 17 00:00:00 2001 From: daniyal Date: Mon, 21 Oct 2024 14:33:40 +0500 Subject: [PATCH] fix: move getTotalZapAmount from relay controller to ndkContext --- src/components/ModCard.tsx | 7 +- src/contexts/NDKContext.tsx | 71 +++++++++++-- src/controllers/relay.ts | 128 +---------------------- src/hooks/useNDKContext.ts | 20 +--- src/pages/mod/internal/comment/index.tsx | 17 ++- src/pages/mod/internal/zap/index.tsx | 20 ++-- 6 files changed, 87 insertions(+), 176 deletions(-) diff --git a/src/components/ModCard.tsx b/src/components/ModCard.tsx index 49fb142..c14372f 100644 --- a/src/components/ModCard.tsx +++ b/src/components/ModCard.tsx @@ -5,8 +5,7 @@ import { handleModImageError } from '../utils' import { ModDetails } from 'types' import { getModPageRoute } from 'routes' import { kinds, nip19 } from 'nostr-tools' -import { useDidMount, useReactions } from 'hooks' -import { RelayController } from 'controllers' +import { useDidMount, useNDKContext, useReactions } from 'hooks' import { toast } from 'react-toastify' import { useComments } from 'hooks/useComments' @@ -19,10 +18,10 @@ export const ModCard = React.memo((props: ModDetails) => { eTag: props.id, aTag: props.aTag }) + const { getTotalZapAmount } = useNDKContext() useDidMount(() => { - RelayController.getInstance() - .getTotalZapAmount(props.author, props.id, props.aTag) + getTotalZapAmount(props.author, props.id, props.aTag) .then((res) => { setTotalZappedAmount(res.accumulatedZapAmount) }) diff --git a/src/contexts/NDKContext.tsx b/src/contexts/NDKContext.tsx index 4c84f7f..da9c2c4 100644 --- a/src/contexts/NDKContext.tsx +++ b/src/contexts/NDKContext.tsx @@ -5,7 +5,8 @@ import NDK, { NDKKind, NDKRelaySet, NDKSubscriptionCacheUsage, - NDKUser + NDKUser, + zapInvoiceFromEvent } from '@nostr-dev-kit/ndk' import NDKCacheAdapterDexie from '@nostr-dev-kit/ndk-cache-dexie' import { MOD_FILTER_LIMIT, T_TAG_VALUE } from 'constants.ts' @@ -36,16 +37,25 @@ interface NDKContextType { fetchEvents: (filter: NDKFilter) => Promise fetchEvent: (filter: NDKFilter) => Promise fetchEventsFromUserRelays: ( - filter: NDKFilter, + filter: NDKFilter | NDKFilter[], hexKey: string, userRelaysType: UserRelaysType ) => Promise fetchEventFromUserRelays: ( - filter: NDKFilter, + filter: NDKFilter | NDKFilter[], hexKey: string, userRelaysType: UserRelaysType ) => Promise findMetadata: (pubkey: string) => Promise + getTotalZapAmount: ( + user: string, + eTag: string, + aTag?: string, + currentLoggedInUser?: string + ) => Promise<{ + accumulatedZapAmount: number + hasZapped: boolean + }> } // Create the context with an initial value of `null` @@ -218,10 +228,10 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => { * @returns A promise that resolves with an array of events. */ const fetchEventsFromUserRelays = async ( - filter: NDKFilter, + filter: NDKFilter | NDKFilter[], hexKey: string, userRelaysType: UserRelaysType - ) => { + ): Promise => { // Find the user's relays. const relayUrls = await getRelayListForUser(hexKey, ndk) .then((ndkRelayList) => { @@ -266,7 +276,7 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => { * @returns A promise that resolves to the fetched event or null if the operation fails. */ const fetchEventFromUserRelays = async ( - filter: NDKFilter, + filter: NDKFilter | NDKFilter[], hexKey: string, userRelaysType: UserRelaysType ) => { @@ -296,6 +306,52 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => { return userProfile } + const getTotalZapAmount = async ( + user: string, + eTag: string, + aTag?: string, + currentLoggedInUser?: string + ) => { + const filters: NDKFilter[] = [ + { + kinds: [NDKKind.Zap], + '#e': [eTag], + '#p': [user] + } + ] + + if (aTag) { + filters.push({ + kinds: [NDKKind.Zap], + '#a': [aTag], + '#p': [user] + }) + } + + const zapEvents = await fetchEventsFromUserRelays( + filters, + user, + UserRelaysType.Read + ) + + let accumulatedZapAmount = 0 + let hasZapped = false + + zapEvents.forEach((zap) => { + const zapInvoice = zapInvoiceFromEvent(zap) + if (zapInvoice) { + accumulatedZapAmount += Math.round(zapInvoice.amount / 1000) + + if (!hasZapped) hasZapped = zapInvoice.zappee === currentLoggedInUser + } + }) + + return { + accumulatedZapAmount, + hasZapped + } + } + return ( { fetchEvent, fetchEventsFromUserRelays, fetchEventFromUserRelays, - findMetadata + findMetadata, + getTotalZapAmount }} > {children} diff --git a/src/controllers/relay.ts b/src/controllers/relay.ts index 64c22c2..e8855c6 100644 --- a/src/controllers/relay.ts +++ b/src/controllers/relay.ts @@ -1,11 +1,5 @@ -import { Event, Filter, kinds, nip57, Relay } from 'nostr-tools' -import { - extractZapAmount, - log, - LogType, - normalizeWebSocketURL, - timeout -} from '../utils' +import { Event, Relay } from 'nostr-tools' +import { log, LogType, normalizeWebSocketURL, timeout } from '../utils' import { MetadataController, UserRelaysType } from './metadata' /** @@ -371,122 +365,4 @@ export class RelayController { // Return the list of relay URLs where the event was successfully published return publishedOnRelays } - - getTotalZapAmount = async ( - user: string, - eTag: string, - aTag?: string, - currentLoggedInUser?: string - ) => { - const metadataController = await MetadataController.getInstance() - - const relayUrls = await metadataController.findUserRelays( - user, - UserRelaysType.Read - ) - - const appRelay = import.meta.env.VITE_APP_RELAY - if (!relayUrls.includes(appRelay)) { - relayUrls.push(appRelay) - } - - // Connect to all specified relays - const relayPromises = relayUrls.map((relayUrl) => - this.connectRelay(relayUrl) - ) - - // Use Promise.allSettled to wait for all promises to settle - const results = await Promise.allSettled(relayPromises) - - // Extract non-null values from fulfilled promises in a single pass - const relays = results.reduce((acc, result) => { - if (result.status === 'fulfilled') { - const value = result.value - if (value) { - acc.push(value) - } - } - return acc - }, []) - - let accumulatedZapAmount = 0 - let hasZapped = false - - const eventIds = new Set() // To keep track of event IDs and avoid duplicates - - const filters: Filter[] = [ - { - kinds: [kinds.Zap], - '#e': [eTag] - } - ] - - if (aTag) { - filters.push({ - kinds: [kinds.Zap], - '#a': [aTag] - }) - } - - // Create a promise for each relay subscription - const subPromises = relays.map((relay) => { - return new Promise((resolve) => { - // Subscribe to the relay with the specified filter - const sub = relay.subscribe(filters, { - // Handle incoming events - onevent: (e) => { - // Add the event to the array if it's not a duplicate - if (!eventIds.has(e.id)) { - eventIds.add(e.id) // Record the event ID - - const zapRequestStr = e.tags.find( - (t) => t[0] === 'description' - )?.[1] - if (!zapRequestStr) return - - const error = nip57.validateZapRequest(zapRequestStr) - if (error) return - - let zapRequest: Event | null = null - - try { - zapRequest = JSON.parse(zapRequestStr) - } catch (error) { - log( - true, - LogType.Error, - 'Error occurred in parsing zap request', - error - ) - } - - if (!zapRequest) return - - const amount = extractZapAmount(zapRequest) - accumulatedZapAmount += amount - - if (amount > 0) { - if (!hasZapped) { - hasZapped = zapRequest.pubkey === currentLoggedInUser - } - } - } - }, - // Handle the End-Of-Stream (EOSE) message - oneose: () => { - sub.close() // Close the subscription - resolve() // Resolve the promise when EOSE is received - } - }) - }) - }) - - // Wait for all subscriptions to complete - await Promise.allSettled(subPromises) - - return { - accumulatedZapAmount, - hasZapped - } - } } diff --git a/src/hooks/useNDKContext.ts b/src/hooks/useNDKContext.ts index b551d5e..f7383df 100644 --- a/src/hooks/useNDKContext.ts +++ b/src/hooks/useNDKContext.ts @@ -9,23 +9,5 @@ export const useNDKContext = () => { 'NDKContext should not be used in out component tree hierarchy' ) - const { - ndk, - fetchEvents, - fetchEvent, - fetchEventsFromUserRelays, - fetchEventFromUserRelays, - fetchMods, - findMetadata - } = ndkContext - - return { - ndk, - fetchEvents, - fetchEvent, - fetchEventsFromUserRelays, - fetchEventFromUserRelays, - fetchMods, - findMetadata - } + return { ...ndkContext } } diff --git a/src/pages/mod/internal/comment/index.tsx b/src/pages/mod/internal/comment/index.tsx index 2f5b721..aeeaf7b 100644 --- a/src/pages/mod/internal/comment/index.tsx +++ b/src/pages/mod/internal/comment/index.tsx @@ -496,20 +496,19 @@ const Reactions = (props: Event) => { const Zap = (props: Event) => { const [isOpen, setIsOpen] = useState(false) + const [totalZappedAmount, setTotalZappedAmount] = useState(0) const [hasZapped, setHasZapped] = useState(false) const userState = useAppSelector((state) => state.user) - - const [totalZappedAmount, setTotalZappedAmount] = useState(0) + const { getTotalZapAmount } = useNDKContext() useDidMount(() => { - RelayController.getInstance() - .getTotalZapAmount( - props.pubkey, - props.id, - undefined, - userState.user?.pubkey as string - ) + getTotalZapAmount( + props.pubkey, + props.id, + undefined, + userState.user?.pubkey as string + ) .then((res) => { setTotalZappedAmount(res.accumulatedZapAmount) setHasZapped(res.hasZapped) diff --git a/src/pages/mod/internal/zap/index.tsx b/src/pages/mod/internal/zap/index.tsx index 93e8bad..a0169bb 100644 --- a/src/pages/mod/internal/zap/index.tsx +++ b/src/pages/mod/internal/zap/index.tsx @@ -1,6 +1,5 @@ import { ZapSplit } from 'components/Zap' -import { RelayController } from 'controllers' -import { useAppSelector, useDidMount } from 'hooks' +import { useAppSelector, useDidMount, useNDKContext } from 'hooks' import { useState } from 'react' import { toast } from 'react-toastify' import { ModDetails } from 'types' @@ -12,20 +11,19 @@ type ZapProps = { export const Zap = ({ modDetails }: ZapProps) => { const [isOpen, setIsOpen] = useState(false) + const [totalZappedAmount, setTotalZappedAmount] = useState(0) const [hasZapped, setHasZapped] = useState(false) const userState = useAppSelector((state) => state.user) - - const [totalZappedAmount, setTotalZappedAmount] = useState(0) + const { getTotalZapAmount } = useNDKContext() useDidMount(() => { - RelayController.getInstance() - .getTotalZapAmount( - modDetails.author, - modDetails.id, - modDetails.aTag, - userState.user?.pubkey as string - ) + getTotalZapAmount( + modDetails.author, + modDetails.id, + modDetails.aTag, + userState.user?.pubkey as string + ) .then((res) => { setTotalZappedAmount(res.accumulatedZapAmount) setHasZapped(res.hasZapped)