mirror of
https://github.com/plebbit/seedit.git
synced 2026-02-15 08:21:19 -05:00
feat(inbox): add functionality to 'context' button, highlighting reply in single comment thread
This commit is contained in:
@@ -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 />} />
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
39
src/lib/utils/cid-utils.ts
Normal file
39
src/lib/utils/cid-utils.ts
Normal 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;
|
||||
@@ -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`);
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user