mirror of
https://github.com/plebbit/seedit.git
synced 2026-02-08 04:50:57 -05:00
refactor submit page
This commit is contained in:
@@ -1,30 +1,32 @@
|
||||
import { ChangeEvent, useCallback, useEffect, useState, useRef } from 'react';
|
||||
import { Link, useNavigate, useParams } from 'react-router-dom';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { Capacitor } from '@capacitor/core';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useDropzone } from 'react-dropzone';
|
||||
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { Link, useNavigate, useParams } from 'react-router-dom';
|
||||
import { useAccount, usePublishComment, useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
||||
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
|
||||
import { Capacitor } from '@capacitor/core';
|
||||
import FileUploader from '../../plugins/file-uploader';
|
||||
import { getLinkMediaInfo } from '../../lib/utils/media-utils';
|
||||
import { isValidURL } from '../../lib/utils/url-utils';
|
||||
import usePublishPostStore from '../../stores/use-publish-post-store';
|
||||
import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
|
||||
import useIsSubplebbitOffline from '../../hooks/use-is-subplebbit-offline';
|
||||
import { getLinkMediaInfo } from '../../lib/utils/media-utils';
|
||||
import { isValidURL } from '../../lib/utils/url-utils';
|
||||
import Embed from '../../components/post/embed';
|
||||
import LoadingEllipsis from '../../components/loading-ellipsis';
|
||||
import Markdown from '../../components/markdown';
|
||||
import FileUploader from '../../plugins/file-uploader';
|
||||
import Embed from '../../components/post/embed';
|
||||
import styles from './submit-page.module.css';
|
||||
|
||||
const isAndroid = Capacitor.getPlatform() === 'android';
|
||||
const isElectron = window.isElectron === true;
|
||||
const warningMessage =
|
||||
'This feature cannot work in browsers. It is only available on Seedit Android app, or desktop app (win/mac/linux) versions.\n\nGo to the download links page on GitHub?';
|
||||
|
||||
const UrlField = ({ url, setUrl, urlRef }: { url: string; setUrl: (url: string) => void; urlRef: React.RefObject<HTMLInputElement> }) => {
|
||||
const UrlField = () => {
|
||||
const { t } = useTranslation();
|
||||
const { setPublishPostStore } = usePublishPostStore();
|
||||
const { link: url, setPublishPostStore } = usePublishPostStore();
|
||||
const [mediaError, setMediaError] = useState(false);
|
||||
|
||||
const mediaInfo = getLinkMediaInfo(url);
|
||||
const mediaInfo = url ? getLinkMediaInfo(url) : null;
|
||||
const mediaType = mediaInfo?.type;
|
||||
|
||||
let mediaComponent;
|
||||
@@ -38,11 +40,11 @@ const UrlField = ({ url, setUrl, urlRef }: { url: string; setUrl: (url: string)
|
||||
} else if (mediaType === 'audio') {
|
||||
mediaComponent = <audio src={url} controls />;
|
||||
} else if (mediaType === 'iframe') {
|
||||
mediaComponent = <Embed url={url} />;
|
||||
mediaComponent = <Embed url={url || ''} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.box}>
|
||||
{url && isValidURL(url) ? (
|
||||
<span className={styles.boxTitleOptional}>{mediaType}</span>
|
||||
) : (
|
||||
@@ -53,12 +55,11 @@ const UrlField = ({ url, setUrl, urlRef }: { url: string; setUrl: (url: string)
|
||||
)}
|
||||
<div className={styles.boxContent}>
|
||||
{url && (
|
||||
<span className={styles.urlCancelButton} onClick={() => setUrl('')}>
|
||||
<span className={styles.urlCancelButton} onClick={() => setPublishPostStore({ link: undefined })}>
|
||||
x
|
||||
</span>
|
||||
)}
|
||||
<input
|
||||
ref={urlRef}
|
||||
className={`${styles.input} ${styles.inputUrl}`}
|
||||
type='text'
|
||||
value={url ?? ''}
|
||||
@@ -66,9 +67,8 @@ const UrlField = ({ url, setUrl, urlRef }: { url: string; setUrl: (url: string)
|
||||
autoComplete='off'
|
||||
spellCheck='false'
|
||||
onChange={(e) => {
|
||||
setUrl(e.target.value);
|
||||
setMediaError(false);
|
||||
setPublishPostStore({ link: e.target.value });
|
||||
setMediaError(false);
|
||||
}}
|
||||
/>
|
||||
{url && isValidURL(url) ? (
|
||||
@@ -77,14 +77,11 @@ const UrlField = ({ url, setUrl, urlRef }: { url: string; setUrl: (url: string)
|
||||
<div className={styles.description}>{t('submit_url_description')}</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const warningMessage =
|
||||
'This feature cannot work in browsers. It is only available on Seedit Android app, or desktop app (win/mac/linux) versions.\n\nGo to the download links page on GitHub?';
|
||||
|
||||
const UploadMediaForm = ({ setUrl }: { setUrl: (url: string) => void }) => {
|
||||
const UploadMediaForm = () => {
|
||||
const { t } = useTranslation();
|
||||
const { setPublishPostStore } = usePublishPostStore();
|
||||
|
||||
@@ -129,14 +126,12 @@ const UploadMediaForm = ({ setUrl }: { setUrl: (url: string) => void }) => {
|
||||
|
||||
const result = await FileUploader.uploadMedia(fileData as { fileData?: string; fileName: string });
|
||||
if (result.url) {
|
||||
setUrl(result.url);
|
||||
setPublishPostStore({ link: result.url || undefined });
|
||||
}
|
||||
} else if (isAndroid) {
|
||||
// android can handle File objects directly
|
||||
const result = await FileUploader.uploadMedia(acceptedFiles[0]);
|
||||
if (result.url) {
|
||||
setUrl(result.url);
|
||||
setPublishPostStore({ link: result.url || undefined });
|
||||
}
|
||||
}
|
||||
@@ -150,7 +145,7 @@ const UploadMediaForm = ({ setUrl }: { setUrl: (url: string) => void }) => {
|
||||
}
|
||||
}
|
||||
},
|
||||
[setUrl, setPublishPostStore, t],
|
||||
[setPublishPostStore, t],
|
||||
);
|
||||
|
||||
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
||||
@@ -189,7 +184,6 @@ const UploadMediaForm = ({ setUrl }: { setUrl: (url: string) => void }) => {
|
||||
});
|
||||
|
||||
if (uploadResult?.url) {
|
||||
setUrl(uploadResult.url);
|
||||
setPublishPostStore({ link: uploadResult.url });
|
||||
} else {
|
||||
throw new Error('No URL returned from upload');
|
||||
@@ -208,7 +202,7 @@ const UploadMediaForm = ({ setUrl }: { setUrl: (url: string) => void }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.box}>
|
||||
<span className={styles.boxTitleOptional}>image/video/audio</span>
|
||||
<div className={styles.boxContent}>
|
||||
{isUploading ? (
|
||||
@@ -227,20 +221,228 @@ const UploadMediaForm = ({ setUrl }: { setUrl: (url: string) => void }) => {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Submit = () => {
|
||||
const TitleField = () => {
|
||||
const { t } = useTranslation();
|
||||
const { title, setPublishPostStore } = usePublishPostStore();
|
||||
|
||||
return (
|
||||
<div className={styles.box}>
|
||||
<span className={styles.boxTitleRequired}>{t('title')}</span>
|
||||
<div className={styles.boxContent}>
|
||||
<textarea
|
||||
className={`${styles.input} ${styles.inputTitle}`}
|
||||
value={title}
|
||||
onChange={(e) => {
|
||||
setPublishPostStore({ title: e.target.value });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ContentField = () => {
|
||||
const { t } = useTranslation();
|
||||
const [showPreview, setShowPreview] = useState(false);
|
||||
|
||||
const { content, setPublishPostStore } = usePublishPostStore();
|
||||
|
||||
return (
|
||||
<div className={styles.box}>
|
||||
<span className={styles.boxTitleOptional}>{t('text')}</span>
|
||||
<span className={styles.optional}> ({t('optional')})</span>
|
||||
<div className={styles.boxContent}>
|
||||
{!showPreview ? (
|
||||
<textarea
|
||||
className={`${styles.input} ${styles.inputText}`}
|
||||
value={content || ''}
|
||||
onChange={(e) => {
|
||||
setPublishPostStore({ content: e.target.value });
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div className={styles.contentPreview}>
|
||||
<div className={styles.contentPreviewMarkdown}>
|
||||
<Markdown content={content || ''} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<button className={styles.previewButton} disabled={!content} onClick={() => setShowPreview(!showPreview)}>
|
||||
{showPreview ? t('edit') : t('preview')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const SubplebbitAddressField = () => {
|
||||
const { t } = useTranslation();
|
||||
const { subscriptions } = useAccount() || {};
|
||||
const defaultSubplebbitAddresses = useDefaultSubplebbitAddresses();
|
||||
const { subplebbitAddress: inputAddress, setPublishPostStore } = usePublishPostStore();
|
||||
|
||||
const filteredSubplebbitAddresses = defaultSubplebbitAddresses.filter((address) => address?.toLowerCase()?.includes(inputAddress?.toLowerCase() || '')).slice(0, 10);
|
||||
const [isInputAddressFocused, setIsInputAddressFocused] = useState(false);
|
||||
const [activeDropdownIndex, setActiveDropdownIndex] = useState<number>(-1);
|
||||
|
||||
// show list of random subplebbits only once when the component mounts
|
||||
const [randomSubplebbitSuggestions, setRandomSubplebbitSuggestions] = useState<string[]>([]);
|
||||
useEffect(() => {
|
||||
const generatedSubplebbits = getRandomSubplebbits(defaultSubplebbitAddresses, 10);
|
||||
setRandomSubplebbitSuggestions(generatedSubplebbits);
|
||||
}, [defaultSubplebbitAddresses]);
|
||||
const listSource = subscriptions?.length > 5 ? subscriptions : randomSubplebbitSuggestions;
|
||||
|
||||
const defaultSubplebbitsDropdown = inputAddress && (
|
||||
<ul className={styles.dropdown}>
|
||||
{filteredSubplebbitAddresses.map((subplebbitAddress, index) => (
|
||||
<li
|
||||
key={subplebbitAddress}
|
||||
className={`${styles.dropdownItem} ${index === activeDropdownIndex ? styles.activeDropdownItem : ''}`}
|
||||
onClick={() => handleSubplebbitSelect(subplebbitAddress)}
|
||||
onMouseEnter={() => setActiveDropdownIndex(index)}
|
||||
>
|
||||
{subplebbitAddress}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e: KeyboardEvent) => {
|
||||
if (e.key === 'ArrowDown') {
|
||||
setActiveDropdownIndex((prevIndex) => (prevIndex < filteredSubplebbitAddresses.length - 1 ? prevIndex + 1 : prevIndex));
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
setActiveDropdownIndex((prevIndex) => (prevIndex > 0 ? prevIndex - 1 : 0));
|
||||
} else if (e.key === 'Enter') {
|
||||
if (activeDropdownIndex !== -1) {
|
||||
const selectedAddress = filteredSubplebbitAddresses[activeDropdownIndex];
|
||||
setPublishPostStore({ subplebbitAddress: selectedAddress });
|
||||
}
|
||||
setActiveDropdownIndex(-1);
|
||||
setIsInputAddressFocused(false);
|
||||
}
|
||||
},
|
||||
[filteredSubplebbitAddresses, activeDropdownIndex, setPublishPostStore],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
}, [handleKeyDown]);
|
||||
|
||||
const handleSubplebbitSelect = (subplebbitAddress: string) => {
|
||||
setPublishPostStore({ subplebbitAddress: subplebbitAddress });
|
||||
setIsInputAddressFocused(false);
|
||||
setActiveDropdownIndex(-1);
|
||||
};
|
||||
|
||||
const getRandomSubplebbits = (addresses: string[], count: number) => {
|
||||
let shuffled = addresses.sort(() => 0.5 - Math.random());
|
||||
return shuffled.slice(0, count);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.box}>
|
||||
<span className={styles.boxTitleRequired}>{t('submit_choose')}</span>
|
||||
<div className={styles.boxContent}>
|
||||
<span className={styles.boxSubtitle}>{t('community_address')}:</span>
|
||||
<input
|
||||
className={`${styles.input} ${styles.inputCommunity}`}
|
||||
type='text'
|
||||
placeholder={`"community.eth/.sol" ${t('or')} "12D3KooW..."`}
|
||||
value={inputAddress}
|
||||
onChange={(e) => {
|
||||
setPublishPostStore({ subplebbitAddress: e.target.value });
|
||||
}}
|
||||
autoCorrect='off'
|
||||
autoComplete='off'
|
||||
spellCheck='false'
|
||||
onFocus={() => setIsInputAddressFocused(true)}
|
||||
onBlur={() => setTimeout(() => setIsInputAddressFocused(false), 100)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
(e.target as HTMLInputElement).blur();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{inputAddress && isInputAddressFocused && defaultSubplebbitsDropdown}
|
||||
<div className={styles.subsDescription}>{subscriptions?.length > 5 ? t('submit_subscriptions') : t('submit_subscriptions_notice')}</div>
|
||||
<div className={styles.subs}>
|
||||
{listSource.map((subscription: string) => (
|
||||
<span
|
||||
key={subscription}
|
||||
className={styles.sub}
|
||||
onClick={() => {
|
||||
setPublishPostStore({ subplebbitAddress: subscription });
|
||||
}}
|
||||
>
|
||||
{Plebbit.getShortAddress(subscription)}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const RulesInfo = ({ shortAddress, rules }: { shortAddress: string; rules: string[] }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className={styles.box}>
|
||||
<span className={`${styles.boxTitle} ${styles.rulesTitle}`}>
|
||||
{t('rules_for')} p/{shortAddress}
|
||||
</span>
|
||||
<div className={styles.boxContent}>
|
||||
<div className={styles.description}>
|
||||
<ol className={styles.rules}>{rules?.map((rule: string, index: number) => <li key={index}>{rule}</li>)}</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const SubmitOptions = () => {
|
||||
const { t } = useTranslation();
|
||||
const { setPublishPostStore } = usePublishPostStore();
|
||||
|
||||
return (
|
||||
<div className={styles.box}>
|
||||
<div className={styles.boxTitle}>{t('options')}</div>
|
||||
<div className={styles.boxContent}>
|
||||
<div className={styles.options}>
|
||||
<div className={styles.option}>
|
||||
<label>
|
||||
<input type='checkbox' onChange={(e) => setPublishPostStore({ spoiler: e.target.checked })} />
|
||||
{t('spoiler')}
|
||||
</label>
|
||||
</div>
|
||||
<div className={styles.option}>
|
||||
<label>
|
||||
<input type='checkbox' onChange={(e) => setPublishPostStore({ nsfw: e.target.checked })} />
|
||||
{t('nsfw')}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const SubmitPage = () => {
|
||||
const { t } = useTranslation();
|
||||
const params = useParams();
|
||||
const navigate = useNavigate();
|
||||
const { setPublishPostStore, resetPublishPostStore } = usePublishPostStore();
|
||||
|
||||
const [inputAddress, setInputAddress] = useState(params.subplebbitAddress || '');
|
||||
const [selectedSubplebbit, setSelectedSubplebbit] = useState(params.subplebbitAddress || '');
|
||||
const [url, setUrl] = useState('');
|
||||
const urlRef = useRef<HTMLInputElement>(null);
|
||||
const { link, title, subplebbitAddress, publishCommentOptions, setPublishPostStore, resetPublishPostStore } = usePublishPostStore();
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
@@ -249,28 +451,15 @@ const Submit = () => {
|
||||
}, [resetPublishPostStore]);
|
||||
|
||||
useEffect(() => {
|
||||
setInputAddress(params.subplebbitAddress || '');
|
||||
setSelectedSubplebbit(params.subplebbitAddress || '');
|
||||
setPublishPostStore({ subplebbitAddress: params.subplebbitAddress || '' });
|
||||
}, [params.subplebbitAddress, setPublishPostStore]);
|
||||
|
||||
const {
|
||||
title,
|
||||
content,
|
||||
link,
|
||||
subplebbitAddress,
|
||||
publishCommentOptions,
|
||||
setPublishPostStore: setSubmitStoreHook,
|
||||
resetPublishPostStore: resetSubmitStoreHook,
|
||||
} = usePublishPostStore();
|
||||
const { index, publishComment } = usePublishComment(publishCommentOptions);
|
||||
const { subscriptions } = useAccount() || {};
|
||||
const defaultSubplebbitAddresses = useDefaultSubplebbitAddresses();
|
||||
|
||||
const selectedSubplebbitData = useSubplebbit({ subplebbitAddress: selectedSubplebbit });
|
||||
|
||||
const selectedSubplebbitData = useSubplebbit({ subplebbitAddress });
|
||||
const { shortAddress, rules } = selectedSubplebbitData;
|
||||
const { isOffline, offlineTitle } = useIsSubplebbitOffline(selectedSubplebbitData);
|
||||
|
||||
const { index, publishComment } = usePublishComment(publishCommentOptions);
|
||||
|
||||
const onPublish = () => {
|
||||
if (!title) {
|
||||
alert(`Missing title`);
|
||||
@@ -291,86 +480,17 @@ const Submit = () => {
|
||||
// redirect to pending page when pending comment is created
|
||||
useEffect(() => {
|
||||
if (typeof index === 'number') {
|
||||
resetSubmitStoreHook();
|
||||
resetPublishPostStore();
|
||||
navigate(`/profile/${index}`);
|
||||
}
|
||||
}, [index, resetSubmitStoreHook, navigate]);
|
||||
|
||||
const subsDescription = <div className={styles.subsDescription}>{subscriptions?.length > 5 ? t('submit_subscriptions') : t('submit_subscriptions_notice')}</div>;
|
||||
|
||||
const getRandomSubplebbits = (addresses: string[], count: number) => {
|
||||
let shuffled = addresses.sort(() => 0.5 - Math.random());
|
||||
return shuffled.slice(0, count);
|
||||
};
|
||||
|
||||
// show list of random subplebbits only once when the component mounts
|
||||
const [randomSubplebbits, setRandomSubplebbits] = useState<string[]>([]);
|
||||
useEffect(() => {
|
||||
const generatedSubplebbits = getRandomSubplebbits(defaultSubplebbitAddresses, 10);
|
||||
setRandomSubplebbits(generatedSubplebbits);
|
||||
}, [defaultSubplebbitAddresses]);
|
||||
const listSource = subscriptions?.length > 5 ? subscriptions : randomSubplebbits;
|
||||
|
||||
const [activeDropdownIndex, setActiveDropdownIndex] = useState<number>(-1);
|
||||
const [isInputAddressFocused, setIsInputAddressFocused] = useState(false);
|
||||
|
||||
const filteredSubplebbitAddresses = defaultSubplebbitAddresses.filter((address) => address?.toLowerCase()?.includes(inputAddress?.toLowerCase())).slice(0, 10);
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e: KeyboardEvent) => {
|
||||
if (e.key === 'ArrowDown') {
|
||||
setActiveDropdownIndex((prevIndex) => (prevIndex < filteredSubplebbitAddresses.length - 1 ? prevIndex + 1 : prevIndex));
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
setActiveDropdownIndex((prevIndex) => (prevIndex > 0 ? prevIndex - 1 : 0));
|
||||
} else if (e.key === 'Enter') {
|
||||
if (activeDropdownIndex !== -1) {
|
||||
const selectedAddress = filteredSubplebbitAddresses[activeDropdownIndex];
|
||||
setSelectedSubplebbit(selectedAddress);
|
||||
setSubmitStoreHook({ subplebbitAddress: selectedAddress });
|
||||
setInputAddress(selectedAddress);
|
||||
}
|
||||
setActiveDropdownIndex(-1);
|
||||
setIsInputAddressFocused(false);
|
||||
}
|
||||
},
|
||||
[filteredSubplebbitAddresses, activeDropdownIndex, setSubmitStoreHook],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
}, [handleKeyDown]);
|
||||
|
||||
const defaultSubplebbitsDropdown = inputAddress && (
|
||||
<ul className={styles.dropdown}>
|
||||
{filteredSubplebbitAddresses.map((subplebbitAddress, index) => (
|
||||
<li
|
||||
key={subplebbitAddress}
|
||||
className={`${styles.dropdownItem} ${index === activeDropdownIndex ? styles.activeDropdownItem : ''}`}
|
||||
onClick={() => handleSubplebbitSelect(subplebbitAddress)}
|
||||
onMouseEnter={() => setActiveDropdownIndex(index)}
|
||||
>
|
||||
{subplebbitAddress}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
|
||||
const handleAddressChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const newValue = e.target.value;
|
||||
setInputAddress(newValue);
|
||||
setSelectedSubplebbit(newValue);
|
||||
setSubmitStoreHook({ subplebbitAddress: newValue });
|
||||
};
|
||||
}, [index, resetPublishPostStore, navigate]);
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, []);
|
||||
|
||||
const documentTitle = t('submit_to_string', {
|
||||
string: selectedSubplebbitData?.title || selectedSubplebbitData?.shortAddress || 'Seedit',
|
||||
string: selectedSubplebbitData?.title || shortAddress || 'Seedit',
|
||||
interpolation: { escapeValue: false },
|
||||
});
|
||||
|
||||
@@ -378,16 +498,6 @@ const Submit = () => {
|
||||
document.title = documentTitle;
|
||||
}, [documentTitle]);
|
||||
|
||||
const handleSubplebbitSelect = (subplebbitAddress: string) => {
|
||||
setSelectedSubplebbit(subplebbitAddress);
|
||||
setInputAddress(subplebbitAddress);
|
||||
setSubmitStoreHook({ subplebbitAddress: subplebbitAddress });
|
||||
setIsInputAddressFocused(false);
|
||||
setActiveDropdownIndex(-1);
|
||||
};
|
||||
|
||||
const [showPreview, setShowPreview] = useState(false);
|
||||
|
||||
return (
|
||||
<div className={styles.content}>
|
||||
<h1>
|
||||
@@ -395,135 +505,23 @@ const Submit = () => {
|
||||
i18nKey='submit_to'
|
||||
shouldUnescape={true}
|
||||
values={{
|
||||
link: selectedSubplebbitData?.title || selectedSubplebbitData?.shortAddress || 'seedit',
|
||||
link: selectedSubplebbitData?.title || shortAddress || 'seedit',
|
||||
}}
|
||||
components={{
|
||||
1: selectedSubplebbitData?.shortAddress ? (
|
||||
<Link key={selectedSubplebbit} to={`/p/${selectedSubplebbit}`} className={styles.location} />
|
||||
) : (
|
||||
<span key={selectedSubplebbit} />
|
||||
),
|
||||
1: shortAddress ? <Link key={subplebbitAddress} to={`/p/${subplebbitAddress}`} className={styles.location} /> : <span key={subplebbitAddress} />,
|
||||
}}
|
||||
/>
|
||||
</h1>
|
||||
<div className={styles.form}>
|
||||
<div className={styles.formContent}>
|
||||
{isOffline && selectedSubplebbit && <div className={styles.infobar}>{offlineTitle}</div>}
|
||||
<div className={styles.box}>
|
||||
<UrlField url={url} setUrl={setUrl} urlRef={urlRef} />
|
||||
</div>
|
||||
{url.length === 0 && (
|
||||
<div className={styles.box}>
|
||||
<UploadMediaForm setUrl={setUrl} />
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.box}>
|
||||
<span className={styles.boxTitleRequired}>{t('title')}</span>
|
||||
<div className={styles.boxContent}>
|
||||
<textarea
|
||||
className={`${styles.input} ${styles.inputTitle}`}
|
||||
onChange={(e) => {
|
||||
setSubmitStoreHook({ title: e.target.value });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.box}>
|
||||
<span className={styles.boxTitleOptional}>{t('text')}</span>
|
||||
<span className={styles.optional}> ({t('optional')})</span>
|
||||
<div className={styles.boxContent}>
|
||||
{!showPreview ? (
|
||||
<textarea
|
||||
className={`${styles.input} ${styles.inputText}`}
|
||||
value={content || ''}
|
||||
onChange={(e) => {
|
||||
setSubmitStoreHook({ content: e.target.value });
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div className={styles.contentPreview}>
|
||||
<div className={styles.contentPreviewMarkdown}>
|
||||
<Markdown content={content || ''} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<button className={styles.previewButton} disabled={!content} onClick={() => setShowPreview(!showPreview)}>
|
||||
{showPreview ? t('edit') : t('preview')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.box}>
|
||||
<span className={styles.boxTitleRequired}>{t('submit_choose')}</span>
|
||||
<div className={styles.boxContent}>
|
||||
<span className={styles.boxSubtitle}>{t('community_address')}:</span>
|
||||
<input
|
||||
className={`${styles.input} ${styles.inputCommunity}`}
|
||||
type='text'
|
||||
placeholder={`"community.eth/.sol" ${t('or')} "12D3KooW..."`}
|
||||
value={inputAddress}
|
||||
onChange={handleAddressChange}
|
||||
autoCorrect='off'
|
||||
autoComplete='off'
|
||||
spellCheck='false'
|
||||
onFocus={() => setIsInputAddressFocused(true)}
|
||||
onBlur={() => setTimeout(() => setIsInputAddressFocused(false), 100)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
(e.target as HTMLInputElement).blur();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{inputAddress && isInputAddressFocused && defaultSubplebbitsDropdown}
|
||||
{subsDescription}
|
||||
<div className={styles.subs}>
|
||||
{listSource.map((subscription: string) => (
|
||||
<span
|
||||
key={subscription}
|
||||
className={styles.sub}
|
||||
onClick={() => {
|
||||
setSelectedSubplebbit(subscription);
|
||||
setInputAddress(subscription);
|
||||
setSubmitStoreHook({ subplebbitAddress: subscription });
|
||||
}}
|
||||
>
|
||||
{Plebbit.getShortAddress(subscription)}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{selectedSubplebbitData?.rules?.length > 0 && (
|
||||
<div className={styles.box}>
|
||||
<span className={`${styles.boxTitle} ${styles.rulesTitle}`}>
|
||||
{t('rules_for')} p/{selectedSubplebbitData.shortAddress}
|
||||
</span>
|
||||
<div className={styles.boxContent}>
|
||||
<div className={styles.description}>
|
||||
<ol className={styles.rules}>{selectedSubplebbitData.rules?.map((rule: string, index: number) => <li key={index}>{rule}</li>)}</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.box}>
|
||||
<div className={styles.boxTitle}>{t('options')}</div>
|
||||
<div className={styles.boxContent}>
|
||||
<div className={styles.options}>
|
||||
<div className={styles.option}>
|
||||
<label>
|
||||
<input type='checkbox' onChange={(e) => setSubmitStoreHook({ spoiler: e.target.checked })} />
|
||||
{t('spoiler')}
|
||||
</label>
|
||||
</div>
|
||||
<div className={styles.option}>
|
||||
<label>
|
||||
<input type='checkbox' onChange={(e) => setSubmitStoreHook({ nsfw: e.target.checked })} />
|
||||
{t('nsfw')}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{isOffline && subplebbitAddress && <div className={styles.infobar}>{offlineTitle}</div>}
|
||||
<UrlField />
|
||||
{link?.length === 0 && <UploadMediaForm />}
|
||||
<TitleField />
|
||||
<ContentField />
|
||||
<SubplebbitAddressField />
|
||||
{rules?.length > 0 && <RulesInfo shortAddress={shortAddress} rules={rules} />}
|
||||
<SubmitOptions />
|
||||
<div className={`${styles.box} ${styles.notice}`}>{t('submit_notice')}</div>
|
||||
<div>*{t('required')}</div>
|
||||
<div className={styles.submit}>
|
||||
@@ -537,4 +535,4 @@ const Submit = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default Submit;
|
||||
export default SubmitPage;
|
||||
|
||||
Reference in New Issue
Block a user