From 95f3a54b9deb2e35143fe16de2fc049ebce2dee9 Mon Sep 17 00:00:00 2001 From: Tommaso Casaburi Date: Wed, 3 Jun 2026 17:10:37 +0700 Subject: [PATCH] fix(post form): reject non-media file links --- public/translations/ar/default.json | 4 +- public/translations/bn/default.json | 4 +- public/translations/cs/default.json | 4 +- public/translations/da/default.json | 4 +- public/translations/de/default.json | 4 +- public/translations/el/default.json | 4 +- public/translations/en/default.json | 4 +- public/translations/es/default.json | 4 +- public/translations/fa/default.json | 4 +- public/translations/fi/default.json | 4 +- public/translations/fil/default.json | 4 +- public/translations/fr/default.json | 4 +- public/translations/he/default.json | 4 +- public/translations/hi/default.json | 4 +- public/translations/hu/default.json | 4 +- public/translations/id/default.json | 4 +- public/translations/it/default.json | 4 +- public/translations/ja/default.json | 4 +- public/translations/ko/default.json | 4 +- public/translations/mr/default.json | 4 +- public/translations/nl/default.json | 4 +- public/translations/no/default.json | 4 +- public/translations/pl/default.json | 4 +- public/translations/pt/default.json | 4 +- public/translations/ro/default.json | 4 +- public/translations/ru/default.json | 4 +- public/translations/sq/default.json | 4 +- public/translations/sv/default.json | 4 +- public/translations/te/default.json | 4 +- public/translations/th/default.json | 4 +- public/translations/tr/default.json | 4 +- public/translations/uk/default.json | 4 +- public/translations/ur/default.json | 4 +- public/translations/vi/default.json | 4 +- public/translations/zh/default.json | 4 +- .../post-form/__tests__/post-form.test.tsx | 42 +++++++++++++++++-- src/components/post-form/post-form.module.css | 4 ++ src/components/post-form/post-form.tsx | 28 +++++++++++-- 38 files changed, 171 insertions(+), 43 deletions(-) diff --git a/public/translations/ar/default.json b/public/translations/ar/default.json index 7b75de37..1da463de 100644 --- a/public/translations/ar/default.json +++ b/public/translations/ar/default.json @@ -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": "ليس ملفًا" } diff --git a/public/translations/bn/default.json b/public/translations/bn/default.json index ba17fba3..d9b2b4ce 100644 --- a/public/translations/bn/default.json +++ b/public/translations/bn/default.json @@ -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": "ফাইল নয়" } diff --git a/public/translations/cs/default.json b/public/translations/cs/default.json index 70f68c45..04673127 100644 --- a/public/translations/cs/default.json +++ b/public/translations/cs/default.json @@ -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" } diff --git a/public/translations/da/default.json b/public/translations/da/default.json index 4a03040d..0397592c 100644 --- a/public/translations/da/default.json +++ b/public/translations/da/default.json @@ -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" } diff --git a/public/translations/de/default.json b/public/translations/de/default.json index 43814ca7..fdf82079 100644 --- a/public/translations/de/default.json +++ b/public/translations/de/default.json @@ -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" } diff --git a/public/translations/el/default.json b/public/translations/el/default.json index 599edb78..71a2eed8 100644 --- a/public/translations/el/default.json +++ b/public/translations/el/default.json @@ -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": "δεν είναι αρχείο" } diff --git a/public/translations/en/default.json b/public/translations/en/default.json index d393d476..4aa844ab 100644 --- a/public/translations/en/default.json +++ b/public/translations/en/default.json @@ -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" } diff --git a/public/translations/es/default.json b/public/translations/es/default.json index 34c39a07..9bf550c3 100644 --- a/public/translations/es/default.json +++ b/public/translations/es/default.json @@ -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" } diff --git a/public/translations/fa/default.json b/public/translations/fa/default.json index c710b6e6..6d210d82 100644 --- a/public/translations/fa/default.json +++ b/public/translations/fa/default.json @@ -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": "فایل نیست" } diff --git a/public/translations/fi/default.json b/public/translations/fi/default.json index f753c021..11ccd79c 100644 --- a/public/translations/fi/default.json +++ b/public/translations/fi/default.json @@ -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" } diff --git a/public/translations/fil/default.json b/public/translations/fil/default.json index 8d5c5318..5484954b 100644 --- a/public/translations/fil/default.json +++ b/public/translations/fil/default.json @@ -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" } diff --git a/public/translations/fr/default.json b/public/translations/fr/default.json index 8ee72bfe..49175495 100644 --- a/public/translations/fr/default.json +++ b/public/translations/fr/default.json @@ -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" } diff --git a/public/translations/he/default.json b/public/translations/he/default.json index 891110df..95070897 100644 --- a/public/translations/he/default.json +++ b/public/translations/he/default.json @@ -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": "לא קובץ" } diff --git a/public/translations/hi/default.json b/public/translations/hi/default.json index ea562343..3ef9d22a 100644 --- a/public/translations/hi/default.json +++ b/public/translations/hi/default.json @@ -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": "फ़ाइल नहीं है" } diff --git a/public/translations/hu/default.json b/public/translations/hu/default.json index 6b5feaac..81ff2b74 100644 --- a/public/translations/hu/default.json +++ b/public/translations/hu/default.json @@ -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" } diff --git a/public/translations/id/default.json b/public/translations/id/default.json index b6d9a0fc..2eb7b8ee 100644 --- a/public/translations/id/default.json +++ b/public/translations/id/default.json @@ -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" } diff --git a/public/translations/it/default.json b/public/translations/it/default.json index d60fb11a..da5f5700 100644 --- a/public/translations/it/default.json +++ b/public/translations/it/default.json @@ -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" } diff --git a/public/translations/ja/default.json b/public/translations/ja/default.json index 88c2855d..fddb5d90 100644 --- a/public/translations/ja/default.json +++ b/public/translations/ja/default.json @@ -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": "ファイルではありません" } diff --git a/public/translations/ko/default.json b/public/translations/ko/default.json index 178c0ef8..b616a53c 100644 --- a/public/translations/ko/default.json +++ b/public/translations/ko/default.json @@ -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": "파일이 아님" } diff --git a/public/translations/mr/default.json b/public/translations/mr/default.json index d0f1a36a..d2d4140c 100644 --- a/public/translations/mr/default.json +++ b/public/translations/mr/default.json @@ -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": "फाइल नाही" } diff --git a/public/translations/nl/default.json b/public/translations/nl/default.json index 2fe388d2..20aba5b2 100644 --- a/public/translations/nl/default.json +++ b/public/translations/nl/default.json @@ -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" } diff --git a/public/translations/no/default.json b/public/translations/no/default.json index 3d0d2a76..011eeb25 100644 --- a/public/translations/no/default.json +++ b/public/translations/no/default.json @@ -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" } diff --git a/public/translations/pl/default.json b/public/translations/pl/default.json index b31d354a..9a2307b4 100644 --- a/public/translations/pl/default.json +++ b/public/translations/pl/default.json @@ -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" } diff --git a/public/translations/pt/default.json b/public/translations/pt/default.json index 2ceb1a3a..604fb03d 100644 --- a/public/translations/pt/default.json +++ b/public/translations/pt/default.json @@ -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" } diff --git a/public/translations/ro/default.json b/public/translations/ro/default.json index 5d2098b7..30a7408d 100644 --- a/public/translations/ro/default.json +++ b/public/translations/ro/default.json @@ -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" } diff --git a/public/translations/ru/default.json b/public/translations/ru/default.json index 77e0097b..18e8f0d6 100644 --- a/public/translations/ru/default.json +++ b/public/translations/ru/default.json @@ -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": "не файл" } diff --git a/public/translations/sq/default.json b/public/translations/sq/default.json index 777dbc19..276255f7 100644 --- a/public/translations/sq/default.json +++ b/public/translations/sq/default.json @@ -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" } diff --git a/public/translations/sv/default.json b/public/translations/sv/default.json index a22c816a..e963b684 100644 --- a/public/translations/sv/default.json +++ b/public/translations/sv/default.json @@ -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" } diff --git a/public/translations/te/default.json b/public/translations/te/default.json index d07b5d19..3808cc21 100644 --- a/public/translations/te/default.json +++ b/public/translations/te/default.json @@ -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": "ఫైల్ కాదు" } diff --git a/public/translations/th/default.json b/public/translations/th/default.json index d746051c..fec8d6e1 100644 --- a/public/translations/th/default.json +++ b/public/translations/th/default.json @@ -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": "ไม่ใช่ไฟล์" } diff --git a/public/translations/tr/default.json b/public/translations/tr/default.json index 843700f0..85eeb819 100644 --- a/public/translations/tr/default.json +++ b/public/translations/tr/default.json @@ -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" } diff --git a/public/translations/uk/default.json b/public/translations/uk/default.json index 1c5c37b4..23c06b86 100644 --- a/public/translations/uk/default.json +++ b/public/translations/uk/default.json @@ -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": "не файл" } diff --git a/public/translations/ur/default.json b/public/translations/ur/default.json index 1bab8998..5edfab7d 100644 --- a/public/translations/ur/default.json +++ b/public/translations/ur/default.json @@ -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": "فائل نہیں ہے" } diff --git a/public/translations/vi/default.json b/public/translations/vi/default.json index 8cfa60e7..2ed2102c 100644 --- a/public/translations/vi/default.json +++ b/public/translations/vi/default.json @@ -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" } diff --git a/public/translations/zh/default.json b/public/translations/zh/default.json index 559c07da..1c69f03b 100644 --- a/public/translations/zh/default.json +++ b/public/translations/zh/default.json @@ -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": "不是文件" } diff --git a/src/components/post-form/__tests__/post-form.test.tsx b/src/components/post-form/__tests__/post-form.test.tsx index 6168e578..d8587961 100644 --- a/src/components/post-form/__tests__/post-form.test.tsx +++ b/src/components/post-form/__tests__/post-form.test.tsx @@ -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'); + }); }); diff --git a/src/components/post-form/post-form.module.css b/src/components/post-form/post-form.module.css index 5581372e..92067da8 100644 --- a/src/components/post-form/post-form.module.css +++ b/src/components/post-form/post-form.module.css @@ -113,6 +113,10 @@ text-transform: lowercase; } +.linkTypeError { + color: red; +} + .linkField input[type="text"] { width: 190px !important; } diff --git a/src/components/post-form/post-form.tsx b/src/components/post-form/post-form.tsx index 447f2cd4..e63d6edd 100644 --- a/src/components/post-form/post-form.tsx +++ b/src/components/post-form/post-form.tsx @@ -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 => { 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 {t('not_a_file')}; + } + 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) => void; + disableLinkInput: boolean; setPublishPostOptions: (opts: Record) => void; setPublishReplyOptions: (opts: Record) => 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); }} /> - {url && } + {url && } {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}