From b8120d9ee13201102b9e8cbd8482556815e5d48c Mon Sep 17 00:00:00 2001 From: Kieran Date: Fri, 6 Jan 2023 13:01:54 +0000 Subject: [PATCH] Basic indented thread layout --- src/element/Note.css | 5 +++ src/element/Thread.js | 63 ++++++++++++++++++++++++++++++-------- src/feed/EventPublisher.js | 3 ++ src/nostr/Thread.js | 3 ++ 4 files changed, 62 insertions(+), 12 deletions(-) diff --git a/src/element/Note.css b/src/element/Note.css index e7122ded..57ce054d 100644 --- a/src/element/Note.css +++ b/src/element/Note.css @@ -34,4 +34,9 @@ .note > .footer { padding: 10px 0; text-align: right; +} + +.indented { + border-left: 3px solid #444; + padding-left: 2px; } \ No newline at end of file diff --git a/src/element/Thread.js b/src/element/Thread.js index 610743d1..91deeb98 100644 --- a/src/element/Thread.js +++ b/src/element/Thread.js @@ -1,3 +1,4 @@ +import { useMemo } from "react"; import Event from "../nostr/Event"; import EventKind from "../nostr/EventKind"; import Note from "./Note"; @@ -10,25 +11,63 @@ export default function Thread(props) { const notes = props.notes?.map(a => Event.FromObject(a)); // root note has no thread info - const root = notes.find(a => a.GetThread() === null); + const root = useMemo(() => notes.find(a => a.GetThread() === null), [notes]); + + const chains = useMemo(() => { + let chains = new Map(); + notes.filter(a => a.Kind === EventKind.TextNote).sort((a, b) => b.CreatedAt - a.CreatedAt).forEach((v) => { + let thread = v.GetThread(); + let replyTo = thread?.ReplyTo?.Event ?? thread?.Root?.Event; + if (replyTo) { + if (!chains.has(replyTo)) { + chains.set(replyTo, [v]); + } else { + chains.get(replyTo).push(v); + } + } else if(v.Tags.length > 0) { + console.log("Not replying to anything: ", v); + } + }); + + return chains; + }, [notes]); function reactions(id, kind = EventKind.Reaction) { return notes?.filter(a => a.Kind === kind && a.Tags.find(a => a.Key === "e" && a.Event === id)); } - const repliesToRoot = notes?. - filter(a => a.GetThread()?.Root !== null && a.Kind === EventKind.TextNote && a.Id !== thisEvent && a.Id !== root?.Id) - .sort((a, b) => a.CreatedAt - b.CreatedAt); - const thisNote = notes?.find(a => a.Id === thisEvent); - const thisIsRootNote = thisNote?.Id === root?.Id; + function renderRoot() { + if (root) { + return + } else { + return + } + } + + function renderChain(from, acc) { + if (from && chains) { + let replies = chains.get(from); + if (replies) { + return ( +
+ {replies.map(a => { + return ( + <> + + {renderChain(a.Id)} + + ) + })} +
+ ) + } + } + } + return ( <> - {root === undefined ? - - : } - {thisNote && !thisIsRootNote ? : null} -

Other Replies

- {repliesToRoot?.map(a => )} + {renderRoot()} + {root ? renderChain(root.Id) : null} ); } \ No newline at end of file diff --git a/src/feed/EventPublisher.js b/src/feed/EventPublisher.js index 5a1016b4..288ca4b9 100644 --- a/src/feed/EventPublisher.js +++ b/src/feed/EventPublisher.js @@ -66,6 +66,9 @@ export default function useEventPublisher() { if (thread) { if (thread.Root) { ev.Tags.push(new Tag(["e", thread.Root.Event, "", "root"], ev.Tags.length)); + } else { + let unRootedReply = thread.Reply.GetThread(); + ev.Tags.push(new Tag(["e", unRootedReply.ReplyTo.Event, "", "root"], ev.Tags.length)); } if (thread.Reply) { ev.Tags.push(new Tag(["e", thread.Reply.Id, "", "reply"], ev.Tags.length)); diff --git a/src/nostr/Thread.js b/src/nostr/Thread.js index 0ebb438f..58aa3941 100644 --- a/src/nostr/Thread.js +++ b/src/nostr/Thread.js @@ -30,10 +30,13 @@ export default class Thread { let marked = eTags.some(a => a.Marker !== null); if (!marked) { ret.Root = eTags[0]; + ret.Root.Marker = "root"; if (eTags.length > 2) { ret.Mentions = eTags.slice(1, -1); + ret.Mentions.forEach(a => a.Marker = "mention"); } ret.ReplyTo = eTags[eTags.length - 1]; + ret.ReplyTo.Marker = "reply"; } else { let root = eTags.find(a => a.Marker === "root"); let reply = eTags.find(a => a.Marker === "reply");