From d26bac8a42cf4869ee82e7847ba933ebd61ac84f Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Wed, 14 Aug 2024 09:28:26 -0700 Subject: [PATCH] Speed up data loading, make it more lazy --- src/app/base.ts | 22 +++----- src/app/components/LogIn.svelte | 8 +-- src/app/components/PrimaryNav.svelte | 6 +- src/app/state.ts | 82 ++++++++++++---------------- src/routes/+layout.svelte | 7 +-- 5 files changed, 49 insertions(+), 76 deletions(-) diff --git a/src/app/base.ts b/src/app/base.ts index e93c4b1b..d3ddeb65 100644 --- a/src/app/base.ts +++ b/src/app/base.ts @@ -2,7 +2,7 @@ import {derived} from "svelte/store" import {memoize, assoc} from '@welshman/lib' import type {CustomEvent} 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 type {ISigner} 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 pk = synced('pk', null) +export const pk = withGetter(synced('pk', null)) -export const getPk = getter(pk) +export const sessions = withGetter(synced>('sessions', {})) -export const sessions = synced>('sessions', {}) +export const session = withGetter(derived([pk, sessions], ([$pk, $sessions]) => $pk ? $sessions[$pk] : null)) -export const getSessions = getter(sessions) - -export const session = derived([pk, sessions], ([$pk, $sessions]) => $pk ? $sessions[$pk] : null) - -export const getSession = getter(session) +export const getSession = (pubkey: string) => sessions.get()[pubkey] export const addSession = (session: Session) => { sessions.update(assoc(session.pubkey, session)) pk.set(session.pubkey) } -export const makeSigner = memoize((session: Session) => { +export const getSigner = memoize((session: Session) => { switch (session?.method) { case "extension": return new Nip07Signer() @@ -49,9 +45,7 @@ export const makeSigner = memoize((session: Session) => { } }) -export const signer = derived(session, makeSigner) - -export const getSigner = getter(signer) +export const signer = withGetter(derived(session, getSigner)) const seenChallenges = new Set() @@ -65,7 +59,7 @@ Object.assign(NetworkContext, { seenChallenges.add(challenge) - const event = await getSigner()!.sign( + const event = await signer.get()!.sign( createEvent(22242, { tags: [ ["relay", url], diff --git a/src/app/components/LogIn.svelte b/src/app/components/LogIn.svelte index bbd4a42d..06d510e3 100644 --- a/src/app/components/LogIn.svelte +++ b/src/app/components/LogIn.svelte @@ -10,7 +10,7 @@ import {pushModal, clearModal} from '@app/modal' import {pushToast} from '@app/toast' import {addSession} from '@app/base' - import {loadUserData, loadHandle} from '@app/state' + import {loadHandle} from '@app/state' const back = () => history.back() @@ -27,12 +27,8 @@ const {pubkey, relays = []} = handle 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}) pushToast({message: "Successfully logged in!"}) clearModal() diff --git a/src/app/components/PrimaryNav.svelte b/src/app/components/PrimaryNav.svelte index 095d9ed8..98cafef8 100644 --- a/src/app/components/PrimaryNav.svelte +++ b/src/app/components/PrimaryNav.svelte @@ -13,7 +13,7 @@ import PrimaryNavItem from "@lib/components/PrimaryNavItem.svelte" import SpaceAdd from '@app/components/SpaceAdd.svelte' 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" const addSpace = () => pushModal(SpaceAdd) @@ -27,10 +27,12 @@ const $userGroupsByNom = new Map() for (const id of $membership?.ids || []) { - const nom = getGroupNom(id) + const [url, nom] = splitGroupId(id) const group = $qualifiedGroupsById.get(id) const groups = $userGroupsByNom.get(nom) || [] + loadGroup(nom, [url]) + if (group) { groups.push(group) } diff --git a/src/app/state.ts b/src/app/state.ts index da32219a..b10f5aa1 100644 --- a/src/app/state.ts +++ b/src/app/state.ts @@ -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 {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 {SubscribeRequest} from '@welshman/net' import {publish, subscribe} from '@welshman/net' import {decrypt} from '@welshman/signer' import {deriveEvents, deriveEventsMapped, getter, withGetter} from "@welshman/store" import {synced, parseJson} from '@lib/util' 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 @@ -56,14 +57,15 @@ export const createCollection = ({ return {indexStore, getIndex, deriveItem, loadItem, getItem} } -export const load = ({relays, filters}: {relays: string[], filters: Filter[]}) => +export const load = (request: SubscribeRequest) => new Promise>(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) => { const e: CustomEvent = {...event, fetched_at: now()} repository.publish(e) + sub.close() resolve(e) }) @@ -73,14 +75,14 @@ export const load = ({relays, filters}: {relays: string[], filters: Filter[]}) = export type ModifyTags = (tags: string[][]) => string[][] export const updateList = async (kind: number, modifyTags: ModifyTags) => { - const pk = getPk()! - const signer = getSigner()! - const [prev] = repository.query([{kinds: [kind], authors: [pk]}]) + const $pk = pk.get()! + const $signer = signer.get()! + const [prev] = repository.query([{kinds: [kind], authors: [$pk]}]) // 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 - const relays = [...INDEXER_RELAYS, ...getWriteRelayUrls(getRelaySelections(pk))] - const encrypt = (content: string) => signer.nip44.encrypt(pk, content) + const relays = [...INDEXER_RELAYS, ...getWriteRelayUrls(await loadRelaySelections($pk))] + const encrypt = (content: string) => $signer.nip44.encrypt($pk, content) let encryptable if (prev) { @@ -97,7 +99,7 @@ export const updateList = async (kind: number, modifyTags: ModifyTags) => { } 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}) } @@ -113,12 +115,10 @@ export const setPlaintext = (e: CustomEvent, content: string) => export const ensurePlaintext = async (e: CustomEvent) => { if (e.content && !getPlaintext(e)) { - const sessions = getSessions() - const session = sessions[e.pubkey] - const signer = makeSigner(session) + const $signer = getSigner(getSession(e.pubkey)) - if (signer) { - setPlaintext(e, await decrypt(signer, e.pubkey, e.content)) + if ($signer) { + setPlaintext(e, await decrypt($signer, e.pubkey, e.content)) } } @@ -136,7 +136,7 @@ export const { getIndex: getRelaysByUrl, deriveItem: deriveRelay, loadItem: loadRelay, - getItem: getRelay, + // getItem: getRelay, } = createCollection({ store: relays, getKey: (relay: Relay) => relay.url, @@ -166,7 +166,7 @@ export const { getIndex: getHandlesByPubkey, deriveItem: deriveHandle, loadItem: loadHandle, - getItem: getHandle, + // getItem: getHandle, } = createCollection({ store: handles, getKey: (handle: Handle) => handle.pubkey, @@ -201,13 +201,14 @@ export const { getIndex: getProfilesByPubkey, deriveItem: deriveProfile, loadItem: loadProfile, - getItem: getProfile, + // getItem: getProfile, } = createCollection({ store: profiles, getKey: profile => profile.event.pubkey, isStale: (profile: PublishedProfile) => profile.event.fetched_at < now() - 3600, - load: (pubkey: string, relays = []) => + load: (pubkey: string, relays = [], request: Partial = {}) => load({ + ...request, relays: [...relays, ...INDEXER_RELAYS], filters: [{kinds: [PROFILE], authors: [pubkey]}], }), @@ -228,13 +229,14 @@ export const { getIndex: getRelaySelectionsByPubkey, deriveItem: deriveRelaySelections, loadItem: loadRelaySelections, - getItem: getRelaySelections, + // getItem: getRelaySelections, } = createCollection({ store: relaySelections, getKey: relaySelections => relaySelections.pubkey, isStale: (relaySelections: CustomEvent) => relaySelections.fetched_at < now() - 3600, - load: (pubkey: string, relays = []) => + load: (pubkey: string, relays = [], request: Partial = {}) => load({ + ...request, relays: [...relays, ...INDEXER_RELAYS], filters: [{kinds: [RELAYS], authors: [pubkey]}], }) @@ -259,13 +261,14 @@ export const { getIndex: getFollowsByPubkey, deriveItem: deriveFollows, loadItem: loadFollows, - getItem: getFollows, + // getItem: getFollows, } = createCollection({ store: follows, getKey: follows => follows.event.pubkey, isStale: (follows: PublishedList) => follows.event.fetched_at < now() - 3600, - load: (pubkey: string, relays = []) => + load: (pubkey: string, relays = [], request: Partial = {}) => load({ + ...request, relays: [...relays, ...INDEXER_RELAYS], filters: [{kinds: [FOLLOWS], authors: [pubkey]}], }) @@ -290,13 +293,14 @@ export const { getIndex: getMutesByPubkey, deriveItem: deriveMutes, loadItem: loadMutes, - getItem: getMutes, + // getItem: getMutes, } = createCollection({ store: mutes, getKey: mute => mute.event.pubkey, isStale: (mutes: PublishedList) => mutes.event.fetched_at < now() - 3600, - load: (pubkey: string, relays = []) => + load: (pubkey: string, relays = [], request: Partial = {}) => load({ + ...request, relays: [...relays, ...INDEXER_RELAYS], filters: [{kinds: [MUTES], authors: [pubkey]}], }) @@ -354,15 +358,16 @@ export const { getIndex: getGroupsByNom, deriveItem: deriveGroup, loadItem: loadGroup, - getItem: getGroup, + // getItem: getGroup, } = createCollection({ store: groups, getKey: (group: PublishedGroup) => group.nom, isStale: (group: PublishedGroup) => group.event.fetched_at < now() - 3600, - load: (nom: string, relays: string[] = []) => + load: (nom: string, relays: string[] = [], request: Partial = {}) => Promise.all([ ...relays.map(loadRelay), load({ + ...request, relays, filters: [{kinds: [GROUP_META], '#d': [nom]}], }), @@ -426,34 +431,15 @@ export const { getIndex: getGroupMembersipsByPubkey, deriveItem: deriveGroupMembership, loadItem: loadGroupMembership, - getItem: getGroupMembership, + // getItem: getGroupMembership, } = createCollection({ store: groupMemberships, getKey: groupMembership => groupMembership.event.pubkey, isStale: (groupMembership: PublishedGroupMembership) => groupMembership.event.fetched_at < now() - 3600, - load: (pubkey: string, relays = []) => + load: (pubkey: string, relays = [], request: Partial = {}) => load({ + ...request, relays: [...relays, ...INDEXER_RELAYS], 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])) - ) - } -} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 1e09926f..862c1e1a 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -9,8 +9,7 @@ import PrimaryNav from "@app/components/PrimaryNav.svelte" import SecondaryNav from "@app/components/SecondaryNav.svelte" import {modals, clearModal} from "@app/modal" - import {pk, session} from "@app/base" - import {loadUserData} from "@app/state" + import {session} from "@app/base" let dialog: HTMLDialogElement let prev: any @@ -32,10 +31,6 @@ } onMount(() => { - if ($pk) { - loadUserData($pk) - } - dialog.addEventListener('close', () => { if (modal) { clearModal()