mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(post-form): auto-convert YouTube links to thumbnail URLs
Add a countdown notice and shared hook to replace YouTube watch links with img.youtube.com thumbnails in post and reply forms, with i18n copy.
This commit is contained in:
@@ -34,6 +34,7 @@ import useIsMobile from '../../hooks/use-is-mobile';
|
||||
import { useResolvedCommunityAddress } from '../../hooks/use-resolved-community-address';
|
||||
import useSafeAccountComment from '../../hooks/use-safe-account-comment';
|
||||
import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
|
||||
import { useYouTubeThumbnailLinkConversion } from '../../hooks/use-youtube-thumbnail-link-conversion';
|
||||
import usePublishPost from '../../hooks/use-publish-post';
|
||||
import usePublishReply from '../../hooks/use-publish-reply';
|
||||
import { useFileUpload } from '../../hooks/use-file-upload';
|
||||
@@ -144,10 +145,10 @@ interface PostFormFieldsProps {
|
||||
lengthError: string | null;
|
||||
handleContentChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
|
||||
handleContentValueChange: (content: string, options?: string) => void;
|
||||
handleLinkChange: (link: string) => void;
|
||||
handleOptionsChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
setPublishPostOptions: (opts: Record<string, unknown>) => void;
|
||||
setPublishReplyOptions: (opts: Record<string, unknown>) => void;
|
||||
setUrl: (url: string) => void;
|
||||
isUploading: boolean;
|
||||
uploadedFileName: string | null | undefined;
|
||||
showUploadControls: boolean;
|
||||
@@ -195,10 +196,10 @@ const PostFormFields = ({
|
||||
lengthError,
|
||||
handleContentChange,
|
||||
handleContentValueChange,
|
||||
handleLinkChange,
|
||||
handleOptionsChange,
|
||||
setPublishPostOptions,
|
||||
setPublishReplyOptions,
|
||||
setUrl,
|
||||
isUploading,
|
||||
uploadedFileName,
|
||||
showUploadControls,
|
||||
@@ -356,12 +357,7 @@ const PostFormFields = ({
|
||||
ref={urlRef}
|
||||
disabled={isUploading}
|
||||
onChange={(e) => {
|
||||
setUrl(e.target.value);
|
||||
if (isInPostView) {
|
||||
setPublishReplyOptions({ link: e.target.value });
|
||||
} else {
|
||||
setPublishPostOptions({ link: e.target.value });
|
||||
}
|
||||
handleLinkChange(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<span className={styles.linkType}> {url && <LinkTypePreviewer link={url} />}</span>
|
||||
@@ -602,6 +598,8 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
||||
};
|
||||
|
||||
const onPublishPost = () => {
|
||||
const appliedYouTubeConversion = applyPendingConversion();
|
||||
|
||||
const currentTitle = subjectRef.current?.value.trim() || '';
|
||||
const currentContent = textRef.current?.value || '';
|
||||
const currentUrl = urlRef.current?.value.trim() || '';
|
||||
@@ -653,7 +651,7 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
||||
};
|
||||
|
||||
nonokoRedirectPathRef.current = hasNonokoOption(currentOptions) ? getBoardIndexPath() : null;
|
||||
publishPost({ content: publishContent, ...publishOptions });
|
||||
publishPost({ content: publishContent, ...(appliedYouTubeConversion ? { link: currentUrl } : {}), ...publishOptions });
|
||||
};
|
||||
|
||||
// redirect to pending page when pending comment is created
|
||||
@@ -725,6 +723,8 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
||||
};
|
||||
|
||||
const onPublishReply = () => {
|
||||
const appliedYouTubeConversion = applyPendingConversion();
|
||||
|
||||
const currentUrl = urlRef.current?.value.trim() || '';
|
||||
const currentOptions = optionsRef.current?.value || '';
|
||||
const currentOptionsError = getPostOptionsValidationError(currentOptions, postOptionsDirectoryCode);
|
||||
@@ -764,7 +764,36 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
||||
const flagPublishOptions = getCommentFlagPublishOptionsForDirectory(directoryEntry, flagRef.current?.value);
|
||||
|
||||
nonokoRedirectPathRef.current = hasNonokoOption(currentOptions) ? getBoardIndexPath() : null;
|
||||
publishReply({ content: publishContent, ...flagPublishOptions });
|
||||
publishReply({ content: publishContent, ...(appliedYouTubeConversion ? { link: currentUrl } : {}), ...flagPublishOptions });
|
||||
};
|
||||
|
||||
const setLinkValue = (nextUrl: string) => {
|
||||
setUrl(nextUrl);
|
||||
if (isInPostView) {
|
||||
setPublishReplyOptions({ link: nextUrl });
|
||||
} else {
|
||||
setPublishPostOptions({ link: nextUrl });
|
||||
}
|
||||
};
|
||||
|
||||
const {
|
||||
applyPendingConversion,
|
||||
cancelPendingConversion,
|
||||
noticeCountdown: youtubeThumbnailConversionCountdown,
|
||||
queueLinkConversion,
|
||||
} = useYouTubeThumbnailLinkConversion({
|
||||
enabled: requirePostLinkIsMedia,
|
||||
onContentChange: handleContentValueChange,
|
||||
onLinkChange: setLinkValue,
|
||||
textRef,
|
||||
urlRef,
|
||||
});
|
||||
|
||||
const handleLinkChange = (nextUrl: string) => {
|
||||
setLinkValue(nextUrl);
|
||||
if (queueLinkConversion(nextUrl)) {
|
||||
setFormError(null);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@@ -782,33 +811,22 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
||||
const { isUploading, uploadedFileName, handleUpload, uploadFile } = useFileUpload({
|
||||
onUploadComplete: (uploadedUrl: string) => {
|
||||
if (uploadedUrl) {
|
||||
setUrl(uploadedUrl);
|
||||
cancelPendingConversion();
|
||||
setLinkValue(uploadedUrl);
|
||||
if (urlRef.current) {
|
||||
urlRef.current.value = uploadedUrl;
|
||||
}
|
||||
if (isInPostView) {
|
||||
setPublishReplyOptions({ link: uploadedUrl });
|
||||
} else {
|
||||
setPublishPostOptions({ link: uploadedUrl });
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
const handleOekakiClearUploadedUrl = useCallback(
|
||||
(uploadedUrl: string) => {
|
||||
if ((urlRef.current?.value || url) !== uploadedUrl) return;
|
||||
setUrl('');
|
||||
if (urlRef.current) {
|
||||
urlRef.current.value = '';
|
||||
}
|
||||
if (isInPostView) {
|
||||
setPublishReplyOptions({ link: '' });
|
||||
} else {
|
||||
setPublishPostOptions({ link: '' });
|
||||
}
|
||||
},
|
||||
[isInPostView, setPublishPostOptions, setPublishReplyOptions, url],
|
||||
);
|
||||
const handleOekakiClearUploadedUrl = (uploadedUrl: string) => {
|
||||
if ((urlRef.current?.value || url) !== uploadedUrl) return;
|
||||
cancelPendingConversion();
|
||||
setLinkValue('');
|
||||
if (urlRef.current) {
|
||||
urlRef.current.value = '';
|
||||
}
|
||||
};
|
||||
const uploadMode = useMediaHostingStore((state) => state.uploadMode);
|
||||
const showUploadControls = getShowUploadControls(uploadMode, isWebRuntime());
|
||||
|
||||
@@ -846,10 +864,10 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
||||
lengthError={lengthError}
|
||||
handleContentChange={handleContentChange}
|
||||
handleContentValueChange={handleContentValueChange}
|
||||
handleLinkChange={handleLinkChange}
|
||||
handleOptionsChange={handleOptionsChange}
|
||||
setPublishPostOptions={setPublishPostOptions}
|
||||
setPublishReplyOptions={setPublishReplyOptions}
|
||||
setUrl={setUrl}
|
||||
isUploading={isUploading}
|
||||
uploadedFileName={uploadedFileName}
|
||||
showUploadControls={showUploadControls}
|
||||
@@ -881,7 +899,11 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
||||
</tbody>
|
||||
</table>
|
||||
{moderationPostingWarning ? <div className={`${styles.error} ${styles.formError}`}>{moderationPostingWarning}</div> : null}
|
||||
{formError ? (
|
||||
{youtubeThumbnailConversionCountdown !== null ? (
|
||||
<div className={`${styles.error} ${styles.formError}`} aria-live='polite'>
|
||||
{t('youtube_thumbnail_link_conversion_notice', { count: youtubeThumbnailConversionCountdown })}
|
||||
</div>
|
||||
) : formError ? (
|
||||
<div className={`${styles.error} ${styles.formError}`}>
|
||||
{isPostOptionsValidationError(formError) ? <PostOptionsErrorMessage error={formError} directories={directories} /> : formError}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user