mirror of
https://github.com/plebbit/seedit.git
synced 2026-05-01 20:45:54 -04:00
feat(reply): add inbox reply design for notifications
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Author, useAccount, useComment, useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
||||
import styles from './comment-tools.module.css';
|
||||
@@ -6,6 +6,7 @@ import HideMenu from './hide-menu';
|
||||
import ModTools from './mod-menu';
|
||||
import ShareMenu from './share-menu';
|
||||
import { FailedLabel, PendingLabel, SpoilerLabel } from '../label';
|
||||
import { isInboxView } from '../../../lib/utils/view-utils';
|
||||
|
||||
interface CommentToolsProps {
|
||||
author?: Author;
|
||||
@@ -115,9 +116,10 @@ const CommentTools = ({
|
||||
const authorRole = useSubplebbit({ subplebbitAddress })?.roles?.[account?.author?.address]?.role;
|
||||
const isMod = authorRole === 'admin' || authorRole === 'owner' || authorRole === 'moderator';
|
||||
hasLabel = spoiler || (cid === undefined && !isReply);
|
||||
const isInboxPage = isInboxView(useLocation().pathname);
|
||||
|
||||
return (
|
||||
<ul className={`${styles.buttons} ${isReply ? styles.buttonsReply : ''} ${hasLabel ? styles.buttonsLabel : ''}`}>
|
||||
<ul className={`${styles.buttons} ${isReply && !isInboxPage ? styles.buttonsReply : ''} ${hasLabel ? styles.buttonsLabel : ''}`}>
|
||||
{hasLabel && <CommentToolsLabel cid={cid} failed={failed} isReply={isReply} spoiler={spoiler} subplebbitAddress={subplebbitAddress} />}
|
||||
{isReply ? (
|
||||
isSingleReply ? (
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
|
||||
.unreadNotification {
|
||||
background-color: var(--gray-overlay);
|
||||
border: 1px solid var(--gray-border);
|
||||
border: 1px solid var(--gray-overlay-border);
|
||||
margin-left: 30px;
|
||||
margin-right: 15px;
|
||||
padding: 6px;
|
||||
padding: 10px 5px;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 2px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.midcol {
|
||||
@@ -184,4 +184,44 @@
|
||||
|
||||
.parentSubplebbit {
|
||||
color: var(--text-primary)
|
||||
}
|
||||
|
||||
.inboxParentLinkSubject {
|
||||
color: var(--text);
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.inboxParentLink {
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
margin-left: 10px;
|
||||
font-size: 12px;
|
||||
color: var(--link);
|
||||
outline: none;
|
||||
overflow: hidden;
|
||||
unicode-bidi: isolate;
|
||||
}
|
||||
|
||||
.inboxParentLinkWrapper {
|
||||
margin-left: 7px;
|
||||
}
|
||||
|
||||
.inboxParentInfo {
|
||||
color: var(--green);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.inboxParentInfo a {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.inboxParentInfoButton {
|
||||
cursor: pointer;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.addMargin {
|
||||
margin-left: 29px;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { Comment, useAccountComment, useAuthorAddress, useComment, useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
||||
import { flattenCommentsPages } from '@plebbit/plebbit-react-hooks/dist/lib/utils';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styles from './reply.module.css';
|
||||
import useReplies from '../../hooks/use-replies';
|
||||
@@ -18,6 +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';
|
||||
|
||||
interface ReplyAuthorProps {
|
||||
address: string;
|
||||
@@ -103,7 +104,8 @@ const ReplyMedia = ({ commentMediaInfo, content, expanded, hasThumbnail, link, l
|
||||
};
|
||||
|
||||
type ParentLinkProps = {
|
||||
postCid: string;
|
||||
commentCid?: string;
|
||||
postCid?: string;
|
||||
};
|
||||
|
||||
const ParentLink = ({ postCid }: ParentLinkProps) => {
|
||||
@@ -129,6 +131,49 @@ const ParentLink = ({ postCid }: ParentLinkProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const InboxParentLink = ({ commentCid }: ParentLinkProps) => {
|
||||
const inboxComment = useComment({ commentCid });
|
||||
const { postCid, parentCid } = inboxComment || {};
|
||||
const parentComment = useComment({ commentCid: inboxComment?.postCid });
|
||||
const { cid, content, title, subplebbitAddress } = parentComment || {};
|
||||
// const { t } = useTranslation();
|
||||
const postTitle = (title?.length > 300 ? title?.slice(0, 300) + '...' : title) || (content?.length > 300 ? content?.slice(0, 300) + '...' : content);
|
||||
|
||||
const isInboxCommentReply = postCid !== parentCid;
|
||||
const isInboxPostReply = postCid === parentCid;
|
||||
|
||||
return (
|
||||
<div className={styles.inboxParentLinkWrapper}>
|
||||
<span className={styles.inboxParentLinkSubject}>{isInboxCommentReply ? 'comment reply' : isInboxPostReply ? 'post reply' : ''}</span>
|
||||
<Link to={`/p/${subplebbitAddress}/c/${cid}`} className={styles.inboxParentLink}>
|
||||
{postTitle}
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const InboxParentInfo = ({ commentCid }: ParentLinkProps) => {
|
||||
const parent = useComment({ commentCid });
|
||||
const { author, cid, subplebbitAddress, timestamp } = parent || {};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.inboxParentInfo}>
|
||||
from{' '}
|
||||
<Link to={`/u/${author?.address}/c/${cid}`} className={styles.inboxParentAuthor}>
|
||||
u/{author?.shortAddress}{' '}
|
||||
</Link>
|
||||
via{' '}
|
||||
<Link to={`/p/${subplebbitAddress}`} className={styles.inboxParentSubplebbit}>
|
||||
p/{subplebbitAddress}{' '}
|
||||
</Link>
|
||||
sent {getFormattedTimeAgo(timestamp)}
|
||||
</div>
|
||||
<div className={styles.inboxParentInfoButton}>show parent</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
interface ReplyProps {
|
||||
depth?: number;
|
||||
index?: number;
|
||||
@@ -196,72 +241,77 @@ const Reply = ({ depth = 0, isSingle, isNotification = false, reply = {} }: Repl
|
||||
const pendingReply = useAccountComment({ commentIndex: reply?.index });
|
||||
const parentOfPendingReply = useComment({ commentCid: pendingReply?.parentCid });
|
||||
|
||||
const location = useLocation();
|
||||
const isInboxPage = isInboxView(location.pathname);
|
||||
|
||||
return (
|
||||
<div className={styles.reply}>
|
||||
{isSingle && <ParentLink postCid={cid ? postCid : parentOfPendingReply?.postCid} />}
|
||||
<div
|
||||
className={`${!isSingle ? styles.replyWrapper : styles.singleReplyWrapper} ${depth > 1 && styles.nested} ${
|
||||
isNotification && !markedAsRead ? styles.unreadNotification : ''
|
||||
}`}
|
||||
>
|
||||
{isSingle && !isInboxPage && <ParentLink postCid={cid ? postCid : parentOfPendingReply?.postCid} />}
|
||||
{isInboxPage && <InboxParentLink commentCid={cid} />}
|
||||
<div className={`${!isSingle ? styles.replyWrapper : styles.singleReplyWrapper} ${depth > 1 && styles.nested}`}>
|
||||
{!collapsed && (
|
||||
<div className={styles.midcol}>
|
||||
<div className={`${styles.arrow} ${upvoted ? styles.upvoted : styles.arrowUp}`} onClick={() => cid && upvote()} />
|
||||
<div className={`${styles.arrow} ${downvoted ? styles.downvoted : styles.arrowDown}`} onClick={() => cid && downvote()} />
|
||||
</div>
|
||||
)}
|
||||
<div className={`${styles.entry} ${collapsed && styles.collapsedEntry}`}>
|
||||
<p className={styles.tagline}>
|
||||
<span className={styles.expand} onClick={() => setCollapsed(!collapsed)}>
|
||||
[{collapsed ? '+' : '–'}]
|
||||
</span>
|
||||
<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>
|
||||
{collapsed && <span className={styles.children}> ({childrenString})</span>}
|
||||
{stateLabel}
|
||||
{!collapsed && flair && (
|
||||
<>
|
||||
{' '}
|
||||
<Flair flair={flair} />
|
||||
</>
|
||||
<div className={`${isNotification && !markedAsRead ? styles.unreadNotification : ''}`}>
|
||||
<div className={`${styles.entry} ${collapsed && styles.collapsedEntry}`}>
|
||||
{!isInboxPage && (
|
||||
<p className={styles.tagline}>
|
||||
<span className={styles.expand} onClick={() => setCollapsed(!collapsed)}>
|
||||
[{collapsed ? '+' : '–'}]
|
||||
</span>
|
||||
<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>
|
||||
{collapsed && <span className={styles.children}> ({childrenString})</span>}
|
||||
{stateLabel}
|
||||
{!collapsed && flair && (
|
||||
<>
|
||||
{' '}
|
||||
<Flair flair={flair} />
|
||||
</>
|
||||
)}
|
||||
{state === 'pending' && loadingString}
|
||||
</p>
|
||||
)}
|
||||
{state === 'pending' && loadingString}
|
||||
</p>
|
||||
{isInboxPage && <InboxParentInfo commentCid={cid} />}
|
||||
{!collapsed && (
|
||||
<div className={styles.usertext}>
|
||||
{commentMediaInfo && (
|
||||
<ReplyMedia
|
||||
commentMediaInfo={commentMediaInfo}
|
||||
content={content}
|
||||
expanded={expanded}
|
||||
hasThumbnail={hasThumbnail}
|
||||
link={link}
|
||||
linkHeight={linkHeight}
|
||||
linkWidth={linkWidth}
|
||||
toggleExpanded={toggleExpanded}
|
||||
/>
|
||||
)}
|
||||
<div className={styles.md}>{contentString}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{!collapsed && (
|
||||
<div className={styles.usertext}>
|
||||
{commentMediaInfo && (
|
||||
<ReplyMedia
|
||||
commentMediaInfo={commentMediaInfo}
|
||||
content={content}
|
||||
expanded={expanded}
|
||||
hasThumbnail={hasThumbnail}
|
||||
link={link}
|
||||
linkHeight={linkHeight}
|
||||
linkWidth={linkWidth}
|
||||
toggleExpanded={toggleExpanded}
|
||||
/>
|
||||
)}
|
||||
<div className={styles.md}>{contentString}</div>
|
||||
<div className={isInboxPage && markedAsRead ? styles.addMargin : ''}>
|
||||
<CommentTools
|
||||
cid={reply.cid}
|
||||
isReply={true}
|
||||
isSingleReply={isSingle}
|
||||
index={reply?.index}
|
||||
parentCid={reply?.postCid}
|
||||
replyCount={replies.length}
|
||||
spoiler={spoiler}
|
||||
subplebbitAddress={reply.subplebbitAddress}
|
||||
showReplyForm={showReplyForm}
|
||||
/>
|
||||
{isReplying && <ReplyForm cid={cid} isReplyingToReply={true} hideReplyForm={hideReplyForm} />}
|
||||
{!isSingle && replies.map((reply, index) => <Reply key={`${index}${reply.cid}`} reply={reply} depth={(reply.depth || 1) + 1} />)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{!collapsed && (
|
||||
<>
|
||||
<CommentTools
|
||||
cid={reply.cid}
|
||||
isReply={true}
|
||||
isSingleReply={isSingle}
|
||||
index={reply?.index}
|
||||
parentCid={reply?.postCid}
|
||||
replyCount={replies.length}
|
||||
spoiler={spoiler}
|
||||
subplebbitAddress={reply.subplebbitAddress}
|
||||
showReplyForm={showReplyForm}
|
||||
/>
|
||||
{isReplying && <ReplyForm cid={cid} isReplyingToReply={true} hideReplyForm={hideReplyForm} />}
|
||||
{!isSingle && replies.map((reply, index) => <Reply key={`${index}${reply.cid}`} reply={reply} depth={(reply.depth || 1) + 1} />)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
--red: red;
|
||||
--gray-contrast: #888;
|
||||
--gray-overlay: #F7F7F7;
|
||||
--gray-overlay-border: #E9E9E9;
|
||||
--gray-border: #ddd;
|
||||
--gray-light: #CCCCCA;
|
||||
--box-shadow-modal: 4px 4px 4px #ccc;
|
||||
@@ -52,6 +53,7 @@
|
||||
--red: rgb(200, 0, 0);
|
||||
--gray-contrast: #c7c7c7;
|
||||
--gray-overlay: #1f1f1f;
|
||||
--gray-overlay-border: #3e3e3e;
|
||||
--gray-border: #3e3e3e;
|
||||
--gray-light: #3e3e3e9d;
|
||||
--box-shadow-modal: 4px 4px 4px #000;
|
||||
|
||||
Reference in New Issue
Block a user