mirror of
https://github.com/plebbit/seedit.git
synced 2026-05-19 14:19:24 -04:00
@@ -27,12 +27,12 @@
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-i18next": "13.2.2",
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-markdown": "9.0.1",
|
||||
"react-router-dom": "6.16.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-virtuoso": "4.6.0",
|
||||
"rehype-sanitize": "^6.0.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"rehype-sanitize": "6.0.0",
|
||||
"remark-gfm": "4.0.0",
|
||||
"tcp-port-used": "1.0.2",
|
||||
"typescript": "5.2.2",
|
||||
"zustand": "4.4.3"
|
||||
|
||||
@@ -80,6 +80,9 @@ function App() {
|
||||
</Route>
|
||||
<Route element={pagesLayout}>
|
||||
<Route path='/submit' element={<Submit />} />
|
||||
<Route path='/about' element={<About />} />
|
||||
|
||||
<Route path='/p/all/about' element={<About />} />
|
||||
|
||||
<Route path='/p/:subplebbitAddress/c/:commentCid' element={<Post />} />
|
||||
<Route path='/p/:subplebbitAddress/c/:commentCid/about' element={<About />} />
|
||||
|
||||
@@ -7,10 +7,12 @@ import {
|
||||
getAboutLink,
|
||||
isAboutView,
|
||||
isAllView,
|
||||
isAllAboutView,
|
||||
isAuthorView,
|
||||
isAuthorCommentsView,
|
||||
isAuthorSubmittedView,
|
||||
isProfileDownvotedView,
|
||||
isHomeAboutView,
|
||||
isHomeView,
|
||||
isInboxView,
|
||||
isPendingView,
|
||||
@@ -235,6 +237,7 @@ const HeaderTabs = () => {
|
||||
const location = useLocation();
|
||||
const isInAllView = isAllView(location.pathname);
|
||||
const isInAuthorView = isAuthorView(location.pathname);
|
||||
const isInHomeAboutView = isHomeAboutView(location.pathname);
|
||||
const isInHomeView = isHomeView(location.pathname, params);
|
||||
const isInInboxView = isInboxView(location.pathname);
|
||||
const isInPendingView = isPendingView(location.pathname, params);
|
||||
@@ -247,7 +250,7 @@ const HeaderTabs = () => {
|
||||
|
||||
if (isInPostView) {
|
||||
return <CommentsButton />;
|
||||
} else if (isInHomeView || (isInSubplebbitView && !isInSubplebbitSubmitView && !isInSubplebbitSettingsView) || isInAllView) {
|
||||
} else if (isInHomeView || isInHomeAboutView || (isInSubplebbitView && !isInSubplebbitSubmitView && !isInSubplebbitSettingsView) || isInAllView) {
|
||||
return <SortItems />;
|
||||
} else if ((isInProfileView || isInAuthorView) && !isInPendingView) {
|
||||
return <AuthorHeaderTabs />;
|
||||
@@ -321,6 +324,7 @@ const Header = () => {
|
||||
|
||||
const isMobile = window.innerWidth < 768;
|
||||
const isInAboutView = isAboutView(location.pathname);
|
||||
const isInAllAboutView = isAllAboutView(location.pathname);
|
||||
const isInAllView = isAllView(location.pathname);
|
||||
const isInAuthorView = isAuthorView(location.pathname);
|
||||
const isInHomeView = isHomeView(location.pathname, params);
|
||||
@@ -343,7 +347,7 @@ const Header = () => {
|
||||
isInHomeView ||
|
||||
(isInSubplebbitView && !isInSubplebbitSubmitView && !isInSubplebbitSettingsView && !isInPostView && !isInAboutView) ||
|
||||
(isInProfileView && !isInAboutView) ||
|
||||
isInAllView ||
|
||||
(isInAllView && !isInAllAboutView) ||
|
||||
(isInAuthorView && !isInAboutView);
|
||||
const logoSrc = isInSubplebbitView ? suggested?.avatarUrl : isInProfileView ? imageUrl : 'assets/logo/seedit.png';
|
||||
const logoIsAvatar = (isInSubplebbitView && suggested?.avatarUrl) || (isInProfileView && imageUrl);
|
||||
@@ -385,7 +389,7 @@ const Header = () => {
|
||||
<div className={`${styles.tabs} ${hasFewTabs ? styles.fewTabs : ''}`}>
|
||||
<ul className={styles.tabMenu}>
|
||||
<HeaderTabs />
|
||||
{(isInSubplebbitView || isInSubplebbitSubmitView || isInPostView) && <AboutButton />}
|
||||
{(isInHomeView || isInAllView || isInAboutView || isInSubplebbitView || isInSubplebbitSubmitView || isInPostView) && <AboutButton />}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -258,11 +258,14 @@ a {
|
||||
}
|
||||
|
||||
.footer {
|
||||
font-family: arial, verdana, helvetica, sans-serif;
|
||||
font: normal x-small verdana, arial, helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.footer ul {
|
||||
text-align: left;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
@@ -286,11 +289,32 @@ a {
|
||||
}
|
||||
|
||||
.version {
|
||||
margin: 14px 0 24px 0;
|
||||
margin: 20px 0 24px 0;
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.unstable a {
|
||||
color: var(--yellow);
|
||||
}
|
||||
|
||||
.desktopAd {
|
||||
padding: 0px 0 15px 0;
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
.desktopAdSubtitle {
|
||||
font: normal x-small verdana, arial, helvetica, sans-serif;
|
||||
font-size: 11px;
|
||||
color: var(--gray-strong);
|
||||
}
|
||||
|
||||
.desktopAdLogo img {
|
||||
height: 40px;
|
||||
margin: 5px 10px 0 0;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.footerSecondTitle {
|
||||
margin-top: 10px;
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { useAccount, useBlock, Role, useSubplebbitStats, useAccountComment } fro
|
||||
import styles from './sidebar.module.css';
|
||||
import { getFormattedDate, getFormattedTimeDuration, getFormattedTimeAgo } from '../../lib/utils/time-utils';
|
||||
import { findSubplebbitCreator } from '../../lib/utils/user-utils';
|
||||
import { isAboutView, isAllView, isHomeView, isPendingView, isPostView, isSubplebbitSettingsView, isSubplebbitsView } from '../../lib/utils/view-utils';
|
||||
import { isAboutView, isAllView, isHomeAboutView, isHomeView, isPendingView, isPostView, isSubplebbitSettingsView, isSubplebbitsView } from '../../lib/utils/view-utils';
|
||||
import Markdown from '../markdown';
|
||||
import SearchBar from '../search-bar';
|
||||
import SubscribeButton from '../subscribe-button';
|
||||
@@ -109,6 +109,7 @@ const Sidebar = ({ address, cid, createdAt, description, downvoteCount = 0, role
|
||||
const params = useParams();
|
||||
const isInAboutView = isAboutView(location.pathname);
|
||||
const isInAllView = isAllView(location.pathname);
|
||||
const isInHomeAboutView = isHomeAboutView(location.pathname);
|
||||
const isInHomeView = isHomeView(location.pathname, params);
|
||||
const isInPendingView = isPendingView(location.pathname, params);
|
||||
const isInPostView = isPostView(location.pathname, params);
|
||||
@@ -118,7 +119,8 @@ const Sidebar = ({ address, cid, createdAt, description, downvoteCount = 0, role
|
||||
|
||||
const subplebbitCreator = findSubplebbitCreator(roles);
|
||||
const creatorAddress = subplebbitCreator === 'anonymous' ? 'anonymous' : `${getShortAddress(subplebbitCreator)}`;
|
||||
const submitRoute = isInHomeView || isInAllView ? '/submit' : isInPendingView ? `/p/${pendingPost?.subplebbitAddress}/submit` : `/p/${address}/submit`;
|
||||
const submitRoute =
|
||||
isInHomeView || isInHomeAboutView || isInAllView ? '/submit' : isInPendingView ? `/p/${pendingPost?.subplebbitAddress}/submit` : `/p/${address}/submit`;
|
||||
|
||||
const { blocked, unblock, block } = useBlock({ address });
|
||||
|
||||
@@ -152,13 +154,7 @@ const Sidebar = ({ address, cid, createdAt, description, downvoteCount = 0, role
|
||||
<div className={styles.nub} />
|
||||
</div>
|
||||
</Link>
|
||||
<Link to='/communities/create' onClick={(e) => e.preventDefault()}>
|
||||
<div className={styles.largeButton} onClick={alertCreateCommunity}>
|
||||
{t('create_community')}
|
||||
<div className={styles.nub} />
|
||||
</div>
|
||||
</Link>
|
||||
{!isInHomeView && !isInAllView && !isInPendingView && !isInSubplebbitsView && (
|
||||
{!isInHomeView && !isInHomeAboutView && !isInAllView && !isInPendingView && !isInSubplebbitsView && (
|
||||
<div className={styles.titleBox}>
|
||||
<Link className={styles.title} to={`/p/${address}`}>
|
||||
{address}
|
||||
@@ -200,9 +196,26 @@ const Sidebar = ({ address, cid, createdAt, description, downvoteCount = 0, role
|
||||
)}
|
||||
{isModerator && <ModerationTools address={address} />}
|
||||
{roles && <ModeratorsList roles={roles} />}
|
||||
<Link to='/communities/create' onClick={(e) => e.preventDefault()}>
|
||||
<div className={styles.largeButton} onClick={alertCreateCommunity}>
|
||||
{t('create_community')}
|
||||
<div className={styles.nub} />
|
||||
</div>
|
||||
</Link>
|
||||
<div className={styles.desktopAd}>
|
||||
<a className={styles.desktopAdLogo} href='https://github.com/plebbit/seedit/releases/latest' target='_blank' rel='noopener noreferrer'>
|
||||
<img src='icon.png' alt='seedit mascot' />
|
||||
</a>
|
||||
<span className={styles.desktopAdSubtitle}>
|
||||
<br />
|
||||
...each community needs to be seeded.
|
||||
<br />
|
||||
...the desktop app seeds automatically!
|
||||
</span>
|
||||
</div>
|
||||
<div className={styles.footer}>
|
||||
<div className={styles.footerTitle}>{t('about')}</div>
|
||||
<ul>
|
||||
<li className={styles.footerTitle}>about</li>
|
||||
<li>
|
||||
<a href='https://plebbit.com' target='_blank' rel='noopener noreferrer'>
|
||||
plebbit
|
||||
@@ -224,9 +237,32 @@ const Sidebar = ({ address, cid, createdAt, description, downvoteCount = 0, role
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div className={`${styles.footerTitle} ${styles.footerSecondTitle}`}>apps & tools</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href={`https://github.com/plebbit/seedit/releases/download/v${version}/seedit-${version}.AppImage`} target='_blank' rel='noopener noreferrer'>
|
||||
download for linux
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href={`https://github.com/plebbit/seedit/releases/download/v${version}/seedit.Portable.${version}.exe`} target='_blank' rel='noopener noreferrer'>
|
||||
download for windows
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href={`https://github.com/plebbit/seedit/releases/download/v${version}/seedit-${version}.dmg`} target='_blank' rel='noopener noreferrer'>
|
||||
download for macOS
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href={`https://github.com/plebbit/seedit/releases/latest`} target='_blank' rel='noopener noreferrer'>
|
||||
download for android
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div className={`${styles.version} ${commitRef ? styles.unstable : ''}`}>
|
||||
<a href='https://github.com/plebbit/seedit/releases/latest' target='_blank' rel='noopener noreferrer'>
|
||||
seedit {commitRef ? 'development build ' + commitRef : window.electron && window.electron.isElectron ? 'desktop' : 'web'} v{version} - GPL 2.0
|
||||
<a href={`https://github.com/plebbit/seedit/releases/tag/v${version}`} target='_blank' rel='noopener noreferrer'>
|
||||
seedit {commitRef ? 'dev build (unstable) ' + commitRef : window.electron && window.electron.isElectron ? 'desktop' : 'web'} v{version} - GPL 2.0
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useAccount } from '@plebbit/plebbit-react-hooks';
|
||||
import { getShortAddress } from '@plebbit/plebbit-js';
|
||||
import styles from './topbar.module.css';
|
||||
import { SubplebbitWithDisplay, useDefaultAndSubscriptionsSubplebbits } from '../../lib/utils/addresses-utils';
|
||||
import { useDefaultSubplebbitAddresses } from '../../lib/utils/addresses-utils';
|
||||
import useTimeFilter from '../../hooks/use-time-filter';
|
||||
import { isAllView, isHomeView, isSubplebbitView } from '../../lib/utils/view-utils';
|
||||
|
||||
@@ -12,17 +12,18 @@ const sortTypes = ['hot', 'new', 'active', 'controversialAll', 'topAll'];
|
||||
|
||||
const TopBar = () => {
|
||||
const account = useAccount();
|
||||
const subplebbitAddresses = useDefaultAndSubscriptionsSubplebbits();
|
||||
const subplebbitAddresses = useDefaultSubplebbitAddresses();
|
||||
const { t } = useTranslation();
|
||||
const location = useLocation();
|
||||
const params = useParams();
|
||||
const { timeFilterNames } = useTimeFilter();
|
||||
const selectedTimeFilterName = params.timeFilterName || timeFilterNames[5];
|
||||
const subscriptions = account?.subscriptions;
|
||||
const isAll = isAllView(location.pathname);
|
||||
const isHome = isHomeView(location.pathname, params);
|
||||
const isSubplebbit = isSubplebbitView(location.pathname, params);
|
||||
const homeButtonClass = isHome ? styles.selected : styles.choice;
|
||||
const isinAllView = isAllView(location.pathname);
|
||||
const isInHomeView = isHomeView(location.pathname, params);
|
||||
const isInSubplebbitView = isSubplebbitView(location.pathname, params);
|
||||
const homeButtonClass = isInHomeView ? styles.selected : styles.choice;
|
||||
|
||||
const selectedTimeFilterName = params.timeFilterName || (isInHomeView ? timeFilterNames[2] : timeFilterNames[5]);
|
||||
|
||||
const [isSubsDropdownOpen, setIsSubsDropdownOpen] = useState(false);
|
||||
const toggleSubsDropdown = () => setIsSubsDropdownOpen(!isSubsDropdownOpen);
|
||||
@@ -46,9 +47,9 @@ const TopBar = () => {
|
||||
const [selectedSortType, setSelectedSortType] = useState(params.sortType || '/hot');
|
||||
|
||||
const getTimeFilterLink = (choice: string) => {
|
||||
return isSubplebbit
|
||||
return isInSubplebbitView
|
||||
? `/p/${params.subplebbitAddress}/${selectedSortType}/${choice}`
|
||||
: isAll
|
||||
: isinAllView
|
||||
? `p/all/${selectedSortType}/${choice}`
|
||||
: `/${selectedSortType}/${choice}`;
|
||||
};
|
||||
@@ -125,7 +126,7 @@ const TopBar = () => {
|
||||
<span className={styles.selectedTitle}>{getSelectedSortLabel()}</span>
|
||||
<div className={`${styles.dropChoices} ${styles.sortsDropChoices} ${sortsDropdownClass}`} ref={sortsDropdownChoicesRef}>
|
||||
{sortTypes.map((choice, index) => (
|
||||
<Link to={isSubplebbit ? `/p/${params.subplebbitAddress}/${choice}` : choice} key={index} className={styles.dropdownChoice}>
|
||||
<Link to={isInSubplebbitView ? `/p/${params.subplebbitAddress}/${choice}` : choice} key={index} className={styles.dropdownChoice}>
|
||||
{sortLabels[index]}
|
||||
</Link>
|
||||
))}
|
||||
@@ -150,16 +151,16 @@ const TopBar = () => {
|
||||
</li>
|
||||
<li>
|
||||
<span className={styles.separator}>-</span>
|
||||
<Link to='/p/all' className={isAll ? styles.selected : styles.choice}>
|
||||
<Link to='/p/all' className={isinAllView ? styles.selected : styles.choice}>
|
||||
{t('all')}
|
||||
</Link>
|
||||
</li>
|
||||
<span className={styles.separator}> | </span>
|
||||
{subplebbitAddresses?.map((subplebbit: SubplebbitWithDisplay, index) => (
|
||||
{subplebbitAddresses?.map((address, index) => (
|
||||
<li key={index}>
|
||||
{index !== 0 && <span className={styles.separator}>-</span>}
|
||||
<Link to={`/p/${subplebbit.address}`} className={params.subplebbitAddress === subplebbit.address ? styles.selected : styles.choice}>
|
||||
{subplebbit.displayAddress}
|
||||
<Link to={`/p/${address}`} className={params.subplebbitAddress === address ? styles.selected : styles.choice}>
|
||||
{address}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
|
||||
@@ -21,6 +21,8 @@ export const getAboutLink = (pathname: string, params: ParamsType): string => {
|
||||
return '/profile/about';
|
||||
} else if (pathname.startsWith('/u/')) {
|
||||
return `/u/${params.authorAddress}/c/${params.commentCid}/about`;
|
||||
} else if (pathname.startsWith('/p/all')) {
|
||||
return '/p/all/about';
|
||||
} else {
|
||||
return '/about';
|
||||
}
|
||||
@@ -34,6 +36,10 @@ export const isAllView = (pathname: string): boolean => {
|
||||
return pathname.startsWith('/p/all');
|
||||
};
|
||||
|
||||
export const isAllAboutView = (pathname: string): boolean => {
|
||||
return pathname === '/p/all/about';
|
||||
};
|
||||
|
||||
export const isAuthorView = (pathname: string): boolean => {
|
||||
return pathname.startsWith('/u/');
|
||||
};
|
||||
@@ -51,7 +57,11 @@ export const isProfileDownvotedView = (pathname: string): boolean => {
|
||||
};
|
||||
|
||||
export const isHomeView = (pathname: string, params: ParamsType): boolean => {
|
||||
return pathname === '/' || sortTypes.includes(pathname) || (timeFilterNames.includes(params.timeFilterName as TimeFilterKey) && !pathname.startsWith('/p/all'));
|
||||
return pathname === '/' || sortTypes.includes(pathname) || (timeFilterNames.includes(params.timeFilterName as TimeFilterKey) && !pathname.startsWith('/p/'));
|
||||
};
|
||||
|
||||
export const isHomeAboutView = (pathname: string): boolean => {
|
||||
return pathname === '/about';
|
||||
};
|
||||
|
||||
export const isInboxView = (pathname: string): boolean => {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
--code-background: #fcfcfb;
|
||||
--code-border: #e6e6de;
|
||||
--red: red;
|
||||
--gray-strong: dimgray;
|
||||
--gray-footer: #777;
|
||||
--gray-contrast: #888;
|
||||
--gray-overlay: #F7F7F7;
|
||||
@@ -68,6 +69,7 @@
|
||||
--code-background: rgb(19, 19, 13);
|
||||
--code-border: rgb(52, 58, 60);
|
||||
--red: rgb(200, 0, 0);
|
||||
--gray-strong: rgb(168, 160, 147);
|
||||
--gray-footer: rgb(158, 149, 136);
|
||||
--gray-contrast: #c7c7c7;
|
||||
--gray-overlay: #1f1f1f;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.content {
|
||||
padding: 7px 0px 0px 0px;
|
||||
padding: 7px 5px 0px 0px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
|
||||
@@ -27,7 +27,7 @@ const Home = () => {
|
||||
|
||||
const params = useParams<{ sortType?: string; timeFilterName?: string }>();
|
||||
const sortType = params?.sortType || 'hot';
|
||||
const timeFilterName = (params.timeFilterName as TimeFilterKey) || 'all';
|
||||
const timeFilterName = (params.timeFilterName as TimeFilterKey) || '1w';
|
||||
const { timeFilter } = useTimeFilter(sortType, timeFilterName);
|
||||
|
||||
const { feed, hasMore, loadMore } = useFeed({
|
||||
|
||||
@@ -104,6 +104,21 @@
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.challengeOption {
|
||||
font-size: 15px;
|
||||
margin-top: 10px !important;
|
||||
}
|
||||
|
||||
.challengeDescription {
|
||||
margin: 5px 0 15px 0;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.challengeOptionDescription {
|
||||
margin: 5px 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.moderator {
|
||||
font-size: 15px;
|
||||
color: var(--text);
|
||||
|
||||
@@ -146,67 +146,128 @@ const Moderators = ({ roles }: { roles: RolesCollection | undefined }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const challenges = ['text-math', 'captcha-canvas-v3', 'fail', 'blacklist', 'question', 'evm-contract-call'];
|
||||
|
||||
const Challenge = ({ challenge, selected, setSelected }: { challenge: string; selected: string; setSelected: (challenge: string) => void }) => {
|
||||
const { t } = useTranslation();
|
||||
const [selectedChallenge, setSelectedChallenge] = useState('captcha');
|
||||
const [selectedChallenge, setSelectedChallenge] = useState('none');
|
||||
|
||||
return (
|
||||
<div className={styles.box}>
|
||||
<div className={styles.boxTitle}>{t('challenge')}</div>
|
||||
<div className={styles.boxSubtitle}>choose a challenge to prevent spam</div>
|
||||
<div className={`${styles.boxInput} ${styles.captchaSelect}`}>
|
||||
<select defaultValue='captcha' onChange={(e) => setSelectedChallenge(e.target.value)}>
|
||||
<option value='captcha'>captcha</option>
|
||||
<option value='karma'>karma</option>
|
||||
<option value='token'>token</option>
|
||||
<option value='password'>password</option>
|
||||
<option value='custom'>custom</option>
|
||||
<select defaultValue='none' onChange={(e) => setSelectedChallenge(e.target.value)}>
|
||||
{challenges.map((challenge) => (
|
||||
<option key={challenge} value={challenge}>
|
||||
{challenge}
|
||||
</option>
|
||||
))}
|
||||
<option value='none'>none</option>
|
||||
</select>
|
||||
{selectedChallenge === 'captcha' && (
|
||||
{selectedChallenge === 'text-math' && (
|
||||
<>
|
||||
<br />
|
||||
<label>
|
||||
<input type='checkbox' /> case sensitive
|
||||
</label>
|
||||
<div className={styles.challengeDescription}>Ask a plain text math question, insecure, use ONLY for testing.</div>
|
||||
<div className={styles.challengeOption}>
|
||||
Difficulty
|
||||
<div className={styles.challengeOptionDescription}>The math difficulty of the challenge between 1-3.</div>
|
||||
<input type='number' defaultValue={1} placeholder='1' />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{selectedChallenge === 'karma' && (
|
||||
{selectedChallenge === 'captcha-canvas-v3' && (
|
||||
<>
|
||||
<br />
|
||||
<label>
|
||||
Minimum karma: <input type='number' />
|
||||
</label>
|
||||
</>
|
||||
)}
|
||||
{selectedChallenge === 'token' && (
|
||||
<>
|
||||
<br />
|
||||
<label>
|
||||
Contract address (ERC20): <br />
|
||||
<input type='text' />
|
||||
</label>
|
||||
<br />
|
||||
<label>
|
||||
Minumum balance: <br />
|
||||
<div className={styles.challengeDescription}>make custom image captcha</div>
|
||||
<div className={styles.challengeOption}>
|
||||
Characters
|
||||
<div className={styles.challengeOptionDescription}>Amount of characters of the captcha.</div>
|
||||
<input type='number' />
|
||||
</label>
|
||||
</div>
|
||||
<div className={styles.challengeOption}>
|
||||
Width
|
||||
<div className={styles.challengeOptionDescription}>Height of the captcha.</div>
|
||||
<input type='number' />
|
||||
</div>
|
||||
<div className={styles.challengeOption}>
|
||||
Height
|
||||
<div className={styles.challengeOptionDescription}>Width of the captcha.</div>
|
||||
<input type='number' />
|
||||
</div>
|
||||
<div className={styles.challengeOption}>
|
||||
Color
|
||||
<div className={styles.challengeOptionDescription}>Color of the captcha.</div>
|
||||
<input type='color' />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{selectedChallenge === 'password' && (
|
||||
{selectedChallenge === 'fail' && (
|
||||
<>
|
||||
<br />
|
||||
<label>
|
||||
Password (case sensitive): <input type='password' />
|
||||
</label>
|
||||
<div className={styles.challengeDescription}>A challenge that automatically fails with a custom error message.</div>
|
||||
<div className={styles.challengeOption}>
|
||||
Error
|
||||
<div className={styles.challengeOptionDescription}>The error to display to the author.</div>
|
||||
<input type='text' defaultValue="You're not allowed to publish." placeholder="You're not allowed to publish." />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{selectedChallenge === 'custom' && (
|
||||
{selectedChallenge === 'blacklist' && (
|
||||
<>
|
||||
<br />
|
||||
<label>
|
||||
Paste code: <textarea />
|
||||
</label>
|
||||
<div className={styles.challengeDescription}>Blacklist author addresses.</div>
|
||||
<div className={styles.challengeOption}>
|
||||
Blacklist
|
||||
<div className={styles.challengeOptionDescription}>Comma separated list of author addresses to be blacklisted.</div>
|
||||
<input type='text' placeholder='address1.eth,address2.eth,address3.eth' />
|
||||
</div>
|
||||
<div className={styles.challengeOption}>
|
||||
Error
|
||||
<div className={styles.challengeOptionDescription}>The error to display to the author.</div>
|
||||
<input type='text' defaultValue="You're blacklisted." placeholder="You're blacklisted." />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{selectedChallenge === 'question' && (
|
||||
<>
|
||||
<div className={styles.challengeDescription}>Ask a question, like 'What is the password?'</div>
|
||||
<div className={styles.challengeOption}>
|
||||
Question
|
||||
<div className={styles.challengeOptionDescription}>The question to answer.</div>
|
||||
<input type='text' />
|
||||
</div>
|
||||
<div className={styles.challengeOption}>
|
||||
Answer
|
||||
<div className={styles.challengeOptionDescription}>The answer to the question.</div>
|
||||
<input type='text' />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{selectedChallenge === 'evm-contract-call' && (
|
||||
<>
|
||||
<div className={styles.challengeDescription}>The response from an EVM contract call passes a condition, e.g. a token balance challenge.</div>
|
||||
<div className={styles.challengeOption}>
|
||||
chainTicker
|
||||
<div className={styles.challengeOptionDescription}>The chain ticker</div>
|
||||
<input type='text' placeholder='eth' defaultValue='eth' />
|
||||
</div>
|
||||
<div className={styles.challengeOption}>
|
||||
Address
|
||||
<div className={styles.challengeOptionDescription}>The contract address.</div>
|
||||
<input type='text' placeholder='0x...' />
|
||||
</div>
|
||||
<div className={styles.challengeOption}>
|
||||
ABI
|
||||
<div className={styles.challengeOptionDescription}>The ABI of the contract method.</div>
|
||||
<textarea placeholder='{"constant":true,"inputs":[{"internalType":"address","name":"account...' autoCorrect='off' autoComplete='off' spellCheck='false' />
|
||||
</div>
|
||||
<div className={styles.challengeOption}>
|
||||
Condition
|
||||
<div className={styles.challengeOptionDescription}>The condition the contract call response must pass.</div>
|
||||
<textarea placeholder='>1000' autoCorrect='off' autoComplete='off' spellCheck='false' />
|
||||
</div>
|
||||
<div className={styles.challengeOption}>
|
||||
Error
|
||||
<div className={styles.challengeOptionDescription}>The error to display to the author.</div>
|
||||
<input type='text' defaultValue="Contract call response doesn't pass condition." placeholder="Contract call response doesn't pass condition." />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{selectedChallenge === 'none' && <span className={styles.noChallengeWarning}>Warning: vulnerable to spam attacks.</span>}
|
||||
|
||||
@@ -13248,7 +13248,7 @@ react-is@^18.0.0:
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
|
||||
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
|
||||
|
||||
react-markdown@^9.0.1:
|
||||
react-markdown@9.0.1:
|
||||
version "9.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-9.0.1.tgz#c05ddbff67fd3b3f839f8c648e6fb35d022397d1"
|
||||
integrity sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==
|
||||
@@ -13567,7 +13567,7 @@ regjsparser@^0.9.1:
|
||||
dependencies:
|
||||
jsesc "~0.5.0"
|
||||
|
||||
rehype-sanitize@^6.0.0:
|
||||
rehype-sanitize@6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/rehype-sanitize/-/rehype-sanitize-6.0.0.tgz#16e95f4a67a69cbf0f79e113c8e0df48203db73c"
|
||||
integrity sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==
|
||||
@@ -13580,7 +13580,7 @@ relateurl@^0.2.7:
|
||||
resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
|
||||
integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==
|
||||
|
||||
remark-gfm@^4.0.0:
|
||||
remark-gfm@4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/remark-gfm/-/remark-gfm-4.0.0.tgz#aea777f0744701aa288b67d28c43565c7e8c35de"
|
||||
integrity sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==
|
||||
|
||||
Reference in New Issue
Block a user