diff --git a/public/translations/ar/default.json b/public/translations/ar/default.json index 9a5ef220..f473890d 100644 --- a/public/translations/ar/default.json +++ b/public/translations/ar/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "المساهمون", "post_link_required_alert": "يجب أن تنشر رابطًا.", "post_media_link_required_alert": "يجب أن تنشر رابط صورة أو فيديو.", - "post_form_code_tags_prompt": "يمكنك تمييز بناء الجملة والحفاظ على المسافات البيضاء باستخدام وسوم [code]." + "post_form_code_tags_prompt": "يمكنك تمييز بناء الجملة والحفاظ على المسافات البيضاء باستخدام وسوم [code].", + "reply_link_required_alert": "يجب عليك الرد برابط.", + "reply_media_link_required_alert": "يجب عليك الرد برابط لصورة أو فيديو.", + "reply_links_not_allowed_alert": "لا يُسمح بروابط الردود في هذه اللوحة." } diff --git a/public/translations/bn/default.json b/public/translations/bn/default.json index 930c6d07..ddb9577d 100644 --- a/public/translations/bn/default.json +++ b/public/translations/bn/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "অবদানকারীরা", "post_link_required_alert": "আপনাকে একটি লিঙ্ক পোস্ট করতে হবে।", "post_media_link_required_alert": "আপনাকে একটি ছবি বা ভিডিওর লিঙ্ক পোস্ট করতে হবে।", - "post_form_code_tags_prompt": "আপনি [code] ট্যাগ ব্যবহার করে সিনট্যাক্স হাইলাইট করতে এবং সাদা স্থান সংরক্ষণ করতে পারেন।" + "post_form_code_tags_prompt": "আপনি [code] ট্যাগ ব্যবহার করে সিনট্যাক্স হাইলাইট করতে এবং সাদা স্থান সংরক্ষণ করতে পারেন।", + "reply_link_required_alert": "আপনাকে একটি লিঙ্ক দিয়ে উত্তর দিতে হবে।", + "reply_media_link_required_alert": "আপনাকে একটি ছবি বা ভিডিও লিঙ্ক দিয়ে উত্তর দিতে হবে।", + "reply_links_not_allowed_alert": "এই বোর্ডে উত্তরের লিঙ্ক অনুমোদিত নয়।" } diff --git a/public/translations/cs/default.json b/public/translations/cs/default.json index 557eac27..1ba3c22e 100644 --- a/public/translations/cs/default.json +++ b/public/translations/cs/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "Přispěvatelé", "post_link_required_alert": "Musíte přidat odkaz.", "post_media_link_required_alert": "Musíte přidat odkaz na obrázek nebo video.", - "post_form_code_tags_prompt": "Syntax můžete zvýraznit a zachovat mezery pomocí značek [code]." + "post_form_code_tags_prompt": "Syntax můžete zvýraznit a zachovat mezery pomocí značek [code].", + "reply_link_required_alert": "Musíte odpovědět odkazem.", + "reply_media_link_required_alert": "Musíte odpovědět odkazem na obrázek nebo video.", + "reply_links_not_allowed_alert": "Odkazy v odpovědích nejsou na této nástěnce povoleny." } diff --git a/public/translations/da/default.json b/public/translations/da/default.json index 39f4fb5a..1dca3eba 100644 --- a/public/translations/da/default.json +++ b/public/translations/da/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "Bidragydere", "post_link_required_alert": "Du skal poste et link.", "post_media_link_required_alert": "Du skal poste et billed- eller videolink.", - "post_form_code_tags_prompt": "Du kan fremhæve syntaks og bevare mellemrum ved at bruge [code]-tags." + "post_form_code_tags_prompt": "Du kan fremhæve syntaks og bevare mellemrum ved at bruge [code]-tags.", + "reply_link_required_alert": "Du skal svare med et link.", + "reply_media_link_required_alert": "Du skal svare med et billed- eller videolink.", + "reply_links_not_allowed_alert": "Svarlinks er ikke tilladt på dette board." } diff --git a/public/translations/de/default.json b/public/translations/de/default.json index 15a864a4..8b2c8e96 100644 --- a/public/translations/de/default.json +++ b/public/translations/de/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "Mitwirkende", "post_link_required_alert": "Du musst einen Link posten.", "post_media_link_required_alert": "Du musst einen Bild- oder Videolink posten.", - "post_form_code_tags_prompt": "Sie können Syntax hervorheben und Leerzeichen beibehalten, indem Sie [code]-Tags verwenden." + "post_form_code_tags_prompt": "Sie können Syntax hervorheben und Leerzeichen beibehalten, indem Sie [code]-Tags verwenden.", + "reply_link_required_alert": "Du musst mit einem Link antworten.", + "reply_media_link_required_alert": "Du musst mit einem Bild- oder Videolink antworten.", + "reply_links_not_allowed_alert": "Antwortlinks sind auf diesem Board nicht erlaubt." } diff --git a/public/translations/el/default.json b/public/translations/el/default.json index 20a3361d..861ee00a 100644 --- a/public/translations/el/default.json +++ b/public/translations/el/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "Συνεισφέροντες", "post_link_required_alert": "Πρέπει να δημοσιεύσετε έναν σύνδεσμο.", "post_media_link_required_alert": "Πρέπει να δημοσιεύσετε σύνδεσμο εικόνας ή βίντεο.", - "post_form_code_tags_prompt": "Μπορείτε να επισημάνετε τη σύνταξη και να διατηρήσετε τα κενά χρησιμοποιώντας ετικέτες [code]." + "post_form_code_tags_prompt": "Μπορείτε να επισημάνετε τη σύνταξη και να διατηρήσετε τα κενά χρησιμοποιώντας ετικέτες [code].", + "reply_link_required_alert": "Πρέπει να απαντήσετε με έναν σύνδεσμο.", + "reply_media_link_required_alert": "Πρέπει να απαντήσετε με σύνδεσμο εικόνας ή βίντεο.", + "reply_links_not_allowed_alert": "Οι σύνδεσμοι στις απαντήσεις δεν επιτρέπονται σε αυτόν τον πίνακα." } diff --git a/public/translations/en/default.json b/public/translations/en/default.json index 45c975ee..ea0ac4a1 100644 --- a/public/translations/en/default.json +++ b/public/translations/en/default.json @@ -465,5 +465,8 @@ "site_legal_meta_contributors_link": "Contributors", "post_link_required_alert": "You must post a link.", "post_media_link_required_alert": "You must post an image or video link.", - "post_form_code_tags_prompt": "You may highlight syntax and preserve whitespace by using [code] tags." + "post_form_code_tags_prompt": "You may highlight syntax and preserve whitespace by using [code] tags.", + "reply_link_required_alert": "You must reply with a link.", + "reply_media_link_required_alert": "You must reply with an image or video link.", + "reply_links_not_allowed_alert": "Reply links are not allowed on this board." } diff --git a/public/translations/es/default.json b/public/translations/es/default.json index 2b4cb782..43b8d3e4 100644 --- a/public/translations/es/default.json +++ b/public/translations/es/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "Colaboradores", "post_link_required_alert": "Debes publicar un enlace.", "post_media_link_required_alert": "Debes publicar un enlace a una imagen o un video.", - "post_form_code_tags_prompt": "Puede resaltar la sintaxis y conservar los espacios en blanco usando etiquetas [code]." + "post_form_code_tags_prompt": "Puede resaltar la sintaxis y conservar los espacios en blanco usando etiquetas [code].", + "reply_link_required_alert": "Debes responder con un enlace.", + "reply_media_link_required_alert": "Debes responder con un enlace a una imagen o un video.", + "reply_links_not_allowed_alert": "Los enlaces en respuestas no están permitidos en este tablón." } diff --git a/public/translations/fa/default.json b/public/translations/fa/default.json index 04fb95dc..9fdb82b5 100644 --- a/public/translations/fa/default.json +++ b/public/translations/fa/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "مشارکت‌کنندگان", "post_link_required_alert": "باید یک پیوند ارسال کنید.", "post_media_link_required_alert": "باید پیوند یک تصویر یا ویدیو را ارسال کنید.", - "post_form_code_tags_prompt": "می‌توانید با استفاده از برچسب‌های [code] نحو را برجسته کرده و فاصله‌ها را حفظ کنید." + "post_form_code_tags_prompt": "می‌توانید با استفاده از برچسب‌های [code] نحو را برجسته کرده و فاصله‌ها را حفظ کنید.", + "reply_link_required_alert": "باید با یک پیوند پاسخ دهید.", + "reply_media_link_required_alert": "باید با یک پیوند تصویر یا ویدیو پاسخ دهید.", + "reply_links_not_allowed_alert": "پیوندها در پاسخ‌ها در این برد مجاز نیستند." } diff --git a/public/translations/fi/default.json b/public/translations/fi/default.json index e8850639..0ee9b748 100644 --- a/public/translations/fi/default.json +++ b/public/translations/fi/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "Avustajat", "post_link_required_alert": "Sinun on julkaistava linkki.", "post_media_link_required_alert": "Sinun on julkaistava kuva- tai videolinkki.", - "post_form_code_tags_prompt": "Voit korostaa syntaksia ja säilyttää välilyönnit käyttämällä [code]-tageja." + "post_form_code_tags_prompt": "Voit korostaa syntaksia ja säilyttää välilyönnit käyttämällä [code]-tageja.", + "reply_link_required_alert": "Sinun on vastattava linkillä.", + "reply_media_link_required_alert": "Sinun on vastattava kuva- tai videolinkillä.", + "reply_links_not_allowed_alert": "Vastauslinkit eivät ole sallittuja tällä laudalla." } diff --git a/public/translations/fil/default.json b/public/translations/fil/default.json index 2a6f9dcb..e8a9cd19 100644 --- a/public/translations/fil/default.json +++ b/public/translations/fil/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "Mga Kontribyutor", "post_link_required_alert": "Kailangan mong mag-post ng link.", "post_media_link_required_alert": "Kailangan mong mag-post ng link ng larawan o video.", - "post_form_code_tags_prompt": "Maaari mong i-highlight ang syntax at panatilihin ang whitespace gamit ang [code] tags." + "post_form_code_tags_prompt": "Maaari mong i-highlight ang syntax at panatilihin ang whitespace gamit ang [code] tags.", + "reply_link_required_alert": "Kailangan mong tumugon gamit ang isang link.", + "reply_media_link_required_alert": "Kailangan mong tumugon gamit ang link ng larawan o video.", + "reply_links_not_allowed_alert": "Hindi pinapayagan ang mga link sa tugon sa board na ito." } diff --git a/public/translations/fr/default.json b/public/translations/fr/default.json index d80fb61c..5d985c65 100644 --- a/public/translations/fr/default.json +++ b/public/translations/fr/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "Contributeurs", "post_link_required_alert": "Vous devez publier un lien.", "post_media_link_required_alert": "Vous devez publier un lien vers une image ou une vidéo.", - "post_form_code_tags_prompt": "Vous pouvez surligner la syntaxe et préserver les espaces en utilisant des balises [code]." + "post_form_code_tags_prompt": "Vous pouvez surligner la syntaxe et préserver les espaces en utilisant des balises [code].", + "reply_link_required_alert": "Vous devez répondre avec un lien.", + "reply_media_link_required_alert": "Vous devez répondre avec un lien d'image ou de vidéo.", + "reply_links_not_allowed_alert": "Les liens dans les réponses ne sont pas autorisés sur ce board." } diff --git a/public/translations/he/default.json b/public/translations/he/default.json index 530fbeda..608cb5c8 100644 --- a/public/translations/he/default.json +++ b/public/translations/he/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "תורמים", "post_link_required_alert": "עליך לפרסם קישור.", "post_media_link_required_alert": "עליך לפרסם קישור לתמונה או לסרטון.", - "post_form_code_tags_prompt": "ניתן להדגיש תחביר ולשמור על רווחים לבנים באמצעות תגיות [code]." + "post_form_code_tags_prompt": "ניתן להדגיש תחביר ולשמור על רווחים לבנים באמצעות תגיות [code].", + "reply_link_required_alert": "עליך להשיב עם קישור.", + "reply_media_link_required_alert": "עליך להשיב עם קישור לתמונה או לסרטון.", + "reply_links_not_allowed_alert": "קישורים בתגובות אינם מותרים בלוח הזה." } diff --git a/public/translations/hi/default.json b/public/translations/hi/default.json index 2176dc02..29439467 100644 --- a/public/translations/hi/default.json +++ b/public/translations/hi/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "योगदानकर्ता", "post_link_required_alert": "आपको एक लिंक पोस्ट करना होगा।", "post_media_link_required_alert": "आपको एक छवि या वीडियो लिंक पोस्ट करना होगा।", - "post_form_code_tags_prompt": "आप [code] टैग का उपयोग करके सिंटैक्स को हाइलाइट कर सकते हैं और व्हाइटस्पेस को संरक्षित कर सकते हैं।" + "post_form_code_tags_prompt": "आप [code] टैग का उपयोग करके सिंटैक्स को हाइलाइट कर सकते हैं और व्हाइटस्पेस को संरक्षित कर सकते हैं।", + "reply_link_required_alert": "आपको एक लिंक के साथ जवाब देना होगा।", + "reply_media_link_required_alert": "आपको किसी छवि या वीडियो लिंक के साथ जवाब देना होगा।", + "reply_links_not_allowed_alert": "इस बोर्ड पर जवाबों में लिंक की अनुमति नहीं है।" } diff --git a/public/translations/hu/default.json b/public/translations/hu/default.json index 90353302..02e76efa 100644 --- a/public/translations/hu/default.json +++ b/public/translations/hu/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "Közreműködők", "post_link_required_alert": "Közzé kell tenned egy linket.", "post_media_link_required_alert": "Közzé kell tenned egy kép- vagy videólinket.", - "post_form_code_tags_prompt": "A szintaxis kiemelhető és a szóközök megőrizhetők a [code] címkék használatával." + "post_form_code_tags_prompt": "A szintaxis kiemelhető és a szóközök megőrizhetők a [code] címkék használatával.", + "reply_link_required_alert": "Linkkel kell válaszolnod.", + "reply_media_link_required_alert": "Kép- vagy videólinkkel kell válaszolnod.", + "reply_links_not_allowed_alert": "Ezen a táblán nem engedélyezettek a válaszlinkek." } diff --git a/public/translations/id/default.json b/public/translations/id/default.json index 3a0f1a1d..357197e3 100644 --- a/public/translations/id/default.json +++ b/public/translations/id/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "Kontributor", "post_link_required_alert": "Anda harus memposting tautan.", "post_media_link_required_alert": "Anda harus memposting tautan gambar atau video.", - "post_form_code_tags_prompt": "Anda dapat menyorot sintaks dan mempertahankan spasi dengan menggunakan tag [code]." + "post_form_code_tags_prompt": "Anda dapat menyorot sintaks dan mempertahankan spasi dengan menggunakan tag [code].", + "reply_link_required_alert": "Anda harus membalas dengan tautan.", + "reply_media_link_required_alert": "Anda harus membalas dengan tautan gambar atau video.", + "reply_links_not_allowed_alert": "Tautan balasan tidak diizinkan di papan ini." } diff --git a/public/translations/it/default.json b/public/translations/it/default.json index c8c54c72..09986ea8 100644 --- a/public/translations/it/default.json +++ b/public/translations/it/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "Collaboratori", "post_link_required_alert": "Devi pubblicare un link.", "post_media_link_required_alert": "Devi pubblicare un link a un'immagine o a un video.", - "post_form_code_tags_prompt": "Puoi evidenziare la sintassi e preservare gli spazi bianchi usando i tag [code]." + "post_form_code_tags_prompt": "Puoi evidenziare la sintassi e preservare gli spazi bianchi usando i tag [code].", + "reply_link_required_alert": "Devi rispondere con un link.", + "reply_media_link_required_alert": "Devi rispondere con un link a un'immagine o a un video.", + "reply_links_not_allowed_alert": "I link nelle risposte non sono consentiti su questa board." } diff --git a/public/translations/ja/default.json b/public/translations/ja/default.json index d38f9322..93a3f3e4 100644 --- a/public/translations/ja/default.json +++ b/public/translations/ja/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "貢献者", "post_link_required_alert": "リンクを投稿する必要があります。", "post_media_link_required_alert": "画像または動画のリンクを投稿する必要があります。", - "post_form_code_tags_prompt": "[code] タグを使用して、構文をハイライトし、空白を保持できます。" + "post_form_code_tags_prompt": "[code] タグを使用して、構文をハイライトし、空白を保持できます。", + "reply_link_required_alert": "リンク付きで返信する必要があります。", + "reply_media_link_required_alert": "画像または動画のリンク付きで返信する必要があります。", + "reply_links_not_allowed_alert": "この板では返信リンクは許可されていません。" } diff --git a/public/translations/ko/default.json b/public/translations/ko/default.json index d4b637f0..4ddbb106 100644 --- a/public/translations/ko/default.json +++ b/public/translations/ko/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "기여자", "post_link_required_alert": "링크를 게시해야 합니다.", "post_media_link_required_alert": "이미지 또는 동영상 링크를 게시해야 합니다.", - "post_form_code_tags_prompt": "[code] 태그를 사용하여 구문을 강조 표시하고 공백을 유지할 수 있습니다." + "post_form_code_tags_prompt": "[code] 태그를 사용하여 구문을 강조 표시하고 공백을 유지할 수 있습니다.", + "reply_link_required_alert": "링크를 포함해 답글을 달아야 합니다.", + "reply_media_link_required_alert": "이미지 또는 동영상 링크를 포함해 답글을 달아야 합니다.", + "reply_links_not_allowed_alert": "이 게시판에서는 답글 링크가 허용되지 않습니다." } diff --git a/public/translations/mr/default.json b/public/translations/mr/default.json index 761482d3..a6a0b107 100644 --- a/public/translations/mr/default.json +++ b/public/translations/mr/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "योगदानकर्ते", "post_link_required_alert": "तुम्हाला एक लिंक पोस्ट करावी लागेल.", "post_media_link_required_alert": "तुम्हाला प्रतिमा किंवा व्हिडिओची लिंक पोस्ट करावी लागेल.", - "post_form_code_tags_prompt": "आप [code] टॅग वापरून सिंटॅक्स हायलाइट करू शकता आणि व्हाइटस्पेस जतन करू शकता." + "post_form_code_tags_prompt": "आप [code] टॅग वापरून सिंटॅक्स हायलाइट करू शकता आणि व्हाइटस्पेस जतन करू शकता.", + "reply_link_required_alert": "तुम्ही दुव्यासह उत्तर देणे आवश्यक आहे.", + "reply_media_link_required_alert": "तुम्ही प्रतिमा किंवा व्हिडिओ दुव्यासह उत्तर देणे आवश्यक आहे.", + "reply_links_not_allowed_alert": "या बोर्डवर उत्तरांमधील दुवे परवानगी नाहीत." } diff --git a/public/translations/nl/default.json b/public/translations/nl/default.json index 32917977..8c632d44 100644 --- a/public/translations/nl/default.json +++ b/public/translations/nl/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "Bijdragers", "post_link_required_alert": "Je moet een link plaatsen.", "post_media_link_required_alert": "Je moet een afbeelding- of videolink plaatsen.", - "post_form_code_tags_prompt": "U kunt syntaxis markeren en witruimte behouden door [code]-tags te gebruiken." + "post_form_code_tags_prompt": "U kunt syntaxis markeren en witruimte behouden door [code]-tags te gebruiken.", + "reply_link_required_alert": "Je moet antwoorden met een link.", + "reply_media_link_required_alert": "Je moet antwoorden met een afbeeldings- of videolink.", + "reply_links_not_allowed_alert": "Antwoordlinks zijn niet toegestaan op dit bord." } diff --git a/public/translations/no/default.json b/public/translations/no/default.json index 6d3c32ea..4048d6e8 100644 --- a/public/translations/no/default.json +++ b/public/translations/no/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "Bidragsytere", "post_link_required_alert": "Du må poste en lenke.", "post_media_link_required_alert": "Du må poste en bilde- eller videolenke.", - "post_form_code_tags_prompt": "Du kan fremheve syntaks og bevare mellomrom ved å bruke [code]-tagger." + "post_form_code_tags_prompt": "Du kan fremheve syntaks og bevare mellomrom ved å bruke [code]-tagger.", + "reply_link_required_alert": "Du må svare med en lenke.", + "reply_media_link_required_alert": "Du må svare med en bilde- eller videolenke.", + "reply_links_not_allowed_alert": "Svarlenker er ikke tillatt på dette forumet." } diff --git a/public/translations/pl/default.json b/public/translations/pl/default.json index 2cfe6c5b..febba8d6 100644 --- a/public/translations/pl/default.json +++ b/public/translations/pl/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "Współtwórcy", "post_link_required_alert": "Musisz opublikować link.", "post_media_link_required_alert": "Musisz opublikować link do obrazu lub filmu.", - "post_form_code_tags_prompt": "Możesz podświetlać składnię i zachować spacje, używając tagów [code]." + "post_form_code_tags_prompt": "Możesz podświetlać składnię i zachować spacje, używając tagów [code].", + "reply_link_required_alert": "Musisz odpowiedzieć linkiem.", + "reply_media_link_required_alert": "Musisz odpowiedzieć linkiem do obrazu lub filmu.", + "reply_links_not_allowed_alert": "Linki w odpowiedziach nie są dozwolone na tej tablicy." } diff --git a/public/translations/pt/default.json b/public/translations/pt/default.json index 26778600..66e31302 100644 --- a/public/translations/pt/default.json +++ b/public/translations/pt/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "Colaboradores", "post_link_required_alert": "Você deve postar um link.", "post_media_link_required_alert": "Você deve postar um link de imagem ou vídeo.", - "post_form_code_tags_prompt": "Você pode destacar a sintaxe e preservar espaços em branco usando tags [code]." + "post_form_code_tags_prompt": "Você pode destacar a sintaxe e preservar espaços em branco usando tags [code].", + "reply_link_required_alert": "Você deve responder com um link.", + "reply_media_link_required_alert": "Você deve responder com um link de imagem ou vídeo.", + "reply_links_not_allowed_alert": "Links em respostas não são permitidos neste board." } diff --git a/public/translations/ro/default.json b/public/translations/ro/default.json index 1b3e45ba..bdf22181 100644 --- a/public/translations/ro/default.json +++ b/public/translations/ro/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "Contributori", "post_link_required_alert": "Trebuie să postezi un link.", "post_media_link_required_alert": "Trebuie să postezi un link către o imagine sau un videoclip.", - "post_form_code_tags_prompt": "Puteți evidenția sintaxa și păstra spațiile albe folosind tag-uri [code]." + "post_form_code_tags_prompt": "Puteți evidenția sintaxa și păstra spațiile albe folosind tag-uri [code].", + "reply_link_required_alert": "Trebuie să răspunzi cu un link.", + "reply_media_link_required_alert": "Trebuie să răspunzi cu un link de imagine sau video.", + "reply_links_not_allowed_alert": "Linkurile în răspunsuri nu sunt permise pe acest board." } diff --git a/public/translations/ru/default.json b/public/translations/ru/default.json index 5990c944..12510029 100644 --- a/public/translations/ru/default.json +++ b/public/translations/ru/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "Участники", "post_link_required_alert": "Вы должны опубликовать ссылку.", "post_media_link_required_alert": "Вы должны опубликовать ссылку на изображение или видео.", - "post_form_code_tags_prompt": "Вы можете выделять синтаксис и сохранять пробелы с помощью тегов [code]." + "post_form_code_tags_prompt": "Вы можете выделять синтаксис и сохранять пробелы с помощью тегов [code].", + "reply_link_required_alert": "Вы должны ответить ссылкой.", + "reply_media_link_required_alert": "Вы должны ответить ссылкой на изображение или видео.", + "reply_links_not_allowed_alert": "Ссылки в ответах на этой доске запрещены." } diff --git a/public/translations/sq/default.json b/public/translations/sq/default.json index f51780b5..531fc740 100644 --- a/public/translations/sq/default.json +++ b/public/translations/sq/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "Kontribuesit", "post_link_required_alert": "Duhet të postosh një lidhje.", "post_media_link_required_alert": "Duhet të postosh një lidhje imazhi ose videoje.", - "post_form_code_tags_prompt": "Mund të theksosh sintaksën dhe të ruash hapësirat duke përdorur etiketat [code]." + "post_form_code_tags_prompt": "Mund të theksosh sintaksën dhe të ruash hapësirat duke përdorur etiketat [code].", + "reply_link_required_alert": "Duhet të përgjigjesh me një lidhje.", + "reply_media_link_required_alert": "Duhet të përgjigjesh me një lidhje imazhi ose videoje.", + "reply_links_not_allowed_alert": "Lidhjet në përgjigje nuk lejohen në këtë bord." } diff --git a/public/translations/sv/default.json b/public/translations/sv/default.json index daf8066e..23556097 100644 --- a/public/translations/sv/default.json +++ b/public/translations/sv/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "Bidragsgivare", "post_link_required_alert": "Du måste posta en länk.", "post_media_link_required_alert": "Du måste posta en bild- eller videolänk.", - "post_form_code_tags_prompt": "Du kan markera syntax och bevara mellanslag genom att använda [code]-taggar." + "post_form_code_tags_prompt": "Du kan markera syntax och bevara mellanslag genom att använda [code]-taggar.", + "reply_link_required_alert": "Du måste svara med en länk.", + "reply_media_link_required_alert": "Du måste svara med en bild- eller videolänk.", + "reply_links_not_allowed_alert": "Svarslänkar är inte tillåtna på detta bräde." } diff --git a/public/translations/te/default.json b/public/translations/te/default.json index c0496810..03f78828 100644 --- a/public/translations/te/default.json +++ b/public/translations/te/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "సహకారులు", "post_link_required_alert": "మీరు ఒక లింక్ పోస్ట్ చేయాలి.", "post_media_link_required_alert": "మీరు చిత్రం లేదా వీడియో లింక్ పోస్ట్ చేయాలి.", - "post_form_code_tags_prompt": "మీరు [code] ట్యాగ్‌లను ఉపయోగించి సింటాక్స్‌ను హైలైట్ చేయవచ్చు మరియు ఖాళీ స్థలాలను కాపాడవచ్చు." + "post_form_code_tags_prompt": "మీరు [code] ట్యాగ్‌లను ఉపయోగించి సింటాక్స్‌ను హైలైట్ చేయవచ్చు మరియు ఖాళీ స్థలాలను కాపాడవచ్చు.", + "reply_link_required_alert": "మీరు లింక్‌తో ప్రత్యుత్తరం ఇవ్వాలి.", + "reply_media_link_required_alert": "మీరు చిత్రం లేదా వీడియో లింక్‌తో ప్రత్యుత్తరం ఇవ్వాలి.", + "reply_links_not_allowed_alert": "ఈ బోర్డులో ప్రత్యుత్తర లింక్‌లకు అనుమతి లేదు." } diff --git a/public/translations/th/default.json b/public/translations/th/default.json index cfdbbeb4..e62fa11f 100644 --- a/public/translations/th/default.json +++ b/public/translations/th/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "ผู้มีส่วนร่วม", "post_link_required_alert": "คุณต้องโพสต์ลิงก์", "post_media_link_required_alert": "คุณต้องโพสต์ลิงก์รูปภาพหรือวิดีโอ", - "post_form_code_tags_prompt": "คุณสามารถไฮไลต์ไวยากรณ์และรักษาช่องว่างได้โดยใช้แท็ก [code]" + "post_form_code_tags_prompt": "คุณสามารถไฮไลต์ไวยากรณ์และรักษาช่องว่างได้โดยใช้แท็ก [code]", + "reply_link_required_alert": "คุณต้องตอบกลับพร้อมลิงก์", + "reply_media_link_required_alert": "คุณต้องตอบกลับพร้อมลิงก์รูปภาพหรือวิดีโอ", + "reply_links_not_allowed_alert": "ไม่อนุญาตให้ใส่ลิงก์ในคำตอบบนบอร์ดนี้" } diff --git a/public/translations/tr/default.json b/public/translations/tr/default.json index 9432bc87..3dcede1a 100644 --- a/public/translations/tr/default.json +++ b/public/translations/tr/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "Katkıda Bulunanlar", "post_link_required_alert": "Bir bağlantı paylaşmalısınız.", "post_media_link_required_alert": "Bir görsel veya video bağlantısı paylaşmalısınız.", - "post_form_code_tags_prompt": "[code] etiketlerini kullanarak sözdizimini vurgulayabilir ve boşlukları koruyabilirsiniz." + "post_form_code_tags_prompt": "[code] etiketlerini kullanarak sözdizimini vurgulayabilir ve boşlukları koruyabilirsiniz.", + "reply_link_required_alert": "Bir bağlantıyla yanıt vermelisiniz.", + "reply_media_link_required_alert": "Bir görsel veya video bağlantısıyla yanıt vermelisiniz.", + "reply_links_not_allowed_alert": "Bu panoda yanıt bağlantılarına izin verilmiyor." } diff --git a/public/translations/uk/default.json b/public/translations/uk/default.json index f1a0eecc..48f5e741 100644 --- a/public/translations/uk/default.json +++ b/public/translations/uk/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "Учасники", "post_link_required_alert": "Потрібно опублікувати посилання.", "post_media_link_required_alert": "Потрібно опублікувати посилання на зображення або відео.", - "post_form_code_tags_prompt": "Ви можете підсвічувати синтаксис і зберігати пробіли, використовуючи теги [code]." + "post_form_code_tags_prompt": "Ви можете підсвічувати синтаксис і зберігати пробіли, використовуючи теги [code].", + "reply_link_required_alert": "Ви маєте відповісти посиланням.", + "reply_media_link_required_alert": "Ви маєте відповісти посиланням на зображення або відео.", + "reply_links_not_allowed_alert": "Посилання у відповідях на цій дошці заборонені." } diff --git a/public/translations/ur/default.json b/public/translations/ur/default.json index b80361e0..af33246c 100644 --- a/public/translations/ur/default.json +++ b/public/translations/ur/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "شراکت کنندگان", "post_link_required_alert": "آپ کو ایک لنک پوسٹ کرنا ہوگا۔", "post_media_link_required_alert": "آپ کو تصویر یا ویڈیو کا لنک پوسٹ کرنا ہوگا۔", - "post_form_code_tags_prompt": "آپ [code] ٹیگز استعمال کر کے نحو کو نمایاں کر سکتے ہیں اور خالی جگہ کو محفوظ رکھ سکتے ہیں۔" + "post_form_code_tags_prompt": "آپ [code] ٹیگز استعمال کر کے نحو کو نمایاں کر سکتے ہیں اور خالی جگہ کو محفوظ رکھ سکتے ہیں۔", + "reply_link_required_alert": "آپ کو ایک لنک کے ساتھ جواب دینا ہوگا۔", + "reply_media_link_required_alert": "آپ کو تصویر یا ویڈیو کے لنک کے ساتھ جواب دینا ہوگا۔", + "reply_links_not_allowed_alert": "اس بورڈ پر جوابات میں لنکس کی اجازت نہیں ہے۔" } diff --git a/public/translations/vi/default.json b/public/translations/vi/default.json index 5eba98e9..83d332fe 100644 --- a/public/translations/vi/default.json +++ b/public/translations/vi/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "Người đóng góp", "post_link_required_alert": "Bạn phải đăng một liên kết.", "post_media_link_required_alert": "Bạn phải đăng liên kết hình ảnh hoặc video.", - "post_form_code_tags_prompt": "Bạn có thể tô sáng cú pháp và giữ nguyên khoảng trắng bằng cách sử dụng thẻ [code]." + "post_form_code_tags_prompt": "Bạn có thể tô sáng cú pháp và giữ nguyên khoảng trắng bằng cách sử dụng thẻ [code].", + "reply_link_required_alert": "Bạn phải trả lời bằng một liên kết.", + "reply_media_link_required_alert": "Bạn phải trả lời bằng một liên kết hình ảnh hoặc video.", + "reply_links_not_allowed_alert": "Liên kết trong trả lời không được phép trên bảng này." } diff --git a/public/translations/zh/default.json b/public/translations/zh/default.json index 6ee11821..d8228bc6 100644 --- a/public/translations/zh/default.json +++ b/public/translations/zh/default.json @@ -446,5 +446,8 @@ "site_legal_meta_contributors_link": "贡献者", "post_link_required_alert": "你必须发布一个链接。", "post_media_link_required_alert": "你必须发布图片或视频链接。", - "post_form_code_tags_prompt": "您可以使用 [code] 标签来高亮语法并保留空白字符。" + "post_form_code_tags_prompt": "您可以使用 [code] 标签来高亮语法并保留空白字符。", + "reply_link_required_alert": "你必须使用链接回复。", + "reply_media_link_required_alert": "你必须使用图片或视频链接回复。", + "reply_links_not_allowed_alert": "此版块不允许回复链接。" } diff --git a/src/components/post-form/__tests__/post-form.test.tsx b/src/components/post-form/__tests__/post-form.test.tsx index 9d020956..adf7a886 100644 --- a/src/components/post-form/__tests__/post-form.test.tsx +++ b/src/components/post-form/__tests__/post-form.test.tsx @@ -1783,6 +1783,93 @@ describe('PostForm', () => { expect(testState.publishReplyMock).toHaveBeenCalledTimes(1); }); + it('does not require an inline reply link when only post links are required', async () => { + testState.comments = { + 'thread-cid': { + postCid: 'thread-cid', + }, + }; + testState.resolvedCommunityAddress = 'music-posting.eth'; + testState.communities['music-posting.eth'] = { + address: 'music-posting.eth', + features: { requirePostLink: true }, + }; + + await renderPostForm('/mu/thread/thread-cid'); + await clickByText(container, 'post_a_reply'); + + const table = container.querySelector('table') as HTMLTableElement; + const textarea = table.querySelector('textarea') as HTMLTextAreaElement; + + await dispatchInput(textarea, 'Reply body'); + await clickByText(table, 'post'); + + expect(container.textContent).not.toContain('post_link_required_alert'); + expect(testState.publishReplyMock).toHaveBeenCalledTimes(1); + }); + + it('requires an inline reply link when reply links are required', async () => { + testState.comments = { + 'thread-cid': { + postCid: 'thread-cid', + }, + }; + testState.resolvedCommunityAddress = 'music-posting.eth'; + testState.communities['music-posting.eth'] = { + address: 'music-posting.eth', + features: { requireReplyLink: true }, + }; + + await renderPostForm('/mu/thread/thread-cid'); + await clickByText(container, 'post_a_reply'); + + const table = container.querySelector('table') as HTMLTableElement; + const textarea = table.querySelector('textarea') as HTMLTextAreaElement; + const linkInput = table.querySelector('input[aria-label="link"]') as HTMLInputElement; + + await dispatchInput(textarea, 'Reply body'); + await clickByText(table, 'post'); + + expect(container.textContent).toContain('error: reply_link_required_alert'); + expect(testState.publishReplyMock).not.toHaveBeenCalled(); + + await dispatchInput(linkInput, 'https://example.com/reply'); + await clickByText(table, 'post'); + + expect(testState.setPublishReplyOptionsMock).toHaveBeenCalledWith({ link: 'https://example.com/reply' }); + expect(testState.publishReplyMock).toHaveBeenCalledTimes(1); + expect(testState.publishReplyMock).toHaveBeenCalledWith(expect.objectContaining({ content: 'Reply body' })); + }); + + it('rejects inline reply links when reply links are disabled', async () => { + testState.comments = { + 'thread-cid': { + postCid: 'thread-cid', + }, + }; + testState.resolvedCommunityAddress = 'music-posting.eth'; + testState.communities['music-posting.eth'] = { + address: 'music-posting.eth', + features: { noReplyLinks: true }, + }; + + await renderPostForm('/mu/thread/thread-cid'); + await clickByText(container, 'post_a_reply'); + + const table = container.querySelector('table') as HTMLTableElement; + const textarea = table.querySelector('textarea') as HTMLTextAreaElement; + const linkInput = table.querySelector('input[aria-label="link"]') as HTMLInputElement; + expect(linkInput.disabled).toBe(true); + + linkInput.disabled = false; + await dispatchInput(textarea, 'Reply body'); + await dispatchInput(linkInput, 'https://example.com/reply.png'); + await clickByText(table, 'post'); + + expect(container.textContent).toContain('error: reply_links_not_allowed_alert'); + expect(testState.publishReplyMock).not.toHaveBeenCalled(); + }); + it('publishes known twimg query-format reply links with a path extension without editing the field', async () => { const twimgLink = 'https://pbs.twimg.com/media/HJxnhNKWMAAhqFU?format=jpg&name=medium'; const publishLink = 'https://pbs.twimg.com/media/HJxnhNKWMAAhqFU.jpg'; diff --git a/src/components/post-form/post-form.tsx b/src/components/post-form/post-form.tsx index 85c237a2..4df6bb4c 100644 --- a/src/components/post-form/post-form.tsx +++ b/src/components/post-form/post-form.tsx @@ -7,7 +7,16 @@ import getShortAddress from '../../lib/get-short-address'; import { communitiesPagesStore as useCommunitiesPagesStore } from '../../lib/bitsocial-internals/stores'; import { getDisplayMediaInfoType, getLinkMediaInfo, getTwimgMediaFilePublishUrl } from '../../lib/utils/media-utils'; import { getExpiringMediaLinkAlert, getPublishFileDisplayName, getPublishLinkOptions, isPublishFileMediaType } from '../../lib/utils/media-link-validation-utils'; -import { getEffectivePublishLinkFeatures, getPublishLinkValidationError, getRequirePostLink, getRequirePostLinkIsMedia } from '../../lib/utils/publish-link-requirements'; +import { + getEffectivePostLinkFeatures, + getEffectiveReplyLinkFeatures, + getNoReplyLinks, + getPublishLinkValidationError, + getRequirePostLink, + getRequirePostLinkIsMedia, + getRequireReplyLink, + getRequireReplyLinkIsMedia, +} from '../../lib/utils/publish-link-requirements'; import { type DiceRoll, type FortuneEntry, @@ -177,7 +186,7 @@ interface PostFormFieldsProps { subscriptions: string[]; communityAddress: string | undefined; rulesPath: string; - requirePostLinkIsMedia: boolean; + requireCurrentLinkIsMedia: boolean; flagOptions: CommentFlagSelectOption[]; flashTagOptions: FlashTagOption[]; showFlashTagSelector: boolean; @@ -232,7 +241,7 @@ const PostFormFields = ({ subscriptions, communityAddress, rulesPath, - requirePostLinkIsMedia, + requireCurrentLinkIsMedia, flagOptions, flashTagOptions, showFlashTagSelector, @@ -367,15 +376,15 @@ const PostFormFields = ({ )} - {requirePostLinkIsMedia ? t('link_to_file') : t('link')} + {requireCurrentLinkIsMedia ? t('link_to_file') : t('link')} { @@ -383,7 +392,7 @@ const PostFormFields = ({ }} onBlur={handleLinkBlur} /> - {url && } + {url && } {showUploadControls && ( @@ -402,8 +411,12 @@ const PostFormFields = ({ isUploading={isUploading} showUploadControls={showUploadControls} /> - - {isUploading ? : getPostFormFileDisplayLabel(url, uploadedFileName, t('no_file_chosen'), requirePostLinkIsMedia)} + + {isUploading ? ( + + ) : ( + getPostFormFileDisplayLabel(url, uploadedFileName, t('no_file_chosen'), requireCurrentLinkIsMedia) + )} @@ -577,9 +590,14 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid: const postOptionsDirectoryCode = getPostOptionsDirectoryCode(directoryEntry, location.pathname); const showOekakiControls = postOptionsDirectoryCode === 'i' || directoryEntry?.directoryCode === 'i'; const communityFeatures = useCommunityField(effectiveBoardAddress, (community) => community?.features); - const publishLinkFeatures = getEffectivePublishLinkFeatures(communityFeatures, directoryEntry?.features); - const requirePostLink = getRequirePostLink(publishLinkFeatures); - const requirePostLinkIsMedia = getRequirePostLinkIsMedia(publishLinkFeatures, isInAllView || isInSubscriptionsView); + const postLinkFeatures = getEffectivePostLinkFeatures(communityFeatures, directoryEntry?.features); + const replyLinkFeatures = getEffectiveReplyLinkFeatures(communityFeatures, directoryEntry?.features); + const requirePostLink = getRequirePostLink(postLinkFeatures); + const requirePostLinkIsMedia = getRequirePostLinkIsMedia(postLinkFeatures, isInAllView || isInSubscriptionsView); + const requireReplyLink = getRequireReplyLink(replyLinkFeatures); + const requireReplyLinkIsMedia = getRequireReplyLinkIsMedia(replyLinkFeatures, isInAllView || isInSubscriptionsView); + const noReplyLinks = getNoReplyLinks(replyLinkFeatures); + const requireCurrentLinkIsMedia = isInPostView ? requireReplyLinkIsMedia : requirePostLinkIsMedia; const flagOptions = getCommentFlagOptionsForDirectory(directoryEntry); const showFlashUploadPrompt = isFlashDirectoryCode(postOptionsDirectoryCode); const showFlashTagSelector = showFlashUploadPrompt && !isInPostView; @@ -687,6 +705,8 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid: link: currentUrl, requireLink: requirePostLink, requireMedia: requirePostLinkIsMedia, + requiredLinkAlertKey: 'post_link_required_alert', + requiredMediaLinkAlertKey: 'post_media_link_required_alert', t, }); if (linkValidationError) { @@ -816,8 +836,12 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid: const linkValidationError = getPublishLinkValidationError({ link: currentUrl, - requireLink: requirePostLink, - requireMedia: requirePostLinkIsMedia, + noLinks: noReplyLinks, + requireLink: requireReplyLink, + requireMedia: requireReplyLinkIsMedia, + requiredLinkAlertKey: 'reply_link_required_alert', + requiredMediaLinkAlertKey: 'reply_media_link_required_alert', + noLinksAlertKey: 'reply_links_not_allowed_alert', t, }); if (linkValidationError) { @@ -856,7 +880,7 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid: noticeCountdown: youtubeThumbnailConversionCountdown, queueLinkConversion, } = useYouTubeThumbnailLinkConversion({ - enabled: requirePostLinkIsMedia, + enabled: requireCurrentLinkIsMedia && !(isInPostView && noReplyLinks), onContentChange: handleContentValueChange, onLinkChange: setLinkValue, textRef, @@ -954,13 +978,13 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid: handleLinkChange={handleLinkChange} handleLinkBlur={handleLinkBlur} handleOptionsChange={handleOptionsChange} - disableLinkInput={isUploading || youtubeThumbnailConversionCountdown !== null} + disableLinkInput={isUploading || youtubeThumbnailConversionCountdown !== null || (isInPostView && noReplyLinks)} setPublishPostOptions={setPublishPostOptions} setPublishReplyOptions={setPublishReplyOptions} isUploading={isUploading} uploadedFileName={uploadedFileName} - showUploadControls={showUploadControls} - showOekakiControls={showOekakiControls} + showUploadControls={showUploadControls && !(isInPostView && noReplyLinks)} + showOekakiControls={showOekakiControls && !(isInPostView && noReplyLinks)} showSpoilerForPost={showSpoilerForPost} showSpoilerForReply={showSpoilerForReply} isInAllView={isInAllView} @@ -971,7 +995,7 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid: subscriptions={subscriptions} communityAddress={communityAddress} rulesPath={rulesPath} - requirePostLinkIsMedia={requirePostLinkIsMedia} + requireCurrentLinkIsMedia={requireCurrentLinkIsMedia} flagOptions={flagOptions} flashTagOptions={FLASH_TAG_OPTIONS} showFlashTagSelector={showFlashTagSelector} diff --git a/src/components/reply-modal/__tests__/reply-modal.test.tsx b/src/components/reply-modal/__tests__/reply-modal.test.tsx index 3ec0e3e2..05d01d93 100644 --- a/src/components/reply-modal/__tests__/reply-modal.test.tsx +++ b/src/components/reply-modal/__tests__/reply-modal.test.tsx @@ -530,7 +530,7 @@ describe('ReplyModal', () => { testState.directoryByAddress['oekaki-posting.bso'] = { address: 'oekaki-posting.bso', directoryCode: 'i', - features: { requirePostLink: true, requirePostLinkIsMedia: true }, + features: { requireReplyLink: true, requireReplyLinkIsMedia: true }, title: '/i/ - Oekaki', }; testState.communities['oekaki-posting.bso'] = { address: 'oekaki-posting.bso' }; @@ -765,7 +765,7 @@ describe('ReplyModal', () => { expect(testState.publishReplyMock).toHaveBeenCalledTimes(1); }); - it('requires a link when live community features require post links', async () => { + it('does not require a reply link when only live community features require post links', async () => { testState.openEmpty = true; testState.selectedText = 'Reply body'; testState.communities['music-posting.eth'] = { @@ -777,8 +777,24 @@ describe('ReplyModal', () => { await clickButtonByText('post'); - expect(container.textContent).toContain('error: post_link_required_alert'); - const linkRequiredError = Array.from(container.querySelectorAll('div')).find((element) => element.textContent === 'error: post_link_required_alert'); + expect(container.textContent).not.toContain('post_link_required_alert'); + expect(testState.publishReplyMock).toHaveBeenCalledTimes(1); + }); + + it('requires a link when live community features require reply links', async () => { + testState.openEmpty = true; + testState.selectedText = 'Reply body'; + testState.communities['music-posting.eth'] = { + address: 'music-posting.eth', + features: { requireReplyLink: true }, + }; + + await renderReplyModal('/mu/thread/post-1'); + + await clickButtonByText('post'); + + expect(container.textContent).toContain('error: reply_link_required_alert'); + const linkRequiredError = Array.from(container.querySelectorAll('div')).find((element) => element.textContent === 'error: reply_link_required_alert'); expect(linkRequiredError?.className).toContain('error'); expect(testState.publishReplyMock).not.toHaveBeenCalled(); @@ -790,20 +806,20 @@ describe('ReplyModal', () => { expect(testState.publishReplyMock).toHaveBeenCalledTimes(1); }); - it('requires a media link when live community features require post links to be media', async () => { + it('requires a media link when live community features require reply links to be media', async () => { testState.openEmpty = true; testState.selectedText = 'Reply body'; testState.communities['music-posting.eth'] = { address: 'music-posting.eth', - features: { requirePostLink: true, requirePostLinkIsMedia: true }, + features: { requireReplyLink: true, requireReplyLinkIsMedia: true }, }; await renderReplyModal('/mu/thread/post-1'); await clickButtonByText('post'); - expect(container.textContent).toContain('error: post_media_link_required_alert'); - const mediaRequiredError = Array.from(container.querySelectorAll('div')).find((element) => element.textContent === 'error: post_media_link_required_alert'); + expect(container.textContent).toContain('error: reply_media_link_required_alert'); + const mediaRequiredError = Array.from(container.querySelectorAll('div')).find((element) => element.textContent === 'error: reply_media_link_required_alert'); expect(mediaRequiredError?.className).toContain('error'); expect(testState.publishReplyMock).not.toHaveBeenCalled(); @@ -826,14 +842,14 @@ describe('ReplyModal', () => { testState.selectedText = 'Reply body'; testState.communities['music-posting.eth'] = { address: 'music-posting.eth', - features: { requirePostLink: false, requirePostLinkIsMedia: true }, + features: { requireReplyLink: false, requireReplyLinkIsMedia: true }, }; await renderReplyModal('/mu/thread/post-1'); await clickButtonByText('post'); - expect(container.textContent).not.toContain('post_media_link_required_alert'); + expect(container.textContent).not.toContain('reply_media_link_required_alert'); expect(testState.publishReplyMock).toHaveBeenCalledTimes(1); testState.publishReplyMock.mockClear(); @@ -845,6 +861,27 @@ describe('ReplyModal', () => { expect(testState.publishReplyMock).not.toHaveBeenCalled(); }); + it('rejects reply links when live community features disallow reply links', async () => { + testState.openEmpty = true; + testState.selectedText = 'Reply body'; + testState.communities['music-posting.eth'] = { + address: 'music-posting.eth', + features: { noReplyLinks: true }, + }; + + await renderReplyModal('/mu/thread/post-1'); + + const linkInput = container.querySelectorAll('input[type="text"]')[2]; + expect(linkInput.disabled).toBe(true); + + linkInput.disabled = false; + await dispatchInput(linkInput, 'https://example.com/reply.png'); + await clickButtonByText('post'); + + expect(container.textContent).toContain('error: reply_links_not_allowed_alert'); + expect(testState.publishReplyMock).not.toHaveBeenCalled(); + }); + it('publishes twimg query-format reply links with a path extension', async () => { testState.openEmpty = true; testState.selectedText = 'reply body'; @@ -887,7 +924,7 @@ describe('ReplyModal', () => { testState.selectedText = 'reply body'; testState.directoryByAddress['music-posting.eth'] = { address: 'music-posting.eth', - features: { requirePostLinkIsMedia: true }, + features: { requireReplyLinkIsMedia: true }, title: '/mu/ - Music', }; @@ -930,7 +967,7 @@ describe('ReplyModal', () => { testState.selectedText = 'reply body'; testState.directoryByAddress['music-posting.eth'] = { address: 'music-posting.eth', - features: { requirePostLinkIsMedia: true }, + features: { requireReplyLinkIsMedia: true }, title: '/mu/ - Music', }; @@ -960,7 +997,7 @@ describe('ReplyModal', () => { testState.selectedText = 'reply body'; testState.directoryByAddress['music-posting.eth'] = { address: 'music-posting.eth', - features: { requirePostLinkIsMedia: true }, + features: { requireReplyLinkIsMedia: true }, title: '/mu/ - Music', }; diff --git a/src/components/reply-modal/reply-modal.tsx b/src/components/reply-modal/reply-modal.tsx index c3ebcd44..5dcbad60 100644 --- a/src/components/reply-modal/reply-modal.tsx +++ b/src/components/reply-modal/reply-modal.tsx @@ -5,7 +5,13 @@ import type { TFunction } from 'i18next'; import { setAccount, useAccount } from '@bitsocial/bitsocial-react-hooks'; import { getExpiringMediaLinkAlert, getPublishFileDisplayName, getPublishLinkOptions } from '../../lib/utils/media-link-validation-utils'; import { getTwimgMediaFilePublishUrl } from '../../lib/utils/media-utils'; -import { getEffectivePublishLinkFeatures, getPublishLinkValidationError, getRequirePostLink, getRequirePostLinkIsMedia } from '../../lib/utils/publish-link-requirements'; +import { + getEffectiveReplyLinkFeatures, + getNoReplyLinks, + getPublishLinkValidationError, + getRequireReplyLink, + getRequireReplyLinkIsMedia, +} from '../../lib/utils/publish-link-requirements'; import { getCommentFlagOptionsForDirectory, getCommentFlagPublishOptionsForDirectory } from '../../lib/comment-flag-selection'; import { type DiceRoll, @@ -79,9 +85,10 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa const showOekakiControls = postOptionsDirectoryCode === 'i' || directoryEntry?.directoryCode === 'i'; const showTexButton = isMathDirectoryCode(postOptionsDirectoryCode) || isMathDirectoryCode(directoryEntry?.directoryCode); const communityFeatures = useCommunityField(communityAddress, (community) => community?.features); - const publishLinkFeatures = getEffectivePublishLinkFeatures(communityFeatures, directoryEntry?.features); - const requirePostLink = getRequirePostLink(publishLinkFeatures); - const requirePostLinkIsMedia = getRequirePostLinkIsMedia(publishLinkFeatures, isInAllView || isInSubscriptionsView); + const replyLinkFeatures = getEffectiveReplyLinkFeatures(communityFeatures, directoryEntry?.features); + const requireReplyLink = getRequireReplyLink(replyLinkFeatures); + const requireReplyLinkIsMedia = getRequireReplyLinkIsMedia(replyLinkFeatures, isInAllView || isInSubscriptionsView); + const noReplyLinks = getNoReplyLinks(replyLinkFeatures); const flagOptions = getCommentFlagOptionsForDirectory(directoryEntry); const { isResolvingExternalQuotes, publishReply, publishReplyError, publishReplyStateMessage, resetPublishReplyOptions, replyIndex, setPublishReplyOptions } = usePublishReply({ @@ -188,8 +195,12 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa const linkValidationError = getPublishLinkValidationError({ link: currentUrl, - requireLink: requirePostLink, - requireMedia: requirePostLinkIsMedia, + noLinks: noReplyLinks, + requireLink: requireReplyLink, + requireMedia: requireReplyLinkIsMedia, + requiredLinkAlertKey: 'reply_link_required_alert', + requiredMediaLinkAlertKey: 'reply_media_link_required_alert', + noLinksAlertKey: 'reply_links_not_allowed_alert', t, }); if (linkValidationError) { @@ -444,7 +455,7 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa noticeCountdown: youtubeThumbnailConversionCountdown, queueLinkConversion, } = useYouTubeThumbnailLinkConversion({ - enabled: requirePostLinkIsMedia, + enabled: requireReplyLinkIsMedia && !noReplyLinks, onContentChange: handleConvertedContentChange, onLinkChange: setLinkValue, textRef, @@ -538,7 +549,7 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa }; const uploadMode = useMediaHostingStore((state) => state.uploadMode); const showUploadControls = getShowUploadControls(uploadMode, isWebRuntime()); - const displayedFileName = getPublishFileDisplayName(url, uploadedFileName, requirePostLinkIsMedia); + const displayedFileName = getPublishFileDisplayName(url, uploadedFileName, requireReplyLinkIsMedia); const youtubeThumbnailConversionNotice = youtubeThumbnailConversionCountdown !== null ? t('youtube_thumbnail_link_conversion_notice', { count: youtubeThumbnailConversionCountdown }) : null; @@ -652,22 +663,22 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa { handleLinkChange(e.target.value); }} onBlur={handleLinkBlur} /> - {showOekakiControls && ( + {showOekakiControls && !noReplyLinks && (
Draw
)} - {showOekakiControls && isWebRuntime() ?
{OEKAKI_WEB_WARNING_TEXT}
: null} + {showOekakiControls && !noReplyLinks && isWebRuntime() ?
{OEKAKI_WEB_WARNING_TEXT}
: null} {flagOptions.length > 0 && (