From 2156aeaeb56a00fc489891a1ccf63c5dfeb5f985 Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Mon, 7 Oct 2024 14:03:12 -0700 Subject: [PATCH] Add update functions to lists, document Encryptable, fix tag inheritance --- packages/app/src/tags.ts | 4 +-- packages/util/src/Encryptable.ts | 28 ++++++++++++++++++--- packages/util/src/List.ts | 42 ++++++++++++++++++++++++++++++-- 3 files changed, 67 insertions(+), 7 deletions(-) diff --git a/packages/app/src/tags.ts b/packages/app/src/tags.ts index b682a91..f4125c2 100644 --- a/packages/app/src/tags.ts +++ b/packages/app/src/tags.ts @@ -40,14 +40,14 @@ export const tagReplyTo = (event: TrustedEvent) => { } // Inherit p-tag mentions - for (const pubkey of getPubkeyTagValues(tags)) { + for (const pubkey of getPubkeyTagValues(event.tags)) { if (pubkey !== $pubkey) { tags.push(tagPubkey(pubkey)) } } // Based on NIP 10 legacy tags, order is root, mentions, reply - const {roots, replies, mentions} = getAncestorTags(tags) + const {roots, replies, mentions} = getAncestorTags(event.tags) // Root comes first if (roots.length > 0) { diff --git a/packages/util/src/Encryptable.ts b/packages/util/src/Encryptable.ts index c46dc88..17938a9 100644 --- a/packages/util/src/Encryptable.ts +++ b/packages/util/src/Encryptable.ts @@ -8,6 +8,8 @@ export type EncryptableParams = { content?: string } +export type EncryptableUpdates = Partial + export type EncryptableResult = { kind: number, tags: string[][] @@ -15,15 +17,35 @@ export type EncryptableResult = { } export type DecryptedEvent = TrustedEvent & { - plaintext: Partial + plaintext: EncryptableUpdates } -export const asDecryptedEvent = (event: TrustedEvent, plaintext: Partial = {}) => +export const asDecryptedEvent = (event: TrustedEvent, plaintext: EncryptableUpdates = {}) => ({...event, plaintext}) as DecryptedEvent +/** + * Represents an encryptable event with optional updates. + */ export class Encryptable { - constructor(readonly event: EncryptableParams, readonly updates: Partial) {} + /** + * Creates an instance of Encryptable. + * @param event - An EventTemplate with optional tags and content. + * @param updates - Plaintext updates to be applied to the event content. + * @example + * Here's an example which enables updating a private mute list: + * ``` + * const event = {kind: 10000, content: "", tags: []} // An event, only kind is required + * const encryptable = new Encryptable(event, {content: JSON.stringify([["e", "bad word"]])}) + * const eventTemplate = await encryptable.reconcile(myEncryptFunction) + * ``` + */ + constructor(readonly event: EncryptableParams, readonly updates: EncryptableUpdates) {} + /** + * Encrypts plaintext updates and merges them into the event template. + * @param encrypt - The encryption function to be used. + * @returns A promise that resolves to the reconciled and encrypted event. + */ async reconcile(encrypt: Encrypt): Promise { const encryptContent = () => { if (!this.updates.content) return null diff --git a/packages/util/src/List.ts b/packages/util/src/List.ts index 69364bb..7afcc76 100644 --- a/packages/util/src/List.ts +++ b/packages/util/src/List.ts @@ -1,7 +1,8 @@ -import {parseJson} from "@welshman/lib" +import {parseJson, append, nthNe, nthEq} from "@welshman/lib" import {Address} from "./Address" import {isShareableRelayUrl} from "./Relay" -import {DecryptedEvent} from "./Encryptable" +import {Encryptable, DecryptedEvent} from "./Encryptable" +import type {EncryptableUpdates} from "./Encryptable" export type ListParams = { kind: number @@ -41,3 +42,40 @@ export const readList = (event: DecryptedEvent): PublishedList => { export const getListTags = (list: List | undefined) => [...list?.publicTags || [], ...list?.privateTags || []] + +export const removeFromList = (list: List, value: string) => { + const plaintext: EncryptableUpdates = {} + const template = { + kind: list.kind, + content: list.event?.content || "", + tags: list.publicTags.filter(nthNe(1, value)), + } + + // Avoid redundant encrypt calls if possible + if (list.privateTags.some(nthEq(1, value))) { + plaintext.content = JSON.stringify(list.privateTags.filter(nthNe(1, value))) + } + + return new Encryptable(template, plaintext) +} + +export const addToListPublicly = (list: List, tag: string[]) => { + const template = { + kind: list.kind, + content: list.event?.content || "", + tags: append(tag, list.publicTags), + } + + return new Encryptable(template, {}) +} + +export const addToListPrivately = (list: List, tag: string[]) => { + const template = { + kind: list.kind, + tags: list.publicTags, + } + + return new Encryptable(template, { + content: JSON.stringify(append(tag, list.privateTags)), + }) +}