snort/src/element/Thread.js

93 lines
3.1 KiB
JavaScript
Raw Normal View History

2023-01-06 13:01:54 +00:00
import { useMemo } from "react";
2023-01-06 14:05:50 +00:00
import { Link } from "react-router-dom";
2022-12-20 12:08:41 +00:00
import Event from "../nostr/Event";
2022-12-20 23:14:13 +00:00
import EventKind from "../nostr/EventKind";
2022-12-20 12:08:41 +00:00
import Note from "./Note";
2022-12-28 17:05:20 +00:00
import NoteGhost from "./NoteGhost";
2022-12-20 12:08:41 +00:00
export default function Thread(props) {
2022-12-28 22:09:39 +00:00
const thisEvent = props.this;
2022-12-20 12:08:41 +00:00
/** @type {Array<Event>} */
const notes = props.notes?.map(a => Event.FromObject(a));
// root note has no thread info
2023-01-06 13:01:54 +00:00
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);
}
2023-01-06 14:05:50 +00:00
} else if (v.Tags.length > 0) {
2023-01-06 13:01:54 +00:00
console.log("Not replying to anything: ", v);
}
});
return chains;
}, [notes]);
2022-12-20 12:08:41 +00:00
2023-01-06 14:05:50 +00:00
const brokenChains = useMemo(() => {
return Array.from(chains?.keys()).filter(a => !notes.some(b => b.Id === a));
}, [chains]);
function reactions(id, kind = EventKind.Reaction) {
return notes?.filter(a => a.Kind === kind && a.Tags.find(a => a.Key === "e" && a.Event === id));
2022-12-20 23:14:13 +00:00
}
2023-01-06 13:01:54 +00:00
function renderRoot() {
if (root) {
return <Note data-ev={root} reactions={reactions(root.Id)} deletion={reactions(root.Id, EventKind.Deletion)} />
} else {
2023-01-06 14:05:50 +00:00
return <NoteGhost>
Loading thread root.. ({notes.length} notes loaded)
</NoteGhost>
2023-01-06 13:01:54 +00:00
}
}
2023-01-06 14:05:50 +00:00
function renderChain(from) {
2023-01-06 13:01:54 +00:00
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>
)
}
}
}
2022-12-20 12:08:41 +00:00
return (
<>
2023-01-06 13:01:54 +00:00
{renderRoot()}
{root ? renderChain(root.Id) : null}
2023-01-06 14:05:50 +00:00
{root ? null : <>
<h3>Other Replies</h3>
{brokenChains.map(a => {
return (
<>
<NoteGhost>
Missing event <Link to={`/e/${a}`}>{a.substring(0, 8)}</Link>
</NoteGhost>
{renderChain(a)}
</>
)
})}
</>}
2022-12-20 12:08:41 +00:00
</>
);
}