157 lines
4.3 KiB
TypeScript
157 lines
4.3 KiB
TypeScript
import {writable, Subscriber} from "svelte/store"
|
|
import {Zapper, TrustedEvent, Zap, getTagValues, zapFromEvent} from "@welshman/util"
|
|
import {
|
|
removeUndefined,
|
|
fetchJson,
|
|
bech32ToHex,
|
|
hexToBech32,
|
|
tryCatch,
|
|
batcher,
|
|
postJson,
|
|
} from "@welshman/lib"
|
|
import {
|
|
getter,
|
|
deriveItems,
|
|
deriveDeduplicated,
|
|
makeForceLoadItem,
|
|
makeLoadItem,
|
|
makeDeriveItem,
|
|
} from "@welshman/store"
|
|
import {deriveProfile, loadProfile} from "./profiles.js"
|
|
import {appContext} from "./context.js"
|
|
|
|
export const zappersByLnurl = writable(new Map<string, Zapper>())
|
|
|
|
export const zappers = deriveItems(zappersByLnurl)
|
|
|
|
export const getZappersByLnurl = getter(zappersByLnurl)
|
|
|
|
export const getZapper = (lnurl: string) => getZappersByLnurl().get(lnurl)
|
|
|
|
export const zapperSubscribers: Subscriber<Zapper>[] = []
|
|
|
|
export const notifyZapper = (zapper: Zapper) => zapperSubscribers.forEach(sub => sub(zapper))
|
|
|
|
export const onZapper = (sub: (zapper: Zapper) => void) => {
|
|
zapperSubscribers.push(sub)
|
|
|
|
return () => {
|
|
const i = zapperSubscribers.findIndex(s => s === sub)
|
|
|
|
if (i !== -1) zapperSubscribers.splice(i, 1)
|
|
}
|
|
}
|
|
|
|
export const fetchZapper = batcher(800, async (lnurls: string[]) => {
|
|
const base = appContext.dufflepudUrl
|
|
const result = new Map<string, Zapper>()
|
|
const valid = lnurls.filter(lnurl => lnurl.startsWith("lnurl1"))
|
|
|
|
const addZapper = (lnurl: string, info: any) => {
|
|
if (info) {
|
|
try {
|
|
result.set(lnurl, {...info, lnurl})
|
|
} catch (e) {
|
|
// pass
|
|
}
|
|
}
|
|
}
|
|
|
|
// Use dufflepud if we it's set up to protect user privacy, otherwise fetch directly
|
|
if (base) {
|
|
const hexUrls = valid.map(bech32ToHex)
|
|
const res = await tryCatch(() => postJson(`${base}/zapper/info`, {lnurls: hexUrls}))
|
|
|
|
for (const {lnurl, info} of res?.data || []) {
|
|
addZapper(hexToBech32("lnurl", lnurl), info)
|
|
}
|
|
} else {
|
|
await Promise.all(
|
|
valid.map(async lnurl => {
|
|
addZapper(lnurl, await tryCatch(() => fetchJson(bech32ToHex(lnurl))))
|
|
}),
|
|
)
|
|
}
|
|
|
|
if (result.size > 0) {
|
|
zappersByLnurl.update($zappersByLnurl => {
|
|
for (const [lnurl, zapper] of result) {
|
|
$zappersByLnurl.set(lnurl, zapper)
|
|
}
|
|
|
|
return $zappersByLnurl
|
|
})
|
|
|
|
for (const zapper of result.values()) {
|
|
notifyZapper(zapper)
|
|
}
|
|
}
|
|
|
|
return lnurls.map(lnurl => result.get(lnurl))
|
|
})
|
|
|
|
export const forceLoadZapper = makeForceLoadItem(fetchZapper, getZapper)
|
|
|
|
export const loadZapper = makeLoadItem(fetchZapper, getZapper)
|
|
|
|
export const deriveZapper = makeDeriveItem(zappersByLnurl, loadZapper)
|
|
|
|
export const loadZapperForPubkey = async (pubkey: string, relays: string[] = []) => {
|
|
const $profile = await loadProfile(pubkey, relays)
|
|
|
|
return $profile?.lnurl ? loadZapper($profile.lnurl) : undefined
|
|
}
|
|
|
|
export const deriveZapperForPubkey = (pubkey: string, relays: string[] = []) => {
|
|
loadZapperForPubkey(pubkey, relays)
|
|
|
|
return deriveDeduplicated(
|
|
[zappersByLnurl, deriveProfile(pubkey, relays)],
|
|
([$zappersByLnurl, $profile]) => {
|
|
return $profile?.lnurl ? $zappersByLnurl.get($profile.lnurl) : undefined
|
|
},
|
|
)
|
|
}
|
|
|
|
export const getLnUrlsForEvent = async (event: TrustedEvent) => {
|
|
const pubkeys = getTagValues("zap", event.tags)
|
|
|
|
if (pubkeys.length > 0) {
|
|
const profiles = await Promise.all(pubkeys.map(pubkey => loadProfile(pubkey)))
|
|
const lnurls = removeUndefined(profiles.map(profile => profile?.lnurl))
|
|
|
|
if (lnurls.length > 0) {
|
|
return lnurls
|
|
}
|
|
}
|
|
|
|
const profile = await loadProfile(event.pubkey)
|
|
|
|
return removeUndefined([profile?.lnurl])
|
|
}
|
|
|
|
export const getZapperForZap = async (zap: TrustedEvent, parent: TrustedEvent) => {
|
|
const lnurls = await getLnUrlsForEvent(parent)
|
|
|
|
return lnurls.length > 0 ? loadZapper(lnurls[0]) : undefined
|
|
}
|
|
|
|
export const getValidZap = async (zap: TrustedEvent, parent: TrustedEvent) => {
|
|
const zapper = await getZapperForZap(zap, parent)
|
|
|
|
return zapper ? zapFromEvent(zap, zapper) : undefined
|
|
}
|
|
|
|
export const getValidZaps = async (zaps: TrustedEvent[], parent: TrustedEvent) =>
|
|
removeUndefined(await Promise.all(zaps.map(zap => getValidZap(zap, parent))))
|
|
|
|
export const deriveValidZaps = (zaps: TrustedEvent[], parent: TrustedEvent) => {
|
|
const store = writable<Zap[]>([])
|
|
|
|
getValidZaps(zaps, parent).then(validZaps => {
|
|
store.set(validZaps)
|
|
})
|
|
|
|
return store
|
|
}
|