diff --git a/public/manifest.json b/public/manifest.json index 1f2f141f..5992131b 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,6 +1,6 @@ { - "short_name": "React App", - "name": "Create React App Sample", + "short_name": "Seedit", + "name": "Seedit", "icons": [ { "src": "favicon.ico", diff --git a/src/app.tsx b/src/app.tsx index 01b0530b..f5c50150 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -15,7 +15,7 @@ import Profile from './views/profile'; import Settings from './views/settings'; import SubmitPage from './views/submit-page'; import Subplebbit from './views/subplebbit'; -import SubplebbitSettings from './views/subplebbit/subplebbit-settings'; +import SubplebbitSettings from './views/subplebbit-settings'; import Subplebbits from './views/subplebbits'; import AccountBar from './components/account-bar/'; import ChallengeModal from './components/challenge-modal'; diff --git a/src/components/header/header.tsx b/src/components/header/header.tsx index e7ed9227..054dd64e 100644 --- a/src/components/header/header.tsx +++ b/src/components/header/header.tsx @@ -136,6 +136,7 @@ const AuthorHeaderTabs = () => { !isInProfileCommentsView && !isInProfileSubmittedView && !isInAuthorCommentsView && + !isInProfileHiddenView && !isInAuthorSubmittedView ? styles.selected : styles.choice; diff --git a/src/components/newer-posts-button/newer-posts-button.module.css b/src/components/newer-posts-button/newer-posts-button.module.css index 62e7581a..3d33852b 100644 --- a/src/components/newer-posts-button/newer-posts-button.module.css +++ b/src/components/newer-posts-button/newer-posts-button.module.css @@ -1,43 +1,21 @@ .newerPostsButton { position: fixed; left: 10%; - border-radius: 25px; - background-color: green; - color: white; z-index: 5; transition: top 0.3s ease-in-out; - font-size: 13px; } .resetButton { - display: inline-block; + display: inline-block; cursor: pointer; - font-weight: 700; - padding: 5px 10px; } -.resetButton:hover { - opacity: 0.8; -} .hide { display: none; } .hideButton { - display: inline-block; cursor: pointer; - background-image: url("/public/assets/buttons/close-x-button.png"); - background-size: cover; - height: 12px; - width: 12px; - transform: translateY(1px); -} - -.hideButtonWrapper { - padding-right: 10px; -} - -.hideButton:hover { - opacity: 0.8; + padding: 0px 3px; } \ No newline at end of file diff --git a/src/components/newer-posts-button/newer-posts-button.tsx b/src/components/newer-posts-button/newer-posts-button.tsx index 922ee958..e6001a42 100644 --- a/src/components/newer-posts-button/newer-posts-button.tsx +++ b/src/components/newer-posts-button/newer-posts-button.tsx @@ -38,11 +38,13 @@ const NewerPostsButton = ({ reset, subplebbitAddressesWithNewerPosts }: NewerPos return (
- - Newer Posts - + - setHideButton(true)} /> +
); diff --git a/src/components/post/post.module.css b/src/components/post/post.module.css index e3cbd42d..6d15ab6e 100644 --- a/src/components/post/post.module.css +++ b/src/components/post/post.module.css @@ -122,9 +122,7 @@ display: inline-block; overflow: hidden; white-space: nowrap; - text-overflow: ellipsis; vertical-align: middle; - max-width: 19em; text-decoration: none; } diff --git a/src/components/post/post.tsx b/src/components/post/post.tsx index 97e43645..bbe59732 100644 --- a/src/components/post/post.tsx +++ b/src/components/post/post.tsx @@ -208,7 +208,7 @@ const Post = ({ index, post = {} }: PostProps) => { ( - {linkUrl} + {linkUrl.length > 25 ? linkUrl.slice(0, 25) + '...' : linkUrl} ) diff --git a/src/components/sidebar/sidebar.module.css b/src/components/sidebar/sidebar.module.css index 925f9efe..04124a8a 100644 --- a/src/components/sidebar/sidebar.module.css +++ b/src/components/sidebar/sidebar.module.css @@ -58,13 +58,13 @@ a { .descriptionTitle { padding-top: 5px; - color: var(--text-markdown); + color: var(--text); } .description { padding: 5px 0; word-wrap: break-word; - color: var(--text-markdown); + color: var(--text); line-height: 15px; } @@ -183,7 +183,7 @@ a { .rules { padding-bottom: 5px; padding-top: 7px; - color: var(--text-markdown); + color: var(--text); line-height: 15px; word-wrap: break-word; } diff --git a/src/views/profile/profile.tsx b/src/views/profile/profile.tsx index fd926274..246e5c92 100644 --- a/src/views/profile/profile.tsx +++ b/src/views/profile/profile.tsx @@ -67,27 +67,74 @@ const SortDropdown = ({ onSortChange }: SortDropdownProps) => { ); }; +const pageSize = 10; + const Profile = () => { const { t } = useTranslation(); const account = useAccount(); const location = useLocation(); const params = useParams(); - let { accountComments } = useAccountComments(); - accountComments = [...accountComments].reverse(); - const { accountVotes } = useAccountVotes(); + const isMobile = useWindowWidth() < 640; + + const [activeTab, setActiveTab] = useState('overview'); + const [currentPage, setCurrentPage] = useState(1); + const isInProfileUpvotedView = isProfileUpvotedView(location.pathname); const isInProfileDownvotedView = isProfileDownvotedView(location.pathname); const isInProfileHiddenView = isProfileHiddenView(location.pathname); const isInCommentsView = isProfileCommentsView(location.pathname); const isInSubmittedView = isProfileSubmittedView(location.pathname); - const isMobile = useWindowWidth() < 640; - // get comments for upvoted/downvoted/comments/submitted pages + useEffect(() => { + if (isInProfileUpvotedView) setActiveTab('upvoted'); + else if (isInProfileDownvotedView) setActiveTab('downvoted'); + else if (isInProfileHiddenView) setActiveTab('hidden'); + else if (isInCommentsView) setActiveTab('comments'); + else if (isInSubmittedView) setActiveTab('submitted'); + else setActiveTab('overview'); + + setCurrentPage(1); // Reset page when changing tabs + }, [isInProfileUpvotedView, isInProfileDownvotedView, isInProfileHiddenView, isInCommentsView, isInSubmittedView]); + + const { accountComments } = useAccountComments(); + const { accountVotes } = useAccountVotes(); + const postComments = useMemo(() => accountComments?.filter((comment) => !comment.parentCid) || [], [accountComments]); const replyComments = useMemo(() => accountComments?.filter((comment) => comment.parentCid) || [], [accountComments]); - 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]); - const hiddenCommentCids = useMemo(() => Object.keys(account?.blockedCids ?? {}), [account?.blockedCids]); + + const upvotedCommentCids = useMemo(() => { + const allUpvotedCids = accountVotes?.filter((vote) => vote.vote === 1).map((vote) => vote.commentCid) || []; + return allUpvotedCids.slice(0, currentPage * pageSize); + }, [accountVotes, currentPage]); + + const downvotedCommentCids = useMemo(() => { + const allDownvotedCids = accountVotes?.filter((vote) => vote.vote === -1).map((vote) => vote.commentCid) || []; + return allDownvotedCids.slice(0, currentPage * pageSize); + }, [accountVotes, currentPage]); + + const hiddenCommentCids = useMemo(() => { + const allHiddenCids = Object.keys(account?.blockedCids ?? {}); + return allHiddenCids.slice(0, currentPage * pageSize); + }, [account?.blockedCids, currentPage]); + + const { hasMoreUpvoted, hasMoreDownvoted, hasMoreHidden } = useMemo(() => { + const allUpvotedCids = accountVotes?.filter((vote) => vote.vote === 1).map((vote) => vote.commentCid) || []; + const allDownvotedCids = accountVotes?.filter((vote) => vote.vote === -1).map((vote) => vote.commentCid) || []; + const allHiddenCids = Object.keys(account?.blockedCids ?? {}); + + return { + hasMoreUpvoted: currentPage * pageSize < allUpvotedCids.length, + hasMoreDownvoted: currentPage * pageSize < allDownvotedCids.length, + hasMoreHidden: currentPage * pageSize < allHiddenCids.length, + }; + }, [accountVotes, account?.blockedCids, currentPage]); + + const hasMore = useMemo(() => { + if (isInProfileUpvotedView) return hasMoreUpvoted; + if (isInProfileDownvotedView) return hasMoreDownvoted; + if (isInProfileHiddenView) return hasMoreHidden; + return false; + }, [hasMoreUpvoted, hasMoreDownvoted, hasMoreHidden, isInProfileUpvotedView, isInProfileDownvotedView, isInProfileHiddenView]); const { comments: upvotedComments } = useComments({ commentCids: upvotedCommentCids }); const { comments: downvotedComments } = useComments({ commentCids: downvotedCommentCids }); @@ -96,45 +143,51 @@ const Profile = () => { const [sortType, setSortType] = useState('new'); const handleSortChange = (newSortType: string) => { setSortType(newSortType); + setCurrentPage(1); }; const comments = useMemo(() => { - if (isInProfileUpvotedView) { - return upvotedComments; - } else if (isInProfileDownvotedView) { - return downvotedComments; - } else if (isInCommentsView) { - return replyComments; - } else if (isInSubmittedView) { - return postComments; - } else if (isInProfileHiddenView) { - return hiddenComments; - } else { - return accountComments; + let selectedComments; + switch (activeTab) { + case 'upvoted': + selectedComments = upvotedComments; + break; + case 'downvoted': + selectedComments = downvotedComments; + break; + case 'hidden': + selectedComments = hiddenComments; + break; + case 'comments': + selectedComments = replyComments; + break; + case 'submitted': + selectedComments = postComments; + break; + case 'overview': + default: + selectedComments = [...postComments, ...replyComments]; } - }, [ - isInProfileUpvotedView, - isInProfileDownvotedView, - isInProfileHiddenView, - isInCommentsView, - isInSubmittedView, - upvotedComments, - downvotedComments, - replyComments, - postComments, - hiddenComments, - accountComments, - ]); - const virtuosoData = useMemo(() => { - let sortedData = [...comments]; - if (sortType === 'new') { - sortedData.sort((a, b) => b!.timestamp - a!.timestamp); - } else { - sortedData.sort((a, b) => a!.timestamp - b!.timestamp); - } - return sortedData; - }, [sortType, comments]); + // Sort comments + selectedComments.sort((a, b) => (sortType === 'new' ? b!.timestamp - a!.timestamp : a!.timestamp - b!.timestamp)); + + return selectedComments; + }, [activeTab, upvotedComments, downvotedComments, hiddenComments, replyComments, postComments, sortType]); + + const loadMore = useCallback(() => { + console.log('LoadMore called, current page:', currentPage); + setCurrentPage((prevPage) => { + const newPage = prevPage + 1; + console.log('Setting new page:', newPage); + return newPage; + }); + }, [currentPage]); + + const profileTitle = account?.author?.displayName ? `${account?.author?.displayName} (u/${account?.author?.shortAddress})` : `u/${account?.author?.shortAddress}`; + useEffect(() => { + document.title = profileTitle + ' - Seedit'; + }, [t, profileTitle]); // save last virtuoso state on each scroll const virtuosoRef = useRef(null); @@ -150,28 +203,23 @@ const Profile = () => { return () => window.removeEventListener('scroll', setLastVirtuosoState); }, [account?.shortAddress, params.sortType]); - const profileTitle = account?.author?.displayName ? `${account?.author?.displayName} (u/${account?.author?.shortAddress})` : `u/${account?.author?.shortAddress}`; - useEffect(() => { - document.title = profileTitle + ' - Seedit'; - }, [t, profileTitle]); - return (
- {account && !accountComments.length ? ( - t('no_posts') + {account && comments.length === 0 ? ( +
{t('no_posts')}
) : ( { - const isReply = post?.parentCid; - return !isReply ? : ; - }} + data={comments} + totalCount={comments.length} + itemContent={(index, post) => + post?.parentCid ? : + } + endReached={hasMore ? loadMore : undefined} useWindowScroll={true} ref={virtuosoRef} restoreStateFrom={lastVirtuosoState} diff --git a/src/views/settings/account-settings/account-settings.tsx b/src/views/settings/account-settings/account-settings.tsx index e29eadfc..45e85850 100644 --- a/src/views/settings/account-settings/account-settings.tsx +++ b/src/views/settings/account-settings/account-settings.tsx @@ -102,6 +102,7 @@ const AccountSettings = () => { await importAccount(fileContent); setSwitchToLastAccount(true); alert(`Imported ${newAccount.account?.name}`); + window.location.reload(); }; reader.readAsText(file); } catch (error) { diff --git a/src/views/subplebbit/subplebbit-settings/index.ts b/src/views/subplebbit-settings/index.ts similarity index 100% rename from src/views/subplebbit/subplebbit-settings/index.ts rename to src/views/subplebbit-settings/index.ts diff --git a/src/views/subplebbit/subplebbit-settings/subplebbit-settings.module.css b/src/views/subplebbit-settings/subplebbit-settings.module.css similarity index 100% rename from src/views/subplebbit/subplebbit-settings/subplebbit-settings.module.css rename to src/views/subplebbit-settings/subplebbit-settings.module.css diff --git a/src/views/subplebbit/subplebbit-settings/subplebbit-settings.tsx b/src/views/subplebbit-settings/subplebbit-settings.tsx similarity index 97% rename from src/views/subplebbit/subplebbit-settings/subplebbit-settings.tsx rename to src/views/subplebbit-settings/subplebbit-settings.tsx index a258f2ae..cd1a5010 100644 --- a/src/views/subplebbit/subplebbit-settings/subplebbit-settings.tsx +++ b/src/views/subplebbit-settings/subplebbit-settings.tsx @@ -9,17 +9,17 @@ import { useSubplebbit, usePublishSubplebbitEdit, } from '@plebbit/plebbit-react-hooks'; -import { Roles } from '../../../lib/utils/user-utils'; +import { Roles } from '../../lib/utils/user-utils'; import { useTranslation } from 'react-i18next'; import { create } from 'zustand'; import styles from './subplebbit-settings.module.css'; -import { isValidURL } from '../../../lib/utils/url-utils'; -import { isCreateSubplebbitView, isSubplebbitSettingsView } from '../../../lib/utils/view-utils'; -import useChallengesOptions from '../../../hooks/use-challenges-options'; -import useChallengeSettings from '../../../hooks/use-challenge-settings'; -import LoadingEllipsis from '../../../components/loading-ellipsis'; -import Markdown from '../../../components/markdown'; -import Sidebar from '../../../components/sidebar'; +import { isValidURL } from '../../lib/utils/url-utils'; +import { isCreateSubplebbitView, isSubplebbitSettingsView } from '../../lib/utils/view-utils'; +import useChallengesOptions from '../../hooks/use-challenges-options'; +import useChallengeSettings from '../../hooks/use-challenge-settings'; +import LoadingEllipsis from '../../components/loading-ellipsis'; +import Markdown from '../../components/markdown'; +import Sidebar from '../../components/sidebar'; import _ from 'lodash'; type SubplebbitSettingsState = { @@ -315,7 +315,7 @@ const Moderators = ({ isReadOnly = false }: { isReadOnly?: boolean }) => { )} {roles && - Object.entries(roles).map(([address, role], index) => ( + Object.entries(roles)?.map(([address, role], index) => (
{t('moderator')} #{index + 1} {!isReadOnly && (isReadOnly ? {} : handleDeleteModerator(address))} />} @@ -399,7 +399,7 @@ const ChallengeSettings = ({ challenge, index, isReadOnly, setSubplebbitSettings const handleOptionChange = (optionName: string, newValue: string) => { const updatedOptions = { ...options, [optionName]: newValue }; - const updatedChallenges = settings.challenges.map((ch: any, idx: number) => (idx === index ? { ...ch, options: updatedOptions } : ch)); + const updatedChallenges = settings.challenges?.map((ch: any, idx: number) => (idx === index ? { ...ch, options: updatedOptions } : ch)); setSubplebbitSettingsStore({ settings: { ...settings, challenges: updatedChallenges } }); }; @@ -431,9 +431,9 @@ const ChallengeSettings = ({ challenge, index, isReadOnly, setSubplebbitSettings }; const handleExcludeChange = (excludeIndex: number, type: keyof Exclude | 'not post' | 'not reply' | 'not vote', value: any) => { - const updatedChallenges = settings.challenges.map((ch: any, idx: number) => { + const updatedChallenges = settings.challenges?.map((ch: any, idx: number) => { if (idx === index) { - const updatedExclude = ch.exclude.map((ex: any, exIdx: number) => { + const updatedExclude = ch.exclude?.map((ex: any, exIdx: number) => { if (exIdx === excludeIndex) { let newEx = { ...ex }; @@ -483,7 +483,7 @@ const ChallengeSettings = ({ challenge, index, isReadOnly, setSubplebbitSettings const handleExcludeAddress = (excludeIndex: number, value: string) => { const addresses = value .split(',') - .map((addr) => addr.trim()) + ?.map((addr) => addr.trim()) .filter((addr) => addr !== ''); handleExcludeChange(excludeIndex, 'address', addresses); }; @@ -498,7 +498,7 @@ const ChallengeSettings = ({ challenge, index, isReadOnly, setSubplebbitSettings ) : (
{challengeSettings?.description}
)} - {challengeSettings?.optionInputs.map((setting: OptionInput) => ( + {challengeSettings?.optionInputs?.map((setting: OptionInput) => (
{setting?.label}
@@ -594,7 +594,7 @@ const ChallengeSettings = ({ challenge, index, isReadOnly, setSubplebbitSettings
User's role
Is any of the following:
- {rolesToExclude.map((role) => + {rolesToExclude?.map((role) => isReadOnly && !exclude?.role?.includes(role) ? null : (
{isReadOnly ? ( @@ -619,7 +619,7 @@ const ChallengeSettings = ({ challenge, index, isReadOnly, setSubplebbitSettings
User's action
Is all of the following:
- {actionsToExclude.map((action) => + {actionsToExclude?.map((action) => isReadOnly && !exclude?.[action] ? null : (
{isReadOnly ? ( @@ -638,7 +638,7 @@ const ChallengeSettings = ({ challenge, index, isReadOnly, setSubplebbitSettings
), )} - {nonActionsToExclude.map((nonAction) => + {nonActionsToExclude?.map((nonAction) => isReadOnly && exclude?.[nonAction.replace('not ', '')] !== null ? null : (
{isReadOnly ? ( @@ -717,7 +717,7 @@ const Challenges = ({ isReadOnly, readOnlyChallenges }: { isReadOnly: boolean; r const { t } = useTranslation(); const { settings, setSubplebbitSettingsStore } = useSubplebbitSettingsStore(); const challenges = settings?.challenges || readOnlyChallenges || []; - const [showSettings, setShowSettings] = useState(challenges.map(() => false)); + const [showSettings, setShowSettings] = useState(challenges?.map(() => false)); const challengeOptions = useChallengesOptions(); const location = useLocation(); @@ -764,7 +764,7 @@ const Challenges = ({ isReadOnly, readOnlyChallenges }: { isReadOnly: boolean; r )} {challenges.length === 0 && !isInCreateSubplebbitView && {t('warning_spam')}} - {challenges.map((challenge: any, index: number) => ( + {challenges?.map((challenge: any, index: number) => (
Challenge #{index + 1} {!isReadOnly && (isReadOnly ? {} : handleDeleteChallenge(index))} />} @@ -773,7 +773,7 @@ const Challenges = ({ isReadOnly, readOnlyChallenges }: { isReadOnly: boolean; r {challenge?.name} ) : (