mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
@@ -10,6 +10,12 @@
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<title>5chan</title>
|
||||
<!-- React Grab: Hold ⌘C and click any element to provide visual context to AI coding agents (dev only) -->
|
||||
<script type="module">
|
||||
if (import.meta.env.DEV) {
|
||||
import("react-grab");
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
// Redirect non-hash URLs to hash URLs on initial load
|
||||
if (window.location.pathname && window.location.pathname !== '/' && window.location.pathname !== '/index.html' && !window.location.hash) {
|
||||
|
||||
@@ -121,6 +121,7 @@
|
||||
"oxfmt": "0.20.0",
|
||||
"oxlint": "1.35.0",
|
||||
"progress": "2.0.3",
|
||||
"react-grab": "0.0.94",
|
||||
"react-scan": "0.4.3",
|
||||
"stream-browserify": "3.0.0",
|
||||
"vite": "npm:rolldown-vite@7.2.11",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "ما هو 5chan؟",
|
||||
"go": "اذهب",
|
||||
"or": "أو",
|
||||
"5chan_description": "5chan هو بديل لـ 4chan بدون خوادم، بدون إدارة، لامركزي ومفتوح المصدر، حيث يمكن لأي شخص إنشاء وامتلاك عدد غير محدود من المنتديات بشكل مشفر. جميع المنتديات مستضافة ومراقبة من قبل مالكيها، الذين يديرون عقدًا يتصل بها المستخدمون ويشاركونها، من نظير إلى نظير. جميع البيانات نصية فقط، بما في ذلك الروابط لتضمين الصور. لا تتردد في النقر على منتدى أدناه يثير اهتمامك والبدء مباشرة!",
|
||||
"5chan_description": "5chan هو بديل 4chan لامركزي بالكامل ومفتوح المصدر، حيث يمكن لأي شخص إنشاء وامتلاك لوحات غير محدودة بشكل مشفر. يضيف مالكو اللوحات ويشرفون على لوحاتهم، ويضعون <1>القواعد</1> الخاصة بها. جميع البيانات نصية فقط ويتم مشاركتها من نظير إلى نظير، بما في ذلك روابط تضمين الصور. تحقق من <2>الأسئلة الشائعة</2> لمعرفة المزيد. لا تتردد في النقر على لوحة أدناه تهمك والبدء فورًا!",
|
||||
"no_global_rules_info": "يمكنك دائمًا الاتصال بأي منتدى، بشكل كامل P2P، لا يمكن لأحد إيقافك. ببساطة الصق عنوانه في حقل البحث أعلاه.",
|
||||
"boards": "منتديات",
|
||||
"popular_threads": "المواضيع الشائعة",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} ردود محذوفة. <1>اضغط هنا</1> لعرضها.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} ردود و{{linksCount}} روابط محذوفة. <1>اضغط هنا</1> لعرضها.",
|
||||
"this_thread_is_closed": "الموضوع مغلق",
|
||||
"reply_to_cid": "الرد على {{cid}}",
|
||||
"reply_to_no": "الرد على المنشور رقم{{no}}",
|
||||
"submit": "إرسال",
|
||||
"gif": "جي آي إف",
|
||||
"moderating": "تدير",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "حساب",
|
||||
"crypto_address": "عنوان العملة الرقمية",
|
||||
"crypto_wallets": "محافظ العملات الرقمية",
|
||||
"plebbit_options": "خيارات plebbit",
|
||||
"reset": "إعادة",
|
||||
"import": "استيراد",
|
||||
"export": "تصدير",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "مخزنة محليًا ({{location}})، ليست متزامنة عبر الأجهزة",
|
||||
"choose_one": "اختر واحدة:",
|
||||
"create": "إنشاء",
|
||||
"plebbit_account": "حساب Plebbit",
|
||||
"save_changes": "حفظ التغييرات",
|
||||
"reset_changes": "إعادة تعيين التغييرات",
|
||||
"crypto_address_yours": "عنوان العملة الرقمية ينتمي إلى هذا الحساب.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "التحديثات",
|
||||
"downloading_board": "جارٍ تنزيل المنتدى",
|
||||
"subscriptions_subtitle": "لقد اشتركت في {{count}} لوحة",
|
||||
"subscriptions_subtitle_one": "لقد اشتركت في لوحة واحدة"
|
||||
"subscriptions_subtitle_one": "لقد اشتركت في لوحة واحدة",
|
||||
"all_boards": "جميع اللوحات",
|
||||
"nsfw_boards_only": "لوحات NSFW فقط",
|
||||
"worksafe_boards_only": "لوحات آمنة للعمل فقط",
|
||||
"bitsocial_account": "حساب Bitsocial",
|
||||
"p2p_options": "خيارات P2P"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "5chan কি?",
|
||||
"go": "যাও",
|
||||
"or": "অথবা",
|
||||
"5chan_description": "5chan হল একটি সার্ভারহীন, অ্যাডমিনহীন, বিকেন্দ্রীকৃত এবং ওপেন-সোর্স 4chan বিকল্প, যেখানে যে কেউ সীমাহীন বোর্ড তৈরি করতে এবং ক্রিপ্টোগ্রাফিকভাবে মালিকানা নিতে পারে। সমস্ত বোর্ড তাদের মালিকদের দ্বারা হোস্ট এবং মডারেট করা হয়, যারা নোড চালায় যেখানে ব্যবহারকারীরা সংযুক্ত হয় এবং সিড করে, পিয়ার-টু-পিয়ার। সমস্ত ডেটা শুধুমাত্র পাঠ্য, এমবেড ইমেজের লিঙ্ক সহ। নীচে একটি বোর্ডে ক্লিক করতে নির্দ্বিধায় যা আপনাকে আগ্রহী করে এবং সরাসরি শুরু করুন!",
|
||||
"5chan_description": "5chan একটি সম্পূর্ণ বিকেন্দ্রীকৃত এবং ওপেন-সোর্স 4chan বিকল্প, যেখানে যে কেউ সীমাহীন বোর্ড তৈরি করতে এবং ক্রিপ্টোগ্রাফিকভাবে মালিকানা নিতে পারে। বোর্ড মালিকরা তাদের বোর্ড হোস্ট এবং মডারেট করে এবং এর <1>নিয়ম</1> সেট করে। সমস্ত ডেটা শুধুমাত্র পাঠ্য এবং এটি পিয়ার-টু-পিয়ার শেয়ার করা হয়, এমবেড ইমেজের লিঙ্ক সহ। আরও জানতে <2>FAQ</2> দেখুন। নীচে যে বোর্ডটি আপনাকে আগ্রহী করে তোলে সেটিতে ক্লিক করতে এবং সরাসরি শুরু করতে নির্দ্বিধায়!",
|
||||
"no_global_rules_info": "আপনি সবসময় যেকোনো বোর্ডে সংযোগ করতে পারেন, সম্পূর্ণ P2P, কেউ আপনাকে থামাতে পারে না। উপরের সার্চ ফিল্ডে এর ঠিকানা পেস্ট করুন।",
|
||||
"boards": "বোর্ডসমূহ",
|
||||
"popular_threads": "জনপ্রিয় থ্রেডগুলি",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}}টি উত্তর বাদ দেওয়া হয়েছে। <1>দেখতে এখানে ক্লিক করুন</1>।",
|
||||
"replies_and_links_omitted": "{{repliesCount}}টি উত্তর এবং {{linksCount}}টি লিঙ্ক বাদ দেওয়া হয়েছে। <1>দেখতে এখানে ক্লিক করুন</1>।",
|
||||
"this_thread_is_closed": "থ্রেডটি বন্ধ করা হয়েছে",
|
||||
"reply_to_cid": "{{cid}}-এ উত্তর দিন",
|
||||
"reply_to_no": "পোস্ট নং{{no}}-এ উত্তর দিন",
|
||||
"submit": "জমা দিন",
|
||||
"gif": "জিআইএফ",
|
||||
"moderating": "মডারেটিং",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "অ্যাকাউন্ট",
|
||||
"crypto_address": "ক্রিপ্টো ঠিকানা",
|
||||
"crypto_wallets": "ক্রিপ্টো ওয়ালেট",
|
||||
"plebbit_options": "প্লেবিট বিকল্প",
|
||||
"reset": "রিসেট",
|
||||
"import": "আমদানি",
|
||||
"export": "রপ্তানি",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "স্থানীয়ভাবে সংরক্ষিত ({{location}}), ডিভাইসের মধ্যে সিঙ্ক করা হয়নি",
|
||||
"choose_one": "একটি নির্বাচন করুন:",
|
||||
"create": "তৈরি করুন",
|
||||
"plebbit_account": "Plebbit অ্যাকাউন্ট",
|
||||
"save_changes": "পরিবর্তনগুলি সংরক্ষণ করুন",
|
||||
"reset_changes": "পরিবর্তনগুলি রিসেট করুন",
|
||||
"crypto_address_yours": "ক্রিপ্টো ঠিকানা এই অ্যাকাউন্টের অংশ।",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "আপডেট",
|
||||
"downloading_board": "বোর্ড ডাউনলোড করা হচ্ছে",
|
||||
"subscriptions_subtitle": "আপনি {{count}}টি board-এ সাবস্ক্রাইব করেছেন",
|
||||
"subscriptions_subtitle_one": "আপনি ১টি board-এ সাবস্ক্রাইব করেছেন"
|
||||
"subscriptions_subtitle_one": "আপনি ১টি board-এ সাবস্ক্রাইব করেছেন",
|
||||
"all_boards": "সব বোর্ড",
|
||||
"nsfw_boards_only": "NSFW বোর্ড শুধুমাত্র",
|
||||
"worksafe_boards_only": "কাজের জন্য নিরাপদ বোর্ড শুধুমাত্র",
|
||||
"bitsocial_account": "Bitsocial অ্যাকাউন্ট",
|
||||
"p2p_options": "P2P বিকল্প"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "Co je 5chan?",
|
||||
"go": "jít",
|
||||
"or": "nebo",
|
||||
"5chan_description": "5chan je bezserverová, bez administrátora, decentralizovaná a open-source alternativa k 4chan, kde kdokoli může vytvářet a kryptograficky vlastnit neomezené desky. Všechny desky jsou hostovány a moderovány jejich majiteli, kteří provozují uzly, ke kterým se uživatelé připojují a sdílejí, peer-to-peer. Všechna data jsou pouze textová, včetně odkazů na vložené obrázky. Neváhejte kliknout na desku níže, která vás zajímá, a začněte hned!",
|
||||
"5chan_description": "5chan je plně decentralizovaná a open-source alternativa k 4chanu, kde kdokoli může vytvořit a kryptograficky vlastnit neomezené množství desek. Vlastníci desek hostují a moderují své desky a nastavují jejich <1>Pravidla</1>. Všechna data jsou pouze textová a sdílejí se peer-to-peer, včetně odkazů na vložené obrázky. Podívejte se na <2>FAQ</2>, abyste se dozvěděli více. Neváhejte kliknout na desku níže, která vás zajímá, a začněte hned!",
|
||||
"no_global_rules_info": "Můžete se vždy připojit k jakékoli nástěnce, plně P2P, nikdo vás nemůže zastavit. Jednoduše vložte její adresu do vyhledávacího pole výše.",
|
||||
"boards": "nástěnky",
|
||||
"popular_threads": "Oblíbená vlákna",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} odpovědí vynecháno. <1>Klikněte zde</1> pro zobrazení.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} odpovědí a {{linksCount}} odkazů vynecháno. <1>Klikněte zde</1> pro zobrazení.",
|
||||
"this_thread_is_closed": "Vlákno je uzavřeno",
|
||||
"reply_to_cid": "Odpovědět na {{cid}}",
|
||||
"reply_to_no": "Odpovědět na příspěvek č.{{no}}",
|
||||
"submit": "Odeslat",
|
||||
"gif": "GIF",
|
||||
"moderating": "moderování",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "účet",
|
||||
"crypto_address": "adresa kryptoměny",
|
||||
"crypto_wallets": "Kryptoměny",
|
||||
"plebbit_options": "plebbit možnosti",
|
||||
"reset": "obnovit",
|
||||
"import": "import",
|
||||
"export": "export",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "Uloženo lokálně ({{location}}), není synchronizováno mezi zařízeními",
|
||||
"choose_one": "Vyberte jednu:",
|
||||
"create": "Vytvořit",
|
||||
"plebbit_account": "Účet Plebbit",
|
||||
"save_changes": "Uložit změny",
|
||||
"reset_changes": "Obnovit změny",
|
||||
"crypto_address_yours": "Adresa kryptoměny patří k tomuto účtu.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "Aktualizace",
|
||||
"downloading_board": "stahování fóra",
|
||||
"subscriptions_subtitle": "Přihlásili jste se k {{count}} boardu",
|
||||
"subscriptions_subtitle_one": "Přihlásili jste se k {{count}} boardu"
|
||||
"subscriptions_subtitle_one": "Přihlásili jste se k {{count}} boardu",
|
||||
"all_boards": "Všechny desky",
|
||||
"nsfw_boards_only": "Pouze NSFW desky",
|
||||
"worksafe_boards_only": "Pouze bezpečné desky",
|
||||
"bitsocial_account": "Účet Bitsocial",
|
||||
"p2p_options": "Možnosti P2P"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "Hvad er 5chan?",
|
||||
"go": "gå",
|
||||
"or": "eller",
|
||||
"5chan_description": "5chan er et serverløst, adminløst, decentraliseret og open-source alternativ til 4chan, hvor alle kan oprette og kryptografisk eje ubegrænsede boards. Alle boards hostes og modereres af deres ejere, som kører noder, som brugere forbinder til og seed, peer-to-peer. Alle data er kun tekst, inklusive links til at indlejre billeder. Du er velkommen til at klikke på et board nedenfor, der interesserer dig, og komme i gang med det samme!",
|
||||
"5chan_description": "5chan er en fuldt decentraliseret og open-source 4chan-alternativ, hvor alle kan oprette og kryptografisk eje ubegrænsede boards. Board-ejere hoster og modererer deres board og sætter dets <1>Regler</1>. Alle data er kun tekst og deles peer-to-peer, inklusive links til indlejrede billeder. Tjek <2>FAQ</2> for at lære mere. Du er velkommen til at klikke på et board nedenfor, der interesserer dig, og komme i gang med det samme!",
|
||||
"no_global_rules_info": "Du kan altid forbinde til enhver opslagstavle, fuldt P2P, ingen kan stoppe dig. Indsæt simpelthen dens adresse i søgefeltet ovenfor.",
|
||||
"boards": "opslagstavler",
|
||||
"popular_threads": "Populære tråde",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} svar udeladt. <1>Klik her</1> for at se dem.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} svar og {{linksCount}} links udeladt. <1>Klik her</1> for at se dem.",
|
||||
"this_thread_is_closed": "Tråden er lukket",
|
||||
"reply_to_cid": "Svar til {{cid}}",
|
||||
"reply_to_no": "Svar til opslag No.{{no}}",
|
||||
"submit": "Indsend",
|
||||
"gif": "GIF",
|
||||
"moderating": "modererer",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "konto",
|
||||
"crypto_address": "kryptoadresse",
|
||||
"crypto_wallets": "Crypto Wallets",
|
||||
"plebbit_options": "plebbit indstillinger",
|
||||
"reset": "nulstil",
|
||||
"import": "importer",
|
||||
"export": "eksportér",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "Lager lokalt ({{location}}), ikke synkroniseret på tværs af enheder",
|
||||
"choose_one": "Vælg en:",
|
||||
"create": "Opret",
|
||||
"plebbit_account": "Plebbit-konto",
|
||||
"save_changes": "Gem ændringer",
|
||||
"reset_changes": "Nulstil ændringer",
|
||||
"crypto_address_yours": "Crypto-adresse tilhører denne konto.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "Opdateringer",
|
||||
"downloading_board": "downloader forum",
|
||||
"subscriptions_subtitle": "Du har abonneret på {{count}} board",
|
||||
"subscriptions_subtitle_one": "Du har abonneret på {{count}} board"
|
||||
"subscriptions_subtitle_one": "Du har abonneret på {{count}} board",
|
||||
"all_boards": "Alle tavler",
|
||||
"nsfw_boards_only": "Kun NSFW-tavler",
|
||||
"worksafe_boards_only": "Kun arbejdssikre tavler",
|
||||
"bitsocial_account": "Bitsocial-konto",
|
||||
"p2p_options": "P2P-indstillinger"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "Was ist 5chan?",
|
||||
"go": "gehen",
|
||||
"or": "oder",
|
||||
"5chan_description": "5chan ist eine serverlose, adminlose, dezentralisierte und Open-Source-Alternative zu 4chan, bei der jeder unbegrenzte Boards erstellen und kryptografisch besitzen kann. Alle Boards werden von ihren Besitzern gehostet und moderiert, die Knoten betreiben, zu denen sich Benutzer peer-to-peer verbinden und seeden. Alle Daten sind nur Text, einschließlich Links zum Einbetten von Bildern. Klicken Sie gerne auf ein Board unten, das Sie interessiert, und steigen Sie direkt ein!",
|
||||
"5chan_description": "5chan ist eine vollständig dezentralisierte und quelloffene Alternative zu 4chan, bei der jeder unbegrenzt Boards erstellen und kryptografisch besitzen kann. Board-Besitzer hosten und moderieren ihr Board und legen dessen <1>Regeln</1> fest. Alle Daten sind nur Text und werden Peer-to-Peer geteilt, einschließlich Links zu eingebetteten Bildern. Schauen Sie sich die <2>FAQs</2> an, um mehr zu erfahren. Klicken Sie gerne auf ein Board unten, das Sie interessiert, und steigen Sie direkt ein!",
|
||||
"no_global_rules_info": "Sie können sich jederzeit mit jedem Forum verbinden, vollständig P2P, niemand kann Sie aufhalten. Fügen Sie einfach seine Adresse in das Suchfeld oben ein.",
|
||||
"boards": "Foren",
|
||||
"popular_threads": "Beliebte Threads",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} Antworten ausgelassen. <1>Klicken Sie hier</1>, um sie anzuzeigen.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} Antworten und {{linksCount}} Links ausgelassen. <1>Klicken Sie hier</1>, um sie anzuzeigen.",
|
||||
"this_thread_is_closed": "Der Thread ist geschlossen",
|
||||
"reply_to_cid": "Antwort an {{cid}}",
|
||||
"reply_to_no": "Antwort auf Beitrag Nr.{{no}}",
|
||||
"submit": "Einreichen",
|
||||
"gif": "GIF",
|
||||
"moderating": "moderieren",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "Konto",
|
||||
"crypto_address": "Krypto-Adresse",
|
||||
"crypto_wallets": "Krypto-Wallets",
|
||||
"plebbit_options": "plebbit optionen",
|
||||
"reset": "zurücksetzen",
|
||||
"import": "importiere",
|
||||
"export": "exportiere",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "Lokal gespeichert ({{location}}), nicht geräteübergreifend synchronisiert",
|
||||
"choose_one": "Wählen Sie eine:",
|
||||
"create": "Erstellen",
|
||||
"plebbit_account": "Plebbit-Konto",
|
||||
"save_changes": "Änderungen speichern",
|
||||
"reset_changes": "Änderungen zurücksetzen",
|
||||
"crypto_address_yours": "Krypto-Adresse gehört zu diesem Konto.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "Aktualisierungen",
|
||||
"downloading_board": "Forum wird heruntergeladen",
|
||||
"subscriptions_subtitle": "Sie haben {{count}} Board abonniert",
|
||||
"subscriptions_subtitle_one": "Sie haben {{count}} Board abonniert"
|
||||
"subscriptions_subtitle_one": "Sie haben {{count}} Board abonniert",
|
||||
"all_boards": "Alle Boards",
|
||||
"nsfw_boards_only": "Nur NSFW-Boards",
|
||||
"worksafe_boards_only": "Nur arbeitsplatzsichere Boards",
|
||||
"bitsocial_account": "Bitsocial-Konto",
|
||||
"p2p_options": "P2P-Optionen"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "Τι είναι το 5chan;",
|
||||
"go": "πήγαινε",
|
||||
"or": "ή",
|
||||
"5chan_description": "Το 5chan είναι μια εναλλακτική λύση χωρίς διακομιστές, χωρίς διαχειριστές, αποκεντρωμένη και ανοιχτού κώδικα για το 4chan, όπου οποιοσδήποτε μπορεί να δημιουργήσει και να κατέχει κρυπτογραφικά απεριόριστα forums. Όλα τα forums φιλοξενούνται και διαχειρίζονται από τους ιδιοκτήτες τους, οι οποίοι λειτουργούν κόμβους στους οποίους οι χρήστες συνδέονται και διαμοιράζονται, peer-to-peer. Όλα τα δεδομένα είναι μόνο κείμενο, συμπεριλαμβανομένων συνδέσμων για ενσωμάτωση εικόνων. Αισθανθείτε ελεύθεροι να κάνετε κλικ σε ένα forum παρακάτω που σας ενδιαφέρει και ξεκινήστε αμέσως!",
|
||||
"5chan_description": "Το 5chan είναι μια πλήρως αποκεντρωμένη και ανοιχτού κώδικα εναλλακτική λύση του 4chan, όπου οποιοσδήποτε μπορεί να δημιουργήσει και να κατέχει κρυπτογραφικά απεριόριστα σανίδια. Οι ιδιοκτήτες σανιδιών φιλοξενούν και διαχειρίζονται το σανίδι τους και ορίζουν τους <1>Κανόνες</1> του. Όλα τα δεδομένα είναι μόνο κείμενο και μοιράζονται από άτομο σε άτομο, συμπεριλαμβανομένων συνδέσμων για ενσωματωμένες εικόνες. Δείτε τις <2>Συχνές Ερωτήσεις</2> για να μάθετε περισσότερα. Μη διστάσετε να κάνετε κλικ σε ένα σανίδι παρακάτω που σας ενδιαφέρει και να ξεκινήσετε αμέσως!",
|
||||
"no_global_rules_info": "Μπορείτε πάντα να συνδεθείτε σε οποιοδήποτε board, πλήρως P2P, κανείς δεν μπορεί να σας σταματήσει. Απλά επικολλήστε τη διεύθυνσή του στο πεδίο αναζήτησης παραπάνω.",
|
||||
"boards": "διαδικτυακές πινακίδες",
|
||||
"popular_threads": "Δημοφιλή νήματα",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} απαντήσεις παραλείφθηκαν. <1>Κάντε κλικ εδώ</1> για να τις δείτε.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} απαντήσεις και {{linksCount}} σύνδεσμοι παραλείφθηκαν. <1>Κάντε κλικ εδώ</1> για να τις δείτε.",
|
||||
"this_thread_is_closed": "Το νήμα έχει κλείσει",
|
||||
"reply_to_cid": "Απάντηση σε {{cid}}",
|
||||
"reply_to_no": "Απάντηση στην ανάρτηση No.{{no}}",
|
||||
"submit": "Υποβολή",
|
||||
"gif": "GIF",
|
||||
"moderating": "διαχειρίζεστε",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "λογαριασμός",
|
||||
"crypto_address": "διεύθυνση κρυπτονομίσματος",
|
||||
"crypto_wallets": "Πορτοφόλια κρυπτονομισμάτων",
|
||||
"plebbit_options": "επιλογές plebbit",
|
||||
"reset": "επαναφορά",
|
||||
"import": "εισαγωγή",
|
||||
"export": "εξαγωγή",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "Αποθηκευμένο τοπικά ({{location}}), δεν συγχρονίζεται σε όλες τις συσκευές",
|
||||
"choose_one": "Επιλέξτε μία:",
|
||||
"create": "Δημιουργία",
|
||||
"plebbit_account": "Λογαριασμός Plebbit",
|
||||
"save_changes": "Αποθήκευση αλλαγών",
|
||||
"reset_changes": "Επαναφορά αλλαγών",
|
||||
"crypto_address_yours": "Η διεύθυνση κρυπτονομίσματος ανήκει σε αυτόν τον λογαριασμό.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "Ενημερώσεις",
|
||||
"downloading_board": "λήψη φόρουμ",
|
||||
"subscriptions_subtitle": "Έχετε εγγραφεί σε {{count}} board",
|
||||
"subscriptions_subtitle_one": "Έχετε εγγραφεί σε {{count}} board"
|
||||
"subscriptions_subtitle_one": "Έχετε εγγραφεί σε {{count}} board",
|
||||
"all_boards": "Όλες οι σανίδες",
|
||||
"nsfw_boards_only": "Μόνο σανίδες NSFW",
|
||||
"worksafe_boards_only": "Μόνο ασφαλείς σανίδες",
|
||||
"bitsocial_account": "Λογαριασμός Bitsocial",
|
||||
"p2p_options": "Επιλογές P2P"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "What is 5chan?",
|
||||
"go": "go",
|
||||
"or": "or",
|
||||
"5chan_description": "5chan is a serverless, adminless, decentralized and open-source 4chan alternative, where anyone can create and cryptographically own unlimited boards. All boards are hosted and moderated by their owners, who run nodes that users connect to and seed, peer-to-peer. All data is text-only, including links to embed images. Feel free to click on a board below that interests you and jump right in!",
|
||||
"5chan_description": "5chan is a fully decentralized and open-source 4chan alternative, where anyone can create and cryptographically own unlimited boards. Board owners host and moderate their board, and set its <1>Rules</1>. All data is text-only and it's shared peer-to-peer, including links to embed images. Check out the <2>FAQs</2> to learn more. Feel free to click on a board below that interests you and jump right in!",
|
||||
"no_global_rules_info": "You can always connect to any board, fully P2P, nobody can stop you. Simply paste its address in the search field above.",
|
||||
"boards": "boards",
|
||||
"popular_threads": "Popular threads",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} replies omitted. <1>Click here</1> to view.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} replies and {{linksCount}} links omitted. <1>Click here</1> to view.",
|
||||
"this_thread_is_closed": "This thread is closed",
|
||||
"reply_to_cid": "Reply to {{cid}}",
|
||||
"reply_to_no": "Reply to Thread No.{{no}}",
|
||||
"submit": "Submit",
|
||||
"gif": "GIF",
|
||||
"moderating": "moderating",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "account",
|
||||
"crypto_address": "crypto address",
|
||||
"crypto_wallets": "Crypto Wallets",
|
||||
"plebbit_options": "plebbit options",
|
||||
"reset": "reset",
|
||||
"import": "import",
|
||||
"export": "export",
|
||||
@@ -193,6 +192,9 @@
|
||||
"use_catalog": "Use Catalog",
|
||||
"show_worksafe_boards_only": "Show Worksafe Boards Only",
|
||||
"show_all_content": "Show All Content",
|
||||
"all_boards": "All Boards",
|
||||
"nsfw_boards_only": "NSFW Boards Only",
|
||||
"worksafe_boards_only": "Worksafe Boards Only",
|
||||
"link_type": "Link type",
|
||||
"comment_edited_at_timestamp": "Comment edited at <1>{{timestamp}}</1>",
|
||||
"reason_reason": "Reason: \"{{reason}}\".",
|
||||
@@ -240,7 +242,6 @@
|
||||
"stored_locally": "Stored locally ({{location}}), not synced across devices",
|
||||
"choose_one": "Choose one:",
|
||||
"create": "Create",
|
||||
"plebbit_account": "Plebbit Account",
|
||||
"save_changes": "Save Changes",
|
||||
"reset_changes": "Reset Changes",
|
||||
"crypto_address_yours": "Crypto address belongs to this account.",
|
||||
@@ -300,5 +301,7 @@
|
||||
"administrator_of_this_board": "This user is an administrator of this board",
|
||||
"copy_content_id": "Copy content ID",
|
||||
"subscriptions_subtitle": "You have subscribed to {{count}} boards",
|
||||
"subscriptions_subtitle_one": "You have subscribed to {{count}} board"
|
||||
"subscriptions_subtitle_one": "You have subscribed to {{count}} board",
|
||||
"bitsocial_account": "Bitsocial Account",
|
||||
"p2p_options": "P2P options"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "¿Qué es 5chan?",
|
||||
"go": "ir",
|
||||
"or": "o",
|
||||
"5chan_description": "5chan es una alternativa descentralizada, sin servidor, sin administrador y de código abierto a 4chan, donde cualquiera puede crear y poseer criptográficamente foros ilimitados. Todos los foros son alojados y moderados por sus propietarios, quienes ejecutan nodos a los que los usuarios se conectan y comparten, peer-to-peer. Todos los datos son solo texto, incluyendo enlaces para incrustar imágenes. ¡Siéntete libre de hacer clic en un foro a continuación que te interese y únete de inmediato!",
|
||||
"5chan_description": "5chan es una alternativa completamente descentralizada y de código abierto a 4chan, donde cualquiera puede crear y poseer criptográficamente tableros ilimitados. Los propietarios de tableros alojan y moderan su tablero y establecen sus <1>Reglas</1>. Todos los datos son solo texto y se comparten de igual a igual, incluidos los enlaces para incrustar imágenes. Consulta las <2>Preguntas Frecuentes</2> para obtener más información. ¡Siéntete libre de hacer clic en un tablero a continuación que te interese y comienza de inmediato!",
|
||||
"no_global_rules_info": "Siempre puedes conectarte a cualquier foro, completamente P2P, nadie puede detenerte. Simplemente pega su dirección en el campo de búsqueda de arriba.",
|
||||
"boards": "foros",
|
||||
"popular_threads": "Hilos populares",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} respuestas omitidas. <1>Haz clic aquí</1> para verlas.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} respuestas y {{linksCount}} enlaces omitidos. <1>Haz clic aquí</1> para verlos.",
|
||||
"this_thread_is_closed": "El hilo está cerrado",
|
||||
"reply_to_cid": "Responder a {{cid}}",
|
||||
"reply_to_no": "Responder al post No.{{no}}",
|
||||
"submit": "Enviar",
|
||||
"gif": "GIF",
|
||||
"moderating": "moderando",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "cuenta",
|
||||
"crypto_address": "dirección de cripto",
|
||||
"crypto_wallets": "Billeteras cripto",
|
||||
"plebbit_options": "opciones plebbit",
|
||||
"reset": "restablecer",
|
||||
"import": "importar",
|
||||
"export": "exportar",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "Almacenado localmente ({{location}}), no sincronizado entre dispositivos",
|
||||
"choose_one": "Elige uno:",
|
||||
"create": "Crear",
|
||||
"plebbit_account": "Cuenta Plebbit",
|
||||
"save_changes": "Guardar cambios",
|
||||
"reset_changes": "Restablecer cambios",
|
||||
"crypto_address_yours": "La dirección de criptomoneda pertenece a esta cuenta.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "Actualizaciones",
|
||||
"downloading_board": "descargando foro",
|
||||
"subscriptions_subtitle": "Te has suscrito a {{count}} board",
|
||||
"subscriptions_subtitle_one": "Te has suscrito a {{count}} board"
|
||||
"subscriptions_subtitle_one": "Te has suscrito a {{count}} board",
|
||||
"all_boards": "Todas las tablas",
|
||||
"nsfw_boards_only": "Solo tablas NSFW",
|
||||
"worksafe_boards_only": "Solo tablas seguras para el trabajo",
|
||||
"bitsocial_account": "Cuenta Bitsocial",
|
||||
"p2p_options": "Opciones P2P"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "5chan چیست؟",
|
||||
"go": "برو",
|
||||
"or": "یا",
|
||||
"5chan_description": "5chan یک جایگزین بدون سرور، بدون ادمین، غیرمتمرکز و متن باز برای 4chan است، جایی که هر کسی میتواند انجمنهای نامحدود ایجاد کند و به صورت رمزنگاری شده مالک آنها باشد. همه انجمنها توسط مالکانشان میزبانی و مدیریت میشوند، که نودهایی را اجرا میکنند که کاربران به آنها متصل میشوند و به اشتراک میگذارند، همتا به همتا. همه دادهها فقط متن هستند، از جمله لینکهای جاسازی تصاویر. آزادانه روی یک انجمن زیر کلیک کنید که شما را علاقهمند میکند و مستقیماً شروع کنید!",
|
||||
"5chan_description": "5chan یک جایگزین کاملاً غیرمتمرکز و منبع باز برای 4chan است، جایی که هر کسی میتواند تختههای نامحدود ایجاد کند و به صورت رمزنگاری مالکیت داشته باشد. صاحبان تخته میزبان و نظارت بر تخته خود را انجام میدهند و <1>قوانین</1> آن را تنظیم میکنند. تمام دادهها فقط متن هستند و به صورت همتا به همتا به اشتراک گذاشته میشوند، از جمله لینکهای جاسازی تصاویر. برای کسب اطلاعات بیشتر، <2>سوالات متداول</2> را بررسی کنید. آزادانه روی تخته زیر که مورد علاقه شماست کلیک کنید و مستقیماً شروع کنید!",
|
||||
"no_global_rules_info": "شما همیشه میتوانید به هر تالاری متصل شوید، کاملاً P2P، هیچ کس نمیتواند شما را متوقف کند. به سادگی آدرس آن را در فیلد جستجوی بالا قرار دهید.",
|
||||
"boards": "تالارهای پیام",
|
||||
"popular_threads": "نخستین موضوعات",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} پاسخ حذف شده است. <1>اینجا کلیک کنید</1> تا آنها را مشاهده کنید.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} پاسخ و {{linksCount}} لینک حذف شده است. <1>اینجا کلیک کنید</1> تا آنها را مشاهده کنید.",
|
||||
"this_thread_is_closed": "موضوع بسته شده است",
|
||||
"reply_to_cid": "پاسخ به {{cid}}",
|
||||
"reply_to_no": "پاسخ به پست شماره{{no}}",
|
||||
"submit": "ارسال",
|
||||
"gif": "گیف",
|
||||
"moderating": "مدیریت",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "حساب",
|
||||
"crypto_address": "آدرس رمزارز",
|
||||
"crypto_wallets": "کیف پول رمز ارز",
|
||||
"plebbit_options": "گزینه های plebbit",
|
||||
"reset": "بازنشانی",
|
||||
"import": "وارد کردن",
|
||||
"export": "صدور",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "محلی ذخیره شده ({{location}})، با دستگاهها همگامسازی نشده است",
|
||||
"choose_one": "یکی را انتخاب کنید:",
|
||||
"create": "ایجاد",
|
||||
"plebbit_account": "حساب Plebbit",
|
||||
"save_changes": "ذخیره تغییرات",
|
||||
"reset_changes": "بازنشانی تغییرات",
|
||||
"crypto_address_yours": "آدرس رمزارز به این حساب تعلق دارد.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "بهروزرسانیها",
|
||||
"downloading_board": "در حال دانلود تالار",
|
||||
"subscriptions_subtitle": "شما به {{count}} board مشترک شدهاید",
|
||||
"subscriptions_subtitle_one": "شما به {{count}} board مشترک شدهاید"
|
||||
"subscriptions_subtitle_one": "شما به {{count}} board مشترک شدهاید",
|
||||
"all_boards": "همه تابلوها",
|
||||
"nsfw_boards_only": "فقط تابلوهای NSFW",
|
||||
"worksafe_boards_only": "فقط تابلوهای ایمن برای کار",
|
||||
"bitsocial_account": "حساب Bitsocial",
|
||||
"p2p_options": "گزینههای P2P"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "Mikä on 5chan?",
|
||||
"go": "mennä",
|
||||
"or": "tai",
|
||||
"5chan_description": "5chan on palvelimeton, hallintaton, hajautettu ja avoimen lähdekoodin vaihtoehto 4chanille, jossa kuka tahansa voi luoda ja kryptografisesti omistaa rajattomia tauluja. Kaikki taulut isännöidään ja moderoivat niiden omistajat, jotka ajavat solmuja, joihin käyttäjät yhdistävät ja jakavat, peer-to-peer. Kaikki tiedot ovat vain tekstiä, mukaan lukien linkit upotettaviin kuviin. Voit vapaasti klikata alla olevaa taulua, joka kiinnostaa sinua, ja aloittaa heti!",
|
||||
"5chan_description": "5chan on täysin hajautettu ja avoimen lähdekoodin vaihtoehto 4chanille, jossa kuka tahansa voi luoda ja kryptografisesti omistaa rajattomia tauluja. Taulujen omistajat isännöivät ja moderovat tauluaan ja asettavat sen <1>Säännöt</1>. Kaikki tiedot ovat vain tekstiä ja ne jaetaan vertaisverkossa, mukaan lukien linkit upotettaviin kuviin. Tutustu <2>UKK</2> saadaksesi lisätietoja. Voit vapaasti klikata alla olevaa taulua, joka kiinnostaa sinua, ja aloittaa heti!",
|
||||
"no_global_rules_info": "Voit aina yhdistää mihin tahansa foorumiin, täysin P2P, kukaan ei voi pysäyttää sinua. Liitä vain sen osoite yllä olevaan hakukenttään.",
|
||||
"boards": "foorumit",
|
||||
"popular_threads": "Suositut langat",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} vastausta jätetty pois. <1>Napsauta tästä</1> nähdäksesi ne.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} vastausta ja {{linksCount}} linkkiä jätetty pois. <1>Napsauta tästä</1> nähdäksesi ne.",
|
||||
"this_thread_is_closed": "Ketju on suljettu",
|
||||
"reply_to_cid": "Vastaa {{cid}}",
|
||||
"reply_to_no": "Vastaa viestiin No.{{no}}",
|
||||
"submit": "Lähetä",
|
||||
"gif": "GIF",
|
||||
"moderating": "moderoit",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "tili",
|
||||
"crypto_address": "krypto-osoite",
|
||||
"crypto_wallets": "Krypto-lompakot",
|
||||
"plebbit_options": "plebbit-vaihtoehdot",
|
||||
"reset": "nollaa",
|
||||
"import": "tuo",
|
||||
"export": "vie",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "Tallennettu paikallisesti ({{location}}), ei synkronoitu laitteiden välillä",
|
||||
"choose_one": "Valitse yksi:",
|
||||
"create": "Luo",
|
||||
"plebbit_account": "Plebbit-tili",
|
||||
"save_changes": "Tallenna muutokset",
|
||||
"reset_changes": "Nollaa muutokset",
|
||||
"crypto_address_yours": "Krypto-osoite kuuluu tähän tiliin.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "Päivitykset",
|
||||
"downloading_board": "ladataan foorumia",
|
||||
"subscriptions_subtitle": "Olet tilannut {{count}} boardia",
|
||||
"subscriptions_subtitle_one": "Olet tilannut {{count}} boardin"
|
||||
"subscriptions_subtitle_one": "Olet tilannut {{count}} boardin",
|
||||
"all_boards": "Kaikki taulut",
|
||||
"nsfw_boards_only": "Vain NSFW-taulut",
|
||||
"worksafe_boards_only": "Vain työturvalliset taulut",
|
||||
"bitsocial_account": "Bitsocial-tili",
|
||||
"p2p_options": "P2P-asetukset"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "Ano ang 5chan?",
|
||||
"go": "pumunta",
|
||||
"or": "o",
|
||||
"5chan_description": "Ang 5chan ay isang serverless, adminless, decentralized at open-source na alternatibo sa 4chan, kung saan sinuman ay maaaring lumikha at cryptographically magmamay-ari ng walang limitasyong boards. Lahat ng boards ay hosted at moderated ng kanilang mga may-ari, na nagpapatakbo ng nodes na ikinokonekta at siniseed ng mga user, peer-to-peer. Lahat ng data ay text-only, kasama ang mga link para i-embed ang mga imahe. Huwag mag-atubiling mag-click sa isang board sa ibaba na interesado ka at magsimula kaagad!",
|
||||
"5chan_description": "Ang 5chan ay isang ganap na desentralisado at open-source na alternatibo sa 4chan, kung saan sinuman ay maaaring lumikha at kriptograpikong magmay-ari ng walang limitasyong boards. Ang mga may-ari ng board ay nagho-host at nagmo-moderate ng kanilang board, at nagtatakda ng <1>Mga Patakaran</1> nito. Ang lahat ng data ay teksto lamang at ibinabahagi nang peer-to-peer, kasama ang mga link sa embed na mga larawan. Tingnan ang <2>Mga FAQ</2> para matuto pa. Huwag mag-atubiling i-click ang isang board sa ibaba na interesado ka at magsimula kaagad!",
|
||||
"no_global_rules_info": "Maaari mong palaging kumonekta sa anumang board, ganap na P2P, walang makakapigil sa iyo. Ilagay lamang ang address nito sa search field sa itaas.",
|
||||
"boards": "mga board",
|
||||
"popular_threads": "Mga popular na thread",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} mga tugon ang hindi ipinakita. <1>I-click dito</1> upang makita.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} mga tugon at {{linksCount}} mga link ang hindi ipinakita. <1>I-click dito</1> upang makita.",
|
||||
"this_thread_is_closed": "Sarado na ang thread",
|
||||
"reply_to_cid": "Tumugon sa {{cid}}",
|
||||
"reply_to_no": "Tumugon sa post No.{{no}}",
|
||||
"submit": "Isumite",
|
||||
"gif": "GIF",
|
||||
"moderating": "namamahala",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "account",
|
||||
"crypto_address": "crypto address",
|
||||
"crypto_wallets": "Mga Wallet ng Crypto",
|
||||
"plebbit_options": "mga pagpipilian ng plebbit",
|
||||
"reset": "i-reset",
|
||||
"import": "i-import",
|
||||
"export": "i-export",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "Nakaimbak nang lokal ({{location}}), hindi naka-sync sa iba pang device",
|
||||
"choose_one": "Pumili ng isa:",
|
||||
"create": "Lumikha",
|
||||
"plebbit_account": "Plebbit Account",
|
||||
"save_changes": "I-save ang mga pagbabago",
|
||||
"reset_changes": "I-reset ang mga pagbabago",
|
||||
"crypto_address_yours": "Ang crypto address ay nauugnay sa account na ito.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "Mga Update",
|
||||
"downloading_board": "nagda-download ng board",
|
||||
"subscriptions_subtitle": "Nag-subscribe ka sa {{count}} na board",
|
||||
"subscriptions_subtitle_one": "Nag-subscribe ka sa {{count}} na board"
|
||||
"subscriptions_subtitle_one": "Nag-subscribe ka sa {{count}} na board",
|
||||
"all_boards": "Lahat ng boards",
|
||||
"nsfw_boards_only": "NSFW boards lang",
|
||||
"worksafe_boards_only": "Worksafe boards lang",
|
||||
"bitsocial_account": "Account ng Bitsocial",
|
||||
"p2p_options": "Mga Opsyon ng P2P"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "Qu'est-ce que 5chan?",
|
||||
"go": "aller",
|
||||
"or": "ou",
|
||||
"5chan_description": "5chan est une alternative décentralisée, sans serveur, sans administrateur et open-source à 4chan où n'importe qui peut créer et posséder cryptographiquement des forums illimités. Tous les forums sont hébergés et modérés par leurs propriétaires, qui exécutent des nœuds auxquels les utilisateurs se connectent et partagent, peer-to-peer. Toutes les données sont uniquement du texte, y compris les liens pour intégrer des images. N'hésitez pas à cliquer sur un forum ci-dessous qui vous intéresse et à vous joindre immédiatement !",
|
||||
"5chan_description": "5chan est une alternative entièrement décentralisée et open-source à 4chan, où n'importe qui peut créer et posséder cryptographiquement des tableaux illimités. Les propriétaires de tableaux hébergent et modèrent leur tableau et définissent ses <1>Règles</1>. Toutes les données sont uniquement du texte et sont partagées en peer-to-peer, y compris les liens vers les images intégrées. Consultez les <2>FAQ</2> pour en savoir plus. N'hésitez pas à cliquer sur un tableau ci-dessous qui vous intéresse et à commencer tout de suite!",
|
||||
"no_global_rules_info": "Vous pouvez toujours vous connecter à n'importe quel forum, entièrement P2P, personne ne peut vous arrêter. Collez simplement son adresse dans le champ de recherche ci-dessus.",
|
||||
"boards": "forums",
|
||||
"popular_threads": "Fils populaires",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} réponses omises. <1>Cliquez ici</1> pour les afficher.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} réponses et {{linksCount}} liens omis. <1>Cliquez ici</1> pour les afficher.",
|
||||
"this_thread_is_closed": "Le fil est fermé",
|
||||
"reply_to_cid": "Répondre à {{cid}}",
|
||||
"reply_to_no": "Répondre au post No.{{no}}",
|
||||
"submit": "Soumettre",
|
||||
"gif": "GIF",
|
||||
"moderating": "modération",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "compte",
|
||||
"crypto_address": "adresse crypto",
|
||||
"crypto_wallets": "Portefeuilles de crypto",
|
||||
"plebbit_options": "options plebbit",
|
||||
"reset": "réinitialiser",
|
||||
"import": "importer",
|
||||
"export": "exporter",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "Stocké localement ({{location}}), non synchronisé entre les appareils",
|
||||
"choose_one": "Choisissez-en un :",
|
||||
"create": "Créer",
|
||||
"plebbit_account": "Compte Plebbit",
|
||||
"save_changes": "Enregistrer les modifications",
|
||||
"reset_changes": "Réinitialiser les modifications",
|
||||
"crypto_address_yours": "L'adresse crypto appartient à ce compte.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "Mises à jour",
|
||||
"downloading_board": "téléchargement du forum",
|
||||
"subscriptions_subtitle": "Vous vous êtes abonné à {{count}} board",
|
||||
"subscriptions_subtitle_one": "Vous vous êtes abonné à {{count}} board"
|
||||
"subscriptions_subtitle_one": "Vous vous êtes abonné à {{count}} board",
|
||||
"all_boards": "Tous les tableaux",
|
||||
"nsfw_boards_only": "Tableaux NSFW uniquement",
|
||||
"worksafe_boards_only": "Tableaux sûrs uniquement",
|
||||
"bitsocial_account": "Compte Bitsocial",
|
||||
"p2p_options": "Options P2P"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "מה זה 5chan?",
|
||||
"go": "לך",
|
||||
"or": "או",
|
||||
"5chan_description": "5chan הוא חלופה ללא שרת, ללא מנהל, מבוזרת וקוד פתוח ל-4chan, שבה כל אחד יכול ליצור ולבעול בצורה קריפטוגרפית לוחות בלתי מוגבלים. כל הלוחות מתארחים ומנוהלים על ידי בעליהם, שמפעילים צמתים שמשתמשים מתחברים אליהם ומשתפים, peer-to-peer. כל הנתונים הם טקסט בלבד, כולל קישורים להטמעת תמונות. אל תהססו ללחוץ על לוח למטה שמעניין אתכם ולהתחיל מיד!",
|
||||
"5chan_description": "5chan הוא חלופה מבוזרת לחלוטין וקוד פתוח ל-4chan, שבה כל אחד יכול ליצור ולבעול בצורה קריפטוגרפית לוחות בלתי מוגבלים. בעלי הלוחות מארחים ומנהלים את הלוח שלהם וקובעים את <1>הכללים</1> שלו. כל הנתונים הם טקסט בלבד והם משותפים peer-to-peer, כולל קישורים לתמונות מוטבעות. בדוק את <2>השאלות הנפוצות</2> כדי ללמוד עוד. אל תהסס ללחוץ על לוח למטה שמעניין אותך ולהתחיל מיד!",
|
||||
"no_global_rules_info": "תמיד תוכל להתחבר לכל לוח, לגמרי P2P, אף אחד לא יכול לעצור אותך. פשוט הדבק את הכתובת שלו בשדה החיפוש למעלה.",
|
||||
"boards": "פורומים",
|
||||
"popular_threads": "אשכולות פופולריים",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} תגובות נשמטו. <1>לחץ כאן</1> לצפייה.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} תגובות ו{{linksCount}} קישורים נשמטו. <1>לחץ כאן</1> לצפייה.",
|
||||
"this_thread_is_closed": "האשכול סגור",
|
||||
"reply_to_cid": "תגובה ל{{cid}}",
|
||||
"reply_to_no": "תגובה להודעה מס'{{no}}",
|
||||
"submit": "שלח",
|
||||
"gif": "גיף",
|
||||
"moderating": "מנהל",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "חשבון",
|
||||
"crypto_address": "כתובת קריפטו",
|
||||
"crypto_wallets": "ארנק קריפטו",
|
||||
"plebbit_options": "אפשרויות plebbit",
|
||||
"reset": "איפוס",
|
||||
"import": "יבא",
|
||||
"export": "יצוא",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "מאוחסן מקומית ({{location}}), לא מסונכרן בין מכשירים",
|
||||
"choose_one": "בחר אחת:",
|
||||
"create": "צור",
|
||||
"plebbit_account": "חשבון Plebbit",
|
||||
"save_changes": "שמור שינויים",
|
||||
"reset_changes": "איפוס שינויים",
|
||||
"crypto_address_yours": "כתובת המטבע הקריפטוגרפי שייכת לחשבון זה.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "עדכונים",
|
||||
"downloading_board": "מוריד פורום",
|
||||
"subscriptions_subtitle": "נרשמת ל-{{count}} board",
|
||||
"subscriptions_subtitle_one": "נרשמת ל-board אחד"
|
||||
"subscriptions_subtitle_one": "נרשמת ל-board אחד",
|
||||
"all_boards": "כל הלוחות",
|
||||
"nsfw_boards_only": "לוחות NSFW בלבד",
|
||||
"worksafe_boards_only": "לוחות בטוחים לעבודה בלבד",
|
||||
"bitsocial_account": "חשבון Bitsocial",
|
||||
"p2p_options": "אפשרויות P2P"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "5chan क्या है?",
|
||||
"go": "जाओ",
|
||||
"or": "या",
|
||||
"5chan_description": "5chan एक सर्वरहीन, एडमिनहीन, विकेंद्रीकृत और ओपन-सोर्स 4chan विकल्प है, जहाँ कोई भी असीमित बोर्ड बना सकता है और क्रिप्टोग्राफिक रूप से उनका स्वामित्व ले सकता है। सभी बोर्ड उनके मालिकों द्वारा होस्ट और मॉडरेट किए जाते हैं, जो नोड्स चलाते हैं जिनसे उपयोगकर्ता जुड़ते हैं और सीड करते हैं, peer-to-peer। सभी डेटा केवल टेक्स्ट है, जिसमें इमेज एम्बेड करने के लिंक शामिल हैं। नीचे एक बोर्ड पर क्लिक करने के लिए स्वतंत्र महसूस करें जो आपकी रुचि रखता है और तुरंत शुरू करें!",
|
||||
"5chan_description": "5chan एक पूरी तरह से विकेंद्रीकृत और ओपन-सोर्स 4chan विकल्प है, जहाँ कोई भी असीमित बोर्ड बना सकता है और क्रिप्टोग्राफिक रूप से उनका मालिक हो सकता है। बोर्ड मालिक अपने बोर्ड को होस्ट और मॉडरेट करते हैं, और इसके <1>नियम</1> निर्धारित करते हैं। सभी डेटा केवल पाठ है और इसे peer-to-peer साझा किया जाता है, जिसमें एम्बेड छवियों के लिंक शामिल हैं। अधिक जानने के लिए <2>FAQ</2> देखें। नीचे दिए गए बोर्ड पर क्लिक करने के लिए स्वतंत्र महसूस करें जो आपकी रुचि रखता है और तुरंत शुरू करें!",
|
||||
"no_global_rules_info": "आप हमेशा किसी भी बोर्ड से जुड़ सकते हैं, पूरी तरह से P2P, कोई आपको रोक नहीं सकता। बस उसका पता ऊपर के खोज क्षेत्र में पेस्ट करें।",
|
||||
"boards": "मंच",
|
||||
"popular_threads": "लोकप्रिय थ्रेड्स",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} उत्तर छोड़े गए हैं। <1>देखने के लिए यहां क्लिक करें</1>।",
|
||||
"replies_and_links_omitted": "{{repliesCount}} उत्तर और {{linksCount}} लिंक छोड़े गए हैं। <1>देखने के लिए यहां क्लिक करें</1>।",
|
||||
"this_thread_is_closed": "यह थ्रेड बंद है",
|
||||
"reply_to_cid": "{{cid}} का उत्तर दें",
|
||||
"reply_to_no": "पोस्ट No.{{no}} का उत्तर दें",
|
||||
"submit": "जमा करें",
|
||||
"gif": "जीआईएफ",
|
||||
"moderating": "मॉडरेटिंग",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "खाता",
|
||||
"crypto_address": "क्रिप्टो पता",
|
||||
"crypto_wallets": "क्रिप्टो वॉलेट्स",
|
||||
"plebbit_options": "plebbit विकल्प",
|
||||
"reset": "रीसेट करें",
|
||||
"import": "आयात",
|
||||
"export": "निर्यात",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "स्थानीय रूप से संग्रहीत ({{location}}), उपकरणों के बीच समन्वयित नहीं",
|
||||
"choose_one": "एक चुनें:",
|
||||
"create": "बनाएं",
|
||||
"plebbit_account": "Plebbit खाता",
|
||||
"save_changes": "परिवर्तन सहेजें",
|
||||
"reset_changes": "परिवर्तन रीसेट करें",
|
||||
"crypto_address_yours": "क्रिप्टो पता इस खाते से संबंधित है।",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "अपडेट",
|
||||
"downloading_board": "फोरम डाउनलोड हो रहा है",
|
||||
"subscriptions_subtitle": "आपने {{count}} board की सदस्यता ली है",
|
||||
"subscriptions_subtitle_one": "आपने {{count}} board की सदस्यता ली है"
|
||||
"subscriptions_subtitle_one": "आपने {{count}} board की सदस्यता ली है",
|
||||
"all_boards": "सभी बोर्ड",
|
||||
"nsfw_boards_only": "केवल NSFW बोर्ड",
|
||||
"worksafe_boards_only": "केवल कार्य-सुरक्षित बोर्ड",
|
||||
"bitsocial_account": "Bitsocial खाता",
|
||||
"p2p_options": "P2P विकल्प"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "Mi az a 5chan?",
|
||||
"go": "menni",
|
||||
"or": "vagy",
|
||||
"5chan_description": "A 5chan egy szerver nélküli, adminisztrátor nélküli, decentralizált és nyílt forráskódú alternatíva a 4chan számára, ahol bárki létrehozhat és kriptográfiailag birtokolhat korlátlan táblákat. Minden táblát a tulajdonosaik üzemeltetnek és moderálnak, akik olyan csomópontokat futtatnak, amelyekhez a felhasználók peer-to-peer módon csatlakoznak és seedelnek. Minden adat csak szöveg, beleértve a képek beágyazásához szükséges linkeket. Nyugodtan kattintson egy alábbi táblára, amely érdekli, és azonnal kezdjen bele!",
|
||||
"5chan_description": "A 5chan egy teljesen decentralizált és nyílt forráskódú alternatíva a 4chan számára, ahol bárki létrehozhat és kriptográfiailag birtokolhat korlátlan táblákat. A táblák tulajdonosai üzemeltetik és moderálják táblájukat, és beállítják annak <1>Szabályait</1>. Minden adat csak szöveg és peer-to-peer módon osztjuk meg, beleértve a beágyazott képekre mutató linkeket is. Tekintse meg a <2>GYIK</2> oldalt, hogy többet megtudjon. Nyugodtan kattintson az alábbi táblára, amely érdekli, és azonnal kezdjen el!",
|
||||
"no_global_rules_info": "Bármikor csatlakozhatsz bármely fórumhoz, teljesen P2P, senki sem tud megállítani. Egyszerűen illeszd be a címét a fenti keresőmezőbe.",
|
||||
"boards": "fórumok",
|
||||
"popular_threads": "Népszerű szálak",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} válasz kihagyva. <1>Kattintson ide</1> a megtekintéshez.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} válasz és {{linksCount}} link kihagyva. <1>Kattintson ide</1> a megtekintéshez.",
|
||||
"this_thread_is_closed": "A téma lezárva",
|
||||
"reply_to_cid": "Válasz erre: {{cid}}",
|
||||
"reply_to_no": "Válasz a No.{{no}} bejegyzésre",
|
||||
"submit": "Beküldés",
|
||||
"gif": "GIF",
|
||||
"moderating": "moderálás",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "fiók",
|
||||
"crypto_address": "kriptovaluta cím",
|
||||
"crypto_wallets": "Kriptopénztárcák",
|
||||
"plebbit_options": "plebbit lehetőségek",
|
||||
"reset": "alaphelyzetbe állít",
|
||||
"import": "importálás",
|
||||
"export": "exportálás",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "Helyben tárolva ({{location}}), nem szinkronizálva eszközök között",
|
||||
"choose_one": "Válasszon egyet:",
|
||||
"create": "Létrehozás",
|
||||
"plebbit_account": "Plebbit-fiók",
|
||||
"save_changes": "Változások mentése",
|
||||
"reset_changes": "Változások visszaállítása",
|
||||
"crypto_address_yours": "A kripto-cím ehhez a fiókhoz tartozik.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "Frissítések",
|
||||
"downloading_board": "fórum letöltése",
|
||||
"subscriptions_subtitle": "Feliratkoztál {{count}} boardra",
|
||||
"subscriptions_subtitle_one": "Feliratkoztál {{count}} boardra"
|
||||
"subscriptions_subtitle_one": "Feliratkoztál {{count}} boardra",
|
||||
"all_boards": "Minden tábla",
|
||||
"nsfw_boards_only": "Csak NSFW táblák",
|
||||
"worksafe_boards_only": "Csak munkavédelmi táblák",
|
||||
"bitsocial_account": "Bitsocial fiók",
|
||||
"p2p_options": "P2P beállítások"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "Apa itu 5chan?",
|
||||
"go": "pergi",
|
||||
"or": "atau",
|
||||
"5chan_description": "5chan adalah alternatif 4chan tanpa server, tanpa admin, terdesentralisasi dan open-source, di mana siapa pun dapat membuat dan secara kriptografis memiliki papan tak terbatas. Semua papan dihosting dan dimoderasi oleh pemiliknya, yang menjalankan node yang pengguna sambungkan dan seed, peer-to-peer. Semua data hanya teks, termasuk tautan untuk menyematkan gambar. Jangan ragu untuk mengklik papan di bawah yang menarik minat Anda dan langsung mulai!",
|
||||
"5chan_description": "5chan adalah alternatif 4chan yang sepenuhnya terdesentralisasi dan open-source, di mana siapa pun dapat membuat dan secara kriptografis memiliki papan tak terbatas. Pemilik papan menghosting dan memoderasi papan mereka, dan menetapkan <1>Aturannya</1>. Semua data hanya teks dan dibagikan peer-to-peer, termasuk tautan ke gambar yang disematkan. Lihat <2>FAQ</2> untuk mempelajari lebih lanjut. Jangan ragu untuk mengklik papan di bawah yang menarik minat Anda dan langsung mulai!",
|
||||
"no_global_rules_info": "Anda selalu dapat terhubung ke papan mana pun, sepenuhnya P2P, tidak ada yang bisa menghentikan Anda. Cukup tempelkan alamatnya di bidang pencarian di atas.",
|
||||
"boards": "papan pesan",
|
||||
"popular_threads": "Benang populer",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} balasan dihilangkan. <1>Klik di sini</1> untuk melihat.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} balasan dan {{linksCount}} tautan dihilangkan. <1>Klik di sini</1> untuk melihat.",
|
||||
"this_thread_is_closed": "Thread ini ditutup",
|
||||
"reply_to_cid": "Balas ke {{cid}}",
|
||||
"reply_to_no": "Balas ke pos No.{{no}}",
|
||||
"submit": "Kirim",
|
||||
"gif": "GIF",
|
||||
"moderating": "memoderasi",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "akun",
|
||||
"crypto_address": "alamat kripto",
|
||||
"crypto_wallets": "Dompet Crypto",
|
||||
"plebbit_options": "opsi plebbit",
|
||||
"reset": "ulang",
|
||||
"import": "impor",
|
||||
"export": "ekspor",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "Disimpan secara lokal ({{location}}), tidak disinkronkan antar perangkat",
|
||||
"choose_one": "Pilih satu:",
|
||||
"create": "Buat",
|
||||
"plebbit_account": "Akun Plebbit",
|
||||
"save_changes": "Simpan Perubahan",
|
||||
"reset_changes": "Atur Ulang Perubahan",
|
||||
"crypto_address_yours": "Alamat kripto milik akun ini.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "Pembaruan",
|
||||
"downloading_board": "mengunduh forum",
|
||||
"subscriptions_subtitle": "Anda telah berlangganan {{count}} board",
|
||||
"subscriptions_subtitle_one": "Anda telah berlangganan {{count}} board"
|
||||
"subscriptions_subtitle_one": "Anda telah berlangganan {{count}} board",
|
||||
"all_boards": "Semua papan",
|
||||
"nsfw_boards_only": "Hanya papan NSFW",
|
||||
"worksafe_boards_only": "Hanya papan aman untuk kerja",
|
||||
"bitsocial_account": "Akun Bitsocial",
|
||||
"p2p_options": "Opsi P2P"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "Cos'è 5chan?",
|
||||
"go": "vai",
|
||||
"or": "oppure",
|
||||
"5chan_description": "5chan è un'alternativa decentralizzata, senza server, senza amministratore e open-source a 4chan, dove chiunque può creare e possedere criptograficamente board illimitati. Tutte le board sono ospitate e moderate dai loro proprietari, che eseguono nodi a cui gli utenti si connettono e condividono, peer-to-peer. Tutti i dati sono solo testo, inclusi i link per incorporare immagini. Sentiti libero di cliccare su una board qui sotto che ti interessa e inizia subito!",
|
||||
"5chan_description": "5chan è un'alternativa completamente decentralizzata e open-source a 4chan, dove chiunque può creare e possedere criptograficamente board illimitati. I proprietari dei board ospitano e moderano il loro board e ne stabiliscono le <1>Regole</1>. Tutti i dati sono solo testo e vengono condivisi peer-to-peer, inclusi i link alle immagini incorporate. Consulta le <2>FAQ</2> per saperne di più. Sentiti libero di cliccare su un board qui sotto che ti interessa e inizia subito!",
|
||||
"no_global_rules_info": "Puoi sempre connetterti a qualsiasi board, completamente P2P, nessuno può fermarti. Basta incollare il suo indirizzo nel campo di ricerca sopra.",
|
||||
"boards": "board",
|
||||
"popular_threads": "Thread popolari",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} risposte omesse. <1>Clicca qui</1> per visualizzare.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} risposte e {{linksCount}} link omessi. <1>Clicca qui</1> per visualizzare.",
|
||||
"this_thread_is_closed": "Questo thread è chiuso",
|
||||
"reply_to_cid": "Rispondi a {{cid}}",
|
||||
"reply_to_no": "Rispondi al post No.{{no}}",
|
||||
"submit": "Invia",
|
||||
"gif": "GIF",
|
||||
"moderating": "stai moderando",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "account",
|
||||
"crypto_address": "indirizzo crypto",
|
||||
"crypto_wallets": "Wallet crypto",
|
||||
"plebbit_options": "opzioni plebbit",
|
||||
"reset": "ripristina",
|
||||
"import": "importa",
|
||||
"export": "esporta",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "Memorizzato localmente ({{location}}), non sincronizzato tra i dispositivi",
|
||||
"choose_one": "Scegline una:",
|
||||
"create": "Crea",
|
||||
"plebbit_account": "Account Plebbit",
|
||||
"save_changes": "Salva Modifiche",
|
||||
"reset_changes": "Ripristina Modifiche",
|
||||
"crypto_address_yours": "L'indirizzo cripto appartiene a questo account.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "Aggiornamenti",
|
||||
"downloading_board": "scaricando board",
|
||||
"subscriptions_subtitle": "Ti sei iscritto a {{count}} board",
|
||||
"subscriptions_subtitle_one": "Ti sei iscritto a {{count}} board"
|
||||
"subscriptions_subtitle_one": "Ti sei iscritto a {{count}} board",
|
||||
"all_boards": "Tutte le bacheche",
|
||||
"nsfw_boards_only": "Solo bacheche NSFW",
|
||||
"worksafe_boards_only": "Solo bacheche sicure per il lavoro",
|
||||
"bitsocial_account": "Account Bitsocial",
|
||||
"p2p_options": "Opzioni P2P"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "5chanとは何ですか?",
|
||||
"go": "行く",
|
||||
"or": "または",
|
||||
"5chan_description": "5chanは、サーバーレス、管理者なし、分散型、オープンソースの4chanの代替品で、誰でも無制限のボードを作成し、暗号的に所有できます。すべてのボードは所有者によってホストおよびモデレートされ、ユーザーが接続してシードするノードを実行します(ピアツーピア)。すべてのデータはテキストのみで、画像を埋め込むためのリンクを含みます。興味のあるボードをクリックして、すぐに始めてください!",
|
||||
"5chan_description": "5chanは完全に分散化されたオープンソースの4chanの代替品で、誰でも無制限のボードを作成し、暗号的に所有できます。ボードの所有者はボードをホストし、モデレートし、その<1>ルール</1>を設定します。すべてのデータはテキストのみで、埋め込み画像へのリンクを含めてピアツーピアで共有されます。詳細については<2>FAQ</2>をご覧ください。興味のあるボードをクリックして、すぐに始めてください!",
|
||||
"no_global_rules_info": "いつでも任意のボードに接続できます、完全にP2P、誰もあなたを止めることはできません。上記の検索フィールドにそのアドレスを貼り付けるだけです。",
|
||||
"boards": "掲示板",
|
||||
"popular_threads": "人気のあるスレッド",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}}件の返信が省略されました。 <1>ここをクリック</1>して表示。",
|
||||
"replies_and_links_omitted": "{{repliesCount}}件の返信と{{linksCount}}件のリンクが省略されました。 <1>ここをクリック</1>して表示。",
|
||||
"this_thread_is_closed": "このスレッドは閉じられています",
|
||||
"reply_to_cid": "{{cid}}に返信",
|
||||
"reply_to_no": "投稿 No.{{no}}に返信",
|
||||
"submit": "送信",
|
||||
"gif": "GIF",
|
||||
"moderating": "モデレート中",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "アカウント",
|
||||
"crypto_address": "暗号通貨アドレス",
|
||||
"crypto_wallets": "暗号通貨ウォレット",
|
||||
"plebbit_options": "plebbitのオプション",
|
||||
"reset": "リセット",
|
||||
"import": "インポート",
|
||||
"export": "エクスポート",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "ローカルに保存されています ({{location}})、デバイス間で同期されていません",
|
||||
"choose_one": "いずれかを選択してください:",
|
||||
"create": "作成",
|
||||
"plebbit_account": "Plebbit アカウント",
|
||||
"save_changes": "変更を保存",
|
||||
"reset_changes": "変更をリセット",
|
||||
"crypto_address_yours": "暗号通貨アドレスはこのアカウントに属しています。",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "更新",
|
||||
"downloading_board": "掲示板をダウンロード中",
|
||||
"subscriptions_subtitle": "{{count}}個のboardを購読しています",
|
||||
"subscriptions_subtitle_one": "{{count}}個のboardを購読しています"
|
||||
"subscriptions_subtitle_one": "{{count}}個のboardを購読しています",
|
||||
"all_boards": "すべてのボード",
|
||||
"nsfw_boards_only": "NSFWボードのみ",
|
||||
"worksafe_boards_only": "職場安全ボードのみ",
|
||||
"bitsocial_account": "Bitsocialアカウント",
|
||||
"p2p_options": "P2Pオプション"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "5chan이란 무엇인가요?",
|
||||
"go": "가기",
|
||||
"or": "또는",
|
||||
"5chan_description": "5chan은 서버리스, 관리자 없음, 분산형 및 오픈 소스 4chan 대안으로, 누구나 무제한 보드를 생성하고 암호화적으로 소유할 수 있습니다. 모든 보드는 소유자가 호스팅하고 관리하며, 사용자가 연결하고 시드하는 노드를 실행합니다(피어 투 피어). 모든 데이터는 텍스트 전용이며 이미지를 임베드하는 링크를 포함합니다. 관심 있는 보드를 클릭하고 바로 시작하세요!",
|
||||
"5chan_description": "5chan은 완전히 분산되고 오픈 소스인 4chan 대안으로, 누구나 무제한 보드를 만들고 암호화적으로 소유할 수 있습니다. 보드 소유자는 보드를 호스팅하고 관리하며 <1>규칙</1>을 설정합니다. 모든 데이터는 텍스트 전용이며 임베드 이미지 링크를 포함하여 피어 투 피어로 공유됩니다. 자세한 내용은 <2>FAQ</2>를 확인하세요. 관심 있는 보드를 클릭하고 바로 시작하세요!",
|
||||
"no_global_rules_info": "언제든지 어떤 게시판이든 연결할 수 있습니다, 완전히 P2P, 아무도 당신을 막을 수 없습니다. 위의 검색 필드에 주소를 붙여넣기만 하면 됩니다.",
|
||||
"boards": "게시판",
|
||||
"popular_threads": "인기 있는 스레드",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}}개의 답글이 생략되었습니다. <1>여기를 클릭하십시오</1> 보려면.",
|
||||
"replies_and_links_omitted": "{{repliesCount}}개의 답글과 {{linksCount}}개의 링크가 생략되었습니다. <1>여기를 클릭하십시오</1> 보려면.",
|
||||
"this_thread_is_closed": "이 스레드는 닫혔습니다",
|
||||
"reply_to_cid": "{{cid}}에 답변",
|
||||
"reply_to_no": "게시글 No.{{no}}에 답변",
|
||||
"submit": "제출",
|
||||
"gif": "GIF",
|
||||
"moderating": "중재 중",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "계정",
|
||||
"crypto_address": "암호 화폐 주소",
|
||||
"crypto_wallets": "암호 화폐 지갑",
|
||||
"plebbit_options": "plebbit 옵션",
|
||||
"reset": "재설정",
|
||||
"import": "가져오기",
|
||||
"export": "내보내기",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "로컬에 저장됨 ({{location}}), 장치 간에 동기화되지 않음",
|
||||
"choose_one": "하나를 선택하세요:",
|
||||
"create": "생성",
|
||||
"plebbit_account": "Plebbit 계정",
|
||||
"save_changes": "변경 사항 저장",
|
||||
"reset_changes": "변경 사항 초기화",
|
||||
"crypto_address_yours": "암호 주소는이 계정에 속합니다.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "업데이트",
|
||||
"downloading_board": "게시판 다운로드 중",
|
||||
"subscriptions_subtitle": "{{count}}개의 board를 구독했습니다",
|
||||
"subscriptions_subtitle_one": "{{count}}개의 board를 구독했습니다"
|
||||
"subscriptions_subtitle_one": "{{count}}개의 board를 구독했습니다",
|
||||
"all_boards": "모든 게시판",
|
||||
"nsfw_boards_only": "NSFW 게시판만",
|
||||
"worksafe_boards_only": "직장 안전 게시판만",
|
||||
"bitsocial_account": "Bitsocial 계정",
|
||||
"p2p_options": "P2P 옵션"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "5chan काय आहे?",
|
||||
"go": "जा",
|
||||
"or": "किव्हा",
|
||||
"5chan_description": "5chan हे सर्व्हरहीन, अॅडमिनहीन, विकेंद्रीकृत आणि ओपन-सोर्स 4chan पर्याय आहे, जेथे कोणीही अमर्यादित बोर्ड तयार करू शकतो आणि क्रिप्टोग्राफिकदृष्ट्या त्यांची मालकी घेऊ शकतो. सर्व बोर्ड त्यांच्या मालकांद्वारे होस्ट आणि मॉडरेट केले जातात, जे नोड्स चालवतात ज्यांना वापरकर्ते कनेक्ट करतात आणि सीड करतात, peer-to-peer. सर्व डेटा फक्त मजकूर आहे, प्रतिमा एम्बेड करण्यासाठी लिंक समाविष्ट आहेत. खाली असलेल्या बोर्डवर क्लिक करण्यासाठी मोकळ्या मनाने जे तुम्हाला स्वारस्य वाटते आणि त्वरित सुरू करा!",
|
||||
"5chan_description": "5chan हे पूर्णपणे विकेंद्रीकृत आणि ओपन-सोर्स 4chan पर्याय आहे, जेथे कोणीही अमर्यादित बोर्ड तयार करू शकतो आणि क्रिप्टोग्राफिकदृष्ट्या मालकी घेऊ शकतो. बोर्ड मालक त्यांच्या बोर्डचे होस्टिंग आणि मॉडरेशन करतात आणि त्याचे <1>नियम</1> सेट करतात. सर्व डेटा फक्त मजकूर आहे आणि तो पीअर-टू-पीअर शेअर केला जातो, एम्बेड इमेजच्या लिंक्ससह. अधिक जाणून घेण्यासाठी <2>FAQ</2> तपासा. खाली असलेल्या बोर्डवर क्लिक करण्यासाठी मोकळ्या मनाने जे तुम्हाला स्वारस्य आहे आणि लगेच सुरू करा!",
|
||||
"no_global_rules_info": "तुम्ही नेहमी कोणत्याही बोर्डशी कनेक्ट होऊ शकता, पूर्णपणे P2P, कोणीही तुम्हाला थांबवू शकत नाही. फक्त वरील शोध फील्डमध्ये त्याचा पत्ता पेस्ट करा.",
|
||||
"boards": "संदेश फलक",
|
||||
"popular_threads": "लोकप्रिय थ्रेड्स",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} प्रतिसाद वगळले गेले आहेत. <1>पाहण्यासाठी येथे क्लिक करा</1>.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} प्रतिसाद आणि {{linksCount}} दुवे वगळले गेले आहेत. <1>पाहण्यासाठी येथे क्लिक करा</1>.",
|
||||
"this_thread_is_closed": "हा थ्रेड बंद आहे",
|
||||
"reply_to_cid": "{{cid}} ला उत्तर द्या",
|
||||
"reply_to_no": "पोस्ट No.{{no}} ला उत्तर द्या",
|
||||
"submit": "सबमिट करा",
|
||||
"gif": "जीआईएफ",
|
||||
"moderating": "मॉडरेट करीत आहे",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "खाता",
|
||||
"crypto_address": "क्रिप्टो पत्ता",
|
||||
"crypto_wallets": "क्रिप्टो बटव्या",
|
||||
"plebbit_options": "प्लेबिट पर्याय",
|
||||
"reset": "रीसेट करा",
|
||||
"import": "आयात करा",
|
||||
"export": "निर्यात करा",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "स्थानिकपणे संग्रहित ({{location}}), उपकरणांमध्ये समन्वयित केलेले नाही",
|
||||
"choose_one": "एक निवडा:",
|
||||
"create": "तयार करा",
|
||||
"plebbit_account": "Plebbit खाता",
|
||||
"save_changes": "बदलांना जतन करा",
|
||||
"reset_changes": "बदलांना रीसेट करा",
|
||||
"crypto_address_yours": "क्रिप्टो पत्ता हा खात्याकडे संबंधित आहे.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "अपडेट्स",
|
||||
"downloading_board": "फोरम डाउनलोड करत आहे",
|
||||
"subscriptions_subtitle": "तुम्ही {{count}} board ला सदस्यता घेतली आहे",
|
||||
"subscriptions_subtitle_one": "तुम्ही {{count}} board ला सदस्यता घेतली आहे"
|
||||
"subscriptions_subtitle_one": "तुम्ही {{count}} board ला सदस्यता घेतली आहे",
|
||||
"all_boards": "सर्व बोर्ड",
|
||||
"nsfw_boards_only": "फक्त NSFW बोर्ड",
|
||||
"worksafe_boards_only": "फक्त कामासाठी सुरक्षित बोर्ड",
|
||||
"bitsocial_account": "Bitsocial खाते",
|
||||
"p2p_options": "P2P पर्याय"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "Wat is 5chan?",
|
||||
"go": "gaan",
|
||||
"or": "of",
|
||||
"5chan_description": "5chan is een serverloos, adminloos, gedecentraliseerd en open-source alternatief voor 4chan, waar iedereen onbeperkte boards kan maken en cryptografisch bezitten. Alle boards worden gehost en gemodereerd door hun eigenaren, die nodes draaien waarmee gebruikers verbinden en seeden, peer-to-peer. Alle gegevens zijn alleen tekst, inclusief links om afbeeldingen in te sluiten. Voel je vrij om op een board hieronder te klikken dat je interesseert en begin meteen!",
|
||||
"5chan_description": "5chan is een volledig gedecentraliseerd en open-source alternatief voor 4chan, waar iedereen onbeperkt boards kan maken en cryptografisch bezitten. Board-eigenaren hosten en modereren hun board en stellen de <1>Regels</1> ervan in. Alle gegevens zijn alleen tekst en worden peer-to-peer gedeeld, inclusief links naar ingesloten afbeeldingen. Bekijk de <2>FAQ</2> voor meer informatie. Voel je vrij om op een board hieronder te klikken dat je interesseert en begin meteen!",
|
||||
"no_global_rules_info": "Je kunt altijd verbinden met elk forum, volledig P2P, niemand kan je stoppen. Plak gewoon het adres in het zoekveld hierboven.",
|
||||
"boards": "forums",
|
||||
"popular_threads": "Populaire threads",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} reacties weggelaten. <1>Klik hier</1> om ze te bekijken.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} reacties en {{linksCount}} links weggelaten. <1>Klik hier</1> om ze te bekijken.",
|
||||
"this_thread_is_closed": "De thread is gesloten",
|
||||
"reply_to_cid": "Reageren op {{cid}}",
|
||||
"reply_to_no": "Reageren op post No.{{no}}",
|
||||
"submit": "Verzenden",
|
||||
"gif": "GIF",
|
||||
"moderating": "modereren",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "account",
|
||||
"crypto_address": "crypto adres",
|
||||
"crypto_wallets": "Crypto-portefeuilles",
|
||||
"plebbit_options": "plebbit-opties",
|
||||
"reset": "resetten",
|
||||
"import": "importeer",
|
||||
"export": "exporteer",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "Lokaal opgeslagen ({{location}}), niet gesynchroniseerd tussen apparaten",
|
||||
"choose_one": "Kies er een:",
|
||||
"create": "Maak aan",
|
||||
"plebbit_account": "Plebbit-account",
|
||||
"save_changes": "Wijzigingen opslaan",
|
||||
"reset_changes": "Wijzigingen resetten",
|
||||
"crypto_address_yours": "Crypto-adres behoort tot deze account.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "Updates",
|
||||
"downloading_board": "board downloaden",
|
||||
"subscriptions_subtitle": "Je bent geabonneerd op {{count}} board",
|
||||
"subscriptions_subtitle_one": "Je bent geabonneerd op {{count}} board"
|
||||
"subscriptions_subtitle_one": "Je bent geabonneerd op {{count}} board",
|
||||
"all_boards": "Alle borden",
|
||||
"nsfw_boards_only": "Alleen NSFW-borden",
|
||||
"worksafe_boards_only": "Alleen werkveilige borden",
|
||||
"bitsocial_account": "Bitsocial-account",
|
||||
"p2p_options": "P2P-opties"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "Hva er 5chan?",
|
||||
"go": "gå",
|
||||
"or": "eller",
|
||||
"5chan_description": "5chan er et serverløst, adminløst, desentralisert og åpen kildekode-alternativ til 4chan, hvor alle kan opprette og kryptografisk eie ubegrensede boards. Alle boards hostes og modereres av sine eiere, som kjører noder som brukere kobler til og seed, peer-to-peer. Alle data er kun tekst, inkludert lenker for å bygge inn bilder. Gjerne klikk på et board nedenfor som interesserer deg og kom i gang med en gang!",
|
||||
"5chan_description": "5chan er et fullstendig desentralisert og åpen kildekode-alternativ til 4chan, hvor hvem som helst kan opprette og kryptografisk eie ubegrensede boards. Board-eiere hoster og modererer sitt board og setter dets <1>Regler</1>. Alle data er kun tekst og deles peer-to-peer, inkludert lenker til innebygde bilder. Sjekk ut <2>FAQ</2> for å lære mer. Ta gjerne en titt på et board nedenfor som interesserer deg og kom i gang med en gang!",
|
||||
"no_global_rules_info": "Du kan alltid koble til hvilket som helst forum, fullt P2P, ingen kan stoppe deg. Lim bare adressen i søkefeltet ovenfor.",
|
||||
"boards": "fora",
|
||||
"popular_threads": "Populære tråder",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} svar utelatt. <1>Klikk her</1> for å se dem.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} svar og {{linksCount}} lenker utelatt. <1>Klikk her</1> for å se dem.",
|
||||
"this_thread_is_closed": "Tråden er stengt",
|
||||
"reply_to_cid": "Svar til {{cid}}",
|
||||
"reply_to_no": "Svar til innlegg No.{{no}}",
|
||||
"submit": "Send inn",
|
||||
"gif": "GIF",
|
||||
"moderating": "modererer",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "konto",
|
||||
"crypto_address": "kryptoadresse",
|
||||
"crypto_wallets": "Crypto-lommebøker",
|
||||
"plebbit_options": "plebbit alternativer",
|
||||
"reset": "nullstill",
|
||||
"import": "importer",
|
||||
"export": "eksporter",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "Lagring lokalt ({{location}}), ikke synkronisert på tvers av enheter",
|
||||
"choose_one": "Velg en:",
|
||||
"create": "Opprett",
|
||||
"plebbit_account": "Plebbit-konto",
|
||||
"save_changes": "Lagre endringer",
|
||||
"reset_changes": "Tilbakestill endringer",
|
||||
"crypto_address_yours": "Crypto-adressen tilhører denne kontoen.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "Oppdateringer",
|
||||
"downloading_board": "laster ned forum",
|
||||
"subscriptions_subtitle": "Du har abonnert på {{count}} board",
|
||||
"subscriptions_subtitle_one": "Du har abonnert på {{count}} board"
|
||||
"subscriptions_subtitle_one": "Du har abonnert på {{count}} board",
|
||||
"all_boards": "Alle tavler",
|
||||
"nsfw_boards_only": "Kun NSFW-tavler",
|
||||
"worksafe_boards_only": "Kun arbeidssikre tavler",
|
||||
"bitsocial_account": "Bitsocial-konto",
|
||||
"p2p_options": "P2P-alternativer"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "Co to jest 5chan?",
|
||||
"go": "iść",
|
||||
"or": "lub",
|
||||
"5chan_description": "5chan to bezserwerowa, bez administratora, zdecentralizowana i open-source alternatywa dla 4chan, gdzie każdy może tworzyć i kryptograficznie posiadać nieograniczone tablice. Wszystkie tablice są hostowane i moderowane przez ich właścicieli, którzy uruchamiają węzły, do których użytkownicy się łączą i seedują, peer-to-peer. Wszystkie dane są tylko tekstem, w tym linki do osadzania obrazów. Śmiało kliknij na tablicę poniżej, która Cię interesuje i zacznij od razu!",
|
||||
"5chan_description": "5chan to w pełni zdecentralizowana i open-source alternatywa dla 4chan, gdzie każdy może tworzyć i kryptograficznie posiadać nieograniczone tablice. Właściciele tablic hostują i moderują swoje tablice oraz ustawiają ich <1>Zasady</1>. Wszystkie dane są tylko tekstem i są udostępniane peer-to-peer, w tym linki do osadzonych obrazów. Sprawdź <2>FAQ</2>, aby dowiedzieć się więcej. Śmiało kliknij na tablicę poniżej, która Cię interesuje i zacznij od razu!",
|
||||
"no_global_rules_info": "Zawsze możesz połączyć się z dowolnym forum, w pełni P2P, nikt nie może cię zatrzymać. Po prostu wklej jego adres w polu wyszukiwania powyżej.",
|
||||
"boards": "fora",
|
||||
"popular_threads": "Popularne wątki",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "Pominięto {{repliesCount}} odpowiedzi. <1>Kliknij tutaj</1>, aby je zobaczyć.",
|
||||
"replies_and_links_omitted": "Pominięto {{repliesCount}} odpowiedzi i {{linksCount}} linków. <1>Kliknij tutaj</1>, aby je zobaczyć.",
|
||||
"this_thread_is_closed": "Wątek jest zamknięty",
|
||||
"reply_to_cid": "Odpowiedz na {{cid}}",
|
||||
"reply_to_no": "Odpowiedz na post No.{{no}}",
|
||||
"submit": "Zatwierdź",
|
||||
"gif": "GIF",
|
||||
"moderating": "moderujesz",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "konto",
|
||||
"crypto_address": "adres kryptowalutowy",
|
||||
"crypto_wallets": "Portfele kryptowalut",
|
||||
"plebbit_options": "opcje plebbit",
|
||||
"reset": "zresetuj",
|
||||
"import": "importuj",
|
||||
"export": "eksportuj",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "Przechowywane lokalnie ({{location}}), nie synchronizowane między urządzeniami",
|
||||
"choose_one": "Wybierz jeden:",
|
||||
"create": "Utwórz",
|
||||
"plebbit_account": "Konto Plebbit",
|
||||
"save_changes": "Zapisz zmiany",
|
||||
"reset_changes": "Zresetuj zmiany",
|
||||
"crypto_address_yours": "Adres kryptowaluty należy do tego konta.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "Aktualizacje",
|
||||
"downloading_board": "pobieranie forum",
|
||||
"subscriptions_subtitle": "Subskrybujesz {{count}} board",
|
||||
"subscriptions_subtitle_one": "Subskrybujesz {{count}} board"
|
||||
"subscriptions_subtitle_one": "Subskrybujesz {{count}} board",
|
||||
"all_boards": "Wszystkie tablice",
|
||||
"nsfw_boards_only": "Tylko tablice NSFW",
|
||||
"worksafe_boards_only": "Tylko bezpieczne tablice",
|
||||
"bitsocial_account": "Konto Bitsocial",
|
||||
"p2p_options": "Opcje P2P"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "O que é 5chan?",
|
||||
"go": "vai",
|
||||
"or": "ou",
|
||||
"5chan_description": "5chan é uma alternativa descentralizada, sem servidor, sem administrador e de código aberto ao 4chan, onde qualquer pessoa pode criar e possuir criptograficamente quadros ilimitados. Todos os quadros são hospedados e moderados por seus proprietários, que executam nós aos quais os usuários se conectam e compartilham, peer-to-peer. Todos os dados são apenas texto, incluindo links para incorporar imagens. Sinta-se à vontade para clicar em um quadro abaixo que lhe interesse e comece imediatamente!",
|
||||
"5chan_description": "5chan é uma alternativa totalmente descentralizada e de código aberto ao 4chan, onde qualquer pessoa pode criar e possuir criptograficamente quadros ilimitados. Os proprietários dos quadros hospedam e moderam seu quadro e definem suas <1>Regras</1>. Todos os dados são apenas texto e são compartilhados peer-to-peer, incluindo links para imagens incorporadas. Confira as <2>Perguntas Frequentes</2> para saber mais. Sinta-se à vontade para clicar em um quadro abaixo que lhe interesse e comece imediatamente!",
|
||||
"no_global_rules_info": "Você sempre pode se conectar a qualquer fórum, completamente P2P, ninguém pode impedi-lo. Simplesmente cole o endereço no campo de pesquisa acima.",
|
||||
"boards": "fóruns",
|
||||
"popular_threads": "Tópicos populares",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} respostas omitidas. <1>Clique aqui</1> para visualizar.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} respostas e {{linksCount}} links omitidos. <1>Clique aqui</1> para visualizar.",
|
||||
"this_thread_is_closed": "O tópico está fechado",
|
||||
"reply_to_cid": "Responder a {{cid}}",
|
||||
"reply_to_no": "Responder ao post No.{{no}}",
|
||||
"submit": "Enviar",
|
||||
"gif": "GIF",
|
||||
"moderating": "moderando",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "conta",
|
||||
"crypto_address": "endereço de cripto",
|
||||
"crypto_wallets": "Carteiras cripto",
|
||||
"plebbit_options": "opções plebbit",
|
||||
"reset": "redefinir",
|
||||
"import": "importar",
|
||||
"export": "exportar",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "Armazenado localmente ({{location}}), não sincronizado entre dispositivos",
|
||||
"choose_one": "Escolha um:",
|
||||
"create": "Criar",
|
||||
"plebbit_account": "Conta Plebbit",
|
||||
"save_changes": "Salvar Alterações",
|
||||
"reset_changes": "Redefinir Alterações",
|
||||
"crypto_address_yours": "O endereço de criptomoeda pertence a esta conta.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "Atualizações",
|
||||
"downloading_board": "baixando fórum",
|
||||
"subscriptions_subtitle": "Você se inscreveu em {{count}} board",
|
||||
"subscriptions_subtitle_one": "Você se inscreveu em {{count}} board"
|
||||
"subscriptions_subtitle_one": "Você se inscreveu em {{count}} board",
|
||||
"all_boards": "Todos os quadros",
|
||||
"nsfw_boards_only": "Apenas quadros NSFW",
|
||||
"worksafe_boards_only": "Apenas quadros seguros para o trabalho",
|
||||
"bitsocial_account": "Conta Bitsocial",
|
||||
"p2p_options": "Opções P2P"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "Ce este 5chan?",
|
||||
"go": "du-te",
|
||||
"or": "sau",
|
||||
"5chan_description": "5chan este o alternativă descentralizată, fără server, fără administrator și open-source la 4chan, unde oricine poate crea și deține criptografic panouri nelimitate. Toate panourile sunt găzduite și moderate de proprietarii lor, care rulează noduri la care utilizatorii se conectează și distribuie, peer-to-peer. Toate datele sunt doar text, inclusiv link-uri pentru a încorpora imagini. Simțiți-vă liber să faceți clic pe un panou de mai jos care vă interesează și începeți imediat!",
|
||||
"5chan_description": "5chan este o alternativă complet descentralizată și open-source la 4chan, unde oricine poate crea și deține criptografic panouri nelimitate. Proprietarii panourilor găzduiesc și modelează panoul lor și stabilesc <1>Regulile</1> acestuia. Toate datele sunt doar text și sunt partajate peer-to-peer, inclusiv link-uri către imagini încorporate. Consultă <2>Întrebările Frecvente</2> pentru a afla mai multe. Simte-te liber să dai click pe un panou de mai jos care te interesează și începe imediat!",
|
||||
"no_global_rules_info": "Poți să te conectezi întotdeauna la orice forum, complet P2P, nimeni nu te poate opri. Pur și simplu lipește adresa în câmpul de căutare de mai sus.",
|
||||
"boards": "forumuri",
|
||||
"popular_threads": "Threaduri populare",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} răspunsuri omise. <1>Click aici</1> pentru a le vizualiza.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} răspunsuri și {{linksCount}} linkuri omise. <1>Click aici</1> pentru a le vizualiza.",
|
||||
"this_thread_is_closed": "Acest fir de discuție este închis",
|
||||
"reply_to_cid": "Răspundeți la {{cid}}",
|
||||
"reply_to_no": "Răspundeți la postarea No.{{no}}",
|
||||
"submit": "Trimite",
|
||||
"gif": "GIF",
|
||||
"moderating": "moderează",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "cont",
|
||||
"crypto_address": "adresa cripto",
|
||||
"crypto_wallets": "Portofele crypto",
|
||||
"plebbit_options": "opțiuni plebbit",
|
||||
"reset": "resetează",
|
||||
"import": "importați",
|
||||
"export": "exportați",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "Stocat local ({{location}}), nu este sincronizat între dispozitive",
|
||||
"choose_one": "Alege una:",
|
||||
"create": "Creează",
|
||||
"plebbit_account": "Cont Plebbit",
|
||||
"save_changes": "Salvează modificările",
|
||||
"reset_changes": "Resetează modificările",
|
||||
"crypto_address_yours": "Adresa crypto aparține acestui cont.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "Actualizări",
|
||||
"downloading_board": "se descarcă forumul",
|
||||
"subscriptions_subtitle": "Te-ai abonat la {{count}} board",
|
||||
"subscriptions_subtitle_one": "Te-ai abonat la {{count}} board"
|
||||
"subscriptions_subtitle_one": "Te-ai abonat la {{count}} board",
|
||||
"all_boards": "Toate panourile",
|
||||
"nsfw_boards_only": "Doar panouri NSFW",
|
||||
"worksafe_boards_only": "Doar panouri sigure pentru muncă",
|
||||
"bitsocial_account": "Cont Bitsocial",
|
||||
"p2p_options": "Opțiuni P2P"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "Что такое 5chan?",
|
||||
"go": "идти",
|
||||
"or": "или",
|
||||
"5chan_description": "5chan — это серверная, без администратора, децентрализованная и открытая альтернатива 4chan, где любой может создавать и криптографически владеть неограниченным количеством досок. Все доски размещаются и модерируются их владельцами, которые запускают узлы, к которым пользователи подключаются и раздают, peer-to-peer. Все данные только текстовые, включая ссылки для встраивания изображений. Не стесняйтесь нажать на доску ниже, которая вас заинтересует, и сразу же начните!",
|
||||
"5chan_description": "5chan — это полностью децентрализованная альтернатива 4chan с открытым исходным кодом, где любой может создавать и криптографически владеть неограниченным количеством досок. Владельцы досок размещают и модерируют свои доски и устанавливают их <1>Правила</1>. Все данные — это только текст, который распространяется одноранговым способом, включая ссылки на встроенные изображения. Ознакомьтесь с <2>Часто задаваемыми вопросами</2>, чтобы узнать больше. Не стесняйтесь нажать на доску ниже, которая вас заинтересует, и сразу же начать!",
|
||||
"no_global_rules_info": "Вы всегда можете подключиться к любому форуму, полностью P2P, никто не может вас остановить. Просто вставьте адрес в поле поиска выше.",
|
||||
"boards": "форумы",
|
||||
"popular_threads": "Популярные темы",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} ответов пропущено. <1>Нажмите здесь</1>, чтобы посмотреть.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} ответов и {{linksCount}} ссылок пропущено. <1>Нажмите здесь</1>, чтобы посмотреть.",
|
||||
"this_thread_is_closed": "Тема закрыта",
|
||||
"reply_to_cid": "Ответить на {{cid}}",
|
||||
"reply_to_no": "Ответить на пост No.{{no}}",
|
||||
"submit": "Отправить",
|
||||
"gif": "ГИФ",
|
||||
"moderating": "модерируете",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "аккаунт",
|
||||
"crypto_address": "адрес криптовалюты",
|
||||
"crypto_wallets": "Криптокошельки",
|
||||
"plebbit_options": "опции plebbit",
|
||||
"reset": "сбросить",
|
||||
"import": "импортировать",
|
||||
"export": "экспортировать",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "Сохранено локально ({{location}}), не синхронизировано между устройствами",
|
||||
"choose_one": "Выберите один:",
|
||||
"create": "Создать",
|
||||
"plebbit_account": "Учетная запись Plebbit",
|
||||
"save_changes": "Сохранить изменения",
|
||||
"reset_changes": "Сбросить изменения",
|
||||
"crypto_address_yours": "Адрес криптовалюты принадлежит этому аккаунту.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "Обновления",
|
||||
"downloading_board": "загрузка форума",
|
||||
"subscriptions_subtitle": "Вы подписались на {{count}} board",
|
||||
"subscriptions_subtitle_one": "Вы подписались на {{count}} board"
|
||||
"subscriptions_subtitle_one": "Вы подписались на {{count}} board",
|
||||
"all_boards": "Все доски",
|
||||
"nsfw_boards_only": "Только доски NSFW",
|
||||
"worksafe_boards_only": "Только безопасные для работы доски",
|
||||
"bitsocial_account": "Аккаунт Bitsocial",
|
||||
"p2p_options": "Опции P2P"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "Çfarë është 5chan?",
|
||||
"go": "shko",
|
||||
"or": "ose",
|
||||
"5chan_description": "5chan është një alternativë pa server, pa administrator, e decentralizuar dhe me kod të hapur për 4chan, ku kushdo mund të krijojë dhe të zotërojë kriptografikisht borda të pakufizuara. Të gjitha bordat hostohen dhe moderohen nga pronarët e tyre, të cilët ekzekutojnë nyje me të cilat përdoruesit lidhen dhe shpërndajnë, peer-to-peer. Të gjitha të dhënat janë vetëm tekst, duke përfshirë lidhjet për të integruar imazhe. Mos ngurroni të klikoni në një bord më poshtë që ju intereson dhe filloni menjëherë!",
|
||||
"5chan_description": "5chan është një alternativë plotësisht e decentralizuar dhe me burim të hapur për 4chan, ku kushdo mund të krijojë dhe të zotërojë kriptografikisht bordet e pakufizuara. Pronarët e bordit hostojnë dhe moderojnë bordin e tyre dhe vendosin <1>Rregullat</1> e tij. Të gjitha të dhënat janë vetëm tekst dhe shpërndahen peer-to-peer, duke përfshirë lidhjet me imazhet e integruara. Shikoni <2>Pyetjet e Shpeshta</2> për të mësuar më shumë. Mos hezitoni të klikoni në një bord më poshtë që ju intereson dhe filloni menjëherë!",
|
||||
"no_global_rules_info": "Mund të lidheni gjithmonë me çdo forum, plotësisht P2P, askush nuk mund t'ju ndalojë. Thjesht ngjisni adresën në fushën e kërkimit më sipër.",
|
||||
"boards": "forume",
|
||||
"popular_threads": "Threadet e njohur",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} përgjigje janë lënë jashtë. <1>Kliko këtu</1> për t'i shikuar.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} përgjigje dhe {{linksCount}} lidhje janë lënë jashtë. <1>Kliko këtu</1> për t'i shikuar.",
|
||||
"this_thread_is_closed": "Temë është mbyllur",
|
||||
"reply_to_cid": "Përgjigju në {{cid}}",
|
||||
"reply_to_no": "Përgjigju në postin No.{{no}}",
|
||||
"submit": "Dorëzo",
|
||||
"gif": "GIF",
|
||||
"moderating": "moderimi",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "llogari",
|
||||
"crypto_address": "adresa e kripto",
|
||||
"crypto_wallets": "Portofolitë e cryptos",
|
||||
"plebbit_options": "opsione plebbit",
|
||||
"reset": "rithqas",
|
||||
"import": "importo",
|
||||
"export": "eksport",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "Ruajtur lokalmente ({{location}}), nuk është sinkronizuar ndërmjet pajisjeve",
|
||||
"choose_one": "Zgjidhni një:",
|
||||
"create": "Krijo",
|
||||
"plebbit_account": "Llogaria Plebbit",
|
||||
"save_changes": "Ruaj ndryshimet",
|
||||
"reset_changes": "Rivendosni ndryshimet",
|
||||
"crypto_address_yours": "Adresa e kriptovalutës i përket këtij llogarie.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "Përditësime",
|
||||
"downloading_board": "duke shkarkuar forum",
|
||||
"subscriptions_subtitle": "Jeni abonuar në {{count}} board",
|
||||
"subscriptions_subtitle_one": "Jeni abonuar në {{count}} board"
|
||||
"subscriptions_subtitle_one": "Jeni abonuar në {{count}} board",
|
||||
"all_boards": "Të gjitha tabelat",
|
||||
"nsfw_boards_only": "Vetëm tabelat NSFW",
|
||||
"worksafe_boards_only": "Vetëm tabelat e sigurta për punë",
|
||||
"bitsocial_account": "Llogaria Bitsocial",
|
||||
"p2p_options": "Opsionet P2P"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "Vad är 5chan?",
|
||||
"go": "gå",
|
||||
"or": "eller",
|
||||
"5chan_description": "5chan är ett serverlöst, adminlöst, decentraliserat och öppen källkodsalternativ till 4chan, där vem som helst kan skapa och kryptografiskt äga obegränsade tavlor. Alla tavlor hostas och modereras av sina ägare, som kör noder som användare ansluter till och seedar, peer-to-peer. All data är endast text, inklusive länkar för att bädda in bilder. Känn dig fri att klicka på en tavla nedan som intresserar dig och börja direkt!",
|
||||
"5chan_description": "5chan är ett helt decentraliserat och öppet källkodsalternativ till 4chan, där vem som helst kan skapa och kryptografiskt äga obegränsade tavlor. Tavlornas ägare hostar och modererar sin tavla och ställer in dess <1>Regler</1>. All data är endast text och delas peer-to-peer, inklusive länkar till inbäddade bilder. Kolla in <2>FAQ</2> för att lära dig mer. Klicka gärna på en tavla nedan som intresserar dig och börja direkt!",
|
||||
"no_global_rules_info": "Du kan alltid ansluta till vilket forum som helst, helt P2P, ingen kan stoppa dig. Klistra bara in adressen i sökfältet ovan.",
|
||||
"boards": "forum",
|
||||
"popular_threads": "Populära trådar",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} svar utelämnade. <1>Klicka här</1> för att visa dem.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} svar och {{linksCount}} länkar utelämnade. <1>Klicka här</1> för att visa dem.",
|
||||
"this_thread_is_closed": "Tråden är stängd",
|
||||
"reply_to_cid": "Svara på {{cid}}",
|
||||
"reply_to_no": "Svara på inlägg No.{{no}}",
|
||||
"submit": "Skicka in",
|
||||
"gif": "GIF",
|
||||
"moderating": "modererar",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "konto",
|
||||
"crypto_address": "kryptoadress",
|
||||
"crypto_wallets": "Crypto-plånböcker",
|
||||
"plebbit_options": "plebbit-alternativ",
|
||||
"reset": "återställ",
|
||||
"import": "importera",
|
||||
"export": "exportera",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "Lagrad lokalt ({{location}}), inte synkroniserad mellan enheter",
|
||||
"choose_one": "Välj en:",
|
||||
"create": "Skapa",
|
||||
"plebbit_account": "Plebbit-konto",
|
||||
"save_changes": "Spara ändringar",
|
||||
"reset_changes": "Återställ ändringar",
|
||||
"crypto_address_yours": "Crypto-adressen tillhör denna konto.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "Uppdateringar",
|
||||
"downloading_board": "laddar ner forum",
|
||||
"subscriptions_subtitle": "Du har prenumererat på {{count}} board",
|
||||
"subscriptions_subtitle_one": "Du har prenumererat på {{count}} board"
|
||||
"subscriptions_subtitle_one": "Du har prenumererat på {{count}} board",
|
||||
"all_boards": "Alla tavlor",
|
||||
"nsfw_boards_only": "Endast NSFW-tavlor",
|
||||
"worksafe_boards_only": "Endast arbetsplats-säkra tavlor",
|
||||
"bitsocial_account": "Bitsocial-konto",
|
||||
"p2p_options": "P2P-alternativ"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "5chan ఏమిటి?",
|
||||
"go": "వెళ్లు",
|
||||
"or": "లేదా",
|
||||
"5chan_description": "5chan అనేది సర్వర్-లేని, అడ్మిన్-లేని, వికేంద్రీకృత మరియు ఓపెన్-సోర్స్ 4chan ప్రత్యామ్నాయం, ఇక్కడ ఎవరైనా అపరిమిత బోర్డులను సృష్టించవచ్చు మరియు క్రిప్టోగ్రాఫిక్గా కలిగి ఉండవచ్చు. అన్ని బోర్డులు వాటి యజమానులచే హోస్ట్ చేయబడతాయి మరియు మోడరేట్ చేయబడతాయి, వారు నోడ్లను అమలు చేస్తారు, వీటికి వినియోగదారులు కనెక్ట్ అవుతారు మరియు సీడ్ చేస్తారు, peer-to-peer. అన్ని డేటా టెక్స్ట్ మాత్రమే, చిత్రాలను ఎంబెడ్ చేయడానికి లింక్లతో సహా. మీకు ఆసక్తి ఉన్న క్రింది బోర్డుపై క్లిక్ చేయడానికి స్వేచ్ఛగా ఉండండి మరియు వెంటనే ప్రారంభించండి!",
|
||||
"5chan_description": "5chan అనేది పూర్తిగా వికేంద్రీకృతమైన మరియు ఓపెన్-సోర్స్ 4chan ప్రత్యామ్నాయం, ఇక్కడ ఎవరైనా అపరిమిత బోర్డులను సృష్టించవచ్చు మరియు క్రిప్టోగ్రాఫిక్గా కలిగి ఉండవచ్చు. బోర్డ్ యజమానులు వారి బోర్డ్ను హోస్ట్ చేస్తారు మరియు మోడరేట్ చేస్తారు మరియు దాని <1>నియమాలు</1> సెట్ చేస్తారు. అన్ని డేటా టెక్స్ట్ మాత్రమే మరియు ఇది పీర్-టు-పీర్గా భాగస్వామ్యం చేయబడుతుంది, ఎంబెడ్ చిత్రాలకు లింక్లతో సహా. మరింత తెలుసుకోవడానికి <2>FAQ</2>ని చూడండి. మీకు ఆసక్తి ఉన్న క్రింది బోర్డ్పై క్లిక్ చేయడానికి సంకోచించకండి మరియు వెంటనే ప్రారంభించండి!",
|
||||
"no_global_rules_info": "మీరు ఎప్పుడైనా ఏదైనా బోర్డ్కు కనెక్ట్ అవ్వవచ్చు, పూర్తిగా P2P, మిమ్మల్ని ఎవరూ ఆపలేరు. దాని చిరునామాను పైన ఉన్న శోధన ఫీల్డ్లో అతికించండి.",
|
||||
"boards": "సందేశ బోర్డులు",
|
||||
"popular_threads": "జనప్రియమైన థ్రెడ్లు",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} స్పందనలు వదిలివేయబడ్డాయి. <1>చూడటానికి ఇక్కడ క్లిక్ చేయండి</1>.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} స్పందనలు మరియు {{linksCount}} లింకులు వదిలివేయబడ్డాయి. <1>చూడటానికి ఇక్కడ క్లిక్ చేయండి</1>.",
|
||||
"this_thread_is_closed": "ఈ థ్రెడ్ మూసివేయబడింది",
|
||||
"reply_to_cid": "{{cid}}కి స్పందించండి",
|
||||
"reply_to_no": "పోస్ట్ No.{{no}}కి స్పందించండి",
|
||||
"submit": "సమర్పించు",
|
||||
"gif": "జీఐఎఫ్",
|
||||
"moderating": "మాడరేటింగ్",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "ఖాతా",
|
||||
"crypto_address": "క్రిప్టో చిరునామా",
|
||||
"crypto_wallets": "క్రిప్టో వాలెట్లు",
|
||||
"plebbit_options": "plebbit ఎంపికలు",
|
||||
"reset": "రీసెట్",
|
||||
"import": "దిగుమతి",
|
||||
"export": "ఎగుమతి",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "స్థానికంగా నిల్వ ({{location}}), పరికరాల మధ్య సమన్వయించడం లేదు",
|
||||
"choose_one": "ఒకటి ఎంపిక చేసుకోండి:",
|
||||
"create": "సృష్టించండి",
|
||||
"plebbit_account": "Plebbit ఖాతా",
|
||||
"save_changes": "మార్పులను సేవ్ చేయండి",
|
||||
"reset_changes": "మార్పులను రీసెట్ చేయండి",
|
||||
"crypto_address_yours": "క్రిప్టో చిరునామా ఈ ఖాతాకు చేరుకుంది.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "నవీకరణలు",
|
||||
"downloading_board": "ఫోరం డౌన్లోడ్ చేయడం",
|
||||
"subscriptions_subtitle": "మీరు {{count}} boardకు సభ్యత్వం పొందారు",
|
||||
"subscriptions_subtitle_one": "మీరు {{count}} boardకు సభ్యత్వం పొందారు"
|
||||
"subscriptions_subtitle_one": "మీరు {{count}} boardకు సభ్యత్వం పొందారు",
|
||||
"all_boards": "అన్ని బోర్డులు",
|
||||
"nsfw_boards_only": "NSFW బోర్డులు మాత్రమే",
|
||||
"worksafe_boards_only": "పని కోసం సురక్షితమైన బోర్డులు మాత్రమే",
|
||||
"bitsocial_account": "Bitsocial ఖాతా",
|
||||
"p2p_options": "P2P ఎంపికలు"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "5chan คืออะไร?",
|
||||
"go": "ไป",
|
||||
"or": "หรือ",
|
||||
"5chan_description": "5chan เป็นทางเลือกที่ไม่มีเซิร์ฟเวอร์ ไม่มีผู้ดูแล กระจายอำนาจ และโอเพนซอร์สสำหรับ 4chan ซึ่งทุกคนสามารถสร้างและเป็นเจ้าของบอร์ดได้ไม่จำกัดด้วยการเข้ารหัส บอร์ดทั้งหมดถูกโฮสต์และดูแลโดยเจ้าของ ซึ่งรันโหนดที่ผู้ใช้เชื่อมต่อและแชร์ peer-to-peer ข้อมูลทั้งหมดเป็นข้อความเท่านั้น รวมถึงลิงก์สำหรับฝังรูปภาพ อย่าลังเลที่จะคลิกที่บอร์ดด้านล่างที่คุณสนใจและเริ่มต้นทันที!",
|
||||
"5chan_description": "5chan เป็นทางเลือกที่กระจายอำนาจอย่างสมบูรณ์และโอเพนซอร์สสำหรับ 4chan โดยทุกคนสามารถสร้างและเป็นเจ้าของบอร์ดได้ไม่จำกัดด้วยการเข้ารหัส เจ้าของบอร์ดโฮสต์และดูแลบอร์ดของตนและกำหนด <1>กฎ</1> ของมัน ข้อมูลทั้งหมดเป็นข้อความเท่านั้นและถูกแชร์แบบ peer-to-peer รวมถึงลิงก์ไปยังรูปภาพที่ฝังอยู่ ดู <2>FAQ</2> เพื่อเรียนรู้เพิ่มเติม อย่าลังเลที่จะคลิกบอร์ดด้านล่างที่คุณสนใจและเริ่มต้นทันที!",
|
||||
"no_global_rules_info": "คุณสามารถเชื่อมต่อกับบอร์ดใดก็ได้เสมอ แบบ P2P เต็มรูปแบบ ไม่มีใครสามารถหยุดคุณได้ เพียงแค่วางที่อยู่ของมันในช่องค้นหาด้านบน",
|
||||
"boards": "กระดานข่าว",
|
||||
"popular_threads": "กระทู้ยอดนิยม",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "ข้าม {{repliesCount}} คำตอบ <1>คลิกที่นี่</1> เพื่อดู.",
|
||||
"replies_and_links_omitted": "ข้าม {{repliesCount}} คำตอบและ {{linksCount}} ลิงก์ <1>คลิกที่นี่</1> เพื่อดู.",
|
||||
"this_thread_is_closed": "กระทู้นี้ถูกปิด",
|
||||
"reply_to_cid": "ตอบกลับ {{cid}}",
|
||||
"reply_to_no": "ตอบกลับกระทู้ No.{{no}}",
|
||||
"submit": "ส่ง",
|
||||
"gif": "จีไอเอฟ",
|
||||
"moderating": "กำลังดูแล",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "บัญชี",
|
||||
"crypto_address": "ที่อยู่คริปโต",
|
||||
"crypto_wallets": "กระเป๋าเงินดิจิทัล",
|
||||
"plebbit_options": "ตัวเลือก plebbit",
|
||||
"reset": "รีเซ็ต",
|
||||
"import": "นำเข้า",
|
||||
"export": "ส่งออก",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "จัดเก็บในเครื่อง ({{location}}) ไม่ได้ซิงค์ข้ามอุปกรณ์",
|
||||
"choose_one": "เลือกหนึ่ง:",
|
||||
"create": "สร้าง",
|
||||
"plebbit_account": "บัญชี Plebbit",
|
||||
"save_changes": "บันทึกการเปลี่ยนแปลง",
|
||||
"reset_changes": "รีเซ็ตการเปลี่ยนแปลง",
|
||||
"crypto_address_yours": "ที่อยู่คริปโตเป็นของบัญชีนี้",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "อัปเดต",
|
||||
"downloading_board": "กำลังดาวน์โหลดกระดาน",
|
||||
"subscriptions_subtitle": "คุณได้สมัครสมาชิก {{count}} board แล้ว",
|
||||
"subscriptions_subtitle_one": "คุณได้สมัครสมาชิก {{count}} board แล้ว"
|
||||
"subscriptions_subtitle_one": "คุณได้สมัครสมาชิก {{count}} board แล้ว",
|
||||
"all_boards": "บอร์ดทั้งหมด",
|
||||
"nsfw_boards_only": "บอร์ด NSFW เท่านั้น",
|
||||
"worksafe_boards_only": "บอร์ดที่ปลอดภัยสำหรับงานเท่านั้น",
|
||||
"bitsocial_account": "บัญชี Bitsocial",
|
||||
"p2p_options": "ตัวเลือก P2P"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "5chan nedir?",
|
||||
"go": "git",
|
||||
"or": "veya",
|
||||
"5chan_description": "5chan, herkesin sınırsız panolar oluşturabileceği ve kriptografik olarak sahip olabileceği sunucusuz, yöneticisiz, merkezi olmayan ve açık kaynaklı bir 4chan alternatifidir. Tüm panolar, kullanıcıların bağlandığı ve paylaştığı düğümleri çalıştıran sahipleri tarafından barındırılır ve yönetilir, peer-to-peer. Tüm veriler yalnızca metindir, görüntüleri gömme bağlantıları dahil. İlginizi çeken aşağıdaki bir panoya tıklamaktan çekinmeyin ve hemen başlayın!",
|
||||
"5chan_description": "5chan, herkesin sınırsız pano oluşturabileceği ve kriptografik olarak sahip olabileceği tamamen merkezi olmayan ve açık kaynaklı bir 4chan alternatifidir. Pano sahipleri panolarını barındırır ve yönetir ve <1>Kurallarını</1> belirler. Tüm veriler yalnızca metindir ve gömülü görüntülere bağlantılar dahil olmak üzere peer-to-peer paylaşılır. Daha fazla bilgi edinmek için <2>SSS</2> bölümüne bakın. İlginizi çeken aşağıdaki bir panoya tıklamaktan çekinmeyin ve hemen başlayın!",
|
||||
"no_global_rules_info": "Her zaman herhangi bir foruma bağlanabilirsiniz, tamamen P2P, kimse sizi durduramaz. Adresini yukarıdaki arama alanına yapıştırmanız yeterli.",
|
||||
"boards": "forumlar",
|
||||
"popular_threads": "Popüler konular",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} yanıt göz ardı edildi. <1>Buraya tıklayın</1> görüntülemek için.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} yanıt ve {{linksCount}} bağlantı göz ardı edildi. <1>Buraya tıklayın</1> görüntülemek için.",
|
||||
"this_thread_is_closed": "Bu konu kapatılmıştır",
|
||||
"reply_to_cid": "{{cid}}’ye yanıt ver",
|
||||
"reply_to_no": "No.{{no}} gönderisine yanıt ver",
|
||||
"submit": "Gönder",
|
||||
"gif": "GIF",
|
||||
"moderating": "moderatörlük yapıyorsun",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "hesap",
|
||||
"crypto_address": "kripto adres",
|
||||
"crypto_wallets": "Kripto Cüzdanlar",
|
||||
"plebbit_options": "plebbit seçenekleri",
|
||||
"reset": "sıfırla",
|
||||
"import": "ithal et",
|
||||
"export": "dışa aktar",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "Yerel olarak saklanıyor ({{location}}), cihazlar arasında senkronize edilmedi",
|
||||
"choose_one": "Birini seç:",
|
||||
"create": "Oluştur",
|
||||
"plebbit_account": "Plebbit Hesabı",
|
||||
"save_changes": "Değişiklikleri Kaydet",
|
||||
"reset_changes": "Değişiklikleri Sıfırla",
|
||||
"crypto_address_yours": "Kripto adres bu hesaba aittir.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "Güncellemeler",
|
||||
"downloading_board": "forum indiriliyor",
|
||||
"subscriptions_subtitle": "{{count}} board'a abone oldunuz",
|
||||
"subscriptions_subtitle_one": "{{count}} board'a abone oldunuz"
|
||||
"subscriptions_subtitle_one": "{{count}} board'a abone oldunuz",
|
||||
"all_boards": "Tüm panolar",
|
||||
"nsfw_boards_only": "Sadece NSFW panoları",
|
||||
"worksafe_boards_only": "Sadece iş için güvenli panolar",
|
||||
"bitsocial_account": "Bitsocial Hesabı",
|
||||
"p2p_options": "P2P Seçenekleri"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "Що таке 5chan?",
|
||||
"go": "йди",
|
||||
"or": "або",
|
||||
"5chan_description": "5chan — це безсерверна, без адміністратора, децентралізована та відкрита альтернатива 4chan, де будь-хто може створювати та криптографічно володіти необмеженою кількістю дошок. Усі дошки розміщуються та модеруються їх власниками, які запускають вузли, до яких користувачі підключаються та роздають, peer-to-peer. Усі дані лише текстові, включаючи посилання для вбудовування зображень. Не соромтеся натиснути на дошку нижче, яка вас цікавить, і одразу почніть!",
|
||||
"5chan_description": "5chan — це повністю децентралізована альтернатива 4chan з відкритим вихідним кодом, де будь-хто може створювати та криптографічно володіти необмеженою кількістю дошок. Власники дошок розміщують та модерують свої дошки та встановлюють їхні <1>Правила</1>. Всі дані — це лише текст, який поширюється одноранговим способом, включаючи посилання на вбудовані зображення. Ознайомтеся з <2>Часто задаваними питаннями</2>, щоб дізнатися більше. Не соромтеся натиснути на дошку нижче, яка вас зацікавить, і одразу почніть!",
|
||||
"no_global_rules_info": "Ви завжди можете підключитися до будь-якого форуму, повністю P2P, ніхто не може вас зупинити. Просто вставте його адресу в поле пошуку вище.",
|
||||
"boards": "форуми",
|
||||
"popular_threads": "Популярні теми",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} відповідей пропущено. <1>Натисніть тут</1>, щоб переглянути.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} відповідей та {{linksCount}} посилань пропущено. <1>Натисніть тут</1>, щоб переглянути.",
|
||||
"this_thread_is_closed": "Тему закрито",
|
||||
"reply_to_cid": "Відповісти на {{cid}}",
|
||||
"reply_to_no": "Відповісти на пост No.{{no}}",
|
||||
"submit": "Надіслати",
|
||||
"gif": "ГІФ",
|
||||
"moderating": "модеруєте",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "рахунок",
|
||||
"crypto_address": "адреса криптовалюти",
|
||||
"crypto_wallets": "Криптокошельки",
|
||||
"plebbit_options": "плебіт варіанти",
|
||||
"reset": "скинути",
|
||||
"import": "імпортувати",
|
||||
"export": "експортувати",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "Збережено локально ({{location}}), не синхронізується між пристроями",
|
||||
"choose_one": "Виберіть один:",
|
||||
"create": "Створити",
|
||||
"plebbit_account": "Обліковий запис Plebbit",
|
||||
"save_changes": "Зберегти зміни",
|
||||
"reset_changes": "Скинути зміни",
|
||||
"crypto_address_yours": "Адреса криптовалюти належить цьому обліковому запису.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "Оновлення",
|
||||
"downloading_board": "завантаження форуму",
|
||||
"subscriptions_subtitle": "Ви підписались на {{count}} board",
|
||||
"subscriptions_subtitle_one": "Ви підписались на {{count}} board"
|
||||
"subscriptions_subtitle_one": "Ви підписались на {{count}} board",
|
||||
"all_boards": "Всі дошки",
|
||||
"nsfw_boards_only": "Тільки дошки NSFW",
|
||||
"worksafe_boards_only": "Тільки безпечні для роботи дошки",
|
||||
"bitsocial_account": "Обліковий запис Bitsocial",
|
||||
"p2p_options": "Опції P2P"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "5chan کیا ہے؟",
|
||||
"go": "جا",
|
||||
"or": "یا",
|
||||
"5chan_description": "5chan ایک سرور-فری، ایڈمن-فری، غیر مرکزی اور اوپن سورس 4chan متبادل ہے، جہاں کوئی بھی لامحدود بورڈز بنا سکتا ہے اور کریپٹوگرافک طور پر ان کا مالک ہو سکتا ہے۔ تمام بورڈز ان کے مالکان کی طرف سے ہوسٹ اور ماڈریٹ کیے جاتے ہیں، جو نوڈز چلاتے ہیں جن سے صارفین جڑتے ہیں اور سیڈ کرتے ہیں، peer-to-peer۔ تمام ڈیٹا صرف متن ہے، بشمول تصاویر کو ایمبیڈ کرنے کے لنکس۔ نیچے دیے گئے بورڈ پر کلک کرنے میں آزاد محسوس کریں جو آپ کی دلچسپی رکھتا ہے اور فوراً شروع کریں!",
|
||||
"5chan_description": "5chan ایک مکمل طور پر غیر مرکزی اور اوپن سورس 4chan متبادل ہے، جہاں کوئی بھی لامحدود بورڈز بنا سکتا ہے اور کریپٹوگرافک طور پر ان کا مالک ہو سکتا ہے۔ بورڈ مالکان اپنے بورڈ کی میزبانی اور نظم و نسق کرتے ہیں اور اس کے <1>قواعد</1> طے کرتے ہیں۔ تمام ڈیٹا صرف متن ہے اور یہ peer-to-peer شیئر کیا جاتا ہے، بشمول ایمبیڈ تصاویر کے لنکس۔ مزید جاننے کے لیے <2>FAQ</2> دیکھیں۔ نیچے دیے گئے بورڈ پر کلک کرنے میں آزاد محسوس کریں جو آپ کی دلچسپی رکھتا ہے اور فوراً شروع کریں!",
|
||||
"no_global_rules_info": "آپ ہمیشہ کسی بھی بورڈ سے کنیکٹ ہو سکتے ہیں، مکمل طور پر P2P، کوئی آپ کو روک نہیں سکتا۔ بس اس کا پتہ اوپر والے سرچ فیلڈ میں پیسٹ کریں۔",
|
||||
"boards": "پیغام بورڈز",
|
||||
"popular_threads": "مقبول تھریڈز",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} جوابات چھوڑ دیے گئے ہیں۔ <1>مکمل دیکھنے کے لیے یہاں کلک کریں</1>۔",
|
||||
"replies_and_links_omitted": "{{repliesCount}} جوابات اور {{linksCount}} لنکس چھوڑ دیے گئے ہیں۔ <1>مکمل دیکھنے کے لیے یہاں کلک کریں</1>۔",
|
||||
"this_thread_is_closed": "یہ دھاگہ بند ہے",
|
||||
"reply_to_cid": "{{cid}} کا جواب دیں",
|
||||
"reply_to_no": "پوسٹ No.{{no}} کا جواب دیں",
|
||||
"submit": "جمع کرائیں",
|
||||
"gif": "جی آئی ایف",
|
||||
"moderating": "منتظم کر رہے ہیں",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "اکاؤنٹ",
|
||||
"crypto_address": "کرپٹو پتہ",
|
||||
"crypto_wallets": "کرپٹو والٹس",
|
||||
"plebbit_options": "پلیبٹ اختیارات",
|
||||
"reset": "ری سیٹ کریں",
|
||||
"import": "درآمد کریں",
|
||||
"export": "برآمد کریں",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "مقامی طور پر محفوظ ({{location}})، آلات کے درمیان ہم وقت سازی نہیں کی گئی",
|
||||
"choose_one": "ایک کا انتخاب کریں:",
|
||||
"create": "تخلیق کریں",
|
||||
"plebbit_account": "Plebbit اکاؤنٹ",
|
||||
"save_changes": "تبدیلیاں محفوظ کریں",
|
||||
"reset_changes": "تبدیلیاں ری سیٹ کریں",
|
||||
"crypto_address_yours": "کرپٹو پتا اس اکاؤنٹ کا حصہ ہے۔",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "اپڈیٹس",
|
||||
"downloading_board": "فورم ڈاؤن لوڈ ہو رہا ہے",
|
||||
"subscriptions_subtitle": "آپ نے {{count}} board کی سبسکرپشن لی ہے",
|
||||
"subscriptions_subtitle_one": "آپ نے {{count}} board کی سبسکرپشن لی ہے"
|
||||
"subscriptions_subtitle_one": "آپ نے {{count}} board کی سبسکرپشن لی ہے",
|
||||
"all_boards": "تمام بورڈز",
|
||||
"nsfw_boards_only": "صرف NSFW بورڈز",
|
||||
"worksafe_boards_only": "صرف کام کے لیے محفوظ بورڈز",
|
||||
"bitsocial_account": "Bitsocial اکاؤنٹ",
|
||||
"p2p_options": "P2P اختیارات"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "5chan là gì?",
|
||||
"go": "đi",
|
||||
"or": "hoặc",
|
||||
"5chan_description": "5chan là một thay thế không máy chủ, không quản trị viên, phi tập trung và mã nguồn mở cho 4chan, nơi bất kỳ ai cũng có thể tạo và sở hữu mã hóa các bảng không giới hạn. Tất cả các bảng được lưu trữ và điều hành bởi chủ sở hữu của chúng, những người chạy các nút mà người dùng kết nối và chia sẻ, peer-to-peer. Tất cả dữ liệu chỉ là văn bản, bao gồm các liên kết để nhúng hình ảnh. Hãy thoải mái nhấp vào một bảng bên dưới mà bạn quan tâm và bắt đầu ngay!",
|
||||
"5chan_description": "5chan là một giải pháp thay thế hoàn toàn phi tập trung và mã nguồn mở cho 4chan, nơi bất kỳ ai cũng có thể tạo và sở hữu mã hóa các bảng không giới hạn. Chủ sở hữu bảng lưu trữ và kiểm duyệt bảng của họ và đặt <1>Quy tắc</1> của nó. Tất cả dữ liệu chỉ là văn bản và được chia sẻ peer-to-peer, bao gồm các liên kết đến hình ảnh nhúng. Xem <2>FAQ</2> để tìm hiểu thêm. Hãy thoải mái nhấp vào một bảng bên dưới mà bạn quan tâm và bắt đầu ngay!",
|
||||
"no_global_rules_info": "Bạn luôn có thể kết nối với bất kỳ diễn đàn nào, hoàn toàn P2P, không ai có thể ngăn cản bạn. Chỉ cần dán địa chỉ của nó vào trường tìm kiếm ở trên.",
|
||||
"boards": "diễn đàn",
|
||||
"popular_threads": "Chủ đề phổ biến",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "{{repliesCount}} câu trả lời bị bỏ qua. <1>Nhấn vào đây</1> để xem.",
|
||||
"replies_and_links_omitted": "{{repliesCount}} câu trả lời và {{linksCount}} liên kết bị bỏ qua. <1>Nhấn vào đây</1> để xem.",
|
||||
"this_thread_is_closed": "Chủ đề này đã đóng",
|
||||
"reply_to_cid": "Trả lời {{cid}}",
|
||||
"reply_to_no": "Trả lời bài No.{{no}}",
|
||||
"submit": "Gửi",
|
||||
"gif": "GIF",
|
||||
"moderating": "đang điều hành",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "tài khoản",
|
||||
"crypto_address": "địa chỉ tiền điện tử",
|
||||
"crypto_wallets": "Ví tiền điện tử",
|
||||
"plebbit_options": "tùy chọn plebbit",
|
||||
"reset": "đặt lại",
|
||||
"import": "nhập",
|
||||
"export": "xuất",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "Lưu trữ cục bộ ({{location}}), không được đồng bộ qua các thiết bị",
|
||||
"choose_one": "Chọn một:",
|
||||
"create": "Tạo",
|
||||
"plebbit_account": "Tài khoản Plebbit",
|
||||
"save_changes": "Lưu thay đổi",
|
||||
"reset_changes": "Đặt lại thay đổi",
|
||||
"crypto_address_yours": "Địa chỉ tiền điện tử thuộc về tài khoản này.",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "Cập nhật",
|
||||
"downloading_board": "đang tải diễn đàn",
|
||||
"subscriptions_subtitle": "Bạn đã đăng ký {{count}} board",
|
||||
"subscriptions_subtitle_one": "Bạn đã đăng ký {{count}} board"
|
||||
"subscriptions_subtitle_one": "Bạn đã đăng ký {{count}} board",
|
||||
"all_boards": "Tất cả bảng",
|
||||
"nsfw_boards_only": "Chỉ bảng NSFW",
|
||||
"worksafe_boards_only": "Chỉ bảng an toàn cho công việc",
|
||||
"bitsocial_account": "Tài khoản Bitsocial",
|
||||
"p2p_options": "Tùy chọn P2P"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"what_is_5chan": "5chan是什么?",
|
||||
"go": "去",
|
||||
"or": "或者",
|
||||
"5chan_description": "5chan是一个无服务器、无管理员、去中心化和开源的4chan替代品,任何人都可以创建并加密拥有无限数量的板块。所有板块都由其所有者托管和审核,他们运行节点,用户连接到这些节点并进行种子分享,点对点。所有数据都只是文本,包括用于嵌入图像的链接。请随时点击下面您感兴趣的板块并立即开始!",
|
||||
"5chan_description": "5chan是一个完全去中心化和开源的4chan替代品,任何人都可以创建并加密拥有无限板块。板块所有者托管和审核他们的板块,并设置其<1>规则</1>。所有数据都是纯文本,并通过点对点共享,包括嵌入图像的链接。查看<2>常见问题</2>以了解更多信息。请随时点击下面您感兴趣的板块并立即开始!",
|
||||
"no_global_rules_info": "您始终可以连接到任何论坛,完全P2P,没有人能阻止您。只需将其地址粘贴到上面的搜索字段中即可。",
|
||||
"boards": "论坛",
|
||||
"popular_threads": "热门主题",
|
||||
@@ -60,7 +60,7 @@
|
||||
"replies_omitted": "省略了{{repliesCount}}条回复。 <1>点击这里</1>查看。",
|
||||
"replies_and_links_omitted": "省略了{{repliesCount}}条回复和{{linksCount}}个链接。 <1>点击这里</1>查看。",
|
||||
"this_thread_is_closed": "该主题已关闭",
|
||||
"reply_to_cid": "回复{{cid}}",
|
||||
"reply_to_no": "回复帖子 No.{{no}}",
|
||||
"submit": "提交",
|
||||
"gif": "GIF",
|
||||
"moderating": "正在管理",
|
||||
@@ -107,7 +107,6 @@
|
||||
"account": "帐户",
|
||||
"crypto_address": "加密地址",
|
||||
"crypto_wallets": "加密货币钱包",
|
||||
"plebbit_options": "plebbit选项",
|
||||
"reset": "重置",
|
||||
"import": "导入",
|
||||
"export": "导出",
|
||||
@@ -239,7 +238,6 @@
|
||||
"stored_locally": "本地存储 ({{location}}),未在设备之间同步",
|
||||
"choose_one": "选择一个:",
|
||||
"create": "创建",
|
||||
"plebbit_account": "Plebbit 账户",
|
||||
"save_changes": "保存更改",
|
||||
"reset_changes": "重置更改",
|
||||
"crypto_address_yours": "加密地址属于此帐户。",
|
||||
@@ -301,5 +299,10 @@
|
||||
"updates": "更新",
|
||||
"downloading_board": "正在下载论坛",
|
||||
"subscriptions_subtitle": "您已订阅 {{count}} 个 board",
|
||||
"subscriptions_subtitle_one": "您已订阅 {{count}} 个 board"
|
||||
"subscriptions_subtitle_one": "您已订阅 {{count}} 个 board",
|
||||
"all_boards": "所有板块",
|
||||
"nsfw_boards_only": "仅NSFW板块",
|
||||
"worksafe_boards_only": "仅工作安全板块",
|
||||
"bitsocial_account": "Bitsocial账户",
|
||||
"p2p_options": "P2P选项"
|
||||
}
|
||||
|
||||
@@ -18,6 +18,15 @@ const notFoundImages = readdirSync(notFoundDir)
|
||||
.filter((file) => /^not-found-.*\.(jpg|jpeg|gif|png)$/i.test(file))
|
||||
.map((file) => `assets/not-found/${file}`);
|
||||
|
||||
// Scan for button images (theme-specific buttons like cross, plus, minus, help, button-fade)
|
||||
const buttonsDir = join(publicDir, 'assets', 'buttons');
|
||||
const buttonImages = readdirSync(buttonsDir)
|
||||
.filter((file) => /\.(jpg|jpeg|gif|png)$/i.test(file))
|
||||
.map((file) => `assets/buttons/${file}`);
|
||||
|
||||
// Theme background images
|
||||
const themeBackgrounds = ['assets/background-fade.png', 'assets/background-fade-blue.png'];
|
||||
|
||||
// Generate TypeScript file with proper formatting (matches prettier config)
|
||||
const formatArray = (arr) => {
|
||||
if (arr.length === 0) return '[]';
|
||||
@@ -32,6 +41,12 @@ const output = `// Auto-generated file - do not edit manually
|
||||
export const BANNERS = ${formatArray(banners)} as const;
|
||||
|
||||
export const NOT_FOUND_IMAGES = ${formatArray(notFoundImages)} as const;
|
||||
|
||||
// Theme button images (cross, plus, minus, help, button-fade variants)
|
||||
export const THEME_BUTTON_IMAGES = ${formatArray(buttonImages)} as const;
|
||||
|
||||
// Theme background pattern images
|
||||
export const THEME_BACKGROUND_IMAGES = ${formatArray(themeBackgrounds)} as const;
|
||||
`;
|
||||
|
||||
// Ensure directory exists
|
||||
@@ -41,4 +56,6 @@ mkdirSync(generatedDir, { recursive: true });
|
||||
const outputPath = join(generatedDir, 'asset-manifest.ts');
|
||||
writeFileSync(outputPath, output, 'utf8');
|
||||
|
||||
console.log(`✅ Generated asset manifest with ${banners.length} banners and ${notFoundImages.length} not-found images`);
|
||||
console.log(
|
||||
`✅ Generated asset manifest with ${banners.length} banners, ${notFoundImages.length} not-found images, ${buttonImages.length} button images, and ${themeBackgrounds.length} background images`,
|
||||
);
|
||||
|
||||
+11
-2
@@ -3,6 +3,7 @@ import { Outlet, Route, Routes, useLocation, useParams } from 'react-router-dom'
|
||||
import { useAccountComment } from '@plebbit/plebbit-react-hooks';
|
||||
import { initSnow, removeSnow } from './lib/snow';
|
||||
import { isAllView, isModView, isSubscriptionsView } from './lib/utils/view-utils';
|
||||
import { preloadThemeAssets } from './lib/utils/preload-utils';
|
||||
import useReplyModalStore from './stores/use-reply-modal-store';
|
||||
import useCreateBoardModalStore from './stores/use-create-board-modal-store';
|
||||
import useSpecialThemeStore from './stores/use-special-theme-store';
|
||||
@@ -13,6 +14,7 @@ import { getSubplebbitAddress, isPostRoute, isPendingPostRoute } from './lib/uti
|
||||
import styles from './app.module.css';
|
||||
import FAQ from './views/faq';
|
||||
import Home from './views/home';
|
||||
import Rules from './views/rules';
|
||||
import NotFound from './views/not-found';
|
||||
import PendingPost from './views/pending-post';
|
||||
import Post from './views/post';
|
||||
@@ -30,6 +32,10 @@ import DirectoryModal from './components/directory-modal';
|
||||
import DisclaimerModal from './components/disclaimer-modal';
|
||||
import SettingsModal from './components/settings-modal';
|
||||
|
||||
// Preload all theme assets (buttons, backgrounds) immediately on app load
|
||||
// to prevent visible loading delays when switching themes
|
||||
preloadThemeAssets();
|
||||
|
||||
const BoardLayout = () => {
|
||||
const { accountCommentIndex, boardIdentifier } = useParams();
|
||||
const location = useLocation();
|
||||
@@ -77,8 +83,8 @@ const BoardLayout = () => {
|
||||
{isMobile
|
||||
? (subplebbitAddress || isInAllView || isInModView || isInSubscriptionsView || pendingPost?.subplebbitAddress) && (
|
||||
<>
|
||||
<MobileBoardButtons />
|
||||
<PostForm key={key} />
|
||||
<MobileBoardButtons />
|
||||
</>
|
||||
)
|
||||
: (subplebbitAddress || isInAllView || isInModView || isInSubscriptionsView || pendingPost?.subplebbitAddress) && (
|
||||
@@ -113,7 +119,7 @@ const GlobalLayout = () => {
|
||||
}
|
||||
}, [theme]);
|
||||
|
||||
const { activeCid, threadCid, subplebbitAddress, closeModal, showReplyModal, scrollY } = useReplyModalStore();
|
||||
const { activeCid, parentNumber, threadNumber, threadCid, subplebbitAddress, closeModal, showReplyModal, scrollY } = useReplyModalStore();
|
||||
|
||||
const location = useLocation();
|
||||
const isInSettingsView = location.pathname.endsWith('/settings');
|
||||
@@ -125,6 +131,8 @@ const GlobalLayout = () => {
|
||||
<ReplyModal
|
||||
closeModal={closeModal}
|
||||
parentCid={activeCid}
|
||||
parentNumber={parentNumber}
|
||||
threadNumber={threadNumber}
|
||||
postCid={threadCid}
|
||||
scrollY={scrollY}
|
||||
showReplyModal={showReplyModal}
|
||||
@@ -143,6 +151,7 @@ const App = () => (
|
||||
<Route element={<GlobalLayout />}>
|
||||
<Route path='/' element={<Home />} />
|
||||
<Route path='/faq' element={<FAQ />} />
|
||||
<Route path='/rules' element={<Rules />} />
|
||||
<Route element={<BoardLayout />}>
|
||||
<Route path='/all/:timeFilterName?' element={null} />
|
||||
<Route path='/all/:timeFilterName?/settings' element={null} />
|
||||
|
||||
@@ -10,6 +10,7 @@ import useCatalogFiltersStore from '../../stores/use-catalog-filters-store';
|
||||
import useCatalogStyleStore from '../../stores/use-catalog-style-store';
|
||||
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
||||
import useSortingStore from '../../stores/use-sorting-store';
|
||||
import useAllFeedFilterStore from '../../stores/use-all-feed-filter-store';
|
||||
import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
|
||||
import useIsMobile from '../../hooks/use-is-mobile';
|
||||
import useTimeFilter from '../../hooks/use-time-filter';
|
||||
@@ -247,14 +248,14 @@ export const TimeFilter = ({ isInAllView, isInCatalogView, isInSubscriptionsView
|
||||
? `/all/catalog/${timeFilterName}`
|
||||
: `/all/${timeFilterName}`
|
||||
: isInSubscriptionsView
|
||||
? isInCatalogView
|
||||
? `/subs/catalog/${timeFilterName}`
|
||||
: `/subs/${timeFilterName}`
|
||||
: isInModView
|
||||
? isInCatalogView
|
||||
? `/mod/catalog/${timeFilterName}`
|
||||
: `/mod/${timeFilterName}`
|
||||
: null;
|
||||
? isInCatalogView
|
||||
? `/subs/catalog/${timeFilterName}`
|
||||
: `/subs/${timeFilterName}`
|
||||
: isInModView
|
||||
? isInCatalogView
|
||||
? `/mod/catalog/${timeFilterName}`
|
||||
: `/mod/${timeFilterName}`
|
||||
: null;
|
||||
link && navigate(link);
|
||||
};
|
||||
|
||||
@@ -282,6 +283,22 @@ export const TimeFilter = ({ isInAllView, isInCatalogView, isInSubscriptionsView
|
||||
);
|
||||
};
|
||||
|
||||
const AllFeedFilter = () => {
|
||||
const { t } = useTranslation();
|
||||
const { filter, setFilter } = useAllFeedFilterStore();
|
||||
|
||||
return (
|
||||
<>
|
||||
<span>{t('show')}</span>:
|
||||
<select className='capitalize' value={filter} onChange={(e) => setFilter(e.target.value as 'all' | 'nsfw' | 'sfw')}>
|
||||
<option value='all'>{t('all_boards')}</option>
|
||||
<option value='nsfw'>{t('nsfw_boards_only')}</option>
|
||||
<option value='sfw'>{t('worksafe_boards_only')}</option>
|
||||
</select>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const MobileBoardButtons = () => {
|
||||
const { t } = useTranslation();
|
||||
const params = useParams();
|
||||
@@ -341,6 +358,14 @@ export const MobileBoardButtons = () => {
|
||||
</span>
|
||||
)
|
||||
)}
|
||||
{isInAllView && (
|
||||
<>
|
||||
<hr />
|
||||
<div className={styles.options}>
|
||||
<AllFeedFilter />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{isInCatalogView && (
|
||||
<>
|
||||
<hr />
|
||||
@@ -409,11 +434,9 @@ export const DesktopBoardButtons = () => {
|
||||
{isInPostView || isInPendingPostPage ? (
|
||||
<>
|
||||
[
|
||||
<ReturnButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />
|
||||
] [
|
||||
<ReturnButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />] [
|
||||
<CatalogButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />] [
|
||||
<UpdateButton />
|
||||
] [
|
||||
<UpdateButton />] [
|
||||
<AutoButton />]
|
||||
<span className={styles.rightSideButtons}>
|
||||
<PostPageStats />
|
||||
@@ -462,6 +485,7 @@ export const DesktopBoardButtons = () => {
|
||||
<ShowOPCommentOption />
|
||||
</>
|
||||
)}
|
||||
{isInAllView && <AllFeedFilter />}
|
||||
{(isInAllView || isInSubscriptionsView || isInModView) && (
|
||||
<TimeFilter isInAllView={isInAllView} isInCatalogView={isInCatalogView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />
|
||||
)}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { createPortal } from 'react-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link, useLocation, useParams } from 'react-router-dom';
|
||||
import { useFloating, offset, size, autoUpdate, Placement } from '@floating-ui/react';
|
||||
import { Comment } from '@plebbit/plebbit-react-hooks';
|
||||
import { Comment, useReplies } from '@plebbit/plebbit-react-hooks';
|
||||
import Plebbit from '@plebbit/plebbit-js';
|
||||
import { shouldShowSnow } from '../../lib/snow';
|
||||
import { getHasThumbnail } from '../../lib/utils/media-utils';
|
||||
@@ -19,7 +19,6 @@ import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
|
||||
import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
|
||||
import useHide from '../../hooks/use-hide';
|
||||
import useWindowWidth from '../../hooks/use-window-width';
|
||||
import useReplies from '../../hooks/use-replies';
|
||||
import { ContentPreview } from '../../views/home/popular-threads-box';
|
||||
import PostMenuDesktop from '../post-desktop/post-menu-desktop';
|
||||
import styles from './catalog-row.module.css';
|
||||
@@ -181,8 +180,8 @@ const CatalogPost = ({ post }: { post: Comment }) => {
|
||||
update();
|
||||
}, [update, windowWidth]);
|
||||
|
||||
const replies = useReplies(post);
|
||||
const lastReply = replies.length > 0 ? replies[replies.length - 1] : null;
|
||||
const { replies } = useReplies({ comment: post });
|
||||
const lastReply = replies?.length > 0 ? replies[replies.length - 1] : null;
|
||||
|
||||
const { isCommentAuthorMod: isCatalogPostAuthorMod, commentAuthorRole: catalogPostAuthorRole } = useEditCommentPrivileges({
|
||||
commentAuthorAddress: author?.address,
|
||||
|
||||
@@ -11,6 +11,7 @@ import Embed, { canEmbed } from '../embed';
|
||||
interface MediaProps {
|
||||
commentMediaInfo?: CommentMediaInfo;
|
||||
deleted?: boolean;
|
||||
disableToggle?: boolean;
|
||||
displayHeight?: string;
|
||||
displayWidth?: string;
|
||||
isFloatingEmbed?: boolean;
|
||||
@@ -83,7 +84,7 @@ const Thumbnail = ({ commentMediaInfo, deleted, displayHeight, displayWidth, isF
|
||||
);
|
||||
};
|
||||
|
||||
const Media = ({ commentMediaInfo, isReply, setShowThumbnail }: MediaProps) => {
|
||||
const Media = ({ commentMediaInfo, disableToggle, isReply, setShowThumbnail }: MediaProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { thumbnail, type, url } = commentMediaInfo || {};
|
||||
const isMobile = useIsMobile();
|
||||
@@ -98,11 +99,11 @@ const Media = ({ commentMediaInfo, isReply, setShowThumbnail }: MediaProps) => {
|
||||
{type === 'iframe' && url ? (
|
||||
<Embed url={url} />
|
||||
) : type === 'gif' ? (
|
||||
<img src={url} alt='' onClick={() => setShowThumbnail(true)} />
|
||||
<img src={url} alt='' onClick={disableToggle ? undefined : () => setShowThumbnail(true)} />
|
||||
) : type === 'video' ? (
|
||||
<video src={url} controls autoPlay loop muted />
|
||||
) : type === 'webpage' ? (
|
||||
<img src={thumbnail} alt='' onClick={() => setShowThumbnail(true)} />
|
||||
<img src={thumbnail} alt='' onClick={disableToggle ? undefined : () => setShowThumbnail(true)} />
|
||||
) : null}
|
||||
{isMobile && type && (
|
||||
<div className={styles.fileInfo}>
|
||||
@@ -126,19 +127,21 @@ const Media = ({ commentMediaInfo, isReply, setShowThumbnail }: MediaProps) => {
|
||||
|
||||
interface ImageProps {
|
||||
commentMediaInfo: CommentMediaInfo;
|
||||
disableToggle?: boolean;
|
||||
displayHeight: string;
|
||||
displayWidth: string;
|
||||
initialExpanded?: boolean;
|
||||
isOutOfFeed: boolean;
|
||||
parentCid?: string;
|
||||
spoiler?: boolean;
|
||||
}
|
||||
|
||||
const Image = ({ commentMediaInfo, displayHeight, displayWidth, isOutOfFeed, parentCid, spoiler }: ImageProps) => {
|
||||
const Image = ({ commentMediaInfo, disableToggle = false, displayHeight, displayWidth, initialExpanded = false, isOutOfFeed, parentCid, spoiler }: ImageProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { type, url } = commentMediaInfo || {};
|
||||
const isReply = parentCid;
|
||||
const isMobile = useIsMobile();
|
||||
const [isImageExpanded, setIsImageExpanded] = useState(false);
|
||||
const [isImageExpanded, setIsImageExpanded] = useState(initialExpanded);
|
||||
const { fitExpandedImagesToScreen } = useExpandedMediaStore();
|
||||
const mediaDimensions = getMediaDimensions(commentMediaInfo);
|
||||
const mediaClass = `${isMobile ? styles.mediaMobile : isReply ? styles.mediaDesktopReply : styles.mediaDesktopOp} ${
|
||||
@@ -171,7 +174,7 @@ const Image = ({ commentMediaInfo, displayHeight, displayWidth, isOutOfFeed, par
|
||||
{hasError ? (
|
||||
<img src='assets/filedeleted-res.gif' alt='File deleted' />
|
||||
) : (
|
||||
<img src={url} onError={handleError} alt='' onClick={() => setIsImageExpanded(!isImageExpanded)} />
|
||||
<img src={url} onError={handleError} alt='' onClick={disableToggle ? undefined : () => setIsImageExpanded(!isImageExpanded)} />
|
||||
)}
|
||||
</span>
|
||||
{isImageExpanded && type && (
|
||||
@@ -193,7 +196,7 @@ const Image = ({ commentMediaInfo, displayHeight, displayWidth, isOutOfFeed, par
|
||||
{hasError ? (
|
||||
<img src='assets/filedeleted-res.gif' alt='File deleted' />
|
||||
) : (
|
||||
<img src={url} onError={handleError} alt='' onClick={() => setIsImageExpanded(!isImageExpanded)} />
|
||||
<img src={url} onError={handleError} alt='' onClick={disableToggle ? undefined : () => setIsImageExpanded(!isImageExpanded)} />
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
@@ -202,6 +205,7 @@ const Image = ({ commentMediaInfo, displayHeight, displayWidth, isOutOfFeed, par
|
||||
const CommentMedia = ({
|
||||
commentMediaInfo,
|
||||
deleted,
|
||||
disableToggle,
|
||||
isFloatingEmbed,
|
||||
linkHeight,
|
||||
linkWidth,
|
||||
@@ -253,10 +257,13 @@ const CommentMedia = ({
|
||||
<span className={styles.content}>
|
||||
{commentMediaInfo?.type === 'image' ? (
|
||||
// images just enlarge when clicked, so they don't need two separate components
|
||||
// when showThumbnail is explicitly false (e.g., from embed button), start expanded
|
||||
<Image
|
||||
commentMediaInfo={commentMediaInfo}
|
||||
disableToggle={disableToggle}
|
||||
displayHeight={displayHeight}
|
||||
displayWidth={displayWidth}
|
||||
initialExpanded={showThumbnail === false}
|
||||
isOutOfFeed={isOutOfFeed}
|
||||
parentCid={parentCid}
|
||||
spoiler={spoiler}
|
||||
@@ -279,7 +286,7 @@ const CommentMedia = ({
|
||||
)}
|
||||
{isMobile && type && <div className={styles.fileInfo}>{`${spoiler ? `${t('spoiler')} - ` : ''} ${getDisplayMediaInfoType(type, t)}`}</div>}
|
||||
</span>
|
||||
{!showThumbnail && <Media commentMediaInfo={commentMediaInfo} isReply={!!parentCid} setShowThumbnail={setShowThumbnail} />}
|
||||
{!showThumbnail && <Media commentMediaInfo={commentMediaInfo} disableToggle={disableToggle} isReply={!!parentCid} setShowThumbnail={setShowThumbnail} />}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { copyToClipboard } from '../../lib/utils/clipboard-utils';
|
||||
import styles from './error-display.module.css';
|
||||
@@ -6,8 +6,27 @@ import styles from './error-display.module.css';
|
||||
const ErrorDisplay = ({ error }: { error: any }) => {
|
||||
const { t } = useTranslation();
|
||||
const [feedbackMessageKey, setFeedbackMessageKey] = useState<string | null>(null);
|
||||
const [shouldShow, setShouldShow] = useState(false);
|
||||
|
||||
const originalDisplayMessage = error?.message ? `${t('error')}: ${error.message}` : null;
|
||||
useEffect(() => {
|
||||
const hasError = !!(error?.message || error?.stack || error?.details || error);
|
||||
if (!hasError) {
|
||||
setShouldShow(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
setShouldShow(true);
|
||||
}, 1000); // delay to avoid false positives, for example when accessing cached feeds that may appear offline for a second or so
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [error]);
|
||||
|
||||
if (!shouldShow) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const originalDisplayMessage = error?.message ? `${t('error')}: ${error.message}` : typeof error === 'string' ? error : null;
|
||||
|
||||
const handleMessageClick = async () => {
|
||||
if (!error || !error.message || feedbackMessageKey) return;
|
||||
@@ -45,19 +64,17 @@ const ErrorDisplay = ({ error }: { error: any }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
(error?.message || error?.stack || error?.details || error) && (
|
||||
<div className={styles.error}>
|
||||
{currentDisplayMessage && (
|
||||
<span
|
||||
className={classNames.join(' ')}
|
||||
onClick={isClickable ? handleMessageClick : undefined}
|
||||
title={isClickable ? t('clickToCopyFullError', 'Click to copy full error') : undefined}
|
||||
>
|
||||
{currentDisplayMessage}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
<div className={styles.error}>
|
||||
{currentDisplayMessage && (
|
||||
<span
|
||||
className={classNames.join(' ')}
|
||||
onClick={isClickable ? handleMessageClick : undefined}
|
||||
title={isClickable ? t('clickToCopyFullError', 'Click to copy full error') : undefined}
|
||||
>
|
||||
{currentDisplayMessage}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -90,14 +90,21 @@ const ContentLinkEmbed = ({ children, href, linkMediaInfo }: ContentLinkEmbedPro
|
||||
{showMedia && (
|
||||
<>
|
||||
<br />
|
||||
<CommentMedia isReply={false} setShowThumbnail={setShowMedia} commentMediaInfo={linkMediaInfo} showThumbnail={false} />
|
||||
<CommentMedia commentMediaInfo={linkMediaInfo} disableToggle={true} isReply={false} setShowThumbnail={setShowMedia} showThumbnail={false} />
|
||||
</>
|
||||
)}
|
||||
{getHasThumbnail(linkMediaInfo, href) && (
|
||||
<FloatingPortal>
|
||||
{isOpen && !isMobile && (
|
||||
<div className={styles.floatingEmbed} ref={refs.setFloating} style={floatingStyles} {...getFloatingProps()}>
|
||||
<CommentMedia isReply={false} isFloatingEmbed={true} setShowThumbnail={setShowMedia} commentMediaInfo={linkMediaInfo} showThumbnail={true} />
|
||||
<CommentMedia
|
||||
commentMediaInfo={linkMediaInfo}
|
||||
disableToggle={true}
|
||||
isFloatingEmbed={true}
|
||||
isReply={false}
|
||||
setShowThumbnail={setShowMedia}
|
||||
showThumbnail={false}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</FloatingPortal>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { Link, useLocation, useParams } from 'react-router-dom';
|
||||
import { Comment, useAuthorAvatar, useEditedComment } from '@plebbit/plebbit-react-hooks';
|
||||
import { Link, useLocation, useNavigationType, useParams } from 'react-router-dom';
|
||||
import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso';
|
||||
import { Comment, useAuthorAvatar, useEditedComment, useReplies } from '@plebbit/plebbit-react-hooks';
|
||||
import Plebbit from '@plebbit/plebbit-js';
|
||||
import styles from '../../views/post/post.module.css';
|
||||
import { CommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail, getMediaDimensions } from '../../lib/utils/media-utils';
|
||||
@@ -17,7 +18,6 @@ import { useCommentMediaInfo } from '../../hooks/use-comment-media-info';
|
||||
import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
|
||||
import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
|
||||
import useHide from '../../hooks/use-hide';
|
||||
import useReplies from '../../hooks/use-replies';
|
||||
import useStateString from '../../hooks/use-state-string';
|
||||
import CommentContent from '../comment-content';
|
||||
import CommentMedia from '../comment-media';
|
||||
@@ -34,6 +34,9 @@ import { shouldShowSnow } from '../../lib/snow';
|
||||
import useReplyModalStore from '../../stores/use-reply-modal-store';
|
||||
import { selectPostMenuProps } from '../../lib/utils/post-menu-props';
|
||||
|
||||
// Store scroll position for replies virtuoso across navigations
|
||||
const lastVirtuosoStates: { [key: string]: StateSnapshot } = {};
|
||||
|
||||
interface ShowOmittedRepliesState {
|
||||
showOmittedReplies: Record<string, boolean>;
|
||||
setShowOmittedReplies: (cid: string, showOmittedReplies: boolean) => void;
|
||||
@@ -50,11 +53,11 @@ const useShowOmittedReplies = create<ShowOmittedRepliesState>((set) => ({
|
||||
})),
|
||||
}));
|
||||
|
||||
const PostInfo = ({ post, postReplyCount = 0, roles, isHidden }: PostProps) => {
|
||||
const PostInfo = ({ post, postReplyCount = 0, roles, isHidden, threadNumber }: PostProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { author, cid, deleted, locked, pinned, parentCid, postCid, reason, removed, state, subplebbitAddress, timestamp } = post || {};
|
||||
const title = post?.title?.trim();
|
||||
const replies = useReplies(post);
|
||||
const { replies } = useReplies({ comment: post });
|
||||
const { address, shortAddress } = author || {};
|
||||
const displayName = author?.displayName?.trim();
|
||||
const authorRole = roles?.[address]?.role?.replace('moderator', 'mod');
|
||||
@@ -88,10 +91,10 @@ const PostInfo = ({ post, postReplyCount = 0, roles, isHidden }: PostProps) => {
|
||||
? alert(t('this_reply_was_deleted'))
|
||||
: alert(t('this_thread_was_deleted'))
|
||||
: removed
|
||||
? isReply
|
||||
? alert(t('this_reply_was_removed'))
|
||||
: alert(t('this_thread_was_removed'))
|
||||
: openReplyModal && openReplyModal(cid, postCid, subplebbitAddress);
|
||||
? isReply
|
||||
? alert(t('this_reply_was_removed'))
|
||||
: alert(t('this_thread_was_removed'))
|
||||
: openReplyModal && openReplyModal(cid, post?.number, postCid, threadNumber, subplebbitAddress);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -322,7 +325,7 @@ const PostMedia = ({
|
||||
);
|
||||
};
|
||||
|
||||
const Reply = ({ postReplyCount, reply, roles }: PostProps) => {
|
||||
const Reply = ({ postReplyCount, reply, roles, threadNumber }: PostProps) => {
|
||||
let post = reply;
|
||||
// handle pending mod or author edit
|
||||
const { editedComment } = useEditedComment({ comment: reply });
|
||||
@@ -350,7 +353,7 @@ const Reply = ({ postReplyCount, reply, roles }: PostProps) => {
|
||||
<div className={styles.replyDesktop}>
|
||||
<div className={styles.sideArrows}>{'>>'}</div>
|
||||
<div className={`${styles.reply} ${isRouteLinkToReply && styles.highlight}`} data-cid={cid} data-author-address={author?.shortAddress} data-post-cid={postCid}>
|
||||
<PostInfo post={post} postReplyCount={postReplyCount} roles={roles} isHidden={hidden} />
|
||||
<PostInfo post={post} postReplyCount={postReplyCount} roles={roles} isHidden={hidden} threadNumber={threadNumber} />
|
||||
{link && !hidden && !(deleted || removed) && isValidURL(link) && (
|
||||
<PostMedia
|
||||
commentMediaInfo={commentMediaInfo}
|
||||
@@ -377,6 +380,7 @@ const PostDesktop = ({ post, roles, showAllReplies, showReplies = true }: PostPr
|
||||
const { author, cid, content, deleted, link, linkHeight, linkWidth, pinned, postCid, removed, spoiler, state, subplebbitAddress, thumbnailUrl, parentCid } = post || {};
|
||||
const params = useParams();
|
||||
const location = useLocation();
|
||||
const navigationType = useNavigationType();
|
||||
const isInPendingPostView = isPendingPostView(location.pathname, params);
|
||||
const isInPostPageView = isPostPageView(location.pathname, params);
|
||||
const isInAllView = isAllView(location.pathname);
|
||||
@@ -387,7 +391,7 @@ const PostDesktop = ({ post, roles, showAllReplies, showReplies = true }: PostPr
|
||||
const { hidden, unhide, hide } = useHide({ cid });
|
||||
const isHidden = hidden && !isInPostPageView;
|
||||
|
||||
const replies = useReplies(post);
|
||||
const { replies, hasMore, loadMore } = useReplies({ comment: post });
|
||||
const visiblelinksCount = useCountLinksInReplies(post, 5);
|
||||
const totalLinksCount = useCountLinksInReplies(post);
|
||||
const replyCount = replies?.length;
|
||||
@@ -401,6 +405,38 @@ const PostDesktop = ({ post, roles, showAllReplies, showReplies = true }: PostPr
|
||||
const commentMediaInfo = useCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight);
|
||||
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
|
||||
|
||||
// Filter out deleted replies with no children for both virtuoso and non-virtuoso rendering
|
||||
const filteredReplies = useMemo(() => (replies || []).filter((reply) => !(reply.deleted && (reply.replyCount === 0 || !reply.replyCount))), [replies]);
|
||||
|
||||
// Virtuoso scroll position management for infinite replies
|
||||
const virtuosoRef = useRef<VirtuosoHandle | null>(null);
|
||||
const virtuosoStateKey = `replies-desktop-${cid}`;
|
||||
|
||||
useEffect(() => {
|
||||
if (!showAllReplies || !isInPostPageView) return;
|
||||
|
||||
const currentKey = virtuosoStateKey;
|
||||
const setLastVirtuosoState = () => {
|
||||
virtuosoRef.current?.getState((snapshot: StateSnapshot) => {
|
||||
if (snapshot?.ranges?.length) {
|
||||
lastVirtuosoStates[currentKey] = snapshot;
|
||||
}
|
||||
});
|
||||
};
|
||||
window.addEventListener('scroll', setLastVirtuosoState);
|
||||
return () => window.removeEventListener('scroll', setLastVirtuosoState);
|
||||
}, [virtuosoStateKey, showAllReplies, isInPostPageView]);
|
||||
|
||||
const lastVirtuosoState = navigationType === 'POP' ? lastVirtuosoStates?.[virtuosoStateKey] : undefined;
|
||||
|
||||
// Footer component for Virtuoso showing loading state
|
||||
const RepliesFooter = () =>
|
||||
hasMore ? (
|
||||
<div className={styles.stateString}>
|
||||
<LoadingEllipsis string={t('loading')} />
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<div className={styles.postDesktop}>
|
||||
{showReplies ? (
|
||||
@@ -433,7 +469,7 @@ const PostDesktop = ({ post, roles, showAllReplies, showReplies = true }: PostPr
|
||||
isInSubscriptionsView={isInSubscriptionsView}
|
||||
/>
|
||||
)}
|
||||
<PostInfo isHidden={hidden} post={post} postReplyCount={replyCount} roles={roles} />
|
||||
<PostInfo isHidden={hidden} post={post} postReplyCount={replyCount} roles={roles} threadNumber={post?.number} />
|
||||
{!isHidden && !content && !(deleted || removed) && <div className={styles.spacer} />}
|
||||
{!isHidden && <CommentContent comment={post} />}
|
||||
</div>
|
||||
@@ -462,19 +498,48 @@ const PostDesktop = ({ post, roles, showAllReplies, showReplies = true }: PostPr
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
{/* Virtuoso infinite scroll for post page view with more than 25 replies */}
|
||||
{!isHidden && showAllReplies && !isInPendingPostView && showReplies && replyCount > 25 && (
|
||||
<Virtuoso
|
||||
increaseViewportBy={{ bottom: 1200, top: 1200 }}
|
||||
totalCount={filteredReplies.length}
|
||||
data={filteredReplies}
|
||||
itemContent={(index, reply) => (
|
||||
<div className={styles.replyContainer}>
|
||||
<Reply reply={reply} roles={roles} postReplyCount={replyCount} threadNumber={post?.number} />
|
||||
</div>
|
||||
)}
|
||||
useWindowScroll={true}
|
||||
components={{ Footer: RepliesFooter }}
|
||||
endReached={loadMore}
|
||||
ref={virtuosoRef}
|
||||
restoreStateFrom={lastVirtuosoState}
|
||||
initialScrollTop={lastVirtuosoState?.scrollTop}
|
||||
/>
|
||||
)}
|
||||
{/* Non-virtualized rendering for post page view with 25 or fewer replies */}
|
||||
{!isHidden &&
|
||||
showAllReplies &&
|
||||
!isInPendingPostView &&
|
||||
showReplies &&
|
||||
replyCount <= 25 &&
|
||||
filteredReplies.map((reply, index) => (
|
||||
<div key={index} className={styles.replyContainer}>
|
||||
<Reply reply={reply} roles={roles} postReplyCount={replyCount} threadNumber={post?.number} />
|
||||
</div>
|
||||
))}
|
||||
{/* Non-virtualized rendering for board view (last 5 replies or show omitted) */}
|
||||
{!isHidden &&
|
||||
!showAllReplies &&
|
||||
!(pinned && !isInPostPageView && !showOmittedReplies[cid]) &&
|
||||
!isInPendingPostView &&
|
||||
replies &&
|
||||
showReplies &&
|
||||
(showAllReplies || showOmittedReplies[cid] ? replies : replies.slice(-5))
|
||||
// Don't render deleted replies that have no children (replyCount = 0)
|
||||
.filter((reply) => !(reply.deleted && (reply.replyCount === 0 || !reply.replyCount)))
|
||||
.map((reply, index) => (
|
||||
<div key={index} className={styles.replyContainer}>
|
||||
<Reply reply={reply} roles={roles} postReplyCount={replyCount} />
|
||||
</div>
|
||||
))}
|
||||
(showOmittedReplies[cid] ? filteredReplies : filteredReplies.slice(-5)).map((reply, index) => (
|
||||
<div key={index} className={styles.replyContainer}>
|
||||
<Reply reply={reply} roles={roles} postReplyCount={replyCount} threadNumber={post?.number} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{!isInPendingPostView && stateString && stateString !== 'Failed' && state !== 'succeeded' && isInPostPageView && !(!showReplies && !showAllReplies) ? (
|
||||
<div className={styles.stateString}>
|
||||
|
||||
@@ -139,7 +139,7 @@
|
||||
}
|
||||
|
||||
.postFormMobile {
|
||||
margin-bottom: 6px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.postFormTable {
|
||||
|
||||
@@ -8,7 +8,7 @@ import useSubplebbitsPagesStore from '@plebbit/plebbit-react-hooks/dist/stores/s
|
||||
import { getHasThumbnail, getLinkMediaInfo } from '../../lib/utils/media-utils';
|
||||
import { formatMarkdown } from '../../lib/utils/post-utils';
|
||||
import { isValidURL } from '../../lib/utils/url-utils';
|
||||
import { isAllView, isModView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
||||
import { isAllView, isCatalogView, isModView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
||||
import { useDefaultSubplebbits } from '../../hooks/use-default-subplebbits';
|
||||
import { useResolvedSubplebbitAddress } from '../../hooks/use-resolved-subplebbit-address';
|
||||
import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
|
||||
@@ -361,6 +361,7 @@ const PostForm = () => {
|
||||
const isInAllView = isAllView(location.pathname);
|
||||
const isInModView = isModView(location.pathname);
|
||||
const isInSubscriptionsView = isSubscriptionsView(location.pathname, params);
|
||||
const isInCatalogView = isCatalogView(location.pathname, params);
|
||||
|
||||
const commentCid = params?.commentCid;
|
||||
const post = useSubplebbitsPagesStore((state) => state.comments[commentCid as string]);
|
||||
@@ -424,6 +425,7 @@ const PostForm = () => {
|
||||
{showForm && <PostFormTable closeForm={() => setShowForm(false)} postCid={postCid} />}
|
||||
</>
|
||||
)}
|
||||
{isInCatalogView && <hr />}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link, useLocation, useParams } from 'react-router-dom';
|
||||
import { Comment, useAuthorAvatar, useEditedComment } from '@plebbit/plebbit-react-hooks';
|
||||
import { Link, useLocation, useNavigationType, useParams } from 'react-router-dom';
|
||||
import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso';
|
||||
import { Comment, useAuthorAvatar, useEditedComment, useReplies } from '@plebbit/plebbit-react-hooks';
|
||||
import Plebbit from '@plebbit/plebbit-js';
|
||||
import styles from '../../views/post/post.module.css';
|
||||
import { shouldShowSnow } from '../../lib/snow';
|
||||
@@ -16,7 +17,6 @@ import useAuthorAddressClick from '../../hooks/use-author-address-click';
|
||||
import { useCommentMediaInfo } from '../../hooks/use-comment-media-info';
|
||||
import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
|
||||
import useHide from '../../hooks/use-hide';
|
||||
import useReplies from '../../hooks/use-replies';
|
||||
import useStateString from '../../hooks/use-state-string';
|
||||
import CommentContent from '../comment-content';
|
||||
import CommentMedia from '../comment-media';
|
||||
@@ -29,7 +29,10 @@ import _ from 'lodash';
|
||||
import useReplyModalStore from '../../stores/use-reply-modal-store';
|
||||
import { selectPostMenuProps } from '../../lib/utils/post-menu-props';
|
||||
|
||||
const PostInfoAndMedia = ({ post, postReplyCount = 0, roles }: PostProps) => {
|
||||
// Store scroll position for replies virtuoso across navigations
|
||||
const lastVirtuosoStates: { [key: string]: StateSnapshot } = {};
|
||||
|
||||
const PostInfoAndMedia = ({ post, postReplyCount = 0, roles, threadNumber }: PostProps) => {
|
||||
const { t } = useTranslation();
|
||||
const defaultSubplebbits = useDefaultSubplebbits();
|
||||
const { author, cid, deleted, link, linkHeight, linkWidth, locked, parentCid, pinned, postCid, reason, removed, state, subplebbitAddress, timestamp, thumbnailUrl } =
|
||||
@@ -72,10 +75,10 @@ const PostInfoAndMedia = ({ post, postReplyCount = 0, roles }: PostProps) => {
|
||||
? alert(t('this_reply_was_deleted'))
|
||||
: alert(t('this_thread_was_deleted'))
|
||||
: removed
|
||||
? isReply
|
||||
? alert(t('this_reply_was_removed'))
|
||||
: alert(t('this_thread_was_removed'))
|
||||
: openReplyModal && openReplyModal(cid, postCid, subplebbitAddress);
|
||||
? isReply
|
||||
? alert(t('this_reply_was_removed'))
|
||||
: alert(t('this_thread_was_removed'))
|
||||
: openReplyModal && openReplyModal(cid, post?.number, postCid, threadNumber, subplebbitAddress);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -228,7 +231,7 @@ const PostMediaContent = ({ post, link }: { post: any; link: string }) => {
|
||||
|
||||
const ReplyBacklinks = ({ post }: PostProps) => {
|
||||
const { cid, parentCid } = post || {};
|
||||
const replies = useReplies(post);
|
||||
const { replies } = useReplies({ comment: post });
|
||||
|
||||
return (
|
||||
cid &&
|
||||
@@ -246,7 +249,7 @@ const ReplyBacklinks = ({ post }: PostProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Reply = ({ postReplyCount, reply, roles }: PostProps) => {
|
||||
const Reply = ({ postReplyCount, reply, roles, threadNumber }: PostProps) => {
|
||||
let post = reply;
|
||||
// handle pending mod or author edit
|
||||
const { editedComment } = useEditedComment({ comment: reply });
|
||||
@@ -270,7 +273,7 @@ const Reply = ({ postReplyCount, reply, roles }: PostProps) => {
|
||||
data-author-address={author?.shortAddress}
|
||||
data-post-cid={postCid}
|
||||
>
|
||||
<PostInfoAndMedia post={post} postReplyCount={postReplyCount} roles={roles} />
|
||||
<PostInfoAndMedia post={post} postReplyCount={postReplyCount} roles={roles} threadNumber={threadNumber} />
|
||||
{!hidden && (!(removed || deleted) || ((removed || deleted) && reason)) && <CommentContent comment={post} />}
|
||||
<ReplyBacklinks post={reply} />
|
||||
</div>
|
||||
@@ -284,18 +287,51 @@ const PostMobile = ({ post, roles, showAllReplies, showReplies = true }: PostPro
|
||||
const { author, cid, pinned, postCid, replyCount, state, subplebbitAddress } = post || {};
|
||||
const params = useParams();
|
||||
const location = useLocation();
|
||||
const navigationType = useNavigationType();
|
||||
const isInPendingPostView = isPendingPostView(location.pathname, params);
|
||||
const isInPostView = isPostPageView(location.pathname, params);
|
||||
const defaultSubplebbits = useDefaultSubplebbits();
|
||||
const boardPath = subplebbitAddress ? getBoardPath(subplebbitAddress, defaultSubplebbits) : undefined;
|
||||
const linksCount = useCountLinksInReplies(post);
|
||||
const replies = useReplies(post);
|
||||
const { replies, hasMore, loadMore } = useReplies({ comment: post });
|
||||
|
||||
const isInPostPageView = isPostPageView(location.pathname, params);
|
||||
const { hidden, unhide } = useHide({ cid });
|
||||
|
||||
const stateString = useStateString(post) || t('loading_post');
|
||||
|
||||
// Filter out deleted replies with no children for both virtuoso and non-virtuoso rendering
|
||||
const filteredReplies = useMemo(() => (replies || []).filter((reply) => !(reply.deleted && (reply.replyCount === 0 || !reply.replyCount))), [replies]);
|
||||
|
||||
// Virtuoso scroll position management for infinite replies
|
||||
const virtuosoRef = useRef<VirtuosoHandle | null>(null);
|
||||
const virtuosoStateKey = `replies-mobile-${cid}`;
|
||||
|
||||
useEffect(() => {
|
||||
if (!showAllReplies || !isInPostPageView) return;
|
||||
|
||||
const currentKey = virtuosoStateKey;
|
||||
const setLastVirtuosoState = () => {
|
||||
virtuosoRef.current?.getState((snapshot: StateSnapshot) => {
|
||||
if (snapshot?.ranges?.length) {
|
||||
lastVirtuosoStates[currentKey] = snapshot;
|
||||
}
|
||||
});
|
||||
};
|
||||
window.addEventListener('scroll', setLastVirtuosoState);
|
||||
return () => window.removeEventListener('scroll', setLastVirtuosoState);
|
||||
}, [virtuosoStateKey, showAllReplies, isInPostPageView]);
|
||||
|
||||
const lastVirtuosoState = navigationType === 'POP' ? lastVirtuosoStates?.[virtuosoStateKey] : undefined;
|
||||
|
||||
// Footer component for Virtuoso showing loading state
|
||||
const RepliesFooter = () =>
|
||||
hasMore ? (
|
||||
<div className={styles.stateString}>
|
||||
<LoadingEllipsis string={t('loading')} />
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{hidden && !isInPostPageView ? (
|
||||
@@ -323,7 +359,7 @@ const PostMobile = ({ post, roles, showAllReplies, showReplies = true }: PostPro
|
||||
data-post-cid={postCid}
|
||||
>
|
||||
{shouldShowSnow() && <img src='assets/xmashat.gif' className={styles.xmasHat} alt='' />}
|
||||
<PostInfoAndMedia post={post} postReplyCount={replyCount} roles={roles} />
|
||||
<PostInfoAndMedia post={post} postReplyCount={replyCount} roles={roles} threadNumber={post?.number} />
|
||||
<CommentContent comment={post} />
|
||||
</div>
|
||||
{!isInPostView && !isInPendingPostView && showReplies && (
|
||||
@@ -338,18 +374,47 @@ const PostMobile = ({ post, roles, showAllReplies, showReplies = true }: PostPro
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* Virtuoso infinite scroll for post page view with more than 25 replies */}
|
||||
{!(pinned && !isInPostView) && showAllReplies && !isInPendingPostView && showReplies && replyCount > 25 && (
|
||||
<Virtuoso
|
||||
increaseViewportBy={{ bottom: 1200, top: 1200 }}
|
||||
totalCount={filteredReplies.length}
|
||||
data={filteredReplies}
|
||||
itemContent={(index, reply) => (
|
||||
<div className={styles.replyContainer}>
|
||||
<Reply postReplyCount={replyCount} reply={reply} roles={roles} threadNumber={post?.number} />
|
||||
</div>
|
||||
)}
|
||||
useWindowScroll={true}
|
||||
components={{ Footer: RepliesFooter }}
|
||||
endReached={loadMore}
|
||||
ref={virtuosoRef}
|
||||
restoreStateFrom={lastVirtuosoState}
|
||||
initialScrollTop={lastVirtuosoState?.scrollTop}
|
||||
/>
|
||||
)}
|
||||
{/* Non-virtualized rendering for post page view with 25 or fewer replies */}
|
||||
{!(pinned && !isInPostView) &&
|
||||
showAllReplies &&
|
||||
!isInPendingPostView &&
|
||||
showReplies &&
|
||||
replyCount <= 25 &&
|
||||
filteredReplies.map((reply, index) => (
|
||||
<div key={index} className={styles.replyContainer}>
|
||||
<Reply postReplyCount={replyCount} reply={reply} roles={roles} threadNumber={post?.number} />
|
||||
</div>
|
||||
))}
|
||||
{/* Non-virtualized rendering for board view (last 5 replies) */}
|
||||
{!(pinned && !isInPostView) &&
|
||||
!showAllReplies &&
|
||||
!isInPendingPostView &&
|
||||
replies &&
|
||||
showReplies &&
|
||||
(showAllReplies ? replies : replies.slice(-5))
|
||||
// Don't render deleted replies that have no children (replyCount = 0)
|
||||
.filter((reply) => !(reply.deleted && (reply.replyCount === 0 || !reply.replyCount)))
|
||||
.map((reply, index) => (
|
||||
<div key={index} className={styles.replyContainer}>
|
||||
<Reply postReplyCount={replyCount} reply={reply} roles={roles} />
|
||||
</div>
|
||||
))}
|
||||
filteredReplies.slice(-5).map((reply, index) => (
|
||||
<div key={index} className={styles.replyContainer}>
|
||||
<Reply postReplyCount={replyCount} reply={reply} roles={roles} threadNumber={post?.number} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{!isInPendingPostView && stateString && stateString !== 'Failed' && state !== 'succeeded' && isInPostPageView && !(!showReplies && !showAllReplies) ? (
|
||||
<div className={styles.stateString}>
|
||||
|
||||
@@ -3,7 +3,6 @@ import { useLocation, useParams } from 'react-router-dom';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { setAccount, useAccount } from '@plebbit/plebbit-react-hooks';
|
||||
import useSubplebbitsStore from '@plebbit/plebbit-react-hooks/dist/stores/subplebbits';
|
||||
import Plebbit from '@plebbit/plebbit-js';
|
||||
import { formatMarkdown } from '../../lib/utils/post-utils';
|
||||
import { getFormattedTimeAgo } from '../../lib/utils/time-utils';
|
||||
import { isValidURL } from '../../lib/utils/url-utils';
|
||||
@@ -25,12 +24,14 @@ interface ReplyModalProps {
|
||||
closeModal: () => void;
|
||||
showReplyModal: boolean;
|
||||
parentCid: string;
|
||||
parentNumber: number | null;
|
||||
threadNumber: number | null;
|
||||
postCid: string;
|
||||
scrollY: number;
|
||||
subplebbitAddress: string;
|
||||
}
|
||||
|
||||
const ReplyModal = ({ closeModal, showReplyModal, parentCid, postCid, scrollY, subplebbitAddress }: ReplyModalProps) => {
|
||||
const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threadNumber, postCid, scrollY, subplebbitAddress }: ReplyModalProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { setPublishReplyOptions, publishReply, resetPublishReplyOptions, replyIndex } = usePublishReply({
|
||||
cid: parentCid,
|
||||
@@ -178,7 +179,7 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, postCid, scrollY, s
|
||||
}
|
||||
}, []);
|
||||
|
||||
const contentPrefix = `>>${parentCid && Plebbit.getShortCid({ cid: parentCid })}\n`;
|
||||
const contentPrefix = `>>${parentNumber ?? '?'}\n`;
|
||||
|
||||
// enable spellcheck after the prefix is set
|
||||
useEffect(() => {
|
||||
@@ -259,7 +260,7 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, postCid, scrollY, s
|
||||
}}
|
||||
>
|
||||
<div className={`replyModalHandle ${styles.title}`} {...(!isMobile ? bind() : {})}>
|
||||
{t('reply_to_cid', { cid: `CID:${parentCid && Plebbit.getShortCid({ cid: parentCid })}`, interpolation: { escapeValue: false } })}
|
||||
{t('reply_to_no', { no: threadNumber ?? '?' })}
|
||||
<button
|
||||
className={styles.closeIcon}
|
||||
onClick={(e) => {
|
||||
|
||||
@@ -144,7 +144,7 @@ const DesktopQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, i
|
||||
onClick={(e) => handleClick(e, backlinkReply?.cid, backlinkReply?.subplebbitAddress)}
|
||||
>
|
||||
{'>>'}
|
||||
{backlinkReply?.shortCid}
|
||||
{backlinkReply?.number ?? '?'}
|
||||
</Link>
|
||||
{hoveredCid === backlinkReply?.cid &&
|
||||
outOfViewCid === backlinkReply?.cid &&
|
||||
@@ -172,7 +172,7 @@ const DesktopQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, i
|
||||
onMouseLeave={() => handleMouseLeave(quotelinkReply?.cid)}
|
||||
onClick={(e) => handleClick(e, quotelinkReply?.cid, quotelinkReply?.subplebbitAddress)}
|
||||
>
|
||||
{quotelinkReply?.shortCid && `>>${quotelinkReply?.shortCid}`}
|
||||
{`>>${quotelinkReply?.number ?? '?'}`}
|
||||
{quotelinkReply?.author?.address === account?.author?.address && ' (You)'}
|
||||
</Link>
|
||||
<br />
|
||||
@@ -251,9 +251,9 @@ const MobileQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, is
|
||||
onMouseOver={() => handleMouseOver(backlinkReply?.cid)}
|
||||
onMouseLeave={() => handleMouseLeave(backlinkReply?.cid)}
|
||||
>
|
||||
{backlinkReply?.shortCid && `>>${backlinkReply?.shortCid}`}
|
||||
{`>>${backlinkReply?.number ?? '?'}`}
|
||||
</span>
|
||||
{backlinkReply?.shortCid &&
|
||||
{backlinkReply?.number &&
|
||||
(() => {
|
||||
const backlinkBoardPath = backlinkReply?.subplebbitAddress ? getBoardPath(backlinkReply.subplebbitAddress, defaultSubplebbits) : undefined;
|
||||
const backlinkRoute = backlinkReply?.cid ? (backlinkBoardPath ? `/${backlinkBoardPath}/thread/${backlinkReply.cid}` : `/thread/${backlinkReply.cid}`) : '#';
|
||||
@@ -285,10 +285,10 @@ const MobileQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, is
|
||||
onMouseOver={() => handleMouseOver(quotelinkReply?.cid)}
|
||||
onMouseLeave={() => handleMouseLeave(quotelinkReply?.cid)}
|
||||
>
|
||||
{quotelinkReply?.shortCid && `>>${quotelinkReply?.shortCid}`}
|
||||
{`>>${quotelinkReply?.number ?? '?'}`}
|
||||
{quotelinkReply?.author?.address === account?.author?.address && ' (You)'}
|
||||
</span>
|
||||
{quotelinkReply?.shortCid &&
|
||||
{quotelinkReply?.number &&
|
||||
(() => {
|
||||
const quotelinkBoardPath = quotelinkReply?.subplebbitAddress ? getBoardPath(quotelinkReply.subplebbitAddress, defaultSubplebbits) : undefined;
|
||||
const quotelinkRoute = quotelinkReply?.cid
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './pkc-options';
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
import { RefObject, useRef, useState } from 'react';
|
||||
import { setAccount, useAccount, usePlebbitRpcSettings } from '@plebbit/plebbit-react-hooks';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styles from './plebbit-options.module.css';
|
||||
import styles from './pkc-options.module.css';
|
||||
|
||||
interface SettingsProps {
|
||||
ipfsGatewayUrlsRef?: RefObject<HTMLTextAreaElement>;
|
||||
@@ -113,7 +113,7 @@ const PlebbitRPCSettings = ({ plebbitRpcRef }: SettingsProps) => {
|
||||
</div>
|
||||
{showInfo && (
|
||||
<div className={styles.plebbitRpcSettingsInfo}>
|
||||
use a plebbit full node locally, or remotely with SSL
|
||||
use a PKC full node locally, or remotely with SSL
|
||||
<br />
|
||||
<ol>
|
||||
<li>get secret auth key from the node</li>
|
||||
@@ -264,7 +264,7 @@ const PlebbitOptions = () => {
|
||||
</span>
|
||||
</div>
|
||||
<div className={styles.category}>
|
||||
<span className={styles.categoryTitle}>plebbit RPC:</span>
|
||||
<span className={styles.categoryTitle}>Node RPC:</span>
|
||||
<span className={styles.categorySettings}>
|
||||
<PlebbitRPCSettings plebbitRpcRef={plebbitRpcRef} />
|
||||
</span>
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './plebbit-options';
|
||||
@@ -8,7 +8,7 @@ import BlockedAddressesSetting from './blocked-addresses-setting';
|
||||
import CryptoAddressSetting from './crypto-address-setting';
|
||||
import CryptoWalletsSetting from './crypto-wallets-setting';
|
||||
import InterfaceSettings from './interface-settings';
|
||||
import PlebbitOptions from './plebbit-options';
|
||||
import PlebbitOptions from './pkc-options';
|
||||
import SubscriptionsSetting from './subscriptions-setting';
|
||||
|
||||
const SettingsModal = () => {
|
||||
@@ -66,7 +66,7 @@ const SettingsModal = () => {
|
||||
if (showCryptoWalletSettings && 'crypto-wallet-settings' !== excludeCategoryId) return 'crypto-wallet-settings';
|
||||
if (showSubscriptionsSettings && 'subscriptions-settings' !== excludeCategoryId) return 'subscriptions-settings';
|
||||
if (showBlockedAddressesSetting && 'blocked-addresses-settings' !== excludeCategoryId) return 'blocked-addresses-settings';
|
||||
if (showPlebbitOptionsSettings && 'plebbit-options-settings' !== excludeCategoryId) return 'plebbit-options-settings';
|
||||
if (showPlebbitOptionsSettings && 'pkc-options-settings' !== excludeCategoryId) return 'pkc-options-settings';
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -106,7 +106,7 @@ const SettingsModal = () => {
|
||||
setShowCryptoWalletSettings(hash === 'crypto-wallet-settings');
|
||||
setShowSubscriptionsSettings(hash === 'subscriptions-settings');
|
||||
setShowBlockedAddressesSetting(hash === 'blocked-addresses-settings');
|
||||
setShowPlebbitOptionsSettings(hash === 'plebbit-options-settings');
|
||||
setShowPlebbitOptionsSettings(hash === 'pkc-options-settings');
|
||||
}
|
||||
}, [hash]);
|
||||
|
||||
@@ -147,7 +147,7 @@ const SettingsModal = () => {
|
||||
<div id='account-settings' className={`${styles.setting} ${styles.category}`}>
|
||||
<label onClick={() => handleCategoryClick('account-settings', showAccountSettings, setShowAccountSettings)}>
|
||||
<span className={showAccountSettings ? styles.hideButton : styles.showButton} />
|
||||
{t('plebbit_account')}
|
||||
{t('bitsocial_account')}
|
||||
</label>
|
||||
</div>
|
||||
{showAccountSettings && <AccountSettings />}
|
||||
@@ -186,10 +186,10 @@ const SettingsModal = () => {
|
||||
</label>
|
||||
</div>
|
||||
{showBlockedAddressesSetting && <BlockedAddressesSetting />}
|
||||
<div id='plebbit-options-settings' className={`${styles.setting} ${styles.category}`}>
|
||||
<label onClick={() => handleCategoryClick('plebbit-options-settings', showPlebbitOptionsSettings, setShowPlebbitOptionsSettings)}>
|
||||
<div id='pkc-options-settings' className={`${styles.setting} ${styles.category}`}>
|
||||
<label onClick={() => handleCategoryClick('pkc-options-settings', showPlebbitOptionsSettings, setShowPlebbitOptionsSettings)}>
|
||||
<span className={showPlebbitOptionsSettings ? styles.hideButton : styles.showButton} />
|
||||
{t('plebbit_options')}
|
||||
{t('p2p_options')}
|
||||
</label>
|
||||
</div>
|
||||
{showPlebbitOptionsSettings && <PlebbitOptions />}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useDefaultSubplebbits } from './use-default-subplebbits';
|
||||
import useAllFeedFilterStore from '../stores/use-all-feed-filter-store';
|
||||
|
||||
export const useFilteredDefaultSubplebbitAddresses = () => {
|
||||
const defaultSubplebbits = useDefaultSubplebbits();
|
||||
const { filter } = useAllFeedFilterStore();
|
||||
|
||||
return useMemo(() => {
|
||||
if (filter === 'all') {
|
||||
return defaultSubplebbits.map((subplebbit) => subplebbit.address);
|
||||
}
|
||||
if (filter === 'nsfw') {
|
||||
return defaultSubplebbits.filter((subplebbit) => subplebbit.nsfw === true).map((subplebbit) => subplebbit.address);
|
||||
}
|
||||
// filter === 'sfw'
|
||||
return defaultSubplebbits.filter((subplebbit) => subplebbit.nsfw !== true).map((subplebbit) => subplebbit.address);
|
||||
}, [defaultSubplebbits, filter]);
|
||||
};
|
||||
@@ -36,8 +36,8 @@ const useIsSubplebbitOffline = (subplebbit: Subplebbit) => {
|
||||
const offlineTitle = isLoading
|
||||
? 'downloading board...'
|
||||
: updatedAt
|
||||
? isOffline && t('posts_last_synced_info', { time: getFormattedTimeAgo(updatedAt), interpolation: { escapeValue: false } })
|
||||
: t('subplebbit_offline_info');
|
||||
? isOffline && t('posts_last_synced_info', { time: getFormattedTimeAgo(updatedAt), interpolation: { escapeValue: false } })
|
||||
: t('subplebbit_offline_info');
|
||||
|
||||
return { isOffline: !isOnline && isOffline, isOnlineStatusLoading: !isOnline && isLoading, offlineIconClass, offlineTitle };
|
||||
};
|
||||
|
||||
@@ -37,10 +37,13 @@ const usePublishPost = ({ subplebbitAddress }: { subplebbitAddress?: string }) =
|
||||
const setPublishPostOptions = useCallback(
|
||||
(options: Partial<Comment>) => {
|
||||
const baseOptions = createBaseOptions();
|
||||
const sanitizedOptions = Object.entries(options).reduce((acc, [key, value]) => {
|
||||
acc[key] = value === '' ? undefined : value;
|
||||
return acc;
|
||||
}, {} as Partial<Comment>);
|
||||
const sanitizedOptions = Object.entries(options).reduce(
|
||||
(acc, [key, value]) => {
|
||||
acc[key] = value === '' ? undefined : value;
|
||||
return acc;
|
||||
},
|
||||
{} as Partial<Comment>,
|
||||
);
|
||||
|
||||
const newOptions = { ...baseOptions, ...sanitizedOptions };
|
||||
setPublishPostStore(newOptions);
|
||||
|
||||
@@ -38,10 +38,13 @@ const usePublishReply = ({ cid, subplebbitAddress, postCid }: { cid: string; sub
|
||||
const setPublishReplyOptions = useCallback(
|
||||
(options: Partial<Comment>) => {
|
||||
const baseOptions = createBaseOptions();
|
||||
const sanitizedOptions = Object.entries(options).reduce((acc, [key, value]) => {
|
||||
acc[key] = value === '' ? undefined : value;
|
||||
return acc;
|
||||
}, {} as Partial<Comment>);
|
||||
const sanitizedOptions = Object.entries(options).reduce(
|
||||
(acc, [key, value]) => {
|
||||
acc[key] = value === '' ? undefined : value;
|
||||
return acc;
|
||||
},
|
||||
{} as Partial<Comment>,
|
||||
);
|
||||
|
||||
const newOptions = { ...baseOptions, ...sanitizedOptions };
|
||||
setPublishReplyStore(newOptions);
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import { useMemo } from 'react';
|
||||
import { Comment, useAccountComments } from '@plebbit/plebbit-react-hooks';
|
||||
import { flattenCommentsPages } from '@plebbit/plebbit-react-hooks/dist/lib/utils';
|
||||
|
||||
const useReplies = (comment: Comment) => {
|
||||
// flatten all replies including nested ones from the original comment
|
||||
const flattenedReplies = useMemo(() => flattenCommentsPages(comment?.replies), [comment?.replies]);
|
||||
|
||||
// generate a Set of CIDs from flattened replies for quick lookup
|
||||
const replyCids = useMemo(() => new Set(flattenedReplies.map((reply) => reply?.cid)), [flattenedReplies]);
|
||||
|
||||
const { accountComments } = useAccountComments();
|
||||
|
||||
const filteredAccountComments = useMemo(() => {
|
||||
const commentMap = new Map(accountComments.map((c) => [c.cid, c]));
|
||||
|
||||
return accountComments.filter((accountComment) => {
|
||||
let currentCid = accountComment.parentCid;
|
||||
while (currentCid && currentCid !== comment?.cid) {
|
||||
if (replyCids.has(currentCid)) {
|
||||
return true;
|
||||
}
|
||||
const parent = commentMap.get(currentCid);
|
||||
if (!parent) {
|
||||
return false;
|
||||
}
|
||||
currentCid = parent.parentCid;
|
||||
}
|
||||
return currentCid === comment?.cid;
|
||||
});
|
||||
}, [accountComments, comment?.cid, replyCids]);
|
||||
|
||||
// the account's replies have a delay before getting published, so get them locally from accountComments instead
|
||||
const accountRepliesNotYetPublished = useMemo(() => {
|
||||
const replies = flattenedReplies || [];
|
||||
const replyCids = new Set(replies.map((reply: Comment) => reply?.cid));
|
||||
// filter out the account comments already in comment.replies, so they don't appear twice
|
||||
return filteredAccountComments.filter((accountReply) => !replyCids.has(accountReply?.cid));
|
||||
}, [flattenedReplies, filteredAccountComments]);
|
||||
|
||||
const repliesAndNotYetPublishedReplies = useMemo(() => {
|
||||
const repliesSortedByPinnedAndTimestamp = [...accountRepliesNotYetPublished.reverse(), ...(flattenedReplies || [])];
|
||||
|
||||
return repliesSortedByPinnedAndTimestamp.sort((a: Comment, b: Comment) => {
|
||||
// Sort by pinned status first, with pinned comments at the top
|
||||
if (a.pinned && !b.pinned) {
|
||||
return -1;
|
||||
} else if (!a.pinned && b.pinned) {
|
||||
return 1;
|
||||
}
|
||||
// If both are pinned or both are not pinned, sort by timestamp
|
||||
return a.timestamp - b.timestamp;
|
||||
});
|
||||
}, [flattenedReplies, accountRepliesNotYetPublished]);
|
||||
|
||||
return repliesAndNotYetPublishedReplies;
|
||||
};
|
||||
|
||||
export default useReplies;
|
||||
@@ -5,6 +5,7 @@ import useSelectedTextStore from '../stores/use-selected-text-store';
|
||||
const useReplyModal = () => {
|
||||
const [showReplyModal, setShowReplyModal] = useState(false);
|
||||
const [activeCid, setActiveCid] = useState<string | null>(null);
|
||||
const [parentNumber, setParentNumber] = useState<number | null>(null);
|
||||
const [threadCid, setThreadCid] = useState<string | null>(null);
|
||||
const [subplebbitAddress, setSubplebbitAddress] = useState<string | null>(null);
|
||||
const { resetSelectedText, setSelectedText } = useSelectedTextStore();
|
||||
@@ -16,6 +17,7 @@ const useReplyModal = () => {
|
||||
const closeModal = useCallback(() => {
|
||||
resetSelectedText();
|
||||
setActiveCid(null);
|
||||
setParentNumber(null);
|
||||
setShowReplyModal(false);
|
||||
}, [resetSelectedText, setActiveCid, setShowReplyModal]);
|
||||
|
||||
@@ -24,7 +26,7 @@ const useReplyModal = () => {
|
||||
if (text) setSelectedText(`>${text}\n`);
|
||||
};
|
||||
|
||||
const openReplyModal = (parentCid: string, postCid: string, subplebbitAddress: string) => {
|
||||
const openReplyModal = (parentCid: string, parentNum: number | undefined, postCid: string, subplebbitAddress: string) => {
|
||||
getSelectedText();
|
||||
|
||||
if (isMobile) {
|
||||
@@ -36,12 +38,13 @@ const useReplyModal = () => {
|
||||
return;
|
||||
}
|
||||
setActiveCid(parentCid);
|
||||
setParentNumber(parentNum ?? null);
|
||||
setThreadCid(postCid);
|
||||
setShowReplyModal(true);
|
||||
setSubplebbitAddress(subplebbitAddress);
|
||||
};
|
||||
|
||||
return { activeCid, threadCid, closeModal, openReplyModal, scrollY, showReplyModal, subplebbitAddress };
|
||||
return { activeCid, parentNumber, threadCid, closeModal, openReplyModal, scrollY, showReplyModal, subplebbitAddress };
|
||||
};
|
||||
|
||||
export default useReplyModal;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { THEME_BUTTON_IMAGES, THEME_BACKGROUND_IMAGES } from '../../generated/asset-manifest';
|
||||
|
||||
/**
|
||||
* Preloads an array of image URLs by creating Image objects.
|
||||
* Images are loaded in the background and cached by the browser.
|
||||
*/
|
||||
const preloadImages = (imagePaths: readonly string[]): void => {
|
||||
imagePaths.forEach((path) => {
|
||||
const img = new Image();
|
||||
img.src = `/${path}`;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Preloads all theme-related assets (buttons, backgrounds) on app startup.
|
||||
* This prevents visible loading delays when switching themes.
|
||||
*/
|
||||
export const preloadThemeAssets = (): void => {
|
||||
preloadImages(THEME_BUTTON_IMAGES);
|
||||
preloadImages(THEME_BACKGROUND_IMAGES);
|
||||
};
|
||||
+52
-18
@@ -148,42 +148,76 @@ const isValidDomain = (str: string): boolean => {
|
||||
return str.includes('.') && str.split('.').length >= 2 && str.split('.').every((part) => part.length > 0);
|
||||
};
|
||||
|
||||
// Check if a plain text pattern is a valid 5chan subplebbit reference
|
||||
export const isValidSubplebbitPattern = (pattern: string): boolean => {
|
||||
// Must start with "p/"
|
||||
if (!pattern.startsWith('p/')) {
|
||||
// Check if a plain text pattern is a valid 5chan cross-board reference (>>>/...)
|
||||
export const isValidCrossboardPattern = (pattern: string): boolean => {
|
||||
// Must start with ">>>/"
|
||||
if (!pattern.startsWith('>>>/')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const pathPart = pattern.substring(2); // Remove "p/"
|
||||
const pathPart = pattern.substring(4); // Remove ">>>/"
|
||||
|
||||
// Check if it's a post pattern: subplebbitAddress/c/cid
|
||||
const postMatch = pathPart.match(/^([^/]+)\/c\/([^/]+)$/);
|
||||
if (postMatch) {
|
||||
const [, subplebbitAddress, cid] = postMatch;
|
||||
// CID should be at least 10 characters (minimum reasonable CID length)
|
||||
return (isValidDomain(subplebbitAddress) || isValidIPNSKey(subplebbitAddress)) && cid.length >= 10;
|
||||
// Check if it's a directory pattern with trailing slash: >>>/biz/
|
||||
if (/^[a-zA-Z0-9]{1,10}\/$/.test(pathPart)) {
|
||||
return true; // Directory codes are always valid (highest-voted boards)
|
||||
}
|
||||
|
||||
// Check if it's just a subplebbit pattern: subplebbitAddress
|
||||
// Check if it's a directory + thread pattern: >>>/biz/fullCid
|
||||
const directoryThreadMatch = pathPart.match(/^([a-zA-Z0-9]{1,10})\/([a-zA-Z0-9]{46})$/);
|
||||
if (directoryThreadMatch) {
|
||||
return true; // CID is exactly 46 alphanumeric chars
|
||||
}
|
||||
|
||||
// Check if it's a full address + thread pattern: >>>/board.eth/fullCid
|
||||
const addressThreadMatch = pathPart.match(/^([^/]+)\/([a-zA-Z0-9]{46})$/);
|
||||
if (addressThreadMatch) {
|
||||
const [, address] = addressThreadMatch;
|
||||
// Address must be valid domain or IPNS key
|
||||
return isValidDomain(address) || isValidIPNSKey(address);
|
||||
}
|
||||
|
||||
// Check if it's just a full address pattern: >>>/board.eth
|
||||
return isValidDomain(pathPart) || isValidIPNSKey(pathPart);
|
||||
};
|
||||
|
||||
// Preprocess content to convert plain text 5chan patterns to markdown links
|
||||
// Preprocess content to convert plain text 5chan cross-board patterns to markdown links
|
||||
export const preprocess5chanPatterns = (content: string): string => {
|
||||
// Pattern to match "p/something" or "p/something/c/something"
|
||||
// Pattern to match ">>>/something" or ">>>/something/cid"
|
||||
// Negative lookbehind prevents matching patterns that are already part of URLs
|
||||
const pattern = /(?<!https?:\/\/[^\s]*)\bp\/([a-zA-Z0-9\-.]+(?:\/c\/[a-zA-Z0-9]{10,100})?)[.,:;!?]*/g;
|
||||
// Matches: >>>/directory/, >>>/directory/cid (46 chars), >>>/address, >>>/address/cid (46 chars)
|
||||
const pattern = /(?<!https?:\/\/[^\s]*)>>>\/([a-zA-Z0-9]{1,10}\/(?:[a-zA-Z0-9]{46})?|[a-zA-Z0-9\-.]+(?:\/[a-zA-Z0-9]{46})?)[.,:;!?]*/g;
|
||||
|
||||
return content.replace(pattern, (match, capturedPath) => {
|
||||
// Remove any trailing punctuation from the captured path
|
||||
const cleanPath = capturedPath.replace(/[.,:;!?]+$/, '');
|
||||
const fullPattern = `p/${cleanPath}`;
|
||||
const fullPattern = `>>>/${cleanPath}`;
|
||||
|
||||
if (isValidCrossboardPattern(fullPattern)) {
|
||||
// Generate internal route based on pattern type
|
||||
let internalRoute: string;
|
||||
|
||||
// Directory with trailing slash: >>>/biz/ → /biz
|
||||
if (/^[a-zA-Z0-9]{1,10}\/$/.test(cleanPath)) {
|
||||
internalRoute = `/${cleanPath.slice(0, -1)}`;
|
||||
}
|
||||
// Directory + CID: >>>/biz/cid → /biz/thread/cid (CID is 46 chars)
|
||||
else if (/^[a-zA-Z0-9]{1,10}\/[a-zA-Z0-9]{46}$/.test(cleanPath)) {
|
||||
const [code, cid] = cleanPath.split('/');
|
||||
internalRoute = `/${code}/thread/${cid}`;
|
||||
}
|
||||
// Full address + CID: >>>/board.eth/cid → /board.eth/thread/cid (CID is 46 chars)
|
||||
else if (/^[^/]+\/[a-zA-Z0-9]{46}$/.test(cleanPath)) {
|
||||
const [address, cid] = cleanPath.split('/');
|
||||
internalRoute = `/${address}/thread/${cid}`;
|
||||
}
|
||||
// Just a full address: >>>/board.eth → /board.eth
|
||||
else {
|
||||
internalRoute = `/${cleanPath}`;
|
||||
}
|
||||
|
||||
if (isValidSubplebbitPattern(fullPattern)) {
|
||||
// Preserve trailing punctuation outside the link
|
||||
const trailingPunctuation = match.slice(fullPattern.length);
|
||||
return `[${fullPattern}](/${fullPattern})${trailingPunctuation}`;
|
||||
return `[${fullPattern}](${internalRoute})${trailingPunctuation}`;
|
||||
}
|
||||
|
||||
return match;
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
export type AllFeedFilter = 'all' | 'nsfw' | 'sfw';
|
||||
|
||||
interface AllFeedFilterStore {
|
||||
filter: AllFeedFilter;
|
||||
setFilter: (value: AllFeedFilter) => void;
|
||||
}
|
||||
|
||||
const getStoredFilter = (): AllFeedFilter => {
|
||||
try {
|
||||
const stored = localStorage.getItem('5chan-all-feed-filter') as AllFeedFilter;
|
||||
return stored === 'nsfw' || stored === 'sfw' ? stored : 'all';
|
||||
} catch {
|
||||
return 'all';
|
||||
}
|
||||
};
|
||||
|
||||
const useAllFeedFilterStore = create<AllFeedFilterStore>((set) => ({
|
||||
filter: getStoredFilter(),
|
||||
setFilter: (value: AllFeedFilter) => {
|
||||
set({ filter: value });
|
||||
try {
|
||||
localStorage.setItem('5chan-all-feed-filter', value);
|
||||
} catch (error) {
|
||||
console.warn('Failed to save allFeedFilter to localStorage:', error);
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
export default useAllFeedFilterStore;
|
||||
@@ -4,16 +4,20 @@ import useSelectedTextStore from './use-selected-text-store';
|
||||
interface ReplyModalState {
|
||||
showReplyModal: boolean;
|
||||
activeCid: string | null;
|
||||
parentNumber: number | null;
|
||||
threadNumber: number | null;
|
||||
threadCid: string | null;
|
||||
subplebbitAddress: string | null;
|
||||
scrollY: number;
|
||||
closeModal: () => void;
|
||||
openReplyModal: (parentCid: string, postCid: string, subplebbitAddress: string) => void;
|
||||
openReplyModal: (parentCid: string, parentNumber: number | undefined, postCid: string, threadNumber: number | undefined, subplebbitAddress: string) => void;
|
||||
}
|
||||
|
||||
const useReplyModalStore = create<ReplyModalState>((set, get) => ({
|
||||
showReplyModal: false,
|
||||
activeCid: null,
|
||||
parentNumber: null,
|
||||
threadNumber: null,
|
||||
threadCid: null,
|
||||
subplebbitAddress: null,
|
||||
scrollY: 0,
|
||||
@@ -24,10 +28,12 @@ const useReplyModalStore = create<ReplyModalState>((set, get) => ({
|
||||
set({
|
||||
showReplyModal: false,
|
||||
activeCid: null,
|
||||
parentNumber: null,
|
||||
threadNumber: null,
|
||||
});
|
||||
},
|
||||
|
||||
openReplyModal: (parentCid, postCid, subplebbitAddress) => {
|
||||
openReplyModal: (parentCid, parentNumber, postCid, threadNumber, subplebbitAddress) => {
|
||||
// Don't update if already open with different parent
|
||||
if (get().activeCid && get().activeCid !== parentCid) {
|
||||
window.alert('Multiple quotes are not possible on 5chan for the time being, because of a protocol limitation. Please reply to one post at a time.');
|
||||
@@ -46,6 +52,8 @@ const useReplyModalStore = create<ReplyModalState>((set, get) => ({
|
||||
|
||||
set({
|
||||
activeCid: parentCid,
|
||||
parentNumber: parentNumber ?? null,
|
||||
threadNumber: threadNumber ?? null,
|
||||
threadCid: postCid,
|
||||
showReplyModal: true,
|
||||
subplebbitAddress,
|
||||
|
||||
@@ -7,13 +7,14 @@ import styles from './board.module.css';
|
||||
import { shouldShowSnow } from '../../lib/snow';
|
||||
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
|
||||
import { useDefaultSubplebbitAddresses, useDefaultSubplebbits } from '../../hooks/use-default-subplebbits';
|
||||
import { useFilteredDefaultSubplebbitAddresses } from '../../hooks/use-filtered-default-subplebbit-addresses';
|
||||
import { useResolvedSubplebbitAddress, useBoardPath } from '../../hooks/use-resolved-subplebbit-address';
|
||||
import { useFeedStateString } from '../../hooks/use-state-string';
|
||||
import useTimeFilter, { timeFilterNameToSeconds } from '../../hooks/use-time-filter';
|
||||
import useInterfaceSettingsStore from '../../stores/use-interface-settings-store';
|
||||
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
||||
import useSortingStore from '../../stores/use-sorting-store';
|
||||
import { getSubplebbitAddress } from '../../lib/utils/route-utils';
|
||||
import { getSubplebbitAddress, isDirectoryBoard } from '../../lib/utils/route-utils';
|
||||
import ErrorDisplay from '../../components/error-display/error-display';
|
||||
import LoadingEllipsis from '../../components/loading-ellipsis';
|
||||
import { Post } from '../post';
|
||||
@@ -60,6 +61,7 @@ const Board = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp, t
|
||||
|
||||
const boardPath = useBoardPath(subplebbitAddress);
|
||||
const defaultSubplebbitAddresses = useDefaultSubplebbitAddresses();
|
||||
const filteredDefaultSubplebbitAddresses = useFilteredDefaultSubplebbitAddresses();
|
||||
|
||||
const account = useAccount();
|
||||
const subscriptions = account?.subscriptions;
|
||||
@@ -69,7 +71,7 @@ const Board = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp, t
|
||||
|
||||
const subplebbitAddresses = useMemo(() => {
|
||||
if (isInAllView) {
|
||||
return defaultSubplebbitAddresses;
|
||||
return filteredDefaultSubplebbitAddresses;
|
||||
}
|
||||
if (isInSubscriptionsView) {
|
||||
return subscriptions || [];
|
||||
@@ -78,7 +80,16 @@ const Board = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp, t
|
||||
return accountSubplebbitAddresses;
|
||||
}
|
||||
return [subplebbitAddress];
|
||||
}, [isInAllView, isInSubscriptionsView, isInModView, subplebbitAddress, defaultSubplebbitAddresses, subscriptions, accountSubplebbitAddresses]);
|
||||
}, [
|
||||
isInAllView,
|
||||
isInSubscriptionsView,
|
||||
isInModView,
|
||||
subplebbitAddress,
|
||||
defaultSubplebbitAddresses,
|
||||
filteredDefaultSubplebbitAddresses,
|
||||
subscriptions,
|
||||
accountSubplebbitAddresses,
|
||||
]);
|
||||
|
||||
const { sortType } = useSortingStore();
|
||||
const { timeFilterSeconds: timeFilterSecondsFromHook, timeFilterName: timeFilterNameFromHook } = useTimeFilter();
|
||||
@@ -324,9 +335,35 @@ const Board = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp, t
|
||||
|
||||
useEffect(() => {
|
||||
if (!isVisible) return;
|
||||
const boardTitle = title ? title : shortAddress || subplebbitAddress;
|
||||
const boardIdentifier = params.boardIdentifier || boardIdentifierProp;
|
||||
const isDirectory = boardIdentifier ? isDirectoryBoard(boardIdentifier, defaultSubplebbits) : false;
|
||||
|
||||
let boardTitle: string;
|
||||
if (isInAllView) {
|
||||
boardTitle = t('all');
|
||||
} else if (isInSubscriptionsView) {
|
||||
boardTitle = t('subscriptions');
|
||||
} else if (isInModView) {
|
||||
boardTitle = t('mod');
|
||||
} else if (isDirectory) {
|
||||
boardTitle = `/${boardIdentifier}/`;
|
||||
} else {
|
||||
boardTitle = title ? title : shortAddress || subplebbitAddress || '';
|
||||
}
|
||||
document.title = boardTitle + ' - 5chan';
|
||||
}, [title, shortAddress, subplebbitAddress, isVisible]);
|
||||
}, [
|
||||
title,
|
||||
shortAddress,
|
||||
subplebbitAddress,
|
||||
isVisible,
|
||||
params.boardIdentifier,
|
||||
boardIdentifierProp,
|
||||
defaultSubplebbits,
|
||||
isInAllView,
|
||||
isInSubscriptionsView,
|
||||
isInModView,
|
||||
t,
|
||||
]);
|
||||
|
||||
const shouldShowErrorToUser = error?.message && feed.length === 0;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso';
|
||||
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
|
||||
import useCatalogFeedRows from '../../hooks/use-catalog-feed-rows';
|
||||
import { useDefaultSubplebbits } from '../../hooks/use-default-subplebbits';
|
||||
import { useFilteredDefaultSubplebbitAddresses } from '../../hooks/use-filtered-default-subplebbit-addresses';
|
||||
import { useResolvedSubplebbitAddress, useBoardPath } from '../../hooks/use-resolved-subplebbit-address';
|
||||
import { useFeedStateString } from '../../hooks/use-state-string';
|
||||
import useTimeFilter, { timeFilterNameToSeconds } from '../../hooks/use-time-filter';
|
||||
@@ -14,9 +15,10 @@ import useCatalogStyleStore from '../../stores/use-catalog-style-store';
|
||||
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
||||
import useSortingStore from '../../stores/use-sorting-store';
|
||||
import useCatalogFiltersStore from '../../stores/use-catalog-filters-store';
|
||||
import { getSubplebbitAddress } from '../../lib/utils/route-utils';
|
||||
import { getSubplebbitAddress, isDirectoryBoard } from '../../lib/utils/route-utils';
|
||||
import CatalogRow from '../../components/catalog-row';
|
||||
import LoadingEllipsis from '../../components/loading-ellipsis';
|
||||
import ErrorDisplay from '../../components/error-display/error-display';
|
||||
import styles from './catalog.module.css';
|
||||
import { commentMatchesPattern } from '../../lib/utils/pattern-utils';
|
||||
|
||||
@@ -152,19 +154,18 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
||||
|
||||
const account = useAccount();
|
||||
const subscriptions = account?.subscriptions;
|
||||
const filteredDefaultSubplebbitAddresses = useFilteredDefaultSubplebbitAddresses();
|
||||
|
||||
const subplebbitAddresses = useMemo(() => {
|
||||
const filteredDefaultSubplebbits = defaultSubplebbits.map((subplebbit) => subplebbit.address).filter(Boolean); // Filter out any undefined/null values
|
||||
|
||||
if (isInAllView) {
|
||||
return filteredDefaultSubplebbits;
|
||||
return filteredDefaultSubplebbitAddresses;
|
||||
}
|
||||
if (isInSubscriptionsView) {
|
||||
return (subscriptions || []).filter(Boolean); // Filter out any undefined/null values
|
||||
}
|
||||
// Only include subplebbitAddress if it's defined
|
||||
return subplebbitAddress ? [subplebbitAddress] : [];
|
||||
}, [isInAllView, isInSubscriptionsView, subplebbitAddress, defaultSubplebbits, subscriptions]);
|
||||
}, [isInAllView, isInSubscriptionsView, subplebbitAddress, filteredDefaultSubplebbitAddresses, subscriptions]);
|
||||
|
||||
const { imageSize } = useCatalogStyleStore();
|
||||
const columnWidth = imageSize === 'Large' ? 270 : 180;
|
||||
@@ -345,12 +346,7 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
||||
) : (
|
||||
hasMore && <LoadingEllipsis string={loadingStateString} />
|
||||
)}
|
||||
{error && (
|
||||
<div className='red'>
|
||||
<br />
|
||||
{error.message}
|
||||
</div>
|
||||
)}
|
||||
<ErrorDisplay error={error} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -491,11 +487,21 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
||||
|
||||
useEffect(() => {
|
||||
if (!isVisible) return;
|
||||
let documentTitle = title ? title : shortAddress;
|
||||
if (isInAllView) documentTitle = t('all');
|
||||
else if (isInSubscriptionsView) documentTitle = t('subscriptions');
|
||||
const boardIdentifier = params.boardIdentifier || boardIdentifierProp;
|
||||
const isDirectory = boardIdentifier ? isDirectoryBoard(boardIdentifier, defaultSubplebbits) : false;
|
||||
|
||||
let documentTitle: string;
|
||||
if (isInAllView) {
|
||||
documentTitle = t('all');
|
||||
} else if (isInSubscriptionsView) {
|
||||
documentTitle = t('subscriptions');
|
||||
} else if (isDirectory) {
|
||||
documentTitle = `/${boardIdentifier}/`;
|
||||
} else {
|
||||
documentTitle = title ? title : shortAddress || subplebbitAddress || '';
|
||||
}
|
||||
document.title = documentTitle + ` - ${t('catalog')} - 5chan`;
|
||||
}, [title, shortAddress, isInAllView, isInSubscriptionsView, t, isVisible]);
|
||||
}, [title, shortAddress, subplebbitAddress, isInAllView, isInSubscriptionsView, t, isVisible, params.boardIdentifier, boardIdentifierProp, defaultSubplebbits]);
|
||||
|
||||
// Clear matched filters when component mounts or when subplebbit changes
|
||||
useEffect(() => {
|
||||
|
||||
@@ -86,7 +86,7 @@ const FAQ = () => {
|
||||
What is 5chan?
|
||||
</dt>
|
||||
<dd>
|
||||
5chan is a serverless, adminless, decentralized 4chan alternative where any pleb can create and own unlimited boards. All data comes from the Plebbit
|
||||
5chan is a serverless, adminless, decentralized 4chan alternative where anyone can create and own unlimited boards. All data comes from the Bitsocial
|
||||
protocol, it's all text including links from which media is embedded, shared peer-to-peer. Users do not need to register an account before participating
|
||||
in the community.
|
||||
</dd>
|
||||
@@ -106,8 +106,8 @@ const FAQ = () => {
|
||||
To post as "Anonymous", simply do not fill in the [Name] field when submitting content.
|
||||
<br />
|
||||
<br />
|
||||
5chan uses the Plebbit protocol to function, which does not leak IP addresses of people who post. When you post on 5chan, no board admin can know your
|
||||
IP address, nor can the app itself, which is just static HTML. However, the Plebbit protocol is not fully anonymous, it uses IPFS, which means your IP
|
||||
5chan uses the Bitsocial protocol to function, which does not leak IP addresses of people who post. When you post on 5chan, no board admin can know your
|
||||
IP address, nor can the app itself, which is just static HTML. However, the Bitsocial protocol is not fully anonymous, it uses IPFS, which means your IP
|
||||
address is part of a public P2P swarm, similarly to how BitTorrent works.
|
||||
</dd>
|
||||
<dt id='register'>Can I register a username?</dt>
|
||||
@@ -141,10 +141,10 @@ const FAQ = () => {
|
||||
when submitting content. You can paste any link, not just image links. You can also get an image link from social media, by right clicking the image and
|
||||
selecting "Copy image URL", or by using tools such as <a href='https://cobalt.tools/'>cobalt</a>.<br />
|
||||
<br />
|
||||
The reason why uploading media directly to boards is not possible, is because 5chan is a client for the Plebbit protocol, which is text-only (including
|
||||
links, from which media is embedded by clients). However, Plebbit uses IPFS, so in theory it could let users upload media to the subplebbit (board)
|
||||
owner's IPFS node, and then post the direct IPFS link for the media. This is not enabled because loading media from IPFS is extremely slow at the moment
|
||||
(because most people have slow internet).
|
||||
The reason why uploading media directly to boards is not possible, is because 5chan is a client for the Bitsocial protocol, which is text-only
|
||||
(including links, from which media is embedded by clients). However, Plebbit uses IPFS, so in theory it could let users upload media to the subplebbit
|
||||
(board) owner's IPFS node, and then post the direct IPFS link for the media. This is not enabled because loading media from IPFS is extremely slow at
|
||||
the moment (because most people have slow internet).
|
||||
</dd>
|
||||
<dt id='postimage'>Must I post an image?</dt>
|
||||
<dd>
|
||||
@@ -164,7 +164,7 @@ const FAQ = () => {
|
||||
<br />
|
||||
<br />
|
||||
Unlike 4chan and other imageboards, 5chan does <i>not</i> allow to quote more than one post at a time. You can only reply to one post at a time. This is
|
||||
because 5chan is a client for the Plebbit protocol, which is designed to be an alternative to Reddit-like social media, in which you can only reply to
|
||||
because 5chan is a client for the Bitsocial protocol, which is designed to be an alternative to Reddit-like social media, in which you can only reply to
|
||||
one post at a time.
|
||||
<br />
|
||||
<br />
|
||||
|
||||
@@ -60,7 +60,8 @@ const InfoBox = () => {
|
||||
i18nKey='5chan_description'
|
||||
shouldUnescape={true}
|
||||
components={{
|
||||
1: <Link key='plebbit-link' to='https://plebbit.com' target='_blank' rel='noopener noreferrer' />,
|
||||
1: <Link key='rules-link' to='/rules' />,
|
||||
2: <Link key='faqs-link' to='/faq' />,
|
||||
}}
|
||||
/>
|
||||
<br />
|
||||
@@ -133,6 +134,9 @@ export const Footer = () => {
|
||||
<li>
|
||||
<Link to='/faq'>FAQ</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to='/rules'>Rules</Link>
|
||||
</li>
|
||||
<li>
|
||||
<a href='https://t.me/plebbit' target='_blank' rel='noopener noreferrer'>
|
||||
Telegram
|
||||
@@ -164,7 +168,6 @@ export const Footer = () => {
|
||||
<a href='https://github.com/plebbit/5chan/graphs/contributors' target='_blank' rel='noopener noreferrer'>
|
||||
Contact
|
||||
</a>
|
||||
<span></span>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
|
||||
+29
-8
@@ -2,9 +2,11 @@ import { useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Comment, Role, useComment, useEditedComment, useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
||||
import useSubplebbitsStore from '@plebbit/plebbit-react-hooks/dist/stores/subplebbits';
|
||||
import { useLocation, useParams } from 'react-router-dom';
|
||||
import { useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import { isAllView } from '../../lib/utils/view-utils';
|
||||
import { useResolvedSubplebbitAddress } from '../../hooks/use-resolved-subplebbit-address';
|
||||
import { useDefaultSubplebbits } from '../../hooks/use-default-subplebbits';
|
||||
import { isDirectoryBoard } from '../../lib/utils/route-utils';
|
||||
import useIsMobile from '../../hooks/use-is-mobile';
|
||||
import ErrorDisplay from '../../components/error-display/error-display';
|
||||
import PostDesktop from '../../components/post-desktop';
|
||||
@@ -21,6 +23,7 @@ export interface PostProps {
|
||||
roles?: Role[];
|
||||
showAllReplies?: boolean;
|
||||
showReplies?: boolean;
|
||||
threadNumber?: number;
|
||||
}
|
||||
|
||||
export const Post = ({ post, showAllReplies = false, showReplies = true }: PostProps) => {
|
||||
@@ -57,8 +60,17 @@ const PostPage = () => {
|
||||
const isInAllView = isAllView(location.pathname);
|
||||
|
||||
const comment = useComment({ commentCid });
|
||||
|
||||
const navigate = useNavigate();
|
||||
useEffect(() => {
|
||||
if (comment?.subplebbitAddress && subplebbitAddress && comment.subplebbitAddress !== subplebbitAddress) {
|
||||
navigate('/not-found', { replace: true });
|
||||
}
|
||||
}, [comment?.subplebbitAddress, subplebbitAddress, navigate]);
|
||||
|
||||
const subplebbit = useSubplebbit({ subplebbitAddress });
|
||||
const { shortAddress, title } = subplebbit || {};
|
||||
const defaultSubplebbits = useDefaultSubplebbits();
|
||||
|
||||
// if the comment is a reply, return the post comment instead, then the reply will be highlighted in the thread
|
||||
const postComment = useComment({ commentCid: comment?.postCid });
|
||||
@@ -69,26 +81,35 @@ const PostPage = () => {
|
||||
post = comment;
|
||||
}
|
||||
|
||||
const { error, replyCount } = post || {};
|
||||
const { error } = post || {};
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const boardTitle = title ? title : shortAddress || subplebbitAddress;
|
||||
const boardIdentifier = params.boardIdentifier;
|
||||
const isDirectory = boardIdentifier ? isDirectoryBoard(boardIdentifier, defaultSubplebbits) : false;
|
||||
|
||||
let boardTitle: string;
|
||||
if (isInAllView) {
|
||||
boardTitle = t('all');
|
||||
} else if (isDirectory) {
|
||||
boardTitle = `/${boardIdentifier}/`;
|
||||
} else {
|
||||
boardTitle = title ? title : shortAddress || subplebbitAddress || '';
|
||||
}
|
||||
|
||||
const postTitle = post?.title?.slice(0, 30) || post?.content?.slice(0, 30);
|
||||
const postDucumentTitle = (postTitle ? postTitle.trim() + '... - ' : '') + boardTitle + ' - 5chan';
|
||||
document.title = isInAllView ? `${t('all')} - 5chan` : postDucumentTitle;
|
||||
}, [title, shortAddress, subplebbitAddress, post?.title, post?.content, isInAllView, t]);
|
||||
const postTitlePart = postTitle ? ` - ${postTitle.trim()}...` : '';
|
||||
document.title = `${boardTitle}${postTitlePart} - 5chan`;
|
||||
}, [title, shortAddress, subplebbitAddress, post?.title, post?.content, isInAllView, t, params.boardIdentifier, defaultSubplebbits]);
|
||||
|
||||
// probably not necessary to show the error to the user if the post loaded successfully
|
||||
const shouldShowErrorToUser = post?.error && ((post?.replyCount > 0 && post?.replies?.length === 0) || (post?.state === 'failed' && post?.error));
|
||||
|
||||
return (
|
||||
<div className={styles.content}>
|
||||
{/* TODO: remove this replyCount error once api supports scrolling replies pages */}
|
||||
{replyCount > 60 && <span className={styles.error}>Error: this thread has too many replies, some of them cannot be displayed right now.</span>}
|
||||
{shouldShowErrorToUser && (
|
||||
<div className={styles.error}>
|
||||
<ErrorDisplay error={error} />
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './rules';
|
||||
@@ -0,0 +1,172 @@
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
height: 100%;
|
||||
margin: auto;
|
||||
text-align: left;
|
||||
width: 750px;
|
||||
min-width: 750px;
|
||||
}
|
||||
|
||||
.box {
|
||||
position: relative;
|
||||
margin-bottom: 0.5em;
|
||||
padding-bottom: 0.5em;
|
||||
border: 1px solid;
|
||||
}
|
||||
|
||||
.boxBar {
|
||||
padding-left: 0.5em;
|
||||
line-height: 2em;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.boxBar h2 {
|
||||
font-size: 131%;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.boxContent {
|
||||
line-height: 1.5em;
|
||||
font-size: 93%;
|
||||
padding: 0.5em;
|
||||
padding-top: 0.25em;
|
||||
padding-bottom: 0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.infoBox {
|
||||
background: var(--homepage-box-background-color);
|
||||
color: var(--homepage-infobox-text-color);
|
||||
border: 1px solid var(--homepage-box-border-color);
|
||||
}
|
||||
|
||||
.infoBox .boxBar {
|
||||
background: var(--homepage-box-bar-background-color);
|
||||
color: var(--homepage-box-bar-text-color);
|
||||
}
|
||||
|
||||
.selectorBox {
|
||||
background: #efe;
|
||||
color: #060;
|
||||
}
|
||||
|
||||
.selectorBox .boxBar {
|
||||
background: #9c6;
|
||||
color: #060;
|
||||
}
|
||||
|
||||
.selectorRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.boardSelect {
|
||||
padding: 5px 10px;
|
||||
font-size: 14px;
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 0;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
min-width: 200px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.orSeparator {
|
||||
color: #666;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.customAddressForm {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.addressInput {
|
||||
padding: 5px 10px;
|
||||
font-size: 14px;
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 0;
|
||||
min-width: 200px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.addressInput:focus {
|
||||
border-color: #ea8;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.goButton {
|
||||
padding: 5px 10px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.rulesBox {
|
||||
background: #eff;
|
||||
color: #006;
|
||||
}
|
||||
|
||||
.rulesBox .boxBar {
|
||||
background: #59a;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.box ol {
|
||||
margin: 1em;
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
.box ol li {
|
||||
list-style: decimal outside;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.content {
|
||||
min-width: 0;
|
||||
width: auto;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.selectorRow {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.boardSelect,
|
||||
.addressInput {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.orSeparator {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.customAddressForm {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.goButton {
|
||||
box-sizing: border-box;
|
||||
margin-top: 5px;
|
||||
padding: 5px 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.selectorBoxTitle, .rulesBoxTitle {
|
||||
text-transform: none;
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
import { useEffect, useState, FormEvent } from 'react';
|
||||
import { useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
||||
import { Footer, HomeLogo } from '../home';
|
||||
import { useDefaultSubplebbits, MultisubSubplebbit } from '../../hooks/use-default-subplebbits';
|
||||
import styles from './rules.module.css';
|
||||
|
||||
const getBoardShortCode = (title?: string): string => {
|
||||
if (!title) return '';
|
||||
const match = title.match(/^\/([^/]+)\//);
|
||||
return match ? match[1] : '';
|
||||
};
|
||||
|
||||
const getBoardName = (title?: string): string => {
|
||||
if (!title) return '';
|
||||
const match = title.match(/^\/[^/]+\/\s*-\s*(.+)$/);
|
||||
return match ? match[1] : title;
|
||||
};
|
||||
|
||||
const BoardRulesDisplay = ({ subplebbitAddress, defaultSubplebbits }: { subplebbitAddress: string; defaultSubplebbits: MultisubSubplebbit[] }) => {
|
||||
const subplebbit = useSubplebbit({ subplebbitAddress });
|
||||
const { rules, state, title, shortAddress } = subplebbit || {};
|
||||
|
||||
let loadingText: string | null = null;
|
||||
if (!subplebbit) {
|
||||
loadingText = 'connecting...';
|
||||
} else {
|
||||
switch (state) {
|
||||
case 'fetching-ipns':
|
||||
case 'fetching-ipfs':
|
||||
loadingText = 'loading...';
|
||||
break;
|
||||
case 'failed':
|
||||
loadingText = 'failed to load';
|
||||
break;
|
||||
case 'succeeded':
|
||||
loadingText = null;
|
||||
break;
|
||||
default:
|
||||
loadingText = state ? `${state}...` : 'loading...';
|
||||
}
|
||||
}
|
||||
|
||||
const isLoaded = state === 'succeeded';
|
||||
|
||||
const defaultSub = defaultSubplebbits.find((sub) => sub.address === subplebbitAddress);
|
||||
let displayTitle: string;
|
||||
if (defaultSub?.title) {
|
||||
const shortCode = getBoardShortCode(defaultSub.title);
|
||||
const boardName = getBoardName(defaultSub.title);
|
||||
displayTitle = `Rules for: /${shortCode}/ - ${boardName}`;
|
||||
} else if (title) {
|
||||
const shortCode = getBoardShortCode(title);
|
||||
const boardName = getBoardName(title);
|
||||
if (shortCode && boardName && boardName !== title) {
|
||||
displayTitle = `Rules for: /${shortCode}/ - ${boardName}`;
|
||||
} else {
|
||||
displayTitle = `Rules for: ${shortAddress || subplebbitAddress}`;
|
||||
}
|
||||
} else {
|
||||
displayTitle = `Rules for: ${shortAddress || subplebbitAddress}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`${styles.box} ${styles.rulesBox}`}>
|
||||
<div className={styles.boxBar}>
|
||||
<h2 className={styles.rulesBoxTitle}>{displayTitle}</h2>
|
||||
</div>
|
||||
<div className={styles.boxContent}>
|
||||
{!isLoaded ? (
|
||||
<p>
|
||||
<em>{loadingText}</em>
|
||||
</p>
|
||||
) : rules && rules.length > 0 ? (
|
||||
<ol>
|
||||
{rules.map((rule, index) => (
|
||||
<li key={index}>{rule}</li>
|
||||
))}
|
||||
</ol>
|
||||
) : (
|
||||
<p>
|
||||
<em>This board has no rules set by its owner.</em>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const BoardSelector = ({
|
||||
defaultSubplebbits,
|
||||
selectedAddress,
|
||||
onSelect,
|
||||
}: {
|
||||
defaultSubplebbits: MultisubSubplebbit[];
|
||||
selectedAddress: string;
|
||||
onSelect: (address: string) => void;
|
||||
}) => {
|
||||
const [customAddress, setCustomAddress] = useState('');
|
||||
|
||||
const handleSelectChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const value = e.target.value;
|
||||
if (value) {
|
||||
onSelect(value);
|
||||
setCustomAddress('');
|
||||
}
|
||||
};
|
||||
|
||||
const handleCustomSubmit = (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (customAddress.trim()) {
|
||||
onSelect(customAddress.trim());
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`${styles.box} ${styles.selectorBox}`}>
|
||||
<div className={styles.boxBar}>
|
||||
<h2 className={styles.selectorBoxTitle}>Load rules from a board</h2>
|
||||
</div>
|
||||
<div className={styles.boxContent}>
|
||||
<div className={styles.selectorRow}>
|
||||
<select value={selectedAddress} onChange={handleSelectChange} className={styles.boardSelect}>
|
||||
<option value=''>Select board...</option>
|
||||
{defaultSubplebbits.map((sub) => {
|
||||
const shortCode = getBoardShortCode(sub.title);
|
||||
const boardName = getBoardName(sub.title);
|
||||
return (
|
||||
<option key={sub.address} value={sub.address}>
|
||||
/{shortCode}/ - {boardName}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
<span className={styles.orSeparator}>or</span>
|
||||
<form onSubmit={handleCustomSubmit} className={styles.customAddressForm}>
|
||||
<input
|
||||
type='text'
|
||||
placeholder='enter board address'
|
||||
value={customAddress}
|
||||
onChange={(e) => setCustomAddress(e.target.value)}
|
||||
className={styles.addressInput}
|
||||
/>
|
||||
<button type='submit' className={styles.goButton}>
|
||||
Go
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Rules = () => {
|
||||
const defaultSubplebbits = useDefaultSubplebbits();
|
||||
const [selectedAddress, setSelectedAddress] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
document.title = 'Rules - 5chan';
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.content}>
|
||||
<HomeLogo />
|
||||
<div className={`${styles.box} ${styles.infoBox}`}>
|
||||
<div className={styles.boxBar}>
|
||||
<h2>Rules</h2>
|
||||
</div>
|
||||
<div className={styles.boxContent}>
|
||||
5chan is a serverless and adminless tool to browse and post to decentralized imageboards. 5chan does not have global rules or moderators. Each board is
|
||||
independently owned and moderated, with its own set of rules determined by the board owner.
|
||||
<br />
|
||||
<br />
|
||||
Please read and respect the rules of whatever board you decide to post to.
|
||||
</div>
|
||||
</div>
|
||||
<BoardSelector defaultSubplebbits={defaultSubplebbits} selectedAddress={selectedAddress} onSelect={setSelectedAddress} />
|
||||
{selectedAddress && <BoardRulesDisplay subplebbitAddress={selectedAddress} defaultSubplebbits={defaultSubplebbits} />}
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Rules;
|
||||
@@ -5309,6 +5309,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.11.tgz#b79de2d67389734c57c52595f7a7305e30c2d608"
|
||||
integrity sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==
|
||||
|
||||
"@xstate/solid@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@xstate/solid/-/solid-2.0.0.tgz#22d21610ad3edb663bc0d05e7695b836a89f6b06"
|
||||
integrity sha512-ycGMMJWPtfnc+CGhUxxe664wTVr4M2NXEJx4EsShbcT5G0d2bP378AtAsVvVhSjr6W5+7Br6htDivND1I6DOfQ==
|
||||
|
||||
JSONStream@^1.3.5:
|
||||
version "1.3.5"
|
||||
resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0"
|
||||
@@ -5811,6 +5816,13 @@ bippy@^0.3.8:
|
||||
dependencies:
|
||||
"@types/react-reconciler" "^0.28.9"
|
||||
|
||||
bippy@^0.5.27:
|
||||
version "0.5.27"
|
||||
resolved "https://registry.yarnpkg.com/bippy/-/bippy-0.5.27.tgz#030dc48cda43e7e5c05857a8ce97dea5da3d9045"
|
||||
integrity sha512-LvGYTQm5cMqEhnUT2tr6mSE1mrhmX/xxu3jiypHAscThhxC+9SWdaQW8Nye5CT/sR79LObNJlQpOUgi4DopTMg==
|
||||
dependencies:
|
||||
"@types/react-reconciler" "^0.28.9"
|
||||
|
||||
bl@^1.0.0:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.3.tgz#1e8dd80142eac80d7158c9dccc047fb620e035e7"
|
||||
@@ -7041,6 +7053,11 @@ csstype@^3.0.2:
|
||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
|
||||
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
|
||||
|
||||
csstype@^3.1.0:
|
||||
version "3.2.3"
|
||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a"
|
||||
integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==
|
||||
|
||||
cz-conventional-changelog@3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/cz-conventional-changelog/-/cz-conventional-changelog-3.3.0.tgz#9246947c90404149b3fe2cf7ee91acad3b7d22d2"
|
||||
@@ -13057,6 +13074,16 @@ react-dom@19.1.2:
|
||||
dependencies:
|
||||
scheduler "^0.26.0"
|
||||
|
||||
react-grab@0.0.94:
|
||||
version "0.0.94"
|
||||
resolved "https://registry.yarnpkg.com/react-grab/-/react-grab-0.0.94.tgz#3844eeba0f36faf24d6cfeae86ee4fac240ef204"
|
||||
integrity sha512-C/4w8G5/AgCRfZB6sHmxemkGIReUmtFPjcB43uLv3zSJTvnOrcx+ELnZlv1uZ+OTck2sO4Jf4JSintgbWUXxLA==
|
||||
dependencies:
|
||||
"@xstate/solid" "^2.0.0"
|
||||
bippy "^0.5.27"
|
||||
solid-js "^1.9.10"
|
||||
xstate "^5.25.0"
|
||||
|
||||
react-i18next@13.2.2:
|
||||
version "13.2.2"
|
||||
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-13.2.2.tgz#b1e78ed66a54f4bc819616f68b98221e1b1a1936"
|
||||
@@ -13738,6 +13765,16 @@ serialize-javascript@^6.0.1:
|
||||
dependencies:
|
||||
randombytes "^2.1.0"
|
||||
|
||||
seroval-plugins@~1.3.0:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/seroval-plugins/-/seroval-plugins-1.3.3.tgz#51bcacf09e5384080d7ea4002b08fd9f6166daf5"
|
||||
integrity sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==
|
||||
|
||||
seroval@~1.3.0:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/seroval/-/seroval-1.3.2.tgz#7e5be0dc1a3de020800ef013ceae3a313f20eca7"
|
||||
integrity sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==
|
||||
|
||||
set-blocking@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
|
||||
@@ -14001,6 +14038,15 @@ socks@^2.6.2:
|
||||
ip-address "^10.0.1"
|
||||
smart-buffer "^4.2.0"
|
||||
|
||||
solid-js@^1.9.10:
|
||||
version "1.9.10"
|
||||
resolved "https://registry.yarnpkg.com/solid-js/-/solid-js-1.9.10.tgz#95e57b004f3788d38f19e3b47101e90a9ab9dd0f"
|
||||
integrity sha512-Coz956cos/EPDlhs6+jsdTxKuJDPT7B5SVIWgABwROyxjY7Xbr8wkzD68Et+NxnV7DLJ3nJdAC2r9InuV/4Jew==
|
||||
dependencies:
|
||||
csstype "^3.1.0"
|
||||
seroval "~1.3.0"
|
||||
seroval-plugins "~1.3.0"
|
||||
|
||||
sort-keys-length@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/sort-keys-length/-/sort-keys-length-1.0.1.tgz#9cb6f4f4e9e48155a6aa0671edd336ff1479a188"
|
||||
@@ -15930,6 +15976,11 @@ xmlchars@^2.2.0:
|
||||
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
|
||||
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
|
||||
|
||||
xstate@^5.25.0:
|
||||
version "5.25.0"
|
||||
resolved "https://registry.yarnpkg.com/xstate/-/xstate-5.25.0.tgz#34b36c4843a9db445ed608f0e8caf8c381d5bce2"
|
||||
integrity sha512-yyWzfhVRoTHNLjLoMmdwZGagAYfmnzpm9gPjlX2MhJZsDojXGqRxODDOi4BsgGRKD46NZRAdcLp6CKOyvQS0Bw==
|
||||
|
||||
xtend@^4.0.0, xtend@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||
|
||||
Reference in New Issue
Block a user