From 4ac0d4437e4baa1564f3e0d429042e0306dbb21d Mon Sep 17 00:00:00 2001 From: "plebeius.eth" Date: Thu, 14 Dec 2023 16:45:24 +0100 Subject: [PATCH 1/3] feat(sort dropdown): add sort UI to author and profile --- src/components/sort-dropdown/index.ts | 1 + .../sort-dropdown/sort-dropdown.module.css | 59 +++++++++++++++++++ .../sort-dropdown/sort-dropdown.tsx | 53 +++++++++++++++++ src/views/author/author.tsx | 2 + src/views/profile/profile.tsx | 6 +- 5 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 src/components/sort-dropdown/index.ts create mode 100644 src/components/sort-dropdown/sort-dropdown.module.css create mode 100644 src/components/sort-dropdown/sort-dropdown.tsx diff --git a/src/components/sort-dropdown/index.ts b/src/components/sort-dropdown/index.ts new file mode 100644 index 00000000..f793bb71 --- /dev/null +++ b/src/components/sort-dropdown/index.ts @@ -0,0 +1 @@ +export {default} from './sort-dropdown'; \ No newline at end of file diff --git a/src/components/sort-dropdown/sort-dropdown.module.css b/src/components/sort-dropdown/sort-dropdown.module.css new file mode 100644 index 00000000..9c9b8495 --- /dev/null +++ b/src/components/sort-dropdown/sort-dropdown.module.css @@ -0,0 +1,59 @@ +.content { + border-bottom: 1px dotted gray; + padding: 5px 10px; + margin: 5px; + margin-top: 0; + overflow: hidden; + font-size: larger; +} + +.dropdownTitle { + color: var(--text) !important; +} + +.dropdown { + display: inline; + position: relative; + cursor: pointer; +} + +.selected { + position: relative; + background: none no-repeat scroll center right; + background-image: url("/public/assets/buttons/droparrowgray.gif"); + padding-right: 21px; + text-decoration: underline; + color: gray; + font-weight: bold; +} + +.dropChoices { + margin-top: 2px; + position: absolute; + left: 90px; + border: 1px solid gray; + z-index: 100; + background-color: var(--background); + white-space: nowrap; + line-height: normal; + margin-top: 3px; +} + +.filter { + cursor: pointer; + padding: 2px 3px 1px 3px; + display: block; + color: var(--text-primary); +} + +.filter:hover { + background-color: var(--background-primary); +} + +.dropChoicesHidden { + display: none; +} + +.dropChoicesVisible { + display: block; +} \ No newline at end of file diff --git a/src/components/sort-dropdown/sort-dropdown.tsx b/src/components/sort-dropdown/sort-dropdown.tsx new file mode 100644 index 00000000..1db69c42 --- /dev/null +++ b/src/components/sort-dropdown/sort-dropdown.tsx @@ -0,0 +1,53 @@ +import { useCallback, useEffect, useRef, useState } from 'react'; +import styles from './sort-dropdown.module.css'; + +const SortDropdown = () => { + const sortLabels = ['new', 'old'] + + const [isDropdownOpen, setIsDropdownOpen] = useState(false); + const dropdownRef = useRef(null); + const dropdownChoicesRef = useRef(null); + const dropChoicesClass = isDropdownOpen ? styles.dropChoicesVisible : styles.dropChoicesHidden; + + const handleClickOutside = useCallback((event: MouseEvent) => { + if ( + dropdownRef.current && + !dropdownRef.current.contains(event.target as Node) && + dropdownChoicesRef.current && + !dropdownChoicesRef.current.contains(event.target as Node) + ) { + setIsDropdownOpen(false); + } + }, []); + + useEffect(() => { + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [handleClickOutside]); + + return ( +
+ sorted by: +
{ + setIsDropdownOpen(!isDropdownOpen); + }} + ref={dropdownRef} + > + new +
+
+ {sortLabels.map((filter: string, index: number) => ( +
setIsDropdownOpen(false)}> + {filter} +
+ ))} +
+
+ ); +}; + +export default SortDropdown; diff --git a/src/views/author/author.tsx b/src/views/author/author.tsx index c9792543..f762812e 100644 --- a/src/views/author/author.tsx +++ b/src/views/author/author.tsx @@ -8,6 +8,7 @@ import AuthorSidebar from '../../components/author-sidebar'; import LoadingEllipsis from '../../components/loading-ellipsis'; import Post from '../../components/post'; import Reply from '../../components/reply/'; +import SortDropdown from '../../components/sort-dropdown'; const lastVirtuosoStates: { [key: string]: StateSnapshot } = {}; @@ -74,6 +75,7 @@ const Author = () => {
+ {authorComments?.length === 0 && !hasMore &&
No posts found
} {
+ {account && !accountComments.length ? ( 'no posts' ) : ( From 1e07ce2ff99be8acbcc9c507a0da5e450c8ba9d4 Mon Sep 17 00:00:00 2001 From: "plebeius.eth" Date: Thu, 14 Dec 2023 17:12:29 +0100 Subject: [PATCH 2/3] feat(author sidebar): add to author and profile mobile views on top of posts, remove about tab --- src/components/author-sidebar/author-sidebar.tsx | 4 ++-- src/components/header/header.tsx | 2 +- src/components/sort-dropdown/sort-dropdown.tsx | 2 +- src/views/author/author.module.css | 8 +++++++- src/views/author/author.tsx | 3 ++- src/views/profile/profile.module.css | 8 +++++++- src/views/profile/profile.tsx | 3 ++- 7 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/components/author-sidebar/author-sidebar.tsx b/src/components/author-sidebar/author-sidebar.tsx index 76be5c02..2a9383e9 100644 --- a/src/components/author-sidebar/author-sidebar.tsx +++ b/src/components/author-sidebar/author-sidebar.tsx @@ -109,11 +109,11 @@ const AuthorSidebar = () => { <>
{postScore} post karma - {isAuthorPage && postScore && ' (estimated)'} + {isAuthorPage && postScore ? ' (estimated)' : null}
{replyScore} comment karma - {isAuthorPage && replyScore && ' (estimated)'} + {isAuthorPage && replyScore ? ' (estimated)' : null}
) : null} diff --git a/src/components/header/header.tsx b/src/components/header/header.tsx index d475220b..1d4413c2 100644 --- a/src/components/header/header.tsx +++ b/src/components/header/header.tsx @@ -313,7 +313,7 @@ const Header = () => {
    - {(isSubplebbitPage || isSubplebbitSubmitPage || isPostPage || isProfilePage || isAuthorPage) && } + {(isSubplebbitPage || isSubplebbitSubmitPage || isPostPage) && }
)} diff --git a/src/components/sort-dropdown/sort-dropdown.tsx b/src/components/sort-dropdown/sort-dropdown.tsx index 1db69c42..98308192 100644 --- a/src/components/sort-dropdown/sort-dropdown.tsx +++ b/src/components/sort-dropdown/sort-dropdown.tsx @@ -2,7 +2,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import styles from './sort-dropdown.module.css'; const SortDropdown = () => { - const sortLabels = ['new', 'old'] + const sortLabels = ['new', 'old']; const [isDropdownOpen, setIsDropdownOpen] = useState(false); const dropdownRef = useRef(null); diff --git a/src/views/author/author.module.css b/src/views/author/author.module.css index b443a519..02a010c8 100644 --- a/src/views/author/author.module.css +++ b/src/views/author/author.module.css @@ -7,7 +7,13 @@ div[data-viewport-type="window"] { } @media (max-width: 768px) { - .sidebar { + .sidebarDesktop { display: none; } + + .sidebarMobile { + width: 300px; + display: inline-block; + padding: 10px 0 0px 5px; + } } \ No newline at end of file diff --git a/src/views/author/author.tsx b/src/views/author/author.tsx index f762812e..17402241 100644 --- a/src/views/author/author.tsx +++ b/src/views/author/author.tsx @@ -20,6 +20,7 @@ const Author = () => { const params = useParams(); const isAuthorCommentsPage = isAuthorCommentsView(location.pathname, params); const isAuthorSubmittedPage = isAuthorSubmittedView(location.pathname, params); + const isMobile = window.innerWidth < 768; const { authorComments, lastCommentCid, hasMore, loadMore } = useAuthorComments({ commentCid, authorAddress }); @@ -72,7 +73,7 @@ const Author = () => { return (
-
+
diff --git a/src/views/profile/profile.module.css b/src/views/profile/profile.module.css index b443a519..02a010c8 100644 --- a/src/views/profile/profile.module.css +++ b/src/views/profile/profile.module.css @@ -7,7 +7,13 @@ div[data-viewport-type="window"] { } @media (max-width: 768px) { - .sidebar { + .sidebarDesktop { display: none; } + + .sidebarMobile { + width: 300px; + display: inline-block; + padding: 10px 0 0px 5px; + } } \ No newline at end of file diff --git a/src/views/profile/profile.tsx b/src/views/profile/profile.tsx index 57b1808b..92f4997d 100644 --- a/src/views/profile/profile.tsx +++ b/src/views/profile/profile.tsx @@ -22,6 +22,7 @@ const Profile = () => { const isDownvotedPage = isDownvotedView(location.pathname); const isCommentsPage = isProfileCommentsView(location.pathname); const isSubmittedPage = isProfileSubmittedView(location.pathname); + const isMobile = window.innerWidth < 768; const upvotedCommentCids = useMemo(() => accountVotes?.filter((vote) => vote.vote === 1).map((vote) => vote.commentCid) || [], [accountVotes]); const downvotedCommentCids = useMemo(() => accountVotes?.filter((vote) => vote.vote === -1).map((vote) => vote.commentCid) || [], [accountVotes]); @@ -61,7 +62,7 @@ const Profile = () => { return (
-
+
From 7dfa6240e8bdd658376c52fefdf2c29969a23c8a Mon Sep 17 00:00:00 2001 From: "plebeius.eth" Date: Thu, 14 Dec 2023 20:15:35 +0100 Subject: [PATCH 3/3] fix(home): delay feed rendering until all subscription addresses are fetched, virtuoso seemed to glitch --- src/views/home/home.tsx | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/views/home/home.tsx b/src/views/home/home.tsx index 4ea60489..8c678b52 100644 --- a/src/views/home/home.tsx +++ b/src/views/home/home.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { useParams } from 'react-router-dom'; import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso'; import { useAccount, useFeed } from '@plebbit/plebbit-react-hooks'; @@ -16,20 +16,33 @@ const lastVirtuosoStates: { [key: string]: StateSnapshot } = {}; const NoPosts = () => 'no posts'; const Home = () => { + const { t } = useTranslation(); const account = useAccount(); const defaultSubplebbitAddresses = useDefaultSubplebbitAddresses(); - const subplebbitAddresses = defaultSubplebbitAddresses.concat(account?.subscriptions || []); + const [subplebbitAddresses, setSubplebbitAddresses] = useState(undefined); + + useEffect(() => { + if (defaultSubplebbitAddresses && account?.subscriptions) { + setSubplebbitAddresses(defaultSubplebbitAddresses.concat(account.subscriptions)); + } + }, [defaultSubplebbitAddresses, account?.subscriptions]); + const params = useParams<{ sortType?: string; timeFilterName?: string }>(); const sortType = params?.sortType || 'hot'; const timeFilterName = (params.timeFilterName as TimeFilterKey) || 'all'; const { timeFilter } = useTimeFilter(sortType, timeFilterName); - const { feed, hasMore, loadMore } = useFeed({ subplebbitAddresses, sortType, filter: timeFilter }); - const { t } = useTranslation(); + + const { feed, hasMore, loadMore } = useFeed({ + subplebbitAddresses: subplebbitAddresses || [], + sortType, + filter: timeFilter + }); + let loadingStateString = useFeedStateString(subplebbitAddresses) || t('loading'); const loadingString = (
- {subplebbitAddresses.length === 0 ? ( + {subplebbitAddresses && subplebbitAddresses.length === 0 ? (
{ if (feed?.length === 0) { Footer = NoPosts; } - if (hasMore || subplebbitAddresses.length === 0) { + if (hasMore || subplebbitAddresses && subplebbitAddresses.length === 0) { Footer = () => loadingString; }