Support rumors in thunks

This commit is contained in:
Jon Staab
2024-10-08 13:20:43 -07:00
parent da05362bf1
commit a4085af9e3
3 changed files with 59 additions and 29 deletions
+7 -1
View File
@@ -12,7 +12,7 @@ export const collection = <T, LoadArgs extends any[]>({
name: string name: string
store: Readable<T[]> store: Readable<T[]>
getKey: (item: T) => string getKey: (item: T) => string
load: (key: string, ...args: LoadArgs) => Promise<any> load?: (key: string, ...args: LoadArgs) => Promise<any>
}) => { }) => {
const indexStore = withGetter(derived(store, $items => indexBy(getKey, $items))) const indexStore = withGetter(derived(store, $items => indexBy(getKey, $items)))
const pending = new Map<string, Promise<Maybe<T>>>() const pending = new Map<string, Promise<Maybe<T>>>()
@@ -20,6 +20,12 @@ export const collection = <T, LoadArgs extends any[]>({
const loadItem = async (key: string, ...args: LoadArgs) => { const loadItem = async (key: string, ...args: LoadArgs) => {
const stale = indexStore.get().get(key) const stale = indexStore.get().get(key)
// If we have no loader function, nothing we can do
if (!load) {
return stale
}
const freshness = getFreshness(name, key) const freshness = getFreshness(name, key)
// If we have an item, reload if it's stale // If we have an item, reload if it's stale
+49 -26
View File
@@ -1,7 +1,8 @@
import {writable, get} from 'svelte/store' import {writable, get} from 'svelte/store'
import {Worker, assoc} from '@welshman/lib' import {Worker, assoc} from '@welshman/lib'
import {stamp, own, hash} from "@welshman/signer" import {stamp, own, hash} from "@welshman/signer"
import type {HashedEvent, EventTemplate, SignedEvent} from '@welshman/util' import type {TrustedEvent, HashedEvent, EventTemplate, SignedEvent, StampedEvent, OwnedEvent} from '@welshman/util'
import {isStampedEvent, isOwnedEvent, isHashedEvent, isUnwrappedEvent, isSignedEvent} from '@welshman/util'
import {publish, PublishStatus} from "@welshman/net" import {publish, PublishStatus} from "@welshman/net"
import {repository, tracker} from './core' import {repository, tracker} from './core'
import {pubkey, getSession, getSigner} from './session' import {pubkey, getSession, getSigner} from './session'
@@ -20,7 +21,7 @@ export type PublishStatusDataByUrlById = Record<string, PublishStatusDataByUrl>
export const publishStatusData = writable<PublishStatusDataByUrlById>({}) export const publishStatusData = writable<PublishStatusDataByUrlById>({})
export type Thunk = { export type Thunk = {
event: HashedEvent event: TrustedEvent
relays: string[] relays: string[]
} }
@@ -31,31 +32,45 @@ export type ThunkWithResolve = Thunk & {
export const thunkWorker = new Worker<ThunkWithResolve>() export const thunkWorker = new Worker<ThunkWithResolve>()
thunkWorker.addGlobalHandler(async ({event, relays, resolve}: ThunkWithResolve) => { thunkWorker.addGlobalHandler(async ({event, relays, resolve}: ThunkWithResolve) => {
const session = getSession(event.pubkey) // If we were given a wrapped event, make sure to publish the wrapper, not the rumor
if (isUnwrappedEvent(event)) {
if (!session) { event = event.wrap
return console.warn(`No session found for ${event.pubkey}`)
} }
const signedEvent = await getSigner(session)!.sign(event) // If the event was already signed, leave it alone. Otherwise, sign it now. This is to
// decrease apparent latency in the UI that results from waiting for remote signers
if (!isSignedEvent(event)) {
const signer = getSigner(getSession(event.pubkey))
if (!signer) {
return console.warn(`No signer found for ${event.pubkey}`)
}
event = await signer.sign(event)
}
// We're guaranteed to have a signed event at this point
const signedEvent = event as SignedEvent
const {id, sig} = signedEvent
// Send it off
const pub = publish({event: signedEvent, relays}) const pub = publish({event: signedEvent, relays})
// Copy the signature over since we had deferred it // Copy the signature over since we had deferred it
const savedEvent = repository.getEvent(signedEvent.id) as SignedEvent const savedEvent = repository.getEvent(id) as SignedEvent
// The event may already be replaced or deleted // The event may already be replaced or deleted
if (savedEvent) { if (savedEvent) {
savedEvent.sig = signedEvent.sig savedEvent.sig = sig
} }
// Track publish success // Track publish success
const {id} = event
const statusByUrl: PublishStatusDataByUrl = {} const statusByUrl: PublishStatusDataByUrl = {}
pub.emitter.on("*", (status: PublishStatus, url: string, message: string) => { pub.emitter.on("*", (status: PublishStatus, url: string, message: string) => {
publishStatusData.update( Object.assign(statusByUrl, {[url]: {id, url, status, message}})
assoc(id, Object.assign(statusByUrl, {[url]: {id, url, status, message}})),
) publishStatusData.update(assoc(id, statusByUrl))
if (status === PublishStatus.Success) { if (status === PublishStatus.Success) {
tracker.track(id, url) tracker.track(id, url)
@@ -70,23 +85,31 @@ thunkWorker.addGlobalHandler(async ({event, relays, resolve}: ThunkWithResolve)
}) })
}) })
export type ThunkParams = { export type ThunkEvent = EventTemplate | StampedEvent | OwnedEvent | TrustedEvent
event: EventTemplate
relays: string[]
}
export const makeThunk = ({event, relays}: ThunkParams) => { export const prepEvent = (event: ThunkEvent) => {
const $pubkey = get(pubkey) if (!isStampedEvent(event as StampedEvent)) {
event = stamp(event)
if (!$pubkey) {
throw new Error("Unable to make thunk if no user is logged in")
} }
return {event: hash(own(stamp(event), $pubkey)), relays} if (!isOwnedEvent(event as OwnedEvent)) {
event = own(event as StampedEvent, get(pubkey)!)
}
if (!isHashedEvent(event as HashedEvent)) {
event = hash(event as OwnedEvent)
}
return event as TrustedEvent
} }
export const publishThunk = (thunk: Thunk) => export const makeThunk = ({event, relays}: {event: ThunkEvent, relays: string[]}) =>
({event, relays})
export const publishThunk = ({event, relays}: Thunk) =>
new Promise<PublishStatusDataByUrl>(resolve => { new Promise<PublishStatusDataByUrl>(resolve => {
thunkWorker.push({...thunk, resolve}) event = prepEvent(event)
repository.publish(thunk.event)
thunkWorker.push({event, relays, resolve})
repository.publish(event)
}) })
+3 -2
View File
@@ -1,5 +1,6 @@
import {parseJson, append, nthNe, nthEq} from "@welshman/lib" import {parseJson, append, nthNe, nthEq} from "@welshman/lib"
import {Address} from "./Address" import {Address} from "./Address"
import {uniqTags} from "./Tags"
import {isShareableRelayUrl} from "./Relay" import {isShareableRelayUrl} from "./Relay"
import {Encryptable, DecryptedEvent} from "./Encryptable" import {Encryptable, DecryptedEvent} from "./Encryptable"
import type {EncryptableUpdates} from "./Encryptable" import type {EncryptableUpdates} from "./Encryptable"
@@ -63,7 +64,7 @@ export const addToListPublicly = (list: List, tag: string[]) => {
const template = { const template = {
kind: list.kind, kind: list.kind,
content: list.event?.content || "", content: list.event?.content || "",
tags: append(tag, list.publicTags), tags: uniqTags(append(tag, list.publicTags)),
} }
return new Encryptable(template, {}) return new Encryptable(template, {})
@@ -76,6 +77,6 @@ export const addToListPrivately = (list: List, tag: string[]) => {
} }
return new Encryptable(template, { return new Encryptable(template, {
content: JSON.stringify(append(tag, list.privateTags)), content: JSON.stringify(uniqTags(append(tag, list.privateTags))),
}) })
} }