Speed up data loading, make it more lazy

This commit is contained in:
Jon Staab
2024-08-14 09:28:26 -07:00
parent 5bec4531ea
commit d26bac8a42
5 changed files with 49 additions and 76 deletions
+8 -14
View File
@@ -2,7 +2,7 @@ import {derived} from "svelte/store"
import {memoize, assoc} from '@welshman/lib' import {memoize, assoc} from '@welshman/lib'
import type {CustomEvent} from '@welshman/util' import type {CustomEvent} from '@welshman/util'
import {Repository, createEvent, Relay} from "@welshman/util" import {Repository, createEvent, Relay} from "@welshman/util"
import {getter} from "@welshman/store" import {withGetter} from "@welshman/store"
import {NetworkContext, Tracker} from "@welshman/net" import {NetworkContext, Tracker} from "@welshman/net"
import type {ISigner} from "@welshman/signer" import type {ISigner} from "@welshman/signer"
import {Nip46Broker, Nip46Signer, Nip07Signer, Nip01Signer} from '@welshman/signer' import {Nip46Broker, Nip46Signer, Nip07Signer, Nip01Signer} from '@welshman/signer'
@@ -19,24 +19,20 @@ export const relay = new Relay(repository)
export const tracker = new Tracker() export const tracker = new Tracker()
export const pk = synced<string | null>('pk', null) export const pk = withGetter(synced<string | null>('pk', null))
export const getPk = getter(pk) export const sessions = withGetter(synced<Record<string, Session>>('sessions', {}))
export const sessions = synced<Record<string, Session>>('sessions', {}) export const session = withGetter(derived([pk, sessions], ([$pk, $sessions]) => $pk ? $sessions[$pk] : null))
export const getSessions = getter(sessions) export const getSession = (pubkey: string) => sessions.get()[pubkey]
export const session = derived([pk, sessions], ([$pk, $sessions]) => $pk ? $sessions[$pk] : null)
export const getSession = getter(session)
export const addSession = (session: Session) => { export const addSession = (session: Session) => {
sessions.update(assoc(session.pubkey, session)) sessions.update(assoc(session.pubkey, session))
pk.set(session.pubkey) pk.set(session.pubkey)
} }
export const makeSigner = memoize((session: Session) => { export const getSigner = memoize((session: Session) => {
switch (session?.method) { switch (session?.method) {
case "extension": case "extension":
return new Nip07Signer() return new Nip07Signer()
@@ -49,9 +45,7 @@ export const makeSigner = memoize((session: Session) => {
} }
}) })
export const signer = derived(session, makeSigner) export const signer = withGetter(derived(session, getSigner))
export const getSigner = getter(signer)
const seenChallenges = new Set() const seenChallenges = new Set()
@@ -65,7 +59,7 @@ Object.assign(NetworkContext, {
seenChallenges.add(challenge) seenChallenges.add(challenge)
const event = await getSigner()!.sign( const event = await signer.get()!.sign(
createEvent(22242, { createEvent(22242, {
tags: [ tags: [
["relay", url], ["relay", url],
+2 -6
View File
@@ -10,7 +10,7 @@
import {pushModal, clearModal} from '@app/modal' import {pushModal, clearModal} from '@app/modal'
import {pushToast} from '@app/toast' import {pushToast} from '@app/toast'
import {addSession} from '@app/base' import {addSession} from '@app/base'
import {loadUserData, loadHandle} from '@app/state' import {loadHandle} from '@app/state'
const back = () => history.back() const back = () => history.back()
@@ -27,12 +27,8 @@
const {pubkey, relays = []} = handle const {pubkey, relays = []} = handle
const broker = Nip46Broker.get(pubkey, secret, handler) const broker = Nip46Broker.get(pubkey, secret, handler)
const [profile, success] = await Promise.all([
loadUserData(pubkey, relays),
broker.connect(),
])
if (success) { if (await broker.connect()) {
addSession({method: "nip46", pubkey, secret, handler}) addSession({method: "nip46", pubkey, secret, handler})
pushToast({message: "Successfully logged in!"}) pushToast({message: "Successfully logged in!"})
clearModal() clearModal()
+4 -2
View File
@@ -13,7 +13,7 @@
import PrimaryNavItem from "@lib/components/PrimaryNavItem.svelte" import PrimaryNavItem from "@lib/components/PrimaryNavItem.svelte"
import SpaceAdd from '@app/components/SpaceAdd.svelte' import SpaceAdd from '@app/components/SpaceAdd.svelte'
import {session} from "@app/base" import {session} from "@app/base"
import {deriveGroupMembership, makeGroupId, getGroup, deriveProfile, qualifiedGroupsById, getGroupNom} from "@app/state" import {deriveGroupMembership, makeGroupId, loadGroup, deriveProfile, qualifiedGroupsById, splitGroupId} from "@app/state"
import {pushModal} from "@app/modal" import {pushModal} from "@app/modal"
const addSpace = () => pushModal(SpaceAdd) const addSpace = () => pushModal(SpaceAdd)
@@ -27,10 +27,12 @@
const $userGroupsByNom = new Map() const $userGroupsByNom = new Map()
for (const id of $membership?.ids || []) { for (const id of $membership?.ids || []) {
const nom = getGroupNom(id) const [url, nom] = splitGroupId(id)
const group = $qualifiedGroupsById.get(id) const group = $qualifiedGroupsById.get(id)
const groups = $userGroupsByNom.get(nom) || [] const groups = $userGroupsByNom.get(nom) || []
loadGroup(nom, [url])
if (group) { if (group) {
groups.push(group) groups.push(group)
} }
+34 -48
View File
@@ -4,12 +4,13 @@ import type {Maybe} from "@welshman/lib"
import {uniq, uniqBy, groupBy, pushToMapKey, nthEq, batcher, postJson, stripProtocol, assoc, indexBy, now} from "@welshman/lib" import {uniq, uniqBy, groupBy, pushToMapKey, nthEq, batcher, postJson, stripProtocol, assoc, indexBy, now} from "@welshman/lib"
import {getIdentifier, getRelayTags, getRelayTagValues, normalizeRelayUrl, getPubkeyTagValues, GROUP_META, PROFILE, RELAYS, FOLLOWS, MUTES, GROUPS, getGroupTags, readProfile, readList, asDecryptedEvent, editList, makeList, createList} from "@welshman/util" import {getIdentifier, getRelayTags, getRelayTagValues, normalizeRelayUrl, getPubkeyTagValues, GROUP_META, PROFILE, RELAYS, FOLLOWS, MUTES, GROUPS, getGroupTags, readProfile, readList, asDecryptedEvent, editList, makeList, createList} from "@welshman/util"
import type {Filter, SignedEvent, CustomEvent, PublishedProfile, PublishedList} from '@welshman/util' import type {Filter, SignedEvent, CustomEvent, PublishedProfile, PublishedList} from '@welshman/util'
import type {SubscribeRequest} from '@welshman/net'
import {publish, subscribe} from '@welshman/net' import {publish, subscribe} from '@welshman/net'
import {decrypt} from '@welshman/signer' import {decrypt} from '@welshman/signer'
import {deriveEvents, deriveEventsMapped, getter, withGetter} from "@welshman/store" import {deriveEvents, deriveEventsMapped, getter, withGetter} from "@welshman/store"
import {synced, parseJson} from '@lib/util' import {synced, parseJson} from '@lib/util'
import type {Session, Handle, Relay} from '@app/types' import type {Session, Handle, Relay} from '@app/types'
import {INDEXER_RELAYS, DUFFLEPUD_URL, repository, pk, getPk, getSessions, makeSigner, getSigner} from "@app/base" import {INDEXER_RELAYS, DUFFLEPUD_URL, repository, pk, getSession, getSigner, signer} from "@app/base"
// Utils // Utils
@@ -56,14 +57,15 @@ export const createCollection = <T>({
return {indexStore, getIndex, deriveItem, loadItem, getItem} return {indexStore, getIndex, deriveItem, loadItem, getItem}
} }
export const load = ({relays, filters}: {relays: string[], filters: Filter[]}) => export const load = (request: SubscribeRequest) =>
new Promise<Maybe<CustomEvent>>(resolve => { new Promise<Maybe<CustomEvent>>(resolve => {
const sub = subscribe({relays, filters, closeOnEose: true, timeout: 3000}) const sub = subscribe({closeOnEose: true, timeout: 3000, delay: 50, ...request})
sub.emitter.on('event', (url: string, event: SignedEvent) => { sub.emitter.on('event', (url: string, event: SignedEvent) => {
const e: CustomEvent = {...event, fetched_at: now()} const e: CustomEvent = {...event, fetched_at: now()}
repository.publish(e) repository.publish(e)
sub.close()
resolve(e) resolve(e)
}) })
@@ -73,14 +75,14 @@ export const load = ({relays, filters}: {relays: string[], filters: Filter[]}) =
export type ModifyTags = (tags: string[][]) => string[][] export type ModifyTags = (tags: string[][]) => string[][]
export const updateList = async (kind: number, modifyTags: ModifyTags) => { export const updateList = async (kind: number, modifyTags: ModifyTags) => {
const pk = getPk()! const $pk = pk.get()!
const signer = getSigner()! const $signer = signer.get()!
const [prev] = repository.query([{kinds: [kind], authors: [pk]}]) const [prev] = repository.query([{kinds: [kind], authors: [$pk]}])
// Preserve content instead of use encrypted tags because kind 3 content is used for // Preserve content instead of use encrypted tags because kind 3 content is used for
// relay selections in many places. Content isn't supported for mutes or relays so this is ok // relay selections in many places. Content isn't supported for mutes or relays so this is ok
const relays = [...INDEXER_RELAYS, ...getWriteRelayUrls(getRelaySelections(pk))] const relays = [...INDEXER_RELAYS, ...getWriteRelayUrls(await loadRelaySelections($pk))]
const encrypt = (content: string) => signer.nip44.encrypt(pk, content) const encrypt = (content: string) => $signer.nip44.encrypt($pk, content)
let encryptable let encryptable
if (prev) { if (prev) {
@@ -97,7 +99,7 @@ export const updateList = async (kind: number, modifyTags: ModifyTags) => {
} }
const template = await encryptable.reconcile(encrypt) const template = await encryptable.reconcile(encrypt)
const event = await signer.sign({...template, created_at: now()}) const event = await $signer.sign({...template, created_at: now()})
await publish({event, relays}) await publish({event, relays})
} }
@@ -113,12 +115,10 @@ export const setPlaintext = (e: CustomEvent, content: string) =>
export const ensurePlaintext = async (e: CustomEvent) => { export const ensurePlaintext = async (e: CustomEvent) => {
if (e.content && !getPlaintext(e)) { if (e.content && !getPlaintext(e)) {
const sessions = getSessions() const $signer = getSigner(getSession(e.pubkey))
const session = sessions[e.pubkey]
const signer = makeSigner(session)
if (signer) { if ($signer) {
setPlaintext(e, await decrypt(signer, e.pubkey, e.content)) setPlaintext(e, await decrypt($signer, e.pubkey, e.content))
} }
} }
@@ -136,7 +136,7 @@ export const {
getIndex: getRelaysByUrl, getIndex: getRelaysByUrl,
deriveItem: deriveRelay, deriveItem: deriveRelay,
loadItem: loadRelay, loadItem: loadRelay,
getItem: getRelay, // getItem: getRelay,
} = createCollection({ } = createCollection({
store: relays, store: relays,
getKey: (relay: Relay) => relay.url, getKey: (relay: Relay) => relay.url,
@@ -166,7 +166,7 @@ export const {
getIndex: getHandlesByPubkey, getIndex: getHandlesByPubkey,
deriveItem: deriveHandle, deriveItem: deriveHandle,
loadItem: loadHandle, loadItem: loadHandle,
getItem: getHandle, // getItem: getHandle,
} = createCollection({ } = createCollection({
store: handles, store: handles,
getKey: (handle: Handle) => handle.pubkey, getKey: (handle: Handle) => handle.pubkey,
@@ -201,13 +201,14 @@ export const {
getIndex: getProfilesByPubkey, getIndex: getProfilesByPubkey,
deriveItem: deriveProfile, deriveItem: deriveProfile,
loadItem: loadProfile, loadItem: loadProfile,
getItem: getProfile, // getItem: getProfile,
} = createCollection({ } = createCollection({
store: profiles, store: profiles,
getKey: profile => profile.event.pubkey, getKey: profile => profile.event.pubkey,
isStale: (profile: PublishedProfile) => profile.event.fetched_at < now() - 3600, isStale: (profile: PublishedProfile) => profile.event.fetched_at < now() - 3600,
load: (pubkey: string, relays = []) => load: (pubkey: string, relays = [], request: Partial<SubscribeRequest> = {}) =>
load({ load({
...request,
relays: [...relays, ...INDEXER_RELAYS], relays: [...relays, ...INDEXER_RELAYS],
filters: [{kinds: [PROFILE], authors: [pubkey]}], filters: [{kinds: [PROFILE], authors: [pubkey]}],
}), }),
@@ -228,13 +229,14 @@ export const {
getIndex: getRelaySelectionsByPubkey, getIndex: getRelaySelectionsByPubkey,
deriveItem: deriveRelaySelections, deriveItem: deriveRelaySelections,
loadItem: loadRelaySelections, loadItem: loadRelaySelections,
getItem: getRelaySelections, // getItem: getRelaySelections,
} = createCollection({ } = createCollection({
store: relaySelections, store: relaySelections,
getKey: relaySelections => relaySelections.pubkey, getKey: relaySelections => relaySelections.pubkey,
isStale: (relaySelections: CustomEvent) => relaySelections.fetched_at < now() - 3600, isStale: (relaySelections: CustomEvent) => relaySelections.fetched_at < now() - 3600,
load: (pubkey: string, relays = []) => load: (pubkey: string, relays = [], request: Partial<SubscribeRequest> = {}) =>
load({ load({
...request,
relays: [...relays, ...INDEXER_RELAYS], relays: [...relays, ...INDEXER_RELAYS],
filters: [{kinds: [RELAYS], authors: [pubkey]}], filters: [{kinds: [RELAYS], authors: [pubkey]}],
}) })
@@ -259,13 +261,14 @@ export const {
getIndex: getFollowsByPubkey, getIndex: getFollowsByPubkey,
deriveItem: deriveFollows, deriveItem: deriveFollows,
loadItem: loadFollows, loadItem: loadFollows,
getItem: getFollows, // getItem: getFollows,
} = createCollection({ } = createCollection({
store: follows, store: follows,
getKey: follows => follows.event.pubkey, getKey: follows => follows.event.pubkey,
isStale: (follows: PublishedList) => follows.event.fetched_at < now() - 3600, isStale: (follows: PublishedList) => follows.event.fetched_at < now() - 3600,
load: (pubkey: string, relays = []) => load: (pubkey: string, relays = [], request: Partial<SubscribeRequest> = {}) =>
load({ load({
...request,
relays: [...relays, ...INDEXER_RELAYS], relays: [...relays, ...INDEXER_RELAYS],
filters: [{kinds: [FOLLOWS], authors: [pubkey]}], filters: [{kinds: [FOLLOWS], authors: [pubkey]}],
}) })
@@ -290,13 +293,14 @@ export const {
getIndex: getMutesByPubkey, getIndex: getMutesByPubkey,
deriveItem: deriveMutes, deriveItem: deriveMutes,
loadItem: loadMutes, loadItem: loadMutes,
getItem: getMutes, // getItem: getMutes,
} = createCollection({ } = createCollection({
store: mutes, store: mutes,
getKey: mute => mute.event.pubkey, getKey: mute => mute.event.pubkey,
isStale: (mutes: PublishedList) => mutes.event.fetched_at < now() - 3600, isStale: (mutes: PublishedList) => mutes.event.fetched_at < now() - 3600,
load: (pubkey: string, relays = []) => load: (pubkey: string, relays = [], request: Partial<SubscribeRequest> = {}) =>
load({ load({
...request,
relays: [...relays, ...INDEXER_RELAYS], relays: [...relays, ...INDEXER_RELAYS],
filters: [{kinds: [MUTES], authors: [pubkey]}], filters: [{kinds: [MUTES], authors: [pubkey]}],
}) })
@@ -354,15 +358,16 @@ export const {
getIndex: getGroupsByNom, getIndex: getGroupsByNom,
deriveItem: deriveGroup, deriveItem: deriveGroup,
loadItem: loadGroup, loadItem: loadGroup,
getItem: getGroup, // getItem: getGroup,
} = createCollection({ } = createCollection({
store: groups, store: groups,
getKey: (group: PublishedGroup) => group.nom, getKey: (group: PublishedGroup) => group.nom,
isStale: (group: PublishedGroup) => group.event.fetched_at < now() - 3600, isStale: (group: PublishedGroup) => group.event.fetched_at < now() - 3600,
load: (nom: string, relays: string[] = []) => load: (nom: string, relays: string[] = [], request: Partial<SubscribeRequest> = {}) =>
Promise.all([ Promise.all([
...relays.map(loadRelay), ...relays.map(loadRelay),
load({ load({
...request,
relays, relays,
filters: [{kinds: [GROUP_META], '#d': [nom]}], filters: [{kinds: [GROUP_META], '#d': [nom]}],
}), }),
@@ -426,34 +431,15 @@ export const {
getIndex: getGroupMembersipsByPubkey, getIndex: getGroupMembersipsByPubkey,
deriveItem: deriveGroupMembership, deriveItem: deriveGroupMembership,
loadItem: loadGroupMembership, loadItem: loadGroupMembership,
getItem: getGroupMembership, // getItem: getGroupMembership,
} = createCollection({ } = createCollection({
store: groupMemberships, store: groupMemberships,
getKey: groupMembership => groupMembership.event.pubkey, getKey: groupMembership => groupMembership.event.pubkey,
isStale: (groupMembership: PublishedGroupMembership) => groupMembership.event.fetched_at < now() - 3600, isStale: (groupMembership: PublishedGroupMembership) => groupMembership.event.fetched_at < now() - 3600,
load: (pubkey: string, relays = []) => load: (pubkey: string, relays = [], request: Partial<SubscribeRequest> = {}) =>
load({ load({
...request,
relays: [...relays, ...INDEXER_RELAYS], relays: [...relays, ...INDEXER_RELAYS],
filters: [{kinds: [GROUPS], authors: [pubkey]}], filters: [{kinds: [GROUPS], authors: [pubkey]}],
}) })
}) })
// User stuff
export const loadUserData = async (pubkey: string, hints: string[] = []) => {
const relaySelections = await loadRelaySelections(pubkey, INDEXER_RELAYS)
const relays = uniq([...getRelayTagValues(relaySelections?.tags || []), ...INDEXER_RELAYS, ...hints])
const [membership] = await Promise.all([
loadGroupMembership(pubkey, relays),
loadProfile(pubkey, relays),
loadFollows(pubkey, relays),
loadMutes(pubkey, relays),
])
if (membership) {
await Promise.all(
getGroupTags(membership.event.tags).map(([_, nom, url]) => loadGroup(nom, [url]))
)
}
}
+1 -6
View File
@@ -9,8 +9,7 @@
import PrimaryNav from "@app/components/PrimaryNav.svelte" import PrimaryNav from "@app/components/PrimaryNav.svelte"
import SecondaryNav from "@app/components/SecondaryNav.svelte" import SecondaryNav from "@app/components/SecondaryNav.svelte"
import {modals, clearModal} from "@app/modal" import {modals, clearModal} from "@app/modal"
import {pk, session} from "@app/base" import {session} from "@app/base"
import {loadUserData} from "@app/state"
let dialog: HTMLDialogElement let dialog: HTMLDialogElement
let prev: any let prev: any
@@ -32,10 +31,6 @@
} }
onMount(() => { onMount(() => {
if ($pk) {
loadUserData($pk)
}
dialog.addEventListener('close', () => { dialog.addEventListener('close', () => {
if (modal) { if (modal) {
clearModal() clearModal()