mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(post form): respect reply link rules
This commit is contained in:
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "المساهمون",
|
"site_legal_meta_contributors_link": "المساهمون",
|
||||||
"post_link_required_alert": "يجب أن تنشر رابطًا.",
|
"post_link_required_alert": "يجب أن تنشر رابطًا.",
|
||||||
"post_media_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": "لا يُسمح بروابط الردود في هذه اللوحة."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "অবদানকারীরা",
|
"site_legal_meta_contributors_link": "অবদানকারীরা",
|
||||||
"post_link_required_alert": "আপনাকে একটি লিঙ্ক পোস্ট করতে হবে।",
|
"post_link_required_alert": "আপনাকে একটি লিঙ্ক পোস্ট করতে হবে।",
|
||||||
"post_media_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": "এই বোর্ডে উত্তরের লিঙ্ক অনুমোদিত নয়।"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "Přispěvatelé",
|
"site_legal_meta_contributors_link": "Přispěvatelé",
|
||||||
"post_link_required_alert": "Musíte přidat odkaz.",
|
"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_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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "Bidragydere",
|
"site_legal_meta_contributors_link": "Bidragydere",
|
||||||
"post_link_required_alert": "Du skal poste et link.",
|
"post_link_required_alert": "Du skal poste et link.",
|
||||||
"post_media_link_required_alert": "Du skal poste et billed- eller videolink.",
|
"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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "Mitwirkende",
|
"site_legal_meta_contributors_link": "Mitwirkende",
|
||||||
"post_link_required_alert": "Du musst einen Link posten.",
|
"post_link_required_alert": "Du musst einen Link posten.",
|
||||||
"post_media_link_required_alert": "Du musst einen Bild- oder Videolink 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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "Συνεισφέροντες",
|
"site_legal_meta_contributors_link": "Συνεισφέροντες",
|
||||||
"post_link_required_alert": "Πρέπει να δημοσιεύσετε έναν σύνδεσμο.",
|
"post_link_required_alert": "Πρέπει να δημοσιεύσετε έναν σύνδεσμο.",
|
||||||
"post_media_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": "Οι σύνδεσμοι στις απαντήσεις δεν επιτρέπονται σε αυτόν τον πίνακα."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -465,5 +465,8 @@
|
|||||||
"site_legal_meta_contributors_link": "Contributors",
|
"site_legal_meta_contributors_link": "Contributors",
|
||||||
"post_link_required_alert": "You must post a link.",
|
"post_link_required_alert": "You must post a link.",
|
||||||
"post_media_link_required_alert": "You must post an image or video 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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "Colaboradores",
|
"site_legal_meta_contributors_link": "Colaboradores",
|
||||||
"post_link_required_alert": "Debes publicar un enlace.",
|
"post_link_required_alert": "Debes publicar un enlace.",
|
||||||
"post_media_link_required_alert": "Debes publicar un enlace a una imagen o un video.",
|
"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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "مشارکتکنندگان",
|
"site_legal_meta_contributors_link": "مشارکتکنندگان",
|
||||||
"post_link_required_alert": "باید یک پیوند ارسال کنید.",
|
"post_link_required_alert": "باید یک پیوند ارسال کنید.",
|
||||||
"post_media_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": "پیوندها در پاسخها در این برد مجاز نیستند."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "Avustajat",
|
"site_legal_meta_contributors_link": "Avustajat",
|
||||||
"post_link_required_alert": "Sinun on julkaistava linkki.",
|
"post_link_required_alert": "Sinun on julkaistava linkki.",
|
||||||
"post_media_link_required_alert": "Sinun on julkaistava kuva- tai videolinkki.",
|
"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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "Mga Kontribyutor",
|
"site_legal_meta_contributors_link": "Mga Kontribyutor",
|
||||||
"post_link_required_alert": "Kailangan mong mag-post ng link.",
|
"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_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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "Contributeurs",
|
"site_legal_meta_contributors_link": "Contributeurs",
|
||||||
"post_link_required_alert": "Vous devez publier un lien.",
|
"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_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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "תורמים",
|
"site_legal_meta_contributors_link": "תורמים",
|
||||||
"post_link_required_alert": "עליך לפרסם קישור.",
|
"post_link_required_alert": "עליך לפרסם קישור.",
|
||||||
"post_media_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": "קישורים בתגובות אינם מותרים בלוח הזה."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "योगदानकर्ता",
|
"site_legal_meta_contributors_link": "योगदानकर्ता",
|
||||||
"post_link_required_alert": "आपको एक लिंक पोस्ट करना होगा।",
|
"post_link_required_alert": "आपको एक लिंक पोस्ट करना होगा।",
|
||||||
"post_media_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": "इस बोर्ड पर जवाबों में लिंक की अनुमति नहीं है।"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "Közreműködők",
|
"site_legal_meta_contributors_link": "Közreműködők",
|
||||||
"post_link_required_alert": "Közzé kell tenned egy linket.",
|
"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_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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "Kontributor",
|
"site_legal_meta_contributors_link": "Kontributor",
|
||||||
"post_link_required_alert": "Anda harus memposting tautan.",
|
"post_link_required_alert": "Anda harus memposting tautan.",
|
||||||
"post_media_link_required_alert": "Anda harus memposting tautan gambar atau video.",
|
"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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "Collaboratori",
|
"site_legal_meta_contributors_link": "Collaboratori",
|
||||||
"post_link_required_alert": "Devi pubblicare un link.",
|
"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_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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "貢献者",
|
"site_legal_meta_contributors_link": "貢献者",
|
||||||
"post_link_required_alert": "リンクを投稿する必要があります。",
|
"post_link_required_alert": "リンクを投稿する必要があります。",
|
||||||
"post_media_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": "この板では返信リンクは許可されていません。"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "기여자",
|
"site_legal_meta_contributors_link": "기여자",
|
||||||
"post_link_required_alert": "링크를 게시해야 합니다.",
|
"post_link_required_alert": "링크를 게시해야 합니다.",
|
||||||
"post_media_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": "이 게시판에서는 답글 링크가 허용되지 않습니다."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "योगदानकर्ते",
|
"site_legal_meta_contributors_link": "योगदानकर्ते",
|
||||||
"post_link_required_alert": "तुम्हाला एक लिंक पोस्ट करावी लागेल.",
|
"post_link_required_alert": "तुम्हाला एक लिंक पोस्ट करावी लागेल.",
|
||||||
"post_media_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": "या बोर्डवर उत्तरांमधील दुवे परवानगी नाहीत."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "Bijdragers",
|
"site_legal_meta_contributors_link": "Bijdragers",
|
||||||
"post_link_required_alert": "Je moet een link plaatsen.",
|
"post_link_required_alert": "Je moet een link plaatsen.",
|
||||||
"post_media_link_required_alert": "Je moet een afbeelding- of videolink 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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "Bidragsytere",
|
"site_legal_meta_contributors_link": "Bidragsytere",
|
||||||
"post_link_required_alert": "Du må poste en lenke.",
|
"post_link_required_alert": "Du må poste en lenke.",
|
||||||
"post_media_link_required_alert": "Du må poste en bilde- eller videolenke.",
|
"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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "Współtwórcy",
|
"site_legal_meta_contributors_link": "Współtwórcy",
|
||||||
"post_link_required_alert": "Musisz opublikować link.",
|
"post_link_required_alert": "Musisz opublikować link.",
|
||||||
"post_media_link_required_alert": "Musisz opublikować link do obrazu lub filmu.",
|
"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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "Colaboradores",
|
"site_legal_meta_contributors_link": "Colaboradores",
|
||||||
"post_link_required_alert": "Você deve postar um link.",
|
"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_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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "Contributori",
|
"site_legal_meta_contributors_link": "Contributori",
|
||||||
"post_link_required_alert": "Trebuie să postezi un link.",
|
"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_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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "Участники",
|
"site_legal_meta_contributors_link": "Участники",
|
||||||
"post_link_required_alert": "Вы должны опубликовать ссылку.",
|
"post_link_required_alert": "Вы должны опубликовать ссылку.",
|
||||||
"post_media_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": "Ссылки в ответах на этой доске запрещены."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "Kontribuesit",
|
"site_legal_meta_contributors_link": "Kontribuesit",
|
||||||
"post_link_required_alert": "Duhet të postosh një lidhje.",
|
"post_link_required_alert": "Duhet të postosh një lidhje.",
|
||||||
"post_media_link_required_alert": "Duhet të postosh një lidhje imazhi ose videoje.",
|
"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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "Bidragsgivare",
|
"site_legal_meta_contributors_link": "Bidragsgivare",
|
||||||
"post_link_required_alert": "Du måste posta en länk.",
|
"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_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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "సహకారులు",
|
"site_legal_meta_contributors_link": "సహకారులు",
|
||||||
"post_link_required_alert": "మీరు ఒక లింక్ పోస్ట్ చేయాలి.",
|
"post_link_required_alert": "మీరు ఒక లింక్ పోస్ట్ చేయాలి.",
|
||||||
"post_media_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": "ఈ బోర్డులో ప్రత్యుత్తర లింక్లకు అనుమతి లేదు."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "ผู้มีส่วนร่วม",
|
"site_legal_meta_contributors_link": "ผู้มีส่วนร่วม",
|
||||||
"post_link_required_alert": "คุณต้องโพสต์ลิงก์",
|
"post_link_required_alert": "คุณต้องโพสต์ลิงก์",
|
||||||
"post_media_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": "ไม่อนุญาตให้ใส่ลิงก์ในคำตอบบนบอร์ดนี้"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "Katkıda Bulunanlar",
|
"site_legal_meta_contributors_link": "Katkıda Bulunanlar",
|
||||||
"post_link_required_alert": "Bir bağlantı paylaşmalısınız.",
|
"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_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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "Учасники",
|
"site_legal_meta_contributors_link": "Учасники",
|
||||||
"post_link_required_alert": "Потрібно опублікувати посилання.",
|
"post_link_required_alert": "Потрібно опублікувати посилання.",
|
||||||
"post_media_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": "Посилання у відповідях на цій дошці заборонені."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "شراکت کنندگان",
|
"site_legal_meta_contributors_link": "شراکت کنندگان",
|
||||||
"post_link_required_alert": "آپ کو ایک لنک پوسٹ کرنا ہوگا۔",
|
"post_link_required_alert": "آپ کو ایک لنک پوسٹ کرنا ہوگا۔",
|
||||||
"post_media_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": "اس بورڈ پر جوابات میں لنکس کی اجازت نہیں ہے۔"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "Người đóng góp",
|
"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_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_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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,5 +446,8 @@
|
|||||||
"site_legal_meta_contributors_link": "贡献者",
|
"site_legal_meta_contributors_link": "贡献者",
|
||||||
"post_link_required_alert": "你必须发布一个链接。",
|
"post_link_required_alert": "你必须发布一个链接。",
|
||||||
"post_media_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": "此版块不允许回复链接。"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1783,6 +1783,93 @@ describe('PostForm', () => {
|
|||||||
expect(testState.publishReplyMock).toHaveBeenCalledTimes(1);
|
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<HTMLInputElement>('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<HTMLInputElement>('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 () => {
|
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 twimgLink = 'https://pbs.twimg.com/media/HJxnhNKWMAAhqFU?format=jpg&name=medium';
|
||||||
const publishLink = 'https://pbs.twimg.com/media/HJxnhNKWMAAhqFU.jpg';
|
const publishLink = 'https://pbs.twimg.com/media/HJxnhNKWMAAhqFU.jpg';
|
||||||
|
|||||||
@@ -7,7 +7,16 @@ import getShortAddress from '../../lib/get-short-address';
|
|||||||
import { communitiesPagesStore as useCommunitiesPagesStore } from '../../lib/bitsocial-internals/stores';
|
import { communitiesPagesStore as useCommunitiesPagesStore } from '../../lib/bitsocial-internals/stores';
|
||||||
import { getDisplayMediaInfoType, getLinkMediaInfo, getTwimgMediaFilePublishUrl } from '../../lib/utils/media-utils';
|
import { getDisplayMediaInfoType, getLinkMediaInfo, getTwimgMediaFilePublishUrl } from '../../lib/utils/media-utils';
|
||||||
import { getExpiringMediaLinkAlert, getPublishFileDisplayName, getPublishLinkOptions, isPublishFileMediaType } from '../../lib/utils/media-link-validation-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 {
|
import {
|
||||||
type DiceRoll,
|
type DiceRoll,
|
||||||
type FortuneEntry,
|
type FortuneEntry,
|
||||||
@@ -177,7 +186,7 @@ interface PostFormFieldsProps {
|
|||||||
subscriptions: string[];
|
subscriptions: string[];
|
||||||
communityAddress: string | undefined;
|
communityAddress: string | undefined;
|
||||||
rulesPath: string;
|
rulesPath: string;
|
||||||
requirePostLinkIsMedia: boolean;
|
requireCurrentLinkIsMedia: boolean;
|
||||||
flagOptions: CommentFlagSelectOption[];
|
flagOptions: CommentFlagSelectOption[];
|
||||||
flashTagOptions: FlashTagOption[];
|
flashTagOptions: FlashTagOption[];
|
||||||
showFlashTagSelector: boolean;
|
showFlashTagSelector: boolean;
|
||||||
@@ -232,7 +241,7 @@ const PostFormFields = ({
|
|||||||
subscriptions,
|
subscriptions,
|
||||||
communityAddress,
|
communityAddress,
|
||||||
rulesPath,
|
rulesPath,
|
||||||
requirePostLinkIsMedia,
|
requireCurrentLinkIsMedia,
|
||||||
flagOptions,
|
flagOptions,
|
||||||
flashTagOptions,
|
flashTagOptions,
|
||||||
showFlashTagSelector,
|
showFlashTagSelector,
|
||||||
@@ -367,15 +376,15 @@ const PostFormFields = ({
|
|||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{requirePostLinkIsMedia ? t('link_to_file') : t('link')}</td>
|
<td>{requireCurrentLinkIsMedia ? t('link_to_file') : t('link')}</td>
|
||||||
<td className={styles.linkField}>
|
<td className={styles.linkField}>
|
||||||
<input
|
<input
|
||||||
type='text'
|
type='text'
|
||||||
aria-label={requirePostLinkIsMedia ? t('link_to_file') : t('link')}
|
aria-label={requireCurrentLinkIsMedia ? t('link_to_file') : t('link')}
|
||||||
autoCorrect='off'
|
autoCorrect='off'
|
||||||
autoComplete='off'
|
autoComplete='off'
|
||||||
spellCheck='false'
|
spellCheck='false'
|
||||||
placeholder={requirePostLinkIsMedia ? FILE_LINK_PLACEHOLDER : undefined}
|
placeholder={requireCurrentLinkIsMedia ? FILE_LINK_PLACEHOLDER : undefined}
|
||||||
ref={urlRef}
|
ref={urlRef}
|
||||||
disabled={disableLinkInput}
|
disabled={disableLinkInput}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@@ -383,7 +392,7 @@ const PostFormFields = ({
|
|||||||
}}
|
}}
|
||||||
onBlur={handleLinkBlur}
|
onBlur={handleLinkBlur}
|
||||||
/>
|
/>
|
||||||
<span className={styles.linkType}> {url && <LinkTypePreviewer link={url} requireFile={requirePostLinkIsMedia} />}</span>
|
<span className={styles.linkType}> {url && <LinkTypePreviewer link={url} requireFile={requireCurrentLinkIsMedia} />}</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{showUploadControls && (
|
{showUploadControls && (
|
||||||
@@ -402,8 +411,12 @@ const PostFormFields = ({
|
|||||||
isUploading={isUploading}
|
isUploading={isUploading}
|
||||||
showUploadControls={showUploadControls}
|
showUploadControls={showUploadControls}
|
||||||
/>
|
/>
|
||||||
<span title={getPublishFileDisplayName(url, uploadedFileName, requirePostLinkIsMedia) || undefined}>
|
<span title={getPublishFileDisplayName(url, uploadedFileName, requireCurrentLinkIsMedia) || undefined}>
|
||||||
{isUploading ? <LoadingEllipsis string={t('uploading')} /> : getPostFormFileDisplayLabel(url, uploadedFileName, t('no_file_chosen'), requirePostLinkIsMedia)}
|
{isUploading ? (
|
||||||
|
<LoadingEllipsis string={t('uploading')} />
|
||||||
|
) : (
|
||||||
|
getPostFormFileDisplayLabel(url, uploadedFileName, t('no_file_chosen'), requireCurrentLinkIsMedia)
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -577,9 +590,14 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
|||||||
const postOptionsDirectoryCode = getPostOptionsDirectoryCode(directoryEntry, location.pathname);
|
const postOptionsDirectoryCode = getPostOptionsDirectoryCode(directoryEntry, location.pathname);
|
||||||
const showOekakiControls = postOptionsDirectoryCode === 'i' || directoryEntry?.directoryCode === 'i';
|
const showOekakiControls = postOptionsDirectoryCode === 'i' || directoryEntry?.directoryCode === 'i';
|
||||||
const communityFeatures = useCommunityField(effectiveBoardAddress, (community) => community?.features);
|
const communityFeatures = useCommunityField(effectiveBoardAddress, (community) => community?.features);
|
||||||
const publishLinkFeatures = getEffectivePublishLinkFeatures(communityFeatures, directoryEntry?.features);
|
const postLinkFeatures = getEffectivePostLinkFeatures(communityFeatures, directoryEntry?.features);
|
||||||
const requirePostLink = getRequirePostLink(publishLinkFeatures);
|
const replyLinkFeatures = getEffectiveReplyLinkFeatures(communityFeatures, directoryEntry?.features);
|
||||||
const requirePostLinkIsMedia = getRequirePostLinkIsMedia(publishLinkFeatures, isInAllView || isInSubscriptionsView);
|
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 flagOptions = getCommentFlagOptionsForDirectory(directoryEntry);
|
||||||
const showFlashUploadPrompt = isFlashDirectoryCode(postOptionsDirectoryCode);
|
const showFlashUploadPrompt = isFlashDirectoryCode(postOptionsDirectoryCode);
|
||||||
const showFlashTagSelector = showFlashUploadPrompt && !isInPostView;
|
const showFlashTagSelector = showFlashUploadPrompt && !isInPostView;
|
||||||
@@ -687,6 +705,8 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
|||||||
link: currentUrl,
|
link: currentUrl,
|
||||||
requireLink: requirePostLink,
|
requireLink: requirePostLink,
|
||||||
requireMedia: requirePostLinkIsMedia,
|
requireMedia: requirePostLinkIsMedia,
|
||||||
|
requiredLinkAlertKey: 'post_link_required_alert',
|
||||||
|
requiredMediaLinkAlertKey: 'post_media_link_required_alert',
|
||||||
t,
|
t,
|
||||||
});
|
});
|
||||||
if (linkValidationError) {
|
if (linkValidationError) {
|
||||||
@@ -816,8 +836,12 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
|||||||
|
|
||||||
const linkValidationError = getPublishLinkValidationError({
|
const linkValidationError = getPublishLinkValidationError({
|
||||||
link: currentUrl,
|
link: currentUrl,
|
||||||
requireLink: requirePostLink,
|
noLinks: noReplyLinks,
|
||||||
requireMedia: requirePostLinkIsMedia,
|
requireLink: requireReplyLink,
|
||||||
|
requireMedia: requireReplyLinkIsMedia,
|
||||||
|
requiredLinkAlertKey: 'reply_link_required_alert',
|
||||||
|
requiredMediaLinkAlertKey: 'reply_media_link_required_alert',
|
||||||
|
noLinksAlertKey: 'reply_links_not_allowed_alert',
|
||||||
t,
|
t,
|
||||||
});
|
});
|
||||||
if (linkValidationError) {
|
if (linkValidationError) {
|
||||||
@@ -856,7 +880,7 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
|||||||
noticeCountdown: youtubeThumbnailConversionCountdown,
|
noticeCountdown: youtubeThumbnailConversionCountdown,
|
||||||
queueLinkConversion,
|
queueLinkConversion,
|
||||||
} = useYouTubeThumbnailLinkConversion({
|
} = useYouTubeThumbnailLinkConversion({
|
||||||
enabled: requirePostLinkIsMedia,
|
enabled: requireCurrentLinkIsMedia && !(isInPostView && noReplyLinks),
|
||||||
onContentChange: handleContentValueChange,
|
onContentChange: handleContentValueChange,
|
||||||
onLinkChange: setLinkValue,
|
onLinkChange: setLinkValue,
|
||||||
textRef,
|
textRef,
|
||||||
@@ -954,13 +978,13 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
|||||||
handleLinkChange={handleLinkChange}
|
handleLinkChange={handleLinkChange}
|
||||||
handleLinkBlur={handleLinkBlur}
|
handleLinkBlur={handleLinkBlur}
|
||||||
handleOptionsChange={handleOptionsChange}
|
handleOptionsChange={handleOptionsChange}
|
||||||
disableLinkInput={isUploading || youtubeThumbnailConversionCountdown !== null}
|
disableLinkInput={isUploading || youtubeThumbnailConversionCountdown !== null || (isInPostView && noReplyLinks)}
|
||||||
setPublishPostOptions={setPublishPostOptions}
|
setPublishPostOptions={setPublishPostOptions}
|
||||||
setPublishReplyOptions={setPublishReplyOptions}
|
setPublishReplyOptions={setPublishReplyOptions}
|
||||||
isUploading={isUploading}
|
isUploading={isUploading}
|
||||||
uploadedFileName={uploadedFileName}
|
uploadedFileName={uploadedFileName}
|
||||||
showUploadControls={showUploadControls}
|
showUploadControls={showUploadControls && !(isInPostView && noReplyLinks)}
|
||||||
showOekakiControls={showOekakiControls}
|
showOekakiControls={showOekakiControls && !(isInPostView && noReplyLinks)}
|
||||||
showSpoilerForPost={showSpoilerForPost}
|
showSpoilerForPost={showSpoilerForPost}
|
||||||
showSpoilerForReply={showSpoilerForReply}
|
showSpoilerForReply={showSpoilerForReply}
|
||||||
isInAllView={isInAllView}
|
isInAllView={isInAllView}
|
||||||
@@ -971,7 +995,7 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
|||||||
subscriptions={subscriptions}
|
subscriptions={subscriptions}
|
||||||
communityAddress={communityAddress}
|
communityAddress={communityAddress}
|
||||||
rulesPath={rulesPath}
|
rulesPath={rulesPath}
|
||||||
requirePostLinkIsMedia={requirePostLinkIsMedia}
|
requireCurrentLinkIsMedia={requireCurrentLinkIsMedia}
|
||||||
flagOptions={flagOptions}
|
flagOptions={flagOptions}
|
||||||
flashTagOptions={FLASH_TAG_OPTIONS}
|
flashTagOptions={FLASH_TAG_OPTIONS}
|
||||||
showFlashTagSelector={showFlashTagSelector}
|
showFlashTagSelector={showFlashTagSelector}
|
||||||
|
|||||||
@@ -530,7 +530,7 @@ describe('ReplyModal', () => {
|
|||||||
testState.directoryByAddress['oekaki-posting.bso'] = {
|
testState.directoryByAddress['oekaki-posting.bso'] = {
|
||||||
address: 'oekaki-posting.bso',
|
address: 'oekaki-posting.bso',
|
||||||
directoryCode: 'i',
|
directoryCode: 'i',
|
||||||
features: { requirePostLink: true, requirePostLinkIsMedia: true },
|
features: { requireReplyLink: true, requireReplyLinkIsMedia: true },
|
||||||
title: '/i/ - Oekaki',
|
title: '/i/ - Oekaki',
|
||||||
};
|
};
|
||||||
testState.communities['oekaki-posting.bso'] = { address: 'oekaki-posting.bso' };
|
testState.communities['oekaki-posting.bso'] = { address: 'oekaki-posting.bso' };
|
||||||
@@ -765,7 +765,7 @@ describe('ReplyModal', () => {
|
|||||||
expect(testState.publishReplyMock).toHaveBeenCalledTimes(1);
|
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.openEmpty = true;
|
||||||
testState.selectedText = 'Reply body';
|
testState.selectedText = 'Reply body';
|
||||||
testState.communities['music-posting.eth'] = {
|
testState.communities['music-posting.eth'] = {
|
||||||
@@ -777,8 +777,24 @@ describe('ReplyModal', () => {
|
|||||||
|
|
||||||
await clickButtonByText('post');
|
await clickButtonByText('post');
|
||||||
|
|
||||||
expect(container.textContent).toContain('error: post_link_required_alert');
|
expect(container.textContent).not.toContain('post_link_required_alert');
|
||||||
const linkRequiredError = Array.from(container.querySelectorAll('div')).find((element) => element.textContent === 'error: 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(linkRequiredError?.className).toContain('error');
|
||||||
expect(testState.publishReplyMock).not.toHaveBeenCalled();
|
expect(testState.publishReplyMock).not.toHaveBeenCalled();
|
||||||
|
|
||||||
@@ -790,20 +806,20 @@ describe('ReplyModal', () => {
|
|||||||
expect(testState.publishReplyMock).toHaveBeenCalledTimes(1);
|
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.openEmpty = true;
|
||||||
testState.selectedText = 'Reply body';
|
testState.selectedText = 'Reply body';
|
||||||
testState.communities['music-posting.eth'] = {
|
testState.communities['music-posting.eth'] = {
|
||||||
address: 'music-posting.eth',
|
address: 'music-posting.eth',
|
||||||
features: { requirePostLink: true, requirePostLinkIsMedia: true },
|
features: { requireReplyLink: true, requireReplyLinkIsMedia: true },
|
||||||
};
|
};
|
||||||
|
|
||||||
await renderReplyModal('/mu/thread/post-1');
|
await renderReplyModal('/mu/thread/post-1');
|
||||||
|
|
||||||
await clickButtonByText('post');
|
await clickButtonByText('post');
|
||||||
|
|
||||||
expect(container.textContent).toContain('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: post_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(mediaRequiredError?.className).toContain('error');
|
||||||
expect(testState.publishReplyMock).not.toHaveBeenCalled();
|
expect(testState.publishReplyMock).not.toHaveBeenCalled();
|
||||||
|
|
||||||
@@ -826,14 +842,14 @@ describe('ReplyModal', () => {
|
|||||||
testState.selectedText = 'Reply body';
|
testState.selectedText = 'Reply body';
|
||||||
testState.communities['music-posting.eth'] = {
|
testState.communities['music-posting.eth'] = {
|
||||||
address: 'music-posting.eth',
|
address: 'music-posting.eth',
|
||||||
features: { requirePostLink: false, requirePostLinkIsMedia: true },
|
features: { requireReplyLink: false, requireReplyLinkIsMedia: true },
|
||||||
};
|
};
|
||||||
|
|
||||||
await renderReplyModal('/mu/thread/post-1');
|
await renderReplyModal('/mu/thread/post-1');
|
||||||
|
|
||||||
await clickButtonByText('post');
|
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);
|
expect(testState.publishReplyMock).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
testState.publishReplyMock.mockClear();
|
testState.publishReplyMock.mockClear();
|
||||||
@@ -845,6 +861,27 @@ describe('ReplyModal', () => {
|
|||||||
expect(testState.publishReplyMock).not.toHaveBeenCalled();
|
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<HTMLInputElement>('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 () => {
|
it('publishes twimg query-format reply links with a path extension', async () => {
|
||||||
testState.openEmpty = true;
|
testState.openEmpty = true;
|
||||||
testState.selectedText = 'reply body';
|
testState.selectedText = 'reply body';
|
||||||
@@ -887,7 +924,7 @@ describe('ReplyModal', () => {
|
|||||||
testState.selectedText = 'reply body';
|
testState.selectedText = 'reply body';
|
||||||
testState.directoryByAddress['music-posting.eth'] = {
|
testState.directoryByAddress['music-posting.eth'] = {
|
||||||
address: 'music-posting.eth',
|
address: 'music-posting.eth',
|
||||||
features: { requirePostLinkIsMedia: true },
|
features: { requireReplyLinkIsMedia: true },
|
||||||
title: '/mu/ - Music',
|
title: '/mu/ - Music',
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -930,7 +967,7 @@ describe('ReplyModal', () => {
|
|||||||
testState.selectedText = 'reply body';
|
testState.selectedText = 'reply body';
|
||||||
testState.directoryByAddress['music-posting.eth'] = {
|
testState.directoryByAddress['music-posting.eth'] = {
|
||||||
address: 'music-posting.eth',
|
address: 'music-posting.eth',
|
||||||
features: { requirePostLinkIsMedia: true },
|
features: { requireReplyLinkIsMedia: true },
|
||||||
title: '/mu/ - Music',
|
title: '/mu/ - Music',
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -960,7 +997,7 @@ describe('ReplyModal', () => {
|
|||||||
testState.selectedText = 'reply body';
|
testState.selectedText = 'reply body';
|
||||||
testState.directoryByAddress['music-posting.eth'] = {
|
testState.directoryByAddress['music-posting.eth'] = {
|
||||||
address: 'music-posting.eth',
|
address: 'music-posting.eth',
|
||||||
features: { requirePostLinkIsMedia: true },
|
features: { requireReplyLinkIsMedia: true },
|
||||||
title: '/mu/ - Music',
|
title: '/mu/ - Music',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,13 @@ import type { TFunction } from 'i18next';
|
|||||||
import { setAccount, useAccount } from '@bitsocial/bitsocial-react-hooks';
|
import { setAccount, useAccount } from '@bitsocial/bitsocial-react-hooks';
|
||||||
import { getExpiringMediaLinkAlert, getPublishFileDisplayName, getPublishLinkOptions } from '../../lib/utils/media-link-validation-utils';
|
import { getExpiringMediaLinkAlert, getPublishFileDisplayName, getPublishLinkOptions } from '../../lib/utils/media-link-validation-utils';
|
||||||
import { getTwimgMediaFilePublishUrl } from '../../lib/utils/media-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 { getCommentFlagOptionsForDirectory, getCommentFlagPublishOptionsForDirectory } from '../../lib/comment-flag-selection';
|
||||||
import {
|
import {
|
||||||
type DiceRoll,
|
type DiceRoll,
|
||||||
@@ -79,9 +85,10 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
|
|||||||
const showOekakiControls = postOptionsDirectoryCode === 'i' || directoryEntry?.directoryCode === 'i';
|
const showOekakiControls = postOptionsDirectoryCode === 'i' || directoryEntry?.directoryCode === 'i';
|
||||||
const showTexButton = isMathDirectoryCode(postOptionsDirectoryCode) || isMathDirectoryCode(directoryEntry?.directoryCode);
|
const showTexButton = isMathDirectoryCode(postOptionsDirectoryCode) || isMathDirectoryCode(directoryEntry?.directoryCode);
|
||||||
const communityFeatures = useCommunityField(communityAddress, (community) => community?.features);
|
const communityFeatures = useCommunityField(communityAddress, (community) => community?.features);
|
||||||
const publishLinkFeatures = getEffectivePublishLinkFeatures(communityFeatures, directoryEntry?.features);
|
const replyLinkFeatures = getEffectiveReplyLinkFeatures(communityFeatures, directoryEntry?.features);
|
||||||
const requirePostLink = getRequirePostLink(publishLinkFeatures);
|
const requireReplyLink = getRequireReplyLink(replyLinkFeatures);
|
||||||
const requirePostLinkIsMedia = getRequirePostLinkIsMedia(publishLinkFeatures, isInAllView || isInSubscriptionsView);
|
const requireReplyLinkIsMedia = getRequireReplyLinkIsMedia(replyLinkFeatures, isInAllView || isInSubscriptionsView);
|
||||||
|
const noReplyLinks = getNoReplyLinks(replyLinkFeatures);
|
||||||
const flagOptions = getCommentFlagOptionsForDirectory(directoryEntry);
|
const flagOptions = getCommentFlagOptionsForDirectory(directoryEntry);
|
||||||
const { isResolvingExternalQuotes, publishReply, publishReplyError, publishReplyStateMessage, resetPublishReplyOptions, replyIndex, setPublishReplyOptions } =
|
const { isResolvingExternalQuotes, publishReply, publishReplyError, publishReplyStateMessage, resetPublishReplyOptions, replyIndex, setPublishReplyOptions } =
|
||||||
usePublishReply({
|
usePublishReply({
|
||||||
@@ -188,8 +195,12 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
|
|||||||
|
|
||||||
const linkValidationError = getPublishLinkValidationError({
|
const linkValidationError = getPublishLinkValidationError({
|
||||||
link: currentUrl,
|
link: currentUrl,
|
||||||
requireLink: requirePostLink,
|
noLinks: noReplyLinks,
|
||||||
requireMedia: requirePostLinkIsMedia,
|
requireLink: requireReplyLink,
|
||||||
|
requireMedia: requireReplyLinkIsMedia,
|
||||||
|
requiredLinkAlertKey: 'reply_link_required_alert',
|
||||||
|
requiredMediaLinkAlertKey: 'reply_media_link_required_alert',
|
||||||
|
noLinksAlertKey: 'reply_links_not_allowed_alert',
|
||||||
t,
|
t,
|
||||||
});
|
});
|
||||||
if (linkValidationError) {
|
if (linkValidationError) {
|
||||||
@@ -444,7 +455,7 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
|
|||||||
noticeCountdown: youtubeThumbnailConversionCountdown,
|
noticeCountdown: youtubeThumbnailConversionCountdown,
|
||||||
queueLinkConversion,
|
queueLinkConversion,
|
||||||
} = useYouTubeThumbnailLinkConversion({
|
} = useYouTubeThumbnailLinkConversion({
|
||||||
enabled: requirePostLinkIsMedia,
|
enabled: requireReplyLinkIsMedia && !noReplyLinks,
|
||||||
onContentChange: handleConvertedContentChange,
|
onContentChange: handleConvertedContentChange,
|
||||||
onLinkChange: setLinkValue,
|
onLinkChange: setLinkValue,
|
||||||
textRef,
|
textRef,
|
||||||
@@ -538,7 +549,7 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
|
|||||||
};
|
};
|
||||||
const uploadMode = useMediaHostingStore((state) => state.uploadMode);
|
const uploadMode = useMediaHostingStore((state) => state.uploadMode);
|
||||||
const showUploadControls = getShowUploadControls(uploadMode, isWebRuntime());
|
const showUploadControls = getShowUploadControls(uploadMode, isWebRuntime());
|
||||||
const displayedFileName = getPublishFileDisplayName(url, uploadedFileName, requirePostLinkIsMedia);
|
const displayedFileName = getPublishFileDisplayName(url, uploadedFileName, requireReplyLinkIsMedia);
|
||||||
const youtubeThumbnailConversionNotice =
|
const youtubeThumbnailConversionNotice =
|
||||||
youtubeThumbnailConversionCountdown !== null ? t('youtube_thumbnail_link_conversion_notice', { count: youtubeThumbnailConversionCountdown }) : null;
|
youtubeThumbnailConversionCountdown !== null ? t('youtube_thumbnail_link_conversion_notice', { count: youtubeThumbnailConversionCountdown }) : null;
|
||||||
|
|
||||||
@@ -652,22 +663,22 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
|
|||||||
<input
|
<input
|
||||||
type='text'
|
type='text'
|
||||||
ref={urlRef}
|
ref={urlRef}
|
||||||
aria-label={requirePostLinkIsMedia ? t('link_to_file') : t('link')}
|
aria-label={requireReplyLinkIsMedia ? t('link_to_file') : t('link')}
|
||||||
placeholder={requirePostLinkIsMedia ? FILE_LINK_PLACEHOLDER : capitalize(t('link'))}
|
placeholder={requireReplyLinkIsMedia ? FILE_LINK_PLACEHOLDER : capitalize(t('link'))}
|
||||||
disabled={isUploading}
|
disabled={isUploading || noReplyLinks}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
handleLinkChange(e.target.value);
|
handleLinkChange(e.target.value);
|
||||||
}}
|
}}
|
||||||
onBlur={handleLinkBlur}
|
onBlur={handleLinkBlur}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{showOekakiControls && (
|
{showOekakiControls && !noReplyLinks && (
|
||||||
<div className={styles.oekakiRow}>
|
<div className={styles.oekakiRow}>
|
||||||
<span className={styles.oekakiLabel}>Draw</span>
|
<span className={styles.oekakiLabel}>Draw</span>
|
||||||
<OekakiDrawingControls className={styles.oekakiControls} disabled={isUploading} uploadFile={uploadFile} onClearUploadedUrl={handleOekakiClearUploadedUrl} />
|
<OekakiDrawingControls className={styles.oekakiControls} disabled={isUploading} uploadFile={uploadFile} onClearUploadedUrl={handleOekakiClearUploadedUrl} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{showOekakiControls && isWebRuntime() ? <div className={styles.oekakiWarning}>{OEKAKI_WEB_WARNING_TEXT}</div> : null}
|
{showOekakiControls && !noReplyLinks && isWebRuntime() ? <div className={styles.oekakiWarning}>{OEKAKI_WEB_WARNING_TEXT}</div> : null}
|
||||||
{flagOptions.length > 0 && (
|
{flagOptions.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<select
|
<select
|
||||||
@@ -687,7 +698,7 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className={styles.footer}>
|
<div className={styles.footer}>
|
||||||
{showUploadControls && (
|
{showUploadControls && !noReplyLinks && (
|
||||||
<span className={styles.uploadContainer}>
|
<span className={styles.uploadContainer}>
|
||||||
<span className={styles.uploadButton}>
|
<span className={styles.uploadButton}>
|
||||||
<button type='button' onClick={handleUpload} disabled={isUploading}>
|
<button type='button' onClick={handleUpload} disabled={isUploading}>
|
||||||
|
|||||||
@@ -1,40 +1,105 @@
|
|||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
import type { TFunction } from 'i18next';
|
import type { TFunction } from 'i18next';
|
||||||
import { getEffectivePublishLinkFeatures, getPublishLinkValidationError, getRequirePostLink, getRequirePostLinkIsMedia } from '../publish-link-requirements';
|
import {
|
||||||
|
getEffectivePostLinkFeatures,
|
||||||
|
getEffectiveReplyLinkFeatures,
|
||||||
|
getNoReplyLinks,
|
||||||
|
getPublishLinkValidationError,
|
||||||
|
getRequirePostLink,
|
||||||
|
getRequirePostLinkIsMedia,
|
||||||
|
getRequireReplyLink,
|
||||||
|
getRequireReplyLinkIsMedia,
|
||||||
|
} from '../publish-link-requirements';
|
||||||
|
|
||||||
const t = ((key: string) => key) as TFunction;
|
const t = ((key: string) => key) as TFunction;
|
||||||
|
|
||||||
describe('getEffectivePublishLinkFeatures', () => {
|
describe('getEffectivePostLinkFeatures', () => {
|
||||||
it('uses live community feature values before directory defaults', () => {
|
it('uses live community feature values before directory defaults', () => {
|
||||||
expect(getEffectivePublishLinkFeatures({ requirePostLink: false }, { requirePostLink: true, requirePostLinkIsMedia: true })).toEqual({
|
expect(getEffectivePostLinkFeatures({ requirePostLink: false }, { requirePostLink: true, requirePostLinkIsMedia: true })).toEqual({
|
||||||
requirePostLink: false,
|
requirePostLink: false,
|
||||||
requirePostLinkIsMedia: true,
|
requirePostLinkIsMedia: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('falls back per feature when live community features are partial', () => {
|
it('falls back per feature when live community features are partial', () => {
|
||||||
expect(getEffectivePublishLinkFeatures({ requirePostLinkIsMedia: false }, { requirePostLink: true, requirePostLinkIsMedia: true })).toEqual({
|
expect(getEffectivePostLinkFeatures({ requirePostLinkIsMedia: false }, { requirePostLink: true, requirePostLinkIsMedia: true })).toEqual({
|
||||||
requirePostLink: true,
|
requirePostLink: true,
|
||||||
requirePostLinkIsMedia: false,
|
requirePostLinkIsMedia: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getEffectiveReplyLinkFeatures', () => {
|
||||||
|
it('uses reply features separately from post features', () => {
|
||||||
|
expect(
|
||||||
|
getEffectiveReplyLinkFeatures(
|
||||||
|
{ requirePostLink: true, requirePostLinkIsMedia: true, requireReplyLink: false, noReplyLinks: true },
|
||||||
|
{ requireReplyLink: true, requireReplyLinkIsMedia: true, noReplyLinks: false },
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
requireReplyLink: false,
|
||||||
|
requireReplyLinkIsMedia: true,
|
||||||
|
noReplyLinks: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('getPublishLinkValidationError', () => {
|
describe('getPublishLinkValidationError', () => {
|
||||||
it('does not require an empty link when only media links are enabled', () => {
|
it('does not require an empty link when only media links are enabled', () => {
|
||||||
expect(getPublishLinkValidationError({ link: '', requireLink: false, requireMedia: true, t })).toBeNull();
|
expect(
|
||||||
|
getPublishLinkValidationError({
|
||||||
|
link: '',
|
||||||
|
requireLink: false,
|
||||||
|
requireMedia: true,
|
||||||
|
requiredLinkAlertKey: 'post_link_required_alert',
|
||||||
|
requiredMediaLinkAlertKey: 'post_media_link_required_alert',
|
||||||
|
t,
|
||||||
|
}),
|
||||||
|
).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('requires a media link when both link and media requirements are enabled', () => {
|
it('requires a media link when both link and media requirements are enabled', () => {
|
||||||
expect(getPublishLinkValidationError({ link: '', requireLink: true, requireMedia: true, t })).toBe('error: post_media_link_required_alert');
|
expect(
|
||||||
|
getPublishLinkValidationError({
|
||||||
|
link: '',
|
||||||
|
requireLink: true,
|
||||||
|
requireMedia: true,
|
||||||
|
requiredLinkAlertKey: 'post_link_required_alert',
|
||||||
|
requiredMediaLinkAlertKey: 'post_media_link_required_alert',
|
||||||
|
t,
|
||||||
|
}),
|
||||||
|
).toBe('error: post_media_link_required_alert');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects reply links when links are disabled', () => {
|
||||||
|
expect(
|
||||||
|
getPublishLinkValidationError({
|
||||||
|
link: 'https://example.com/reply.png',
|
||||||
|
noLinks: true,
|
||||||
|
requireLink: false,
|
||||||
|
requireMedia: true,
|
||||||
|
requiredLinkAlertKey: 'reply_link_required_alert',
|
||||||
|
requiredMediaLinkAlertKey: 'reply_media_link_required_alert',
|
||||||
|
noLinksAlertKey: 'reply_links_not_allowed_alert',
|
||||||
|
t,
|
||||||
|
}),
|
||||||
|
).toBe('error: reply_links_not_allowed_alert');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('publish link feature helpers', () => {
|
describe('publish link feature helpers', () => {
|
||||||
it('does not treat media-only validation as a required link', () => {
|
it('does not treat media-only validation as a required link', () => {
|
||||||
const features = getEffectivePublishLinkFeatures({ requirePostLink: false, requirePostLinkIsMedia: true }, undefined);
|
const features = getEffectivePostLinkFeatures({ requirePostLink: false, requirePostLinkIsMedia: true }, undefined);
|
||||||
|
|
||||||
expect(getRequirePostLink(features)).toBe(false);
|
expect(getRequirePostLink(features)).toBe(false);
|
||||||
expect(getRequirePostLinkIsMedia(features, false)).toBe(true);
|
expect(getRequirePostLinkIsMedia(features, false)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not treat post link requirements as reply link requirements', () => {
|
||||||
|
const features = getEffectiveReplyLinkFeatures({ requirePostLink: true, requirePostLinkIsMedia: true }, undefined);
|
||||||
|
|
||||||
|
expect(getRequireReplyLink(features)).toBe(false);
|
||||||
|
expect(getRequireReplyLinkIsMedia(features, false)).toBe(false);
|
||||||
|
expect(getNoReplyLinks(features)).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ interface DirectoryFeatures {
|
|||||||
hasFlags?: boolean;
|
hasFlags?: boolean;
|
||||||
requirePostLink?: boolean;
|
requirePostLink?: boolean;
|
||||||
requirePostLinkIsMedia?: boolean;
|
requirePostLinkIsMedia?: boolean;
|
||||||
|
requireReplyLink?: boolean;
|
||||||
|
requireReplyLinkIsMedia?: boolean;
|
||||||
|
noReplyLinks?: boolean;
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,26 @@ import { isValidPublishURL } from './url-utils';
|
|||||||
interface PublishLinkFeatures {
|
interface PublishLinkFeatures {
|
||||||
requirePostLink?: unknown;
|
requirePostLink?: unknown;
|
||||||
requirePostLinkIsMedia?: unknown;
|
requirePostLinkIsMedia?: unknown;
|
||||||
|
requireReplyLink?: unknown;
|
||||||
|
requireReplyLinkIsMedia?: unknown;
|
||||||
|
noReplyLinks?: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getEffectivePublishLinkFeatures = (
|
interface EffectivePostLinkFeatures {
|
||||||
|
requirePostLink?: unknown;
|
||||||
|
requirePostLinkIsMedia?: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EffectiveReplyLinkFeatures {
|
||||||
|
requireReplyLink?: unknown;
|
||||||
|
requireReplyLinkIsMedia?: unknown;
|
||||||
|
noReplyLinks?: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getEffectivePostLinkFeatures = (
|
||||||
communityFeatures: PublishLinkFeatures | undefined,
|
communityFeatures: PublishLinkFeatures | undefined,
|
||||||
directoryFeatures: PublishLinkFeatures | undefined,
|
directoryFeatures: PublishLinkFeatures | undefined,
|
||||||
): PublishLinkFeatures | undefined => {
|
): EffectivePostLinkFeatures | undefined => {
|
||||||
const requirePostLink = communityFeatures?.requirePostLink ?? directoryFeatures?.requirePostLink;
|
const requirePostLink = communityFeatures?.requirePostLink ?? directoryFeatures?.requirePostLink;
|
||||||
const requirePostLinkIsMedia = communityFeatures?.requirePostLinkIsMedia ?? directoryFeatures?.requirePostLinkIsMedia;
|
const requirePostLinkIsMedia = communityFeatures?.requirePostLinkIsMedia ?? directoryFeatures?.requirePostLinkIsMedia;
|
||||||
|
|
||||||
@@ -24,24 +38,61 @@ export const getEffectivePublishLinkFeatures = (
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getRequirePostLink = (features: PublishLinkFeatures | undefined): boolean => Boolean(features?.requirePostLink);
|
export const getEffectiveReplyLinkFeatures = (
|
||||||
|
communityFeatures: PublishLinkFeatures | undefined,
|
||||||
|
directoryFeatures: PublishLinkFeatures | undefined,
|
||||||
|
): EffectiveReplyLinkFeatures | undefined => {
|
||||||
|
const requireReplyLink = communityFeatures?.requireReplyLink ?? directoryFeatures?.requireReplyLink;
|
||||||
|
const requireReplyLinkIsMedia = communityFeatures?.requireReplyLinkIsMedia ?? directoryFeatures?.requireReplyLinkIsMedia;
|
||||||
|
const noReplyLinks = communityFeatures?.noReplyLinks ?? directoryFeatures?.noReplyLinks;
|
||||||
|
|
||||||
export const getRequirePostLinkIsMedia = (features: PublishLinkFeatures | undefined, useMediaDefault: boolean): boolean =>
|
if (requireReplyLink === undefined && requireReplyLinkIsMedia === undefined && noReplyLinks === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
requireReplyLink,
|
||||||
|
requireReplyLinkIsMedia,
|
||||||
|
noReplyLinks,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getRequirePostLink = (features: EffectivePostLinkFeatures | undefined): boolean => Boolean(features?.requirePostLink);
|
||||||
|
|
||||||
|
export const getRequirePostLinkIsMedia = (features: EffectivePostLinkFeatures | undefined, useMediaDefault: boolean): boolean =>
|
||||||
Boolean(features?.requirePostLinkIsMedia) || (features?.requirePostLinkIsMedia === undefined && useMediaDefault);
|
Boolean(features?.requirePostLinkIsMedia) || (features?.requirePostLinkIsMedia === undefined && useMediaDefault);
|
||||||
|
|
||||||
|
export const getRequireReplyLink = (features: EffectiveReplyLinkFeatures | undefined): boolean => Boolean(features?.requireReplyLink);
|
||||||
|
|
||||||
|
export const getRequireReplyLinkIsMedia = (features: EffectiveReplyLinkFeatures | undefined, useMediaDefault: boolean): boolean =>
|
||||||
|
Boolean(features?.requireReplyLinkIsMedia) || (features?.requireReplyLinkIsMedia === undefined && useMediaDefault);
|
||||||
|
|
||||||
|
export const getNoReplyLinks = (features: EffectiveReplyLinkFeatures | undefined): boolean => Boolean(features?.noReplyLinks);
|
||||||
|
|
||||||
export const getPublishLinkValidationError = ({
|
export const getPublishLinkValidationError = ({
|
||||||
link,
|
link,
|
||||||
|
noLinks = false,
|
||||||
requireLink,
|
requireLink,
|
||||||
requireMedia,
|
requireMedia,
|
||||||
|
requiredLinkAlertKey,
|
||||||
|
requiredMediaLinkAlertKey,
|
||||||
|
noLinksAlertKey,
|
||||||
t,
|
t,
|
||||||
}: {
|
}: {
|
||||||
link: string;
|
link: string;
|
||||||
|
noLinks?: boolean;
|
||||||
requireLink: boolean;
|
requireLink: boolean;
|
||||||
requireMedia: boolean;
|
requireMedia: boolean;
|
||||||
|
requiredLinkAlertKey: string;
|
||||||
|
requiredMediaLinkAlertKey: string;
|
||||||
|
noLinksAlertKey?: string;
|
||||||
t: TFunction;
|
t: TFunction;
|
||||||
}): string | null => {
|
}): string | null => {
|
||||||
|
if (link && noLinks && noLinksAlertKey) {
|
||||||
|
return `${t('error')}: ${t(noLinksAlertKey)}`;
|
||||||
|
}
|
||||||
if (!link && requireLink) {
|
if (!link && requireLink) {
|
||||||
return `${t('error')}: ${t(requireMedia ? 'post_media_link_required_alert' : 'post_link_required_alert')}`;
|
return `${t('error')}: ${t(requireMedia ? requiredMediaLinkAlertKey : requiredLinkAlertKey)}`;
|
||||||
}
|
}
|
||||||
if (link && !isValidPublishURL(link)) {
|
if (link && !isValidPublishURL(link)) {
|
||||||
return `${t('error')}: ${t('invalid_url_alert')}`;
|
return `${t('error')}: ${t('invalid_url_alert')}`;
|
||||||
|
|||||||
Reference in New Issue
Block a user