2023-01-25 18:31:44 +00:00
|
|
|
import "./Thread.css";
|
2023-03-28 14:34:01 +00:00
|
|
|
import { useMemo, useState, ReactNode } from "react";
|
|
|
|
import { useIntl } from "react-intl";
|
|
|
|
import { useNavigate, useLocation, Link, useParams } from "react-router-dom";
|
|
|
|
import { TaggedRawEvent, u256, EventKind } from "@snort/nostr";
|
|
|
|
import { EventExt, Thread as ThreadInfo } from "System/EventExt";
|
2023-01-26 07:25:05 +00:00
|
|
|
|
2023-03-28 14:34:01 +00:00
|
|
|
import { eventLink, unwrap, getReactions, parseNostrLink, getAllReactions } from "Util";
|
2023-01-26 07:25:05 +00:00
|
|
|
import BackButton from "Element/BackButton";
|
2023-01-20 11:11:50 +00:00
|
|
|
import Note from "Element/Note";
|
|
|
|
import NoteGhost from "Element/NoteGhost";
|
2023-02-06 21:42:47 +00:00
|
|
|
import Collapsed from "Element/Collapsed";
|
2023-03-28 14:34:01 +00:00
|
|
|
import useThreadFeed from "Feed/ThreadFeed";
|
2023-02-06 21:42:47 +00:00
|
|
|
|
2023-03-28 14:34:01 +00:00
|
|
|
import messages from "./messages";
|
2023-02-06 21:42:47 +00:00
|
|
|
|
|
|
|
interface DividerProps {
|
2023-02-07 20:04:50 +00:00
|
|
|
variant?: "regular" | "small";
|
2023-02-06 21:42:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const Divider = ({ variant = "regular" }: DividerProps) => {
|
2023-02-07 20:04:50 +00:00
|
|
|
const className = variant === "small" ? "divider divider-small" : "divider";
|
2023-02-06 21:42:47 +00:00
|
|
|
return (
|
|
|
|
<div className="divider-container">
|
2023-02-07 20:04:50 +00:00
|
|
|
<div className={className}></div>
|
2023-02-06 21:42:47 +00:00
|
|
|
</div>
|
2023-02-07 20:04:50 +00:00
|
|
|
);
|
|
|
|
};
|
2023-02-06 21:42:47 +00:00
|
|
|
|
|
|
|
interface SubthreadProps {
|
2023-02-07 20:04:50 +00:00
|
|
|
isLastSubthread?: boolean;
|
|
|
|
from: u256;
|
|
|
|
active: u256;
|
2023-03-28 14:34:01 +00:00
|
|
|
notes: readonly TaggedRawEvent[];
|
|
|
|
related: readonly TaggedRawEvent[];
|
|
|
|
chains: Map<u256, Array<TaggedRawEvent>>;
|
2023-02-07 20:04:50 +00:00
|
|
|
onNavigate: (e: u256) => void;
|
2023-02-06 21:42:47 +00:00
|
|
|
}
|
|
|
|
|
2023-03-28 14:34:01 +00:00
|
|
|
const Subthread = ({ active, notes, related, chains, onNavigate }: SubthreadProps) => {
|
|
|
|
const renderSubthread = (a: TaggedRawEvent, idx: number) => {
|
2023-02-07 20:04:50 +00:00
|
|
|
const isLastSubthread = idx === notes.length - 1;
|
2023-03-28 14:34:01 +00:00
|
|
|
const replies = getReplies(a.id, chains);
|
2023-02-07 20:04:50 +00:00
|
|
|
return (
|
|
|
|
<>
|
2023-02-09 12:26:54 +00:00
|
|
|
<div className={`subthread-container ${replies.length > 0 ? "subthread-multi" : ""}`}>
|
2023-02-07 20:04:50 +00:00
|
|
|
<Divider />
|
|
|
|
<Note
|
2023-03-28 14:34:01 +00:00
|
|
|
highlight={active === a.id}
|
2023-02-09 12:26:54 +00:00
|
|
|
className={`thread-note ${isLastSubthread && replies.length === 0 ? "is-last-note" : ""}`}
|
2023-03-28 14:34:01 +00:00
|
|
|
data={a}
|
|
|
|
key={a.id}
|
2023-02-07 20:04:50 +00:00
|
|
|
related={related}
|
|
|
|
/>
|
|
|
|
<div className="line-container"></div>
|
|
|
|
</div>
|
|
|
|
{replies.length > 0 && (
|
|
|
|
<TierTwo
|
2023-02-06 21:42:47 +00:00
|
|
|
active={active}
|
|
|
|
isLastSubthread={isLastSubthread}
|
2023-03-28 14:34:01 +00:00
|
|
|
from={a.id}
|
2023-02-06 21:42:47 +00:00
|
|
|
notes={replies}
|
|
|
|
related={related}
|
|
|
|
chains={chains}
|
|
|
|
onNavigate={onNavigate}
|
2023-02-07 20:04:50 +00:00
|
|
|
/>
|
|
|
|
)}
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
};
|
2023-02-06 21:42:47 +00:00
|
|
|
|
2023-02-07 20:04:50 +00:00
|
|
|
return <div className="subthread">{notes.map(renderSubthread)}</div>;
|
|
|
|
};
|
2023-02-06 21:42:47 +00:00
|
|
|
|
2023-02-07 20:04:50 +00:00
|
|
|
interface ThreadNoteProps extends Omit<SubthreadProps, "notes"> {
|
2023-03-28 14:34:01 +00:00
|
|
|
note: TaggedRawEvent;
|
2023-02-07 20:04:50 +00:00
|
|
|
isLast: boolean;
|
2023-02-06 21:42:47 +00:00
|
|
|
}
|
|
|
|
|
2023-03-28 14:34:01 +00:00
|
|
|
const ThreadNote = ({ active, note, isLast, isLastSubthread, from, related, chains, onNavigate }: ThreadNoteProps) => {
|
2023-02-08 21:10:26 +00:00
|
|
|
const { formatMessage } = useIntl();
|
2023-03-28 14:34:01 +00:00
|
|
|
const replies = getReplies(note.id, chains);
|
|
|
|
const activeInReplies = replies.map(r => r.id).includes(active);
|
2023-02-07 20:04:50 +00:00
|
|
|
const [collapsed, setCollapsed] = useState(!activeInReplies);
|
2023-03-28 14:34:01 +00:00
|
|
|
const hasMultipleNotes = replies.length > 1;
|
2023-02-07 20:04:50 +00:00
|
|
|
const isLastVisibleNote = isLastSubthread && isLast && !hasMultipleNotes;
|
2023-02-09 12:26:54 +00:00
|
|
|
const className = `subthread-container ${isLast && collapsed ? "subthread-last" : "subthread-multi subthread-mid"}`;
|
2023-02-06 21:42:47 +00:00
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<div className={className}>
|
|
|
|
<Divider variant="small" />
|
|
|
|
<Note
|
2023-03-28 14:34:01 +00:00
|
|
|
highlight={active === note.id}
|
2023-02-07 20:04:50 +00:00
|
|
|
className={`thread-note ${isLastVisibleNote ? "is-last-note" : ""}`}
|
2023-03-28 14:34:01 +00:00
|
|
|
data={note}
|
|
|
|
key={note.id}
|
2023-02-06 21:42:47 +00:00
|
|
|
related={related}
|
|
|
|
/>
|
2023-02-07 20:04:50 +00:00
|
|
|
<div className="line-container"></div>
|
2023-02-06 21:42:47 +00:00
|
|
|
</div>
|
2023-03-28 14:34:01 +00:00
|
|
|
{replies.length > 0 && (
|
|
|
|
<Collapsed text={formatMessage(messages.ShowReplies)} collapsed={collapsed} setCollapsed={setCollapsed}>
|
2023-02-06 21:42:47 +00:00
|
|
|
<TierThree
|
2023-02-07 20:04:50 +00:00
|
|
|
active={active}
|
|
|
|
isLastSubthread={isLastSubthread}
|
|
|
|
from={from}
|
|
|
|
notes={replies}
|
|
|
|
related={related}
|
|
|
|
chains={chains}
|
|
|
|
onNavigate={onNavigate}
|
2023-02-06 21:42:47 +00:00
|
|
|
/>
|
2023-03-28 14:34:01 +00:00
|
|
|
</Collapsed>
|
|
|
|
)}
|
2023-02-06 21:42:47 +00:00
|
|
|
</>
|
2023-02-07 20:04:50 +00:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2023-03-28 14:34:01 +00:00
|
|
|
const TierTwo = ({ active, isLastSubthread, from, notes, related, chains, onNavigate }: SubthreadProps) => {
|
2023-02-07 20:04:50 +00:00
|
|
|
const [first, ...rest] = notes;
|
2023-02-06 21:42:47 +00:00
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<ThreadNote
|
|
|
|
active={active}
|
|
|
|
from={from}
|
|
|
|
onNavigate={onNavigate}
|
|
|
|
note={first}
|
|
|
|
chains={chains}
|
|
|
|
related={related}
|
|
|
|
isLastSubthread={isLastSubthread}
|
|
|
|
isLast={rest.length === 0}
|
|
|
|
/>
|
|
|
|
|
2023-03-28 14:34:01 +00:00
|
|
|
{rest.map((r: TaggedRawEvent, idx: number) => {
|
2023-02-07 20:04:50 +00:00
|
|
|
const lastReply = idx === rest.length - 1;
|
2023-02-06 21:42:47 +00:00
|
|
|
return (
|
2023-02-07 20:04:50 +00:00
|
|
|
<ThreadNote
|
2023-02-06 21:42:47 +00:00
|
|
|
active={active}
|
|
|
|
from={from}
|
|
|
|
onNavigate={onNavigate}
|
|
|
|
note={r}
|
|
|
|
chains={chains}
|
|
|
|
related={related}
|
|
|
|
isLastSubthread={isLastSubthread}
|
|
|
|
isLast={lastReply}
|
|
|
|
/>
|
2023-02-07 20:04:50 +00:00
|
|
|
);
|
|
|
|
})}
|
2023-02-06 21:42:47 +00:00
|
|
|
</>
|
2023-02-07 20:04:50 +00:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2023-03-28 14:34:01 +00:00
|
|
|
const TierThree = ({ active, isLastSubthread, from, notes, related, chains, onNavigate }: SubthreadProps) => {
|
2023-02-07 20:04:50 +00:00
|
|
|
const [first, ...rest] = notes;
|
2023-03-28 14:34:01 +00:00
|
|
|
const replies = getReplies(first.id, chains);
|
2023-02-07 20:04:50 +00:00
|
|
|
const hasMultipleNotes = rest.length > 0 || replies.length > 0;
|
|
|
|
const isLast = replies.length === 0 && rest.length === 0;
|
2023-02-06 21:42:47 +00:00
|
|
|
return (
|
|
|
|
<>
|
2023-02-07 20:04:50 +00:00
|
|
|
<div
|
2023-02-09 12:26:54 +00:00
|
|
|
className={`subthread-container ${hasMultipleNotes ? "subthread-multi" : ""} ${
|
|
|
|
isLast ? "subthread-last" : "subthread-mid"
|
|
|
|
}`}>
|
2023-02-06 21:42:47 +00:00
|
|
|
<Divider variant="small" />
|
|
|
|
<Note
|
2023-03-28 14:34:01 +00:00
|
|
|
highlight={active === first.id}
|
2023-02-09 12:26:54 +00:00
|
|
|
className={`thread-note ${isLastSubthread && isLast ? "is-last-note" : ""}`}
|
2023-03-28 14:34:01 +00:00
|
|
|
data={first}
|
|
|
|
key={first.id}
|
2023-02-06 21:42:47 +00:00
|
|
|
related={related}
|
|
|
|
/>
|
2023-02-07 20:04:50 +00:00
|
|
|
<div className="line-container"></div>
|
2023-02-06 21:42:47 +00:00
|
|
|
</div>
|
|
|
|
|
2023-03-28 14:34:01 +00:00
|
|
|
{replies.length > 0 && (
|
|
|
|
<TierThree
|
|
|
|
active={active}
|
|
|
|
isLastSubthread={isLastSubthread}
|
|
|
|
from={from}
|
|
|
|
notes={replies}
|
|
|
|
related={related}
|
|
|
|
chains={chains}
|
|
|
|
onNavigate={onNavigate}
|
|
|
|
/>
|
|
|
|
)}
|
2023-02-06 21:42:47 +00:00
|
|
|
|
2023-03-28 14:34:01 +00:00
|
|
|
{rest.map((r: TaggedRawEvent, idx: number) => {
|
2023-02-07 20:04:50 +00:00
|
|
|
const lastReply = idx === rest.length - 1;
|
|
|
|
const lastNote = isLastSubthread && lastReply;
|
2023-02-06 21:42:47 +00:00
|
|
|
return (
|
2023-02-07 20:04:50 +00:00
|
|
|
<div
|
2023-03-28 14:34:01 +00:00
|
|
|
key={r.id}
|
2023-02-09 12:26:54 +00:00
|
|
|
className={`subthread-container ${lastReply ? "" : "subthread-multi"} ${
|
|
|
|
lastReply ? "subthread-last" : "subthread-mid"
|
|
|
|
}`}>
|
2023-02-06 21:42:47 +00:00
|
|
|
<Divider variant="small" />
|
|
|
|
<Note
|
2023-02-07 20:04:50 +00:00
|
|
|
className={`thread-note ${lastNote ? "is-last-note" : ""}`}
|
2023-03-28 14:34:01 +00:00
|
|
|
highlight={active === r.id}
|
|
|
|
data={r}
|
|
|
|
key={r.id}
|
2023-02-06 21:42:47 +00:00
|
|
|
related={related}
|
|
|
|
/>
|
2023-02-07 20:04:50 +00:00
|
|
|
<div className="line-container"></div>
|
2023-02-06 21:42:47 +00:00
|
|
|
</div>
|
2023-02-07 20:04:50 +00:00
|
|
|
);
|
|
|
|
})}
|
2023-02-06 21:42:47 +00:00
|
|
|
</>
|
2023-02-07 20:04:50 +00:00
|
|
|
);
|
|
|
|
};
|
2022-12-20 12:08:41 +00:00
|
|
|
|
2023-03-28 14:34:01 +00:00
|
|
|
export default function Thread() {
|
|
|
|
const params = useParams();
|
|
|
|
const location = useLocation();
|
|
|
|
|
|
|
|
const link = parseNostrLink(params.id ?? "");
|
|
|
|
const thread = useThreadFeed(unwrap(link));
|
|
|
|
|
|
|
|
const [currentId, setCurrentId] = useState(link?.id);
|
2023-02-06 21:42:47 +00:00
|
|
|
|
2023-02-07 20:04:50 +00:00
|
|
|
const navigate = useNavigate();
|
2023-03-28 14:34:01 +00:00
|
|
|
const isSingleNote = thread.data?.filter(a => a.kind === EventKind.TextNote).length === 1;
|
2023-02-13 10:30:01 +00:00
|
|
|
const { formatMessage } = useIntl();
|
2023-02-07 20:04:50 +00:00
|
|
|
|
|
|
|
const chains = useMemo(() => {
|
2023-03-28 14:34:01 +00:00
|
|
|
const chains = new Map<u256, Array<TaggedRawEvent>>();
|
|
|
|
if (thread.data) {
|
|
|
|
thread.data
|
|
|
|
?.filter(a => a.kind === EventKind.TextNote)
|
|
|
|
.sort((a, b) => b.created_at - a.created_at)
|
|
|
|
.forEach(v => {
|
|
|
|
const thread = EventExt.extractThread(v);
|
|
|
|
const replyTo = thread?.replyTo?.Event ?? thread?.root?.Event;
|
|
|
|
if (replyTo) {
|
|
|
|
if (!chains.has(replyTo)) {
|
|
|
|
chains.set(replyTo, [v]);
|
|
|
|
} else {
|
|
|
|
unwrap(chains.get(replyTo)).push(v);
|
|
|
|
}
|
|
|
|
} else if (v.tags.length > 0) {
|
|
|
|
//console.log("Not replying to anything: ", v);
|
2023-02-07 20:04:50 +00:00
|
|
|
}
|
2023-03-28 14:34:01 +00:00
|
|
|
});
|
|
|
|
}
|
2023-02-07 20:04:50 +00:00
|
|
|
return chains;
|
2023-03-28 14:34:01 +00:00
|
|
|
}, [thread.data]);
|
2022-12-20 12:08:41 +00:00
|
|
|
|
2023-03-28 14:34:01 +00:00
|
|
|
// Root is the parent of the current note or the current note if its a root note or the root of the thread
|
2023-03-04 21:41:29 +00:00
|
|
|
const root = useMemo(() => {
|
2023-03-29 13:39:23 +00:00
|
|
|
const currentNote =
|
|
|
|
thread.data?.find(ne => ne.id === currentId) ??
|
2023-03-29 18:09:14 +00:00
|
|
|
(location.state && "sig" in location.state ? (location.state as TaggedRawEvent) : undefined);
|
2023-03-28 14:34:01 +00:00
|
|
|
if (currentNote) {
|
|
|
|
const currentThread = EventExt.extractThread(currentNote);
|
|
|
|
const isRoot = (ne?: ThreadInfo) => ne === undefined;
|
2023-03-04 21:41:29 +00:00
|
|
|
|
2023-03-28 14:34:01 +00:00
|
|
|
if (isRoot(currentThread)) {
|
|
|
|
return currentNote;
|
2023-03-04 21:41:29 +00:00
|
|
|
}
|
2023-03-28 14:34:01 +00:00
|
|
|
const replyTo = currentThread?.replyTo?.Event ?? currentThread?.root?.Event;
|
2023-03-04 21:41:29 +00:00
|
|
|
|
2023-03-28 14:34:01 +00:00
|
|
|
// sometimes the root event ID is missing, and we can only take the happy path if the root event ID exists
|
|
|
|
if (replyTo) {
|
|
|
|
return thread.data?.find(a => a.id === replyTo);
|
|
|
|
}
|
2023-02-06 21:42:47 +00:00
|
|
|
|
2023-03-28 14:34:01 +00:00
|
|
|
const possibleRoots = thread.data?.filter(a => {
|
|
|
|
const thread = EventExt.extractThread(a);
|
|
|
|
return isRoot(thread);
|
|
|
|
});
|
|
|
|
if (possibleRoots) {
|
|
|
|
// worst case we need to check every possible root to see which one contains the current note as a child
|
|
|
|
for (const ne of possibleRoots) {
|
|
|
|
const children = chains.get(ne.id) ?? [];
|
2023-02-06 21:42:47 +00:00
|
|
|
|
2023-03-28 14:34:01 +00:00
|
|
|
if (children.find(ne => ne.id === currentId)) {
|
|
|
|
return ne;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-02-07 20:04:50 +00:00
|
|
|
}
|
2023-03-28 14:34:01 +00:00
|
|
|
}, [thread.data, currentId, location]);
|
2023-02-06 21:42:47 +00:00
|
|
|
|
2023-03-28 14:34:01 +00:00
|
|
|
const parent = useMemo(() => {
|
|
|
|
if (root) {
|
|
|
|
const currentThread = EventExt.extractThread(root);
|
|
|
|
return currentThread?.replyTo?.Event ?? currentThread?.root?.Event;
|
2023-02-07 20:04:50 +00:00
|
|
|
}
|
2023-03-28 14:34:01 +00:00
|
|
|
}, [root]);
|
2023-02-06 21:42:47 +00:00
|
|
|
|
2023-03-28 14:34:01 +00:00
|
|
|
const brokenChains = Array.from(chains?.keys()).filter(a => !thread.data?.some(b => b.id === a));
|
2023-01-06 14:05:50 +00:00
|
|
|
|
2023-03-28 14:34:01 +00:00
|
|
|
function renderRoot(note: TaggedRawEvent) {
|
2023-02-07 20:04:50 +00:00
|
|
|
const className = `thread-root ${isSingleNote ? "thread-root-single" : ""}`;
|
|
|
|
if (note) {
|
2023-02-12 12:31:48 +00:00
|
|
|
return (
|
|
|
|
<Note
|
|
|
|
className={className}
|
2023-03-28 14:34:01 +00:00
|
|
|
key={note.id}
|
|
|
|
data={note}
|
|
|
|
related={getReactions(thread.data, note.id)}
|
2023-02-12 12:31:48 +00:00
|
|
|
options={{ showReactionsLink: true }}
|
|
|
|
/>
|
|
|
|
);
|
2023-02-07 20:04:50 +00:00
|
|
|
} else {
|
2023-03-28 14:34:01 +00:00
|
|
|
return <NoteGhost className={className}>Loading thread root.. ({thread.data?.length} notes loaded)</NoteGhost>;
|
2023-01-06 13:01:54 +00:00
|
|
|
}
|
2023-02-07 20:04:50 +00:00
|
|
|
}
|
2023-01-06 13:01:54 +00:00
|
|
|
|
2023-02-07 20:04:50 +00:00
|
|
|
function renderChain(from: u256): ReactNode {
|
|
|
|
if (!from || !chains) {
|
|
|
|
return;
|
|
|
|
}
|
2023-02-07 19:47:57 +00:00
|
|
|
const replies = chains.get(from);
|
2023-03-28 14:34:01 +00:00
|
|
|
if (replies && currentId) {
|
2023-02-07 20:04:50 +00:00
|
|
|
return (
|
|
|
|
<Subthread
|
2023-03-28 14:34:01 +00:00
|
|
|
active={currentId}
|
2023-02-07 20:04:50 +00:00
|
|
|
from={from}
|
|
|
|
notes={replies}
|
2023-03-28 14:34:01 +00:00
|
|
|
related={getAllReactions(
|
|
|
|
thread.data,
|
|
|
|
replies.map(a => a.id)
|
|
|
|
)}
|
2023-02-07 20:04:50 +00:00
|
|
|
chains={chains}
|
2023-03-28 14:34:01 +00:00
|
|
|
onNavigate={() => {
|
|
|
|
//nothing
|
|
|
|
}}
|
2023-02-07 20:04:50 +00:00
|
|
|
/>
|
|
|
|
);
|
2023-02-06 21:42:47 +00:00
|
|
|
}
|
2023-02-07 20:04:50 +00:00
|
|
|
}
|
2023-02-06 21:42:47 +00:00
|
|
|
|
2023-02-07 20:04:50 +00:00
|
|
|
function goBack() {
|
2023-03-28 14:34:01 +00:00
|
|
|
if (parent) {
|
|
|
|
setCurrentId(parent);
|
2023-02-07 20:04:50 +00:00
|
|
|
} else {
|
2023-03-28 14:34:01 +00:00
|
|
|
navigate(-1);
|
2023-01-06 13:01:54 +00:00
|
|
|
}
|
2023-02-07 20:04:50 +00:00
|
|
|
}
|
2023-01-06 13:01:54 +00:00
|
|
|
|
2023-02-13 10:30:01 +00:00
|
|
|
const parentText = formatMessage({
|
|
|
|
defaultMessage: "Parent",
|
|
|
|
description: "Link to parent note in thread",
|
|
|
|
});
|
|
|
|
const backText = formatMessage({
|
|
|
|
defaultMessage: "Back",
|
|
|
|
description: "Navigate back button on threads view",
|
|
|
|
});
|
2023-02-07 20:04:50 +00:00
|
|
|
return (
|
|
|
|
<div className="main-content mt10">
|
2023-03-28 14:34:01 +00:00
|
|
|
<BackButton onClick={goBack} text={parent ? parentText : backText} />
|
2023-02-07 20:04:50 +00:00
|
|
|
<div className="thread-container">
|
2023-03-28 14:34:01 +00:00
|
|
|
{root && renderRoot(root)}
|
|
|
|
{root && renderChain(root.id)}
|
|
|
|
|
|
|
|
{brokenChains.length > 0 && <h3>Other replies</h3>}
|
|
|
|
{brokenChains.map(a => {
|
|
|
|
return (
|
|
|
|
<div className="mb10">
|
|
|
|
<NoteGhost className={`thread-note thread-root ghost-root`} key={a}>
|
|
|
|
Missing event <Link to={eventLink(a)}>{a.substring(0, 8)}</Link>
|
|
|
|
</NoteGhost>
|
|
|
|
{renderChain(a)}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
})}
|
2023-02-06 21:42:47 +00:00
|
|
|
</div>
|
2023-02-07 20:04:50 +00:00
|
|
|
</div>
|
|
|
|
);
|
2023-01-26 07:25:05 +00:00
|
|
|
}
|
2023-02-06 21:42:47 +00:00
|
|
|
|
2023-03-28 14:34:01 +00:00
|
|
|
function getReplies(from: u256, chains?: Map<u256, Array<TaggedRawEvent>>): Array<TaggedRawEvent> {
|
2023-02-07 20:04:50 +00:00
|
|
|
if (!from || !chains) {
|
|
|
|
return [];
|
|
|
|
}
|
2023-02-07 19:47:57 +00:00
|
|
|
const replies = chains.get(from);
|
2023-02-07 20:04:50 +00:00
|
|
|
return replies ? replies : [];
|
2023-02-06 21:42:47 +00:00
|
|
|
}
|