Merge pull request #367 from plebbit/development

Development
This commit is contained in:
Tom (plebeius.eth)
2024-09-09 18:14:21 +02:00
committed by GitHub
13 changed files with 140 additions and 112 deletions

View File

@@ -1,6 +1,6 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"short_name": "Seedit",
"name": "Seedit",
"icons": [
{
"src": "favicon.ico",

View File

@@ -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';

View File

@@ -136,6 +136,7 @@ const AuthorHeaderTabs = () => {
!isInProfileCommentsView &&
!isInProfileSubmittedView &&
!isInAuthorCommentsView &&
!isInProfileHiddenView &&
!isInAuthorSubmittedView
? styles.selected
: styles.choice;

View File

@@ -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;
}

View File

@@ -38,11 +38,13 @@ const NewerPostsButton = ({ reset, subplebbitAddressesWithNewerPosts }: NewerPos
return (
<div className={`${styles.newerPostsButton} ${!hide && styles.show} ${hide && styles.hide}`} style={{ top: `${buttonPosition}px` }}>
<span className={styles.resetButton} onClick={handleNewerPostsButtonClick}>
Newer Posts
</span>
<button className={styles.resetButton} onClick={handleNewerPostsButtonClick}>
new posts
</button>
<span className={styles.hideButtonWrapper}>
<span className={styles.hideButton} onClick={() => setHideButton(true)} />
<button className={styles.hideButton} onClick={() => setHideButton(true)}>
x
</button>
</span>
</div>
);

View File

@@ -122,9 +122,7 @@
display: inline-block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
vertical-align: middle;
max-width: 19em;
text-decoration: none;
}

View File

@@ -208,7 +208,7 @@ const Post = ({ index, post = {} }: PostProps) => {
<span className={styles.domain}>
(
<a href={link} target='_blank' rel='noopener noreferrer'>
{linkUrl}
{linkUrl.length > 25 ? linkUrl.slice(0, 25) + '...' : linkUrl}
</a>
)
</span>

View File

@@ -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;
}

View File

@@ -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<VirtuosoHandle | null>(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 (
<div className={styles.content}>
<div className={isMobile ? styles.sidebarMobile : styles.sidebarDesktop}>
<AuthorSidebar />
</div>
<SortDropdown onSortChange={handleSortChange} />
{account && !accountComments.length ? (
t('no_posts')
{account && comments.length === 0 ? (
<div>{t('no_posts')}</div>
) : (
<Virtuoso
increaseViewportBy={{ bottom: 1200, top: 600 }}
totalCount={accountComments?.length || 0}
data={virtuosoData}
itemContent={(index, post) => {
const isReply = post?.parentCid;
return !isReply ? <Post index={index} post={post} /> : <Reply index={index} isSingleReply={true} reply={post} />;
}}
data={comments}
totalCount={comments.length}
itemContent={(index, post) =>
post?.parentCid ? <Reply key={post?.cid} index={index} isSingleReply={true} reply={post} /> : <Post key={post?.cid} index={index} post={post} />
}
endReached={hasMore ? loadMore : undefined}
useWindowScroll={true}
ref={virtuosoRef}
restoreStateFrom={lastVirtuosoState}

View File

@@ -102,6 +102,7 @@ const AccountSettings = () => {
await importAccount(fileContent);
setSwitchToLastAccount(true);
alert(`Imported ${newAccount.account?.name}`);
window.location.reload();
};
reader.readAsText(file);
} catch (error) {

View File

@@ -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 }) => {
</button>
)}
{roles &&
Object.entries(roles).map(([address, role], index) => (
Object.entries(roles)?.map(([address, role], index) => (
<div className={`${styles.moderator} ${index === 0 && styles.firstModerator}`} key={index}>
{t('moderator')} #{index + 1}
{!isReadOnly && <span className={styles.deleteButton} title='delete moderator' onClick={() => (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
) : (
<div className={styles.challengeDescription}>{challengeSettings?.description}</div>
)}
{challengeSettings?.optionInputs.map((setting: OptionInput) => (
{challengeSettings?.optionInputs?.map((setting: OptionInput) => (
<div key={setting?.option} className={styles.challengeOption}>
<div className={styles.challengeOptionLabel}>{setting?.label}</div>
<div className={styles.challengeOptionDescription}>
@@ -594,7 +594,7 @@ const ChallengeSettings = ({ challenge, index, isReadOnly, setSubplebbitSettings
<div className={styles.challengeOption}>
User's role
<div className={styles.challengeOptionDescription}>Is any of the following:</div>
{rolesToExclude.map((role) =>
{rolesToExclude?.map((role) =>
isReadOnly && !exclude?.role?.includes(role) ? null : (
<div key={role}>
{isReadOnly ? (
@@ -619,7 +619,7 @@ const ChallengeSettings = ({ challenge, index, isReadOnly, setSubplebbitSettings
<div className={styles.challengeOption}>
User's action
<div className={styles.challengeOptionDescription}>Is all of the following:</div>
{actionsToExclude.map((action) =>
{actionsToExclude?.map((action) =>
isReadOnly && !exclude?.[action] ? null : (
<div key={action}>
{isReadOnly ? (
@@ -638,7 +638,7 @@ const ChallengeSettings = ({ challenge, index, isReadOnly, setSubplebbitSettings
</div>
),
)}
{nonActionsToExclude.map((nonAction) =>
{nonActionsToExclude?.map((nonAction) =>
isReadOnly && exclude?.[nonAction.replace('not ', '')] !== null ? null : (
<div key={nonAction}>
{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<boolean[]>(challenges.map(() => false));
const [showSettings, setShowSettings] = useState<boolean[]>(challenges?.map(() => false));
const challengeOptions = useChallengesOptions();
const location = useLocation();
@@ -764,7 +764,7 @@ const Challenges = ({ isReadOnly, readOnlyChallenges }: { isReadOnly: boolean; r
</button>
)}
{challenges.length === 0 && !isInCreateSubplebbitView && <span className={styles.noChallengeWarning}>{t('warning_spam')}</span>}
{challenges.map((challenge: any, index: number) => (
{challenges?.map((challenge: any, index: number) => (
<div key={index} className={styles.challenge}>
Challenge #{index + 1}
{!isReadOnly && <span className={styles.deleteButton} title='delete challenge' onClick={() => (isReadOnly ? {} : handleDeleteChallenge(index))} />}
@@ -773,7 +773,7 @@ const Challenges = ({ isReadOnly, readOnlyChallenges }: { isReadOnly: boolean; r
<span className={styles.readOnlyChallenge}>{challenge?.name}</span>
) : (
<select value={challenge?.name} onChange={(e) => handleChallengeTypeChange(index, e.target.value)} disabled={isReadOnly}>
{challengesNames.map((challenge) => (
{challengesNames?.map((challenge) => (
<option key={challenge} value={challenge}>
{challenge}
</option>