mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
feat(home stats): load stats progressively
This commit is contained in:
@@ -50,7 +50,6 @@
|
||||
"gif": "جي آي إف",
|
||||
"total_posts": "إجمالي المشاركات:",
|
||||
"current_users": "المستخدمون الحاليون:",
|
||||
"boards_tracked": "المنتديات المتابعة:",
|
||||
"challenge_counter": "{{index}} من {{total}}",
|
||||
"embed": "تضمين",
|
||||
"remove": "إزالة",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "فيديو YouTube",
|
||||
"youtube_thumbnail_link_conversion_notice": "تم اكتشاف رابط YouTube. جارٍ لصق الصورة المصغرة خلال {{count}}..."
|
||||
"youtube_thumbnail_link_conversion_notice": "تم اكتشاف رابط YouTube. جارٍ لصق الصورة المصغرة خلال {{count}}...",
|
||||
"stats_scope_all_listed_boards": "جميع اللوحات المُدرجة (بطيء)",
|
||||
"stats_scope_directory_boards": "لوحات الدليل فقط (سريع)",
|
||||
"boards_loaded": "اللوحات المحملة:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "জিআইএফ",
|
||||
"total_posts": "মোট পোস্টসমূহ:",
|
||||
"current_users": "বর্তমান ব্যবহারকারীরা:",
|
||||
"boards_tracked": "ট্র্যাক করা বোর্ডসমূহ:",
|
||||
"challenge_counter": "{{index}} থেকে {{total}}",
|
||||
"embed": "এম্বেড",
|
||||
"remove": "অপসারণ",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube ভিডিও",
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube লিংক শনাক্ত হয়েছে। {{count}}-এর মধ্যে থাম্বনেইল ছবি পেস্ট করা হচ্ছে..."
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube লিংক শনাক্ত হয়েছে। {{count}}-এর মধ্যে থাম্বনেইল ছবি পেস্ট করা হচ্ছে...",
|
||||
"stats_scope_all_listed_boards": "সব তালিকাভুক্ত বোর্ড (ধীর)",
|
||||
"stats_scope_directory_boards": "শুধু ডিরেক্টরি বোর্ড (দ্রুত)",
|
||||
"boards_loaded": "লোড করা বোর্ড:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "Celkem příspěvků:",
|
||||
"current_users": "současní uživatelé:",
|
||||
"boards_tracked": "sledované nástěnky:",
|
||||
"challenge_counter": "{{index}} z {{total}}",
|
||||
"embed": "vložit",
|
||||
"remove": "odstranit",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "video YouTube",
|
||||
"youtube_thumbnail_link_conversion_notice": "Byl zjištěn odkaz na YouTube. Vkládám náhledový obrázek za {{count}}..."
|
||||
"youtube_thumbnail_link_conversion_notice": "Byl zjištěn odkaz na YouTube. Vkládám náhledový obrázek za {{count}}...",
|
||||
"stats_scope_all_listed_boards": "Všechny uvedené nástěnky (pomalé)",
|
||||
"stats_scope_directory_boards": "Pouze nástěnky v adresáři (rychlé)",
|
||||
"boards_loaded": "načtené nástěnky:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "Samlede opslag:",
|
||||
"current_users": "nuværende brugere:",
|
||||
"boards_tracked": "sporede opslagstavler:",
|
||||
"challenge_counter": "{{index}} af {{total}}",
|
||||
"embed": "integrer",
|
||||
"remove": "fjern",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube-video",
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube-link registreret. Indsætter miniaturebillede om {{count}}..."
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube-link registreret. Indsætter miniaturebillede om {{count}}...",
|
||||
"stats_scope_all_listed_boards": "Alle viste boards (langsomt)",
|
||||
"stats_scope_directory_boards": "Kun boards i kataloget (hurtigt)",
|
||||
"boards_loaded": "indlæste boards:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "Gesamtanzahl der Beiträge:",
|
||||
"current_users": "aktuelle Benutzer:",
|
||||
"boards_tracked": "verfolgte Boards:",
|
||||
"challenge_counter": "{{index}} von {{total}}",
|
||||
"embed": "einbetten",
|
||||
"remove": "entfernen",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube-Video",
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube-Link erkannt. Vorschaubild wird in {{count}} eingefügt..."
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube-Link erkannt. Vorschaubild wird in {{count}} eingefügt...",
|
||||
"stats_scope_all_listed_boards": "Alle aufgelisteten Boards (langsam)",
|
||||
"stats_scope_directory_boards": "Nur Verzeichnis-Boards (schnell)",
|
||||
"boards_loaded": "geladene Boards:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "Συνολικές Δημοσιεύσεις:",
|
||||
"current_users": "τρέχοντες χρήστες:",
|
||||
"boards_tracked": "πίνακες που παρακολουθούνται:",
|
||||
"challenge_counter": "{{index}} από {{total}}",
|
||||
"embed": "ενσωμάτωση",
|
||||
"remove": "αφαίρεση",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "βίντεο YouTube",
|
||||
"youtube_thumbnail_link_conversion_notice": "Εντοπίστηκε σύνδεσμος YouTube. Επικόλληση μικρογραφίας σε {{count}}..."
|
||||
"youtube_thumbnail_link_conversion_notice": "Εντοπίστηκε σύνδεσμος YouTube. Επικόλληση μικρογραφίας σε {{count}}...",
|
||||
"stats_scope_all_listed_boards": "Όλα τα καταχωρισμένα boards (αργό)",
|
||||
"stats_scope_directory_boards": "Μόνο boards καταλόγου (γρήγορο)",
|
||||
"boards_loaded": "φορτωμένοι πίνακες:"
|
||||
}
|
||||
|
||||
@@ -56,7 +56,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "Total Posts:",
|
||||
"current_users": "current users:",
|
||||
"boards_tracked": "boards tracked:",
|
||||
"challenge_counter": "{{index}} of {{total}}",
|
||||
"embed": "embed",
|
||||
"remove": "remove",
|
||||
@@ -420,5 +419,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube video",
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube link detected. Pasting thumbnail image in {{count}}..."
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube link detected. Pasting thumbnail image in {{count}}...",
|
||||
"stats_scope_all_listed_boards": "All listed boards (slow)",
|
||||
"stats_scope_directory_boards": "Directory boards only (fast)",
|
||||
"boards_loaded": "boards loaded:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "Total de publicaciones:",
|
||||
"current_users": "usuarios actuales:",
|
||||
"boards_tracked": "foros seguidos:",
|
||||
"challenge_counter": "{{index}} de {{total}}",
|
||||
"embed": "incrustar",
|
||||
"remove": "eliminar",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "video de YouTube",
|
||||
"youtube_thumbnail_link_conversion_notice": "Se detectó un enlace de YouTube. Pegando imagen en miniatura en {{count}}..."
|
||||
"youtube_thumbnail_link_conversion_notice": "Se detectó un enlace de YouTube. Pegando imagen en miniatura en {{count}}...",
|
||||
"stats_scope_all_listed_boards": "Todos los boards listados (lento)",
|
||||
"stats_scope_directory_boards": "Solo boards del directorio (rápido)",
|
||||
"boards_loaded": "tableros cargados:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "گیف",
|
||||
"total_posts": "کل پستها:",
|
||||
"current_users": "کاربران فعلی:",
|
||||
"boards_tracked": "تالارهای پیگیری شده:",
|
||||
"challenge_counter": "{{index}} از {{total}}",
|
||||
"embed": "جاسازی",
|
||||
"remove": "حذف",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "ویدیوی YouTube",
|
||||
"youtube_thumbnail_link_conversion_notice": "پیوند YouTube شناسایی شد. در حال چسباندن تصویر بندانگشتی در {{count}}..."
|
||||
"youtube_thumbnail_link_conversion_notice": "پیوند YouTube شناسایی شد. در حال چسباندن تصویر بندانگشتی در {{count}}...",
|
||||
"stats_scope_all_listed_boards": "همهٔ بردهای فهرستشده (کند)",
|
||||
"stats_scope_directory_boards": "فقط بردهای دایرکتوری (سریع)",
|
||||
"boards_loaded": "بردهای بارگذاریشده:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "Viestejä yhteensä:",
|
||||
"current_users": "nykyiset käyttäjät:",
|
||||
"boards_tracked": "seuratut foorumit:",
|
||||
"challenge_counter": "{{index}} / {{total}}",
|
||||
"embed": "upota",
|
||||
"remove": "poista",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube-video",
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube-linkki havaittu. Liitetään pikkukuva {{count}} kuluttua..."
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube-linkki havaittu. Liitetään pikkukuva {{count}} kuluttua...",
|
||||
"stats_scope_all_listed_boards": "Kaikki listatut boardit (hidas)",
|
||||
"stats_scope_directory_boards": "Vain hakemiston boardit (nopea)",
|
||||
"boards_loaded": "ladatut boardit:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "Kabuuang Posts:",
|
||||
"current_users": "kasalukuyang mga gumagamit:",
|
||||
"boards_tracked": "mga board na sinusubaybayan:",
|
||||
"challenge_counter": "{{index}} ng {{total}}",
|
||||
"embed": "i-embed",
|
||||
"remove": "alisin",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "video sa YouTube",
|
||||
"youtube_thumbnail_link_conversion_notice": "May nakita na link ng YouTube. Ipi-paste ang thumbnail image sa loob ng {{count}}..."
|
||||
"youtube_thumbnail_link_conversion_notice": "May nakita na link ng YouTube. Ipi-paste ang thumbnail image sa loob ng {{count}}...",
|
||||
"stats_scope_all_listed_boards": "Lahat ng nakalistang board (mabagal)",
|
||||
"stats_scope_directory_boards": "Mga board ng directory lang (mabilis)",
|
||||
"boards_loaded": "mga board na na-load:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "Total des publications:",
|
||||
"current_users": "utilisateurs actuels:",
|
||||
"boards_tracked": "forums suivis:",
|
||||
"challenge_counter": "{{index}} sur {{total}}",
|
||||
"embed": "intégrer",
|
||||
"remove": "supprimer",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "vidéo YouTube",
|
||||
"youtube_thumbnail_link_conversion_notice": "Lien YouTube détecté. Collage de l'image miniature dans {{count}}..."
|
||||
"youtube_thumbnail_link_conversion_notice": "Lien YouTube détecté. Collage de l'image miniature dans {{count}}...",
|
||||
"stats_scope_all_listed_boards": "Tous les boards listés (lent)",
|
||||
"stats_scope_directory_boards": "Boards de l'annuaire uniquement (rapide)",
|
||||
"boards_loaded": "boards chargés :"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "גיף",
|
||||
"total_posts": "סה\"כ פוסטים:",
|
||||
"current_users": "משתמשים נוכחיים:",
|
||||
"boards_tracked": "פורומים שנמצאים במעקב:",
|
||||
"challenge_counter": "{{index}} מתוך {{total}}",
|
||||
"embed": "הטמע",
|
||||
"remove": "הסר",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "סרטון YouTube",
|
||||
"youtube_thumbnail_link_conversion_notice": "זוהה קישור YouTube. מדביק תמונת ממוזערת בעוד {{count}}..."
|
||||
"youtube_thumbnail_link_conversion_notice": "זוהה קישור YouTube. מדביק תמונת ממוזערת בעוד {{count}}...",
|
||||
"stats_scope_all_listed_boards": "כל הלוחות הרשומים (איטי)",
|
||||
"stats_scope_directory_boards": "רק לוחות מהספרייה (מהיר)",
|
||||
"boards_loaded": "לוחות שנטענו:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "जीआईएफ",
|
||||
"total_posts": "कुल पोस्ट:",
|
||||
"current_users": "वर्तमान उपयोगकर्ता:",
|
||||
"boards_tracked": "ट्रैक किए गए बोर्ड:",
|
||||
"challenge_counter": "{{index}} में से {{total}}",
|
||||
"embed": "एम्बेड",
|
||||
"remove": "हटाएं",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube वीडियो",
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube लिंक मिला। {{count}} में थंबनेल इमेज पेस्ट की जा रही है..."
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube लिंक मिला। {{count}} में थंबनेल इमेज पेस्ट की जा रही है...",
|
||||
"stats_scope_all_listed_boards": "सभी सूचीबद्ध बोर्ड (धीमा)",
|
||||
"stats_scope_directory_boards": "सिर्फ डायरेक्टरी बोर्ड (तेज़)",
|
||||
"boards_loaded": "लोड किए गए बोर्ड:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "Összes bejegyzés:",
|
||||
"current_users": "jelenlegi felhasználók:",
|
||||
"boards_tracked": "követett fórumok:",
|
||||
"challenge_counter": "{{index}} a(z) {{total}}-ból/ből",
|
||||
"embed": "beágyaz",
|
||||
"remove": "eltávolít",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube-videó",
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube-link észlelve. Bélyegkép beillesztése {{count}} múlva..."
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube-link észlelve. Bélyegkép beillesztése {{count}} múlva...",
|
||||
"stats_scope_all_listed_boards": "Az összes listázott board (lassú)",
|
||||
"stats_scope_directory_boards": "Csak a könyvtár boardjai (gyors)",
|
||||
"boards_loaded": "betöltött boardok:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "Total Posting:",
|
||||
"current_users": "pengguna saat ini:",
|
||||
"boards_tracked": "papan yang dilacak:",
|
||||
"challenge_counter": "{{index}} dari {{total}}",
|
||||
"embed": "sematkan",
|
||||
"remove": "hapus",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "video YouTube",
|
||||
"youtube_thumbnail_link_conversion_notice": "Tautan YouTube terdeteksi. Menempelkan gambar thumbnail dalam {{count}}..."
|
||||
"youtube_thumbnail_link_conversion_notice": "Tautan YouTube terdeteksi. Menempelkan gambar thumbnail dalam {{count}}...",
|
||||
"stats_scope_all_listed_boards": "Semua board yang terdaftar (lambat)",
|
||||
"stats_scope_directory_boards": "Hanya board direktori (cepat)",
|
||||
"boards_loaded": "board dimuat:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "Post totali:",
|
||||
"current_users": "utenti attuali:",
|
||||
"boards_tracked": "board tracciate:",
|
||||
"challenge_counter": "{{index}} di {{total}}",
|
||||
"embed": "incorpora",
|
||||
"remove": "rimuovi",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "video YouTube",
|
||||
"youtube_thumbnail_link_conversion_notice": "Link YouTube rilevato. Incollando l'immagine di anteprima tra {{count}}..."
|
||||
"youtube_thumbnail_link_conversion_notice": "Link YouTube rilevato. Incollando l'immagine di anteprima tra {{count}}...",
|
||||
"stats_scope_all_listed_boards": "Tutte le board elencate (lento)",
|
||||
"stats_scope_directory_boards": "Solo board della directory (veloce)",
|
||||
"boards_loaded": "board caricati:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "総投稿数:",
|
||||
"current_users": "現在のユーザー:",
|
||||
"boards_tracked": "追跡されたボード:",
|
||||
"challenge_counter": "{{index}} / {{total}}",
|
||||
"embed": "埋め込み",
|
||||
"remove": "削除",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube動画",
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTubeリンクを検出しました。{{count}}後にサムネイル画像を貼り付けます..."
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTubeリンクを検出しました。{{count}}後にサムネイル画像を貼り付けます...",
|
||||
"stats_scope_all_listed_boards": "掲載されているすべての掲示板(低速)",
|
||||
"stats_scope_directory_boards": "ディレクトリの掲示板のみ(高速)",
|
||||
"boards_loaded": "読み込まれた板:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "총 게시물:",
|
||||
"current_users": "현재 사용자:",
|
||||
"boards_tracked": "추적된 게시판:",
|
||||
"challenge_counter": "{{index}} 중 {{total}}",
|
||||
"embed": "임베드",
|
||||
"remove": "제거",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube 동영상",
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube 링크가 감지되었습니다. {{count}} 후에 썸네일 이미지를 붙여넣는 중..."
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube 링크가 감지되었습니다. {{count}} 후에 썸네일 이미지를 붙여넣는 중...",
|
||||
"stats_scope_all_listed_boards": "등록된 모든 게시판 (느림)",
|
||||
"stats_scope_directory_boards": "디렉터리 게시판만 (빠름)",
|
||||
"boards_loaded": "불러온 게시판:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "जीआईएफ",
|
||||
"total_posts": "एकूण पोस्ट:",
|
||||
"current_users": "सध्याचे वापरकर्ते:",
|
||||
"boards_tracked": "ट्रॅक केलेले बोर्ड:",
|
||||
"challenge_counter": "{{index}} पैकी {{total}}",
|
||||
"embed": "एम्बेड",
|
||||
"remove": "काढा",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube व्हिडिओ",
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube लिंक आढळली. {{count}} मध्ये थंबनेल प्रतिमा पेस्ट करत आहे..."
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube लिंक आढळली. {{count}} मध्ये थंबनेल प्रतिमा पेस्ट करत आहे...",
|
||||
"stats_scope_all_listed_boards": "सर्व सूचीबद्ध बोर्ड (मंद)",
|
||||
"stats_scope_directory_boards": "फक्त निर्देशिका बोर्ड (जलद)",
|
||||
"boards_loaded": "लोड केलेले बोर्ड:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "Totaal aantal berichten:",
|
||||
"current_users": "huidige gebruikers:",
|
||||
"boards_tracked": "gevolgde borden:",
|
||||
"challenge_counter": "{{index}} van {{total}}",
|
||||
"embed": "insluiten",
|
||||
"remove": "verwijderen",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube-video",
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube-link gedetecteerd. Miniatuurafbeelding wordt over {{count}} geplakt..."
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube-link gedetecteerd. Miniatuurafbeelding wordt over {{count}} geplakt...",
|
||||
"stats_scope_all_listed_boards": "Alle vermelde boards (traag)",
|
||||
"stats_scope_directory_boards": "Alleen directory-boards (snel)",
|
||||
"boards_loaded": "geladen boards:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "Totalt antall innlegg:",
|
||||
"current_users": "nåværende brukere:",
|
||||
"boards_tracked": "sporede fora:",
|
||||
"challenge_counter": "{{index}} av {{total}}",
|
||||
"embed": "bygg inn",
|
||||
"remove": "fjern",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube-video",
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube-lenke oppdaget. Limer inn miniatyrbilde om {{count}}..."
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube-lenke oppdaget. Limer inn miniatyrbilde om {{count}}...",
|
||||
"stats_scope_all_listed_boards": "Alle oppførte boards (tregt)",
|
||||
"stats_scope_directory_boards": "Bare katalog-boards (raskt)",
|
||||
"boards_loaded": "lastede boards:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "Łączna liczba postów:",
|
||||
"current_users": "obecni użytkownicy:",
|
||||
"boards_tracked": "śledzone fora:",
|
||||
"challenge_counter": "{{index}} z {{total}}",
|
||||
"embed": "osadź",
|
||||
"remove": "usuń",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "wideo YouTube",
|
||||
"youtube_thumbnail_link_conversion_notice": "Wykryto link YouTube. Wklejam miniaturę za {{count}}..."
|
||||
"youtube_thumbnail_link_conversion_notice": "Wykryto link YouTube. Wklejam miniaturę za {{count}}...",
|
||||
"stats_scope_all_listed_boards": "Wszystkie wymienione boardy (wolno)",
|
||||
"stats_scope_directory_boards": "Tylko boardy z katalogu (szybko)",
|
||||
"boards_loaded": "załadowane boardy:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "Total de postagens:",
|
||||
"current_users": "usuários atuais:",
|
||||
"boards_tracked": "fóruns rastreados:",
|
||||
"challenge_counter": "{{index}} de {{total}}",
|
||||
"embed": "embutir",
|
||||
"remove": "remover",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "vídeo do YouTube",
|
||||
"youtube_thumbnail_link_conversion_notice": "Link do YouTube detectado. Colando imagem em miniatura em {{count}}..."
|
||||
"youtube_thumbnail_link_conversion_notice": "Link do YouTube detectado. Colando imagem em miniatura em {{count}}...",
|
||||
"stats_scope_all_listed_boards": "Todos os boards listados (lento)",
|
||||
"stats_scope_directory_boards": "Somente boards do diretório (rápido)",
|
||||
"boards_loaded": "boards carregados:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "Total postări:",
|
||||
"current_users": "utilizatori actuali:",
|
||||
"boards_tracked": "forumuri urmărite:",
|
||||
"challenge_counter": "{{index}} din {{total}}",
|
||||
"embed": "încorpora",
|
||||
"remove": "elimina",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "video YouTube",
|
||||
"youtube_thumbnail_link_conversion_notice": "Link YouTube detectat. Lipesc imaginea miniatură în {{count}}..."
|
||||
"youtube_thumbnail_link_conversion_notice": "Link YouTube detectat. Lipesc imaginea miniatură în {{count}}...",
|
||||
"stats_scope_all_listed_boards": "Toate boardurile listate (lent)",
|
||||
"stats_scope_directory_boards": "Doar boardurile din director (rapid)",
|
||||
"boards_loaded": "boarduri încărcate:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "ГИФ",
|
||||
"total_posts": "Всего сообщений:",
|
||||
"current_users": "текущие пользователи:",
|
||||
"boards_tracked": "отслеживаемые форумы:",
|
||||
"challenge_counter": "{{index}} из {{total}}",
|
||||
"embed": "встроить",
|
||||
"remove": "удалить",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "видео YouTube",
|
||||
"youtube_thumbnail_link_conversion_notice": "Обнаружена ссылка YouTube. Вставка миниатюры через {{count}}..."
|
||||
"youtube_thumbnail_link_conversion_notice": "Обнаружена ссылка YouTube. Вставка миниатюры через {{count}}...",
|
||||
"stats_scope_all_listed_boards": "Все перечисленные доски (медленно)",
|
||||
"stats_scope_directory_boards": "Только доски каталога (быстро)",
|
||||
"boards_loaded": "загруженные доски:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "Postime totale:",
|
||||
"current_users": "përdoruesit aktualë:",
|
||||
"boards_tracked": "forume të gjurmueshme:",
|
||||
"challenge_counter": "{{index}} nga {{total}}",
|
||||
"embed": "integro",
|
||||
"remove": "hiq",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "video YouTube",
|
||||
"youtube_thumbnail_link_conversion_notice": "U zbulua lidhje YouTube. Po ngjis imazhin miniaturë pas {{count}}..."
|
||||
"youtube_thumbnail_link_conversion_notice": "U zbulua lidhje YouTube. Po ngjis imazhin miniaturë pas {{count}}...",
|
||||
"stats_scope_all_listed_boards": "Të gjitha bordet e listuara (ngadalë)",
|
||||
"stats_scope_directory_boards": "Vetëm bordet e drejtorisë (shpejt)",
|
||||
"boards_loaded": "bordet e ngarkuara:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "Totalt antal inlägg:",
|
||||
"current_users": "aktuella användare:",
|
||||
"boards_tracked": "spårade forum:",
|
||||
"challenge_counter": "{{index}} av {{total}}",
|
||||
"embed": "bädda in",
|
||||
"remove": "ta bort",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube-video",
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube-länk upptäckt. Klistrar in miniatyrbild om {{count}}..."
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube-länk upptäckt. Klistrar in miniatyrbild om {{count}}...",
|
||||
"stats_scope_all_listed_boards": "Alla listade boards (långsamt)",
|
||||
"stats_scope_directory_boards": "Endast katalogens boards (snabbt)",
|
||||
"boards_loaded": "laddade boards:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "జీఐఎఫ్",
|
||||
"total_posts": "మొత్తం పోస్ట్లు:",
|
||||
"current_users": "ప్రస్తుత వాడుకరులు:",
|
||||
"boards_tracked": "ట్రాక్ చేయబడిన బోర్డులు:",
|
||||
"challenge_counter": "{{index}} లో {{total}}",
|
||||
"embed": "ఎంబెడ్",
|
||||
"remove": "తొలగించు",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube వీడియో",
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube లింక్ గుర్తించబడింది. {{count}}లో థంబ్నెయిల్ చిత్రాన్ని పేస్ట్ చేస్తున్నాము..."
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube లింక్ గుర్తించబడింది. {{count}}లో థంబ్నెయిల్ చిత్రాన్ని పేస్ట్ చేస్తున్నాము...",
|
||||
"stats_scope_all_listed_boards": "జాబితాలో ఉన్న అన్ని బోర్డులు (నెమ్మదిగా)",
|
||||
"stats_scope_directory_boards": "డైరెక్టరీ బోర్డులు మాత్రమే (వేగంగా)",
|
||||
"boards_loaded": "లోడ్ చేసిన బోర్డులు:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "จีไอเอฟ",
|
||||
"total_posts": "โพสต์ทั้งหมด:",
|
||||
"current_users": "ผู้ใช้งานปัจจุบัน:",
|
||||
"boards_tracked": "บอร์ดที่ติดตาม:",
|
||||
"challenge_counter": "{{index}} จาก {{total}}",
|
||||
"embed": "ฝัง",
|
||||
"remove": "ลบ",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "วิดีโอ YouTube",
|
||||
"youtube_thumbnail_link_conversion_notice": "ตรวจพบลิงก์ YouTube กำลังวางรูปภาพขนาดย่อในอีก {{count}}..."
|
||||
"youtube_thumbnail_link_conversion_notice": "ตรวจพบลิงก์ YouTube กำลังวางรูปภาพขนาดย่อในอีก {{count}}...",
|
||||
"stats_scope_all_listed_boards": "บอร์ดที่แสดงทั้งหมด (ช้า)",
|
||||
"stats_scope_directory_boards": "เฉพาะบอร์ดในไดเรกทอรี (เร็ว)",
|
||||
"boards_loaded": "บอร์ดที่โหลดแล้ว:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "Toplam Gönderi:",
|
||||
"current_users": "mevcut kullanıcılar:",
|
||||
"boards_tracked": "takip edilen panolar:",
|
||||
"challenge_counter": "{{index}} / {{total}}",
|
||||
"embed": "göm",
|
||||
"remove": "kaldır",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube videosu",
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube bağlantısı algılandı. Küçük resim {{count}} içinde yapıştırılıyor..."
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube bağlantısı algılandı. Küçük resim {{count}} içinde yapıştırılıyor...",
|
||||
"stats_scope_all_listed_boards": "Listelenen tüm panolar (yavaş)",
|
||||
"stats_scope_directory_boards": "Yalnızca dizin panolari (hızlı)",
|
||||
"boards_loaded": "yüklenen panolar:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "ГІФ",
|
||||
"total_posts": "Всього повідомлень:",
|
||||
"current_users": "поточні користувачі:",
|
||||
"boards_tracked": "відстежувані форуми:",
|
||||
"challenge_counter": "{{index}} з {{total}}",
|
||||
"embed": "вбудувати",
|
||||
"remove": "видалити",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "відео YouTube",
|
||||
"youtube_thumbnail_link_conversion_notice": "Виявлено посилання YouTube. Вставляю мініатюру через {{count}}..."
|
||||
"youtube_thumbnail_link_conversion_notice": "Виявлено посилання YouTube. Вставляю мініатюру через {{count}}...",
|
||||
"stats_scope_all_listed_boards": "Усі перелічені дошки (повільно)",
|
||||
"stats_scope_directory_boards": "Лише дошки каталогу (швидко)",
|
||||
"boards_loaded": "завантажені дошки:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "جی آئی ایف",
|
||||
"total_posts": "کل پوسٹس:",
|
||||
"current_users": "موجودہ صارفین:",
|
||||
"boards_tracked": "ٹریک کیے گئے بورڈز:",
|
||||
"challenge_counter": "{{index}} میں سے {{total}}",
|
||||
"embed": "ایمبیڈ",
|
||||
"remove": "ہٹائیں",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube ویڈیو",
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube لنک مل گیا۔ {{count}} میں تھمب نیل تصویر پیسٹ کی جا رہی ہے..."
|
||||
"youtube_thumbnail_link_conversion_notice": "YouTube لنک مل گیا۔ {{count}} میں تھمب نیل تصویر پیسٹ کی جا رہی ہے...",
|
||||
"stats_scope_all_listed_boards": "تمام فہرست شدہ بورڈز (سست)",
|
||||
"stats_scope_directory_boards": "صرف ڈائریکٹری بورڈز (تیز)",
|
||||
"boards_loaded": "لوڈ شدہ بورڈز:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "Tổng số bài viết:",
|
||||
"current_users": "người dùng hiện tại:",
|
||||
"boards_tracked": "bảng được theo dõi:",
|
||||
"challenge_counter": "{{index}} trong {{total}}",
|
||||
"embed": "nhúng",
|
||||
"remove": "xóa",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "video YouTube",
|
||||
"youtube_thumbnail_link_conversion_notice": "Đã phát hiện liên kết YouTube. Đang dán ảnh thu nhỏ sau {{count}}..."
|
||||
"youtube_thumbnail_link_conversion_notice": "Đã phát hiện liên kết YouTube. Đang dán ảnh thu nhỏ sau {{count}}...",
|
||||
"stats_scope_all_listed_boards": "Tất cả bảng được liệt kê (chậm)",
|
||||
"stats_scope_directory_boards": "Chỉ bảng trong thư mục (nhanh)",
|
||||
"boards_loaded": "bảng đã tải:"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"gif": "GIF",
|
||||
"total_posts": "总帖数:",
|
||||
"current_users": "当前用户:",
|
||||
"boards_tracked": "跟踪的板块:",
|
||||
"challenge_counter": "{{index}} / {{total}}",
|
||||
"embed": "嵌入",
|
||||
"remove": "移除",
|
||||
@@ -401,5 +400,8 @@
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube 视频",
|
||||
"youtube_thumbnail_link_conversion_notice": "检测到 YouTube 链接。将在 {{count}} 后粘贴缩略图..."
|
||||
"youtube_thumbnail_link_conversion_notice": "检测到 YouTube 链接。将在 {{count}} 后粘贴缩略图...",
|
||||
"stats_scope_all_listed_boards": "所有已列出的版块(较慢)",
|
||||
"stats_scope_directory_boards": "仅目录板块(快速)",
|
||||
"boards_loaded": "已加载的版块:"
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ const flushMicrotasks = async () => {
|
||||
};
|
||||
|
||||
const loadBlotterVisibilityStore = async () => (await import('../use-blotter-visibility-store')).default;
|
||||
const loadHomepageStatsOptionsStore = async () => (await import('../use-homepage-stats-options-store')).default;
|
||||
const loadModQueueStore = async () => (await import('../use-mod-queue-store')).default;
|
||||
const loadPopularThreadsOptionsStore = async () => (await import('../use-popular-threads-options-store')).default;
|
||||
|
||||
@@ -56,6 +57,17 @@ describe('persisted extra stores', () => {
|
||||
expect(localStorage.getItem('showNsfwContentOnly')).toBe('true');
|
||||
});
|
||||
|
||||
it('loads homepage stats scope from localStorage defaults and persists changes', async () => {
|
||||
const useHomepageStatsOptionsStore = await loadHomepageStatsOptionsStore();
|
||||
|
||||
expect(useHomepageStatsOptionsStore.getState().statsScope).toBe('directory');
|
||||
|
||||
useHomepageStatsOptionsStore.getState().setStatsScope('all');
|
||||
|
||||
expect(useHomepageStatsOptionsStore.getState().statsScope).toBe('all');
|
||||
expect(localStorage.getItem('5chan-homepage-stats-scope')).toBe('all');
|
||||
});
|
||||
|
||||
it('migrates legacy mod queue storage and computes threshold seconds from the active unit', async () => {
|
||||
localStorage.setItem(
|
||||
'mod-queue-storage',
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
export type HomepageStatsScope = 'directory' | 'all';
|
||||
|
||||
const LOCALSTORAGE_KEY = '5chan-homepage-stats-scope';
|
||||
|
||||
const readInitialStatsScope = (): HomepageStatsScope => (localStorage.getItem(LOCALSTORAGE_KEY) === 'all' ? 'all' : 'directory');
|
||||
|
||||
interface HomepageStatsOptionsStore {
|
||||
statsScope: HomepageStatsScope;
|
||||
setStatsScope: (value: HomepageStatsScope) => void;
|
||||
}
|
||||
|
||||
const useHomepageStatsOptionsStore = create<HomepageStatsOptionsStore>((set) => ({
|
||||
statsScope: readInitialStatsScope(),
|
||||
setStatsScope: (value) => {
|
||||
set({ statsScope: value });
|
||||
localStorage.setItem(LOCALSTORAGE_KEY, value);
|
||||
},
|
||||
}));
|
||||
|
||||
export default useHomepageStatsOptionsStore;
|
||||
@@ -15,9 +15,9 @@ const testState = vi.hoisted(() => ({
|
||||
directoryAddresses: [] as string[],
|
||||
directoryListCodes: [] as string[],
|
||||
directoryListsByCode: {} as Record<string, { boards: Array<{ address: string; score: number; managedByDevs: boolean; addedAt?: number }> }>,
|
||||
loadingStartTimestamps: [] as number[],
|
||||
nowSeconds: 1_704_067_210,
|
||||
navigateMock: vi.fn(),
|
||||
setStatsScopeMock: vi.fn(),
|
||||
statsScope: 'directory' as 'directory' | 'all',
|
||||
communities: {} as Record<string, unknown>,
|
||||
communityStats: {} as Record<string, { allPostCount?: number; weekActiveUserCount?: number; state?: string }>,
|
||||
feedStateString: 'Downloading boards',
|
||||
@@ -70,12 +70,12 @@ vi.mock('../../../hooks/use-communities-stats', () => ({
|
||||
useCommunitiesStatsStore: (selector: (state: { communityStats: typeof testState.communityStats }) => unknown) => selector({ communityStats: testState.communityStats }),
|
||||
}));
|
||||
|
||||
vi.mock('../../../stores/use-communities-loading-start-timestamps-store', () => ({
|
||||
default: () => testState.loadingStartTimestamps,
|
||||
}));
|
||||
|
||||
vi.mock('../../../hooks/use-now-seconds', () => ({
|
||||
useNowSeconds: () => testState.nowSeconds,
|
||||
vi.mock('../../../stores/use-homepage-stats-options-store', () => ({
|
||||
default: (selector: (state: { statsScope: typeof testState.statsScope; setStatsScope: typeof testState.setStatsScopeMock }) => unknown) =>
|
||||
selector({
|
||||
statsScope: testState.statsScope,
|
||||
setStatsScope: testState.setStatsScopeMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('../../../hooks/use-state-string', () => ({
|
||||
@@ -86,6 +86,11 @@ vi.mock('../../../components/loading-ellipsis', () => ({
|
||||
default: ({ string }: { string: string }) => createElement('span', { 'data-testid': 'loading-ellipsis' }, string),
|
||||
}));
|
||||
|
||||
vi.mock('../../../components/tooltip', () => ({
|
||||
default: ({ content, children }: { content: React.ReactNode; children: React.ReactNode }) =>
|
||||
createElement('span', { 'data-testid': 'tooltip', 'data-content': typeof content === 'string' ? content : undefined }, children),
|
||||
}));
|
||||
|
||||
vi.mock('../../../stores/use-directory-modal-store', () => ({
|
||||
default: () => ({
|
||||
closeDirectoryModal: testState.closeDirectoryModalMock,
|
||||
@@ -135,8 +140,6 @@ describe('Home', () => {
|
||||
testState.directoryAddresses = ['music-posting.eth', 'tech-posting.eth'];
|
||||
testState.directoryListCodes = [];
|
||||
testState.directoryListsByCode = {};
|
||||
testState.loadingStartTimestamps = [];
|
||||
testState.nowSeconds = 1_704_067_210;
|
||||
testState.communities = {
|
||||
'music-posting.eth': { address: 'music-posting.eth' },
|
||||
'tech-posting.eth': { address: 'tech-posting.eth' },
|
||||
@@ -146,6 +149,10 @@ describe('Home', () => {
|
||||
'tech-posting.eth': { allPostCount: 7, weekActiveUserCount: 5 },
|
||||
};
|
||||
testState.feedStateString = 'Downloading boards';
|
||||
testState.statsScope = 'directory';
|
||||
testState.setStatsScopeMock.mockImplementation((value: 'directory' | 'all') => {
|
||||
testState.statsScope = value;
|
||||
});
|
||||
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
@@ -160,47 +167,62 @@ describe('Home', () => {
|
||||
it('renders the home view chrome, child sections, collectors, and aggregated stats', () => {
|
||||
renderHome();
|
||||
|
||||
expect(vi.mocked(useFeedStateString)).not.toHaveBeenCalled();
|
||||
expect(vi.mocked(useFeedStateString)).toHaveBeenCalledWith([]);
|
||||
expect(document.title).toBe('5chan');
|
||||
expect(container.querySelector('[data-testid="disclaimer-modal"]')?.textContent).toBe('disclaimer-modal');
|
||||
expect(container.querySelector('[data-testid="directory-modal"]')?.textContent).toBe('directory-modal');
|
||||
expect(container.querySelector('[data-testid="boards-list"]')?.textContent).toBe('boards:2');
|
||||
expect(container.querySelector('[data-testid="popular-threads-box"]')?.textContent).toBe('popular:2:2');
|
||||
expect(container.querySelectorAll('[data-testid="stats-collector"]')).toHaveLength(2);
|
||||
expect(testState.directoryListCodes).toEqual([]);
|
||||
expect(container.textContent).toContain('stats');
|
||||
expect(container.textContent).not.toContain('loaded 2/2 boards p2p');
|
||||
expect(container.querySelector('.yellowOfflineIcon')).toBeNull();
|
||||
expect(container.querySelectorAll('[data-testid="loading-ellipsis"]')).toHaveLength(0);
|
||||
expect(container.textContent).toContain('total_posts 12');
|
||||
expect(container.textContent).toContain('current_users 7');
|
||||
expect(container.textContent).toContain('boards_tracked 2');
|
||||
expect(container.textContent).toContain('boards_loaded 2');
|
||||
expect(container.querySelector('[data-testid="site-legal-meta"]')?.textContent).toBe('site-legal-meta');
|
||||
expect(container.querySelector<HTMLAnchorElement>('a[href="/pass"]')?.textContent).toBe('support_5chan');
|
||||
});
|
||||
|
||||
it('keeps the stats values loading until every directory has loaded stats', () => {
|
||||
it('shows the stats state string while no board stats have loaded yet', () => {
|
||||
testState.communityStats = {};
|
||||
|
||||
renderHome();
|
||||
|
||||
expect(vi.mocked(useFeedStateString)).toHaveBeenCalledWith(['music-posting.eth', 'tech-posting.eth']);
|
||||
expect(container.querySelector('.yellowOfflineIcon')).not.toBeNull();
|
||||
expect(container.querySelector('[data-testid="tooltip"]')?.getAttribute('data-content')).toBe('Downloading boards');
|
||||
expect(container.querySelector('[data-testid="loading-ellipsis"]')?.textContent).toBe('Downloading boards');
|
||||
expect(container.textContent).not.toContain('total_posts');
|
||||
expect(container.textContent).not.toContain('boards_loaded');
|
||||
});
|
||||
|
||||
it('shows partial stats while some boards are still loading', () => {
|
||||
testState.communityStats = {
|
||||
'music-posting.eth': { allPostCount: 5, weekActiveUserCount: 2 },
|
||||
};
|
||||
|
||||
renderHome();
|
||||
|
||||
const loadingValues = Array.from(container.querySelectorAll('[data-testid="loading-ellipsis"]'));
|
||||
expect(loadingValues).toHaveLength(1);
|
||||
expect(loadingValues[0]?.textContent).toBe('Downloading boards');
|
||||
expect(vi.mocked(useFeedStateString)).toHaveBeenCalledWith(['music-posting.eth', 'tech-posting.eth']);
|
||||
expect(container.textContent).not.toContain('total_posts');
|
||||
expect(container.textContent).not.toContain('current_users');
|
||||
expect(container.textContent).not.toContain('boards_tracked');
|
||||
expect(container.textContent).not.toContain('total_posts 5');
|
||||
expect(container.textContent).not.toContain('current_users 2');
|
||||
expect(container.textContent).not.toContain('boards_tracked 1');
|
||||
expect(container.querySelector('.yellowOfflineIcon')).not.toBeNull();
|
||||
expect(container.querySelector('[data-testid="tooltip"]')?.getAttribute('data-content')).toBe('Downloading boards');
|
||||
expect(container.querySelectorAll('[data-testid="loading-ellipsis"]')).toHaveLength(0);
|
||||
expect(container.textContent).not.toContain('loaded 1/2 boards p2p');
|
||||
expect(container.textContent).toContain('total_posts 5');
|
||||
expect(container.textContent).toContain('current_users 2');
|
||||
expect(container.textContent).toContain('boards_loaded 1');
|
||||
});
|
||||
|
||||
it('uses a ranked directory fallback board when the default board stats stay unresolved', () => {
|
||||
it('loads every listed board when the slow stats scope is selected', () => {
|
||||
testState.statsScope = 'all';
|
||||
testState.directories = [
|
||||
{ address: 'business-and-finance.bso', title: '/biz/ - Business & Finance', directoryCode: 'biz' },
|
||||
{ address: 'tech-posting.eth', title: '/g/ - Technology', directoryCode: 'g' },
|
||||
];
|
||||
testState.directoryAddresses = ['business-and-finance.bso', 'tech-posting.eth'];
|
||||
testState.loadingStartTimestamps = [1_704_067_170, 1_704_067_170];
|
||||
testState.directoryListsByCode = {
|
||||
biz: {
|
||||
boards: [
|
||||
@@ -217,12 +239,15 @@ describe('Home', () => {
|
||||
renderHome();
|
||||
|
||||
const collectorAddresses = Array.from(container.querySelectorAll('[data-testid="stats-collector"]')).map((collector) => collector.getAttribute('data-address'));
|
||||
expect(testState.directoryListCodes).toEqual(['biz']);
|
||||
expect(collectorAddresses).toEqual(['business-and-finance.bso', 'tech-posting.eth', 'backup-business.bso']);
|
||||
expect(testState.directoryListCodes).toEqual(['biz', 'g']);
|
||||
expect(collectorAddresses).toEqual(['business-and-finance.bso', 'backup-business.bso', 'tech-posting.eth']);
|
||||
expect(vi.mocked(useFeedStateString)).toHaveBeenCalledWith(['business-and-finance.bso', 'backup-business.bso', 'tech-posting.eth']);
|
||||
expect(container.querySelector('.yellowOfflineIcon')).not.toBeNull();
|
||||
expect(container.querySelectorAll('[data-testid="loading-ellipsis"]')).toHaveLength(0);
|
||||
expect(container.textContent).not.toContain('loaded 2/3 boards p2p');
|
||||
expect(container.textContent).toContain('total_posts 18');
|
||||
expect(container.textContent).toContain('current_users 7');
|
||||
expect(container.textContent).toContain('boards_tracked 2');
|
||||
expect(container.textContent).toContain('boards_loaded 2');
|
||||
});
|
||||
|
||||
it('shows zero totals after every directory has loaded zero-count stats', () => {
|
||||
@@ -234,9 +259,11 @@ describe('Home', () => {
|
||||
renderHome();
|
||||
|
||||
expect(container.querySelectorAll('[data-testid="loading-ellipsis"]')).toHaveLength(0);
|
||||
expect(container.querySelector('.yellowOfflineIcon')).toBeNull();
|
||||
expect(container.textContent).not.toContain('loaded 2/2 boards p2p');
|
||||
expect(container.textContent).toContain('total_posts 0');
|
||||
expect(container.textContent).toContain('current_users 0');
|
||||
expect(container.textContent).toContain('boards_tracked 2');
|
||||
expect(container.textContent).toContain('boards_loaded 2');
|
||||
});
|
||||
|
||||
it('does not keep aggregate stats loading after a directory stats fetch fails', () => {
|
||||
@@ -248,9 +275,31 @@ describe('Home', () => {
|
||||
renderHome();
|
||||
|
||||
expect(container.querySelectorAll('[data-testid="loading-ellipsis"]')).toHaveLength(0);
|
||||
expect(container.querySelector('.yellowOfflineIcon')).toBeNull();
|
||||
expect(container.textContent).not.toContain('loaded 2/2 boards p2p');
|
||||
expect(container.textContent).toContain('total_posts 5');
|
||||
expect(container.textContent).toContain('current_users 2');
|
||||
expect(container.textContent).toContain('boards_tracked 2');
|
||||
expect(container.textContent).toContain('boards_loaded 2');
|
||||
});
|
||||
|
||||
it('switches the stats scope from the options menu', () => {
|
||||
renderHome();
|
||||
|
||||
const optionsButton = Array.from(container.querySelectorAll('button')).find((button) => button.textContent === 'options ▼');
|
||||
expect(optionsButton).toBeTruthy();
|
||||
|
||||
act(() => {
|
||||
optionsButton?.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
|
||||
});
|
||||
|
||||
const option = Array.from(container.querySelectorAll('button')).find((button) => button.textContent === 'stats_scope_all_listed_boards');
|
||||
expect(option).toBeTruthy();
|
||||
|
||||
act(() => {
|
||||
option?.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
|
||||
});
|
||||
|
||||
expect(testState.setStatsScopeMock).toHaveBeenCalledWith('all');
|
||||
});
|
||||
|
||||
it('navigates to the canonical board path when the search form is submitted', async () => {
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.boxBar span, .boxBar > button {
|
||||
.boxBar > span, .boxBar > button {
|
||||
all: unset;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@@ -69,7 +69,7 @@
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.boxBar span:hover, .boxBar > button:hover {
|
||||
.boxBar > span:hover, .boxBar > button:hover {
|
||||
cursor: pointer;
|
||||
color: var(--homepage-box-bar-text-color-hover);
|
||||
}
|
||||
@@ -135,13 +135,14 @@
|
||||
top: 30px;
|
||||
right: 5px;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
text-align: right;
|
||||
background: #fff;
|
||||
border: 1px solid gray;
|
||||
box-shadow: 2px 2px 0 1px rgba(0, 0, 0, .1);
|
||||
padding: 4px 0;
|
||||
line-height: 1.7em;
|
||||
min-width: max-content;
|
||||
white-space: nowrap;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
@@ -155,12 +156,13 @@
|
||||
all: unset;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
padding: 0 10px;
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
line-height: inherit;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.filterModal .option:hover {
|
||||
@@ -259,6 +261,31 @@
|
||||
color: #800;
|
||||
}
|
||||
|
||||
.statsTitle {
|
||||
padding-right: 6em;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.statsLoadingIconWrapper {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-left: 6px;
|
||||
line-height: 1;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.statsLoadingIconWrapper > span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.statsLoadingIcon {
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.stats .stat {
|
||||
display: inline-block;
|
||||
width: 33%;
|
||||
|
||||
+137
-78
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useMemo, useRef, FormEvent } from 'react';
|
||||
import { useEffect, useMemo, useRef, useState, type FormEvent, type KeyboardEvent as ReactKeyboardEvent } from 'react';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import styles from './home.module.css';
|
||||
@@ -9,15 +9,15 @@ import PopularThreadsBox from './popular-threads-box';
|
||||
import BoardsList from './boards-list';
|
||||
import SiteLegalMeta from '../../components/site-legal-meta';
|
||||
import LoadingEllipsis from '../../components/loading-ellipsis';
|
||||
import { useFeedStateString } from '../../hooks/use-state-string';
|
||||
import Tooltip from '../../components/tooltip';
|
||||
import useDirectoryModalStore from '../../stores/use-directory-modal-store';
|
||||
import useHomepageStatsOptionsStore, { type HomepageStatsScope } from '../../stores/use-homepage-stats-options-store';
|
||||
import DisclaimerModal from '../../components/disclaimer-modal';
|
||||
import DirectoryModal from '../../components/directory-modal';
|
||||
import { extractDirectoryFromTitle, getBoardPath } from '../../lib/utils/route-utils';
|
||||
import { isWebRuntime } from '../../lib/media-hosting/show-upload-controls';
|
||||
import { useFeedStateString } from '../../hooks/use-state-string';
|
||||
import lowerCase from 'lodash/lowerCase';
|
||||
import useCommunitiesLoadingStartTimestamps from '../../stores/use-communities-loading-start-timestamps-store';
|
||||
import { useNowSeconds } from '../../hooks/use-now-seconds';
|
||||
|
||||
// https://github.com/bitsocialnet/lists/tree/master/5chan-directories
|
||||
|
||||
@@ -106,103 +106,152 @@ type HomepageStats = {
|
||||
state?: string;
|
||||
};
|
||||
|
||||
const StatsLoading = ({ communityAddresses }: { communityAddresses: string[] }) => {
|
||||
const { t } = useTranslation();
|
||||
const loadingStateString = useFeedStateString(communityAddresses) || t('loading');
|
||||
|
||||
return <LoadingEllipsis string={loadingStateString} />;
|
||||
};
|
||||
|
||||
const STATS_DIRECTORY_FALLBACK_DELAY_SECONDS = 30;
|
||||
|
||||
const getDirectoryCode = (directory: DirectoryCommunity): string | null => directory.directoryCode ?? extractDirectoryFromTitle(directory.title ?? '');
|
||||
|
||||
const getUniqueAddresses = (addresses: string[]): string[] => [...new Set(addresses.filter((address) => address.length > 0))];
|
||||
|
||||
const hasLoadedStats = (stat: HomepageStats | undefined): stat is HomepageStats & { allPostCount: number } => stat?.allPostCount !== undefined;
|
||||
const hasFailedStats = (stat: HomepageStats | undefined): boolean => stat?.state === 'failed';
|
||||
const hasResolvedStats = (stat: HomepageStats | undefined): boolean => hasLoadedStats(stat) || hasFailedStats(stat);
|
||||
|
||||
const STATS_SCOPE_OPTIONS: Array<{ scope: HomepageStatsScope; labelKey: string }> = [
|
||||
{ scope: 'directory', labelKey: 'stats_scope_directory_boards' },
|
||||
{ scope: 'all', labelKey: 'stats_scope_all_listed_boards' },
|
||||
];
|
||||
const EMPTY_STATS_LIST: string[] = [];
|
||||
|
||||
const StatsOptionsModal = () => {
|
||||
const { t } = useTranslation();
|
||||
const [showFilterModal, setShowFilterModal] = useState(false);
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
const statsScope = useHomepageStatsOptionsStore((state) => state.statsScope);
|
||||
const setStatsScope = useHomepageStatsOptionsStore((state) => state.setStatsScope);
|
||||
|
||||
useEffect(() => {
|
||||
if (!showFilterModal) return;
|
||||
|
||||
const handleClickOutside = (event: globalThis.MouseEvent) => {
|
||||
if (modalRef.current && !modalRef.current.contains(event.target as Node) && buttonRef.current && !buttonRef.current.contains(event.target as Node)) {
|
||||
setShowFilterModal(false);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||
}, [showFilterModal]);
|
||||
|
||||
const selectScope = (scope: HomepageStatsScope) => {
|
||||
setStatsScope(scope);
|
||||
setShowFilterModal(false);
|
||||
};
|
||||
|
||||
const handleScopeKey = (event: ReactKeyboardEvent<HTMLButtonElement>, scope: HomepageStatsScope) => {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
event.preventDefault();
|
||||
selectScope(scope);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
type='button'
|
||||
ref={buttonRef}
|
||||
tabIndex={0}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
event.preventDefault();
|
||||
if (!showFilterModal) setShowFilterModal(true);
|
||||
}
|
||||
}}
|
||||
onClick={() => !showFilterModal && setShowFilterModal(true)}
|
||||
>
|
||||
{t('options')} ▼
|
||||
</button>
|
||||
{showFilterModal && (
|
||||
<div ref={modalRef} className={styles.filterModal}>
|
||||
{STATS_SCOPE_OPTIONS.map((option) => (
|
||||
<button
|
||||
key={option.scope}
|
||||
type='button'
|
||||
className={`${styles.option} ${statsScope === option.scope ? styles.selected : ''}`}
|
||||
tabIndex={0}
|
||||
onKeyDown={(event) => handleScopeKey(event, option.scope)}
|
||||
onClick={() => selectScope(option.scope)}
|
||||
>
|
||||
{t(option.labelKey)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Stats = ({ directories }: { directories: DirectoryCommunity[] }) => {
|
||||
const { t } = useTranslation();
|
||||
const statsScope = useHomepageStatsOptionsStore((state) => state.statsScope);
|
||||
const communitiesStats = useCommunitiesStatsStore((state) => state.communityStats);
|
||||
const defaultDirectoryAddresses = useMemo(() => directories.map((directory) => directory.address), [directories]);
|
||||
const loadingStartTimestamps = useCommunitiesLoadingStartTimestamps(defaultDirectoryAddresses);
|
||||
const nowSeconds = useNowSeconds(defaultDirectoryAddresses.length > 0);
|
||||
|
||||
const fallbackDirectoryCodes = useMemo(
|
||||
const allDirectoryCodes = useMemo(
|
||||
() =>
|
||||
directories.flatMap((directory, index) => {
|
||||
const defaultStats = communitiesStats[directory.address];
|
||||
if (hasLoadedStats(defaultStats)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const loadingStartTimestamp = loadingStartTimestamps[index];
|
||||
if (!loadingStartTimestamp || nowSeconds - loadingStartTimestamp < STATS_DIRECTORY_FALLBACK_DELAY_SECONDS) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const directoryCode = getDirectoryCode(directory);
|
||||
return directoryCode ? [directoryCode] : [];
|
||||
}),
|
||||
[communitiesStats, directories, loadingStartTimestamps, nowSeconds],
|
||||
getUniqueAddresses(
|
||||
directories.flatMap((directory) => {
|
||||
const directoryCode = getDirectoryCode(directory);
|
||||
return directoryCode ? [directoryCode] : [];
|
||||
}),
|
||||
),
|
||||
[directories],
|
||||
);
|
||||
|
||||
const { listsByCode } = useDirectoryLists(fallbackDirectoryCodes);
|
||||
const { listsByCode } = useDirectoryLists(statsScope === 'all' ? allDirectoryCodes : EMPTY_STATS_LIST);
|
||||
|
||||
const { collectorAddresses, totalPosts, currentUsers, boardsTracked, allDirectoryStatsLoaded } = useMemo(() => {
|
||||
const collectorAddressSet = new Set(defaultDirectoryAddresses);
|
||||
const collectorAddresses = useMemo(() => {
|
||||
if (statsScope === 'directory') {
|
||||
return defaultDirectoryAddresses;
|
||||
}
|
||||
|
||||
return getUniqueAddresses(
|
||||
directories.flatMap((directory) => {
|
||||
const directoryCode = getDirectoryCode(directory);
|
||||
const directoryList = directoryCode ? listsByCode[directoryCode] : null;
|
||||
const listAddresses = directoryList ? sortDirectoryBoardsByRank(directoryList.boards).map((board) => board.address) : [];
|
||||
return listAddresses.length > 0 ? listAddresses : [directory.address];
|
||||
}),
|
||||
);
|
||||
}, [defaultDirectoryAddresses, directories, listsByCode, statsScope]);
|
||||
|
||||
const { totalPosts, currentUsers, boardsLoaded, boardsWithStats } = useMemo(() => {
|
||||
let totalPosts = 0;
|
||||
let currentUsers = 0;
|
||||
let boardsTracked = 0;
|
||||
let allDirectoryStatsLoaded = directories.length > 0;
|
||||
let boardsLoaded = 0;
|
||||
let boardsWithStats = 0;
|
||||
|
||||
for (const directory of directories) {
|
||||
const directoryCode = getDirectoryCode(directory);
|
||||
const directoryList = directoryCode ? listsByCode[directoryCode] : null;
|
||||
const rankedAddresses = directoryList ? sortDirectoryBoardsByRank(directoryList.boards).map((board) => board.address) : [directory.address];
|
||||
const candidateAddresses = getUniqueAddresses(rankedAddresses.length > 0 ? rankedAddresses : [directory.address]);
|
||||
|
||||
candidateAddresses.forEach((address) => collectorAddressSet.add(address));
|
||||
|
||||
let stat: HomepageStats | undefined;
|
||||
let failedStat: HomepageStats | undefined;
|
||||
let allCandidateStatsResolved = candidateAddresses.length > 0;
|
||||
for (const address of candidateAddresses) {
|
||||
const candidateStats = communitiesStats[address];
|
||||
if (hasLoadedStats(candidateStats)) {
|
||||
stat = candidateStats;
|
||||
break;
|
||||
}
|
||||
if (hasFailedStats(candidateStats)) {
|
||||
failedStat ??= candidateStats;
|
||||
continue;
|
||||
}
|
||||
allCandidateStatsResolved = false;
|
||||
}
|
||||
|
||||
if (!stat && allCandidateStatsResolved) {
|
||||
stat = failedStat;
|
||||
}
|
||||
|
||||
if (!stat || (!hasLoadedStats(stat) && !hasFailedStats(stat))) {
|
||||
allDirectoryStatsLoaded = false;
|
||||
for (const address of collectorAddresses) {
|
||||
const stats = communitiesStats[address];
|
||||
if (!hasResolvedStats(stats)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
totalPosts += stat.allPostCount || 0;
|
||||
currentUsers += stat.weekActiveUserCount || 0;
|
||||
boardsTracked++;
|
||||
boardsLoaded++;
|
||||
if (hasLoadedStats(stats)) {
|
||||
boardsWithStats++;
|
||||
totalPosts += stats.allPostCount || 0;
|
||||
currentUsers += stats.weekActiveUserCount || 0;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
collectorAddresses: [...collectorAddressSet],
|
||||
totalPosts,
|
||||
currentUsers,
|
||||
boardsTracked,
|
||||
allDirectoryStatsLoaded,
|
||||
boardsLoaded,
|
||||
boardsWithStats,
|
||||
};
|
||||
}, [communitiesStats, defaultDirectoryAddresses, directories, listsByCode]);
|
||||
}, [collectorAddresses, communitiesStats]);
|
||||
const hasDisplayableStats = boardsWithStats > 0 || (collectorAddresses.length > 0 && boardsLoaded === collectorAddresses.length);
|
||||
const isStatsLoading = !hasDisplayableStats || boardsLoaded < collectorAddresses.length;
|
||||
const loadingStateString = useFeedStateString(isStatsLoading ? collectorAddresses : EMPTY_STATS_LIST) || t('loading');
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -212,10 +261,20 @@ const Stats = ({ directories }: { directories: DirectoryCommunity[] }) => {
|
||||
))}
|
||||
<div className={styles.box}>
|
||||
<div className={`${styles.boxBar} ${styles.color2ColorBar}`}>
|
||||
<h2 className='capitalize'>{t('stats')}</h2>
|
||||
<h2 className={styles.statsTitle}>
|
||||
{t('stats')}
|
||||
{isStatsLoading && (
|
||||
<span className={styles.statsLoadingIconWrapper}>
|
||||
<Tooltip content={loadingStateString}>
|
||||
<span className={`${styles.statsLoadingIcon} yellowOfflineIcon`} />
|
||||
</Tooltip>
|
||||
</span>
|
||||
)}
|
||||
</h2>
|
||||
<StatsOptionsModal />
|
||||
</div>
|
||||
<div className={`${styles.boxContent} ${styles.stats}`}>
|
||||
{allDirectoryStatsLoaded ? (
|
||||
{hasDisplayableStats ? (
|
||||
<>
|
||||
<div className={styles.stat}>
|
||||
<b>{t('total_posts')}</b> {totalPosts}
|
||||
@@ -224,11 +283,11 @@ const Stats = ({ directories }: { directories: DirectoryCommunity[] }) => {
|
||||
<b>{t('current_users')}</b> {currentUsers}
|
||||
</div>
|
||||
<div className={styles.stat}>
|
||||
<b>{t('boards_tracked')}</b> {boardsTracked}
|
||||
<b>{t('boards_loaded')}</b> {boardsLoaded}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<StatsLoading communityAddresses={collectorAddresses} />
|
||||
<LoadingEllipsis string={loadingStateString} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user