chore: process received events all together instead of one by one which casuses in-consistencies due to async nature of redux updates
All checks were successful
Open PR on Staging / audit_and_check (pull_request) Successful in 44s
All checks were successful
Open PR on Staging / audit_and_check (pull_request) Successful in 44s
This commit is contained in:
parent
c4c0ecba4a
commit
3a09d4c595
@ -27,17 +27,25 @@ import {
|
||||
|
||||
export interface NDKContextType {
|
||||
ndk: NDK
|
||||
fetchEvents: (filter: NDKFilter) => Promise<NDKEvent[]>
|
||||
fetchEvent: (filter: NDKFilter) => Promise<NDKEvent | null>
|
||||
fetchEvents: (
|
||||
filter: NDKFilter,
|
||||
opts?: NDKSubscriptionOptions
|
||||
) => Promise<NDKEvent[]>
|
||||
fetchEvent: (
|
||||
filter: NDKFilter,
|
||||
opts?: NDKSubscriptionOptions
|
||||
) => Promise<NDKEvent | null>
|
||||
fetchEventsFromUserRelays: (
|
||||
filter: NDKFilter | NDKFilter[],
|
||||
hexKey: string,
|
||||
userRelaysType: UserRelaysType
|
||||
userRelaysType: UserRelaysType,
|
||||
opts?: NDKSubscriptionOptions
|
||||
) => Promise<NDKEvent[]>
|
||||
fetchEventFromUserRelays: (
|
||||
filter: NDKFilter | NDKFilter[],
|
||||
hexKey: string,
|
||||
userRelaysType: UserRelaysType
|
||||
userRelaysType: UserRelaysType,
|
||||
opts?: NDKSubscriptionOptions
|
||||
) => Promise<NDKEvent | null>
|
||||
findMetadata: (
|
||||
pubkey: string,
|
||||
@ -67,8 +75,8 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
|
||||
}, [])
|
||||
|
||||
const ndk = useMemo(() => {
|
||||
localStorage.setItem('debug', '*')
|
||||
const dexieAdapter = new NDKCacheAdapterDexie({ dbName: 'degmod-db' })
|
||||
// localStorage.setItem('debug', '*')
|
||||
const dexieAdapter = new NDKCacheAdapterDexie({ dbName: 'sigit-db' })
|
||||
dexieAdapter.locking = true
|
||||
const ndk = new NDK({
|
||||
enableOutboxModel: true,
|
||||
@ -88,11 +96,15 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
|
||||
* @param filter - The filter criteria to find the event.
|
||||
* @returns Returns a promise that resolves to the found event or null if not found.
|
||||
*/
|
||||
const fetchEvents = async (filter: NDKFilter): Promise<NDKEvent[]> => {
|
||||
const fetchEvents = async (
|
||||
filter: NDKFilter,
|
||||
opts?: NDKSubscriptionOptions
|
||||
): Promise<NDKEvent[]> => {
|
||||
return ndk
|
||||
.fetchEvents(filter, {
|
||||
closeOnEose: true,
|
||||
cacheUsage: NDKSubscriptionCacheUsage.PARALLEL
|
||||
cacheUsage: NDKSubscriptionCacheUsage.PARALLEL,
|
||||
...opts
|
||||
})
|
||||
.then((ndkEventSet) => {
|
||||
const ndkEvents = Array.from(ndkEventSet)
|
||||
@ -112,8 +124,11 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
|
||||
* @param filter - The filter criteria to find the event.
|
||||
* @returns Returns a promise that resolves to the found event or null if not found.
|
||||
*/
|
||||
const fetchEvent = async (filter: NDKFilter) => {
|
||||
const events = await fetchEvents(filter)
|
||||
const fetchEvent = async (
|
||||
filter: NDKFilter,
|
||||
opts?: NDKSubscriptionOptions
|
||||
) => {
|
||||
const events = await fetchEvents(filter, opts)
|
||||
if (events.length === 0) return null
|
||||
return events[0]
|
||||
}
|
||||
@ -130,7 +145,8 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
|
||||
const fetchEventsFromUserRelays = async (
|
||||
filter: NDKFilter | NDKFilter[],
|
||||
hexKey: string,
|
||||
userRelaysType: UserRelaysType
|
||||
userRelaysType: UserRelaysType,
|
||||
opts?: NDKSubscriptionOptions
|
||||
): Promise<NDKEvent[]> => {
|
||||
// Find the user's relays (10s timeout).
|
||||
const relayUrls = await Promise.race([
|
||||
@ -156,7 +172,11 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
|
||||
return ndk
|
||||
.fetchEvents(
|
||||
filter,
|
||||
{ closeOnEose: true, cacheUsage: NDKSubscriptionCacheUsage.PARALLEL },
|
||||
{
|
||||
closeOnEose: true,
|
||||
cacheUsage: NDKSubscriptionCacheUsage.PARALLEL,
|
||||
...opts
|
||||
},
|
||||
relayUrls.length
|
||||
? NDKRelaySet.fromRelayUrls(relayUrls, ndk, true)
|
||||
: undefined
|
||||
@ -185,12 +205,14 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
|
||||
const fetchEventFromUserRelays = async (
|
||||
filter: NDKFilter | NDKFilter[],
|
||||
hexKey: string,
|
||||
userRelaysType: UserRelaysType
|
||||
userRelaysType: UserRelaysType,
|
||||
opts?: NDKSubscriptionOptions
|
||||
) => {
|
||||
const events = await fetchEventsFromUserRelays(
|
||||
filter,
|
||||
hexKey,
|
||||
userRelaysType
|
||||
userRelaysType,
|
||||
opts
|
||||
)
|
||||
if (events.length === 0) return null
|
||||
return events[0]
|
||||
|
@ -2,7 +2,13 @@ import { useCallback } from 'react'
|
||||
import { toast } from 'react-toastify'
|
||||
|
||||
import { bytesToHex } from '@noble/hashes/utils'
|
||||
import { NDKEvent, NDKFilter, NDKKind, NDKRelaySet } from '@nostr-dev-kit/ndk'
|
||||
import {
|
||||
NDKEvent,
|
||||
NDKFilter,
|
||||
NDKKind,
|
||||
NDKRelaySet,
|
||||
NDKSubscriptionCacheUsage
|
||||
} from '@nostr-dev-kit/ndk'
|
||||
import _ from 'lodash'
|
||||
import {
|
||||
Event,
|
||||
@ -74,7 +80,9 @@ export const useNDK = () => {
|
||||
'#d': [dTag]
|
||||
}
|
||||
|
||||
const encryptedContent = await fetchEvent(filter)
|
||||
const encryptedContent = await fetchEvent(filter, {
|
||||
cacheUsage: NDKSubscriptionCacheUsage.ONLY_RELAY
|
||||
})
|
||||
.then((event) => {
|
||||
if (event) return event.content
|
||||
|
||||
@ -171,38 +179,40 @@ export const useNDK = () => {
|
||||
}, [usersPubkey, fetchEvent])
|
||||
|
||||
const updateUsersAppData = useCallback(
|
||||
async (meta: Meta) => {
|
||||
async (metaArray: Meta[]) => {
|
||||
if (!appData || !appData.keyPair || !usersPubkey) return null
|
||||
|
||||
const sigits = _.cloneDeep(appData.sigits)
|
||||
|
||||
const createSignatureEvent = await parseJson<Event>(
|
||||
meta.createSignature
|
||||
).catch((err) => {
|
||||
console.log('err in parsing the createSignature event:>> ', err)
|
||||
toast.error(
|
||||
err.message || 'error occurred in parsing the create signature event'
|
||||
)
|
||||
return null
|
||||
})
|
||||
|
||||
if (!createSignatureEvent) return null
|
||||
|
||||
const id = createSignatureEvent.id
|
||||
let isUpdated = false
|
||||
|
||||
// check if sigit already exists
|
||||
if (id in sigits) {
|
||||
// update meta only if incoming meta is more recent
|
||||
// than already existing one
|
||||
const existingMeta = sigits[id]
|
||||
if (existingMeta.modifiedAt < meta.modifiedAt) {
|
||||
for (const meta of metaArray) {
|
||||
const createSignatureEvent = await parseJson<Event>(
|
||||
meta.createSignature
|
||||
).catch((err) => {
|
||||
console.log('Error in parsing the createSignature event:', err)
|
||||
toast.error(
|
||||
err.message ||
|
||||
'Error occurred in parsing the create signature event'
|
||||
)
|
||||
return null
|
||||
})
|
||||
|
||||
if (!createSignatureEvent) continue
|
||||
|
||||
const id = createSignatureEvent.id
|
||||
|
||||
// Check if sigit already exists
|
||||
if (id in sigits) {
|
||||
// Update meta only if incoming meta is more recent
|
||||
const existingMeta = sigits[id]
|
||||
if (existingMeta.modifiedAt < meta.modifiedAt) {
|
||||
sigits[id] = meta
|
||||
isUpdated = true
|
||||
}
|
||||
} else {
|
||||
sigits[id] = meta
|
||||
isUpdated = true
|
||||
}
|
||||
} else {
|
||||
sigits[id] = meta
|
||||
isUpdated = true
|
||||
}
|
||||
|
||||
if (!isUpdated) return null
|
||||
@ -215,34 +225,31 @@ export const useNDK = () => {
|
||||
appData.keyPair.private
|
||||
).catch((err) => {
|
||||
console.log(
|
||||
'An error occurred in uploading user app data file to blossom server',
|
||||
'Error uploading user app data file to Blossom server:',
|
||||
err
|
||||
)
|
||||
toast.error(
|
||||
'An error occurred in uploading user app data file to blossom server'
|
||||
'Error occurred in uploading user app data file to Blossom server'
|
||||
)
|
||||
return null
|
||||
})
|
||||
|
||||
if (!newBlossomUrl) return null
|
||||
|
||||
// insert new blossom url at the start of the array
|
||||
// Insert new blossom URL at the start of the array
|
||||
blossomUrls.unshift(newBlossomUrl)
|
||||
|
||||
// only keep last 10 blossom urls, delete older ones
|
||||
// Keep only the last 10 Blossom URLs, delete older ones
|
||||
if (blossomUrls.length > 10) {
|
||||
const filesToDelete = blossomUrls.splice(10)
|
||||
filesToDelete.forEach((url) => {
|
||||
deleteBlossomFile(url, appData.keyPair!.private).catch((err) => {
|
||||
console.log(
|
||||
'An error occurred in removing old file of user app data from blossom server',
|
||||
err
|
||||
)
|
||||
console.log('Error removing old file from Blossom server:', err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// encrypt content for storing in kind 30078 event
|
||||
// Encrypt content for storing in kind 30078 event
|
||||
const nostrController = NostrController.getInstance()
|
||||
const encryptedContent = await nostrController
|
||||
.nip04Encrypt(
|
||||
@ -253,20 +260,14 @@ export const useNDK = () => {
|
||||
})
|
||||
)
|
||||
.catch((err) => {
|
||||
console.log(
|
||||
'An error occurred in encryption of content for app data',
|
||||
err
|
||||
)
|
||||
toast.error(
|
||||
err.message ||
|
||||
'An error occurred in encryption of content for app data'
|
||||
)
|
||||
console.log('Error encrypting content for app data:', err)
|
||||
toast.error(err.message || 'Error encrypting content for app data')
|
||||
return null
|
||||
})
|
||||
|
||||
if (!encryptedContent) return null
|
||||
|
||||
// generate the identifier for user's appData event
|
||||
// Generate the identifier for user's appData event
|
||||
const dTag = await getDTagForUserAppData()
|
||||
if (!dTag) return null
|
||||
|
||||
@ -281,8 +282,8 @@ export const useNDK = () => {
|
||||
const signedEvent = await nostrController
|
||||
.signEvent(updatedEvent)
|
||||
.catch((err) => {
|
||||
console.log('An error occurred in signing event', err)
|
||||
toast.error(err.message || 'An error occurred in signing event')
|
||||
console.log('Error signing event:', err)
|
||||
toast.error(err.message || 'Error signing event')
|
||||
return null
|
||||
})
|
||||
|
||||
@ -291,16 +292,14 @@ export const useNDK = () => {
|
||||
const ndkEvent = new NDKEvent(ndk, signedEvent)
|
||||
const publishResult = await publish(ndkEvent)
|
||||
|
||||
if (publishResult.length === 0) {
|
||||
toast.error(
|
||||
'An unexpected error occurred in publishing updated app data '
|
||||
)
|
||||
if (publishResult.length === 0 || !publishResult) {
|
||||
toast.error('Unexpected error occurred in publishing updated app data')
|
||||
return null
|
||||
}
|
||||
|
||||
if (!publishResult) return null
|
||||
console.count('updateUserAppData useNDK')
|
||||
|
||||
// update redux store
|
||||
// Update Redux store
|
||||
dispatch(
|
||||
updateUserAppDataAction({
|
||||
sigits,
|
||||
@ -317,94 +316,103 @@ export const useNDK = () => {
|
||||
[appData, dispatch, ndk, publish, usersPubkey]
|
||||
)
|
||||
|
||||
const processReceivedEvent = useCallback(
|
||||
async (event: NDKEvent, difficulty: number = 5) => {
|
||||
// Abort processing if userAppData is undefined
|
||||
const processReceivedEvents = useCallback(
|
||||
async (events: NDKEvent[], difficulty: number = 5) => {
|
||||
if (!processedEvents) return
|
||||
|
||||
if (processedEvents.includes(event.id)) return
|
||||
const validMetaArray: Meta[] = [] // Array to store valid Meta objects
|
||||
const updatedProcessedEvents = [...processedEvents] // Keep track of processed event IDs
|
||||
|
||||
dispatch(updateProcessedGiftWraps([...processedEvents, event.id]))
|
||||
for (const event of events) {
|
||||
// Skip already processed events
|
||||
if (processedEvents.includes(event.id)) continue
|
||||
|
||||
// validate PoW
|
||||
// Count the number of leading zero bits in the hash
|
||||
const leadingZeroes = countLeadingZeroes(event.id)
|
||||
if (leadingZeroes < difficulty) return
|
||||
// Validate PoW
|
||||
const leadingZeroes = countLeadingZeroes(event.id)
|
||||
if (leadingZeroes < difficulty) continue
|
||||
|
||||
// decrypt the content of gift wrap event
|
||||
const nostrController = NostrController.getInstance()
|
||||
const decrypted = await nostrController.nip44Decrypt(
|
||||
event.pubkey,
|
||||
event.content
|
||||
)
|
||||
// Decrypt the content of the gift wrap event
|
||||
const nostrController = NostrController.getInstance()
|
||||
const decrypted = await nostrController
|
||||
.nip44Decrypt(event.pubkey, event.content)
|
||||
.catch((err) => {
|
||||
console.log('An error occurred in decrypting event content', err)
|
||||
return null
|
||||
})
|
||||
|
||||
const internalUnsignedEvent = await parseJson<UnsignedEvent>(
|
||||
decrypted
|
||||
).catch((err) => {
|
||||
console.log(
|
||||
'An error occurred in parsing the internal unsigned event',
|
||||
err
|
||||
)
|
||||
return null
|
||||
})
|
||||
if (!decrypted) continue
|
||||
|
||||
if (!internalUnsignedEvent || internalUnsignedEvent.kind !== 938) return
|
||||
|
||||
const parsedContent = await parseJson<Meta | SigitNotification>(
|
||||
internalUnsignedEvent.content
|
||||
).catch((err) => {
|
||||
console.log(
|
||||
'An error occurred in parsing the internal unsigned event',
|
||||
err
|
||||
)
|
||||
return null
|
||||
})
|
||||
|
||||
if (!parsedContent) return
|
||||
|
||||
let meta: Meta
|
||||
|
||||
if (isSigitNotification(parsedContent)) {
|
||||
const notification = parsedContent
|
||||
if (!notification.keys || !usersPubkey) return
|
||||
|
||||
let encryptionKey: string | undefined
|
||||
|
||||
const { sender, keys } = notification.keys
|
||||
|
||||
// Retrieve the user's public key from the state
|
||||
const usersNpub = hexToNpub(usersPubkey)
|
||||
|
||||
// Check if the user's public key is in the keys object
|
||||
if (usersNpub in keys) {
|
||||
// Instantiate the NostrController to decrypt the encryption key
|
||||
const nostrController = NostrController.getInstance()
|
||||
const decrypted = await nostrController
|
||||
.nip04Decrypt(sender, keys[usersNpub])
|
||||
.catch((err) => {
|
||||
console.log('An error occurred in decrypting encryption key', err)
|
||||
return undefined
|
||||
})
|
||||
|
||||
encryptionKey = decrypted
|
||||
}
|
||||
try {
|
||||
meta = await fetchMetaFromFileStorage(
|
||||
notification.metaUrl,
|
||||
encryptionKey
|
||||
const internalUnsignedEvent = await parseJson<UnsignedEvent>(
|
||||
decrypted
|
||||
).catch((err) => {
|
||||
console.log(
|
||||
'An error occurred in parsing the internal unsigned event',
|
||||
err
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`An error occured fetching meta file from storage`,
|
||||
error
|
||||
)
|
||||
return
|
||||
return null
|
||||
})
|
||||
|
||||
if (!internalUnsignedEvent || internalUnsignedEvent.kind !== 938)
|
||||
continue
|
||||
|
||||
const parsedContent = await parseJson<Meta | SigitNotification>(
|
||||
internalUnsignedEvent.content
|
||||
).catch((err) => {
|
||||
console.log('An error occurred in parsing event content', err)
|
||||
return null
|
||||
})
|
||||
|
||||
if (!parsedContent) continue
|
||||
|
||||
let meta: Meta
|
||||
|
||||
if (isSigitNotification(parsedContent)) {
|
||||
const notification = parsedContent
|
||||
if (!notification.keys || !usersPubkey) continue
|
||||
|
||||
let encryptionKey: string | undefined
|
||||
const { sender, keys } = notification.keys
|
||||
const usersNpub = hexToNpub(usersPubkey)
|
||||
|
||||
if (usersNpub in keys) {
|
||||
encryptionKey = await nostrController
|
||||
.nip04Decrypt(sender, keys[usersNpub])
|
||||
.catch((err) => {
|
||||
console.log(
|
||||
'An error occurred in decrypting encryption key',
|
||||
err
|
||||
)
|
||||
return undefined
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
meta = await fetchMetaFromFileStorage(
|
||||
notification.metaUrl,
|
||||
encryptionKey
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'An error occurred fetching meta file from storage',
|
||||
error
|
||||
)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
meta = parsedContent
|
||||
}
|
||||
} else {
|
||||
meta = parsedContent
|
||||
|
||||
validMetaArray.push(meta) // Add valid Meta to the array
|
||||
updatedProcessedEvents.push(event.id) // Mark event as processed
|
||||
}
|
||||
|
||||
await updateUsersAppData(meta)
|
||||
// Update processed events in the Redux store
|
||||
dispatch(updateProcessedGiftWraps(updatedProcessedEvents))
|
||||
|
||||
// Pass the array of Meta objects to updateUsersAppData
|
||||
if (validMetaArray.length > 0) {
|
||||
await updateUsersAppData(validMetaArray)
|
||||
}
|
||||
},
|
||||
[dispatch, processedEvents, updateUsersAppData, usersPubkey]
|
||||
)
|
||||
@ -421,14 +429,15 @@ export const useNDK = () => {
|
||||
const events = await fetchEventsFromUserRelays(
|
||||
filter,
|
||||
pubkey,
|
||||
UserRelaysType.Read
|
||||
UserRelaysType.Read,
|
||||
{
|
||||
cacheUsage: NDKSubscriptionCacheUsage.ONLY_RELAY
|
||||
}
|
||||
)
|
||||
|
||||
for (const e of events) {
|
||||
await processReceivedEvent(e)
|
||||
}
|
||||
await processReceivedEvents(events)
|
||||
},
|
||||
[fetchEventsFromUserRelays, processReceivedEvent]
|
||||
[fetchEventsFromUserRelays, processReceivedEvents]
|
||||
)
|
||||
|
||||
/**
|
||||
|
@ -904,7 +904,7 @@ export const CreatePage = () => {
|
||||
|
||||
setLoadingSpinnerDesc('Updating user app data')
|
||||
|
||||
const event = await updateUsersAppData(meta)
|
||||
const event = await updateUsersAppData([meta])
|
||||
if (!event) return
|
||||
|
||||
const metaUrl = await uploadMetaToFileStorage(meta, encryptionKey)
|
||||
|
@ -58,7 +58,7 @@ export const HomePage = () => {
|
||||
const usersAppData = useAppSelector((state) => state.userAppData)
|
||||
|
||||
useEffect(() => {
|
||||
if (usersAppData) {
|
||||
if (usersAppData?.sigits) {
|
||||
const getSigitInfo = async () => {
|
||||
const parsedSigits: { [key: string]: SigitCardDisplayInfo } = {}
|
||||
for (const key in usersAppData.sigits) {
|
||||
@ -80,7 +80,7 @@ export const HomePage = () => {
|
||||
setSigits(usersAppData.sigits)
|
||||
getSigitInfo()
|
||||
}
|
||||
}, [usersAppData])
|
||||
}, [usersAppData?.sigits])
|
||||
|
||||
const onDrop = useCallback(
|
||||
async (acceptedFiles: File[]) => {
|
||||
|
@ -646,7 +646,7 @@ export const SignPage = () => {
|
||||
encryptionKey: string | undefined
|
||||
) => {
|
||||
setLoadingSpinnerDesc('Updating users app data')
|
||||
const updatedEvent = await updateUsersAppData(meta)
|
||||
const updatedEvent = await updateUsersAppData([meta])
|
||||
if (!updatedEvent) {
|
||||
setIsLoading(false)
|
||||
return
|
||||
|
@ -353,7 +353,7 @@ export const VerifyPage = () => {
|
||||
updatedMeta.timestamps = [...finalTimestamps]
|
||||
updatedMeta.modifiedAt = unixNow()
|
||||
|
||||
const updatedEvent = await updateUsersAppData(updatedMeta)
|
||||
const updatedEvent = await updateUsersAppData([updatedMeta])
|
||||
if (!updatedEvent) return
|
||||
|
||||
const metaUrl = await uploadMetaToFileStorage(
|
||||
|
Loading…
Reference in New Issue
Block a user