forked from Kieran/snort
Basic indented thread layout
This commit is contained in:
parent
0d67612581
commit
b8120d9ee1
@ -34,4 +34,9 @@
|
|||||||
.note > .footer {
|
.note > .footer {
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.indented {
|
||||||
|
border-left: 3px solid #444;
|
||||||
|
padding-left: 2px;
|
||||||
}
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
import { useMemo } from "react";
|
||||||
import Event from "../nostr/Event";
|
import Event from "../nostr/Event";
|
||||||
import EventKind from "../nostr/EventKind";
|
import EventKind from "../nostr/EventKind";
|
||||||
import Note from "./Note";
|
import Note from "./Note";
|
||||||
@ -10,25 +11,63 @@ export default function Thread(props) {
|
|||||||
const notes = props.notes?.map(a => Event.FromObject(a));
|
const notes = props.notes?.map(a => Event.FromObject(a));
|
||||||
|
|
||||||
// root note has no thread info
|
// 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) {
|
function reactions(id, kind = EventKind.Reaction) {
|
||||||
return notes?.filter(a => a.Kind === kind && a.Tags.find(a => a.Key === "e" && a.Event === id));
|
return notes?.filter(a => a.Kind === kind && a.Tags.find(a => a.Key === "e" && a.Event === id));
|
||||||
}
|
}
|
||||||
|
|
||||||
const repliesToRoot = notes?.
|
function renderRoot() {
|
||||||
filter(a => a.GetThread()?.Root !== null && a.Kind === EventKind.TextNote && a.Id !== thisEvent && a.Id !== root?.Id)
|
if (root) {
|
||||||
.sort((a, b) => a.CreatedAt - b.CreatedAt);
|
return <Note data-ev={root} reactions={reactions(root.Id)} deletion={reactions(root.Id, EventKind.Deletion)} />
|
||||||
const thisNote = notes?.find(a => a.Id === thisEvent);
|
} else {
|
||||||
const thisIsRootNote = thisNote?.Id === root?.Id;
|
return <NoteGhost text={`Loading thread.. ${notes.length} loaded`} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderChain(from, acc) {
|
||||||
|
if (from && chains) {
|
||||||
|
let replies = chains.get(from);
|
||||||
|
if (replies) {
|
||||||
|
return (
|
||||||
|
<div className="indented">
|
||||||
|
{replies.map(a => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Note data-ev={a} key={a.Id} reactions={reactions(a.Id)} deletion={reactions(a.Id, EventKind.Deletion)} hightlight={thisEvent === a.Id} />
|
||||||
|
{renderChain(a.Id)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{root === undefined ?
|
{renderRoot()}
|
||||||
<NoteGhost text={`Loading... (${notes.length} events loaded)`}/>
|
{root ? renderChain(root.Id) : null}
|
||||||
: <Note data-ev={root} reactions={reactions(root?.Id)} />}
|
|
||||||
{thisNote && !thisIsRootNote ? <Note data-ev={thisNote} reactions={reactions(thisNote.Id)} deletion={reactions(thisNote.Id, EventKind.Deletion)}/> : null}
|
|
||||||
<h4>Other Replies</h4>
|
|
||||||
{repliesToRoot?.map(a => <Note key={a.Id} data-ev={a} reactions={reactions(a.Id)} deletion={reactions(a.Id, EventKind.Deletion)}/>)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -66,6 +66,9 @@ export default function useEventPublisher() {
|
|||||||
if (thread) {
|
if (thread) {
|
||||||
if (thread.Root) {
|
if (thread.Root) {
|
||||||
ev.Tags.push(new Tag(["e", thread.Root.Event, "", "root"], ev.Tags.length));
|
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) {
|
if (thread.Reply) {
|
||||||
ev.Tags.push(new Tag(["e", thread.Reply.Id, "", "reply"], ev.Tags.length));
|
ev.Tags.push(new Tag(["e", thread.Reply.Id, "", "reply"], ev.Tags.length));
|
||||||
|
@ -30,10 +30,13 @@ export default class Thread {
|
|||||||
let marked = eTags.some(a => a.Marker !== null);
|
let marked = eTags.some(a => a.Marker !== null);
|
||||||
if (!marked) {
|
if (!marked) {
|
||||||
ret.Root = eTags[0];
|
ret.Root = eTags[0];
|
||||||
|
ret.Root.Marker = "root";
|
||||||
if (eTags.length > 2) {
|
if (eTags.length > 2) {
|
||||||
ret.Mentions = eTags.slice(1, -1);
|
ret.Mentions = eTags.slice(1, -1);
|
||||||
|
ret.Mentions.forEach(a => a.Marker = "mention");
|
||||||
}
|
}
|
||||||
ret.ReplyTo = eTags[eTags.length - 1];
|
ret.ReplyTo = eTags[eTags.length - 1];
|
||||||
|
ret.ReplyTo.Marker = "reply";
|
||||||
} else {
|
} else {
|
||||||
let root = eTags.find(a => a.Marker === "root");
|
let root = eTags.find(a => a.Marker === "root");
|
||||||
let reply = eTags.find(a => a.Marker === "reply");
|
let reply = eTags.find(a => a.Marker === "reply");
|
||||||
|
Loading…
Reference in New Issue
Block a user