fix(post form): reject non-media file links

This commit is contained in:
Tommaso Casaburi
2026-06-03 17:10:37 +07:00
parent 188c44a923
commit 95f3a54b9d
38 changed files with 171 additions and 43 deletions
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "تم اكتشاف رابط YouTube. جارٍ لصق الصورة المصغرة خلال {{count}}...",
"stats_scope_all_listed_boards": "جميع اللوحات المُدرجة (بطيء)",
"stats_scope_directory_boards": "لوحات الدليل فقط (سريع)",
"boards_loaded": "اللوحات المحملة:"
"boards_loaded": "اللوحات المحملة:",
"link_not_image_or_video_alert": "الرابط ليس صورة أو فيديو.",
"not_a_file": "ليس ملفًا"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "YouTube লিংক শনাক্ত হয়েছে। {{count}}-এর মধ্যে থাম্বনেইল ছবি পেস্ট করা হচ্ছে...",
"stats_scope_all_listed_boards": "সব তালিকাভুক্ত বোর্ড (ধীর)",
"stats_scope_directory_boards": "শুধু ডিরেক্টরি বোর্ড (দ্রুত)",
"boards_loaded": "লোড করা বোর্ড:"
"boards_loaded": "লোড করা বোর্ড:",
"link_not_image_or_video_alert": "লিংকটি ছবি বা ভিডিও নয়।",
"not_a_file": "ফাইল নয়"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "Byl zjištěn odkaz na YouTube. Vkládám náhledový obrázek za {{count}}...",
"stats_scope_all_listed_boards": "Všechny uvedené nástěnky (pomalé)",
"stats_scope_directory_boards": "Pouze nástěnky v adresáři (rychlé)",
"boards_loaded": "načtené nástěnky:"
"boards_loaded": "načtené nástěnky:",
"link_not_image_or_video_alert": "Odkaz není obrázek ani video.",
"not_a_file": "není soubor"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "YouTube-link registreret. Indsætter miniaturebillede om {{count}}...",
"stats_scope_all_listed_boards": "Alle viste boards (langsomt)",
"stats_scope_directory_boards": "Kun boards i kataloget (hurtigt)",
"boards_loaded": "indlæste boards:"
"boards_loaded": "indlæste boards:",
"link_not_image_or_video_alert": "Linket er ikke et billede eller en video.",
"not_a_file": "ikke en fil"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "YouTube-Link erkannt. Vorschaubild wird in {{count}} eingefügt...",
"stats_scope_all_listed_boards": "Alle aufgelisteten Boards (langsam)",
"stats_scope_directory_boards": "Nur Verzeichnis-Boards (schnell)",
"boards_loaded": "geladene Boards:"
"boards_loaded": "geladene Boards:",
"link_not_image_or_video_alert": "Der Link ist kein Bild oder Video.",
"not_a_file": "keine Datei"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "Εντοπίστηκε σύνδεσμος YouTube. Επικόλληση μικρογραφίας σε {{count}}...",
"stats_scope_all_listed_boards": "Όλα τα καταχωρισμένα boards (αργό)",
"stats_scope_directory_boards": "Μόνο boards καταλόγου (γρήγορο)",
"boards_loaded": "φορτωμένοι πίνακες:"
"boards_loaded": "φορτωμένοι πίνακες:",
"link_not_image_or_video_alert": "Ο σύνδεσμος δεν είναι εικόνα ή βίντεο.",
"not_a_file": "δεν είναι αρχείο"
}
+3 -1
View File
@@ -422,5 +422,7 @@
"youtube_thumbnail_link_conversion_notice": "YouTube link detected. Pasting thumbnail image in {{count}}...",
"stats_scope_all_listed_boards": "All listed boards (slow)",
"stats_scope_directory_boards": "Directory boards only (fast)",
"boards_loaded": "boards loaded:"
"boards_loaded": "boards loaded:",
"link_not_image_or_video_alert": "Link is not an image or video.",
"not_a_file": "not a file"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "Se detectó un enlace de YouTube. Pegando imagen en miniatura en {{count}}...",
"stats_scope_all_listed_boards": "Todos los boards listados (lento)",
"stats_scope_directory_boards": "Solo boards del directorio (rápido)",
"boards_loaded": "tableros cargados:"
"boards_loaded": "tableros cargados:",
"link_not_image_or_video_alert": "El enlace no es una imagen ni un video.",
"not_a_file": "no es un archivo"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "پیوند YouTube شناسایی شد. در حال چسباندن تصویر بندانگشتی در {{count}}...",
"stats_scope_all_listed_boards": "همهٔ بردهای فهرست‌شده (کند)",
"stats_scope_directory_boards": "فقط بردهای دایرکتوری (سریع)",
"boards_loaded": "بردهای بارگذاری‌شده:"
"boards_loaded": "بردهای بارگذاری‌شده:",
"link_not_image_or_video_alert": "پیوند تصویر یا ویدیو نیست.",
"not_a_file": "فایل نیست"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "YouTube-linkki havaittu. Liitetään pikkukuva {{count}} kuluttua...",
"stats_scope_all_listed_boards": "Kaikki listatut boardit (hidas)",
"stats_scope_directory_boards": "Vain hakemiston boardit (nopea)",
"boards_loaded": "ladatut boardit:"
"boards_loaded": "ladatut boardit:",
"link_not_image_or_video_alert": "Linkki ei ole kuva tai video.",
"not_a_file": "ei ole tiedosto"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "May nakita na link ng YouTube. Ipi-paste ang thumbnail image sa loob ng {{count}}...",
"stats_scope_all_listed_boards": "Lahat ng nakalistang board (mabagal)",
"stats_scope_directory_boards": "Mga board ng directory lang (mabilis)",
"boards_loaded": "mga board na na-load:"
"boards_loaded": "mga board na na-load:",
"link_not_image_or_video_alert": "Ang link ay hindi larawan o video.",
"not_a_file": "hindi file"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "Lien YouTube détecté. Collage de l'image miniature dans {{count}}...",
"stats_scope_all_listed_boards": "Tous les boards listés (lent)",
"stats_scope_directory_boards": "Boards de l'annuaire uniquement (rapide)",
"boards_loaded": "boards chargés :"
"boards_loaded": "boards chargés :",
"link_not_image_or_video_alert": "Le lien n'est pas une image ni une vidéo.",
"not_a_file": "pas un fichier"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "זוהה קישור YouTube. מדביק תמונת ממוזערת בעוד {{count}}...",
"stats_scope_all_listed_boards": "כל הלוחות הרשומים (איטי)",
"stats_scope_directory_boards": "רק לוחות מהספרייה (מהיר)",
"boards_loaded": "לוחות שנטענו:"
"boards_loaded": "לוחות שנטענו:",
"link_not_image_or_video_alert": "הקישור אינו תמונה או וידאו.",
"not_a_file": "לא קובץ"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "YouTube लिंक मिला। {{count}} में थंबनेल इमेज पेस्ट की जा रही है...",
"stats_scope_all_listed_boards": "सभी सूचीबद्ध बोर्ड (धीमा)",
"stats_scope_directory_boards": "सिर्फ डायरेक्टरी बोर्ड (तेज़)",
"boards_loaded": "लोड किए गए बोर्ड:"
"boards_loaded": "लोड किए गए बोर्ड:",
"link_not_image_or_video_alert": "लिंक कोई छवि या वीडियो नहीं है.",
"not_a_file": "फ़ाइल नहीं है"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "YouTube-link észlelve. Bélyegkép beillesztése {{count}} múlva...",
"stats_scope_all_listed_boards": "Az összes listázott board (lassú)",
"stats_scope_directory_boards": "Csak a könyvtár boardjai (gyors)",
"boards_loaded": "betöltött boardok:"
"boards_loaded": "betöltött boardok:",
"link_not_image_or_video_alert": "A hivatkozás nem kép vagy videó.",
"not_a_file": "nem fájl"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "Tautan YouTube terdeteksi. Menempelkan gambar thumbnail dalam {{count}}...",
"stats_scope_all_listed_boards": "Semua board yang terdaftar (lambat)",
"stats_scope_directory_boards": "Hanya board direktori (cepat)",
"boards_loaded": "board dimuat:"
"boards_loaded": "board dimuat:",
"link_not_image_or_video_alert": "Tautan bukan gambar atau video.",
"not_a_file": "bukan file"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "Link YouTube rilevato. Incollando l'immagine di anteprima tra {{count}}...",
"stats_scope_all_listed_boards": "Tutte le board elencate (lento)",
"stats_scope_directory_boards": "Solo board della directory (veloce)",
"boards_loaded": "board caricati:"
"boards_loaded": "board caricati:",
"link_not_image_or_video_alert": "Il link non è un'immagine né un video.",
"not_a_file": "non è un file"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "YouTubeリンクを検出しました。{{count}}後にサムネイル画像を貼り付けます...",
"stats_scope_all_listed_boards": "掲載されているすべての掲示板(低速)",
"stats_scope_directory_boards": "ディレクトリの掲示板のみ(高速)",
"boards_loaded": "読み込まれた板:"
"boards_loaded": "読み込まれた板:",
"link_not_image_or_video_alert": "リンクは画像または動画ではありません。",
"not_a_file": "ファイルではありません"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "YouTube 링크가 감지되었습니다. {{count}} 후에 썸네일 이미지를 붙여넣는 중...",
"stats_scope_all_listed_boards": "등록된 모든 게시판 (느림)",
"stats_scope_directory_boards": "디렉터리 게시판만 (빠름)",
"boards_loaded": "불러온 게시판:"
"boards_loaded": "불러온 게시판:",
"link_not_image_or_video_alert": "링크가 이미지 또는 동영상이 아닙니다.",
"not_a_file": "파일이 아님"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "YouTube लिंक आढळली. {{count}} मध्ये थंबनेल प्रतिमा पेस्ट करत आहे...",
"stats_scope_all_listed_boards": "सर्व सूचीबद्ध बोर्ड (मंद)",
"stats_scope_directory_boards": "फक्त निर्देशिका बोर्ड (जलद)",
"boards_loaded": "लोड केलेले बोर्ड:"
"boards_loaded": "लोड केलेले बोर्ड:",
"link_not_image_or_video_alert": "लिंक प्रतिमा किंवा व्हिडिओ नाही.",
"not_a_file": "फाइल नाही"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "YouTube-link gedetecteerd. Miniatuurafbeelding wordt over {{count}} geplakt...",
"stats_scope_all_listed_boards": "Alle vermelde boards (traag)",
"stats_scope_directory_boards": "Alleen directory-boards (snel)",
"boards_loaded": "geladen boards:"
"boards_loaded": "geladen boards:",
"link_not_image_or_video_alert": "De link is geen afbeelding of video.",
"not_a_file": "geen bestand"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "YouTube-lenke oppdaget. Limer inn miniatyrbilde om {{count}}...",
"stats_scope_all_listed_boards": "Alle oppførte boards (tregt)",
"stats_scope_directory_boards": "Bare katalog-boards (raskt)",
"boards_loaded": "lastede boards:"
"boards_loaded": "lastede boards:",
"link_not_image_or_video_alert": "Lenken er ikke et bilde eller en video.",
"not_a_file": "ikke en fil"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "Wykryto link YouTube. Wklejam miniaturę za {{count}}...",
"stats_scope_all_listed_boards": "Wszystkie wymienione boardy (wolno)",
"stats_scope_directory_boards": "Tylko boardy z katalogu (szybko)",
"boards_loaded": "załadowane boardy:"
"boards_loaded": "załadowane boardy:",
"link_not_image_or_video_alert": "Link nie jest obrazem ani filmem.",
"not_a_file": "to nie plik"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "Link do YouTube detectado. Colando imagem em miniatura em {{count}}...",
"stats_scope_all_listed_boards": "Todos os boards listados (lento)",
"stats_scope_directory_boards": "Somente boards do diretório (rápido)",
"boards_loaded": "boards carregados:"
"boards_loaded": "boards carregados:",
"link_not_image_or_video_alert": "O link não é uma imagem nem um vídeo.",
"not_a_file": "não é um arquivo"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "Link YouTube detectat. Lipesc imaginea miniatură în {{count}}...",
"stats_scope_all_listed_boards": "Toate boardurile listate (lent)",
"stats_scope_directory_boards": "Doar boardurile din director (rapid)",
"boards_loaded": "boarduri încărcate:"
"boards_loaded": "boarduri încărcate:",
"link_not_image_or_video_alert": "Linkul nu este o imagine sau un videoclip.",
"not_a_file": "nu este fișier"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "Обнаружена ссылка YouTube. Вставка миниатюры через {{count}}...",
"stats_scope_all_listed_boards": "Все перечисленные доски (медленно)",
"stats_scope_directory_boards": "Только доски каталога (быстро)",
"boards_loaded": "загруженные доски:"
"boards_loaded": "загруженные доски:",
"link_not_image_or_video_alert": "Ссылка не является изображением или видео.",
"not_a_file": "не файл"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "U zbulua lidhje YouTube. Po ngjis imazhin miniaturë pas {{count}}...",
"stats_scope_all_listed_boards": "Të gjitha bordet e listuara (ngadalë)",
"stats_scope_directory_boards": "Vetëm bordet e drejtorisë (shpejt)",
"boards_loaded": "bordet e ngarkuara:"
"boards_loaded": "bordet e ngarkuara:",
"link_not_image_or_video_alert": "Lidhja nuk është imazh ose video.",
"not_a_file": "nuk është skedar"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "YouTube-länk upptäckt. Klistrar in miniatyrbild om {{count}}...",
"stats_scope_all_listed_boards": "Alla listade boards (långsamt)",
"stats_scope_directory_boards": "Endast katalogens boards (snabbt)",
"boards_loaded": "laddade boards:"
"boards_loaded": "laddade boards:",
"link_not_image_or_video_alert": "Länken är inte en bild eller video.",
"not_a_file": "inte en fil"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "YouTube లింక్ గుర్తించబడింది. {{count}}లో థంబ్‌నెయిల్ చిత్రాన్ని పేస్ట్ చేస్తున్నాము...",
"stats_scope_all_listed_boards": "జాబితాలో ఉన్న అన్ని బోర్డులు (నెమ్మదిగా)",
"stats_scope_directory_boards": "డైరెక్టరీ బోర్డులు మాత్రమే (వేగంగా)",
"boards_loaded": "లోడ్ చేసిన బోర్డులు:"
"boards_loaded": "లోడ్ చేసిన బోర్డులు:",
"link_not_image_or_video_alert": "లింక్ చిత్రం లేదా వీడియో కాదు.",
"not_a_file": "ఫైల్ కాదు"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "ตรวจพบลิงก์ YouTube กำลังวางรูปภาพขนาดย่อในอีก {{count}}...",
"stats_scope_all_listed_boards": "บอร์ดที่แสดงทั้งหมด (ช้า)",
"stats_scope_directory_boards": "เฉพาะบอร์ดในไดเรกทอรี (เร็ว)",
"boards_loaded": "บอร์ดที่โหลดแล้ว:"
"boards_loaded": "บอร์ดที่โหลดแล้ว:",
"link_not_image_or_video_alert": "ลิงก์ไม่ใช่รูปภาพหรือวิดีโอ",
"not_a_file": "ไม่ใช่ไฟล์"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "YouTube bağlantısı algılandı. Küçük resim {{count}} içinde yapıştırılıyor...",
"stats_scope_all_listed_boards": "Listelenen tüm panolar (yavaş)",
"stats_scope_directory_boards": "Yalnızca dizin panolari (hızlı)",
"boards_loaded": "yüklenen panolar:"
"boards_loaded": "yüklenen panolar:",
"link_not_image_or_video_alert": "Bağlantı bir görsel veya video değil.",
"not_a_file": "dosya değil"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "Виявлено посилання YouTube. Вставляю мініатюру через {{count}}...",
"stats_scope_all_listed_boards": "Усі перелічені дошки (повільно)",
"stats_scope_directory_boards": "Лише дошки каталогу (швидко)",
"boards_loaded": "завантажені дошки:"
"boards_loaded": "завантажені дошки:",
"link_not_image_or_video_alert": "Посилання не є зображенням або відео.",
"not_a_file": "не файл"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "YouTube لنک مل گیا۔ {{count}} میں تھمب نیل تصویر پیسٹ کی جا رہی ہے...",
"stats_scope_all_listed_boards": "تمام فہرست شدہ بورڈز (سست)",
"stats_scope_directory_boards": "صرف ڈائریکٹری بورڈز (تیز)",
"boards_loaded": "لوڈ شدہ بورڈز:"
"boards_loaded": "لوڈ شدہ بورڈز:",
"link_not_image_or_video_alert": "لنک تصویر یا ویڈیو نہیں ہے۔",
"not_a_file": "فائل نہیں ہے"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "Đã phát hiện liên kết YouTube. Đang dán ảnh thu nhỏ sau {{count}}...",
"stats_scope_all_listed_boards": "Tất cả bảng được liệt kê (chậm)",
"stats_scope_directory_boards": "Chỉ bảng trong thư mục (nhanh)",
"boards_loaded": "bảng đã tải:"
"boards_loaded": "bảng đã tải:",
"link_not_image_or_video_alert": "Liên kết không phải là hình ảnh hoặc video.",
"not_a_file": "không phải tệp"
}
+3 -1
View File
@@ -403,5 +403,7 @@
"youtube_thumbnail_link_conversion_notice": "检测到 YouTube 链接。将在 {{count}} 后粘贴缩略图...",
"stats_scope_all_listed_boards": "所有已列出的版块(较慢)",
"stats_scope_directory_boards": "仅目录板块(快速)",
"boards_loaded": "已加载的版块:"
"boards_loaded": "已加载的版块:",
"link_not_image_or_video_alert": "链接不是图片或视频。",
"not_a_file": "不是文件"
}
@@ -319,13 +319,19 @@ vi.mock('../../../lib/utils/media-utils', () => ({
if (link.endsWith('.gif')) {
return { type: 'gif', url: link };
}
if (link.endsWith('.png')) {
if (link.endsWith('.jpg') || link.endsWith('.jpeg') || link.endsWith('.png')) {
return { type: 'image', url: link };
}
if (link.endsWith('.mp4')) {
return { type: 'video', url: link };
}
return { type: 'link', url: link };
if (link.endsWith('.mp3')) {
return { type: 'audio', url: link };
}
if (link.includes('youtube.com')) {
return { patternThumbnailUrl: 'https://img.youtube.com/vi/abc123/0.jpg', type: 'iframe', url: link };
}
return { type: 'webpage', url: link };
},
getYouTubeThumbnailUrlFromLink: (link: string) => {
try {
@@ -617,6 +623,12 @@ describe('PostForm', () => {
expect(globalThis.alert).not.toHaveBeenCalled();
expect(container.textContent).toContain('error: invalid_url_alert');
await dispatchInput(linkInput as HTMLInputElement, 'https://example.com/page');
await clickByText(table as HTMLTableElement, 'post');
expect(globalThis.alert).not.toHaveBeenCalled();
expect(container.textContent).toContain('error: link_not_image_or_video_alert');
expect(testState.publishPostMock).not.toHaveBeenCalled();
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');
@@ -668,6 +680,7 @@ describe('PostForm', () => {
await dispatchInput(linkInput, youtubeLink);
expect(linkInput.value).toBe(youtubeLink);
expect(linkInput.disabled).toBe(true);
expect(container.textContent).toContain('youtube_thumbnail_link_conversion_notice');
expect(container.textContent).toContain('3');
expect(table.textContent).not.toContain('youtube_thumbnail_link_conversion_notice');
@@ -677,17 +690,20 @@ describe('PostForm', () => {
vi.advanceTimersByTime(1000);
});
expect(container.textContent).toContain('2');
expect(linkInput.disabled).toBe(true);
await act(async () => {
vi.advanceTimersByTime(1000);
});
expect(container.textContent).toContain('1');
expect(linkInput.disabled).toBe(true);
await act(async () => {
vi.advanceTimersByTime(1000);
});
expect(linkInput.value).toBe(thumbnailLink);
expect(linkInput.disabled).toBe(false);
expect(textarea.value).toBe(youtubeLink);
expect(container.textContent).not.toContain('youtube_thumbnail_link_conversion_notice');
} finally {
@@ -1433,16 +1449,34 @@ describe('LinkTypePreviewer', () => {
await act(async () => {
root.render(createElement(LinkTypePreviewer, { link: 'https://example.com/file.gif' }));
});
expect(container.textContent).toBe('type: animated_gif');
expect(container.textContent).toBe('file: animated_gif');
await act(async () => {
root.render(createElement(LinkTypePreviewer, { link: 'https://example.com/file.mp4' }));
});
expect(container.textContent).toBe('type: video');
expect(container.textContent).toBe('file: video');
await act(async () => {
root.render(createElement(LinkTypePreviewer, { link: 'not-a-url' }));
});
expect(container.textContent).toBe('invalid_url');
});
it('shows unsupported file links as not a file on media-only forms', async () => {
await act(async () => {
root.render(createElement(LinkTypePreviewer, { link: 'https://example.com/file.mp3', requireFile: true }));
});
expect(container.textContent).toBe('not_a_file');
expect(container.querySelector('span')?.className).toContain('linkTypeError');
await act(async () => {
root.render(createElement(LinkTypePreviewer, { link: 'https://www.youtube.com/watch?v=abc123', requireFile: true }));
});
expect(container.textContent).toBe('not_a_file');
await act(async () => {
root.render(createElement(LinkTypePreviewer, { link: 'https://example.com/page', requireFile: true }));
});
expect(container.textContent).toBe('not_a_file');
});
});
@@ -113,6 +113,10 @@
text-transform: lowercase;
}
.linkTypeError {
color: red;
}
.linkField input[type="text"] {
width: 190px !important;
}
+24 -4
View File
@@ -55,6 +55,7 @@ import debounce from 'lodash/debounce';
const FILE_LINK_PLACEHOLDER = 'https://website.com/image.jpg';
const POST_FORM_FILE_DISPLAY_MAX_LENGTH = 28;
const POST_FORM_FILE_MEDIA_TYPES = new Set(['gif', 'image', 'video']);
const mergeFlairs = (...flairGroups: Array<Comment['flairs'] | undefined>): Comment['flairs'] | undefined => {
const flairs = flairGroups.flatMap((group) => (Array.isArray(group) ? group : []));
@@ -67,12 +68,20 @@ const getPostFormFileDisplayLabel = (url: string, uploadedFileName: string | nul
return truncateWithEllipsisInMiddle(raw, POST_FORM_FILE_DISPLAY_MAX_LENGTH);
};
export const LinkTypePreviewer = ({ link }: { link: string }) => {
const isPostFormFileMediaType = (type: string | undefined): boolean => Boolean(type && POST_FORM_FILE_MEDIA_TYPES.has(type));
const isPostFormFileMediaLink = (link: string): boolean => isPostFormFileMediaType(getLinkMediaInfo(link)?.type);
export const LinkTypePreviewer = ({ link, requireFile = false }: { link: string; requireFile?: boolean }) => {
const { t } = useTranslation();
const mediaInfo = getLinkMediaInfo(link);
let type = mediaInfo?.type;
const { status: gifFrameStatus } = useFetchGifFirstFrame(type === 'gif' ? mediaInfo?.url : undefined);
if (requireFile && isValidURL(link) && !isPostFormFileMediaType(type)) {
return <span className={styles.linkTypeError}>{t('not_a_file')}</span>;
}
if (type === 'gif' && gifFrameStatus === 'ready') {
type = t('animated_gif');
} else if (type === 'gif') {
@@ -81,7 +90,7 @@ export const LinkTypePreviewer = ({ link }: { link: string }) => {
type = getDisplayMediaInfoType(type, t);
}
return isValidURL(link) ? `type: ${type}` : t('invalid_url');
return isValidURL(link) ? `${t('file')}: ${type}` : t('invalid_url');
};
const PostFormActions = ({
@@ -149,6 +158,7 @@ interface PostFormFieldsProps {
handleContentValueChange: (content: string, options?: string) => void;
handleLinkChange: (link: string) => void;
handleOptionsChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
disableLinkInput: boolean;
setPublishPostOptions: (opts: Record<string, unknown>) => void;
setPublishReplyOptions: (opts: Record<string, unknown>) => void;
isUploading: boolean;
@@ -200,6 +210,7 @@ const PostFormFields = ({
handleContentValueChange,
handleLinkChange,
handleOptionsChange,
disableLinkInput,
setPublishPostOptions,
setPublishReplyOptions,
isUploading,
@@ -357,12 +368,12 @@ const PostFormFields = ({
spellCheck='false'
placeholder={requirePostLinkIsMedia ? FILE_LINK_PLACEHOLDER : undefined}
ref={urlRef}
disabled={isUploading}
disabled={disableLinkInput}
onChange={(e) => {
handleLinkChange(e.target.value);
}}
/>
<span className={styles.linkType}> {url && <LinkTypePreviewer link={url} />}</span>
<span className={styles.linkType}> {url && <LinkTypePreviewer link={url} requireFile={requirePostLinkIsMedia} />}</span>
</td>
</tr>
{showUploadControls && (
@@ -633,6 +644,10 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
setFormError(`${t('error')}: ${t('invalid_url_alert')}`);
return;
}
if (currentUrl && requirePostLinkIsMedia && !isPostFormFileMediaLink(currentUrl)) {
setFormError(`${t('error')}: ${t('link_not_image_or_video_alert')}`);
return;
}
const expiringMediaLinkAlert = currentUrl ? getExpiringMediaLinkAlert(currentUrl, t) : null;
if (expiringMediaLinkAlert) {
setFormError(expiringMediaLinkAlert);
@@ -757,6 +772,10 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
setFormError(`${t('error')}: ${t('invalid_url_alert')}`);
return;
}
if (currentUrl && requirePostLinkIsMedia && !isPostFormFileMediaLink(currentUrl)) {
setFormError(`${t('error')}: ${t('link_not_image_or_video_alert')}`);
return;
}
const expiringMediaLinkAlert = currentUrl ? getExpiringMediaLinkAlert(currentUrl, t) : null;
if (expiringMediaLinkAlert) {
setFormError(expiringMediaLinkAlert);
@@ -873,6 +892,7 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
handleContentValueChange={handleContentValueChange}
handleLinkChange={handleLinkChange}
handleOptionsChange={handleOptionsChange}
disableLinkInput={isUploading || youtubeThumbnailConversionCountdown !== null}
setPublishPostOptions={setPublishPostOptions}
setPublishReplyOptions={setPublishReplyOptions}
isUploading={isUploading}