From 9deb0328454f1054e4895cf1694612db5323e5d3 Mon Sep 17 00:00:00 2001 From: hzrd149 Date: Tue, 27 Aug 2024 09:25:52 +0300 Subject: [PATCH] Add option to hide zap bubbles on notes --- .changeset/many-rabbits-float.md | 5 + src/classes/local-settings/entry.ts | 96 +++++++++++++++ src/classes/local-settings/types.ts | 34 ++++++ src/components/note/timeline-note/index.tsx | 4 +- src/hooks/use-cache-form.ts | 2 +- src/services/local-settings.ts | 124 ++------------------ src/views/communities/explore.tsx | 2 +- src/views/settings/display-settings.tsx | 18 ++- src/views/streams/index.tsx | 2 +- src/views/tools/event-console/event-row.tsx | 2 +- src/views/tools/event-console/index.tsx | 2 +- src/views/tools/event-publisher/index.tsx | 2 +- 12 files changed, 170 insertions(+), 123 deletions(-) create mode 100644 .changeset/many-rabbits-float.md create mode 100644 src/classes/local-settings/entry.ts create mode 100644 src/classes/local-settings/types.ts diff --git a/.changeset/many-rabbits-float.md b/.changeset/many-rabbits-float.md new file mode 100644 index 000000000..ab68a01e9 --- /dev/null +++ b/.changeset/many-rabbits-float.md @@ -0,0 +1,5 @@ +--- +"nostrudel": minor +--- + +Add option to hide zap bubbles on notes diff --git a/src/classes/local-settings/entry.ts b/src/classes/local-settings/entry.ts new file mode 100644 index 000000000..8e9efcfbb --- /dev/null +++ b/src/classes/local-settings/entry.ts @@ -0,0 +1,96 @@ +import { PersistentSubject } from "../subject"; + +export class NullableLocalStorageEntry extends PersistentSubject { + key: string; + decode?: (raw: string | null) => T | null; + encode?: (value: T) => string | null; + + constructor( + key: string, + initValue: T | null = null, + decode?: (raw: string | null) => T | null, + encode?: (value: T) => string | null, + ) { + let value = initValue; + if (localStorage.hasOwnProperty(key)) { + const raw = localStorage.getItem(key); + + if (decode) value = decode(raw); + else value = raw as T | null; + } + + super(value); + this.key = key; + this.decode = decode; + this.encode = encode; + } + + next(value: T | null) { + if (value === null) { + localStorage.removeItem(this.key); + + super.next(value); + } else { + const encoded = this.encode ? this.encode(value) : String(value); + if (encoded !== null) localStorage.setItem(this.key, encoded); + else localStorage.removeItem(this.key); + + super.next(value); + } + } + + clear() { + this.next(null); + } +} + +export class LocalStorageEntry extends PersistentSubject { + key: string; + fallback: T; + decode?: (raw: string) => T; + encode?: (value: T) => string | null; + + setDefault = false; + + constructor( + key: string, + fallback: T, + decode?: (raw: string) => T, + encode?: (value: T) => string | null, + setDefault = false, + ) { + let value = fallback; + if (localStorage.hasOwnProperty(key)) { + const raw = localStorage.getItem(key); + + if (decode && raw) value = decode(raw); + else if (raw) value = raw as T; + } else if (setDefault) { + const encoded = encode ? encode(fallback) : String(fallback); + if (!encoded) throw new Error("encode can not return null when setDefault is set"); + localStorage.setItem(key, encoded); + } + + super(value); + + this.key = key; + this.decode = decode; + this.encode = encode; + this.fallback = fallback; + this.setDefault = setDefault; + } + + next(value: T) { + const encoded = this.encode ? this.encode(value) : String(value); + if (encoded !== null) localStorage.setItem(this.key, encoded); + else if (this.setDefault && encoded) localStorage.setItem(this.key, encoded); + else localStorage.removeItem(this.key); + + super.next(value); + } + + clear() { + localStorage.removeItem(this.key); + super.next(this.fallback); + } +} diff --git a/src/classes/local-settings/types.ts b/src/classes/local-settings/types.ts new file mode 100644 index 000000000..8d6aa3556 --- /dev/null +++ b/src/classes/local-settings/types.ts @@ -0,0 +1,34 @@ +import { LocalStorageEntry, NullableLocalStorageEntry } from "./entry"; + +export class NumberLocalStorageEntry extends LocalStorageEntry { + constructor(key: string, fallback: number) { + super( + key, + fallback, + (raw) => parseInt(raw), + (value) => String(value), + ); + } +} + +export class NullableNumberLocalStorageEntry extends NullableLocalStorageEntry { + constructor(key: string, fallback: number) { + super( + key, + fallback, + (raw) => (raw !== null ? parseInt(raw) : raw), + (value) => String(value), + ); + } +} + +export class BooleanLocalStorageEntry extends LocalStorageEntry { + constructor(key: string, fallback: boolean) { + super( + key, + fallback, + (raw) => raw === "true", + (value) => String(value), + ); + } +} diff --git a/src/components/note/timeline-note/index.tsx b/src/components/note/timeline-note/index.tsx index 57104bf73..2388085f9 100644 --- a/src/components/note/timeline-note/index.tsx +++ b/src/components/note/timeline-note/index.tsx @@ -46,6 +46,7 @@ import ReplyContext from "./components/reply-context"; import ZapBubbles from "./components/zap-bubbles"; import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref"; import relayHintService from "../../../services/event-relay-hint"; +import localSettings from "../../../services/local-settings"; export type TimelineNoteProps = Omit & { event: NostrEvent; @@ -68,6 +69,7 @@ export function TimelineNote({ }: TimelineNoteProps) { const account = useCurrentAccount(); const { showReactions, showSignatureVerification } = useSubject(appSettings); + const hideZapBubbles = useSubject(localSettings.hideZapBubbles); const replyForm = useDisclosure(); const ref = useEventIntersectionRef(event); @@ -124,7 +126,7 @@ export function TimelineNote({ - + {!hideZapBubbles && } {showReactionsOnNewLine && reactionButtons} diff --git a/src/hooks/use-cache-form.ts b/src/hooks/use-cache-form.ts index 43865520f..a76f0ab41 100644 --- a/src/hooks/use-cache-form.ts +++ b/src/hooks/use-cache-form.ts @@ -17,7 +17,7 @@ export default function useCacheForm>(state); stateRef.current = state; - // NOTE: this watches the dirty state + // NOTE: this watches the state state.isDirty; state.isSubmitted; diff --git a/src/services/local-settings.ts b/src/services/local-settings.ts index d6cdf931c..29d07791b 100644 --- a/src/services/local-settings.ts +++ b/src/services/local-settings.ts @@ -3,121 +3,12 @@ import { bytesToHex, hexToBytes } from "@noble/hashes/utils"; import { PersistentSubject } from "../classes/subject"; import { DEFAULT_SIGNAL_RELAYS } from "../const"; - -class NullableLocalStorageEntry extends PersistentSubject { - key: string; - decode?: (raw: string | null) => T | null; - encode?: (value: T) => string | null; - - constructor( - key: string, - initValue: T | null = null, - decode?: (raw: string | null) => T | null, - encode?: (value: T) => string | null, - ) { - let value = initValue; - if (localStorage.hasOwnProperty(key)) { - const raw = localStorage.getItem(key); - - if (decode) value = decode(raw); - else value = raw as T | null; - } - - super(value); - this.key = key; - this.decode = decode; - this.encode = encode; - } - - next(value: T | null) { - if (value === null) { - localStorage.removeItem(this.key); - - super.next(value); - } else { - const encoded = this.encode ? this.encode(value) : String(value); - if (encoded !== null) localStorage.setItem(this.key, encoded); - else localStorage.removeItem(this.key); - - super.next(value); - } - } - - clear() { - this.next(null); - } -} -class LocalStorageEntry extends PersistentSubject { - key: string; - fallback: T; - decode?: (raw: string) => T; - encode?: (value: T) => string | null; - - setDefault = false; - - constructor( - key: string, - fallback: T, - decode?: (raw: string) => T, - encode?: (value: T) => string | null, - setDefault = false, - ) { - let value = fallback; - if (localStorage.hasOwnProperty(key)) { - const raw = localStorage.getItem(key); - - if (decode && raw) value = decode(raw); - else if (raw) value = raw as T; - } else if (setDefault) { - const encoded = encode ? encode(fallback) : String(fallback); - if (!encoded) throw new Error("encode can not return null when setDefault is set"); - localStorage.setItem(key, encoded); - } - - super(value); - - this.key = key; - this.decode = decode; - this.encode = encode; - this.fallback = fallback; - this.setDefault = setDefault; - } - - next(value: T) { - const encoded = this.encode ? this.encode(value) : String(value); - if (encoded !== null) localStorage.setItem(this.key, encoded); - else if (this.setDefault && encoded) localStorage.setItem(this.key, encoded); - else localStorage.removeItem(this.key); - - super.next(value); - } - - clear() { - localStorage.removeItem(this.key); - super.next(this.fallback); - } -} - -class NumberLocalStorageEntry extends LocalStorageEntry { - constructor(key: string, fallback: number) { - super( - key, - fallback, - (raw) => parseInt(raw), - (value) => String(value), - ); - } -} -class NullableNumberLocalStorageEntry extends NullableLocalStorageEntry { - constructor(key: string, fallback: number) { - super( - key, - fallback, - (raw) => (raw !== null ? parseInt(raw) : raw), - (value) => String(value), - ); - } -} +import { + BooleanLocalStorageEntry, + NullableNumberLocalStorageEntry, + NumberLocalStorageEntry, +} from "../classes/local-settings/types"; +import { LocalStorageEntry } from "../classes/local-settings/entry"; // local relay const idbMaxEvents = new NumberLocalStorageEntry("nostr-idb-max-events", 10_000); @@ -131,6 +22,8 @@ const enableNoteThreadDrawer = new LocalStorageEntry( (v) => String(v), ); +const hideZapBubbles = new BooleanLocalStorageEntry("hide-zap-bubbles", false); + // webrtc relay const webRtcLocalIdentity = new LocalStorageEntry( "nostr-webrtc-identity", @@ -156,6 +49,7 @@ const localSettings = { idbMaxEvents, wasmPersistForDays, enableNoteThreadDrawer, + hideZapBubbles, webRtcLocalIdentity, webRtcSignalingRelays, webRtcRecentConnections, diff --git a/src/views/communities/explore.tsx b/src/views/communities/explore.tsx index ea8ac4a96..b9f6f94aa 100644 --- a/src/views/communities/explore.tsx +++ b/src/views/communities/explore.tsx @@ -72,7 +72,7 @@ function CommunitiesExplorePage() { Back - + Show More diff --git a/src/views/settings/display-settings.tsx b/src/views/settings/display-settings.tsx index c059f70b0..aac1b0f3c 100644 --- a/src/views/settings/display-settings.tsx +++ b/src/views/settings/display-settings.tsx @@ -25,6 +25,7 @@ import localSettings from "../../services/local-settings"; export default function DisplaySettings() { const { register } = useFormContext(); + const hideZapBubbles = useSubject(localSettings.hideZapBubbles); const enableNoteDrawer = useSubject(localSettings.enableNoteThreadDrawer); return ( @@ -123,6 +124,21 @@ export default function DisplaySettings() { Enabled: Removes all emojis in other users usernames and display names + + + + Hide individual zaps on notes + + localSettings.hideZapBubbles.next(!localSettings.hideZapBubbles.value)} + /> + + + Enabled: Hides individual zaps on notes in the timeline + + @@ -141,7 +157,7 @@ export default function DisplaySettings() { localSettings.enableNoteThreadDrawer.next(!localSettings.enableNoteThreadDrawer.value)} /> diff --git a/src/views/streams/index.tsx b/src/views/streams/index.tsx index b148f9d87..b9d759745 100644 --- a/src/views/streams/index.tsx +++ b/src/views/streams/index.tsx @@ -60,7 +60,7 @@ function StreamsPage() { - + Show Ended diff --git a/src/views/tools/event-console/event-row.tsx b/src/views/tools/event-console/event-row.tsx index e73d87db0..d48b6dc04 100644 --- a/src/views/tools/event-console/event-row.tsx +++ b/src/views/tools/event-console/event-row.tsx @@ -48,7 +48,7 @@ export default function EventRow({ event }: { event: NostrEvent }) { {raw.isOpen && ( )} - + Raw diff --git a/src/views/tools/event-console/index.tsx b/src/views/tools/event-console/index.tsx index 599946f86..d0ab20e95 100644 --- a/src/views/tools/event-console/index.tsx +++ b/src/views/tools/event-console/index.tsx @@ -151,7 +151,7 @@ export default function EventConsoleView() { Event Console - + Query Relay {queryRelay.isOpen && ( diff --git a/src/views/tools/event-publisher/index.tsx b/src/views/tools/event-publisher/index.tsx index 2f9eace01..f5af952c3 100644 --- a/src/views/tools/event-publisher/index.tsx +++ b/src/views/tools/event-publisher/index.tsx @@ -117,7 +117,7 @@ export default function EventPublisherView() { Event Publisher - + Publish to Relay {customRelay.isOpen && (