fix: moderation updates
This commit is contained in:
parent
bfdcbca08b
commit
ee9f941b11
@ -1,10 +1,10 @@
|
|||||||
import { dedupe } from "@snort/shared";
|
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";
|
import useLogin from "@/Hooks/useLogin";
|
||||||
|
|
||||||
export class MutedWordTag implements ToNostrEventTag {
|
export class MutedWordTag implements ToNostrEventTag {
|
||||||
constructor(readonly word: string) {}
|
constructor(readonly word: string) { }
|
||||||
|
|
||||||
toEventTag(): string[] | undefined {
|
toEventTag(): string[] | undefined {
|
||||||
return ["word", this.word.toLowerCase()];
|
return ["word", this.word.toLowerCase()];
|
||||||
@ -43,11 +43,11 @@ export default function useModeration() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function addMutedWord(word: string) {
|
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) {
|
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) {
|
function isEventMuted(ev: TaggedNostrEvent | NostrEvent) {
|
||||||
@ -56,13 +56,9 @@ export default function useModeration() {
|
|||||||
|
|
||||||
function getMutedWords() {
|
function getMutedWords() {
|
||||||
return state
|
return state
|
||||||
.getList(EventKind.MuteList, o => {
|
.getList(EventKind.MuteList)
|
||||||
if (o[0] === "word") {
|
.filter(a => a instanceof UnknownTag && a.value[0] === "word")
|
||||||
return new MutedWordTag(o[1]);
|
.map(a => (a as UnknownTag).value[1]);
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter(a => a instanceof MutedWordTag)
|
|
||||||
.map(a => (a as MutedWordTag).word);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -48,16 +48,19 @@ export default function ModerationSettingsPage() {
|
|||||||
value={muteWord}
|
value={muteWord}
|
||||||
onChange={e => setMuteWord(e.target.value.toLowerCase())}
|
onChange={e => setMuteWord(e.target.value.toLowerCase())}
|
||||||
/>
|
/>
|
||||||
<AsyncButton type="button" onClick={() => addMutedWord(muteWord)}>
|
<AsyncButton onClick={async () => {
|
||||||
|
await addMutedWord(muteWord);
|
||||||
|
setMuteWord("");
|
||||||
|
}}>
|
||||||
<FormattedMessage defaultMessage="Add" id="2/2yg+" />
|
<FormattedMessage defaultMessage="Add" id="2/2yg+" />
|
||||||
</AsyncButton>
|
</AsyncButton>
|
||||||
</div>
|
</div>
|
||||||
{getMutedWords().map(v => (
|
{getMutedWords().map(v => (
|
||||||
<div key={v} className="p br b flex items-center justify-between">
|
<div key={v} className="p br b flex items-center justify-between">
|
||||||
<div>{v}</div>
|
<div>{v}</div>
|
||||||
<button type="button" onClick={() => removeMutedWord(v)}>
|
<AsyncButton onClick={() => removeMutedWord(v)}>
|
||||||
<FormattedMessage defaultMessage="Delete" id="K3r6DQ" />
|
<FormattedMessage defaultMessage="Delete" id="K3r6DQ" />
|
||||||
</button>
|
</AsyncButton>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@ -12,12 +12,15 @@ import {
|
|||||||
} from ".";
|
} from ".";
|
||||||
import { findTag } from "./utils";
|
import { findTag } from "./utils";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object which can be stored in a nostr event as a tag
|
||||||
|
*/
|
||||||
export interface ToNostrEventTag {
|
export interface ToNostrEventTag {
|
||||||
toEventTag(): Array<string> | undefined;
|
toEventTag(): Array<string> | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NostrHashtagLink implements ToNostrEventTag {
|
export class NostrHashtagLink implements ToNostrEventTag {
|
||||||
constructor(readonly tag: string) {}
|
constructor(readonly tag: string) { }
|
||||||
|
|
||||||
toEventTag() {
|
toEventTag() {
|
||||||
return ["t", this.tag];
|
return ["t", this.tag];
|
||||||
@ -25,7 +28,7 @@ export class NostrHashtagLink implements ToNostrEventTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class UnknownTag implements ToNostrEventTag {
|
export class UnknownTag implements ToNostrEventTag {
|
||||||
constructor(readonly value: Array<string>) {}
|
constructor(readonly value: Array<string>) { }
|
||||||
toEventTag(): string[] | undefined {
|
toEventTag(): string[] | undefined {
|
||||||
return this.value;
|
return this.value;
|
||||||
}
|
}
|
||||||
@ -191,11 +194,10 @@ export class NostrLink implements ToNostrEventTag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromTag<T = NostrLink>(
|
static fromTag(
|
||||||
tag: Array<string>,
|
tag: Array<string>,
|
||||||
author?: string,
|
author?: string,
|
||||||
kind?: number,
|
kind?: number,
|
||||||
fnOther?: (tag: Array<string>) => T | undefined,
|
|
||||||
) {
|
) {
|
||||||
const relays = tag.length > 2 ? [tag[2]] : undefined;
|
const relays = tag.length > 2 ? [tag[2]] : undefined;
|
||||||
switch (tag[0]) {
|
switch (tag[0]) {
|
||||||
@ -209,17 +211,15 @@ export class NostrLink implements ToNostrEventTag {
|
|||||||
const [kind, author, dTag] = tag[1].split(":");
|
const [kind, author, dTag] = tag[1].split(":");
|
||||||
return new NostrLink(NostrPrefix.Address, dTag, Number(kind), author, relays, tag[3]);
|
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<T = NostrLink>(tags: ReadonlyArray<Array<string>>, fnOther?: (tag: Array<string>) => T | undefined) {
|
static fromTags(tags: ReadonlyArray<Array<string>>) {
|
||||||
return removeUndefined(
|
return removeUndefined(
|
||||||
tags.map(a => {
|
tags.map(a => {
|
||||||
try {
|
try {
|
||||||
return NostrLink.fromTag<T>(a, undefined, undefined, fnOther);
|
return NostrLink.fromTag(a);
|
||||||
} catch {
|
} catch {
|
||||||
// ignored, cant be mapped
|
// 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<string>>): Array<ToNostrEventTag> {
|
||||||
|
return removeUndefined(
|
||||||
|
tags.map(a => {
|
||||||
|
try {
|
||||||
|
return NostrLink.fromTag(a);
|
||||||
|
} catch {
|
||||||
|
return new UnknownTag(a);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
static fromEvent(ev: TaggedNostrEvent | NostrEvent) {
|
static fromEvent(ev: TaggedNostrEvent | NostrEvent) {
|
||||||
const relays = "relays" in ev ? ev.relays : undefined;
|
const relays = "relays" in ev ? ev.relays : undefined;
|
||||||
|
|
||||||
|
@ -343,11 +343,11 @@ export class Query extends EventEmitter<QueryEvents> {
|
|||||||
this.#log("Starting emit of %s", this.id);
|
this.#log("Starting emit of %s", this.id);
|
||||||
const existing = this.filters;
|
const existing = this.filters;
|
||||||
if (!(this.request.options?.skipDiff ?? false) && existing.length > 0) {
|
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);
|
this.#log("Build %s %O", this.id, filters);
|
||||||
filters.forEach(f => this.emit("request", this.id, f));
|
filters.forEach(f => this.emit("request", this.id, f));
|
||||||
} else {
|
} else {
|
||||||
const filters = await this.request.build(this.#system);
|
const filters = this.request.build(this.#system);
|
||||||
this.#log("Build %s %O", this.id, filters);
|
this.#log("Build %s %O", this.id, filters);
|
||||||
filters.forEach(f => this.emit("request", this.id, f));
|
filters.forEach(f => this.emit("request", this.id, f));
|
||||||
}
|
}
|
||||||
|
@ -121,7 +121,7 @@ export class RequestBuilder {
|
|||||||
/**
|
/**
|
||||||
* Detects a change in request from a previous set of filters
|
* Detects a change in request from a previous set of filters
|
||||||
*/
|
*/
|
||||||
async buildDiff(system: SystemInterface, prev: Array<ReqFilter>): Promise<Array<BuiltRawReqFilter>> {
|
buildDiff(system: SystemInterface, prev: Array<ReqFilter>): Array<BuiltRawReqFilter> {
|
||||||
const start = unixNowMs();
|
const start = unixNowMs();
|
||||||
|
|
||||||
let rawFilters = this.buildRaw();
|
let rawFilters = this.buildRaw();
|
||||||
|
@ -45,7 +45,7 @@ export class DiffSyncTags extends EventEmitter<SafeSyncEvents> {
|
|||||||
* Get decrypted content
|
* Get decrypted content
|
||||||
*/
|
*/
|
||||||
get encryptedTags() {
|
get encryptedTags() {
|
||||||
if (this.#decryptedContent) {
|
if (this.#decryptedContent && this.#decryptedContent.startsWith("[") && this.#decryptedContent.endsWith("]")) {
|
||||||
const tags = JSON.parse(this.#decryptedContent) as Array<Array<string>>;
|
const tags = JSON.parse(this.#decryptedContent) as Array<Array<string>>;
|
||||||
return tags;
|
return tags;
|
||||||
}
|
}
|
||||||
@ -99,11 +99,7 @@ export class DiffSyncTags extends EventEmitter<SafeSyncEvents> {
|
|||||||
async sync(signer: EventSigner, system: SystemInterface) {
|
async sync(signer: EventSigner, system: SystemInterface) {
|
||||||
await this.#sync.sync(system);
|
await this.#sync.sync(system);
|
||||||
|
|
||||||
if (
|
if (this.#sync.value?.content) {
|
||||||
this.#sync.value?.content &&
|
|
||||||
this.#sync.value?.content.startsWith("[") &&
|
|
||||||
this.#sync.value?.content.endsWith("]")
|
|
||||||
) {
|
|
||||||
const decrypted = await signer.nip4Decrypt(this.#sync.value.content, await signer.getPubKey());
|
const decrypted = await signer.nip4Decrypt(this.#sync.value.content, await signer.getPubKey());
|
||||||
this.#decryptedContent = decrypted;
|
this.#decryptedContent = decrypted;
|
||||||
}
|
}
|
||||||
@ -140,10 +136,9 @@ export class DiffSyncTags extends EventEmitter<SafeSyncEvents> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// apply changes onto next
|
// apply changes onto next
|
||||||
this.#applyChanges(next.tags, this.#changes);
|
next.tags = this.#applyChanges(next.tags, this.#changes);
|
||||||
if (this.#changesEncrypted.length > 0 && !content) {
|
if (this.#changesEncrypted.length > 0 && !content) {
|
||||||
const encryptedTags = isNew ? [] : this.encryptedTags;
|
const encryptedTags = this.#applyChanges(isNew ? [] : this.encryptedTags, this.#changesEncrypted);
|
||||||
this.#applyChanges(encryptedTags, this.#changesEncrypted);
|
|
||||||
next.content = JSON.stringify(encryptedTags);
|
next.content = JSON.stringify(encryptedTags);
|
||||||
} else if (content) {
|
} else if (content) {
|
||||||
next.content = content;
|
next.content = content;
|
||||||
@ -206,5 +201,17 @@ export class DiffSyncTags extends EventEmitter<SafeSyncEvents> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,7 +188,7 @@ export class UserState<TAppData> extends EventEmitter<UserStateEvents> {
|
|||||||
get muted() {
|
get muted() {
|
||||||
const list = this.#standardLists.get(EventKind.MuteList);
|
const list = this.#standardLists.get(EventKind.MuteList);
|
||||||
if (list) {
|
if (list) {
|
||||||
return NostrLink.fromTags(list.encryptedTags);
|
return NostrLink.fromAllTags(list.encryptedTags);
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@ -396,12 +396,9 @@ export class UserState<TAppData> extends EventEmitter<UserStateEvents> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
getList<T extends ToNostrEventTag>(
|
getList(kind: EventKind): Array<ToNostrEventTag> {
|
||||||
kind: EventKind,
|
|
||||||
fnOther?: (tag: Array<string>) => T | undefined,
|
|
||||||
): Array<ToNostrEventTag> {
|
|
||||||
const list = this.#standardLists.get(kind);
|
const list = this.#standardLists.get(kind);
|
||||||
return NostrLink.fromTags<T>(list?.tags ?? [], fnOther);
|
return NostrLink.fromAllTags(list?.tags ?? []);
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize(): UserStateObject<TAppData> {
|
serialize(): UserStateObject<TAppData> {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user