From 6ed1252eeda7a3845714899b3c7efaf543b18948 Mon Sep 17 00:00:00 2001 From: kPherox Date: Thu, 16 Feb 2023 07:26:26 +0900 Subject: [PATCH 1/8] feat: render kind 1 reposts --- packages/app/src/Element/Note.tsx | 18 +++++++++++++++++- packages/app/src/Element/NoteReaction.tsx | 9 +++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/packages/app/src/Element/Note.tsx b/packages/app/src/Element/Note.tsx index 04925cc..cccfc36 100644 --- a/packages/app/src/Element/Note.tsx +++ b/packages/app/src/Element/Note.tsx @@ -21,6 +21,7 @@ import { setPinned, setBookmarked } from "State/Login"; import type { RootState } from "State/Store"; import messages from "./messages"; +import NoteReaction from "./NoteReaction"; export interface NoteProps { data?: TaggedRawEvent; @@ -65,6 +66,12 @@ export default function Note(props: NoteProps) { const { data, related, highlight, options: opt, ["data-ev"]: parsedEvent, ignoreModeration = false } = props; const [showReactions, setShowReactions] = useState(false); const ev = useMemo(() => parsedEvent ?? new NEvent(data), [data]); + if ( + ev.Kind === EventKind.TextNote && + ev.Tags.some((a, i) => a.Key === "e" && a.Marker === "mention" && ev.Content === `#[${i}]`) + ) { + return ; + } const pubKeys = useMemo(() => ev.Thread?.PubKeys || [], [ev]); const users = useUserProfiles(pubKeys); const deletions = useMemo(() => getReactions(related, ev.Id, EventKind.Deletion), [related]); @@ -98,7 +105,16 @@ export default function Note(props: NoteProps) { }, [reactions]); const positive = groupReactions[Reaction.Positive]; const negative = groupReactions[Reaction.Negative]; - const reposts = useMemo(() => dedupeByPubkey(getReactions(related, ev.Id, EventKind.Repost)), [related, ev]); + const reposts = useMemo( + () => + dedupeByPubkey([ + ...getReactions(related, ev.Id, EventKind.TextNote).filter(e => + e.tags.some((a, i) => a[0] === "e" && a[1] === ev.Id && a[3] === "mention" && e.content === `#[${i}]`) + ), + ...getReactions(related, ev.Id, EventKind.Repost), + ]), + [related, ev] + ); const zaps = useMemo(() => { const sortedZaps = getReactions(related, ev.Id, EventKind.ZapReceipt) .map(parseZap) diff --git a/packages/app/src/Element/NoteReaction.tsx b/packages/app/src/Element/NoteReaction.tsx index 7b4091e..c1de29b 100644 --- a/packages/app/src/Element/NoteReaction.tsx +++ b/packages/app/src/Element/NoteReaction.tsx @@ -30,7 +30,12 @@ export default function NoteReaction(props: NoteReactionProps) { return null; }, [ev]); - if (ev.Kind !== EventKind.Reaction && ev.Kind !== EventKind.Repost) { + if ( + ev.Kind !== EventKind.Reaction && + ev.Kind !== EventKind.Repost && + (ev.Kind !== EventKind.TextNote || + ev.Tags.every((a, i) => a.Event !== refEvent || a.Marker !== "mention" || ev.Content !== `#[${i}]`)) + ) { return null; } @@ -52,7 +57,7 @@ export default function NoteReaction(props: NoteReactionProps) { const root = extractRoot(); const isOpMuted = root && isMuted(root.pubkey); const opt = { - showHeader: ev?.Kind === EventKind.Repost, + showHeader: ev?.Kind === EventKind.Repost || ev?.Kind === EventKind.TextNote, showFooter: false, }; From edabdedced3f1fb21cfd7206472345e90da5056d Mon Sep 17 00:00:00 2001 From: kPherox Date: Thu, 16 Feb 2023 17:49:03 +0900 Subject: [PATCH 2/8] feat: add tracking id for kind1 reposts --- packages/app/src/Feed/TimelineFeed.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/app/src/Feed/TimelineFeed.ts b/packages/app/src/Feed/TimelineFeed.ts index a52f5b4..ebde508 100644 --- a/packages/app/src/Feed/TimelineFeed.ts +++ b/packages/app/src/Feed/TimelineFeed.ts @@ -147,11 +147,21 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel } return s; }); - const reposts = main.store.notes + const repostsByKind6 = main.store.notes .filter(a => a.kind === EventKind.Repost && a.content === "") .map(a => a.tags.find(b => b[0] === "e")) .filter(a => a) .map(a => unwrap(a)[1]); + const repostsByKind1 = main.store.notes + .filter( + a => + (a.kind === EventKind.Repost || a.kind === EventKind.TextNote) && + a.tags.some(b => b[0] === "e" && b[3] === "mention") + ) + .map(a => a.tags.find((b, i) => b[0] === "e" && b[3] === "mention" && a.content === `#[${i}]`)) + .filter(a => a) + .map(a => unwrap(a)[1]); + const reposts = [...repostsByKind6, ...repostsByKind1]; if (reposts.length > 0) { setTrackingParentEvents(s => { if (reposts.some(a => !s.includes(a))) { From 8e5c9686dfd2cfd0b87ff85035feb7aa53f47574 Mon Sep 17 00:00:00 2001 From: kPherox Date: Thu, 16 Feb 2023 18:11:36 +0900 Subject: [PATCH 3/8] feat: pass root event for kind1 reposts --- packages/app/src/Element/Timeline.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/app/src/Element/Timeline.tsx b/packages/app/src/Element/Timeline.tsx index aa7c321..880c768 100644 --- a/packages/app/src/Element/Timeline.tsx +++ b/packages/app/src/Element/Timeline.tsx @@ -72,6 +72,10 @@ export default function Timeline({ return } pubkey={e.pubkey} className="card" />; } case EventKind.TextNote: { + const eRef = e.tags.find((a, i) => a[0] === "e" && a[3] === "mention" && e.content === `#[${i}]`)?.at(1); + if (eRef) { + return a.id === eRef)} />; + } return ; } case EventKind.ZapReceipt: { From dfa2bf5873370f0cc6c9b3f4ef409bf7c2e6cf3f Mon Sep 17 00:00:00 2001 From: kPherox Date: Thu, 16 Feb 2023 18:16:07 +0900 Subject: [PATCH 4/8] fix: no make notification for reposts --- packages/app/src/Notifications.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/app/src/Notifications.ts b/packages/app/src/Notifications.ts index 1c1ef3f..2b4f5ab 100644 --- a/packages/app/src/Notifications.ts +++ b/packages/app/src/Notifications.ts @@ -10,6 +10,10 @@ import { MentionRegex } from "Const"; export async function makeNotification(db: UsersDb, ev: TaggedRawEvent): Promise { switch (ev.kind) { case EventKind.TextNote: { + const isRepost = ev.tags.some((a, i) => a[0] === "e" && a[3] === "mention" && ev.content === `#[${i}]`); + if (isRepost) { + return null; + } const pubkeys = new Set([ev.pubkey, ...ev.tags.filter(a => a[0] === "p").map(a => a[1])]); const users = await db.bulkGet(Array.from(pubkeys)); const fromUser = users.find(a => a?.pubkey === ev.pubkey); From 615c2a974ab64a4d025ea4d4f382a2da631181f9 Mon Sep 17 00:00:00 2001 From: kPherox Date: Thu, 16 Feb 2023 20:42:31 +0900 Subject: [PATCH 5/8] refactor: extract repost filter to Util.isTextRepost resolve https://github.com/v0l/snort/pull/314\#discussion_r1108279333 --- packages/app/src/Element/Note.tsx | 6 ++---- packages/app/src/Element/Timeline.tsx | 4 ++-- packages/app/src/Feed/TimelineFeed.ts | 10 +++------- packages/app/src/Notifications.ts | 4 ++-- packages/app/src/Util.ts | 5 +++++ 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/packages/app/src/Element/Note.tsx b/packages/app/src/Element/Note.tsx index cccfc36..4eb86c9 100644 --- a/packages/app/src/Element/Note.tsx +++ b/packages/app/src/Element/Note.tsx @@ -11,7 +11,7 @@ import Pin from "Icons/Pin"; import { parseZap } from "Element/Zap"; import ProfileImage from "Element/ProfileImage"; import Text from "Element/Text"; -import { eventLink, getReactions, dedupeByPubkey, hexToBech32, normalizeReaction, Reaction } from "Util"; +import { eventLink, getReactions, dedupeByPubkey, isTextRepost, hexToBech32, normalizeReaction, Reaction } from "Util"; import NoteFooter, { Translation } from "Element/NoteFooter"; import NoteTime from "Element/NoteTime"; import { useUserProfiles } from "Feed/ProfileFeed"; @@ -108,9 +108,7 @@ export default function Note(props: NoteProps) { const reposts = useMemo( () => dedupeByPubkey([ - ...getReactions(related, ev.Id, EventKind.TextNote).filter(e => - e.tags.some((a, i) => a[0] === "e" && a[1] === ev.Id && a[3] === "mention" && e.content === `#[${i}]`) - ), + ...getReactions(related, ev.Id, EventKind.TextNote).filter(e => e.tags.some(isTextRepost(e, ev.Id))), ...getReactions(related, ev.Id, EventKind.Repost), ]), [related, ev] diff --git a/packages/app/src/Element/Timeline.tsx b/packages/app/src/Element/Timeline.tsx index 880c768..5f23924 100644 --- a/packages/app/src/Element/Timeline.tsx +++ b/packages/app/src/Element/Timeline.tsx @@ -4,7 +4,7 @@ import { useCallback, useMemo } from "react"; import { useInView } from "react-intersection-observer"; import ArrowUp from "Icons/ArrowUp"; -import { dedupeByPubkey } from "Util"; +import { dedupeByPubkey, isTextRepost } from "Util"; import ProfileImage from "Element/ProfileImage"; import useTimelineFeed, { TimelineSubject } from "Feed/TimelineFeed"; import { TaggedRawEvent } from "@snort/nostr"; @@ -72,7 +72,7 @@ export default function Timeline({ return } pubkey={e.pubkey} className="card" />; } case EventKind.TextNote: { - const eRef = e.tags.find((a, i) => a[0] === "e" && a[3] === "mention" && e.content === `#[${i}]`)?.at(1); + const eRef = e.tags.find(isTextRepost(e))?.at(1); if (eRef) { return a.id === eRef)} />; } diff --git a/packages/app/src/Feed/TimelineFeed.ts b/packages/app/src/Feed/TimelineFeed.ts index ebde508..3651d3e 100644 --- a/packages/app/src/Feed/TimelineFeed.ts +++ b/packages/app/src/Feed/TimelineFeed.ts @@ -1,7 +1,7 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { u256 } from "@snort/nostr"; import { EventKind, Subscriptions } from "@snort/nostr"; -import { unixNow, unwrap } from "Util"; +import { unixNow, unwrap, isTextRepost } from "Util"; import useSubscription from "Feed/Subscription"; import { useSelector } from "react-redux"; import { RootState } from "State/Store"; @@ -153,12 +153,8 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel .filter(a => a) .map(a => unwrap(a)[1]); const repostsByKind1 = main.store.notes - .filter( - a => - (a.kind === EventKind.Repost || a.kind === EventKind.TextNote) && - a.tags.some(b => b[0] === "e" && b[3] === "mention") - ) - .map(a => a.tags.find((b, i) => b[0] === "e" && b[3] === "mention" && a.content === `#[${i}]`)) + .filter(a => (a.kind === EventKind.Repost || a.kind === EventKind.TextNote) && a.tags.some(isTextRepost(a))) + .map(a => a.tags.find(isTextRepost(a))) .filter(a => a) .map(a => unwrap(a)[1]); const reposts = [...repostsByKind6, ...repostsByKind1]; diff --git a/packages/app/src/Notifications.ts b/packages/app/src/Notifications.ts index 2b4f5ab..2b524eb 100644 --- a/packages/app/src/Notifications.ts +++ b/packages/app/src/Notifications.ts @@ -6,12 +6,12 @@ import type { NotificationRequest } from "State/Login"; import { MetadataCache, UsersDb } from "State/Users"; import { getDisplayName } from "Element/ProfileImage"; import { MentionRegex } from "Const"; +import { isTextRepost } from "Util"; export async function makeNotification(db: UsersDb, ev: TaggedRawEvent): Promise { switch (ev.kind) { case EventKind.TextNote: { - const isRepost = ev.tags.some((a, i) => a[0] === "e" && a[3] === "mention" && ev.content === `#[${i}]`); - if (isRepost) { + if (ev.tags.some(isTextRepost(ev))) { return null; } const pubkeys = new Set([ev.pubkey, ...ev.tags.filter(a => a[0] === "p").map(a => a[1])]); diff --git a/packages/app/src/Util.ts b/packages/app/src/Util.ts index 8625f7d..982a114 100644 --- a/packages/app/src/Util.ts +++ b/packages/app/src/Util.ts @@ -191,3 +191,8 @@ export function getNewest(rawNotes: TaggedRawEvent[]) { return notes[0]; } } + +export function isTextRepost(note: TaggedRawEvent, id?: u256): (tag: string[], i: number) => boolean { + return (tag, i) => + tag[0] === "e" && tag[3] === "mention" && note.content === `#[${i}]` && (id ? tag[1] === id : true); +} From d911dc8da321409787efc0a7cff6530d37894ba1 Mon Sep 17 00:00:00 2001 From: kPherox Date: Thu, 16 Feb 2023 20:51:25 +0900 Subject: [PATCH 6/8] fix: not need fallback text note repost --- packages/app/src/Element/Note.tsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/app/src/Element/Note.tsx b/packages/app/src/Element/Note.tsx index 4eb86c9..309d57a 100644 --- a/packages/app/src/Element/Note.tsx +++ b/packages/app/src/Element/Note.tsx @@ -21,7 +21,6 @@ import { setPinned, setBookmarked } from "State/Login"; import type { RootState } from "State/Store"; import messages from "./messages"; -import NoteReaction from "./NoteReaction"; export interface NoteProps { data?: TaggedRawEvent; @@ -66,12 +65,6 @@ export default function Note(props: NoteProps) { const { data, related, highlight, options: opt, ["data-ev"]: parsedEvent, ignoreModeration = false } = props; const [showReactions, setShowReactions] = useState(false); const ev = useMemo(() => parsedEvent ?? new NEvent(data), [data]); - if ( - ev.Kind === EventKind.TextNote && - ev.Tags.some((a, i) => a.Key === "e" && a.Marker === "mention" && ev.Content === `#[${i}]`) - ) { - return ; - } const pubKeys = useMemo(() => ev.Thread?.PubKeys || [], [ev]); const users = useUserProfiles(pubKeys); const deletions = useMemo(() => getReactions(related, ev.Id, EventKind.Deletion), [related]); From 188b599e854cfb9123ceae297f33460868d53c5c Mon Sep 17 00:00:00 2001 From: kPherox Date: Thu, 16 Feb 2023 23:11:29 +0900 Subject: [PATCH 7/8] refactor: rename `isTextRepost` to `tagFilterOfTextRepost` --- packages/app/src/Element/Note.tsx | 4 ++-- packages/app/src/Element/Timeline.tsx | 4 ++-- packages/app/src/Feed/TimelineFeed.ts | 6 +++--- packages/app/src/Notifications.ts | 4 ++-- packages/app/src/Util.ts | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/app/src/Element/Note.tsx b/packages/app/src/Element/Note.tsx index 309d57a..550ac08 100644 --- a/packages/app/src/Element/Note.tsx +++ b/packages/app/src/Element/Note.tsx @@ -11,7 +11,7 @@ import Pin from "Icons/Pin"; import { parseZap } from "Element/Zap"; import ProfileImage from "Element/ProfileImage"; import Text from "Element/Text"; -import { eventLink, getReactions, dedupeByPubkey, isTextRepost, hexToBech32, normalizeReaction, Reaction } from "Util"; +import { eventLink, getReactions, dedupeByPubkey, tagFilterOfTextRepost, hexToBech32, normalizeReaction, Reaction } from "Util"; import NoteFooter, { Translation } from "Element/NoteFooter"; import NoteTime from "Element/NoteTime"; import { useUserProfiles } from "Feed/ProfileFeed"; @@ -101,7 +101,7 @@ export default function Note(props: NoteProps) { const reposts = useMemo( () => dedupeByPubkey([ - ...getReactions(related, ev.Id, EventKind.TextNote).filter(e => e.tags.some(isTextRepost(e, ev.Id))), + ...getReactions(related, ev.Id, EventKind.TextNote).filter(e => e.tags.some(tagFilterOfTextRepost(e, ev.Id))), ...getReactions(related, ev.Id, EventKind.Repost), ]), [related, ev] diff --git a/packages/app/src/Element/Timeline.tsx b/packages/app/src/Element/Timeline.tsx index 5f23924..d5b2999 100644 --- a/packages/app/src/Element/Timeline.tsx +++ b/packages/app/src/Element/Timeline.tsx @@ -4,7 +4,7 @@ import { useCallback, useMemo } from "react"; import { useInView } from "react-intersection-observer"; import ArrowUp from "Icons/ArrowUp"; -import { dedupeByPubkey, isTextRepost } from "Util"; +import { dedupeByPubkey, tagFilterOfTextRepost } from "Util"; import ProfileImage from "Element/ProfileImage"; import useTimelineFeed, { TimelineSubject } from "Feed/TimelineFeed"; import { TaggedRawEvent } from "@snort/nostr"; @@ -72,7 +72,7 @@ export default function Timeline({ return } pubkey={e.pubkey} className="card" />; } case EventKind.TextNote: { - const eRef = e.tags.find(isTextRepost(e))?.at(1); + const eRef = e.tags.find(tagFilterOfTextRepost(e))?.at(1); if (eRef) { return a.id === eRef)} />; } diff --git a/packages/app/src/Feed/TimelineFeed.ts b/packages/app/src/Feed/TimelineFeed.ts index 3651d3e..f4d20f5 100644 --- a/packages/app/src/Feed/TimelineFeed.ts +++ b/packages/app/src/Feed/TimelineFeed.ts @@ -1,7 +1,7 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { u256 } from "@snort/nostr"; import { EventKind, Subscriptions } from "@snort/nostr"; -import { unixNow, unwrap, isTextRepost } from "Util"; +import { unixNow, unwrap, tagFilterOfTextRepost } from "Util"; import useSubscription from "Feed/Subscription"; import { useSelector } from "react-redux"; import { RootState } from "State/Store"; @@ -153,8 +153,8 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel .filter(a => a) .map(a => unwrap(a)[1]); const repostsByKind1 = main.store.notes - .filter(a => (a.kind === EventKind.Repost || a.kind === EventKind.TextNote) && a.tags.some(isTextRepost(a))) - .map(a => a.tags.find(isTextRepost(a))) + .filter(a => (a.kind === EventKind.Repost || a.kind === EventKind.TextNote) && a.tags.some(tagFilterOfTextRepost(a))) + .map(a => a.tags.find(tagFilterOfTextRepost(a))) .filter(a => a) .map(a => unwrap(a)[1]); const reposts = [...repostsByKind6, ...repostsByKind1]; diff --git a/packages/app/src/Notifications.ts b/packages/app/src/Notifications.ts index 2b524eb..09c5a8f 100644 --- a/packages/app/src/Notifications.ts +++ b/packages/app/src/Notifications.ts @@ -6,12 +6,12 @@ import type { NotificationRequest } from "State/Login"; import { MetadataCache, UsersDb } from "State/Users"; import { getDisplayName } from "Element/ProfileImage"; import { MentionRegex } from "Const"; -import { isTextRepost } from "Util"; +import { tagFilterOfTextRepost } from "Util"; export async function makeNotification(db: UsersDb, ev: TaggedRawEvent): Promise { switch (ev.kind) { case EventKind.TextNote: { - if (ev.tags.some(isTextRepost(ev))) { + if (ev.tags.some(tagFilterOfTextRepost(ev))) { return null; } const pubkeys = new Set([ev.pubkey, ...ev.tags.filter(a => a[0] === "p").map(a => a[1])]); diff --git a/packages/app/src/Util.ts b/packages/app/src/Util.ts index 982a114..9c1d491 100644 --- a/packages/app/src/Util.ts +++ b/packages/app/src/Util.ts @@ -192,7 +192,7 @@ export function getNewest(rawNotes: TaggedRawEvent[]) { } } -export function isTextRepost(note: TaggedRawEvent, id?: u256): (tag: string[], i: number) => boolean { +export function tagFilterOfTextRepost(note: TaggedRawEvent, id?: u256): (tag: string[], i: number) => boolean { return (tag, i) => tag[0] === "e" && tag[3] === "mention" && note.content === `#[${i}]` && (id ? tag[1] === id : true); } From b7b23dcfc706f38b7702f266c9b825ed93996c40 Mon Sep 17 00:00:00 2001 From: kPherox Date: Sat, 18 Feb 2023 10:10:18 +0900 Subject: [PATCH 8/8] refactor: yarn format --- packages/app/src/Element/Note.tsx | 10 +++++++++- packages/app/src/Feed/TimelineFeed.ts | 4 +++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/app/src/Element/Note.tsx b/packages/app/src/Element/Note.tsx index 550ac08..eac4178 100644 --- a/packages/app/src/Element/Note.tsx +++ b/packages/app/src/Element/Note.tsx @@ -11,7 +11,15 @@ import Pin from "Icons/Pin"; import { parseZap } from "Element/Zap"; import ProfileImage from "Element/ProfileImage"; import Text from "Element/Text"; -import { eventLink, getReactions, dedupeByPubkey, tagFilterOfTextRepost, hexToBech32, normalizeReaction, Reaction } from "Util"; +import { + eventLink, + getReactions, + dedupeByPubkey, + tagFilterOfTextRepost, + hexToBech32, + normalizeReaction, + Reaction, +} from "Util"; import NoteFooter, { Translation } from "Element/NoteFooter"; import NoteTime from "Element/NoteTime"; import { useUserProfiles } from "Feed/ProfileFeed"; diff --git a/packages/app/src/Feed/TimelineFeed.ts b/packages/app/src/Feed/TimelineFeed.ts index f4d20f5..e8b16e9 100644 --- a/packages/app/src/Feed/TimelineFeed.ts +++ b/packages/app/src/Feed/TimelineFeed.ts @@ -153,7 +153,9 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel .filter(a => a) .map(a => unwrap(a)[1]); const repostsByKind1 = main.store.notes - .filter(a => (a.kind === EventKind.Repost || a.kind === EventKind.TextNote) && a.tags.some(tagFilterOfTextRepost(a))) + .filter( + a => (a.kind === EventKind.Repost || a.kind === EventKind.TextNote) && a.tags.some(tagFilterOfTextRepost(a)) + ) .map(a => a.tags.find(tagFilterOfTextRepost(a))) .filter(a => a) .map(a => unwrap(a)[1]);