Add anonymous session, re-work thunk utilities
This commit is contained in:
@@ -19,6 +19,7 @@ export enum SessionMethod {
|
||||
Nip46 = "nip46",
|
||||
Nip55 = "nip55",
|
||||
Pubkey = "pubkey",
|
||||
Anonymous = "anonymous",
|
||||
}
|
||||
|
||||
export type SessionNip01 = {
|
||||
@@ -53,12 +54,17 @@ export type SessionPubkey = {
|
||||
pubkey: string
|
||||
}
|
||||
|
||||
export type SessionAnonymous = {
|
||||
method: SessionMethod.Anonymous
|
||||
}
|
||||
|
||||
export type SessionAnyMethod =
|
||||
| SessionNip01
|
||||
| SessionNip07
|
||||
| SessionNip46
|
||||
| SessionNip55
|
||||
| SessionPubkey
|
||||
| SessionAnonymous
|
||||
|
||||
export type Session = SessionAnyMethod & {wallet?: Wallet} & Record<string, any>
|
||||
|
||||
|
||||
+107
-86
@@ -3,13 +3,12 @@ import {writable, get} from "svelte/store"
|
||||
import {
|
||||
TaskQueue,
|
||||
ifLet,
|
||||
ensurePlural,
|
||||
dissoc,
|
||||
remove,
|
||||
defer,
|
||||
sleep,
|
||||
assoc,
|
||||
spec,
|
||||
nthEq,
|
||||
nth,
|
||||
} from "@welshman/lib"
|
||||
import {stamp, own, hash} from "@welshman/signer"
|
||||
@@ -68,6 +67,13 @@ export class Thunk {
|
||||
for (const relay of options.relays) {
|
||||
this.status[relay] = PublishStatus.Sending
|
||||
}
|
||||
|
||||
this.controller.signal.addEventListener("abort", () => {
|
||||
console.log("abort")
|
||||
for (const relay of options.relays) {
|
||||
this._setAborted(relay)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
_notify() {
|
||||
@@ -85,6 +91,26 @@ export class Thunk {
|
||||
this._notify()
|
||||
}
|
||||
|
||||
_setPending(relay: string) {
|
||||
this.options.onPending?.(relay)
|
||||
this.status[relay] = PublishStatus.Pending
|
||||
this._notify()
|
||||
}
|
||||
|
||||
_setTimeout(relay: string) {
|
||||
this.options.onTimeout?.(relay)
|
||||
this.status[relay] = PublishStatus.Timeout
|
||||
this.details[relay] = "Publish timed out"
|
||||
this._notify()
|
||||
}
|
||||
|
||||
_setAborted(relay: string) {
|
||||
this.options.onAborted?.(relay)
|
||||
this.status[relay] = PublishStatus.Aborted
|
||||
this.details[relay] = "Publish was aborted"
|
||||
this._notify()
|
||||
}
|
||||
|
||||
async publish() {
|
||||
let event = this.event
|
||||
|
||||
@@ -149,21 +175,13 @@ export class Thunk {
|
||||
this._notify()
|
||||
},
|
||||
onPending: (relay: string) => {
|
||||
this.options.onPending?.(relay)
|
||||
this.status[relay] = PublishStatus.Pending
|
||||
this._notify()
|
||||
this._setPending(relay)
|
||||
},
|
||||
onTimeout: (relay: string) => {
|
||||
this.options.onTimeout?.(relay)
|
||||
this.status[relay] = PublishStatus.Timeout
|
||||
this.details[relay] = "Publish timed out"
|
||||
this._notify()
|
||||
this._setTimeout(relay)
|
||||
},
|
||||
onAborted: (relay: string) => {
|
||||
this.options.onAborted?.(relay)
|
||||
this.status[relay] = PublishStatus.Aborted
|
||||
this.details[relay] = "Publish was aborted"
|
||||
this._notify()
|
||||
this._setAborted(relay)
|
||||
},
|
||||
onComplete: () => {
|
||||
this.options.onComplete?.()
|
||||
@@ -187,24 +205,21 @@ export class Thunk {
|
||||
export class MergedThunk {
|
||||
_subs: Subscriber<MergedThunk>[] = []
|
||||
|
||||
controller = new AbortController()
|
||||
status: PublishStatusByRelay = {}
|
||||
details: Record<string, string> = {}
|
||||
|
||||
constructor(readonly thunks: Thunk[]) {
|
||||
const {Aborted, Failure, Timeout, Pending, Success} = PublishStatus
|
||||
const relays = new Set(thunks.flatMap(thunk => Object.keys(thunk.options.relays)))
|
||||
const {Aborted, Failure, Timeout, Pending, Sending, Success} = PublishStatus
|
||||
const relays = new Set(thunks.flatMap(thunk => thunk.options.relays))
|
||||
|
||||
for (const thunk of thunks) {
|
||||
this.controller.signal.addEventListener("abort", () => thunk.controller.abort())
|
||||
|
||||
thunk.subscribe($thunk => {
|
||||
this.status = {}
|
||||
this.details = {}
|
||||
|
||||
for (const relay of relays) {
|
||||
for (const status of [Aborted, Failure, Timeout, Pending, Success]) {
|
||||
const thunk = thunks.find(spec({[relay]: status}))
|
||||
for (const status of [Aborted, Failure, Timeout, Pending, Sending, Success]) {
|
||||
const thunk = thunks.find(t => t.status[relay] === status)
|
||||
|
||||
if (thunk) {
|
||||
this.status[relay] = thunk.status[relay]!
|
||||
@@ -213,9 +228,11 @@ export class MergedThunk {
|
||||
}
|
||||
}
|
||||
|
||||
console.log(this.status)
|
||||
|
||||
this._notify()
|
||||
|
||||
if (thunks.filter(thunkIsComplete).length === thunks.length) {
|
||||
if (thunks.every(thunkIsComplete)) {
|
||||
this._subs = []
|
||||
}
|
||||
})
|
||||
@@ -246,79 +263,65 @@ export const isThunk = (thunk: AbstractThunk): thunk is Thunk => thunk instanceo
|
||||
export const isMergedThunk = (thunk: AbstractThunk): thunk is MergedThunk =>
|
||||
thunk instanceof MergedThunk
|
||||
|
||||
export const thunkHasStatus = (thunk: AbstractThunk, status: PublishStatus) =>
|
||||
Object.entries(thunk.status).some(nthEq(1, status))
|
||||
// Thunk status urls
|
||||
|
||||
export const thunkUrlsWithStatus = (thunk: AbstractThunk, status: PublishStatus) =>
|
||||
Object.entries(thunk.status).filter(nthEq(1, status)).map(nth(0))
|
||||
|
||||
export const thunkCompleteUrls = (thunk: AbstractThunk) => {
|
||||
const incompleteStatuses = [PublishStatus.Sending, PublishStatus.Pending]
|
||||
export const getThunkUrlsWithStatus = (
|
||||
statuses: PublishStatus | PublishStatus[],
|
||||
thunk: AbstractThunk,
|
||||
) => {
|
||||
statuses = ensurePlural(statuses)
|
||||
|
||||
return Object.entries(thunk.status)
|
||||
.filter(([_, s]) => !incompleteStatuses.includes(s))
|
||||
.map(nth(1))
|
||||
.filter(([_, status]) => statuses.includes(status))
|
||||
.map(nth(0))
|
||||
}
|
||||
|
||||
export const thunkIncompleteUrls = (thunk: AbstractThunk) => {
|
||||
const incompleteStatuses = [PublishStatus.Sending, PublishStatus.Pending]
|
||||
export const getCompleteThunkUrls = (thunk: AbstractThunk) =>
|
||||
getThunkUrlsWithStatus([PublishStatus.Sending, PublishStatus.Pending], thunk)
|
||||
|
||||
return Object.entries(thunk.status)
|
||||
.filter(([_, s]) => incompleteStatuses.includes(s))
|
||||
.map(nth(1))
|
||||
}
|
||||
export const getIncompleteThunkUrls = (thunk: AbstractThunk) =>
|
||||
getThunkUrlsWithStatus([PublishStatus.Sending, PublishStatus.Pending], thunk)
|
||||
|
||||
export const thunkIsComplete = (thunk: AbstractThunk) => thunkCompleteUrls(thunk).length > 0
|
||||
export const getFailedThunkUrls = (thunk: AbstractThunk) =>
|
||||
getThunkUrlsWithStatus([PublishStatus.Failure, PublishStatus.Timeout], thunk)
|
||||
|
||||
export const getThunkError = (thunk: Thunk) =>
|
||||
new Promise<string>(resolve => {
|
||||
thunk.subscribe($thunk => {
|
||||
for (const [relay, status] of Object.entries($thunk.status)) {
|
||||
if (status === PublishStatus.Failure) {
|
||||
resolve($thunk.details[relay])
|
||||
}
|
||||
}
|
||||
// Thunk status checks
|
||||
|
||||
if (thunkIsComplete($thunk)) {
|
||||
resolve("")
|
||||
}
|
||||
})
|
||||
})
|
||||
export const thunkHasStatus = (statuses: PublishStatus | PublishStatus[], thunk: AbstractThunk) =>
|
||||
getThunkUrlsWithStatus(statuses, thunk).length > 0
|
||||
|
||||
export const waitForThunkStatus = (thunk: Thunk, status: PublishStatus) =>
|
||||
new Promise<boolean>(resolve => {
|
||||
thunk.subscribe($thunk => {
|
||||
for (const [_, s] of Object.entries($thunk.status)) {
|
||||
if (s === status) {
|
||||
resolve(true)
|
||||
}
|
||||
}
|
||||
export const thunkIsComplete = (thunk: AbstractThunk) =>
|
||||
!thunkHasStatus([PublishStatus.Sending, PublishStatus.Pending], thunk)
|
||||
|
||||
if (thunkIsComplete($thunk)) {
|
||||
resolve(false)
|
||||
}
|
||||
})
|
||||
})
|
||||
// Thunk errors
|
||||
|
||||
export const waitForThunkCompletion = (thunk: Thunk) =>
|
||||
new Promise<void>(resolve => {
|
||||
thunk.subscribe($thunk => {
|
||||
if (thunkIsComplete($thunk)) {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
export function* walkThunks(thunks: AbstractThunk[]): Iterable<Thunk> {
|
||||
for (const thunk of thunks) {
|
||||
if (thunk instanceof MergedThunk) {
|
||||
yield* walkThunks(thunk.thunks)
|
||||
} else {
|
||||
yield thunk
|
||||
export const getThunkError = (thunk: Thunk) => {
|
||||
for (const [relay, status] of Object.entries(thunk.status)) {
|
||||
if (status === PublishStatus.Failure) {
|
||||
return thunk.details[relay]
|
||||
}
|
||||
}
|
||||
|
||||
if (thunkIsComplete(thunk)) {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// Thunk utilities that return promises
|
||||
|
||||
export const waitForThunkError = (thunk: Thunk) =>
|
||||
new Promise<string>(resolve => {
|
||||
thunk.subscribe($thunk => {
|
||||
const error = getThunkError($thunk)
|
||||
|
||||
if (error !== undefined) {
|
||||
resolve(error)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Thunk state
|
||||
|
||||
export const thunks = writable<Record<string, AbstractThunk>>({})
|
||||
|
||||
export const thunkQueue = new TaskQueue<Thunk>({
|
||||
@@ -328,6 +331,21 @@ export const thunkQueue = new TaskQueue<Thunk>({
|
||||
},
|
||||
})
|
||||
|
||||
// Other thunk utilities
|
||||
|
||||
export const mergeThunks = (thunks: AbstractThunk[]) =>
|
||||
new MergedThunk(Array.from(flattenThunks(thunks)))
|
||||
|
||||
export function* flattenThunks(thunks: AbstractThunk[]): Iterable<Thunk> {
|
||||
for (const thunk of thunks) {
|
||||
if (isMergedThunk(thunk)) {
|
||||
yield* flattenThunks(thunk.thunks)
|
||||
} else {
|
||||
yield thunk
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const publishThunk = (options: ThunkOptions) => {
|
||||
const thunk = new Thunk(options)
|
||||
|
||||
@@ -337,15 +355,18 @@ export const publishThunk = (options: ThunkOptions) => {
|
||||
|
||||
thunks.update(assoc(thunk.event.id, thunk))
|
||||
|
||||
thunk.controller.signal.addEventListener("abort", () => {
|
||||
repository.removeEvent(thunk.event.id)
|
||||
})
|
||||
|
||||
return thunk
|
||||
}
|
||||
|
||||
export const abortThunk = (thunk: Thunk) => {
|
||||
thunk.controller.abort()
|
||||
thunks.update(dissoc(thunk.event.id))
|
||||
repository.removeEvent(thunk.event.id)
|
||||
export const abortThunk = (thunk: AbstractThunk) => {
|
||||
for (const child of flattenThunks([thunk])) {
|
||||
child.controller.abort()
|
||||
thunks.update(dissoc(child.event.id))
|
||||
repository.removeEvent(child.event.id)
|
||||
}
|
||||
}
|
||||
|
||||
export const retryThunk = (thunk: AbstractThunk) =>
|
||||
isMergedThunk(thunk)
|
||||
? mergeThunks(thunk.thunks.map(t => publishThunk(t.options)))
|
||||
: publishThunk(thunk.options)
|
||||
|
||||
Reference in New Issue
Block a user