diff --git a/src/components/views/Board.jsx b/src/components/views/Board.jsx
index 8427ae43..7ea65060 100644
--- a/src/components/views/Board.jsx
+++ b/src/components/views/Board.jsx
@@ -1,9 +1,10 @@
-import React, { useState, useEffect, useRef, Fragment } from 'react';
+import React, { Fragment, useEffect, useMemo, useRef, useState } from 'react';
import { Helmet } from 'react-helmet-async';
import InfiniteScroll from 'react-infinite-scroller';
import { Link, useNavigate, useParams } from 'react-router-dom';
import { Tooltip } from 'react-tooltip';
-import { useFeed, usePublishComment } from '@plebbit/plebbit-react-hooks';
+import { useAccount, useAccountComments, useFeed, usePublishComment } from '@plebbit/plebbit-react-hooks';
+import { flattenCommentsPages } from '@plebbit/plebbit-react-hooks/dist/lib/utils'
import { debounce } from 'lodash';
import useGeneralStore from '../../hooks/stores/useGeneralStore';
import { Container, NavBar, Header, Break, PostFormLink, PostFormTable, PostForm, TopBar, BoardForm } from '../styled/Board.styled';
@@ -19,7 +20,6 @@ import getDate from '../../utils/getDate';
import handleAddressClick from '../../utils/handleAddressClick';
import handleQuoteClick from '../../utils/handleQuoteClick';
import handleStyleChange from '../../utils/handleStyleChange';
-import renderComments from '../../utils/renderComments';
import useClickForm from '../../hooks/useClickForm';
import useError from '../../hooks/useError';
import packageJson from '../../../package.json'
@@ -62,6 +62,62 @@ const Board = () => {
const [errorMessage, setErrorMessage] = useState(null);
useError(errorMessage, [errorMessage]);
+ const account = useAccount();
+
+
+ const flattenedRepliesByThread = useMemo(() => {
+ return selectedFeed.reduce((acc, thread) => {
+ const replies = flattenCommentsPages(thread.replies);
+ acc[thread.cid] = replies;
+ return acc;
+ }, {});
+ }, [selectedFeed]);
+
+
+ const allParentCids = useMemo(() => {
+ const allRepliesCids = Object.values(flattenedRepliesByThread).flatMap(replies => replies.map(reply => reply.cid));
+ const allThreadCids = selectedFeed.map(thread => thread.cid);
+ return [...allThreadCids, ...allRepliesCids];
+ }, [flattenedRepliesByThread, selectedFeed]);
+
+
+ const filter = useMemo(() => ({
+ parentCids: allParentCids
+ }), [allParentCids]);
+
+
+ const { accountComments } = useAccountComments({ filter });
+
+
+ const filteredRepliesByThread = useMemo(() => {
+ const maxRepliesPerThread = 5;
+
+ const accountRepliesNotYetInCommentReplies = selectedFeed.reduce((acc, thread) => {
+ const replyCids = new Set(flattenedRepliesByThread[thread.cid].map(reply => reply.cid));
+ acc[thread.cid] = accountComments.filter(accountReply => !replyCids.has(accountReply.cid) && accountReply.parentCid === thread.cid);
+ return acc;
+ }, {});
+
+ return selectedFeed.reduce((acc, thread) => {
+ const combinedReplies = [...flattenedRepliesByThread[thread.cid], ...accountRepliesNotYetInCommentReplies[thread.cid]].sort((a, b) => a.timestamp - b.timestamp);
+ acc[thread.cid] = {
+ displayedReplies: combinedReplies.slice(0, maxRepliesPerThread),
+ omittedCount: Math.max(combinedReplies.length - maxRepliesPerThread, 0),
+ };
+ return acc;
+ }, {});
+ }, [flattenedRepliesByThread, accountComments, selectedFeed]);
+
+
+ const pendingReplyCounts = useMemo(() => {
+ return selectedFeed.reduce((acc, thread) => {
+ const replyCids = new Set(flattenedRepliesByThread[thread.cid].map(reply => reply.cid));
+ acc[thread.cid] = accountComments.filter(accountReply => !replyCids.has(accountReply.cid) && accountReply.parentCid === thread.cid).length;
+ return acc;
+ }, {});
+ }, [flattenedRepliesByThread, accountComments, selectedFeed]);
+
+
// temporary title from JSON, gets subplebbitAddress from URL
useEffect(() => {
setSelectedAddress(subplebbitAddress);
@@ -396,8 +452,8 @@ const Board = () => {
hasMore={hasMore}
>
{selectedFeed.map((thread) => {
- const { replies: { pages: { topAll: { comments } = {} } = {} } = {} } = thread;
- const { renderedComments, omittedCount } = renderComments(comments);
+ const { replies: { pages: { topAll: {} = {} } = {} } = {} } = thread;
+ const { displayedReplies, omittedCount } = filteredRepliesByThread[thread.cid] || {};
const commentMediaInfo = getCommentMediaInfo(thread);
const fallbackImgUrl = "/assets/filedeleted-res.gif";
return (
@@ -542,7 +598,7 @@ const Board = () => {
) : null}
- {renderedComments?.map((reply) => {
+ {displayedReplies?.map((reply) => {
const replyMediaInfo = getCommentMediaInfo(reply);
const fallbackImgUrl = "/assets/filedeleted-res.gif";
const shortParentCid = findShortParentCid(reply.parentCid, selectedFeed);
@@ -572,9 +628,21 @@ const Board = () => {
onClick={() => handleAddressClick(reply.author.shortAddress)}
>
(u/
-
- {reply.author.shortAddress}
-
+ {reply.author?.shortAddress ?
+ (
+
+ {reply.author?.shortAddress}
+
+ ) : (
+
+ {account?.author?.address.slice(0, 10) + "(...)"}
+
+ )
+ }
)
@@ -583,14 +651,18 @@ const Board = () => {
{}} key={`pl1-${reply.cid}`} onClick={() => {}} title="Link to this post">c/
- {
- if (e.button === 2) return;
- e.preventDefault();
- setIsReplyOpen(true);
- setSelectedShortCid(reply.shortCid);
- setSelectedParentCid(reply.cid);
- }} title="Reply to this post">{reply.shortCid}
+ {reply.shortCid ? (
+ {
+ if (e.button === 2) return;
+ e.preventDefault();
+ setIsReplyOpen(true);
+ setSelectedShortCid(reply.shortCid);
+ setSelectedParentCid(reply.cid);
+ }} title="Reply to this post">{reply.shortCid}
+ ) : (
+ Pending
+ )}
@@ -797,18 +869,18 @@ const Board = () => {
{
- thread.replyCount === 0 ?
+ (thread.replyCount + pendingReplyCounts[thread.cid]) === 0 ?
("No replies")
- : thread.replyCount === 1 ?
+ : (thread.replyCount + pendingReplyCounts[thread.cid]) === 1 ?
("1 reply")
- : thread.replyCount > 1 ?
- (thread.replyCount + " replies")
+ : (thread.replyCount + pendingReplyCounts[thread.cid]) > 1 ?
+ ((thread.replyCount + pendingReplyCounts[thread.cid]) + " replies")
: null
}
setSelectedThread(thread.cid)} className="button-mobile" >View Thread
- {renderedComments?.map((reply) => {
+ {displayedReplies?.map((reply) => {
const replyMediaInfo = getCommentMediaInfo(reply);
const shortParentCid = findShortParentCid(reply.parentCid, selectedFeed);
return (
@@ -837,9 +909,21 @@ const Board = () => {
onClick={() => handleAddressClick(reply.author.shortAddress)}
>
(u/
-
- {reply.author.shortAddress}
-
+ {reply.author?.shortAddress ?
+ (
+
+ {reply.author?.shortAddress}
+
+ ) : (
+
+ {account?.author?.address.slice(0, 8) + "(...)"}
+
+ )
+ }
)
@@ -847,15 +931,19 @@ const Board = () => {
{getDate(reply.timestamp)}
{}} key={`mob-pl1-${reply.cid}`} onClick={() => {}} title="Link to this post">c/
- {
- if (e.button === 2) return;
- e.preventDefault();
- setIsReplyOpen(true);
- setSelectedShortCid(reply.shortCid);
- setSelectedParentCid(reply.cid);
- }} title="Reply to this post">{reply.shortCid}
-
+ {reply.shortCid ? (
+ {
+ if (e.button === 2) return;
+ e.preventDefault();
+ setIsReplyOpen(true);
+ setSelectedShortCid(reply.shortCid);
+ setSelectedParentCid(reply.cid);
+ }} title="Reply to this post">{reply.shortCid}
+
+ ) : (
+ Pending
+ )}
{reply.link ? (
diff --git a/src/components/views/Thread.jsx b/src/components/views/Thread.jsx
index 66649c11..fc3ee46d 100644
--- a/src/components/views/Thread.jsx
+++ b/src/components/views/Thread.jsx
@@ -1,8 +1,9 @@
-import React, { useState, useEffect, useRef } from 'react';
+import React, { useEffect, useMemo, useRef, useState } from 'react';
import { Helmet } from 'react-helmet-async';
import { Link, useNavigate, useParams } from 'react-router-dom';
import { Tooltip } from 'react-tooltip';
-import { useAccount, useAccountComment, useComment, usePublishComment } from '@plebbit/plebbit-react-hooks';
+import { useAccount, useAccountComments, useComment, usePublishComment } from '@plebbit/plebbit-react-hooks';
+import { flattenCommentsPages } from '@plebbit/plebbit-react-hooks/dist/lib/utils'
import { debounce } from 'lodash';
import useGeneralStore from '../../hooks/stores/useGeneralStore';
import { Container, NavBar, Header, Break, PostForm, PostFormTable } from '../styled/Board.styled';
@@ -19,7 +20,6 @@ import getDate from '../../utils/getDate';
import handleAddressClick from '../../utils/handleAddressClick';
import handleQuoteClick from '../../utils/handleQuoteClick';
import handleStyleChange from '../../utils/handleStyleChange';
-import renderThreadComments from '../../utils/renderThreadComments';
import useClickForm from '../../hooks/useClickForm';
import useError from '../../hooks/useError';
import packageJson from '../../../package.json'
@@ -35,7 +35,7 @@ const Thread = () => {
isSettingsOpen, setIsSettingsOpen,
setResolveCaptchaPromise,
setPendingComment,
- pendingCommentIndex, setPendingCommentIndex,
+ setPendingCommentIndex,
selectedAddress, setSelectedAddress,
setSelectedParentCid,
setSelectedShortCid,
@@ -65,9 +65,32 @@ const Thread = () => {
const [errorMessage, setErrorMessage] = useState(null);
useError(errorMessage, [errorMessage]);
- const pendingComment = useAccountComment({commentIndex: pendingCommentIndex});
- const renderedComments = renderThreadComments(comment, pendingComment);
+ const flattenedReplies = useMemo(() =>
+ flattenCommentsPages(comment.replies), [comment.replies]
+ );
+
+
+ const filter = useMemo(() => ({
+ parentCids: [
+ selectedThread || 'n/a', ...flattenedReplies.map(reply => reply.cid)
+ ]
+ }), [flattenedReplies]);
+
+
+ const { accountComments } = useAccountComments({filter});
+
+
+ const accountRepliesNotYetInCommentReplies = useMemo(() => {
+ const commentReplyCids = new Set(flattenedReplies.map(reply => reply.cid))
+ return accountComments.filter(accountReply => !commentReplyCids.has(accountReply.cid))
+ }, [flattenedReplies, accountComments]);
+
+
+ const sortedReplies = useMemo(() => [
+ ...accountRepliesNotYetInCommentReplies, ...flattenedReplies
+ ].sort((a, b) => a.timestamp - b.timestamp
+ ), [accountRepliesNotYetInCommentReplies, flattenedReplies]);
// temporary title from JSON, gets subplebbitAddress and threadCid from URL
useEffect(() => {
@@ -522,7 +545,7 @@ const Thread = () => {
{comment.state === "fetching-ipns" ?
(null) :
(comment.replyCount === undefined ? () : (null))}
- {renderedComments.map((reply, index) => {
+ {sortedReplies.map((reply, index) => {
const replyMediaInfo = getCommentMediaInfo(reply);
const fallbackImgUrl = "/assets/filedeleted-res.gif";
const shortParentCid = findShortParentCid(reply.parentCid, comment);
@@ -758,7 +781,7 @@ const Thread = () => {
{comment.replyCount === undefined ? : null}
- {renderedComments.map((reply, index) => {
+ {sortedReplies.map((reply, index) => {
const replyMediaInfo = getCommentMediaInfo(reply);
const shortParentCid = findShortParentCid(reply.parentCid, comment);
return (
@@ -798,7 +821,7 @@ const Thread = () => {
data-tooltip-content={account?.author?.address}
data-tooltip-place="top"
>
- {account?.author?.address.slice(0, 6) + "(...)"}
+ {account?.author?.address.slice(0, 8) + "(...)"}
)
}
diff --git a/src/utils/renderComments.js b/src/utils/renderComments.js
deleted file mode 100644
index d47827fb..00000000
--- a/src/utils/renderComments.js
+++ /dev/null
@@ -1,30 +0,0 @@
-function renderComments(comments) {
- let displayedCount = 0;
- let omittedCount = 0;
-
- const renderComment = (comment) => {
- const { replyCount, replies: { pages: { topAll: { comments: nestedComments = [] } = {} } = {} } = {} } = comment;
- let renderedNestedComments = [];
-
- if (replyCount > 0 && nestedComments.length > 0) {
- const result = renderComments(nestedComments);
- renderedNestedComments = result.renderedComments;
- omittedCount += result.omittedCount;
- }
-
- const allComments = [comment, ...renderedNestedComments];
- const displayedComments = allComments.slice(0, 5 - displayedCount);
-
- displayedCount += displayedComments.length;
- omittedCount += allComments.length - displayedComments.length;
-
- return displayedComments;
- };
-
- const renderedComments = comments?.flatMap(renderComment);
- renderedComments?.sort((a, b) => a.timestamp - b.timestamp);
-
- return { renderedComments, omittedCount };
-}
-
-export default renderComments;
\ No newline at end of file
diff --git a/src/utils/renderThreadComments.js b/src/utils/renderThreadComments.js
deleted file mode 100644
index 0579f381..00000000
--- a/src/utils/renderThreadComments.js
+++ /dev/null
@@ -1,24 +0,0 @@
-function renderThreadComments(comment, pendingComment = null) {
- const nestedComments = comment?.replies?.pages?.topAll?.comments || {};
- const commentKeys = Object.keys(nestedComments);
-
- const renderedComments = commentKeys.map(key => {
- const comment = nestedComments[key];
- const { replies: { pages: { topAll: { comments: childNestedComments = [] } = {} } = {} } = {} } = comment;
- if (comment.replyCount > 0 && childNestedComments) {
- const renderedNestedComments = renderThreadComments(comment);
- return [comment, ...renderedNestedComments];
- }
- return [comment];
- }).flat();
-
- const sortedComments = renderedComments.sort((a, b) => a.timestamp - b.timestamp);
-
- if (pendingComment?.parentCid === comment?.cid) {
- sortedComments.push(pendingComment);
- }
-
- return sortedComments;
-}
-
-export default renderThreadComments;
\ No newline at end of file