feat(inbox): add filters for "comment replies", "post replies", unread

This commit is contained in:
plebeius.eth
2023-12-22 16:05:51 +01:00
parent 6ae45c6003
commit ffcfe37ce6
6 changed files with 131 additions and 24 deletions

View File

@@ -93,6 +93,9 @@ function App() {
<Route path='/u/:authorAddress/c/:commentCid/about' element={<About />} />
<Route path='/inbox' element={<Inbox />} />
<Route path='inbox/unread' element={<Inbox />} />
<Route path='inbox/commentreplies' element={<Inbox />} />
<Route path='inbox/postreplies' element={<Inbox />} />
</Route>
</Route>
</Routes>

View File

@@ -208,35 +208,38 @@ const HeaderTitle = ({ title, shortAddress }: { title: string; shortAddress: str
const { t } = useTranslation();
const params = useParams();
const location = useLocation();
const isAuthor = isAuthorView(location.pathname);
const isPost = isPostView(location.pathname, params);
const isProfile = isProfileView(location.pathname);
const isSubplebbit = isSubplebbitView(location.pathname, params);
const isSubmit = isSubmitView(location.pathname);
const isSubplebbitSubmit = isSubplebbitSubmitView(location.pathname, params);
const isSettings = isSettingsView(location.pathname);
const isAuthorPage = isAuthorView(location.pathname);
const isInboxPage = isInboxView(location.pathname);
const isPostPage = isPostView(location.pathname, params);
const isProfilePage = isProfileView(location.pathname);
const isSubplebbitPage = isSubplebbitView(location.pathname, params);
const isSubmitPage = isSubmitView(location.pathname);
const isSubplebbitSubmitPage = isSubplebbitSubmitView(location.pathname, params);
const isSettingsPage = isSettingsView(location.pathname);
const subplebbitTitle = <Link to={`/p/${params.subplebbitAddress}`}>{title || shortAddress}</Link>;
const submitTitle = <span className={styles.submitTitle}>{t('submit')}</span>;
const profileTitle = <Link to='/profile'>{account?.author?.shortAddress}</Link>;
const authorTitle = <Link to={`/u/${params.authorAddress}/c/${params.commentCid}`}>{params.authorAddress && getShortAddress(params.authorAddress)}</Link>;
if (isSubplebbitSubmit) {
if (isSubplebbitSubmitPage) {
return (
<>
{subplebbitTitle}: {submitTitle}
</>
);
} else if (isPost || isSubplebbit) {
} else if (isPostPage || isSubplebbitPage) {
return subplebbitTitle;
} else if (isSubmit) {
} else if (isSubmitPage) {
return submitTitle;
} else if (isSettings) {
} else if (isSettingsPage) {
return t('preferences');
} else if (isProfile) {
} else if (isProfilePage) {
return profileTitle;
} else if (isAuthor) {
} else if (isAuthorPage) {
return authorTitle;
} else if (isInboxPage) {
return 'inbox';
}
return null;
};

View File

@@ -58,6 +58,18 @@ export const isInboxView = (pathname: string): boolean => {
return pathname.startsWith('/inbox');
};
export const isInboxCommentRepliesView = (pathname: string): boolean => {
return pathname === `/inbox/commentreplies`;
};
export const isInboxPostRepliesView = (pathname: string): boolean => {
return pathname === `/inbox/postreplies`;
};
export const isInboxUnreadView = (pathname: string): boolean => {
return pathname === `/inbox/unread`;
};
export const isPendingView = (pathname: string, params: ParamsType): boolean => {
return pathname === `/profile/${params.accountCommentIndex}`;
};

View File

@@ -4,8 +4,44 @@
.markAsReadButton {
margin-bottom: 5px;
margin-left: 5px;
}
.notification {
padding: 0 10px;
}
.markAllAsReadButton {
margin-top: 5px;
}
.inboxTabs {
border-bottom: 1px dotted gray;
padding: 5px 10px;
margin-left: 5px;
overflow: hidden;
font-size: larger;
display: inline-block;
margin-right: 15px;
}
.inboxTabs a span {
display: inline;
white-space: nowrap;
}
.inboxTabs a {
text-decoration: none;
color: var(--text-primary);
}
.separator {
color: var(--gray);
margin: 0px .7ex 0px .7ex;
cursor: default;
}
.selected {
color: var(--green) !important;
font-weight: bold;
}

View File

@@ -1,18 +1,71 @@
import { useEffect, useRef } from 'react';
import { useEffect, useMemo, useRef } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { StateSnapshot, Virtuoso, VirtuosoHandle } from 'react-virtuoso';
import { useAccount, useNotifications } from '@plebbit/plebbit-react-hooks';
import styles from './inbox.module.css';
import Reply from '../../components/reply/reply';
import { isInboxCommentRepliesView, isInboxPostRepliesView, isInboxUnreadView } from '../../lib/utils/view-utils';
const lastVirtuosoStates: { [key: string]: StateSnapshot } = {};
const InboxTabs = () => {
const location = useLocation();
const isInboxCommentRepliesPage = isInboxCommentRepliesView(location.pathname);
const isInboxPostRepliesPage = isInboxPostRepliesView(location.pathname);
const isInboxUnreadPage = isInboxUnreadView(location.pathname);
const isAllPage = !isInboxCommentRepliesPage && !isInboxPostRepliesPage && !isInboxUnreadPage;
return (
<div className={styles.inboxTabs}>
<Link to='/inbox' className={isAllPage ? styles.selected : styles.choice}>
all
</Link>
<span className={styles.separator}>|</span>
<Link to='/inbox/unread' className={isInboxUnreadPage ? styles.selected : styles.choice}>
unread
</Link>
<span className={styles.separator}>|</span>
<Link to='/inbox/commentreplies' className={isInboxCommentRepliesPage ? styles.selected : styles.choice}>
comment replies
</Link>
<span className={styles.separator}>|</span>
<Link to='/inbox/postreplies' className={isInboxPostRepliesPage ? styles.selected : styles.choice}>
post replies
</Link>
</div>
);
};
const Inbox = () => {
const account = useAccount();
const { unreadNotificationCount } = account || {};
const { notifications, markAsRead } = useNotifications();
const virtuosoRef = useRef<VirtuosoHandle | null>(null);
const location = useLocation();
const isInboxCommentRepliesPage = isInboxCommentRepliesView(location.pathname);
const isInboxPostRepliesPage = isInboxPostRepliesView(location.pathname);
const isInboxUnreadPage = isInboxUnreadView(location.pathname);
// get comments for unread/commentReplies/postReplies pages
const repliesToUserReplies = useMemo(() => notifications?.filter((comment) => comment.parentCid !== comment.postCid) || [], [notifications]);
const repliesToUserPosts = useMemo(() => notifications?.filter((comment) => comment.parentCid === comment.postCid) || [], [notifications]);
const unreadNotifications = useMemo(() => notifications?.filter((comment) => !comment.markedAsRead) || [], [notifications]);
const comments = useMemo(() => {
if (isInboxCommentRepliesPage) {
return repliesToUserReplies;
} else if (isInboxPostRepliesPage) {
return repliesToUserPosts;
} else if (isInboxUnreadPage) {
return unreadNotifications;
} else {
return notifications;
}
}, [notifications, repliesToUserReplies, repliesToUserPosts, unreadNotifications, isInboxCommentRepliesPage, isInboxPostRepliesPage, isInboxUnreadPage]);
// save last virtuoso state on each scroll
const virtuosoRef = useRef<VirtuosoHandle | null>(null);
const lastVirtuosoState = lastVirtuosoStates?.[unreadNotificationCount];
useEffect(() => {
const setLastVirtuosoState = () =>
virtuosoRef.current?.getState((snapshot) => {
@@ -26,21 +79,24 @@ const Inbox = () => {
return () => window.removeEventListener('scroll', setLastVirtuosoState);
}, [unreadNotificationCount]);
const lastVirtuosoState = lastVirtuosoStates?.[unreadNotificationCount];
if (account && !notifications.length) {
return 'empty';
}
console.log(comments);
return (
<div className={styles.content}>
<button onClick={markAsRead} disabled={!unreadNotificationCount} className={styles.markAsReadButton}>
mark all as read
</button>
<InboxTabs />
<div className={styles.markAllAsReadButton}>
<button onClick={markAsRead} disabled={!unreadNotificationCount} className={styles.markAsReadButton}>
mark all as read
</button>
</div>
<Virtuoso
increaseViewportBy={{ bottom: 1200, top: 600 }}
totalCount={notifications?.length || 0}
data={notifications}
data={comments}
itemContent={(index, notification) => (
<div className={styles.notification}>
<Reply index={index} isSingle={true} reply={notification} isNotification={true} />

View File

@@ -86,13 +86,11 @@ const Profile = () => {
const { comments: upvotedComments } = useComments({ commentCids: upvotedCommentCids });
const { comments: downvotedComments } = useComments({ commentCids: downvotedCommentCids });
// sort dropdown
const [sortType, setSortType] = useState('new');
const handleSortChange = (newSortType: string) => {
setSortType(newSortType);
};
// Define comments with useMemo to avoid redefinition on every render
const comments = useMemo(() => {
if (isUpvotedPage) {
return upvotedComments;
@@ -107,7 +105,6 @@ const Profile = () => {
}
}, [isUpvotedPage, isDownvotedPage, isCommentsPage, isSubmittedPage, upvotedComments, downvotedComments, replyComments, postComments, accountComments]);
// sort comments by sortType
const virtuosoData = useMemo(() => {
let sortedData = [...comments];
if (sortType === 'new') {