From ee9f941b1194ad4d33d18a4caf7ddf5d0101b4c2 Mon Sep 17 00:00:00 2001 From: kieran Date: Mon, 22 Apr 2024 21:15:43 +0100 Subject: [PATCH] fix: moderation updates --- packages/app/src/Hooks/useModeration.tsx | 18 ++++------ .../app/src/Pages/settings/Moderation.tsx | 9 +++-- packages/system/src/nostr-link.ts | 33 ++++++++++++++----- packages/system/src/query.ts | 4 +-- packages/system/src/request-builder.ts | 2 +- packages/system/src/sync/diff-sync.ts | 25 +++++++++----- packages/system/src/user-state.ts | 9 ++--- 7 files changed, 59 insertions(+), 41 deletions(-) diff --git a/packages/app/src/Hooks/useModeration.tsx b/packages/app/src/Hooks/useModeration.tsx index c8e0c185..d2a93771 100644 --- a/packages/app/src/Hooks/useModeration.tsx +++ b/packages/app/src/Hooks/useModeration.tsx @@ -1,10 +1,10 @@ import { dedupe } from "@snort/shared"; -import { EventKind, NostrEvent, NostrLink, TaggedNostrEvent, ToNostrEventTag } from "@snort/system"; +import { EventKind, NostrEvent, NostrLink, TaggedNostrEvent, ToNostrEventTag, UnknownTag } from "@snort/system"; import useLogin from "@/Hooks/useLogin"; export class MutedWordTag implements ToNostrEventTag { - constructor(readonly word: string) {} + constructor(readonly word: string) { } toEventTag(): string[] | undefined { return ["word", this.word.toLowerCase()]; @@ -43,11 +43,11 @@ export default function useModeration() { } async function addMutedWord(word: string) { - await state.addToList(EventKind.MuteList, new MutedWordTag(word.toLowerCase())); + await state.addToList(EventKind.MuteList, new MutedWordTag(word.toLowerCase()), true); } async function removeMutedWord(word: string) { - await state.removeFromList(EventKind.MuteList, new MutedWordTag(word.toLowerCase())); + await state.removeFromList(EventKind.MuteList, new MutedWordTag(word.toLowerCase()), true); } function isEventMuted(ev: TaggedNostrEvent | NostrEvent) { @@ -56,13 +56,9 @@ export default function useModeration() { function getMutedWords() { return state - .getList(EventKind.MuteList, o => { - if (o[0] === "word") { - return new MutedWordTag(o[1]); - } - }) - .filter(a => a instanceof MutedWordTag) - .map(a => (a as MutedWordTag).word); + .getList(EventKind.MuteList) + .filter(a => a instanceof UnknownTag && a.value[0] === "word") + .map(a => (a as UnknownTag).value[1]); } return { diff --git a/packages/app/src/Pages/settings/Moderation.tsx b/packages/app/src/Pages/settings/Moderation.tsx index f91a8340..df3752f5 100644 --- a/packages/app/src/Pages/settings/Moderation.tsx +++ b/packages/app/src/Pages/settings/Moderation.tsx @@ -48,16 +48,19 @@ export default function ModerationSettingsPage() { value={muteWord} onChange={e => setMuteWord(e.target.value.toLowerCase())} /> - addMutedWord(muteWord)}> + { + await addMutedWord(muteWord); + setMuteWord(""); + }}> {getMutedWords().map(v => (
{v}
- +
))} diff --git a/packages/system/src/nostr-link.ts b/packages/system/src/nostr-link.ts index d1e97acc..aef8559e 100644 --- a/packages/system/src/nostr-link.ts +++ b/packages/system/src/nostr-link.ts @@ -12,12 +12,15 @@ import { } from "."; import { findTag } from "./utils"; +/** + * An object which can be stored in a nostr event as a tag + */ export interface ToNostrEventTag { toEventTag(): Array | undefined; } export class NostrHashtagLink implements ToNostrEventTag { - constructor(readonly tag: string) {} + constructor(readonly tag: string) { } toEventTag() { return ["t", this.tag]; @@ -25,7 +28,7 @@ export class NostrHashtagLink implements ToNostrEventTag { } export class UnknownTag implements ToNostrEventTag { - constructor(readonly value: Array) {} + constructor(readonly value: Array) { } toEventTag(): string[] | undefined { return this.value; } @@ -191,11 +194,10 @@ export class NostrLink implements ToNostrEventTag { } } - static fromTag( + static fromTag( tag: Array, author?: string, kind?: number, - fnOther?: (tag: Array) => T | undefined, ) { const relays = tag.length > 2 ? [tag[2]] : undefined; switch (tag[0]) { @@ -209,17 +211,15 @@ export class NostrLink implements ToNostrEventTag { const [kind, author, dTag] = tag[1].split(":"); return new NostrLink(NostrPrefix.Address, dTag, Number(kind), author, relays, tag[3]); } - default: { - return fnOther?.(tag) ?? new UnknownTag(tag); - } } + throw new Error("Unknown tag!"); } - static fromTags(tags: ReadonlyArray>, fnOther?: (tag: Array) => T | undefined) { + static fromTags(tags: ReadonlyArray>) { return removeUndefined( tags.map(a => { try { - return NostrLink.fromTag(a, undefined, undefined, fnOther); + return NostrLink.fromTag(a); } catch { // ignored, cant be mapped } @@ -227,6 +227,21 @@ export class NostrLink implements ToNostrEventTag { ); } + /** + * Parse all tags even if they are unknown + */ + static fromAllTags(tags: ReadonlyArray>): Array { + return removeUndefined( + tags.map(a => { + try { + return NostrLink.fromTag(a); + } catch { + return new UnknownTag(a); + } + }), + ); + } + static fromEvent(ev: TaggedNostrEvent | NostrEvent) { const relays = "relays" in ev ? ev.relays : undefined; diff --git a/packages/system/src/query.ts b/packages/system/src/query.ts index 22f22c72..8d09f8c9 100644 --- a/packages/system/src/query.ts +++ b/packages/system/src/query.ts @@ -343,11 +343,11 @@ export class Query extends EventEmitter { this.#log("Starting emit of %s", this.id); const existing = this.filters; if (!(this.request.options?.skipDiff ?? false) && existing.length > 0) { - const filters = await this.request.buildDiff(this.#system, existing); + const filters = this.request.buildDiff(this.#system, existing); this.#log("Build %s %O", this.id, filters); filters.forEach(f => this.emit("request", this.id, f)); } else { - const filters = await this.request.build(this.#system); + const filters = this.request.build(this.#system); this.#log("Build %s %O", this.id, filters); filters.forEach(f => this.emit("request", this.id, f)); } diff --git a/packages/system/src/request-builder.ts b/packages/system/src/request-builder.ts index 26344bfa..6ea919c7 100644 --- a/packages/system/src/request-builder.ts +++ b/packages/system/src/request-builder.ts @@ -121,7 +121,7 @@ export class RequestBuilder { /** * Detects a change in request from a previous set of filters */ - async buildDiff(system: SystemInterface, prev: Array): Promise> { + buildDiff(system: SystemInterface, prev: Array): Array { const start = unixNowMs(); let rawFilters = this.buildRaw(); diff --git a/packages/system/src/sync/diff-sync.ts b/packages/system/src/sync/diff-sync.ts index c899d009..72699635 100644 --- a/packages/system/src/sync/diff-sync.ts +++ b/packages/system/src/sync/diff-sync.ts @@ -45,7 +45,7 @@ export class DiffSyncTags extends EventEmitter { * Get decrypted content */ get encryptedTags() { - if (this.#decryptedContent) { + if (this.#decryptedContent && this.#decryptedContent.startsWith("[") && this.#decryptedContent.endsWith("]")) { const tags = JSON.parse(this.#decryptedContent) as Array>; return tags; } @@ -99,11 +99,7 @@ export class DiffSyncTags extends EventEmitter { async sync(signer: EventSigner, system: SystemInterface) { await this.#sync.sync(system); - if ( - this.#sync.value?.content && - this.#sync.value?.content.startsWith("[") && - this.#sync.value?.content.endsWith("]") - ) { + if (this.#sync.value?.content) { const decrypted = await signer.nip4Decrypt(this.#sync.value.content, await signer.getPubKey()); this.#decryptedContent = decrypted; } @@ -140,10 +136,9 @@ export class DiffSyncTags extends EventEmitter { } // apply changes onto next - this.#applyChanges(next.tags, this.#changes); + next.tags = this.#applyChanges(next.tags, this.#changes); if (this.#changesEncrypted.length > 0 && !content) { - const encryptedTags = isNew ? [] : this.encryptedTags; - this.#applyChanges(encryptedTags, this.#changesEncrypted); + const encryptedTags = this.#applyChanges(isNew ? [] : this.encryptedTags, this.#changesEncrypted); next.content = JSON.stringify(encryptedTags); } else if (content) { next.content = content; @@ -206,5 +201,17 @@ export class DiffSyncTags extends EventEmitter { } } } + + // remove duplicates + return tags.filter((v, i, arr) => { + let hasAnother = false; + for (let x = i + 1; x < arr.length; x++) { + if (arr[x][0] === v[0] && arr[x][1] === v[1]) { + hasAnother = true; + break; + } + } + return !hasAnother; + }); } } diff --git a/packages/system/src/user-state.ts b/packages/system/src/user-state.ts index 2dac1bdb..e882d88a 100644 --- a/packages/system/src/user-state.ts +++ b/packages/system/src/user-state.ts @@ -188,7 +188,7 @@ export class UserState extends EventEmitter { get muted() { const list = this.#standardLists.get(EventKind.MuteList); if (list) { - return NostrLink.fromTags(list.encryptedTags); + return NostrLink.fromAllTags(list.encryptedTags); } return []; } @@ -396,12 +396,9 @@ export class UserState extends EventEmitter { return false; } - getList( - kind: EventKind, - fnOther?: (tag: Array) => T | undefined, - ): Array { + getList(kind: EventKind): Array { const list = this.#standardLists.get(kind); - return NostrLink.fromTags(list?.tags ?? [], fnOther); + return NostrLink.fromAllTags(list?.tags ?? []); } serialize(): UserStateObject {