feat(inbox): add functionality to 'context' button, highlighting reply in single comment thread

This commit is contained in:
plebeius.eth
2024-02-03 15:26:13 +01:00
parent 328f4ae0b3
commit 7537cbaa6d
7 changed files with 92 additions and 18 deletions

View File

@@ -86,6 +86,7 @@ function App() {
<Route path='/p/all/about' element={<About />} />
<Route path='/p/:subplebbitAddress/c/:commentCid' element={<Post />} />
<Route path='/p/:subplebbitAddress/c/:commentCid/context' element={<Post />} />
<Route path='/p/:subplebbitAddress/c/:commentCid/about' element={<About />} />
<Route path='/p/:subplebbitAddress/submit' element={<Submit />} />

View File

@@ -21,6 +21,7 @@ interface CommentToolsProps {
isReply?: boolean;
isSingleReply?: boolean;
parentCid?: string;
postCid?: string;
removed?: boolean;
replyCount?: number;
spoiler?: boolean;
@@ -86,9 +87,11 @@ const ReplyTools = ({ author, cid, hasLabel, index, isAuthor, isMod, showReplyFo
);
};
const SingleReplyTools = ({ author, cid, hasLabel, index, isAuthor, isMod, parentCid, showReplyForm, subplebbitAddress }: CommentToolsProps) => {
const SingleReplyTools = ({ author, cid, hasLabel, index, isAuthor, isMod, parentCid, postCid, showReplyForm, subplebbitAddress }: CommentToolsProps) => {
const { t } = useTranslation();
const comment = useComment({ commentCid: parentCid });
const comment = useComment({ commentCid: postCid });
const hasContext = parentCid !== postCid;
return (
<>
@@ -100,10 +103,10 @@ const SingleReplyTools = ({ author, cid, hasLabel, index, isAuthor, isMod, paren
</li>
{isAuthor && <EditMenu cid={cid} />}
<li className={styles.button}>
<Link to={cid ? `/p/${subplebbitAddress}/c/${parentCid}` : `/profile/${index}`}>{t('context')}</Link>
<Link to={cid ? (hasContext ? `/p/${subplebbitAddress}/c/${cid}/context` : `/p/${subplebbitAddress}/c/${cid}`) : `/profile/${index}`}>{t('context')}</Link>
</li>
<li className={styles.button}>
<Link to={cid ? `/p/${subplebbitAddress}/c/${parentCid}` : `/profile/${index}`}>
<Link to={cid ? `/p/${subplebbitAddress}/c/${postCid}` : `/profile/${index}`}>
{t('full_comments')} ({comment?.replyCount || 0})
</Link>
</li>
@@ -147,6 +150,7 @@ const CommentTools = ({
isReply,
isSingleReply,
parentCid,
postCid,
removed,
replyCount,
spoiler,
@@ -172,6 +176,7 @@ const CommentTools = ({
isAuthor={isAuthor}
isMod={isMod}
parentCid={parentCid}
postCid={postCid}
showReplyForm={showReplyForm}
subplebbitAddress={subplebbitAddress}
/>
@@ -183,7 +188,6 @@ const CommentTools = ({
index={index}
isAuthor={isAuthor}
isMod={isMod}
parentCid={parentCid}
showReplyForm={showReplyForm}
subplebbitAddress={subplebbitAddress}
/>

View File

@@ -106,6 +106,13 @@
margin-top: 5px;
}
.highlightMedia {
background-color: var(--yellow-highlight);
padding: 2px 5px;
display: inline-block;
width: 100%;
}
.md {
margin-top: 5px;
margin-bottom: 5px;
@@ -121,7 +128,7 @@
color: var(--markdown-link) !important;
}
.singleCommentHighlight {
.highlightContent {
background-color: var(--yellow-highlight);
padding: 2px 5px;
}

View File

@@ -1,7 +1,7 @@
import { Fragment, useEffect, useMemo, useState } from 'react';
import { Comment, useAccountComment, useAuthorAddress, useBlock, useComment, useEditedComment, useSubplebbit } from '@plebbit/plebbit-react-hooks';
import { flattenCommentsPages } from '@plebbit/plebbit-react-hooks/dist/lib/utils';
import { Link, useLocation } from 'react-router-dom';
import { Link, useLocation, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import styles from './reply.module.css';
import useReplies from '../../hooks/use-replies';
@@ -18,7 +18,7 @@ import ReplyForm from '../reply-form';
import useDownvote from '../../hooks/use-downvote';
import useStateString from '../../hooks/use-state-string';
import useUpvote from '../../hooks/use-upvote';
import { isInboxView } from '../../lib/utils/view-utils';
import { isInboxView, isPostContextView } from '../../lib/utils/view-utils';
import { getShortAddress } from '@plebbit/plebbit-js';
import Markdown from '../markdown';
@@ -183,6 +183,7 @@ const InboxParentInfo = ({ address, cid, markedAsRead, shortAddress, subplebbitA
};
interface ReplyProps {
cidOfReplyWithContext?: string;
depth?: number;
index?: number;
isNotification?: boolean;
@@ -191,7 +192,7 @@ interface ReplyProps {
reply: Comment | undefined;
}
const Reply = ({ depth = 0, isSingleComment, isSingleReply, isNotification = false, reply = {} }: ReplyProps) => {
const Reply = ({ cidOfReplyWithContext, depth = 0, isSingleComment, isSingleReply, isNotification = false, reply = {} }: ReplyProps) => {
// handle pending mod or author edit
const { editedComment: editedPost } = useEditedComment({ comment: reply });
if (editedPost) {
@@ -209,6 +210,7 @@ const Reply = ({ depth = 0, isSingleComment, isSingleReply, isNotification = fal
linkWidth,
markedAsRead,
pinned,
parentCid,
postCid,
removed,
spoiler,
@@ -271,7 +273,9 @@ const Reply = ({ depth = 0, isSingleComment, isSingleReply, isNotification = fal
const parentOfPendingReply = useComment({ commentCid: pendingReply?.parentCid });
const location = useLocation();
const params = useParams();
const isInInboxView = isInboxView(location.pathname);
const isInPostContextView = isPostContextView(location.pathname, params);
return (
<div className={styles.reply}>
@@ -291,7 +295,7 @@ const Reply = ({ depth = 0, isSingleComment, isSingleReply, isNotification = fal
<span className={styles.expand} onClick={handleCollapseButton}>
[{collapsed ? '+' : ''}]
</span>
<ReplyAuthor address={author?.address} authorRole={authorRole} cid={cid} displayName={author.displayName} shortAuthorAddress={shortAuthorAddress} />
<ReplyAuthor address={author?.address} authorRole={authorRole} cid={cid} displayName={author?.displayName} shortAuthorAddress={shortAuthorAddress} />
<span className={styles.score}>{scoreString}</span> <span className={styles.time}>{getFormattedTimeAgo(timestamp)}</span>{' '}
{pinned && <span className={styles.pinned}>- {t('stickied_comment')}</span>}
{collapsed && <span className={styles.children}> ({childrenString})</span>}
@@ -316,7 +320,7 @@ const Reply = ({ depth = 0, isSingleComment, isSingleReply, isNotification = fal
/>
)}
{!collapsed && (
<div className={styles.usertext}>
<div className={`${styles.usertext} ${commentMediaInfo && (isSingleComment || cidOfReplyWithContext === cid) ? styles.highlightMedia : ''}`}>
{commentMediaInfo && (
<ReplyMedia
commentMediaInfo={commentMediaInfo}
@@ -329,7 +333,7 @@ const Reply = ({ depth = 0, isSingleComment, isSingleReply, isNotification = fal
toggleExpanded={toggleExpanded}
/>
)}
<div className={`${styles.md} ${isSingleComment ? styles.singleCommentHighlight : ''}`}>
<div className={`${styles.md} ${isSingleComment || cidOfReplyWithContext === cid ? styles.highlightContent : ''}`}>
<Markdown content={contentString} />
</div>
</div>
@@ -344,7 +348,8 @@ const Reply = ({ depth = 0, isSingleComment, isSingleReply, isNotification = fal
isReply={true}
isSingleReply={isSingleReply}
index={reply?.index}
parentCid={postCid}
parentCid={parentCid}
postCid={postCid}
removed={removed}
replyCount={replies.length}
spoiler={spoiler}
@@ -357,7 +362,12 @@ const Reply = ({ depth = 0, isSingleComment, isSingleReply, isNotification = fal
return (
<Fragment key={`${index}-${reply.cid}`}>
{!depth || depth < 9 ? (
<Reply key={`${index}${reply.cid}`} reply={reply} depth={(depth || 0) + 1} />
<Reply
key={`${index}${reply.cid}`}
reply={reply}
depth={(depth || 0) + 1}
cidOfReplyWithContext={isInPostContextView ? params?.commentCid : undefined}
/>
) : (
<div className={styles.continueThisThread}>
<Link to={`/p/${subplebbitAddress}/c/${cid}`}>continue this thread</Link>

View File

@@ -0,0 +1,39 @@
import { Comment } from '@plebbit/plebbit-react-hooks';
export const findTopParentCidOfReply = (replyCid: string, post: Comment): string | null => {
if (!post.replyCount || post.replyCount === 0) {
return null;
}
for (const firstLevelReply of post.replies?.pages?.topAll?.comments) {
if (firstLevelReply.cid === replyCid) {
return firstLevelReply.cid;
}
const result = findInDeeperReplies(replyCid, firstLevelReply, firstLevelReply.cid);
if (result) {
return result;
}
}
return null;
};
const findInDeeperReplies = (replyCid: string, currentReply: Comment, firstLevelParentCid: string): string | null => {
if (currentReply.replyCount > 0 && currentReply.replies?.pages?.topAll?.comments) {
for (const deeperReply of currentReply.replies.pages.topAll.comments) {
if (deeperReply.cid === replyCid) {
return firstLevelParentCid;
}
const result = findInDeeperReplies(replyCid, deeperReply, firstLevelParentCid);
if (result) {
return result;
}
}
}
return null;
};
export default findTopParentCidOfReply;

View File

@@ -88,6 +88,10 @@ export const isPostView = (pathname: string, params: ParamsType): boolean => {
return params.subplebbitAddress && params.commentCid ? pathname.startsWith(`/p/${params.subplebbitAddress}/c/${params.commentCid}`) : false;
};
export const isPostContextView = (pathname: string, params: ParamsType): boolean => {
return params.subplebbitAddress && params.commentCid ? pathname.startsWith(`/p/${params.subplebbitAddress}/c/${params.commentCid}/context`) : false;
};
export const isProfileView = (pathname: string): boolean => {
return pathname.startsWith(`/profile`);
};

View File

@@ -10,25 +10,33 @@ import PostComponent from '../../components/post';
import Sidebar from '../../components/sidebar/';
import useReplies from '../../hooks/use-replies';
import useStateString from '../../hooks/use-state-string';
import { isPendingView } from '../../lib/utils/view-utils';
import { isPendingView, isPostContextView } from '../../lib/utils/view-utils';
import findTopParentCidOfReply from '../../lib/utils/cid-utils';
const Post = () => {
const { t } = useTranslation();
const params = useParams();
const location = useLocation();
const isInPendingView = isPendingView(location.pathname, params);
const isInPostContextView = isPostContextView(location.pathname, params);
const comment = useComment({ commentCid: params?.commentCid });
const pendingPost = useAccountComment({ commentIndex: params?.accountCommentIndex as any });
// if in inbox reply context view, get the context comment
const postComment = useComment({ commentCid: comment?.postCid });
const topParentCid = findTopParentCidOfReply(comment.cid, postComment);
const topParentComment = useComment({ commentCid: topParentCid || '' });
const post = isInPendingView ? pendingPost : comment;
const isSingleComment = comment?.parentCid ? true : false;
// in pending page, redirect to post view when post.cid is received
const navigate = useNavigate();
useEffect(() => {
if (post?.cid && post?.subplebbitAddress) {
if (post?.cid && post?.subplebbitAddress && !isInPostContextView) {
navigate(`/p/${post?.subplebbitAddress}/c/${post?.cid}`, { replace: true });
}
}, [post?.cid, post?.subplebbitAddress, navigate]);
}, [post?.cid, post?.subplebbitAddress, navigate, isInPostContextView]);
const { cid, downvoteCount, postCid, replyCount, subplebbitAddress, timestamp, title, upvoteCount } = comment || {};
const subplebbit = useSubplebbit({ subplebbitAddress });
@@ -99,7 +107,8 @@ const Post = () => {
</div>
)}
<div className={styles.replies}>
{isSingleComment && <Reply key={`singleComment-${comment.cid}`} reply={comment} depth={0} isSingleComment={true} />}
{isSingleComment && isInPostContextView && <Reply key={`contextComment-${topParentComment.cid}-test`} reply={topParentComment} depth={0} />}
{isSingleComment && !isInPostContextView && <Reply key={`singleComment-${comment.cid}`} reply={comment} depth={0} isSingleComment={true} />}
{!isSingleComment && replies.map((reply, index) => <Reply key={`${index}${reply.cid}`} reply={reply} depth={comment.depth} />)}
</div>
</div>