diff --git a/app/javascript/mastodon/features/alt_text_modal/__tests__/index-test.tsx b/app/javascript/mastodon/features/alt_text_modal/__tests__/index-test.tsx
new file mode 100644
index 0000000000..709e0ff6c2
--- /dev/null
+++ b/app/javascript/mastodon/features/alt_text_modal/__tests__/index-test.tsx
@@ -0,0 +1,91 @@
+// app/javascript/mastodon/features/alt_text_modal/__tests__/index-test.tsx
+
+import { IntlProvider } from 'react-intl';
+
+import { List, Map } from 'immutable';
+
+import { render } from '@testing-library/react';
+import { vi } from 'vitest';
+
+import type { RootState } from 'mastodon/store';
+import { useAppSelector } from 'mastodon/store';
+
+import { AltTextModal } from '../index';
+
+vi.mock('mastodon/store', () => ({
+ useAppSelector: vi.fn(),
+ useAppDispatch: () => vi.fn(),
+}));
+
+describe('', () => {
+ const mediaId = '123';
+ const handleClose = vi.fn();
+
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ const renderComponent = () => {
+ return render(
+
+
+ ,
+ );
+ };
+
+ it('renders thumbnail upload button when video is unattached', () => {
+ vi.mocked(useAppSelector).mockImplementation(
+ (selector: (state: RootState) => unknown) => {
+ const mockState = {
+ compose: Map({
+ language: 'en',
+ media_attachments: List([
+ Map({
+ id: mediaId,
+ type: 'video',
+ unattached: true,
+ meta: Map({ focus: Map({ x: 0, y: 0 }) }),
+ }),
+ ]),
+ }),
+ accounts: Map(),
+ } as unknown as RootState;
+
+ return selector(mockState);
+ },
+ );
+
+ const { container } = renderComponent();
+
+ const uploadInput = container.querySelector('#upload-modal__thumbnail');
+ expect(uploadInput).not.toBeNull();
+ });
+
+ it('hides thumbnail upload button when video is attached', () => {
+ vi.mocked(useAppSelector).mockImplementation(
+ (selector: (state: RootState) => unknown) => {
+ const mockState = {
+ compose: Map({
+ language: 'en',
+ media_attachments: List([
+ Map({
+ id: mediaId,
+ type: 'video',
+ unattached: false,
+ meta: Map({ focus: Map({ x: 0, y: 0 }) }),
+ }),
+ ]),
+ }),
+ accounts: Map(),
+ } as unknown as RootState;
+
+ return selector(mockState);
+ },
+ );
+
+ const { container } = renderComponent();
+
+ const uploadInput = container.querySelector('#upload-modal__thumbnail');
+ expect(uploadInput).toBeNull();
+ });
+});
diff --git a/app/javascript/mastodon/features/alt_text_modal/index.tsx b/app/javascript/mastodon/features/alt_text_modal/index.tsx
index 3c54fa4724..fc00e60a51 100644
--- a/app/javascript/mastodon/features/alt_text_modal/index.tsx
+++ b/app/javascript/mastodon/features/alt_text_modal/index.tsx
@@ -283,6 +283,7 @@ export const AltTextModal = forwardRef>(
);
const type = media?.get('type') as string;
const valid = length(description) <= MAX_LENGTH;
+ const unattached = media?.get('unattached') as boolean | undefined;
const handleDescriptionChange = useCallback(
(e: React.ChangeEvent) => {
@@ -433,7 +434,8 @@ export const AltTextModal = forwardRef>(
onPositionChange={handlePositionChange}
/>
- {(type === 'audio' || type === 'video') && (
+ {/* This button is hidden for attached audio/video files, as they are already posted */}
+ {(type === 'audio' || type === 'video') && unattached && (