Add some common commands

This commit is contained in:
Jon Staab
2024-10-08 15:24:08 -07:00
parent a4085af9e3
commit cabeba4533
6 changed files with 70 additions and 31 deletions
+35
View File
@@ -0,0 +1,35 @@
import {get} from 'svelte/store'
import {ctx} from '@welshman/lib'
import {addToListPublicly, removeFromList, makeList, FOLLOWS, MUTES} from '@welshman/util'
import {userFollows, userMutes} from './user'
import {nip44EncryptToSelf} from './session'
import {publishThunk} from './thunk'
export const unfollow = async (value: string) => {
const list = get(userFollows) || makeList({kind: FOLLOWS})
const event = await removeFromList(list, value).reconcile(nip44EncryptToSelf)
return publishThunk({event, relays: ctx.app.router.WriteRelays().getUrls()})
}
export const follow = async (tag: string[]) => {
const list = get(userFollows) || makeList({kind: FOLLOWS})
const event = await addToListPublicly(list, tag).reconcile(nip44EncryptToSelf)
return publishThunk({event, relays: ctx.app.router.WriteRelays().getUrls()})
}
export const unmute = async (value: string) => {
const list = get(userMutes) || makeList({kind: MUTES})
const event = await removeFromList(list, value).reconcile(nip44EncryptToSelf)
return publishThunk({event, relays: ctx.app.router.WriteRelays().getUrls()})
}
export const mute = async (tag: string[]) => {
const list = get(userMutes) || makeList({kind: MUTES})
const event = await addToListPublicly(list, tag).reconcile(nip44EncryptToSelf)
return publishThunk({event, relays: ctx.app.router.WriteRelays().getUrls()})
}
+1
View File
@@ -1,6 +1,7 @@
export * from './context' export * from './context'
export * from './core' export * from './core'
export * from './collection' export * from './collection'
export * from './commands'
export * from './freshness' export * from './freshness'
export * from './follows' export * from './follows'
export * from './handles' export * from './handles'
+11
View File
@@ -82,3 +82,14 @@ export const onAuth = async (url: string, challenge: string) => {
return event return event
} }
export const nip44EncryptToSelf = (payload: string) => {
const $pubkey = pubkey.get()
const $signer = signer.get()
if (!$signer) {
throw new Error("Unable to encrypt to self without valid signer")
}
return $signer.nip44.encrypt($pubkey!, payload)
}
+10 -9
View File
@@ -20,12 +20,9 @@ export type PublishStatusDataByUrlById = Record<string, PublishStatusDataByUrl>
export const publishStatusData = writable<PublishStatusDataByUrlById>({}) export const publishStatusData = writable<PublishStatusDataByUrlById>({})
export type Thunk = { export type ThunkWithResolve = {
event: TrustedEvent event: TrustedEvent
relays: string[] relays: string[]
}
export type ThunkWithResolve = Thunk & {
resolve: (data: PublishStatusDataByUrl) => void resolve: (data: PublishStatusDataByUrl) => void
} }
@@ -103,13 +100,17 @@ export const prepEvent = (event: ThunkEvent) => {
return event as TrustedEvent return event as TrustedEvent
} }
export const makeThunk = ({event, relays}: {event: ThunkEvent, relays: string[]}) => export type ThunkParams = {
({event, relays}) event: ThunkEvent
relays: string[]
}
export const publishThunk = ({event, relays}: Thunk) => export const makeThunk = (params: ThunkParams) => params
export const publishThunk = (params: ThunkParams) =>
new Promise<PublishStatusDataByUrl>(resolve => { new Promise<PublishStatusDataByUrl>(resolve => {
event = prepEvent(event) const event = prepEvent(params.event)
thunkWorker.push({event, relays, resolve}) thunkWorker.push({...params, event, resolve})
repository.publish(event) repository.publish(event)
}) })
+5 -17
View File
@@ -1,21 +1,9 @@
import type {EventContent, TrustedEvent} from './Events' import type {EventContent, TrustedEvent, EventTemplate} from './Events'
export type Encrypt = (x: string) => Promise<string> export type Encrypt = (x: string) => Promise<string>
export type EncryptableParams = {
kind: number,
tags?: string[][]
content?: string
}
export type EncryptableUpdates = Partial<EventContent> export type EncryptableUpdates = Partial<EventContent>
export type EncryptableResult = {
kind: number,
tags: string[][]
content: string
}
export type DecryptedEvent = TrustedEvent & { export type DecryptedEvent = TrustedEvent & {
plaintext: EncryptableUpdates plaintext: EncryptableUpdates
} }
@@ -26,7 +14,7 @@ export const asDecryptedEvent = (event: TrustedEvent, plaintext: EncryptableUpda
/** /**
* Represents an encryptable event with optional updates. * Represents an encryptable event with optional updates.
*/ */
export class Encryptable { export class Encryptable<T extends EventTemplate> {
/** /**
* Creates an instance of Encryptable. * Creates an instance of Encryptable.
* @param event - An EventTemplate with optional tags and content. * @param event - An EventTemplate with optional tags and content.
@@ -39,14 +27,14 @@ export class Encryptable {
* const eventTemplate = await encryptable.reconcile(myEncryptFunction) * const eventTemplate = await encryptable.reconcile(myEncryptFunction)
* ``` * ```
*/ */
constructor(readonly event: EncryptableParams, readonly updates: EncryptableUpdates) {} constructor(readonly event: Partial<T>, readonly updates: EncryptableUpdates) {}
/** /**
* Encrypts plaintext updates and merges them into the event template. * Encrypts plaintext updates and merges them into the event template.
* @param encrypt - The encryption function to be used. * @param encrypt - The encryption function to be used.
* @returns A promise that resolves to the reconciled and encrypted event. * @returns A promise that resolves to the reconciled and encrypted event.
*/ */
async reconcile(encrypt: Encrypt): Promise<EncryptableResult> { async reconcile(encrypt: Encrypt) {
const encryptContent = () => { const encryptContent = () => {
if (!this.updates.content) return null if (!this.updates.content) return null
@@ -72,6 +60,6 @@ export class Encryptable {
...this.event, ...this.event,
tags: tags || this.event.tags || [], tags: tags || this.event.tags || [],
content: content || this.event.content || "", content: content || this.event.content || "",
} } as T
} }
} }
+8 -5
View File
@@ -1,4 +1,4 @@
import {parseJson, append, nthNe, nthEq} from "@welshman/lib" import {parseJson, append, nthEq} from "@welshman/lib"
import {Address} from "./Address" import {Address} from "./Address"
import {uniqTags} from "./Tags" import {uniqTags} from "./Tags"
import {isShareableRelayUrl} from "./Relay" import {isShareableRelayUrl} from "./Relay"
@@ -44,22 +44,25 @@ export const readList = (event: DecryptedEvent): PublishedList => {
export const getListTags = (list: List | undefined) => export const getListTags = (list: List | undefined) =>
[...list?.publicTags || [], ...list?.privateTags || []] [...list?.publicTags || [], ...list?.privateTags || []]
export const removeFromList = (list: List, value: string) => { export const removeFromListByPredicate = (list: List, pred: (t: string[]) => boolean) => {
const plaintext: EncryptableUpdates = {} const plaintext: EncryptableUpdates = {}
const template = { const template = {
kind: list.kind, kind: list.kind,
content: list.event?.content || "", content: list.event?.content || "",
tags: list.publicTags.filter(nthNe(1, value)), tags: list.publicTags.filter(t => !pred(t)),
} }
// Avoid redundant encrypt calls if possible // Avoid redundant encrypt calls if possible
if (list.privateTags.some(nthEq(1, value))) { if (list.privateTags.some(t => pred(t))) {
plaintext.content = JSON.stringify(list.privateTags.filter(nthNe(1, value))) plaintext.content = JSON.stringify(list.privateTags.filter(t => !pred(t)))
} }
return new Encryptable(template, plaintext) return new Encryptable(template, plaintext)
} }
export const removeFromList = (list: List, value: string) =>
removeFromListByPredicate(list, nthEq(1, value))
export const addToListPublicly = (list: List, tag: string[]) => { export const addToListPublicly = (list: List, tag: string[]) => {
const template = { const template = {
kind: list.kind, kind: list.kind,