mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "بيانات الحساب الكاملة",
|
||||
"this_account": "هذا الحساب",
|
||||
"delete": "حذف",
|
||||
"delete_account": "حذف الحساب"
|
||||
"delete_account": "حذف الحساب",
|
||||
"file_type": "نوع الملف",
|
||||
"version": "الإصدار",
|
||||
"spoiler": "تحذير",
|
||||
"interface": "واجهة",
|
||||
"invalid_url": "رابط غير صالح",
|
||||
"no_link_provided": "لم يتم توفير رابط",
|
||||
"image_search": "البحث عن صورة",
|
||||
"view_on": "عرض على",
|
||||
"copy_link": "نسخ الرابط",
|
||||
"hide_post": "إخفاء المنشور",
|
||||
"hide_thread": "إخفاء الموضوع",
|
||||
"unhide_post": "إظهار المنشور",
|
||||
"unhide_thread": "إظهار الموضوع",
|
||||
"search_image_on_google": "البحث عن صورة على جوجل",
|
||||
"search_image_on_yandex": "البحث عن صورة على Yandex",
|
||||
"search_image_on_saucenao": "البحث عن صورة على SauceNAO",
|
||||
"view_on_client": "عرض على {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "সম্পূর্ণ অ্যাকাউন্ট ডেটা",
|
||||
"this_account": "এই অ্যাকাউন্ট",
|
||||
"delete": "মুছে ফেলা",
|
||||
"delete_account": "অ্যাকাউন্ট মুছুন"
|
||||
"delete_account": "অ্যাকাউন্ট মুছুন",
|
||||
"file_type": "ফাইলের ধরন",
|
||||
"version": "সংস্করণ",
|
||||
"spoiler": "স্পোয়েলার",
|
||||
"interface": "ইন্টারফেস",
|
||||
"invalid_url": "অবৈধ ইউআরএল",
|
||||
"no_link_provided": "কোনো লিঙ্ক উপস্থিত নেই",
|
||||
"image_search": "চিত্র অনুসন্ধান",
|
||||
"view_on": "উপর দেখুন",
|
||||
"copy_link": "লিংক অনুলিপি করুন",
|
||||
"hide_post": "পোস্ট লুকাও",
|
||||
"hide_thread": "থ্রেড লুকাও",
|
||||
"unhide_post": "পোস্ট প্রদর্শন করুন",
|
||||
"unhide_thread": "থ্রেড প্রদর্শন করুন",
|
||||
"search_image_on_google": "গুগলে চিত্র অনুসন্ধান করুন",
|
||||
"search_image_on_yandex": "Yandexে চিত্র অনুসন্ধান করুন",
|
||||
"search_image_on_saucenao": "SauceNAO এ চিত্র অনুসন্ধান করুন",
|
||||
"view_on_client": "{{client}} উপর দেখুন"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "plná účetní data",
|
||||
"this_account": "tento účet",
|
||||
"delete": "smazat",
|
||||
"delete_account": "smazat účet"
|
||||
"delete_account": "smazat účet",
|
||||
"file_type": "Typ souboru",
|
||||
"version": "Verze",
|
||||
"spoiler": "spoiler",
|
||||
"interface": "rozhraní",
|
||||
"invalid_url": "Neplatná adresa URL",
|
||||
"no_link_provided": "Není poskytnut žádný odkaz",
|
||||
"image_search": "hledání obrázku",
|
||||
"view_on": "zobrazit na",
|
||||
"copy_link": "kopírovat odkaz",
|
||||
"hide_post": "skrýt příspěvek",
|
||||
"hide_thread": "skrýt vlákno",
|
||||
"unhide_post": "zobrazit příspěvek",
|
||||
"unhide_thread": "zobrazit vlákno",
|
||||
"search_image_on_google": "hledat obrázek na Googlu",
|
||||
"search_image_on_yandex": "hledat obrázek na Yandexu",
|
||||
"search_image_on_saucenao": "hledat obrázek na SauceNAO",
|
||||
"view_on_client": "Zobrazit na {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "fulde kontodata",
|
||||
"this_account": "denne konto",
|
||||
"delete": "slet",
|
||||
"delete_account": "slet konto"
|
||||
"delete_account": "slet konto",
|
||||
"file_type": "Filtype",
|
||||
"version": "Version",
|
||||
"spoiler": "spoiler",
|
||||
"interface": "interface",
|
||||
"invalid_url": "Ugyldig webadresse",
|
||||
"no_link_provided": "Ingen link angivet",
|
||||
"image_search": "billedsøgning",
|
||||
"view_on": "se på",
|
||||
"copy_link": "kopier link",
|
||||
"hide_post": "skjul post",
|
||||
"hide_thread": "skjul tråd",
|
||||
"unhide_post": "vis indlæg",
|
||||
"unhide_thread": "vis tråd",
|
||||
"search_image_on_google": "søg billede på Google",
|
||||
"search_image_on_yandex": "søg billede på Yandex",
|
||||
"search_image_on_saucenao": "søg billede på SauceNAO",
|
||||
"view_on_client": "Se på {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "vollständige Kontodaten",
|
||||
"this_account": "dieses Konto",
|
||||
"delete": "löschen",
|
||||
"delete_account": "Konto löschen"
|
||||
"delete_account": "Konto löschen",
|
||||
"file_type": "Dateityp",
|
||||
"version": "Version",
|
||||
"spoiler": "Spoiler",
|
||||
"interface": "Schnittstelle",
|
||||
"invalid_url": "Ungültige URL",
|
||||
"no_link_provided": "Kein Link angegeben",
|
||||
"image_search": "Bildersuche",
|
||||
"view_on": "anzeigen auf",
|
||||
"copy_link": "Link kopieren",
|
||||
"hide_post": "Beitrag ausblenden",
|
||||
"hide_thread": "Thread ausblenden",
|
||||
"unhide_post": "Beitrag anzeigen",
|
||||
"unhide_thread": "Thread anzeigen",
|
||||
"search_image_on_google": "Bild auf Google suchen",
|
||||
"search_image_on_yandex": "Bild auf Yandex suchen",
|
||||
"search_image_on_saucenao": "Bild auf SauceNAO suchen",
|
||||
"view_on_client": "Anzeigen auf {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "πλήρα δεδομένα λογαριασμού",
|
||||
"this_account": "αυτός ο λογαριασμός",
|
||||
"delete": "διαγραφή",
|
||||
"delete_account": "διαγραφή λογαριασμού"
|
||||
"delete_account": "διαγραφή λογαριασμού",
|
||||
"file_type": "Τύπος αρχείου",
|
||||
"version": "Έκδοση",
|
||||
"spoiler": "spoiler",
|
||||
"interface": "διεπαφή",
|
||||
"invalid_url": "Μη έγκυρη διεύθυνση URL",
|
||||
"no_link_provided": "Δεν παρέχεται σύνδεσμος",
|
||||
"image_search": "αναζήτηση εικόνων",
|
||||
"view_on": "προβολή σε",
|
||||
"copy_link": "αντιγραφή συνδέσμου",
|
||||
"hide_post": "απόκρυψη ανάρτησης",
|
||||
"hide_thread": "απόκρυψη νήματος",
|
||||
"unhide_post": "εμφάνιση ανάρτησης",
|
||||
"unhide_thread": "εμφάνιση νήματος",
|
||||
"search_image_on_google": "αναζήτηση εικόνας στο Google",
|
||||
"search_image_on_yandex": "αναζήτηση εικόνας στο Yandex",
|
||||
"search_image_on_saucenao": "αναζήτηση εικόνας στο SauceNAO",
|
||||
"view_on_client": "Προβολή στο {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "full account data",
|
||||
"this_account": "this account",
|
||||
"delete": "delete",
|
||||
"delete_account": "delete account"
|
||||
"delete_account": "delete account",
|
||||
"file_type": "File type",
|
||||
"version": "Version",
|
||||
"spoiler": "spoiler",
|
||||
"interface": "interface",
|
||||
"invalid_url": "Invalid URL",
|
||||
"no_link_provided": "No link provided",
|
||||
"image_search": "image search",
|
||||
"view_on": "view on",
|
||||
"copy_link": "Copy link",
|
||||
"hide_post": "Hide post",
|
||||
"hide_thread": "Hide thread",
|
||||
"unhide_post": "Unhide post",
|
||||
"unhide_thread": "Unhide thread",
|
||||
"search_image_on_google": "Search image on Google",
|
||||
"search_image_on_yandex": "Search image on Yandex",
|
||||
"search_image_on_saucenao": "Search image on SauceNAO",
|
||||
"view_on_client": "View on {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "datos completos de la cuenta",
|
||||
"this_account": "esta cuenta",
|
||||
"delete": "eliminar",
|
||||
"delete_account": "eliminar cuenta"
|
||||
"delete_account": "eliminar cuenta",
|
||||
"file_type": "Tipo de archivo",
|
||||
"version": "Versión",
|
||||
"spoiler": "spoiler",
|
||||
"interface": "interfaz",
|
||||
"invalid_url": "URL no válida",
|
||||
"no_link_provided": "No se proporcionó ningún enlace",
|
||||
"image_search": "búsqueda de imágenes",
|
||||
"view_on": "ver en",
|
||||
"copy_link": "Copiar enlace",
|
||||
"hide_post": "Ocultar publicación",
|
||||
"hide_thread": "Ocultar hilo",
|
||||
"unhide_post": "Mostrar publicación",
|
||||
"unhide_thread": "Mostrar hilo",
|
||||
"search_image_on_google": "Buscar imagen en Google",
|
||||
"search_image_on_yandex": "Buscar imagen en Yandex",
|
||||
"search_image_on_saucenao": "Buscar imagen en SauceNAO",
|
||||
"view_on_client": "Ver en {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "داده حساب کامل",
|
||||
"this_account": "این حساب",
|
||||
"delete": "حذف کنید",
|
||||
"delete_account": "حذف حساب"
|
||||
"delete_account": "حذف حساب",
|
||||
"file_type": "نوع فایل",
|
||||
"version": "نسخه",
|
||||
"spoiler": "اسپویلر",
|
||||
"interface": "رابط",
|
||||
"invalid_url": "آدرس اینترنتی نامعتبر است",
|
||||
"no_link_provided": "لینکی ارائه نشده است",
|
||||
"image_search": "جستجوی تصویر",
|
||||
"view_on": "نمایش در",
|
||||
"copy_link": "رونوشت لینک",
|
||||
"hide_post": "پنهان کردن پست",
|
||||
"hide_thread": "پنهان کردن موضوع",
|
||||
"unhide_post": "نمایش پست",
|
||||
"unhide_thread": "نمایش موضوع",
|
||||
"search_image_on_google": "جستجوی تصویر در گوگل",
|
||||
"search_image_on_yandex": "جستجوی تصویر در Yandex",
|
||||
"search_image_on_saucenao": "جستجوی تصویر در SauceNAO",
|
||||
"view_on_client": "مشاهده در {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "täydelliset tilitiedot",
|
||||
"this_account": "tämä tili",
|
||||
"delete": "poista",
|
||||
"delete_account": "poista tili"
|
||||
"delete_account": "poista tili",
|
||||
"file_type": "Tiedostotyyppi",
|
||||
"version": "Versio",
|
||||
"spoiler": "spoiler",
|
||||
"interface": "liitäntä",
|
||||
"invalid_url": "Virheellinen URL-osoite",
|
||||
"no_link_provided": "Ei linkkiä annettu",
|
||||
"image_search": "kuvahaku",
|
||||
"view_on": "näytä",
|
||||
"copy_link": "kopioi linkki",
|
||||
"hide_post": "piilota viesti",
|
||||
"hide_thread": "piilota keskustelu",
|
||||
"unhide_post": "näytä viesti",
|
||||
"unhide_thread": "näytä keskustelu",
|
||||
"search_image_on_google": "etsi kuva Googlesta",
|
||||
"search_image_on_yandex": "etsi kuva Yandexistä",
|
||||
"search_image_on_saucenao": "etsi kuva SauceNAO: sta",
|
||||
"view_on_client": "Näytä {{client}}: ssä"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "buong account data",
|
||||
"this_account": "ang account na ito",
|
||||
"delete": "burahin",
|
||||
"delete_account": "tanggalin ang account"
|
||||
"delete_account": "tanggalin ang account",
|
||||
"file_type": "Uri ng file",
|
||||
"version": "Bersyon",
|
||||
"spoiler": "spoiler",
|
||||
"interface": "interface",
|
||||
"invalid_url": "Hindi wastong URL",
|
||||
"no_link_provided": "Walang link na ibinigay",
|
||||
"image_search": "paghahanap ng larawan",
|
||||
"view_on": "tingnan sa",
|
||||
"copy_link": "kopyahin ang link",
|
||||
"hide_post": "itago ang post",
|
||||
"hide_thread": "itago ang thread",
|
||||
"unhide_post": "ipakita ang post",
|
||||
"unhide_thread": "ipakita ang thread",
|
||||
"search_image_on_google": "maghanap ng larawan sa Google",
|
||||
"search_image_on_yandex": "maghanap ng larawan sa Yandex",
|
||||
"search_image_on_saucenao": "maghanap ng larawan sa SauceNAO",
|
||||
"view_on_client": "Tingnan sa {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "données complètes du compte",
|
||||
"this_account": "ce compte",
|
||||
"delete": "supprimer",
|
||||
"delete_account": "supprimer le compte"
|
||||
"delete_account": "supprimer le compte",
|
||||
"file_type": "Type de fichier",
|
||||
"version": "Version",
|
||||
"spoiler": "spoiler",
|
||||
"interface": "interface",
|
||||
"invalid_url": "URL non valide",
|
||||
"no_link_provided": "Aucun lien fourni",
|
||||
"image_search": "recherche d'images",
|
||||
"view_on": "voir sur",
|
||||
"copy_link": "Copier le lien",
|
||||
"hide_post": "Masquer la publication",
|
||||
"hide_thread": "Masquer le fil",
|
||||
"unhide_post": "Afficher la publication",
|
||||
"unhide_thread": "Afficher le fil",
|
||||
"search_image_on_google": "Rechercher une image sur Google",
|
||||
"search_image_on_yandex": "Rechercher une image sur Yandex",
|
||||
"search_image_on_saucenao": "Rechercher une image sur SauceNAO",
|
||||
"view_on_client": "Voir sur {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "נתוני החשבון המלאים",
|
||||
"this_account": "החשבון הזה",
|
||||
"delete": "מחק",
|
||||
"delete_account": "מחק חשבון"
|
||||
"delete_account": "מחק חשבון",
|
||||
"file_type": "סוג הקובץ",
|
||||
"version": "גרסה",
|
||||
"spoiler": "ספוילר",
|
||||
"interface": "ממשק",
|
||||
"invalid_url": "כתובת אינטרנט לא תקינה",
|
||||
"no_link_provided": "לא סופק קישור",
|
||||
"image_search": "חיפוש תמונות",
|
||||
"view_on": "צפה ב",
|
||||
"copy_link": "העתק קישור",
|
||||
"hide_post": "הסתר פוסט",
|
||||
"hide_thread": "הסתר ת’רד",
|
||||
"unhide_post": "הצג פוסט",
|
||||
"unhide_thread": "הצג ת’רד",
|
||||
"search_image_on_google": "חפש תמונה ב-Google",
|
||||
"search_image_on_yandex": "חפש תמונה ב-Yandex",
|
||||
"search_image_on_saucenao": "חפש תמונה ב- SauceNAO",
|
||||
"view_on_client": "הצג ב {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "पूरे खाते का डेटा",
|
||||
"this_account": "इस खाता",
|
||||
"delete": "हटाएँ",
|
||||
"delete_account": "खाता हटाएं"
|
||||
"delete_account": "खाता हटाएं",
|
||||
"file_type": "फ़ाइल का प्रकार",
|
||||
"version": "संस्करण",
|
||||
"spoiler": "स्पॉइलर",
|
||||
"interface": "इंटरफेस",
|
||||
"invalid_url": "अमान्य URL",
|
||||
"no_link_provided": "कोई लिंक नहीं दिया गया है",
|
||||
"image_search": "चित्र खोज",
|
||||
"view_on": "पर देखें",
|
||||
"copy_link": "लिंक कॉपी करें",
|
||||
"hide_post": "पोस्ट छुपाएं",
|
||||
"hide_thread": "धागा छुपाएं",
|
||||
"unhide_post": "पोस्ट प्रदर्शित करें",
|
||||
"unhide_thread": "धागा प्रदर्शित करें",
|
||||
"search_image_on_google": "गूगल पर छवि खोजें",
|
||||
"search_image_on_yandex": "Yandex पर छवि खोजें",
|
||||
"search_image_on_saucenao": "SauceNAO पर छवि खोजें",
|
||||
"view_on_client": "{{client}} पर देखें"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "teljes fiókadata",
|
||||
"this_account": "ez a fiók",
|
||||
"delete": "töröl",
|
||||
"delete_account": "fiók törlése"
|
||||
"delete_account": "fiók törlése",
|
||||
"file_type": "Fájltípus",
|
||||
"version": "Verzió",
|
||||
"spoiler": "spoiler",
|
||||
"interface": "felület",
|
||||
"invalid_url": "Érvénytelen URL-cím",
|
||||
"no_link_provided": "Nincs megadva link",
|
||||
"image_search": "képkeresés",
|
||||
"view_on": "nézet",
|
||||
"copy_link": "link másolása",
|
||||
"hide_post": "bejegyzés elrejtése",
|
||||
"hide_thread": "szál elrejtése",
|
||||
"unhide_post": "bejegyzés megjelenítése",
|
||||
"unhide_thread": "szál megjelenítése",
|
||||
"search_image_on_google": "keresés kép a Google-ben",
|
||||
"search_image_on_yandex": "keresés kép a Yandex-ben",
|
||||
"search_image_on_saucenao": "keresés kép a SauceNAO-n",
|
||||
"view_on_client": "Megtekintés itt: {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "data akun lengkap",
|
||||
"this_account": "akun ini",
|
||||
"delete": "hapus",
|
||||
"delete_account": "hapus akun"
|
||||
"delete_account": "hapus akun",
|
||||
"file_type": "Tipe file",
|
||||
"version": "Versi",
|
||||
"spoiler": "spoiler",
|
||||
"interface": "antarmuka",
|
||||
"invalid_url": "URL tidak valid",
|
||||
"no_link_provided": "Tidak ada tautan yang diberikan",
|
||||
"image_search": "pencarian gambar",
|
||||
"view_on": "lihat di",
|
||||
"copy_link": "salin tautan",
|
||||
"hide_post": "sembunyikan pos",
|
||||
"hide_thread": "sembunyikan utas",
|
||||
"unhide_post": "tampilkan pos",
|
||||
"unhide_thread": "tampilkan utas",
|
||||
"search_image_on_google": "cari gambar di Google",
|
||||
"search_image_on_yandex": "cari gambar di Yandex",
|
||||
"search_image_on_saucenao": "cari gambar di SauceNAO",
|
||||
"view_on_client": "Lihat di {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "i dati completi dell'account",
|
||||
"this_account": "questo account",
|
||||
"delete": "elimina",
|
||||
"delete_account": "elimina account"
|
||||
"delete_account": "elimina account",
|
||||
"file_type": "Tipo di file",
|
||||
"version": "Versione",
|
||||
"spoiler": "spoiler",
|
||||
"interface": "interfaccia",
|
||||
"invalid_url": "URL non valido",
|
||||
"no_link_provided": "Nessun link fornito",
|
||||
"image_search": "ricerca immagini",
|
||||
"view_on": "Vedi su",
|
||||
"copy_link": "Copia link",
|
||||
"hide_post": "Nascondi post",
|
||||
"hide_thread": "Nascondi thread",
|
||||
"unhide_post": "Mostra post",
|
||||
"unhide_thread": "Mostra thread",
|
||||
"search_image_on_google": "Cerca immagine su Google",
|
||||
"search_image_on_yandex": "Cerca immagine su Yandex",
|
||||
"search_image_on_saucenao": "Cerca immagine su SauceNAO",
|
||||
"view_on_client": "Vedi su {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "フルアカウントデータ",
|
||||
"this_account": "このアカウント",
|
||||
"delete": "削除",
|
||||
"delete_account": "アカウントを削除"
|
||||
"delete_account": "アカウントを削除",
|
||||
"file_type": "ファイルタイプ",
|
||||
"version": "バージョン",
|
||||
"spoiler": "ネタバレ",
|
||||
"interface": "インターフェース",
|
||||
"invalid_url": "無効なURL",
|
||||
"no_link_provided": "リンクが指定されていません",
|
||||
"image_search": "画像検索",
|
||||
"view_on": "で表示",
|
||||
"copy_link": "リンクをコピー",
|
||||
"hide_post": "投稿を非表示",
|
||||
"hide_thread": "スレッドを非表示",
|
||||
"unhide_post": "投稿を表示",
|
||||
"unhide_thread": "スレッドを表示",
|
||||
"search_image_on_google": "Google で画像を検索",
|
||||
"search_image_on_yandex": "Yandex で画像を検索",
|
||||
"search_image_on_saucenao": "SauceNAO で画像を検索",
|
||||
"view_on_client": "{{client}} で表示"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "전체 계정 데이터",
|
||||
"this_account": "이 계정",
|
||||
"delete": "삭제",
|
||||
"delete_account": "계정 삭제"
|
||||
"delete_account": "계정 삭제",
|
||||
"file_type": "파일 유형",
|
||||
"version": "버전",
|
||||
"spoiler": "스포일러",
|
||||
"interface": "인터페이스",
|
||||
"invalid_url": "잘못된 URL",
|
||||
"no_link_provided": "링크가 제공되지 않았습니다",
|
||||
"image_search": "이미지 검색",
|
||||
"view_on": "보기",
|
||||
"copy_link": "링크 복사",
|
||||
"hide_post": "게시물 숨기기",
|
||||
"hide_thread": "스레드 숨기기",
|
||||
"unhide_post": "게시물 숨기기 해제",
|
||||
"unhide_thread": "스레드 숨기기 해제",
|
||||
"search_image_on_google": "Google 이미지 검색",
|
||||
"search_image_on_yandex": "Yandex 이미지 검색",
|
||||
"search_image_on_saucenao": "SauceNAO 이미지 검색",
|
||||
"view_on_client": "{{client}}에서 보기"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "पूर्ण खाते डेटा",
|
||||
"this_account": "हे खाते",
|
||||
"delete": "काढा",
|
||||
"delete_account": "खाते हटवा"
|
||||
"delete_account": "खाते हटवा",
|
||||
"file_type": "फाइल प्रकार",
|
||||
"version": "आवृत्ती",
|
||||
"spoiler": "स्पॉइलर",
|
||||
"interface": "इंटरफेस",
|
||||
"invalid_url": "अवैध URL",
|
||||
"no_link_provided": "कोणतेही लिंक दिले नाहीत",
|
||||
"image_search": "प्रतिमा शोध",
|
||||
"view_on": "पाहा",
|
||||
"copy_link": "लिंक कॉपी करा",
|
||||
"hide_post": "पोस्ट लपवा",
|
||||
"hide_thread": "थ्रेड लपवा",
|
||||
"unhide_post": "पोस्ट दाखवा",
|
||||
"unhide_thread": "थ्रेड दाखवा",
|
||||
"search_image_on_google": "Google वर छायाचित्र शोधा",
|
||||
"search_image_on_yandex": "Yandex वर छायाचित्र शोधा",
|
||||
"search_image_on_saucenao": "SauceNAO वर छायाचित्र शोधा",
|
||||
"view_on_client": "{{client}} वर पहा"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "volledige accountgegevens",
|
||||
"this_account": "dit account",
|
||||
"delete": "verwijderen",
|
||||
"delete_account": "account verwijderen"
|
||||
"delete_account": "account verwijderen",
|
||||
"file_type": "Bestandstype",
|
||||
"version": "Versie",
|
||||
"spoiler": "spoiler",
|
||||
"interface": "interface",
|
||||
"invalid_url": "Ongeldige URL",
|
||||
"no_link_provided": "Geen link opgegeven",
|
||||
"image_search": "afbeeldingen zoeken",
|
||||
"view_on": "bekijk op",
|
||||
"copy_link": "link kopiëren",
|
||||
"hide_post": "bericht verbergen",
|
||||
"hide_thread": "thread verbergen",
|
||||
"unhide_post": "bericht weergeven",
|
||||
"unhide_thread": "thread weergeven",
|
||||
"search_image_on_google": "zoek afbeelding op Google",
|
||||
"search_image_on_yandex": "zoek afbeelding op Yandex",
|
||||
"search_image_on_saucenao": "zoek afbeelding op SauceNAO",
|
||||
"view_on_client": "Bekijk op {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "full kontodata",
|
||||
"this_account": "denne kontoen",
|
||||
"delete": "slett",
|
||||
"delete_account": "slett konto"
|
||||
"delete_account": "slett konto",
|
||||
"file_type": "Filtype",
|
||||
"version": "Versjon",
|
||||
"spoiler": "spoiler",
|
||||
"interface": "grensesnitt",
|
||||
"invalid_url": "Ugyldig URL",
|
||||
"no_link_provided": "Ingen lenke oppgitt",
|
||||
"image_search": "bilde søk",
|
||||
"view_on": "se på",
|
||||
"copy_link": "kopier lenke",
|
||||
"hide_post": "skjul innlegg",
|
||||
"hide_thread": "skjul tråd",
|
||||
"unhide_post": "vis innlegg",
|
||||
"unhide_thread": "vis tråd",
|
||||
"search_image_on_google": "søk etter bilde på Google",
|
||||
"search_image_on_yandex": "søk etter bilde på Yandex",
|
||||
"search_image_on_saucenao": "søk etter bilde på SauceNAO",
|
||||
"view_on_client": "Vis på {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "pełne dane konta",
|
||||
"this_account": "ten rachunek",
|
||||
"delete": "usuń",
|
||||
"delete_account": "usuń konto"
|
||||
"delete_account": "usuń konto",
|
||||
"file_type": "Typ pliku",
|
||||
"version": "Wersja",
|
||||
"spoiler": "spoiler",
|
||||
"interface": "interfejs",
|
||||
"invalid_url": "Nieprawidłowy adres URL",
|
||||
"no_link_provided": "Nie podano linku",
|
||||
"image_search": "wyszukiwanie obrazów",
|
||||
"view_on": "zobacz na",
|
||||
"copy_link": "skopiuj link",
|
||||
"hide_post": "ukryj post",
|
||||
"hide_thread": "ukryj wątek",
|
||||
"unhide_post": "pokaż post",
|
||||
"unhide_thread": "pokaż wątek",
|
||||
"search_image_on_google": "szukaj obrazu w Google",
|
||||
"search_image_on_yandex": "szukaj obrazu w Yandex",
|
||||
"search_image_on_saucenao": "szukaj obrazu w SauceNAO",
|
||||
"view_on_client": "Zobacz na {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "dados completos da conta",
|
||||
"this_account": "esta conta",
|
||||
"delete": "apagar",
|
||||
"delete_account": "excluir conta"
|
||||
"delete_account": "excluir conta",
|
||||
"file_type": "Tipo de arquivo",
|
||||
"version": "Versão",
|
||||
"spoiler": "spoiler",
|
||||
"interface": "interface",
|
||||
"invalid_url": "URL inválida",
|
||||
"no_link_provided": "Nenhum link fornecido",
|
||||
"image_search": "pesquisa de imagens",
|
||||
"view_on": "ver em",
|
||||
"copy_link": "Copiar link",
|
||||
"hide_post": "Ocultar postagem",
|
||||
"hide_thread": "Ocultar tópico",
|
||||
"unhide_post": "Mostrar postagem",
|
||||
"unhide_thread": "Mostrar tópico",
|
||||
"search_image_on_google": "Pesquisar imagem no Google",
|
||||
"search_image_on_yandex": "Pesquisar imagem no Yandex",
|
||||
"search_image_on_saucenao": "Pesquisar imagem no SauceNAO",
|
||||
"view_on_client": "Ver no {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "date complete ale contului",
|
||||
"this_account": "acest cont",
|
||||
"delete": "ștergeți",
|
||||
"delete_account": "șterge contul"
|
||||
"delete_account": "șterge contul",
|
||||
"file_type": "Tip de fișier",
|
||||
"version": "Versiune",
|
||||
"spoiler": "spoiler",
|
||||
"interface": "interfață",
|
||||
"invalid_url": "URL invalid",
|
||||
"no_link_provided": "Nu s-a furnizat niciun link",
|
||||
"image_search": "căutare de imagini",
|
||||
"view_on": "vezi pe",
|
||||
"copy_link": "copiați linkul",
|
||||
"hide_post": "ascundeți postarea",
|
||||
"hide_thread": "ascundeți firul",
|
||||
"unhide_post": "afișează postarea",
|
||||
"unhide_thread": "afișează firul",
|
||||
"search_image_on_google": "căutați imaginea pe Google",
|
||||
"search_image_on_yandex": "căutați imaginea pe Yandex",
|
||||
"search_image_on_saucenao": "căutați imaginea pe SauceNAO",
|
||||
"view_on_client": "Vezi pe {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "полные данные аккаунта",
|
||||
"this_account": "этот аккаунт",
|
||||
"delete": "удалить",
|
||||
"delete_account": "удалить аккаунт"
|
||||
"delete_account": "удалить аккаунт",
|
||||
"file_type": "Тип файла",
|
||||
"version": "Версия",
|
||||
"spoiler": "спойлер",
|
||||
"interface": "интерфейс",
|
||||
"invalid_url": "Недействительный URL",
|
||||
"no_link_provided": "Ссылка не предоставлена",
|
||||
"image_search": "поиск изображений",
|
||||
"view_on": "просмотр на",
|
||||
"copy_link": "копировать ссылку",
|
||||
"hide_post": "скрыть пост",
|
||||
"hide_thread": "скрыть ветку",
|
||||
"unhide_post": "показать пост",
|
||||
"unhide_thread": "показать ветку",
|
||||
"search_image_on_google": "поиск изображения в Google",
|
||||
"search_image_on_yandex": "поиск изображения в Yandex",
|
||||
"search_image_on_saucenao": "поиск изображения на SauceNAO",
|
||||
"view_on_client": "Посмотреть на {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "të dhënat e plotë të llogarisë",
|
||||
"this_account": "kjo llogaria",
|
||||
"delete": "fshij",
|
||||
"delete_account": "fshi llogarinë"
|
||||
"delete_account": "fshi llogarinë",
|
||||
"file_type": "Lloji i skedarit",
|
||||
"version": "Version",
|
||||
"spoiler": "spoiler",
|
||||
"interface": "ndërfaqe",
|
||||
"invalid_url": "URL jo e vlefshëm",
|
||||
"no_link_provided": "Nuk është dhënë asnjë lidhje",
|
||||
"image_search": "kërkim imazhesh",
|
||||
"view_on": "shiko në",
|
||||
"copy_link": "kopjo linkun",
|
||||
"hide_post": "fsheh postën",
|
||||
"hide_thread": "fsheh fijen",
|
||||
"unhide_post": "shfaq postën",
|
||||
"unhide_thread": "shfaq fijen",
|
||||
"search_image_on_google": "kërko imazhin në Google",
|
||||
"search_image_on_yandex": "kërko imazhin në Yandex",
|
||||
"search_image_on_saucenao": "kërko imazhin në SauceNAO",
|
||||
"view_on_client": "Shiko në {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "fullständig kontodata",
|
||||
"this_account": "detta konto",
|
||||
"delete": "ta bort",
|
||||
"delete_account": "ta bort konto"
|
||||
"delete_account": "ta bort konto",
|
||||
"file_type": "Filtyp",
|
||||
"version": "Version",
|
||||
"spoiler": "spoiler",
|
||||
"interface": "gränssnitt",
|
||||
"invalid_url": "Ogiltig URL",
|
||||
"no_link_provided": "Ingen länk angiven",
|
||||
"image_search": "bildsökning",
|
||||
"view_on": "se på",
|
||||
"copy_link": "kopiera länk",
|
||||
"hide_post": "dölj inlägg",
|
||||
"hide_thread": "dölj tråd",
|
||||
"unhide_post": "visa inlägg",
|
||||
"unhide_thread": "visa tråd",
|
||||
"search_image_on_google": "sök bild på Google",
|
||||
"search_image_on_yandex": "sök bild på Yandex",
|
||||
"search_image_on_saucenao": "sök bild på SauceNAO",
|
||||
"view_on_client": "Visa på {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "పూర్తి ఖాతా డేటా",
|
||||
"this_account": "ఈ ఖాతా",
|
||||
"delete": "తొలగించండి",
|
||||
"delete_account": "ఖాతాను తొలగించు"
|
||||
"delete_account": "ఖాతాను తొలగించు",
|
||||
"file_type": "ఫైల్ రకం",
|
||||
"version": "వెర్షన్",
|
||||
"spoiler": "స్పోయ్లర్",
|
||||
"interface": "అంతరాఫ్సు",
|
||||
"invalid_url": "చెల్లని URL",
|
||||
"no_link_provided": "లింక్ ఇవ్వబడలేదు",
|
||||
"image_search": "చిత్రాల శోధన",
|
||||
"view_on": "వీక్షించు",
|
||||
"copy_link": "లింక్ ను కాపీ చేయండి",
|
||||
"hide_post": "పోస్ట్ దాచు",
|
||||
"hide_thread": "థ్రెడ్ దాచు",
|
||||
"unhide_post": "పోస్ట్ చూపించు",
|
||||
"unhide_thread": "థ్రెడ్ చూపించు",
|
||||
"search_image_on_google": "గూగుల్లో చిత్రాన్ని వెతుకోండి",
|
||||
"search_image_on_yandex": "యాండెక్స్లో చిత్రాన్ని వెతుకోండి",
|
||||
"search_image_on_saucenao": "SauceNAOలో చిత్రాన్ని వెతుకోండి",
|
||||
"view_on_client": "{{client}} లో చూడండి"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "ข้อมูลบัญชีเต็มรูปแบบ",
|
||||
"this_account": "บัญชีนี้",
|
||||
"delete": "ลบ",
|
||||
"delete_account": "ลบบัญชี"
|
||||
"delete_account": "ลบบัญชี",
|
||||
"file_type": "ประเภทไฟล์",
|
||||
"version": "รุ่น",
|
||||
"spoiler": "สปอยล์เลอร์",
|
||||
"interface": "อินเทอร์เฟซ",
|
||||
"invalid_url": "URL ไม่ถูกต้อง",
|
||||
"no_link_provided": "ไม่มีการให้ลิงก์",
|
||||
"image_search": "ค้นหารูปภาพ",
|
||||
"view_on": "ดูที่",
|
||||
"copy_link": "คัดลอกลิงก์",
|
||||
"hide_post": "ซ่อนโพสต์",
|
||||
"hide_thread": "ซ่อนเธรด",
|
||||
"unhide_post": "แสดงโพสต์",
|
||||
"unhide_thread": "แสดงเธรด",
|
||||
"search_image_on_google": "ค้นหารูปภาพใน Google",
|
||||
"search_image_on_yandex": "ค้นหารูปภาพใน Yandex",
|
||||
"search_image_on_saucenao": "ค้นหารูปภาพใน SauceNAO",
|
||||
"view_on_client": "ดูบน {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "tam hesap verisi",
|
||||
"this_account": "bu hesap",
|
||||
"delete": "sil",
|
||||
"delete_account": "hesabı sil"
|
||||
"delete_account": "hesabı sil",
|
||||
"file_type": "Dosya türü",
|
||||
"version": "Sürüm",
|
||||
"spoiler": "spoiler",
|
||||
"interface": "arayüz",
|
||||
"invalid_url": "Geçersiz URL",
|
||||
"no_link_provided": "Hiçbir bağlantı sağlanmadı",
|
||||
"image_search": "resim arama",
|
||||
"view_on": "üzerinde görüntüle",
|
||||
"copy_link": "bağlantıyı kopyala",
|
||||
"hide_post": "gönderiyi gizle",
|
||||
"hide_thread": "konuyu gizle",
|
||||
"unhide_post": "gönderiyi göster",
|
||||
"unhide_thread": "konuyu göster",
|
||||
"search_image_on_google": "Google'da resim ara",
|
||||
"search_image_on_yandex": "Yandex'da resim ara",
|
||||
"search_image_on_saucenao": "SauceNAO'da resim ara",
|
||||
"view_on_client": "{{client}}'de görüntüle"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "повні дані аккаунта",
|
||||
"this_account": "цей аккаунт",
|
||||
"delete": "видалити",
|
||||
"delete_account": "видалити обліковий запис"
|
||||
"delete_account": "видалити обліковий запис",
|
||||
"file_type": "Тип файлу",
|
||||
"version": "Версія",
|
||||
"spoiler": "спойлер",
|
||||
"interface": "інтерфейс",
|
||||
"invalid_url": "Недійсний URL",
|
||||
"no_link_provided": "Не надано жодного посилання",
|
||||
"image_search": "пошук зображень",
|
||||
"view_on": "перегляд на",
|
||||
"copy_link": "копіювати посилання",
|
||||
"hide_post": "сховати допис",
|
||||
"hide_thread": "сховати нитку",
|
||||
"unhide_post": "показати допис",
|
||||
"unhide_thread": "показати нитку",
|
||||
"search_image_on_google": "пошук зображення в Google",
|
||||
"search_image_on_yandex": "пошук зображення в Yandex",
|
||||
"search_image_on_saucenao": "пошук зображення в SauceNAO",
|
||||
"view_on_client": "Переглянути на {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "مکمل اکاؤنٹ ڈیٹا",
|
||||
"this_account": "یہ اکاؤنٹ",
|
||||
"delete": "حذف کریں",
|
||||
"delete_account": "اکاؤنٹ حذف کریں"
|
||||
"delete_account": "اکاؤنٹ حذف کریں",
|
||||
"file_type": "فائل کی قسم",
|
||||
"version": "ورژن",
|
||||
"spoiler": "سپوائلر",
|
||||
"interface": "انٹرفیس",
|
||||
"invalid_url": "غیر درست یو آر ایل",
|
||||
"no_link_provided": "کوئی لنک فراہم نہیں کیا گیا",
|
||||
"image_search": "تصویر کی تلاش",
|
||||
"view_on": "پر دیکھیں",
|
||||
"copy_link": "لنک کاپی کریں",
|
||||
"hide_post": "پوسٹ چھپائیں",
|
||||
"hide_thread": "دھاگہ چھپائیں",
|
||||
"unhide_post": "پوسٹ دکھائیں",
|
||||
"unhide_thread": "دھاگہ دکھائیں",
|
||||
"search_image_on_google": "گوگل پر تصویر کا مطالعہ کریں",
|
||||
"search_image_on_yandex": "یانڈیکس پر تصویر کا مطالعہ کریں",
|
||||
"search_image_on_saucenao": "SauceNAO پر تصویر کا مطالعہ کریں",
|
||||
"view_on_client": "{{client}} پر دیکھیں"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "dữ liệu tài khoản đầy đủ",
|
||||
"this_account": "tài khoản này",
|
||||
"delete": "xóa",
|
||||
"delete_account": "xóa tài khoản"
|
||||
"delete_account": "xóa tài khoản",
|
||||
"file_type": "Loại tệp",
|
||||
"version": "Phiên bản",
|
||||
"spoiler": "spoiler",
|
||||
"interface": "giao diện",
|
||||
"invalid_url": "URL không hợp lệ",
|
||||
"no_link_provided": "Không có liên kết được cung cấp",
|
||||
"image_search": "tìm kiếm hình ảnh",
|
||||
"view_on": "xem trên",
|
||||
"copy_link": "sao chép liên kết",
|
||||
"hide_post": "Ẩn bài viết",
|
||||
"hide_thread": "Ẩn chủ đề",
|
||||
"unhide_post": "Hiện bài viết",
|
||||
"unhide_thread": "Hiện chủ đề",
|
||||
"search_image_on_google": "Tìm kiếm hình ảnh trên Google",
|
||||
"search_image_on_yandex": "Tìm kiếm hình ảnh trên Yandex",
|
||||
"search_image_on_saucenao": "Tìm kiếm hình ảnh trên SauceNAO",
|
||||
"view_on_client": "Xem trên {{client}}"
|
||||
}
|
||||
@@ -114,5 +114,22 @@
|
||||
"full_account_data": "完整帐户数据",
|
||||
"this_account": "此帐户",
|
||||
"delete": "删除",
|
||||
"delete_account": "删除账户"
|
||||
"delete_account": "删除账户",
|
||||
"file_type": "文件类型",
|
||||
"version": "版本",
|
||||
"spoiler": "spoiler",
|
||||
"interface": "界面",
|
||||
"invalid_url": "无效的网址",
|
||||
"no_link_provided": "未提供链接",
|
||||
"image_search": "图像搜索",
|
||||
"view_on": "查看",
|
||||
"copy_link": "复制链接",
|
||||
"hide_post": "隐藏帖子",
|
||||
"hide_thread": "隐藏主题",
|
||||
"unhide_post": "显示帖子",
|
||||
"unhide_thread": "显示主题",
|
||||
"search_image_on_google": "在Google上搜索图像",
|
||||
"search_image_on_yandex": "在Yandex上搜索图像",
|
||||
"search_image_on_saucenao": "在SauceNAO上搜索图像",
|
||||
"view_on_client": "在{{client}}上查看"
|
||||
}
|
||||
+19
-9
@@ -1,6 +1,7 @@
|
||||
import { useEffect } from 'react';
|
||||
import { Outlet, Route, Routes, useLocation, useParams } from 'react-router-dom';
|
||||
import { isHomeView } from './lib/utils/view-utils';
|
||||
import useIsMobile from './hooks/use-is-mobile';
|
||||
import useTheme from './hooks/use-theme';
|
||||
import styles from './app.module.css';
|
||||
import Board from './views/board';
|
||||
@@ -9,16 +10,17 @@ import Home from './views/home';
|
||||
import NotFound from './views/not-found';
|
||||
import PendingPost from './views/pending-post';
|
||||
import PostPage from './views/post-page';
|
||||
import BoardBanner from './components/board-banner';
|
||||
import { DesktopBoardButtons, MobileBoardButtons } from './components/board-buttons';
|
||||
import BoardNav from './components/board-nav';
|
||||
import BoardHeader from './components/board-header';
|
||||
import ChallengeModal from './components/challenge-modal';
|
||||
import SubplebbitStats from './components/subplebbit-stats';
|
||||
import PostForm from './components/post-form';
|
||||
import SubplebbitStats from './components/subplebbit-stats';
|
||||
import TopBar from './components/topbar';
|
||||
|
||||
const BoardLayout = () => {
|
||||
const { accountCommentIndex, commentCid, subplebbitAddress } = useParams();
|
||||
const location = useLocation();
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const isValidAccountCommentIndex = !accountCommentIndex || (!isNaN(parseInt(accountCommentIndex)) && parseInt(accountCommentIndex) >= 0);
|
||||
const isValidCommentCid = !commentCid || /^Qm[a-zA-Z0-9]{44}$/.test(commentCid);
|
||||
@@ -33,12 +35,20 @@ const BoardLayout = () => {
|
||||
|
||||
return (
|
||||
<div className={styles.boardLayout}>
|
||||
<BoardNav />
|
||||
<BoardBanner />
|
||||
<MobileBoardButtons />
|
||||
<PostForm key={key} />
|
||||
{subplebbitAddress && isValidSubplebbitAddress && <SubplebbitStats />}
|
||||
<DesktopBoardButtons />
|
||||
<TopBar />
|
||||
<BoardHeader />
|
||||
{isMobile ? (
|
||||
<>
|
||||
<PostForm key={key} />
|
||||
<MobileBoardButtons />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<PostForm key={key} />
|
||||
{subplebbitAddress && isValidSubplebbitAddress && <SubplebbitStats />}
|
||||
<DesktopBoardButtons />
|
||||
</>
|
||||
)}
|
||||
<Outlet />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './board-banner';
|
||||
@@ -1,6 +1,6 @@
|
||||
.mobileBoardButtons {
|
||||
text-align: center;
|
||||
margin-top: 9px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.mobileBoardButtons button {
|
||||
@@ -12,9 +12,13 @@
|
||||
all: unset;
|
||||
}
|
||||
|
||||
.mobilePostPageButtonsSecondRow {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 20px;
|
||||
.showBlockedButton {
|
||||
color: var(--button-desktop-text-color);
|
||||
}
|
||||
|
||||
.showBlockedButton:hover {
|
||||
color: var(--button-desktop-text-color-hover);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link, useLocation, useParams } from 'react-router-dom';
|
||||
import { useAccountComment, useSubscribe } from '@plebbit/plebbit-react-hooks';
|
||||
import styles from './board-buttons.module.css';
|
||||
import { isAllView, isCatalogView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
||||
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
||||
import styles from './board-buttons.module.css';
|
||||
|
||||
interface BoardButtonsProps {
|
||||
isInAllView?: boolean;
|
||||
address: string | undefined;
|
||||
address?: string | undefined;
|
||||
isInCatalogView?: boolean;
|
||||
isInSubscriptionsView?: boolean;
|
||||
}
|
||||
@@ -16,16 +17,11 @@ const OptionsButton = () => {
|
||||
return <button className='button'>{t('options')}</button>;
|
||||
};
|
||||
|
||||
const CatalogButton = ({ address, isInAllView, isInCatalogView }: BoardButtonsProps) => {
|
||||
const CatalogButton = ({ address, isInAllView, isInSubscriptionsView }: BoardButtonsProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<button className='button'>
|
||||
{isInCatalogView ? (
|
||||
<Link to={isInAllView ? '/p/all' : `/p/${address}`}>{t('return')}</Link>
|
||||
) : (
|
||||
<Link to={isInAllView ? `/p/all/catalog` : `/p/${address}/catalog`}>{t('catalog')}</Link>
|
||||
)}
|
||||
<Link to={isInAllView ? `/p/all/catalog` : isInSubscriptionsView ? `/p/subscriptions/catalog` : `/p/${address}/catalog`}>{t('catalog')}</Link>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -50,11 +46,12 @@ const ReturnButton = ({ address }: BoardButtonsProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const BottomButton = () => {
|
||||
const { t } = useTranslation();
|
||||
const RefreshButton = () => {
|
||||
const reset = useFeedResetStore((state) => state.reset);
|
||||
|
||||
return (
|
||||
<button className='button' onClick={() => window.scrollTo(0, document.body.scrollHeight)}>
|
||||
{t('bottom')}
|
||||
<button className='button' onClick={() => reset && reset()}>
|
||||
Refresh
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -74,20 +71,22 @@ export const MobileBoardButtons = () => {
|
||||
return (
|
||||
<div className={styles.mobileBoardButtons}>
|
||||
{isInPostView || isInPendingPostPage ? (
|
||||
<div className={styles.mobilePostPageButtons}>
|
||||
<>
|
||||
<ReturnButton address={subplebbitAddress} />
|
||||
<CatalogButton address={subplebbitAddress} isInAllView={isInAllView} />
|
||||
<BottomButton />
|
||||
<div className={styles.mobilePostPageButtonsSecondRow}>
|
||||
<OptionsButton />
|
||||
<SubscribeButton address={subplebbitAddress} />
|
||||
</div>
|
||||
</div>
|
||||
<CatalogButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} />
|
||||
<OptionsButton />
|
||||
<SubscribeButton address={subplebbitAddress} />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{isInCatalogView ? (
|
||||
<ReturnButton address={subplebbitAddress} />
|
||||
) : (
|
||||
<CatalogButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} />
|
||||
)}
|
||||
<OptionsButton />
|
||||
<CatalogButton address={subplebbitAddress} isInAllView={isInAllView} isInCatalogView={isInCatalogView} isInSubscriptionsView={isInSubscriptionsView} />
|
||||
<SubscribeButton address={subplebbitAddress} />
|
||||
<RefreshButton />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
@@ -110,19 +109,40 @@ export const DesktopBoardButtons = () => {
|
||||
<hr />
|
||||
{isInPostView || isInPendingPostPage ? (
|
||||
<>
|
||||
[<ReturnButton address={subplebbitAddress} />] [<CatalogButton address={subplebbitAddress} />] [<BottomButton />] [
|
||||
<OptionsButton />]
|
||||
[
|
||||
<ReturnButton address={subplebbitAddress} />
|
||||
] [
|
||||
<CatalogButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} />
|
||||
] [
|
||||
<OptionsButton />]{' '}
|
||||
<span className={styles.subscribeButton}>
|
||||
[<SubscribeButton address={subplebbitAddress} />]
|
||||
</span>
|
||||
</>
|
||||
) : isInCatalogView ? (
|
||||
<>
|
||||
[
|
||||
<ReturnButton address={subplebbitAddress} />
|
||||
] [
|
||||
<OptionsButton />
|
||||
] [
|
||||
<RefreshButton />]{' '}
|
||||
<span className={styles.subscribeButton}>
|
||||
[<SubscribeButton address={subplebbitAddress} />]
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
[<OptionsButton />] [
|
||||
<CatalogButton address={subplebbitAddress} isInAllView={isInAllView} isInCatalogView={isInCatalogView} isInSubscriptionsView={isInSubscriptionsView} />]
|
||||
[
|
||||
<CatalogButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} />
|
||||
] [
|
||||
<OptionsButton />
|
||||
] [
|
||||
<RefreshButton />]{' '}
|
||||
{!(isInAllView || isInSubscriptionsView) && (
|
||||
<span className={styles.subscribeButton}>
|
||||
[<SubscribeButton address={subplebbitAddress} />]
|
||||
[
|
||||
<SubscribeButton address={subplebbitAddress} />]
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
|
||||
+3
-3
@@ -2,7 +2,7 @@ import { useState } from 'react';
|
||||
import { useLocation, useParams } from 'react-router-dom';
|
||||
import { useAccountComment, useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
||||
import { isAllView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
||||
import styles from './board-banner.module.css';
|
||||
import styles from './board-header.module.css';
|
||||
import { useMultisubMetadata } from '../../hooks/use-default-subplebbits';
|
||||
|
||||
const totalBanners = 57;
|
||||
@@ -16,7 +16,7 @@ const ImageBanner = () => {
|
||||
return <img src={imagePath} alt='' />;
|
||||
};
|
||||
|
||||
const BoardBanner = () => {
|
||||
const BoardHeader = () => {
|
||||
const location = useLocation();
|
||||
const params = useParams();
|
||||
|
||||
@@ -46,4 +46,4 @@ const BoardBanner = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default BoardBanner;
|
||||
export default BoardHeader;
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './board-header';
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './board-nav';
|
||||
@@ -54,6 +54,7 @@ export const CatalogPostMedia = ({ commentMediaInfo, isOutOfFeed, linkWidth, lin
|
||||
thumbnailComponent = thumbnail ? (
|
||||
<img src={thumbnail} alt='' onLoad={handleLoad} onError={handleError} style={loadingStyle} />
|
||||
) : (
|
||||
// show first frame of the video, as a workaround for Safari not loading thumbnails
|
||||
<video src={`${url}#t=0.001`} onLoad={handleLoad} onError={handleError} style={loadingStyle} />
|
||||
);
|
||||
} else if (type === 'webpage' && !hasError) {
|
||||
|
||||
@@ -3,8 +3,8 @@ import Draggable from 'react-draggable';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Challenge as ChallengeType } from '@plebbit/plebbit-react-hooks';
|
||||
import { getPublicationType } from '../../lib/utils/challenge-utils';
|
||||
import useChallenges from '../../hooks/use-challenges';
|
||||
import useIsMobile from '../../hooks/use-is-mobile';
|
||||
import useChallengesStore from '../../stores/use-challenges-store';
|
||||
import styles from './challenge-modal.module.css';
|
||||
import _ from 'lodash';
|
||||
|
||||
@@ -125,7 +125,7 @@ const Challenge = ({ challenge, closeModal }: ChallengeProps) => {
|
||||
};
|
||||
|
||||
const ChallengeModal = () => {
|
||||
const { challenges, removeChallenge } = useChallenges();
|
||||
const { challenges, removeChallenge } = useChallengesStore();
|
||||
const isOpen = !!challenges.length;
|
||||
const closeModal = () => removeChallenge();
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
height: var(--height);
|
||||
max-width: 250px;
|
||||
max-height: 250px;
|
||||
margin: 3px 20px 5px 0;
|
||||
margin: 3px 20px 5px 20px;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
@@ -9,16 +9,16 @@ import Embed, { canEmbed } from '../embed';
|
||||
|
||||
interface MediaProps {
|
||||
commentMediaInfo?: CommentMediaInfo;
|
||||
isDeleted?: boolean;
|
||||
isOutOfFeed?: boolean; // virtuoso wrapper unneeded
|
||||
isReply: boolean;
|
||||
linkHeight?: number;
|
||||
linkWidth?: number;
|
||||
showThumbnail?: boolean;
|
||||
setShowThumbnail: (showThumbnail: boolean) => void;
|
||||
toggleExpanded?: () => void;
|
||||
}
|
||||
|
||||
const Thumbnail = ({ commentMediaInfo, isOutOfFeed, isReply, linkHeight, linkWidth, setShowThumbnail }: MediaProps) => {
|
||||
const Thumbnail = ({ commentMediaInfo, isDeleted, isOutOfFeed, isReply, linkHeight, linkWidth, setShowThumbnail }: MediaProps) => {
|
||||
const { patternThumbnailUrl, thumbnail, type, url } = commentMediaInfo || {};
|
||||
const [hasError, setHasError] = useState(false);
|
||||
const handleError = () => setHasError(true);
|
||||
@@ -49,7 +49,12 @@ const Thumbnail = ({ commentMediaInfo, isOutOfFeed, isReply, linkHeight, linkWid
|
||||
if (type === 'image') {
|
||||
thumbnailComponent = <img src={url} alt='' onError={handleError} onClick={() => setShowThumbnail(false)} />;
|
||||
} else if (type === 'video') {
|
||||
thumbnailComponent = thumbnail ? <img src={thumbnail} alt='' /> : <video src={`${url}#t=0.001`} onClick={() => setShowThumbnail(false)} />;
|
||||
thumbnailComponent = thumbnail ? (
|
||||
<img src={thumbnail} alt='' />
|
||||
) : (
|
||||
// show first frame of the video, as a workaround for Safari not loading thumbnails
|
||||
<video src={`${url}#t=0.001`} onClick={() => setShowThumbnail(false)} />
|
||||
);
|
||||
} else if (type === 'webpage') {
|
||||
thumbnailComponent = <img src={thumbnail} alt='' onClick={() => setShowThumbnail(false)} />;
|
||||
} else if (type === 'iframe') {
|
||||
@@ -60,16 +65,12 @@ const Thumbnail = ({ commentMediaInfo, isOutOfFeed, isReply, linkHeight, linkWid
|
||||
thumbnailComponent = <audio src={url} controls />;
|
||||
}
|
||||
|
||||
if (hasError) {
|
||||
thumbnailComponent = <img className={styles.fileDeleted} src='/assets/filedeleted-res.gif' alt='File deleted' />;
|
||||
}
|
||||
|
||||
const thumbnailSmallPadding = isMobile ? styles.thumbnailMobile : styles.thumbnailReplyDesktop;
|
||||
const thumbnailDimensions = { '--width': displayWidth, '--height': displayHeight } as React.CSSProperties;
|
||||
|
||||
const linkWithoutThumbnail = url && new URL(url);
|
||||
|
||||
return hasError ? (
|
||||
return hasError || isDeleted ? (
|
||||
<img className={styles.fileDeleted} src='/assets/filedeleted-res.gif' alt='File deleted' />
|
||||
) : isOutOfFeed ? (
|
||||
<span className={styles.subplebbitAvatar}>{thumbnailComponent}</span>
|
||||
@@ -132,7 +133,7 @@ const Media = ({ commentMediaInfo, isReply, setShowThumbnail }: MediaProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const CommentMedia = ({ commentMediaInfo, isOutOfFeed, isReply, linkHeight, linkWidth, showThumbnail, setShowThumbnail }: MediaProps) => {
|
||||
const CommentMedia = ({ commentMediaInfo, isDeleted, isOutOfFeed, isReply, linkHeight, linkWidth, showThumbnail, setShowThumbnail }: MediaProps) => {
|
||||
const { t } = useTranslation();
|
||||
const isMobile = useIsMobile();
|
||||
const { type, url } = commentMediaInfo || {};
|
||||
@@ -143,6 +144,7 @@ const CommentMedia = ({ commentMediaInfo, isOutOfFeed, isReply, linkHeight, link
|
||||
{url && (
|
||||
<Thumbnail
|
||||
commentMediaInfo={commentMediaInfo}
|
||||
isDeleted={isDeleted}
|
||||
isOutOfFeed={isOutOfFeed}
|
||||
isReply={isReply}
|
||||
linkHeight={linkHeight}
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default } from './post-form';
|
||||
export { LinkTypePreviewer, default } from './post-form';
|
||||
|
||||
@@ -15,12 +15,7 @@
|
||||
|
||||
.postFormMobile {
|
||||
text-align: center;
|
||||
padding-top: 9px;
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.postFormMobile .showFormButton {
|
||||
margin-bottom: 5px;
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
.closed {
|
||||
@@ -87,16 +82,49 @@
|
||||
|
||||
.linkType {
|
||||
font-weight: normal;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
bottom: 3px;
|
||||
padding-left: 3px;
|
||||
}
|
||||
|
||||
.linkType td {
|
||||
height: 20px;
|
||||
padding-left: 2px;
|
||||
}
|
||||
|
||||
.spoilerButton input[type="checkbox"] {
|
||||
margin: 0 3px;
|
||||
vertical-align: middle;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.spoilerButton td {
|
||||
padding-top: 2px;
|
||||
height: 20px;
|
||||
padding-left: 2px;
|
||||
}
|
||||
|
||||
.spoilerButton {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.postFormDesktop {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.postFormTable {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.postFormTable td input {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.postFormTable td textarea {
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.postFormTable button {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
|
||||
@@ -7,9 +7,9 @@ import { alertChallengeVerificationFailed } from '../../lib/utils/challenge-util
|
||||
import { getLinkMediaInfo } from '../../lib/utils/media-utils';
|
||||
import { isValidURL } from '../../lib/utils/url-utils';
|
||||
import { isAllView, isDescriptionView, isPostPageView, isRulesView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
||||
import challengesStore from '../../hooks/use-challenges';
|
||||
import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
|
||||
import useReply from '../../hooks/use-reply';
|
||||
import useChallengesStore from '../../stores/use-challenges-store';
|
||||
import styles from './post-form.module.css';
|
||||
import _ from 'lodash';
|
||||
|
||||
@@ -18,26 +18,29 @@ type SubmitState = {
|
||||
title: string | undefined;
|
||||
content: string | undefined;
|
||||
link: string | undefined;
|
||||
spoiler: boolean | undefined;
|
||||
publishCommentOptions: PublishCommentOptions;
|
||||
setSubmitStore: (data: Partial<SubmitState>) => void;
|
||||
resetSubmitStore: () => void;
|
||||
};
|
||||
|
||||
const { addChallenge } = challengesStore.getState();
|
||||
const { addChallenge } = useChallengesStore.getState();
|
||||
|
||||
const useSubmitStore = create<SubmitState>((set) => ({
|
||||
subplebbitAddress: undefined,
|
||||
title: undefined,
|
||||
content: undefined,
|
||||
link: undefined,
|
||||
spoiler: undefined,
|
||||
publishCommentOptions: {},
|
||||
setSubmitStore: ({ subplebbitAddress, title, content, link }) =>
|
||||
setSubmitStore: ({ subplebbitAddress, title, content, link, spoiler }) =>
|
||||
set((state) => {
|
||||
const nextState = { ...state };
|
||||
if (subplebbitAddress !== undefined) nextState.subplebbitAddress = subplebbitAddress;
|
||||
if (title !== undefined) nextState.title = title || undefined;
|
||||
if (content !== undefined) nextState.content = content || undefined;
|
||||
if (link !== undefined) nextState.link = link || undefined;
|
||||
if (spoiler !== undefined) nextState.spoiler = spoiler || undefined;
|
||||
|
||||
nextState.publishCommentOptions = {
|
||||
...nextState,
|
||||
@@ -51,12 +54,13 @@ const useSubmitStore = create<SubmitState>((set) => ({
|
||||
};
|
||||
return nextState;
|
||||
}),
|
||||
resetSubmitStore: () => set({ subplebbitAddress: undefined, title: undefined, content: undefined, link: undefined, publishCommentOptions: {} }),
|
||||
resetSubmitStore: () => set({ subplebbitAddress: undefined, title: undefined, content: undefined, link: undefined, spoiler: undefined, publishCommentOptions: {} }),
|
||||
}));
|
||||
|
||||
const LinkTypePreviewer = ({ link }: { link: string }) => {
|
||||
export const LinkTypePreviewer = ({ link }: { link: string }) => {
|
||||
const { t } = useTranslation();
|
||||
const mediaInfo = getLinkMediaInfo(link);
|
||||
return isValidURL(link) ? mediaInfo?.type : 'Invalid URL';
|
||||
return isValidURL(link) ? mediaInfo?.type : t('invalid_url');
|
||||
};
|
||||
|
||||
const PostFormTable = ({ closeForm }: { closeForm: () => void }) => {
|
||||
@@ -209,13 +213,21 @@ const PostFormTable = ({ closeForm }: { closeForm: () => void }) => {
|
||||
isInPostView ? setContent.link(e.target.value) : setSubmitStore({ link: e.target.value });
|
||||
}}
|
||||
/>
|
||||
<span className={styles.linkType}>
|
||||
{url && (
|
||||
<>
|
||||
(<LinkTypePreviewer link={url} />)
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr className={styles.linkType}>
|
||||
<td>{t('file_type')}</td>
|
||||
<td>{url ? <LinkTypePreviewer link={url} /> : t('no_link_provided')}</td>
|
||||
</tr>
|
||||
<tr className={styles.spoilerButton}>
|
||||
<td>{t('options')}</td>
|
||||
<td>
|
||||
[
|
||||
<label>
|
||||
<input type='checkbox' onChange={(e) => (isInPostView ? setContent.spoiler(e.target.checked) : setSubmitStore({ spoiler: e.target.checked }))} />
|
||||
{t('spoiler')}?
|
||||
</label>
|
||||
]
|
||||
</td>
|
||||
</tr>
|
||||
{(isInAllView || isInSubscriptionsView) && (
|
||||
@@ -293,6 +305,7 @@ const PostForm = () => {
|
||||
{showForm ? t('close_post_form') : isInPostView ? t('post_a_reply') : t('start_new_thread')}
|
||||
</button>
|
||||
{showForm && <PostFormTable closeForm={() => setShowForm(false)} />}
|
||||
<hr />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useState } from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { useLocation, useParams } from 'react-router-dom';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useAccount } from '@plebbit/plebbit-react-hooks';
|
||||
import { useAccount, useBlock, useEditedComment } from '@plebbit/plebbit-react-hooks';
|
||||
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
|
||||
import styles from '../post.module.css';
|
||||
import { getCommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail } from '../../../lib/utils/media-utils';
|
||||
@@ -20,7 +20,7 @@ import PostMenuDesktop from './post-menu-desktop/';
|
||||
import { PostProps } from '../post';
|
||||
import _ from 'lodash';
|
||||
|
||||
const PostInfo = ({ openReplyModal, post, roles }: PostProps) => {
|
||||
const PostInfo = ({ openReplyModal, post, roles, isHidden }: PostProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { author, cid, locked, pinned, parentCid, postCid, shortCid, state, subplebbitAddress, timestamp, title } = post || {};
|
||||
const { address, displayName, shortAddress } = author || {};
|
||||
@@ -44,9 +44,11 @@ const PostInfo = ({ openReplyModal, post, roles }: PostProps) => {
|
||||
|
||||
return (
|
||||
<div className={styles.postInfo}>
|
||||
<span className={styles.checkbox}>
|
||||
<input type='checkbox' />
|
||||
</span>
|
||||
{!isHidden && (
|
||||
<span className={styles.checkbox}>
|
||||
<input type='checkbox' />
|
||||
</span>
|
||||
)}
|
||||
{title && <span className={styles.subject}>{displayTitle} </span>}
|
||||
<span className={styles.nameBlock}>
|
||||
<span className={`${styles.name} ${(isDescription || isRules || authorRole) && styles.capcodeMod}`}>
|
||||
@@ -90,7 +92,7 @@ const PostInfo = ({ openReplyModal, post, roles }: PostProps) => {
|
||||
<img src='/assets/icons/closed.gif' alt='' className={styles.closedIcon} title={t('closed')} />
|
||||
</span>
|
||||
)}
|
||||
{!isInPostView && !isReply && (
|
||||
{!isInPostView && !isReply && !isHidden && (
|
||||
<span className={styles.replyButton}>
|
||||
[
|
||||
<Link
|
||||
@@ -109,7 +111,7 @@ const PostInfo = ({ openReplyModal, post, roles }: PostProps) => {
|
||||
|
||||
const PostMedia = ({ post }: PostProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { link, linkHeight, linkWidth, parentCid } = post || {};
|
||||
const { deleted, link, linkHeight, linkWidth, parentCid, removed } = post || {};
|
||||
const { isDescription, isRules } = post || {}; // custom properties, not from api
|
||||
const commentMediaInfo = getCommentMediaInfo(post);
|
||||
const { type, url } = commentMediaInfo || {};
|
||||
@@ -149,22 +151,25 @@ const PostMedia = ({ post }: PostProps) => {
|
||||
)}
|
||||
</div>
|
||||
{(hasThumbnail || (!hasThumbnail && !showThumbnail)) && (
|
||||
<CommentMedia
|
||||
commentMediaInfo={commentMediaInfo}
|
||||
isOutOfFeed={isDescription || isRules} // virtuoso wrapper unneeded
|
||||
isReply={isReply}
|
||||
linkHeight={linkHeight}
|
||||
linkWidth={linkWidth}
|
||||
showThumbnail={showThumbnail}
|
||||
setShowThumbnail={setShowThumbnail}
|
||||
/>
|
||||
<div className={styles.fileThumbnail}>
|
||||
<CommentMedia
|
||||
commentMediaInfo={commentMediaInfo}
|
||||
isOutOfFeed={isDescription || isRules} // virtuoso wrapper unneeded
|
||||
isDeleted={removed || deleted}
|
||||
isReply={isReply}
|
||||
linkHeight={linkHeight}
|
||||
linkWidth={linkWidth}
|
||||
showThumbnail={showThumbnail}
|
||||
setShowThumbnail={setShowThumbnail}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const PostMessage = ({ post }: PostProps) => {
|
||||
const { cid, content, parentCid, postCid, state, subplebbitAddress } = post || {};
|
||||
const { cid, content, deleted, parentCid, postCid, reason, removed, state, subplebbitAddress } = post || {};
|
||||
|
||||
const params = useParams();
|
||||
const location = useLocation();
|
||||
@@ -191,7 +196,20 @@ const PostMessage = ({ post }: PostProps) => {
|
||||
<br />
|
||||
</>
|
||||
)}
|
||||
<Markdown content={displayContent} />
|
||||
{removed ? (
|
||||
<span className={styles.removedContent}>(THIS POST WAS REMOVED)</span>
|
||||
) : deleted ? (
|
||||
<span className={styles.removedContent}>User deleted this post.</span>
|
||||
) : (
|
||||
<Markdown content={displayContent} />
|
||||
)}
|
||||
{(removed || deleted) && reason && (
|
||||
<span>
|
||||
<br />
|
||||
<br />
|
||||
reason: {reason}
|
||||
</span>
|
||||
)}
|
||||
{!isReply && content.length > 1000 && !isInPostView && (
|
||||
<span className={styles.abbr}>
|
||||
<br />
|
||||
@@ -209,13 +227,21 @@ const PostMessage = ({ post }: PostProps) => {
|
||||
};
|
||||
|
||||
const PostDesktop = ({ openReplyModal, post, roles, showAllReplies }: PostProps) => {
|
||||
// handle pending mod or author edit
|
||||
const { editedComment } = useEditedComment({ comment: post });
|
||||
if (editedComment) {
|
||||
post = editedComment;
|
||||
}
|
||||
const { cid, content, link, pinned, replyCount, subplebbitAddress } = post || {};
|
||||
const { isDescription, isRules } = post || {}; // custom properties, not from api
|
||||
|
||||
const params = useParams();
|
||||
const location = useLocation();
|
||||
const isInPendingPostView = isPendingPostView(location.pathname, params);
|
||||
const isInPostView = isPostPageView(location.pathname, params);
|
||||
const isInPostPageView = isPostPageView(location.pathname, params);
|
||||
|
||||
const { blocked, unblock, block } = useBlock({ cid });
|
||||
const isHidden = blocked && !isInPostPageView;
|
||||
|
||||
const replies = useReplies(post);
|
||||
const visiblelinksCount = useCountLinksInReplies(post, 5);
|
||||
@@ -228,49 +254,52 @@ const PostDesktop = ({ openReplyModal, post, roles, showAllReplies }: PostProps)
|
||||
<div className={styles.hrWrapper}>
|
||||
<hr />
|
||||
</div>
|
||||
{!isInPostView && (
|
||||
<span className={styles.hideButtonWrapper}>
|
||||
<span className={`${styles.hideButton} ${styles.hideThread}`} />
|
||||
</span>
|
||||
)}
|
||||
{link && isValidURL(link) && <PostMedia post={post} />}
|
||||
<PostInfo openReplyModal={openReplyModal} post={post} roles={roles} />
|
||||
{!content && <div className={styles.spacer} />}
|
||||
{content && <PostMessage post={post} />}
|
||||
{!isDescription && !isRules && !isInPendingPostView && (replies.length > 5 || (pinned && replies.length > 0)) && !isInPostView && (
|
||||
<span className={styles.summary}>
|
||||
<span className={styles.expandButtonWrapper}>
|
||||
<span className={styles.expandButton} />
|
||||
<div className={isHidden ? styles.postDesktopBlocked : ''}>
|
||||
{!isInPostPageView && !isDescription && !isRules && (
|
||||
<span className={styles.hideButtonWrapper}>
|
||||
<span className={`${styles.hideButton} ${blocked ? styles.unhideThread : styles.hideThread}`} onClick={blocked ? unblock : block} />
|
||||
</span>
|
||||
{linksCount > 0 ? (
|
||||
<Trans
|
||||
i18nKey={'replies_and_links_omitted'}
|
||||
shouldUnescape={true}
|
||||
components={{ 1: <Link to={`/p/${subplebbitAddress}/c/${cid}`} /> }}
|
||||
values={{ repliesCount, linksCount }}
|
||||
/>
|
||||
) : (
|
||||
<Trans i18nKey={'replies_omitted'} shouldUnescape={true} components={{ 1: <Link to={`/p/${subplebbitAddress}/c/${cid}`} /> }} values={{ repliesCount }} />
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
{!(pinned && !isInPostView) &&
|
||||
!isInPendingPostView &&
|
||||
!isDescription &&
|
||||
!isRules &&
|
||||
replies &&
|
||||
(showAllReplies ? replies : replies.slice(-5)).map((reply, index) => (
|
||||
<div key={index} className={styles.replyContainer}>
|
||||
<div className={styles.replyDesktop}>
|
||||
<div className={styles.sideArrows}>{'>>'}</div>
|
||||
<div className={styles.reply}>
|
||||
<PostInfo openReplyModal={openReplyModal} post={reply} roles={roles} />
|
||||
{reply.link && isValidURL(reply.link) && <PostMedia post={reply} />}
|
||||
{reply.content && <PostMessage post={reply} />}
|
||||
)}
|
||||
{link && !isHidden && isValidURL(link) && <PostMedia post={post} />}
|
||||
<PostInfo isHidden={isHidden} openReplyModal={openReplyModal} post={post} roles={roles} />
|
||||
{!isHidden && !content && <div className={styles.spacer} />}
|
||||
{!isHidden && content && <PostMessage post={post} />}
|
||||
{!isHidden && !isDescription && !isRules && !isInPendingPostView && (replies.length > 5 || (pinned && replies.length > 0)) && !isInPostPageView && (
|
||||
<span className={styles.summary}>
|
||||
<span className={styles.expandButtonWrapper}>
|
||||
<span className={styles.expandButton} />
|
||||
</span>
|
||||
{linksCount > 0 ? (
|
||||
<Trans
|
||||
i18nKey={'replies_and_links_omitted'}
|
||||
shouldUnescape={true}
|
||||
components={{ 1: <Link to={`/p/${subplebbitAddress}/c/${cid}`} /> }}
|
||||
values={{ repliesCount, linksCount }}
|
||||
/>
|
||||
) : (
|
||||
<Trans i18nKey={'replies_omitted'} shouldUnescape={true} components={{ 1: <Link to={`/p/${subplebbitAddress}/c/${cid}`} /> }} values={{ repliesCount }} />
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
{!isHidden &&
|
||||
!(pinned && !isInPostPageView) &&
|
||||
!isInPendingPostView &&
|
||||
!isDescription &&
|
||||
!isRules &&
|
||||
replies &&
|
||||
(showAllReplies ? replies : replies.slice(-5)).map((reply, index) => (
|
||||
<div key={index} className={styles.replyContainer}>
|
||||
<div className={styles.replyDesktop}>
|
||||
<div className={styles.sideArrows}>{'>>'}</div>
|
||||
<div className={styles.reply}>
|
||||
<PostInfo openReplyModal={openReplyModal} post={reply} roles={roles} />
|
||||
{reply.link && isValidURL(reply.link) && <PostMedia post={reply} />}
|
||||
{reply.content && <PostMessage post={reply} />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useLocation, useParams } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { autoUpdate, flip, FloatingFocusManager, offset, shift, useClick, useDismiss, useFloating, useId, useInteractions, useRole } from '@floating-ui/react';
|
||||
import { Comment } from '@plebbit/plebbit-react-hooks';
|
||||
import { Comment, useBlock } from '@plebbit/plebbit-react-hooks';
|
||||
import styles from './post-menu-desktop.module.css';
|
||||
import { getCommentMediaInfo } from '../../../../lib/utils/media-utils';
|
||||
import { copyShareLinkToClipboard, isValidURL } from '../../../../lib/utils/url-utils';
|
||||
import { isCatalogView } from '../../../../lib/utils/view-utils';
|
||||
import { isCatalogView, isPostPageView } from '../../../../lib/utils/view-utils';
|
||||
|
||||
interface PostMenuDesktopProps {
|
||||
cid: string;
|
||||
@@ -17,6 +18,7 @@ interface PostMenuDesktopProps {
|
||||
}
|
||||
|
||||
const CopyLinkButton = ({ cid, subplebbitAddress, onClose }: PostMenuDesktopProps) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div
|
||||
onClick={() => {
|
||||
@@ -24,12 +26,13 @@ const CopyLinkButton = ({ cid, subplebbitAddress, onClose }: PostMenuDesktopProp
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
<div className={styles.postMenuItem}>Copy link</div>
|
||||
<div className={styles.postMenuItem}>{t('copy_link')}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ImageSearchButton = ({ url, onClose }: { url: string; onClose: () => void }) => {
|
||||
const { t } = useTranslation();
|
||||
const [isImageSearchMenuOpen, setIsImageSearchMenuOpen] = useState(false);
|
||||
|
||||
const { refs, floatingStyles } = useFloating({
|
||||
@@ -45,7 +48,7 @@ const ImageSearchButton = ({ url, onClose }: { url: string; onClose: () => void
|
||||
ref={refs.setReference}
|
||||
onClick={onClose}
|
||||
>
|
||||
Image search »
|
||||
{t('image_search')} »
|
||||
{isImageSearchMenuOpen && (
|
||||
<div ref={refs.setFloating} style={floatingStyles} className={styles.dropdownMenu}>
|
||||
<a href={`https://lens.google.com/uploadbyurl?url=${url}`} target='_blank' rel='noreferrer'>
|
||||
@@ -64,6 +67,7 @@ const ImageSearchButton = ({ url, onClose }: { url: string; onClose: () => void
|
||||
};
|
||||
|
||||
const ViewOnButton = ({ cid, isDescription, isRules, subplebbitAddress, onClose }: PostMenuDesktopProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [isClientRedirectMenuOpen, setIsClientRedirectMenuOpen] = useState(false);
|
||||
const viewOnOtherClientLink = `p/${subplebbitAddress}${isDescription || isRules ? '' : `/c/${cid}`}`;
|
||||
|
||||
@@ -80,7 +84,7 @@ const ViewOnButton = ({ cid, isDescription, isRules, subplebbitAddress, onClose
|
||||
ref={refs.setReference}
|
||||
onClick={onClose}
|
||||
>
|
||||
View on »
|
||||
{t('view_on')} »
|
||||
{isClientRedirectMenuOpen && (
|
||||
<div ref={refs.setFloating} style={floatingStyles} className={styles.dropdownMenu}>
|
||||
<a href={`https://seedit.app/#/${viewOnOtherClientLink}`} target='_blank' rel='noreferrer'>
|
||||
@@ -96,12 +100,18 @@ const ViewOnButton = ({ cid, isDescription, isRules, subplebbitAddress, onClose
|
||||
};
|
||||
|
||||
const PostMenuDesktop = ({ post }: { post: Comment }) => {
|
||||
const { t } = useTranslation();
|
||||
const { cid, isDescription, isRules, link, postCid, subplebbitAddress } = post || {};
|
||||
const commentMediaInfo = getCommentMediaInfo(post);
|
||||
const { thumbnail, type, url } = commentMediaInfo || {};
|
||||
const [menuBtnRotated, setMenuBtnRotated] = useState(false);
|
||||
|
||||
const isInCatalogView = isCatalogView(useLocation().pathname);
|
||||
const { blocked, unblock, block } = useBlock({ cid });
|
||||
|
||||
const location = useLocation();
|
||||
const params = useParams();
|
||||
const isInCatalogView = isCatalogView(location.pathname);
|
||||
const isInPostPageView = isPostPageView(location.pathname, params);
|
||||
|
||||
const { refs, floatingStyles, context } = useFloating({
|
||||
placement: 'bottom-start',
|
||||
@@ -135,6 +145,17 @@ const PostMenuDesktop = ({ post }: { post: Comment }) => {
|
||||
<FloatingFocusManager context={context} modal={false}>
|
||||
<div className={styles.postMenu} ref={refs.setFloating} style={floatingStyles} aria-labelledby={headingId} {...getFloatingProps()}>
|
||||
{cid && subplebbitAddress && <CopyLinkButton cid={cid} subplebbitAddress={subplebbitAddress} onClose={handleClose} />}
|
||||
{!isInPostPageView && !isDescription && !isRules && (
|
||||
<div
|
||||
className={styles.postMenuItem}
|
||||
onClick={() => {
|
||||
blocked ? unblock() : block();
|
||||
handleClose();
|
||||
}}
|
||||
>
|
||||
{blocked ? (postCid === cid ? t('unhide_thread') : t('unhide_post')) : postCid === cid ? t('hide_thread') : t('hide_post')}
|
||||
</div>
|
||||
)}
|
||||
{link && isValidURL(link) && (type === 'image' || type === 'gif' || thumbnail) && url && <ImageSearchButton url={url} onClose={handleClose} />}
|
||||
<ViewOnButton cid={cid} isDescription={isDescription} isRules={isRules} subplebbitAddress={subplebbitAddress} onClose={handleClose} />
|
||||
</div>
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
.postMenu a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.postMenuItem {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { autoUpdate, flip, FloatingFocusManager, offset, shift, useClick, useDismiss, useFloating, useId, useInteractions, useRole } from '@floating-ui/react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Comment } from '@plebbit/plebbit-react-hooks';
|
||||
import styles from './post-menu-mobile.module.css';
|
||||
import { getCommentMediaInfo } from '../../../../lib/utils/media-utils';
|
||||
@@ -15,6 +16,7 @@ interface PostMenuMobileProps {
|
||||
}
|
||||
|
||||
const CopyLinkButton = ({ cid, subplebbitAddress, onClose }: PostMenuMobileProps) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div
|
||||
onClick={() => {
|
||||
@@ -22,37 +24,38 @@ const CopyLinkButton = ({ cid, subplebbitAddress, onClose }: PostMenuMobileProps
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
<div className={styles.postMenuItem}>Copy link</div>
|
||||
<div className={styles.postMenuItem}>{t('copy_link')}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ImageSearchButtons = ({ url, onClose }: { url: string; onClose: () => void }) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div onClick={onClose}>
|
||||
<a href={`https://lens.google.com/uploadbyurl?url=${url}`} target='_blank' rel='noreferrer'>
|
||||
<div className={styles.postMenuItem}>Search image on Google</div>
|
||||
<div className={styles.postMenuItem}>{t('search_image_on_google')}</div>
|
||||
</a>
|
||||
<a href={`https://www.yandex.com/images/search?img_url=${url}&rpt=imageview`} target='_blank' rel='noreferrer'>
|
||||
<div className={styles.postMenuItem}>Search image on Yandex</div>
|
||||
<div className={styles.postMenuItem}>{t('search_image_on_yandex')}</div>
|
||||
</a>
|
||||
<a href={`https://saucenao.com/search.php?url=${url}`} target='_blank' rel='noreferrer'>
|
||||
<div className={styles.postMenuItem}>Search image on SauceNAO</div>
|
||||
<div className={styles.postMenuItem}>{t('search_image_on_saucenao')}</div>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ViewOnButtons = ({ cid, isDescription, isRules, subplebbitAddress, onClose }: PostMenuMobileProps) => {
|
||||
const { t } = useTranslation();
|
||||
const viewOnOtherClientLink = `p/${subplebbitAddress}${isDescription || isRules ? '' : `/c/${cid}`}`;
|
||||
|
||||
return (
|
||||
<div onClick={onClose}>
|
||||
<a href={`https://seedit.app/#/${viewOnOtherClientLink}`} target='_blank' rel='noreferrer'>
|
||||
<div className={styles.postMenuItem}>View on Seedit</div>
|
||||
<div className={styles.postMenuItem}>{t('view_on_client', { client: 'Seedit' })}</div>
|
||||
</a>
|
||||
<a href={`https://plebones.netlify.app/#/${viewOnOtherClientLink}`} target='_blank' rel='noreferrer'>
|
||||
<div className={styles.postMenuItem}>View on Plebones</div>
|
||||
<div className={styles.postMenuItem}>{t('view_on_client', { client: 'Plebones' })}</div>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { Link, useLocation, useParams } from 'react-router-dom';
|
||||
import { useAccount } from '@plebbit/plebbit-react-hooks';
|
||||
import { useAccount, useEditedComment } from '@plebbit/plebbit-react-hooks';
|
||||
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
|
||||
import styles from '../post.module.css';
|
||||
import { getCommentMediaInfo, getHasThumbnail } from '../../../lib/utils/media-utils';
|
||||
@@ -19,7 +19,7 @@ import _ from 'lodash';
|
||||
|
||||
const PostInfoAndMedia = ({ openReplyModal, post, roles }: PostProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { author, cid, link, linkHeight, linkWidth, locked, parentCid, pinned, shortCid, state, subplebbitAddress, timestamp, title } = post || {};
|
||||
const { author, cid, deleted, link, linkHeight, linkWidth, locked, parentCid, pinned, removed, shortCid, state, subplebbitAddress, timestamp, title } = post || {};
|
||||
const { isDescription, isRules } = post || {}; // custom properties, not from api
|
||||
const { address, displayName, shortAddress } = author || {};
|
||||
|
||||
@@ -98,6 +98,7 @@ const PostInfoAndMedia = ({ openReplyModal, post, roles }: PostProps) => {
|
||||
<CommentMedia
|
||||
commentMediaInfo={commentMediaInfo}
|
||||
isOutOfFeed={isDescription || isRules} // virtuoso wrapper unneeded
|
||||
isDeleted={removed || deleted}
|
||||
isReply={isReply}
|
||||
linkHeight={linkHeight}
|
||||
linkWidth={linkWidth}
|
||||
@@ -110,7 +111,7 @@ const PostInfoAndMedia = ({ openReplyModal, post, roles }: PostProps) => {
|
||||
};
|
||||
|
||||
const PostMessageMobile = ({ post }: PostProps) => {
|
||||
const { cid, content, parentCid, postCid, state, subplebbitAddress } = post || {};
|
||||
const { cid, content, deleted, parentCid, postCid, reason, removed, state, subplebbitAddress } = post || {};
|
||||
|
||||
const params = useParams();
|
||||
const location = useLocation();
|
||||
@@ -138,7 +139,20 @@ const PostMessageMobile = ({ post }: PostProps) => {
|
||||
<br />
|
||||
</>
|
||||
)}
|
||||
<Markdown content={displayContent} />
|
||||
{removed ? (
|
||||
<span className={styles.removedContent}>(THIS POST WAS REMOVED)</span>
|
||||
) : deleted ? (
|
||||
<span className={styles.removedContent}>User deleted this post.</span>
|
||||
) : (
|
||||
<Markdown content={displayContent} />
|
||||
)}
|
||||
{(removed || deleted) && reason && (
|
||||
<span>
|
||||
<br />
|
||||
<br />
|
||||
reason: {reason}
|
||||
</span>
|
||||
)}
|
||||
{!isReply && content.length > 1000 && !isInPostView && (
|
||||
<span className={styles.abbr}>
|
||||
<br />
|
||||
@@ -157,6 +171,11 @@ const PostMessageMobile = ({ post }: PostProps) => {
|
||||
};
|
||||
|
||||
const PostMobile = ({ openReplyModal, post, roles, showAllReplies }: PostProps) => {
|
||||
// handle pending mod or author edit
|
||||
const { editedComment } = useEditedComment({ comment: post });
|
||||
if (editedComment) {
|
||||
post = editedComment;
|
||||
}
|
||||
const { t } = useTranslation();
|
||||
const { cid, content, pinned, replyCount, subplebbitAddress } = post || {};
|
||||
const { isDescription, isRules } = post || {}; // custom properties, not from api
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
}
|
||||
|
||||
.hideButtonWrapper {
|
||||
height: 15px;
|
||||
width: 18px;
|
||||
padding-right: 5px;
|
||||
float: left;
|
||||
}
|
||||
@@ -26,6 +28,10 @@
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.postDesktopBlocked {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* clearfix to contain float elements */
|
||||
.postDesktop::after {
|
||||
content: "";
|
||||
@@ -52,11 +58,11 @@
|
||||
}
|
||||
|
||||
.postDesktop .postInfo {
|
||||
padding-top: 3px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.postDesktop .checkbox, .replyDesktop .checkbox {
|
||||
display: inline-block;
|
||||
padding: 3px 7px 3px 4px;
|
||||
filter: var(--filter80);
|
||||
}
|
||||
@@ -272,6 +278,7 @@
|
||||
.postMobile .postMessage {
|
||||
padding: 10px;
|
||||
font-size: var(--post-mobile-content-font-size);
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.postMobile .postLink {
|
||||
@@ -307,7 +314,7 @@
|
||||
}
|
||||
|
||||
.replyDesktop {
|
||||
padding-bottom: 4px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.replyDesktop .sideArrows {
|
||||
@@ -322,7 +329,7 @@
|
||||
border-left: none;
|
||||
border-top: none;
|
||||
display: table;
|
||||
padding: 2px;
|
||||
padding: 0 2px 2px 2px;
|
||||
}
|
||||
|
||||
.replyDesktop .postInfo {
|
||||
@@ -387,6 +394,11 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.removedContent {
|
||||
font-weight: 700;
|
||||
color: red;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.postDesktop {
|
||||
display: none;
|
||||
|
||||
@@ -6,6 +6,7 @@ import PostMobile from './post-mobile';
|
||||
|
||||
export interface PostProps {
|
||||
index?: number;
|
||||
isHidden?: boolean;
|
||||
post?: any;
|
||||
reply?: any;
|
||||
roles?: Role[];
|
||||
|
||||
@@ -71,6 +71,16 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.footer input[type="checkbox"] {
|
||||
margin: 0 3px;
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
.spoilerButton {
|
||||
display: inline-block;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.parentCid {
|
||||
color: var(--post-form-field-input-color);
|
||||
position: absolute;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Draggable from 'react-draggable';
|
||||
@@ -8,6 +8,7 @@ import { isValidURL } from '../../lib/utils/url-utils';
|
||||
import useReply from '../../hooks/use-reply';
|
||||
import useIsMobile from '../../hooks/use-is-mobile';
|
||||
import styles from './reply-modal.module.css';
|
||||
import { LinkTypePreviewer } from '../post-form';
|
||||
import _ from 'lodash';
|
||||
|
||||
interface ReplyModalProps {
|
||||
@@ -24,6 +25,8 @@ const ReplyModal = ({ closeModal, parentCid, scrollY }: ReplyModalProps) => {
|
||||
const account = useAccount();
|
||||
const { displayName } = account?.author || {};
|
||||
|
||||
const [url, setUrl] = useState('');
|
||||
|
||||
const textRef = useRef<HTMLTextAreaElement>(null);
|
||||
const urlRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
@@ -95,7 +98,15 @@ const ReplyModal = ({ closeModal, parentCid, scrollY }: ReplyModalProps) => {
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.link}>
|
||||
<input type='text' ref={urlRef} placeholder={_.capitalize(t('link'))} onChange={(e) => setContent.link(e.target.value)} />
|
||||
<input
|
||||
type='text'
|
||||
ref={urlRef}
|
||||
placeholder={_.capitalize(t('link'))}
|
||||
onChange={(e) => {
|
||||
setUrl(e.target.value);
|
||||
setContent.link(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
<span className={styles.parentCid} ref={parentCidRef}>
|
||||
@@ -114,6 +125,19 @@ const ReplyModal = ({ closeModal, parentCid, scrollY }: ReplyModalProps) => {
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.footer}>
|
||||
{url && (
|
||||
<>
|
||||
{t('file_type')}: <LinkTypePreviewer link={url} />{' '}
|
||||
</>
|
||||
)}
|
||||
<span className={styles.spoilerButton}>
|
||||
[
|
||||
<label>
|
||||
<input type='checkbox' onChange={(e) => setContent.spoiler(e.target.checked)} />
|
||||
{t('spoiler')}?
|
||||
</label>
|
||||
]
|
||||
</span>
|
||||
<button onClick={onPublishReply}>{t('reply')}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -107,7 +107,7 @@ const InterfaceSettings = () => {
|
||||
return (
|
||||
<div className={styles.interfaceSettings}>
|
||||
<div className={styles.version}>
|
||||
Version:{' '}
|
||||
{t('version')}:{' '}
|
||||
<a href={`https://github.com/plebbit/plebchan/releases/tag/v${packageJson.version}`} target='_blank' rel='noopener noreferrer'>
|
||||
{packageJson.version}
|
||||
</a>
|
||||
|
||||
@@ -44,7 +44,7 @@ const SettingsModal = () => {
|
||||
<div className={`${styles.setting} ${styles.category}`}>
|
||||
<label onClick={() => setShowInterfaceSettings(!showInterfaceSettings)}>
|
||||
<span className={showInterfaceSettings ? styles.hideButton : styles.showButton} />
|
||||
Interface
|
||||
{t('interface')}
|
||||
</label>
|
||||
</div>
|
||||
{showInterfaceSettings && <InterfaceSettings />}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './topbar';
|
||||
+11
-25
@@ -1,17 +1,17 @@
|
||||
|
||||
.boardNavDesktop {
|
||||
font-size: var(--boardnav-font-size);
|
||||
color: var(--boardnav-separator-color);
|
||||
font-size: var(--topbar-font-size);
|
||||
color: var(--topbar-separator-color);
|
||||
}
|
||||
|
||||
.boardNavDesktop a {
|
||||
color: var(--boardnav-desktop-link-text-color);
|
||||
text-decoration: var(--boardnav-desktop-link-text-decoration);
|
||||
color: var(--topbar-desktop-link-text-color);
|
||||
text-decoration: var(--topbar-desktop-link-text-decoration);
|
||||
}
|
||||
|
||||
.boardNavDesktop a:hover {
|
||||
color: var(--boardnav-desktop-link-text-color-hover);
|
||||
text-decoration: var(--boardnav-desktop-link-text-decoration-hover);
|
||||
color: var(--topbar-desktop-link-text-color-hover);
|
||||
text-decoration: var(--topbar-desktop-link-text-decoration-hover);
|
||||
}
|
||||
|
||||
.navTopRight {
|
||||
@@ -21,31 +21,17 @@
|
||||
|
||||
.boardNavMobile {
|
||||
padding: 2px 4px;
|
||||
background-color: var(--boardnav-mobile-background-color);
|
||||
border-bottom: var(--boardnav-mobile-border-bottom);
|
||||
font-size: var(--boardnav-mobile-font-size);
|
||||
background-color: var(--topbar-mobile-background-color);
|
||||
border-bottom: var(--topbar-mobile-border-bottom);
|
||||
font-size: var(--topbar-mobile-font-size);
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: 3;
|
||||
animation-name: stickyMenuAnimation;
|
||||
animation-duration: 1s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: 1;
|
||||
animation-fill-mode: both;
|
||||
animation-play-state: paused;
|
||||
transition: top 0.3s ease-in-out;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
@keyframes stickyMenuAnimation {
|
||||
0% {
|
||||
top: 0px;
|
||||
}
|
||||
100% {
|
||||
top: -50px;
|
||||
}
|
||||
}
|
||||
|
||||
.boardNavMobile .pageJump a {
|
||||
color: var(--button-text-color-mobile);
|
||||
text-decoration: var(--button-text-decoration-mobile);
|
||||
@@ -57,7 +43,7 @@
|
||||
}
|
||||
|
||||
.boardNavMobile select {
|
||||
font-size: var(--boardnav-mobile-font-size);
|
||||
font-size: var(--topbar-mobile-font-size);
|
||||
border: var(--select-border, revert);
|
||||
background-color: var(--select-background-color, revert);
|
||||
color: var(--select-color, revert);
|
||||
@@ -1,11 +1,13 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useAccountComment } from '@plebbit/plebbit-react-hooks';
|
||||
import { Link, useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { isAllView, isCatalogView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
||||
import styles from './board-nav.module.css';
|
||||
import styles from './topbar.module.css';
|
||||
import useDefaultSubplebbits, { categorizeSubplebbits, useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
const BoardNavDesktop = () => {
|
||||
const TopBarDesktop = () => {
|
||||
const { t } = useTranslation();
|
||||
const location = useLocation();
|
||||
const isInCatalogView = isCatalogView(location.pathname);
|
||||
@@ -30,14 +32,14 @@ const BoardNavDesktop = () => {
|
||||
{renderSubplebbits(interestsSubs)}] [{renderSubplebbits(randomSubs)}] [{renderSubplebbits(internationalSubs)}]
|
||||
</span>
|
||||
<span className={styles.navTopRight}>
|
||||
[<Link to={!location.pathname.endsWith('settings') ? location.pathname + '/settings' : location.pathname}>{t('settings')}</Link>] [<Link to='/'>{t('home')}</Link>
|
||||
]
|
||||
[<Link to={!location.pathname.endsWith('settings') ? location.pathname.replace(/\/$/, '') + '/settings' : location.pathname}>{t('settings')}</Link>] [
|
||||
<Link to='/'>{t('home')}</Link>]
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const BoardNavMobile = ({ subplebbitAddress }: { subplebbitAddress: string }) => {
|
||||
const TopBarMobile = ({ subplebbitAddress }: { subplebbitAddress: string }) => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const subplebbitAddresses = useDefaultSubplebbitAddresses();
|
||||
@@ -67,77 +69,49 @@ const BoardNavMobile = ({ subplebbitAddress }: { subplebbitAddress: string }) =>
|
||||
</select>
|
||||
);
|
||||
|
||||
// navbar animation on scroll
|
||||
const [visible, setVisible] = useState(true);
|
||||
const prevScrollPosRef = useRef(0);
|
||||
|
||||
useEffect(() => {
|
||||
const debouncedHandleScroll = debounce(() => {
|
||||
const currentScrollPos = window.scrollY;
|
||||
const prevScrollPos = prevScrollPosRef.current;
|
||||
|
||||
setVisible(prevScrollPos > currentScrollPos || currentScrollPos < 10);
|
||||
prevScrollPosRef.current = currentScrollPos;
|
||||
}, 50);
|
||||
|
||||
window.addEventListener('scroll', debouncedHandleScroll);
|
||||
|
||||
return () => window.removeEventListener('scroll', debouncedHandleScroll);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={styles.boardNavMobile} id='sticky-menu'>
|
||||
<div className={styles.boardNavMobile} id='sticky-menu' style={{ top: visible ? 0 : '-23px' }}>
|
||||
<div className={styles.boardSelect}>
|
||||
<strong>{t('board')}</strong>
|
||||
{boardSelect}
|
||||
</div>
|
||||
<div className={styles.pageJump}>
|
||||
<Link to={useLocation().pathname + '/settings'}>{t('settings')}</Link>
|
||||
<Link to={useLocation().pathname.replace(/\/$/, '') + '/settings'}>{t('settings')}</Link>
|
||||
<Link to='/'>{t('home')}</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// sticky menu animation
|
||||
// will trigger more than once with hot reloading during development
|
||||
if (!window.STICKY_MENU_SCROLL_LISTENER) {
|
||||
window.STICKY_MENU_SCROLL_LISTENER = true;
|
||||
|
||||
const scrollRange = 50; // the animation css px range in stickyMenuAnimation, must also edit css animation 100%: {top}
|
||||
let currentScrollInRange = 0,
|
||||
previousScroll = 0;
|
||||
|
||||
window.addEventListener('scroll', () => {
|
||||
// find difference between current and last scroll position
|
||||
const currentScroll = window.scrollY;
|
||||
const scrollDifference = currentScroll - previousScroll;
|
||||
previousScroll = currentScroll;
|
||||
|
||||
// find new current scroll in range
|
||||
const previousScrollInRange = currentScrollInRange;
|
||||
currentScrollInRange += scrollDifference;
|
||||
if (currentScrollInRange > scrollRange) {
|
||||
currentScrollInRange = 0;
|
||||
} else if (currentScrollInRange < 0) {
|
||||
currentScrollInRange = 0;
|
||||
}
|
||||
|
||||
// fix mobile overflow scroll bug
|
||||
if (currentScroll <= 0) {
|
||||
currentScrollInRange = 0;
|
||||
}
|
||||
|
||||
// no changes
|
||||
if (currentScrollInRange === previousScrollInRange) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the menu element
|
||||
const menuElement = document.getElementById('sticky-menu');
|
||||
if (!menuElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
// control progress of the animation using negative animation-delay (0 to -1s)
|
||||
const animationPercent = currentScrollInRange / scrollRange;
|
||||
menuElement.style.animationDelay = animationPercent * -1 + 's';
|
||||
});
|
||||
}
|
||||
|
||||
const BoardNav = () => {
|
||||
const TopBar = () => {
|
||||
const params = useParams();
|
||||
const accountComment = useAccountComment({ commentIndex: params?.accountCommentIndex as any });
|
||||
const subplebbitAddress = params?.subplebbitAddress || accountComment?.subplebbitAddress;
|
||||
|
||||
return (
|
||||
<>
|
||||
<BoardNavDesktop />
|
||||
<BoardNavMobile subplebbitAddress={subplebbitAddress} />
|
||||
<TopBarDesktop />
|
||||
<TopBarMobile subplebbitAddress={subplebbitAddress} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BoardNav;
|
||||
export default TopBar;
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useMemo } from 'react';
|
||||
import { ChallengeVerification, Comment, PublishCommentOptions, usePublishComment } from '@plebbit/plebbit-react-hooks';
|
||||
import { create } from 'zustand';
|
||||
import useChallengesStore from './use-challenges';
|
||||
import { alertChallengeVerificationFailed } from '../lib/utils/challenge-utils';
|
||||
import useChallengesStore from '../stores/use-challenges-store';
|
||||
|
||||
type SetReplyStoreData = {
|
||||
subplebbitAddress: string;
|
||||
|
||||
@@ -8,6 +8,12 @@ export const isAllView = (pathname: string): boolean => {
|
||||
return pathname.startsWith('/p/all');
|
||||
};
|
||||
|
||||
export const isBoardView = (pathname: string, params: ParamsType): boolean => {
|
||||
// some subs might use emojis in their address, so we need to decode the pathname
|
||||
const decodedPathname = decodeURIComponent(pathname);
|
||||
return params.subplebbitAddress ? decodedPathname.startsWith(`/p/${params.subplebbitAddress}`) : false;
|
||||
};
|
||||
|
||||
export const isCatalogView = (pathname: string): boolean => {
|
||||
return pathname.endsWith('/catalog') || pathname.endsWith('/catalog/settings');
|
||||
};
|
||||
@@ -44,6 +50,8 @@ export const isSubscriptionsView = (pathname: string): boolean => {
|
||||
|
||||
export const isNotFoundView = (pathname: string, params: ParamsType): boolean => {
|
||||
return (
|
||||
!isAllView(pathname) &&
|
||||
!isBoardView(pathname, params) &&
|
||||
!isCatalogView(pathname) &&
|
||||
!isDescriptionView(pathname, params) &&
|
||||
!isHomeView(pathname) &&
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
interface FeedResetState {
|
||||
reset: (() => void) | null;
|
||||
setResetFunction: (resetFunction: () => void) => void;
|
||||
}
|
||||
|
||||
const useFeedResetStore = create<FeedResetState>((set) => ({
|
||||
reset: null,
|
||||
setResetFunction: (resetFunction) => set({ reset: resetFunction }),
|
||||
}));
|
||||
|
||||
export default useFeedResetStore;
|
||||
+60
-60
@@ -6,16 +6,16 @@
|
||||
--board-address-font-size: x-small;
|
||||
|
||||
/* board nav */
|
||||
--boardnav-font-size: 9pt;
|
||||
--boardnav-separator-color: #b86;
|
||||
--boardnav-desktop-link-text-color: #800;
|
||||
--boardnav-desktop-link-text-color-hover: #e00;
|
||||
--boardnav-desktop-link-text-decoration: none;
|
||||
--boardnav-desktop-link-text-decoration-hover: underline;
|
||||
--boardnav-mobile-background-color: #f0e0d6;
|
||||
--boardnav-mobile-border-bottom: 2px solid #d9c5b7;
|
||||
--boardnav-mobile-font-size: 10px;
|
||||
--boardnav-mobile-button-text-color: navy;
|
||||
--topbar-font-size: 9pt;
|
||||
--topbar-separator-color: #b86;
|
||||
--topbar-desktop-link-text-color: #800;
|
||||
--topbar-desktop-link-text-color-hover: #e00;
|
||||
--topbar-desktop-link-text-decoration: none;
|
||||
--topbar-desktop-link-text-decoration-hover: underline;
|
||||
--topbar-mobile-background-color: #f0e0d6;
|
||||
--topbar-mobile-border-bottom: 2px solid #d9c5b7;
|
||||
--topbar-mobile-font-size: 10px;
|
||||
--topbar-mobile-button-text-color: navy;
|
||||
|
||||
/* body */
|
||||
--body-background-base-color: #ffe;
|
||||
@@ -192,16 +192,16 @@
|
||||
--board-address-text-color: #af0a0f;
|
||||
|
||||
/* board nav */
|
||||
--boardnav-font-size: 9pt;
|
||||
--boardnav-separator-color: #89a;
|
||||
--boardnav-desktop-link-text-color: #34345c;
|
||||
--boardnav-desktop-link-text-color-hover: #d00;
|
||||
--boardnav-desktop-link-text-decoration: none;
|
||||
--boardnav-desktop-link-text-decoration-hover: none;
|
||||
--boardnav-mobile-background-color: #d6daf0;
|
||||
--boardnav-mobile-border-bottom: 2px solid #b7c5d9;
|
||||
--boardnav-mobile-font-size: 10px;
|
||||
--boardnav-mobile-button-text-color: #34345c;
|
||||
--topbar-font-size: 9pt;
|
||||
--topbar-separator-color: #89a;
|
||||
--topbar-desktop-link-text-color: #34345c;
|
||||
--topbar-desktop-link-text-color-hover: #d00;
|
||||
--topbar-desktop-link-text-decoration: none;
|
||||
--topbar-desktop-link-text-decoration-hover: none;
|
||||
--topbar-mobile-background-color: #d6daf0;
|
||||
--topbar-mobile-border-bottom: 2px solid #b7c5d9;
|
||||
--topbar-mobile-font-size: 10px;
|
||||
--topbar-mobile-button-text-color: #34345c;
|
||||
|
||||
/* body */
|
||||
--body-background-base-color: #eef2ff;
|
||||
@@ -375,16 +375,16 @@
|
||||
--body-font-size: 16px;
|
||||
|
||||
/* board nav */
|
||||
--boardnav-font-size: 11pt;
|
||||
--boardnav-separator-color: none;
|
||||
--boardnav-desktop-link-text-color: #00e;
|
||||
--boardnav-desktop-link-text-color-hover: red;
|
||||
--boardnav-desktop-link-text-decoration: underline;
|
||||
--boardnav-desktop-link-text-decoration-hover: underline;
|
||||
--boardnav-mobile-background-color: #f0e0d6;
|
||||
--boardnav-mobile-border-bottom: 2px solid #d9c5b7;
|
||||
--boardnav-mobile-font-size: 10pt;
|
||||
--boardnav-mobile-button-text-color: #00f;
|
||||
--topbar-font-size: 11pt;
|
||||
--topbar-separator-color: none;
|
||||
--topbar-desktop-link-text-color: #00e;
|
||||
--topbar-desktop-link-text-color-hover: red;
|
||||
--topbar-desktop-link-text-decoration: underline;
|
||||
--topbar-desktop-link-text-decoration-hover: underline;
|
||||
--topbar-mobile-background-color: #f0e0d6;
|
||||
--topbar-mobile-border-bottom: 2px solid #d9c5b7;
|
||||
--topbar-mobile-font-size: 9pt;
|
||||
--topbar-mobile-button-text-color: #00f;
|
||||
|
||||
/* board title */
|
||||
--board-title-font-size: 32px;
|
||||
@@ -526,16 +526,16 @@
|
||||
--board-address-text-color: #af0a0f;
|
||||
|
||||
/* board nav */
|
||||
--boardnav-font-size: 11pt;
|
||||
--boardnav-separator-color: none;
|
||||
--boardnav-desktop-link-text-color: #34345c;
|
||||
--boardnav-desktop-link-text-color-hover: #d00;
|
||||
--boardnav-desktop-link-text-decoration: underline;
|
||||
--boardnav-desktop-link-text-decoration-hover: underline;
|
||||
--boardnav-mobile-background-color: #d6daf0;
|
||||
--boardnav-mobile-border-bottom: 2px solid #b7c5d9;
|
||||
--boardnav-mobile-font-size: 10pt;
|
||||
--boardnav-mobile-button-text-color: #34345c;
|
||||
--topbar-font-size: 11pt;
|
||||
--topbar-separator-color: none;
|
||||
--topbar-desktop-link-text-color: #34345c;
|
||||
--topbar-desktop-link-text-color-hover: #d00;
|
||||
--topbar-desktop-link-text-decoration: underline;
|
||||
--topbar-desktop-link-text-decoration-hover: underline;
|
||||
--topbar-mobile-background-color: #d6daf0;
|
||||
--topbar-mobile-border-bottom: 2px solid #b7c5d9;
|
||||
--topbar-mobile-font-size: 9pt;
|
||||
--topbar-mobile-button-text-color: #34345c;
|
||||
|
||||
/* body */
|
||||
--body-background-base-color: #eef2ff;
|
||||
@@ -689,16 +689,16 @@
|
||||
--body-font-size: 13px;
|
||||
|
||||
/* board nav */
|
||||
--boardnav-font-size: 9pt;
|
||||
--boardnav-separator-color: #c5c8c6;
|
||||
--boardnav-desktop-link-text-color: #81a2be;
|
||||
--boardnav-desktop-link-text-color-hover: #5f89ac;
|
||||
--boardnav-desktop-link-text-decoration: none;
|
||||
--boardnav-desktop-link-text-decoration-hover: none;
|
||||
--boardnav-mobile-background-color: #1d1f21;
|
||||
--boardnav-mobile-border-bottom: 2px solid #282a2e;
|
||||
--boardnav-mobile-font-size: 10px;
|
||||
--boardnav-mobile-button-text-color: #81a2be;
|
||||
--topbar-font-size: 9pt;
|
||||
--topbar-separator-color: #c5c8c6;
|
||||
--topbar-desktop-link-text-color: #81a2be;
|
||||
--topbar-desktop-link-text-color-hover: #5f89ac;
|
||||
--topbar-desktop-link-text-decoration: none;
|
||||
--topbar-desktop-link-text-decoration-hover: none;
|
||||
--topbar-mobile-background-color: #1d1f21;
|
||||
--topbar-mobile-border-bottom: 2px solid #282a2e;
|
||||
--topbar-mobile-font-size: 10px;
|
||||
--topbar-mobile-button-text-color: #81a2be;
|
||||
|
||||
/* board title */
|
||||
--board-title-font-size: 28px;
|
||||
@@ -875,16 +875,16 @@
|
||||
--body-font-size: 13px;
|
||||
|
||||
/* board nav */
|
||||
--boardnav-font-size: 9pt;
|
||||
--boardnav-separator-color: #333;
|
||||
--boardnav-desktop-link-text-color: #f60;
|
||||
--boardnav-desktop-link-text-color-hover: #f30;
|
||||
--boardnav-desktop-link-text-decoration: none;
|
||||
--boardnav-desktop-link-text-decoration-hover: none;
|
||||
--boardnav-mobile-background-color: #ddd;
|
||||
--boardnav-mobile-border-bottom: 2px solid #ccc;
|
||||
--boardnav-mobile-font-size: 10px;
|
||||
--boardnav-mobile-button-text-color: #f60;
|
||||
--topbar-font-size: 9pt;
|
||||
--topbar-separator-color: #333;
|
||||
--topbar-desktop-link-text-color: #f60;
|
||||
--topbar-desktop-link-text-color-hover: #f30;
|
||||
--topbar-desktop-link-text-decoration: none;
|
||||
--topbar-desktop-link-text-decoration-hover: none;
|
||||
--topbar-mobile-background-color: #ddd;
|
||||
--topbar-mobile-border-bottom: 2px solid #ccc;
|
||||
--topbar-mobile-font-size: 10px;
|
||||
--topbar-mobile-button-text-color: #f60;
|
||||
|
||||
/* board title */
|
||||
--board-title-font-size: 28px;
|
||||
|
||||
@@ -8,6 +8,7 @@ import { isAllView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
||||
import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
|
||||
import useFeedStateString from '../../hooks/use-feed-state-string';
|
||||
import useReplyModal from '../../hooks/use-reply-modal';
|
||||
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
||||
import LoadingEllipsis from '../../components/loading-ellipsis';
|
||||
import Post from '../../components/post';
|
||||
import ReplyModal from '../../components/reply-modal';
|
||||
@@ -40,7 +41,12 @@ const Board = () => {
|
||||
}, [isInAllView, isInSubscriptionsView, subplebbitAddress, defaultSubplebbitAddresses, subscriptions]);
|
||||
|
||||
const sortType = 'active';
|
||||
const { feed, hasMore, loadMore } = useFeed({ subplebbitAddresses, sortType });
|
||||
const { feed, hasMore, loadMore, reset } = useFeed({ subplebbitAddresses, sortType });
|
||||
|
||||
const setResetFunction = useFeedResetStore((state) => state.setResetFunction);
|
||||
useEffect(() => {
|
||||
setResetFunction(reset);
|
||||
}, [reset, setResetFunction]);
|
||||
|
||||
const subplebbit = useSubplebbit({ subplebbitAddress });
|
||||
const { createdAt, description, rules, shortAddress, state, suggested } = subplebbit || {};
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbi
|
||||
import useFeedStateString from '../../hooks/use-feed-state-string';
|
||||
import { useMultisubMetadata } from '../../hooks/use-default-subplebbits';
|
||||
import useWindowWidth from '../../hooks/use-window-width';
|
||||
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
||||
import CatalogRow from '../../components/catalog-row';
|
||||
import LoadingEllipsis from '../../components/loading-ellipsis';
|
||||
import SettingsModal from '../../components/settings-modal';
|
||||
@@ -61,7 +62,6 @@ const useFeedRows = (columnCount: number, feed: any, isFeedLoaded: boolean, subp
|
||||
return _feed;
|
||||
}, [feed, description, rules, address, isFeedLoaded, createdAt, title, shortAddress, avatarUrl, t, isInAllView, multisub]);
|
||||
|
||||
// Memoize rows calculation, ensuring it updates on changes to the modified feed or column count
|
||||
const rows = useMemo(() => {
|
||||
const rows = [];
|
||||
for (let i = 0; i < feedWithDescriptionAndRules.length; i += columnCount) {
|
||||
@@ -102,7 +102,12 @@ const Catalog = () => {
|
||||
// eslint-disable-next-line
|
||||
const postsPerPage = useMemo(() => (columnCount <= 2 ? 10 : columnCount === 3 ? 15 : columnCount === 4 ? 20 : 25), []);
|
||||
|
||||
const { feed, hasMore, loadMore } = useFeed({ subplebbitAddresses, sortType: 'active', postsPerPage });
|
||||
const { feed, hasMore, loadMore, reset } = useFeed({ subplebbitAddresses, sortType: 'active', postsPerPage });
|
||||
|
||||
const setResetFunction = useFeedResetStore((state) => state.setResetFunction);
|
||||
useEffect(() => {
|
||||
setResetFunction(reset);
|
||||
}, [reset, setResetFunction]);
|
||||
|
||||
const subplebbit = useSubplebbit({ subplebbitAddress });
|
||||
const { shortAddress, state, title } = subplebbit || {};
|
||||
@@ -130,10 +135,9 @@ const Catalog = () => {
|
||||
return <div className={styles.footer}>{footerContent}</div>;
|
||||
};
|
||||
|
||||
const isFeedloaded = feed.length > 0 || state === 'failed';
|
||||
const isFeedLoaded = feed.length > 0 || state === 'failed';
|
||||
|
||||
// split feed into rows
|
||||
const rows = useFeedRows(columnCount, feed, isFeedloaded, subplebbit);
|
||||
const rows = useFeedRows(columnCount, feed, isFeedLoaded, subplebbit);
|
||||
|
||||
// save the last Virtuoso state to restore it when navigating back
|
||||
const virtuosoRef = useRef<VirtuosoHandle | null>(null);
|
||||
|
||||
@@ -119,7 +119,6 @@
|
||||
}
|
||||
|
||||
.list a {
|
||||
display: inline-block;
|
||||
color: var(--homepage-box-link-text-color);
|
||||
text-decoration: var(--homepage-box-link-text-decoration);
|
||||
}
|
||||
@@ -140,7 +139,7 @@
|
||||
background-image: url('/public/assets/icons/offline.png');
|
||||
display: inline-block;
|
||||
vertical-align: text-top;
|
||||
margin-left: 2px;
|
||||
margin-right: 2px;
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
background-repeat: no-repeat;
|
||||
@@ -161,7 +160,7 @@
|
||||
float: left;
|
||||
width: 168px;
|
||||
margin-right: 15px;
|
||||
overflow: hidden;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.boardsBoxContent h3 {
|
||||
|
||||
+10
-9
@@ -3,6 +3,7 @@ import styles from './home.module.css';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { Comment, Subplebbit, useAccount, useAccountSubplebbits, useSubplebbits } from '@plebbit/plebbit-react-hooks';
|
||||
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
|
||||
import packageJson from '../../../package.json';
|
||||
import useDefaultSubplebbits, { useMultisubMetadata, useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
|
||||
import usePopularPosts from '../../hooks/use-popular-posts';
|
||||
@@ -73,15 +74,15 @@ const InfoBox = () => {
|
||||
|
||||
const Board = ({ isOffline, subplebbit }: { isOffline: boolean; subplebbit: Subplebbit }) => {
|
||||
const { t } = useTranslation();
|
||||
const { address, title, tags } = subplebbit;
|
||||
const { address, title, tags } = subplebbit || {};
|
||||
const nsfwTags = ['adult', 'gore'];
|
||||
const nsfwTag = tags.find((tag: string) => nsfwTags.includes(tag));
|
||||
|
||||
return (
|
||||
<div className={styles.subplebbit} key={address}>
|
||||
{isOffline && <span className={styles.offlineIcon} />}
|
||||
<Link to={`/p/${address}`}>{title || address}</Link>
|
||||
{nsfwTag && <span className={styles.nsfw}> ({t(nsfwTag)})</span>}
|
||||
{isOffline && <span className={styles.offlineIcon} />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -93,13 +94,13 @@ const Boards = ({ multisub, subplebbits }: { multisub: Subplebbit[]; subplebbits
|
||||
const { accountSubplebbits } = useAccountSubplebbits();
|
||||
const accountSubplebbitAddresses = Object.keys(accountSubplebbits);
|
||||
|
||||
const plebbitSubs = multisub.filter((sub) => sub.tags.includes('plebbit'));
|
||||
const plebbitSubs = multisub.filter((sub) => sub.tags?.includes('plebbit'));
|
||||
const interestsSubs = multisub.filter(
|
||||
(sub) => sub.tags.includes('topic') && !sub.tags.includes('plebbit') && !sub.tags.includes('country') && !sub.tags.includes('international'),
|
||||
(sub) => sub.tags?.includes('topic') && !sub.tags?.includes('plebbit') && !sub.tags?.includes('country') && !sub.tags?.includes('international'),
|
||||
);
|
||||
const randomSubs = multisub.filter((sub) => sub.tags.includes('random') && !sub.tags.includes('plebbit'));
|
||||
const internationalSubs = multisub.filter((sub) => sub.tags.includes('international') || sub.tags.includes('country'));
|
||||
const projectsSubs = multisub.filter((sub) => sub.tags.includes('project') && !sub.tags.includes('plebbit') && !sub.tags.includes('topic'));
|
||||
const randomSubs = multisub.filter((sub) => sub.tags?.includes('random') && !sub.tags?.includes('plebbit'));
|
||||
const internationalSubs = multisub.filter((sub) => sub.tags?.includes('international') || sub.tags?.includes('country'));
|
||||
const projectsSubs = multisub.filter((sub) => sub.tags?.includes('project') && !sub.tags?.includes('plebbit') && !sub.tags?.includes('topic'));
|
||||
|
||||
const isSubOffline = (address: string) => {
|
||||
const subplebbit = subplebbits && subplebbits.find((sub: Subplebbit) => sub?.address === address);
|
||||
@@ -166,7 +167,7 @@ const Boards = ({ multisub, subplebbits }: { multisub: Subplebbit[]; subplebbits
|
||||
{subscriptions.length > 0
|
||||
? subscriptions.map((address: string, index: number) => (
|
||||
<div className={styles.subplebbit} key={index}>
|
||||
<Link to={`/p/${address}`}>{address}</Link>
|
||||
<Link to={`/p/${address}`}>p/{address && Plebbit.getShortAddress(address)}</Link>
|
||||
</div>
|
||||
))
|
||||
: t('not_subscribed')}
|
||||
@@ -176,7 +177,7 @@ const Boards = ({ multisub, subplebbits }: { multisub: Subplebbit[]; subplebbits
|
||||
{accountSubplebbitAddresses.length > 0
|
||||
? accountSubplebbitAddresses.map((address: string, index: number) => (
|
||||
<div className={styles.subplebbit} key={index}>
|
||||
<Link to={`/p/${address}`}>p/{address}</Link>
|
||||
<Link to={`/p/${address}`}>p/{address && Plebbit.getShortAddress(address)}</Link>
|
||||
</div>
|
||||
))
|
||||
: t('not_moderating')}
|
||||
|
||||
Reference in New Issue
Block a user