Merge pull request #376 from plebbit/development

Development
This commit is contained in:
Tom (plebeius.eth)
2024-05-31 16:53:39 +02:00
committed by GitHub
76 changed files with 1145 additions and 361 deletions
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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}} উপর দেখুন"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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ä"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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}} पर देखें"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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}} で表示"
}
+18 -1
View File
@@ -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}}에서 보기"
}
+18 -1
View File
@@ -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}} वर पहा"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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}} లో చూడండి"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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}} پر دیکھیں"
}
+18 -1
View File
@@ -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}}"
}
+18 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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) {
+47 -27
View File
@@ -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>
)}
</>
@@ -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;
+1
View File
@@ -0,0 +1 @@
export { default } from './board-header';
-1
View File
@@ -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;
+11 -9
View File
@@ -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
View File
@@ -1 +1 @@
export { default } from './post-form';
export { LinkTypePreviewer, default } from './post-form';
+38 -10
View File
@@ -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) {
+26 -13
View File
@@ -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
+15 -3
View File
@@ -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;
+1
View File
@@ -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;
+26 -2
View File
@@ -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 />}
+1
View File
@@ -0,0 +1 @@
export { default } from './topbar';
@@ -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 -1
View File
@@ -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
View File
@@ -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) &&
+13
View File
@@ -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
View File
@@ -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;
+7 -1
View File
@@ -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 || {};
+9 -5
View File
@@ -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);
+2 -3
View File
@@ -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
View File
@@ -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')}