perf: improve loading speed (#116)
This commit is contained in:
@@ -2,7 +2,7 @@ import { BIG_RELAY_URLS } from '@/constants'
|
||||
import { checkAlgoRelay } from '@/lib/relay'
|
||||
import { isWebsocketUrl, normalizeUrl } from '@/lib/url'
|
||||
import client from '@/services/client.service'
|
||||
import storage from '@/services/storage.service'
|
||||
import storage from '@/services/local-storage.service'
|
||||
import relayInfoService from '@/services/relay-info.service'
|
||||
import { TFeedType } from '@/types'
|
||||
import { Filter } from 'nostr-tools'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { createFollowListDraftEvent } from '@/lib/draft-event'
|
||||
import { extractPubkeysFromEventTags } from '@/lib/tag'
|
||||
import client from '@/services/client.service'
|
||||
import storage from '@/services/storage.service'
|
||||
import { Event } from 'nostr-tools'
|
||||
import indexedDb from '@/services/indexed-db.service'
|
||||
import { Event, kinds } from 'nostr-tools'
|
||||
import { createContext, useContext, useEffect, useMemo, useState } from 'react'
|
||||
import { useNostr } from './NostrProvider'
|
||||
|
||||
@@ -40,13 +40,16 @@ export function FollowListProvider({ children }: { children: React.ReactNode })
|
||||
const init = async () => {
|
||||
setIsFetching(true)
|
||||
setFollowListEvent(undefined)
|
||||
const storedFollowListEvent = storage.getAccountFollowListEvent(accountPubkey)
|
||||
const storedFollowListEvent = await indexedDb.getReplaceableEvent(
|
||||
accountPubkey,
|
||||
kinds.Contacts
|
||||
)
|
||||
if (storedFollowListEvent) {
|
||||
setFollowListEvent(storedFollowListEvent)
|
||||
}
|
||||
const event = await client.fetchFollowListEvent(accountPubkey)
|
||||
const event = await client.fetchFollowListEvent(accountPubkey, true)
|
||||
if (event) {
|
||||
updateFollowListEvent(event)
|
||||
await updateFollowListEvent(event)
|
||||
}
|
||||
setIsFetching(false)
|
||||
}
|
||||
@@ -54,8 +57,8 @@ export function FollowListProvider({ children }: { children: React.ReactNode })
|
||||
init()
|
||||
}, [accountPubkey])
|
||||
|
||||
const updateFollowListEvent = (event: Event) => {
|
||||
const isNew = storage.setAccountFollowListEvent(event)
|
||||
const updateFollowListEvent = async (event: Event) => {
|
||||
const isNew = await indexedDb.putReplaceableEvent(event)
|
||||
if (!isNew) return
|
||||
setFollowListEvent(event)
|
||||
}
|
||||
@@ -69,7 +72,7 @@ export function FollowListProvider({ children }: { children: React.ReactNode })
|
||||
)
|
||||
const newFollowListEvent = await publish(newFollowListDraftEvent)
|
||||
client.updateFollowListCache(accountPubkey, newFollowListEvent)
|
||||
updateFollowListEvent(newFollowListEvent)
|
||||
await updateFollowListEvent(newFollowListEvent)
|
||||
}
|
||||
|
||||
const unfollow = async (pubkey: string) => {
|
||||
@@ -81,11 +84,11 @@ export function FollowListProvider({ children }: { children: React.ReactNode })
|
||||
)
|
||||
const newFollowListEvent = await publish(newFollowListDraftEvent)
|
||||
client.updateFollowListCache(accountPubkey, newFollowListEvent)
|
||||
updateFollowListEvent(newFollowListEvent)
|
||||
await updateFollowListEvent(newFollowListEvent)
|
||||
}
|
||||
|
||||
const getFollowings = async (pubkey: string) => {
|
||||
const followListEvent = storage.getAccountFollowListEvent(pubkey)
|
||||
const followListEvent = await indexedDb.getReplaceableEvent(pubkey, kinds.Contacts)
|
||||
if (followListEvent) {
|
||||
return extractPubkeysFromEventTags(followListEvent.tags)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createMuteListDraftEvent } from '@/lib/draft-event'
|
||||
import { getLatestEvent } from '@/lib/event'
|
||||
import { extractPubkeysFromEventTags, isSameTag } from '@/lib/tag'
|
||||
import client from '@/services/client.service'
|
||||
import storage from '@/services/storage.service'
|
||||
import indexedDb from '@/services/indexed-db.service'
|
||||
import { Event, kinds } from 'nostr-tools'
|
||||
import { createContext, useContext, useEffect, useMemo, useState } from 'react'
|
||||
import { z } from 'zod'
|
||||
@@ -35,7 +35,7 @@ export function MuteListProvider({ children }: { children: React.ReactNode }) {
|
||||
|
||||
const init = async () => {
|
||||
setMuteListEvent(undefined)
|
||||
const storedMuteListEvent = storage.getAccountMuteListEvent(accountPubkey)
|
||||
const storedMuteListEvent = await indexedDb.getReplaceableEvent(accountPubkey, kinds.Mutelist)
|
||||
if (storedMuteListEvent) {
|
||||
setMuteListEvent(storedMuteListEvent)
|
||||
const tags = await extractMuteTags(storedMuteListEvent)
|
||||
@@ -47,6 +47,7 @@ export function MuteListProvider({ children }: { children: React.ReactNode }) {
|
||||
})
|
||||
const muteEvent = getLatestEvent(events) as Event | undefined
|
||||
if (muteEvent) {
|
||||
await indexedDb.putReplaceableEvent(muteEvent)
|
||||
setMuteListEvent(muteEvent)
|
||||
const tags = await extractMuteTags(muteEvent)
|
||||
setTags(tags)
|
||||
@@ -59,7 +60,7 @@ export function MuteListProvider({ children }: { children: React.ReactNode }) {
|
||||
const extractMuteTags = async (muteListEvent: Event) => {
|
||||
const tags = [...muteListEvent.tags]
|
||||
if (muteListEvent.content) {
|
||||
const storedDecryptedTags = storage.getAccountMuteDecryptedTags(muteListEvent)
|
||||
const storedDecryptedTags = await indexedDb.getMuteDecryptedTags(muteListEvent.id)
|
||||
|
||||
if (storedDecryptedTags) {
|
||||
tags.push(...storedDecryptedTags)
|
||||
@@ -67,7 +68,7 @@ export function MuteListProvider({ children }: { children: React.ReactNode }) {
|
||||
try {
|
||||
const plainText = await nip04Decrypt(muteListEvent.pubkey, muteListEvent.content)
|
||||
const contentTags = z.array(z.array(z.string())).parse(JSON.parse(plainText))
|
||||
storage.setAccountMuteDecryptedTags(muteListEvent, contentTags)
|
||||
await indexedDb.putMuteDecryptedTags(muteListEvent.id, contentTags)
|
||||
tags.push(...contentTags.filter((tag) => tags.every((t) => !isSameTag(t, tag))))
|
||||
} catch (error) {
|
||||
console.error('Failed to decrypt mute list content', error)
|
||||
@@ -77,10 +78,10 @@ export function MuteListProvider({ children }: { children: React.ReactNode }) {
|
||||
return tags
|
||||
}
|
||||
|
||||
const update = (event: Event, tags: string[][]) => {
|
||||
const isNew = storage.setAccountMuteListEvent(event)
|
||||
const update = async (event: Event, tags: string[][]) => {
|
||||
const isNew = await indexedDb.putReplaceableEvent(event)
|
||||
if (!isNew) return
|
||||
storage.setAccountMuteDecryptedTags(event, tags)
|
||||
await indexedDb.putMuteDecryptedTags(event.id, tags)
|
||||
setMuteListEvent(event)
|
||||
setTags(tags)
|
||||
}
|
||||
@@ -92,7 +93,7 @@ export function MuteListProvider({ children }: { children: React.ReactNode }) {
|
||||
const cipherText = await nip04Encrypt(accountPubkey, JSON.stringify(newTags))
|
||||
const newMuteListDraftEvent = createMuteListDraftEvent(muteListEvent?.tags ?? [], cipherText)
|
||||
const newMuteListEvent = await publish(newMuteListDraftEvent)
|
||||
update(newMuteListEvent, newTags)
|
||||
await update(newMuteListEvent, newTags)
|
||||
}
|
||||
|
||||
const unmutePubkey = async (pubkey: string) => {
|
||||
@@ -105,7 +106,7 @@ export function MuteListProvider({ children }: { children: React.ReactNode }) {
|
||||
cipherText
|
||||
)
|
||||
const newMuteListEvent = await publish(newMuteListDraftEvent)
|
||||
update(newMuteListEvent, newTags)
|
||||
await update(newMuteListEvent, newTags)
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -4,7 +4,8 @@ import { useToast } from '@/hooks'
|
||||
import { getProfileFromProfileEvent, getRelayListFromRelayListEvent } from '@/lib/event'
|
||||
import { formatPubkey } from '@/lib/pubkey'
|
||||
import client from '@/services/client.service'
|
||||
import storage from '@/services/storage.service'
|
||||
import storage from '@/services/local-storage.service'
|
||||
import indexedDb from '@/services/indexed-db.service'
|
||||
import { ISigner, TAccount, TAccountPointer, TDraftEvent, TProfile, TRelayList } from '@/types'
|
||||
import dayjs from 'dayjs'
|
||||
import { Event, kinds, VerifiedEvent } from 'nostr-tools'
|
||||
@@ -45,8 +46,8 @@ type TNostrContext = {
|
||||
startLogin: () => void
|
||||
checkLogin: <T>(cb?: () => T) => Promise<T | void>
|
||||
getRelayList: (pubkey: string) => Promise<TRelayList>
|
||||
updateRelayListEvent: (relayListEvent: Event) => void
|
||||
updateProfileEvent: (profileEvent: Event) => void
|
||||
updateRelayListEvent: (relayListEvent: Event) => Promise<void>
|
||||
updateProfileEvent: (profileEvent: Event) => Promise<void>
|
||||
}
|
||||
|
||||
const NostrContext = createContext<TNostrContext | undefined>(undefined)
|
||||
@@ -99,64 +100,79 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
setRelayList(null)
|
||||
setProfile(null)
|
||||
setProfileEvent(null)
|
||||
setNsec(null)
|
||||
if (!account) {
|
||||
return
|
||||
}
|
||||
|
||||
const storedNsec = storage.getAccountNsec(account.pubkey)
|
||||
if (storedNsec) {
|
||||
setNsec(storedNsec)
|
||||
} else {
|
||||
const init = async () => {
|
||||
setRelayList(null)
|
||||
setProfile(null)
|
||||
setProfileEvent(null)
|
||||
setNsec(null)
|
||||
}
|
||||
const storedNcryptsec = storage.getAccountNcryptsec(account.pubkey)
|
||||
if (storedNcryptsec) {
|
||||
setNcryptsec(storedNcryptsec)
|
||||
} else {
|
||||
setNcryptsec(null)
|
||||
}
|
||||
const storedRelayListEvent = storage.getAccountRelayListEvent(account.pubkey)
|
||||
if (storedRelayListEvent) {
|
||||
setRelayList(
|
||||
storedRelayListEvent ? getRelayListFromRelayListEvent(storedRelayListEvent) : null
|
||||
)
|
||||
}
|
||||
const storedProfileEvent = storage.getAccountProfileEvent(account.pubkey)
|
||||
if (storedProfileEvent) {
|
||||
setProfileEvent(storedProfileEvent)
|
||||
setProfile(getProfileFromProfileEvent(storedProfileEvent))
|
||||
}
|
||||
client.fetchRelayListEvent(account.pubkey).then(async (relayListEvent) => {
|
||||
if (!relayListEvent) {
|
||||
if (storedRelayListEvent) return
|
||||
|
||||
setRelayList({ write: BIG_RELAY_URLS, read: BIG_RELAY_URLS, originalRelays: [] })
|
||||
if (!account) {
|
||||
return
|
||||
}
|
||||
const isNew = storage.setAccountRelayListEvent(relayListEvent)
|
||||
if (!isNew) return
|
||||
setRelayList(getRelayListFromRelayListEvent(relayListEvent))
|
||||
})
|
||||
client.fetchProfileEvent(account.pubkey).then(async (profileEvent) => {
|
||||
if (!profileEvent) {
|
||||
if (storedProfileEvent) return
|
||||
|
||||
setProfile({
|
||||
pubkey: account.pubkey,
|
||||
username: formatPubkey(account.pubkey)
|
||||
})
|
||||
return
|
||||
const controller = new AbortController()
|
||||
const storedNsec = storage.getAccountNsec(account.pubkey)
|
||||
if (storedNsec) {
|
||||
setNsec(storedNsec)
|
||||
} else {
|
||||
setNsec(null)
|
||||
}
|
||||
const isNew = storage.setAccountProfileEvent(profileEvent)
|
||||
if (!isNew) return
|
||||
setProfileEvent(profileEvent)
|
||||
setProfile(getProfileFromProfileEvent(profileEvent))
|
||||
})
|
||||
client.initUserIndexFromFollowings(account.pubkey)
|
||||
const storedNcryptsec = storage.getAccountNcryptsec(account.pubkey)
|
||||
if (storedNcryptsec) {
|
||||
setNcryptsec(storedNcryptsec)
|
||||
} else {
|
||||
setNcryptsec(null)
|
||||
}
|
||||
const [storedRelayListEvent, storedProfileEvent] = await Promise.all([
|
||||
indexedDb.getReplaceableEvent(account.pubkey, kinds.RelayList),
|
||||
indexedDb.getReplaceableEvent(account.pubkey, kinds.Metadata)
|
||||
])
|
||||
if (storedRelayListEvent) {
|
||||
setRelayList(
|
||||
storedRelayListEvent ? getRelayListFromRelayListEvent(storedRelayListEvent) : null
|
||||
)
|
||||
}
|
||||
if (storedProfileEvent) {
|
||||
setProfileEvent(storedProfileEvent)
|
||||
setProfile(getProfileFromProfileEvent(storedProfileEvent))
|
||||
}
|
||||
|
||||
client.fetchRelayListEvent(account.pubkey).then(async (relayListEvent) => {
|
||||
if (!relayListEvent) {
|
||||
if (storedRelayListEvent) return
|
||||
|
||||
setRelayList({ write: BIG_RELAY_URLS, read: BIG_RELAY_URLS, originalRelays: [] })
|
||||
return
|
||||
}
|
||||
const event = await indexedDb.getReplaceableEvent(account.pubkey, kinds.RelayList)
|
||||
if (event) {
|
||||
setRelayList(getRelayListFromRelayListEvent(event))
|
||||
}
|
||||
})
|
||||
client.fetchProfileEvent(account.pubkey).then(async (profileEvent) => {
|
||||
if (!profileEvent) {
|
||||
if (storedProfileEvent) return
|
||||
|
||||
setProfile({
|
||||
pubkey: account.pubkey,
|
||||
username: formatPubkey(account.pubkey)
|
||||
})
|
||||
return
|
||||
}
|
||||
const event = await indexedDb.getReplaceableEvent(account.pubkey, kinds.Metadata)
|
||||
if (event) {
|
||||
setProfileEvent(event)
|
||||
setProfile(getProfileFromProfileEvent(event))
|
||||
}
|
||||
})
|
||||
client.initUserIndexFromFollowings(account.pubkey, controller.signal)
|
||||
return controller
|
||||
}
|
||||
const promise = init()
|
||||
return () => {
|
||||
promise.then((controller) => {
|
||||
controller?.abort()
|
||||
})
|
||||
}
|
||||
}, [account])
|
||||
|
||||
useEffect(() => {
|
||||
@@ -379,21 +395,21 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
|
||||
}
|
||||
|
||||
const getRelayList = async (pubkey: string) => {
|
||||
const storedRelayListEvent = storage.getAccountRelayListEvent(pubkey)
|
||||
const storedRelayListEvent = await indexedDb.getReplaceableEvent(pubkey, kinds.RelayList)
|
||||
if (storedRelayListEvent) {
|
||||
return getRelayListFromRelayListEvent(storedRelayListEvent)
|
||||
}
|
||||
return await client.fetchRelayList(pubkey)
|
||||
}
|
||||
|
||||
const updateRelayListEvent = (relayListEvent: Event) => {
|
||||
const isNew = storage.setAccountRelayListEvent(relayListEvent)
|
||||
const updateRelayListEvent = async (relayListEvent: Event) => {
|
||||
const isNew = await indexedDb.putReplaceableEvent(relayListEvent)
|
||||
if (!isNew) return
|
||||
setRelayList(getRelayListFromRelayListEvent(relayListEvent))
|
||||
}
|
||||
|
||||
const updateProfileEvent = (profileEvent: Event) => {
|
||||
const isNew = storage.setAccountProfileEvent(profileEvent)
|
||||
const updateProfileEvent = async (profileEvent: Event) => {
|
||||
const isNew = await indexedDb.putReplaceableEvent(profileEvent)
|
||||
if (!isNew) return
|
||||
setProfileEvent(profileEvent)
|
||||
setProfile(getProfileFromProfileEvent(profileEvent))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { randomString } from '@/lib/random'
|
||||
import { isWebsocketUrl, normalizeUrl } from '@/lib/url'
|
||||
import storage from '@/services/storage.service'
|
||||
import storage from '@/services/local-storage.service'
|
||||
import { TRelaySet } from '@/types'
|
||||
import { createContext, useContext, useEffect, useState } from 'react'
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import storage from '@/services/storage.service'
|
||||
import storage from '@/services/local-storage.service'
|
||||
import { TTheme, TThemeSetting } from '@/types'
|
||||
import { createContext, useContext, useEffect, useState } from 'react'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user