fix(posting): clarify direct file links

This commit is contained in:
Tommaso Casaburi
2026-05-01 18:03:04 +07:00
parent 9d2f9445a3
commit bc213ecea2
6 changed files with 77 additions and 12 deletions
@@ -179,6 +179,7 @@ vi.mock('../../../hooks/use-file-upload', () => ({
}));
vi.mock('../../../lib/utils/media-utils', () => ({
getDisplayMediaInfoType: (type: string, t: (key: string) => string) => t(type),
getLinkMediaInfo: (link: string) => {
if (link.endsWith('.gif')) {
return { type: 'gif', url: link };
@@ -186,6 +187,9 @@ vi.mock('../../../lib/utils/media-utils', () => ({
if (link.endsWith('.png')) {
return { type: 'image', url: link };
}
if (link.endsWith('.mp4')) {
return { type: 'video', url: link };
}
return { type: 'link', url: link };
},
}));
@@ -252,7 +256,9 @@ const clickByText = async (scope: ParentNode, text: string, index = 0) => {
const dispatchInput = async (element: HTMLInputElement | HTMLTextAreaElement, value: string) => {
await act(async () => {
element.value = value;
const prototype = element instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
const descriptor = Object.getOwnPropertyDescriptor(prototype, 'value');
descriptor?.set?.call(element, value);
element.dispatchEvent(new Event('input', { bubbles: true }));
element.dispatchEvent(new Event('change', { bubbles: true }));
});
@@ -379,6 +385,7 @@ describe('PostForm', () => {
expect(nameInput).toBeTruthy();
expect(subjectInput).toBeTruthy();
expect(linkInput).toBeTruthy();
expect(linkInput?.getAttribute('placeholder')).toBe('https://website.com/image.jpg');
expect(textarea).toBeTruthy();
expect(select).toBeTruthy();
@@ -409,6 +416,23 @@ describe('PostForm', () => {
expect(testState.setPublishPostOptionsMock).toHaveBeenCalledWith({ communityAddress: 'music-posting.eth' });
});
it('shows the pasted file-link filename next to the upload button', async () => {
testState.uploadedFileName = null;
await renderPostForm('/all');
await clickByText(container, 'start_new_thread');
const table = container.querySelector('table');
const textInputs = table?.querySelectorAll<HTMLInputElement>('input[type="text"]') || [];
const linkInput = textInputs[2];
expect(table?.textContent).toContain('no_file_chosen');
await dispatchInput(linkInput as HTMLInputElement, 'https://example.com/images/file%20name.jpg?size=large');
expect(table?.textContent).toContain('file name.jpg');
});
it('redirects to the pending route when a post publish index is already available on mount', async () => {
testState.postIndex = 7;
testState.resolvedCommunityAddress = 'music-posting.eth';
@@ -480,7 +504,12 @@ describe('LinkTypePreviewer', () => {
await act(async () => {
root.render(createElement(LinkTypePreviewer, { link: 'https://example.com/file.gif' }));
});
expect(container.textContent).toBe('animated_gif');
expect(container.textContent).toBe('type: animated_gif');
await act(async () => {
root.render(createElement(LinkTypePreviewer, { link: 'https://example.com/file.mp4' }));
});
expect(container.textContent).toBe('type: video');
await act(async () => {
root.render(createElement(LinkTypePreviewer, { link: 'not-a-url' }));
+9 -4
View File
@@ -5,8 +5,8 @@ import { useLocation, useNavigate, useParams } from 'react-router-dom';
import { Comment, setAccount, useAccount, useEditedComment } from '@bitsocial/bitsocial-react-hooks';
import getShortAddress from '../../lib/get-short-address';
import useCommunitiesPagesStore from '@bitsocial/bitsocial-react-hooks/dist/stores/communities-pages';
import { getLinkMediaInfo } from '../../lib/utils/media-utils';
import { isValidPublishURL, isValidURL } from '../../lib/utils/url-utils';
import { getDisplayMediaInfoType, getLinkMediaInfo } from '../../lib/utils/media-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';
import { useDirectories, useDirectoryByAddress } from '../../hooks/use-directories';
@@ -25,6 +25,8 @@ import styles from './post-form.module.css';
import capitalize from 'lodash/capitalize';
import debounce from 'lodash/debounce';
const FILE_LINK_PLACEHOLDER = 'https://website.com/image.jpg';
export const LinkTypePreviewer = ({ link }: { link: string }) => {
const { t } = useTranslation();
const mediaInfo = getLinkMediaInfo(link);
@@ -35,9 +37,11 @@ export const LinkTypePreviewer = ({ link }: { link: string }) => {
type = t('animated_gif');
} else if (type === 'gif') {
type = t('gif');
} else if (type) {
type = getDisplayMediaInfoType(type, t);
}
return isValidURL(link) ? type : t('invalid_url');
return isValidURL(link) ? `type: ${type}` : t('invalid_url');
};
const PostFormActions = ({
@@ -224,6 +228,7 @@ const PostFormFields = ({
autoCorrect='off'
autoComplete='off'
spellCheck='false'
placeholder={requirePostLinkIsMedia ? FILE_LINK_PLACEHOLDER : undefined}
ref={urlRef}
disabled={isUploading}
onChange={(e) => {
@@ -253,7 +258,7 @@ const PostFormFields = ({
isUploading={isUploading}
showUploadControls={showUploadControls}
/>
<span>{isUploading ? t('uploading') : uploadedFileName || t('no_file_chosen')}</span>
<span>{isUploading ? t('uploading') : getPublishURLFilename(url) || uploadedFileName || t('no_file_chosen')}</span>
</td>
</tr>
)}