From b49b4871c82469bf897bc894fd9c67e2bb93752b Mon Sep 17 00:00:00 2001 From: Paul Miller Date: Wed, 15 Nov 2023 13:34:32 -0600 Subject: [PATCH] refactor zap fetch and catch iter error --- src/utils/fetchZaps.ts | 206 ++++++++++++++++++++++++----------------- 1 file changed, 122 insertions(+), 84 deletions(-) diff --git a/src/utils/fetchZaps.ts b/src/utils/fetchZaps.ts index 00928b2..223f78c 100644 --- a/src/utils/fetchZaps.ts +++ b/src/utils/fetchZaps.ts @@ -116,63 +116,55 @@ async function simpleZapFromEvent( const PRIMAL_API = import.meta.env.VITE_PRIMAL; -export const fetchZaps: ResourceFetcher< - string, - { - follows: string[]; - zaps: SimpleZapItem[]; - profiles: Record; - until?: number; +async function fetchFollows(npub: string): Promise { + let pubkey = undefined; + try { + pubkey = await hexpubFromNpub(npub); + } catch (err) { + console.error("Failed to get hexpub from npub"); + throw err; } -> = async (npub, info) => { - const [state, _actions] = useMegaStore(); - console.log("fetching zaps for:", npub); - const follows: string[] = info?.value ? info.value.follows : []; - const zaps: SimpleZapItem[] = []; - const profiles: Record = info.value?.profiles || {}; - let newUntil = undefined; + const response = await fetch(PRIMAL_API, { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify([ + "contact_list", + { pubkey: pubkey, extended_response: false } + ]) + }); - if (!PRIMAL_API) throw new Error("Missing PRIMAL_API environment variable"); + if (!response.ok) { + throw new Error(`Failed to load follows`); + } - // Only have to ask the relays for follows one time - if (follows.length === 0) { - let pubkey = undefined; - try { - pubkey = await hexpubFromNpub(npub); - } catch (err) { - console.error("Failed to get hexpub from npub"); - throw err; - } + const data = await response.json(); - const response = await fetch(PRIMAL_API, { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify([ - "contact_list", - { pubkey: pubkey, extended_response: false } - ]) - }); + const follows: string[] = []; - if (!response.ok) { - throw new Error(`Failed to load follows`); - } - - const data = await response.json(); - - for (const event of data) { - if (event.kind === 3) { - for (const tag of event.tags) { - if (tag[0] === "p") { - follows.push(tag[1]); - } + for (const event of data) { + if (event.kind === 3) { + for (const tag of event.tags) { + if (tag[0] === "p") { + follows.push(tag[1]); } } } } + return follows; +} + +type PrimalResponse = NostrEvent | NostrProfile; + +async function fetchZapsFromPrimal( + follows: string[], + until?: number +): Promise { + if (!PRIMAL_API) throw new Error("Missing PRIMAL_API environment variable"); + const query = { kinds: [9735, 0, 10000113], limit: 100, @@ -182,7 +174,7 @@ export const fetchZaps: ResourceFetcher< const restPayload = JSON.stringify([ "zaps_feed", // If we have a until value, use it, otherwise don't include it - info?.value?.until ? { ...query, since: info.value?.until } : query + until ? { ...query, since: until } : query ]); const response = await fetch(PRIMAL_API, { @@ -199,43 +191,89 @@ export const fetchZaps: ResourceFetcher< const data = await response.json(); - for (const object of data) { - if (object.kind === 10000113) { - try { - const content = JSON.parse(object.content); - if (content?.until) { - newUntil = content?.until + 1; - } - } catch (e) { - console.error("Failed to parse content: ", object.content); - } - } - - if (object.kind === 0) { - profiles[object.pubkey] = object; - } - - if (object.kind === 9735) { - try { - const event = await simpleZapFromEvent( - object, - state.mutiny_wallet! - ); - - // Only add it if it's a valid zap (not undefined) - if (event) { - zaps.push(event); - } - } catch (e) { - console.error("Failed to parse zap event: ", object); - } - } + // A primal response could be an error ({error: "error"}, or an array of events + if (data.error || !Array.isArray(data)) { + throw new Error("Zap response was not an array"); } - return { - follows, - zaps: [...zaps, ...(info?.value?.zaps || [])], - profiles, - until: newUntil ? newUntil : info?.value?.until - }; + return data; +} + +export const fetchZaps: ResourceFetcher< + string, + { + follows: string[]; + zaps: SimpleZapItem[]; + profiles: Record; + until?: number; + } +> = async (npub, info) => { + const [state, _actions] = useMegaStore(); + + try { + console.log("fetching zaps for:", npub); + let follows: string[] = info?.value ? info.value.follows : []; + const zaps: SimpleZapItem[] = []; + const profiles: Record = + info.value?.profiles || {}; + let newUntil = undefined; + + if (!PRIMAL_API) + throw new Error("Missing PRIMAL_API environment variable"); + + // Only have to ask the relays for follows one time + if (follows.length === 0) { + follows = await fetchFollows(npub); + } + + // Ask primal for all the zaps for these follow pubkeys + const data = await fetchZapsFromPrimal(follows, info?.value?.until); + + // Parse the primal response + for (const object of data) { + if (object.kind === 10000113) { + console.log("got a 10000113 object", object); + try { + const content = JSON.parse(object.content); + if (content?.until) { + newUntil = content?.until + 1; + } + } catch (e) { + console.error("Failed to parse content: ", object.content); + } + } + + if (object.kind === 0) { + console.log("got a 0 object", object); + profiles[object.pubkey] = object as NostrProfile; + } + + if (object.kind === 9735) { + console.log("got a 9735 object", object); + try { + const event = await simpleZapFromEvent( + object, + state.mutiny_wallet! + ); + + // Only add it if it's a valid zap (not undefined) + if (event) { + zaps.push(event); + } + } catch (e) { + console.error("Failed to parse zap event: ", object); + } + } + } + + return { + follows, + zaps: [...zaps, ...(info?.value?.zaps || [])], + profiles, + until: newUntil ? newUntil : info?.value?.until + }; + } catch (e) { + console.error("Failed to load zaps: ", e); + throw new Error("Failed to load zaps"); + } };