multiple zapping issues resolved for confirming and showing active states of zaps #36
@ -1,4 +1,4 @@
|
||||
import { Event, Filter, kinds, Relay } from 'nostr-tools'
|
||||
import { Event, Filter, kinds, nip57, Relay } from 'nostr-tools'
|
||||
import {
|
||||
extractZapAmount,
|
||||
log,
|
||||
@ -600,10 +600,8 @@ export class RelayController {
|
||||
const processedEvents: string[] = [] // To keep track of processed events
|
||||
|
||||
// Create a promise for each relay subscription
|
||||
const subPromises = relays.map((relay) => {
|
||||
return new Promise<void>((resolve) => {
|
||||
// Subscribe to the relay with the specified filter
|
||||
const sub = relay.subscribe([filter], {
|
||||
const subscriptions = relays.map((relay) =>
|
||||
relay.subscribe([filter], {
|
||||
// Handle incoming events
|
||||
onevent: (e) => {
|
||||
// Process event only if it hasn't been processed before
|
||||
@ -611,18 +609,11 @@ export class RelayController {
|
||||
processedEvents.push(e.id)
|
||||
eventHandler(e) // Call the event handler with the event
|
||||
}
|
||||
},
|
||||
// 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 subscriptions
|
||||
}
|
||||
|
||||
getTotalZapAmount = async (
|
||||
@ -638,53 +629,90 @@ export class RelayController {
|
||||
UserRelaysType.Read
|
||||
)
|
||||
|
||||
// add app relay to relays array
|
||||
relayUrls.push(import.meta.env.VITE_APP_RELAY)
|
||||
|
||||
// add admin relays to relays array
|
||||
metadataController.adminRelays.forEach((url) => {
|
||||
relayUrls.push(url)
|
||||
})
|
||||
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)
|
||||
)
|
||||
await Promise.allSettled(relayPromises)
|
||||
|
||||
// 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<Relay[]>((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<string>() // To keep track of event IDs and avoid duplicates
|
||||
|
||||
const filter: Filter = {
|
||||
kinds: [kinds.Zap]
|
||||
const filters: Filter[] = [
|
||||
{
|
||||
kinds: [kinds.Zap],
|
||||
'#e': [eTag]
|
||||
}
|
||||
]
|
||||
|
||||
if (aTag) {
|
||||
filter['#a'] = [aTag]
|
||||
} else {
|
||||
filter['#e'] = [eTag]
|
||||
filters.push({
|
||||
kinds: [kinds.Zap],
|
||||
'#a': [aTag]
|
||||
})
|
||||
}
|
||||
|
||||
// Create a promise for each relay subscription
|
||||
const subPromises = this.connectedRelays.map((relay) => {
|
||||
const subPromises = relays.map((relay) => {
|
||||
return new Promise<void>((resolve) => {
|
||||
// Subscribe to the relay with the specified filter
|
||||
const sub = relay.subscribe([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 amount = extractZapAmount(e)
|
||||
|
||||
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 =
|
||||
e.tags.findIndex(
|
||||
(tag) => tag[0] === 'P' && tag[1] === currentLoggedInUser
|
||||
) > -1
|
||||
hasZapped = zapRequest.pubkey === currentLoggedInUser
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -12,6 +12,7 @@ import {
|
||||
} from '../types'
|
||||
import { log, LogType, npubToHex } from '../utils'
|
||||
import { RelayController } from './relay'
|
||||
import { MetadataController, UserRelaysType } from './metadata'
|
||||
|
||||
/**
|
||||
* Singleton class to manage zap related operations.
|
||||
@ -147,7 +148,7 @@ export class ZapController {
|
||||
const cleanup = () => {
|
||||
clearTimeout(timeout)
|
||||
|
||||
sub.close()
|
||||
subscriptions.forEach((subscription) => subscription.close())
|
||||
}
|
||||
|
||||
// Polling timeout
|
||||
@ -160,13 +161,11 @@ export class ZapController {
|
||||
pollingTimeout || 6 * 60 * 1000 // 6 minutes
|
||||
)
|
||||
|
||||
const relay = await RelayController.getInstance().connectRelay(
|
||||
this.appRelay
|
||||
)
|
||||
const relaysTag = zapRequest.tags.find((t) => t[0] === 'relays')
|
||||
if (!relaysTag)
|
||||
throw new Error('Zap request does not contain relays tag.')
|
||||
|
||||
if (!relay) {
|
||||
return reject('Polling Zap Receipt: Could not connect to app relay!')
|
||||
}
|
||||
const relayUrls = relaysTag.slice(1)
|
||||
|
||||
// filter relay for event of kind 9735
|
||||
const filter: Filter = {
|
||||
@ -174,9 +173,11 @@ export class ZapController {
|
||||
since: created_at
|
||||
}
|
||||
|
||||
const sub = relay.subscribe([filter], {
|
||||
// Handle incoming events
|
||||
onevent: async (event) => {
|
||||
const subscriptions =
|
||||
await RelayController.getInstance().subscribeForEvents(
|
||||
filter,
|
||||
relayUrls,
|
||||
async (event) => {
|
||||
// get description tag of the event
|
||||
const description = event.tags.filter(
|
||||
(tag) => tag[0] === 'description'
|
||||
@ -192,7 +193,7 @@ export class ZapController {
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@ -280,11 +281,21 @@ export class ZapController {
|
||||
|
||||
if (!recipientHexKey) throw 'Invalid recipient pubKey.'
|
||||
|
||||
const metadataController = await MetadataController.getInstance()
|
||||
const receiverReadRelays = await metadataController.findUserRelays(
|
||||
recipientHexKey,
|
||||
UserRelaysType.Read
|
||||
)
|
||||
|
||||
if (!receiverReadRelays.includes(this.appRelay)) {
|
||||
receiverReadRelays.push(this.appRelay)
|
||||
}
|
||||
|
||||
const zapRequest: ZapRequest = {
|
||||
kind: kinds.ZapRequest,
|
||||
content,
|
||||
tags: [
|
||||
['relays', `${this.appRelay}`],
|
||||
['relays', ...receiverReadRelays],
|
||||
['amount', `${amount}`],
|
||||
['p', recipientHexKey]
|
||||
],
|
||||
|
@ -104,31 +104,13 @@ export const npubToHex = (pubKey: string): string | null => {
|
||||
* @returns The zap amount in the form of a number, converted from the extracted data, or 0 if the amount cannot be determined.
|
||||
*/
|
||||
export const extractZapAmount = (event: Event): number => {
|
||||
// Find the 'description' tag within the event's tags
|
||||
const description = event.tags.find(
|
||||
(tag) => tag[0] === 'description' && typeof tag[1] === 'string'
|
||||
)
|
||||
|
||||
// If the 'description' tag is found and it has a valid value
|
||||
if (description && description[1]) {
|
||||
try {
|
||||
// Parse the description as JSON to get additional details
|
||||
const parsedDescription: Event = JSON.parse(description[1])
|
||||
|
||||
// Find the 'amount' tag within the parsed description's tags
|
||||
const amountTag = parsedDescription.tags.find(
|
||||
const amountTag = event.tags.find(
|
||||
(tag) => tag[0] === 'amount' && typeof tag[1] === 'string'
|
||||
)
|
||||
|
||||
// If the 'amount' tag is found and it has a valid value, convert it to an integer and return
|
||||
if (amountTag && amountTag[1]) return parseInt(amountTag[1]) / 1000
|
||||
} catch (error) {
|
||||
// Log an error message if JSON parsing fails
|
||||
console.log(
|
||||
`An error occurred while parsing description of zap event: ${error}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Return 0 if the zap amount cannot be determined
|
||||
return 0
|
||||
|
Loading…
Reference in New Issue
Block a user