fix(post form): block temporary media links

This commit is contained in:
Tommaso Casaburi
2026-05-16 16:11:22 +07:00
parent 70cf337895
commit a86c523d17
43 changed files with 164 additions and 38 deletions
@@ -52,7 +52,7 @@ const testState = vi.hoisted(() => ({
vi.mock('react-i18next', () => ({
useTranslation: () => ({
t: (key: string) => key,
t: (key: string, options?: Record<string, unknown>) => (options?.domain ? `${key}:${options.domain}` : key),
}),
}));
@@ -401,8 +401,14 @@ describe('PostForm', () => {
expect(globalThis.alert).toHaveBeenCalledWith('invalid_url_alert');
(globalThis.alert as ReturnType<typeof vi.fn>).mockClear();
await dispatchInput(linkInput as HTMLInputElement, '');
await dispatchInput(textarea as HTMLTextAreaElement, 'A valid body');
await dispatchInput(linkInput as HTMLInputElement, 'https://i.4cdn.org/gif/file.jpg');
await clickByText(table as HTMLTableElement, 'post');
expect(globalThis.alert).not.toHaveBeenCalled();
expect(container.textContent).toContain('error: expiring_media_link_alert:i.4cdn.org');
expect(testState.publishPostMock).not.toHaveBeenCalled();
await dispatchInput(linkInput as HTMLInputElement, '');
await clickByText(table as HTMLTableElement, 'post');
expect(globalThis.alert).toHaveBeenCalledWith('no_board_selected_warning');
@@ -188,6 +188,11 @@
color: red;
}
.formError {
margin: 8px 0;
padding: 6px 5px;
}
.status {
padding: 5px;
}
+17 -2
View File
@@ -6,6 +6,7 @@ import { Comment, setAccount, useAccount, useEditedComment } from '@bitsocial/bi
import getShortAddress from '../../lib/get-short-address';
import useCommunitiesPagesStore from '@bitsocial/bitsocial-react-hooks/dist/stores/communities-pages';
import { getDisplayMediaInfoType, getLinkMediaInfo } from '../../lib/utils/media-utils';
import { getExpiringMediaLinkAlert } from '../../lib/utils/media-link-validation-utils';
import { getPublishURLFilename, isValidPublishURL, isValidURL } from '../../lib/utils/url-utils';
import { isAllView, isCatalogView, isModQueueView, isModView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils';
import { useAccountCommunityAddresses } from '../../hooks/use-account-community-addresses';
@@ -346,6 +347,7 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
const accountCommunityAddresses = useAccountCommunityAddresses();
const [lengthError, setLengthError] = useState<string | null>(null);
const [formError, setFormError] = useState<string | null>(null);
const checkContentLength = useRef(
debounce((content: string, t: TFunction) => {
@@ -377,6 +379,7 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
checkContentLength.cancel();
setLengthError(null);
setFormError(null);
if (!currentTitle && !currentContent && !currentUrl) {
alert(t('empty_comment_alert'));
@@ -386,6 +389,11 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
alert(t('invalid_url_alert'));
return;
}
const expiringMediaLinkAlert = currentUrl ? getExpiringMediaLinkAlert(currentUrl, t) : null;
if (expiringMediaLinkAlert) {
setFormError(expiringMediaLinkAlert);
return;
}
if (currentContent.length > 2000) {
alert(t('error') + ': ' + t('field_too_long'));
@@ -432,6 +440,7 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
checkContentLength.cancel();
setLengthError(null);
setFormError(null);
if (!currentContent && !currentUrl) {
alert(t('empty_comment_alert'));
@@ -442,6 +451,11 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
alert(t('invalid_url_alert'));
return;
}
const expiringMediaLinkAlert = currentUrl ? getExpiringMediaLinkAlert(currentUrl, t) : null;
if (expiringMediaLinkAlert) {
setFormError(expiringMediaLinkAlert);
return;
}
if (currentContent.length > 2000) {
alert(t('error') + ': ' + t('field_too_long'));
@@ -527,8 +541,9 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
/>
</tbody>
</table>
{publishPostError && <div className={styles.error}>{publishPostError}</div>}
{publishReplyError && <div className={styles.error}>{publishReplyError}</div>}
{formError && <div className={`${styles.error} ${styles.formError}`}>{formError}</div>}
{publishPostError && <div className={`${styles.error} ${styles.formError}`}>{publishPostError}</div>}
{publishReplyError && <div className={`${styles.error} ${styles.formError}`}>{publishReplyError}</div>}
{publishReplyStateMessage && <div className={styles.status}>{publishReplyStateMessage}</div>}
</>
);
@@ -414,6 +414,12 @@ describe('ReplyModal', () => {
expect(container.textContent).toContain('error: invalid_url_alert');
expect(testState.setPublishReplyOptionsMock).toHaveBeenCalledWith({ spoiler: true });
await dispatchInput(linkInput, 'https://temp.sh/example.png');
await clickButtonByText('post');
expect(container.textContent).toContain('error: expiring_media_link_alert:{"domain":"temp.sh"}');
expect(testState.publishReplyMock).not.toHaveBeenCalled();
await dispatchInput(linkInput, 'https://example.com/file.png');
await clickButtonByText('post');
@@ -3,6 +3,7 @@ import { useLocation, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import type { TFunction } from 'i18next';
import { setAccount, useAccount } from '@bitsocial/bitsocial-react-hooks';
import { getExpiringMediaLinkAlert } from '../../lib/utils/media-link-validation-utils';
import { getPublishURLFilename, isValidPublishURL } from '../../lib/utils/url-utils';
import { isAllView, isModView, isSubscriptionsView } from '../../lib/utils/view-utils';
import useSelectedTextStore from '../../stores/use-selected-text-store';
@@ -103,6 +104,11 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
setError(t('error') + ': ' + t('invalid_url_alert'));
return;
}
const expiringMediaLinkAlert = currentUrl ? getExpiringMediaLinkAlert(currentUrl, t) : null;
if (expiringMediaLinkAlert) {
setError(expiringMediaLinkAlert);
return;
}
checkContentLengthRef.current.cancel();
setLengthError(null);