From 5e42c5e70c15cc63b65cf2cd1578e28734467914 Mon Sep 17 00:00:00 2001 From: Kieran Date: Mon, 16 Oct 2023 21:24:54 +0100 Subject: [PATCH] feat: add useEventReactions to system-react --- packages/app/src/Hooks/useEventReactions.tsx | 49 ----------------- packages/app/src/SnortUtils/index.ts | 23 -------- packages/shared/package.json | 2 +- packages/shared/src/utils.ts | 22 ++++++++ packages/system-react/package.json | 4 +- packages/system-react/src/index.ts | 4 +- .../system-react/src/useEventReactions.tsx | 55 +++++++++++++++++++ 7 files changed, 83 insertions(+), 76 deletions(-) delete mode 100644 packages/app/src/Hooks/useEventReactions.tsx create mode 100644 packages/system-react/src/useEventReactions.tsx diff --git a/packages/app/src/Hooks/useEventReactions.tsx b/packages/app/src/Hooks/useEventReactions.tsx deleted file mode 100644 index 05a22526..00000000 --- a/packages/app/src/Hooks/useEventReactions.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { EventKind, NostrLink, parseZap, TaggedNostrEvent } from "@snort/system"; -import { UserCache } from "Cache"; -import { useMemo } from "react"; -import { dedupeByPubkey, getLinkReactions, normalizeReaction, Reaction } from "SnortUtils"; - -export function useEventReactions(ev: TaggedNostrEvent, related: ReadonlyArray) { - return useMemo(() => { - const link = NostrLink.fromEvent(ev); - const deletions = getLinkReactions(related, link, EventKind.Deletion); - const reactions = getLinkReactions(related, link, EventKind.Reaction); - const reposts = getLinkReactions(related, link, EventKind.Repost); - - const groupReactions = (() => { - const result = reactions?.reduce( - (acc, reaction) => { - const kind = normalizeReaction(reaction.content); - const rs = acc[kind] || []; - return { ...acc, [kind]: [...rs, reaction] }; - }, - { - [Reaction.Positive]: [] as TaggedNostrEvent[], - [Reaction.Negative]: [] as TaggedNostrEvent[], - }, - ); - return { - [Reaction.Positive]: dedupeByPubkey(result[Reaction.Positive]), - [Reaction.Negative]: dedupeByPubkey(result[Reaction.Negative]), - }; - })(); - const positive = groupReactions[Reaction.Positive]; - const negative = groupReactions[Reaction.Negative]; - - const zaps = getLinkReactions(related, link, EventKind.ZapReceipt) - .map(a => parseZap(a, UserCache, ev)) - .filter(a => a.valid) - .sort((a, b) => b.amount - a.amount); - - return { - deletions, - reactions: { - all: reactions, - positive, - negative, - }, - reposts, - zaps, - }; - }, [ev, related]); -} diff --git a/packages/app/src/SnortUtils/index.ts b/packages/app/src/SnortUtils/index.ts index ba8578f6..6141f2e3 100644 --- a/packages/app/src/SnortUtils/index.ts +++ b/packages/app/src/SnortUtils/index.ts @@ -140,29 +140,6 @@ export function hexToBech32(hrp: string, hex?: string) { return ""; } } - -/** - * Reaction types - */ -export const Reaction = { - Positive: "+", - Negative: "-", -}; - -/** - * Return normalized reaction content - */ -export function normalizeReaction(content: string) { - switch (content) { - case "-": - return Reaction.Negative; - case "👎": - return Reaction.Negative; - default: - return Reaction.Positive; - } -} - export function getLinkReactions( notes: ReadonlyArray | undefined, link: NostrLink, diff --git a/packages/shared/package.json b/packages/shared/package.json index 37e7c10c..35d24077 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@snort/shared", - "version": "1.0.7", + "version": "1.0.8", "description": "Shared components for Snort", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/shared/src/utils.ts b/packages/shared/src/utils.ts index 3714b840..7dd9ebe5 100644 --- a/packages/shared/src/utils.ts +++ b/packages/shared/src/utils.ts @@ -193,3 +193,25 @@ export async function fetchNip05Pubkey(name: string, domain: string, timeout = 2 export function removeUndefined(v: Array) { return v.filter(a => a != undefined).map(a => unwrap(a)); } + +/** + * Reaction types + */ +export const enum Reaction { + Positive = "+", + Negative = "-", +} + +/** + * Return normalized reaction content + */ +export function normalizeReaction(content: string) { + switch (content) { + case "-": + return Reaction.Negative; + case "👎": + return Reaction.Negative; + default: + return Reaction.Positive; + } +} diff --git a/packages/system-react/package.json b/packages/system-react/package.json index 192d5a15..707aa9a7 100644 --- a/packages/system-react/package.json +++ b/packages/system-react/package.json @@ -1,6 +1,6 @@ { "name": "@snort/system-react", - "version": "1.0.16", + "version": "1.0.17", "description": "React hooks for @snort/system", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -15,7 +15,7 @@ "dist" ], "dependencies": { - "@snort/shared": "^1.0.7", + "@snort/shared": "^1.0.8", "@snort/system": "^1.0.22", "react": "^18.2.0" }, diff --git a/packages/system-react/src/index.ts b/packages/system-react/src/index.ts index 7a4d18ca..2aed0afb 100644 --- a/packages/system-react/src/index.ts +++ b/packages/system-react/src/index.ts @@ -1,5 +1,7 @@ +export * from "./context"; + export * from "./useRequestBuilder"; export * from "./useSystemState"; export * from "./useUserProfile"; -export * from "./context"; export * from "./useUserSearch"; +export * from "./useEventReactions"; \ No newline at end of file diff --git a/packages/system-react/src/useEventReactions.tsx b/packages/system-react/src/useEventReactions.tsx new file mode 100644 index 00000000..0c9daa50 --- /dev/null +++ b/packages/system-react/src/useEventReactions.tsx @@ -0,0 +1,55 @@ +import { useContext, useMemo } from "react"; +import { normalizeReaction, Reaction } from "@snort/shared"; +import { EventKind, NostrLink, parseZap, TaggedNostrEvent } from "@snort/system"; + +import { SnortContext } from "./context"; + +/** + * Parse reactions to a given event from a set of related events + * @param ev + * @param related + * @returns + */ +export function useEventReactions(ev: TaggedNostrEvent, related: ReadonlyArray) { + const system = useContext(SnortContext); + + return useMemo(() => { + const link = NostrLink.fromEvent(ev); + const reactionKinds = related.reduce((acc, v) => { + if (link.isReplyToThis(v)) { + acc[v.kind.toString()] ??= []; + acc[v.kind.toString()].push(v); + } + return acc; + }, {} as Record>); + + const deletions = reactionKinds[EventKind.Deletion.toString()] ?? []; + const reactions = reactionKinds[EventKind.Reaction.toString()] ?? []; + const reposts = reactionKinds[EventKind.Repost.toString()] ?? []; + + const groupReactions = reactions?.reduce((acc, reaction) => { + const kind = normalizeReaction(reaction.content); + acc[kind] ??= []; + acc[kind].push(reaction); + return acc; + }, {} as Record>); + + const zaps = (reactionKinds[EventKind.ZapReceipt] ?? []) + .map(a => parseZap(a, system.ProfileLoader.Cache, ev)) + .filter(a => a.valid) + .sort((a, b) => b.amount - a.amount); + + return { + deletions, + reactions: { + positive: groupReactions[Reaction.Positive], + negative: groupReactions[Reaction.Negative], + }, + reposts, + zaps, + others: Object.fromEntries( + Object.entries(reactionKinds) + .filter(([k]) => ![EventKind.Deletion, EventKind.Reaction, EventKind.Repost, EventKind.ZapReceipt].includes(Number(k)))) + }; + }, [ev, related]); +}