feat(blotter): replace board stats with changelog-backed blotter

Add static blotter snapshot, route /blotter, compact board blotter (Hide/Show All).
Include yarn blotter tooling and AGENTS rules so every changelog version has a
release line. 4chan-style table UI, subplebbit→board and plebchan→5chan in messages.
This commit is contained in:
plebeius
2026-02-22 19:37:49 +08:00
parent a13c72f888
commit 0cb7b1a970
54 changed files with 1180 additions and 430 deletions
+7
View File
@@ -367,6 +367,13 @@ React Doctor is advisory quality tooling for React architecture/perf/correctness
- Prioritize `error` diagnostics first, then `warning`.
- Score is informative only; no merge blocking based on score yet.
## Blotter Maintenance
- Always run `yarn blotter:check` when `CHANGELOG.md` or `package.json` version changes.
- If check fails for missing release line, AI must read that changelog section and add a concise one-liner release entry.
- AI must ignore manual entries for release coverage logic.
- Manual dev messages should be added through `yarn blotter` / `yarn blotter:manual`.
## Agent Workflow
### Verify Before Done
+4
View File
@@ -89,6 +89,10 @@
"live:postimages:auto": "bash scripts/run-postimages-live-emulator-test.sh",
"smoke:upload-selectors": "node scripts/smoke-upload-selectors.js",
"retest:quality": "yarn test && yarn build && yarn lint && yarn type-check && yarn doctor",
"blotter": "node scripts/update-blotter.js",
"blotter:check": "node scripts/update-blotter.js check",
"blotter:release": "node scripts/update-blotter.js release",
"blotter:manual": "node scripts/update-blotter.js manual",
"android:connectedTest": "cd android && ./gradlew :app:connectedDebugAndroidTest -Pandroid.experimental.androidTest.useUnifiedTestPlatform=false"
},
"browserslist": {
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "ورقة بيضاء",
"token": "رمز",
"start_new_thread": "بدء موضوع جديد",
"show_stats": "عرض الإحصائيات",
"hide": "إخفاء",
"board_stats_hour": "في الساعة الماضية، <1>{{userCount}}</1> مستخدم قاموا بإرسال <1>{{postCount}}</1> منشور",
"board_stats_day": "خلال اليوم الماضي، <1>{{userCount}}</1> مستخدم قاموا بإرسال <1>{{postCount}}</1> منشور",
"board_stats_week": "خلال الأسبوع الماضي، <1>{{userCount}}</1> مستخدم قاموا بإرسال <1>{{postCount}}</1> منشور",
"board_stats_month": "خلال الشهر الماضي، <1>{{userCount}}</1> مستخدم قاموا بإرسال <1>{{postCount}}</1> منشور",
"board_stats_all": "منذ ذلك الحين، <1>{{userCount}}</1> مستخدم قاموا بإرسال <1>{{postCount}}</1> منشور",
"board_created": "منتدى تم إنشاؤه",
"settings": "الإعدادات",
"home": "الصفحة الرئيسية",
"subscribe": "اشتراك",
@@ -280,5 +273,11 @@
"prev": "السابق",
"next": "التالي",
"bottom": "أسفل",
"enable_infinite_scroll_tip": "لا ينطبق على اللوحات المتعددة، التي تستخدم التمرير اللانهائي دائماً."
"enable_infinite_scroll_tip": "لا ينطبق على اللوحات المتعددة، التي تستخدم التمرير اللانهائي دائماً.",
"blotter": "لوحة الإعلانات",
"date": "تاريخ",
"hide_blotter": "إخفاء البلوتير",
"message": "رسالة",
"show_all": "عرض الكل",
"show_blotter": "إظهار لوحة الإعلانات"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "হোয়াইট পেপার",
"token": "টোকেন",
"start_new_thread": "নতুন থ্রেড শুরু করুন",
"show_stats": "প্রদর্শন পরিসংখ্যান",
"hide": "লুকান",
"board_stats_hour": "গত এক ঘণ্টায়, <1>{{userCount}}</1> ব্যবহারকারী <1>{{postCount}}</1> পোস্ট করেছেন",
"board_stats_day": "গত দিনে, <1>{{userCount}}</1> ব্যবহারকারী <1>{{postCount}}</1> পোস্ট করেছেন",
"board_stats_week": "গত সপ্তাহে, <1>{{userCount}}</1> ব্যবহারকারী <1>{{postCount}}</1> পোস্ট করেছেন",
"board_stats_month": "গত মাসে, <1>{{userCount}}</1> ব্যবহারকারী <1>{{postCount}}</1> পোস্ট করেছেন",
"board_stats_all": "তারপর থেকে, <1>{{userCount}}</1> ব্যবহারকারী <1>{{postCount}}</1> পোস্ট করেছেন",
"board_created": "বোর্ড তৈরি হয়েছে",
"settings": "সেটিংস",
"home": "হোম",
"subscribe": "সাবস্ক্রাইব",
@@ -280,5 +273,11 @@
"prev": "পূর্ববর্তী",
"next": "পরবর্তী",
"bottom": "নিচ",
"enable_infinite_scroll_tip": "মাল্টিবোর্ডে প্রযোজ্য নয়, যা সবসময় অসীম স্ক্রল ব্যবহার করে।"
"enable_infinite_scroll_tip": "মাল্টিবোর্ডে প্রযোজ্য নয়, যা সবসময় অসীম স্ক্রল ব্যবহার করে।",
"blotter": "ঘোষণা",
"date": "তারিখ",
"hide_blotter": "ব্লটার লুকিয়ে রাখুন",
"message": "বার্তা",
"show_all": "সব দেখুন",
"show_blotter": "ব্লটার দেখান"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "Bílá kniha",
"token": "Token",
"start_new_thread": "Začít nové vlákno",
"show_stats": "Zobrazit statistiky",
"hide": "Skrýt",
"board_stats_hour": "V poslední hodině <1>{{userCount}}</1> uživatelů napsalo <1>{{postCount}}</1> příspěvků",
"board_stats_day": "V posledním dni <1>{{userCount}}</1> uživatelů napsalo <1>{{postCount}}</1> příspěvků",
"board_stats_week": "V posledním týdnu <1>{{userCount}}</1> uživatelů napsalo <1>{{postCount}}</1> příspěvků",
"board_stats_month": "V posledním měsíci <1>{{userCount}}</1> uživatelů napsalo <1>{{postCount}}</1> příspěvků",
"board_stats_all": "Od té doby <1>{{userCount}}</1> uživatelů napsalo <1>{{postCount}}</1> příspěvků",
"board_created": "Fórum vytvořeno",
"settings": "Nastavení",
"home": "Domovská stránka",
"subscribe": "Přihlásit se k odběru",
@@ -280,5 +273,11 @@
"prev": "Předchozí",
"next": "Další",
"bottom": "Dole",
"enable_infinite_scroll_tip": "Netýká se multidesek, které vždy používají nekonečné posouvání."
"enable_infinite_scroll_tip": "Netýká se multidesek, které vždy používají nekonečné posouvání.",
"blotter": "Oznámení",
"date": "Datum",
"hide_blotter": "Skrýt Blotter",
"message": "Zpráva",
"show_all": "Zobrazit vše",
"show_blotter": "Zobrazit Blotter"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "Whitepaper",
"token": "Token",
"start_new_thread": "Start en ny tråd",
"show_stats": "Vis statistikker",
"hide": "Skjul",
"board_stats_hour": "I den sidste time har <1>{{userCount}}</1> brugere sendt <1>{{postCount}}</1> indlæg",
"board_stats_day": "I går har <1>{{userCount}}</1> brugere sendt <1>{{postCount}}</1> indlæg",
"board_stats_week": "I den sidste uge har <1>{{userCount}}</1> brugere sendt <1>{{postCount}}</1> indlæg",
"board_stats_month": "I den sidste måned har <1>{{userCount}}</1> brugere sendt <1>{{postCount}}</1> indlæg",
"board_stats_all": "Siden da har <1>{{userCount}}</1> brugere sendt <1>{{postCount}}</1> indlæg",
"board_created": "Forum oprettet",
"settings": "Indstillinger",
"home": "Hjem",
"subscribe": "Abonner",
@@ -280,5 +273,11 @@
"prev": "Forrige",
"next": "Næste",
"bottom": "Bund",
"enable_infinite_scroll_tip": "Gælder ikke for multiboard, som altid bruger uendelig scroll."
"enable_infinite_scroll_tip": "Gælder ikke for multiboard, som altid bruger uendelig scroll.",
"blotter": "Meddelelser",
"date": "Dato",
"hide_blotter": "Skjul Blotter",
"message": "Besked",
"show_all": "Vis alle",
"show_blotter": "Vis Blotter"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "Whitepaper",
"token": "Token",
"start_new_thread": "Starte einen neuen Thread",
"show_stats": "Statistiken anzeigen",
"hide": "Ausblenden",
"board_stats_hour": "In der letzten Stunde haben <1>{{userCount}}</1> Benutzer <1>{{postCount}}</1> Beiträge veröffentlicht",
"board_stats_day": "Im letzten Tag haben <1>{{userCount}}</1> Benutzer <1>{{postCount}}</1> Beiträge veröffentlicht",
"board_stats_week": "In der letzten Woche haben <1>{{userCount}}</1> Benutzer <1>{{postCount}}</1> Beiträge veröffentlicht",
"board_stats_month": "Im letzten Monat haben <1>{{userCount}}</1> Benutzer <1>{{postCount}}</1> Beiträge veröffentlicht",
"board_stats_all": "Seitdem haben <1>{{userCount}}</1> Benutzer <1>{{postCount}}</1> Beiträge veröffentlicht",
"board_created": "Forum erstellt",
"settings": "Einstellungen",
"home": "Startseite",
"subscribe": "Abonnieren",
@@ -280,5 +273,11 @@
"prev": "Zurück",
"next": "Weiter",
"bottom": "Unten",
"enable_infinite_scroll_tip": "Gilt nicht für Multiboards, die immer Endlos-Scrolling verwenden."
"enable_infinite_scroll_tip": "Gilt nicht für Multiboards, die immer Endlos-Scrolling verwenden.",
"blotter": "Ankündigungen",
"date": "Datum",
"hide_blotter": "Blotter ausblenden",
"message": "Nachricht",
"show_all": "Alle anzeigen",
"show_blotter": "Blotter anzeigen"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "Λευκή Βίβλος",
"token": "Τόκεν",
"start_new_thread": "Ξεκινήστε ένα νέο νήμα",
"show_stats": "Εμφάνιση στατιστικών",
"hide": "Απόκρυψη",
"board_stats_hour": "Την τελευταία ώρα, <1>{{userCount}}</1> χρήστες έχουν στείλει <1>{{postCount}}</1> μηνύματα",
"board_stats_day": "Την προηγούμενη μέρα, <1>{{userCount}}</1> χρήστες έχουν στείλει <1>{{postCount}}</1> μηνύματα",
"board_stats_week": "Την προηγούμενη εβδομάδα, <1>{{userCount}}</1> χρήστες έχουν στείλει <1>{{postCount}}</1> μηνύματα",
"board_stats_month": "Τον προηγούμενο μήνα, <1>{{userCount}}</1> χρήστες έχουν στείλει <1>{{postCount}}</1> μηνύματα",
"board_stats_all": "Από τότε, <1>{{userCount}}</1> χρήστες έχουν στείλει <1>{{postCount}}</1> μηνύματα",
"board_created": "Δημιουργήθηκε φόρουμ",
"settings": "Ρυθμίσεις",
"home": "Αρχική σελίδα",
"subscribe": "Εγγραφή",
@@ -280,5 +273,11 @@
"prev": "Προηγ",
"next": "Επόμενο",
"bottom": "Κάτω",
"enable_infinite_scroll_tip": "Δεν ισχύει για τους πολυπίνακες, που χρησιμοποιούν πάντα την ατελείωτη κύλιση."
"enable_infinite_scroll_tip": "Δεν ισχύει για τους πολυπίνακες, που χρησιμοποιούν πάντα την ατελείωτη κύλιση.",
"blotter": "Ανακοινώσεις",
"date": "Ημερομηνία",
"hide_blotter": "Απόκρυψη Blotter",
"message": "Μήνυμα",
"show_all": "Εμφάνιση όλων",
"show_blotter": "Εμφάνιση Blotter"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "Whitepaper",
"token": "Token",
"start_new_thread": "Start a New Thread",
"show_stats": "Show Stats",
"hide": "hide",
"board_stats_hour": "In the past hour, <1>{{userCount}}</1> users made <1>{{postCount}}</1> posts",
"board_stats_day": "In the past day, <1>{{userCount}}</1> users made <1>{{postCount}}</1> posts",
"board_stats_week": "In the past week, <1>{{userCount}}</1> users made <1>{{postCount}}</1> posts",
"board_stats_month": "In the past month, <1>{{userCount}}</1> users made <1>{{postCount}}</1> posts",
"board_stats_all": "Since then, <1>{{userCount}}</1> users made <1>{{postCount}}</1> posts",
"board_created": "Board created",
"settings": "Settings",
"home": "Home",
"subscribe": "Subscribe",
@@ -280,5 +273,11 @@
"enable_infinite_scroll": "Enable infinite scroll",
"prev": "Prev",
"next": "Next",
"enable_infinite_scroll_tip": "Does not apply to multiboards, which always use infinite scroll."
"enable_infinite_scroll_tip": "Does not apply to multiboards, which always use infinite scroll.",
"blotter": "Blotter",
"date": "Date",
"hide_blotter": "Hide Blotter",
"message": "Message",
"show_all": "Show All",
"show_blotter": "Show Blotter"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "Libro blanco",
"token": "Token",
"start_new_thread": "Iniciar un Nuevo Hilo",
"show_stats": "Mostrar Estadísticas",
"hide": "ocultar",
"board_stats_hour": "En la última hora, <1>{{userCount}}</1> usuarios han enviado <1>{{postCount}}</1> publicaciones",
"board_stats_day": "En el último día, <1>{{userCount}}</1> usuarios han enviado <1>{{postCount}}</1> publicaciones",
"board_stats_week": "En la última semana, <1>{{userCount}}</1> usuarios han enviado <1>{{postCount}}</1> publicaciones",
"board_stats_month": "En el último mes, <1>{{userCount}}</1> usuarios han enviado <1>{{postCount}}</1> publicaciones",
"board_stats_all": "Desde entonces, <1>{{userCount}}</1> usuarios han enviado <1>{{postCount}}</1> publicaciones",
"board_created": "Foro creado",
"settings": "Configuración",
"home": "Inicio",
"subscribe": "Suscribirse",
@@ -280,5 +273,11 @@
"prev": "Anterior",
"next": "Siguiente",
"bottom": "Abajo",
"enable_infinite_scroll_tip": "No aplica a los multiboard, que siempre usan scroll infinito."
"enable_infinite_scroll_tip": "No aplica a los multiboard, que siempre usan scroll infinito.",
"blotter": "Anuncios",
"date": "Fecha",
"hide_blotter": "Ocultar Blotter",
"message": "Mensaje",
"show_all": "Mostrar todo",
"show_blotter": "Mostrar Blotter"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "کاغذ سفید",
"token": "توکن",
"start_new_thread": "شروع موضوع جدید",
"show_stats": "نمایش آمار",
"hide": "پنهان کردن",
"board_stats_hour": "در ساعت گذشته، <1>{{userCount}}</1> کاربر <1>{{postCount}}</1> پست ارسال کردند",
"board_stats_day": "در گذشته، <1>{{userCount}}</1> کاربر <1>{{postCount}}</1> پست ارسال کردند",
"board_stats_week": "در هفته گذشته، <1>{{userCount}}</1> کاربر <1>{{postCount}}</1> پست ارسال کردند",
"board_stats_month": "در ماه گذشته، <1>{{userCount}}</1> کاربر <1>{{postCount}}</1> پست ارسال کردند",
"board_stats_all": "از آن زمان، <1>{{userCount}}</1> کاربر <1>{{postCount}}</1> پست ارسال کرده‌اند",
"board_created": "انجمن ایجاد شده",
"settings": "تنظیمات",
"home": "خانه",
"subscribe": "اشتراک",
@@ -280,5 +273,11 @@
"prev": "قبلی",
"next": "بعدی",
"bottom": "پایین",
"enable_infinite_scroll_tip": "برای تابلوهای چندگانه اعمال نمی‌شود، که همیشه از اسکرول بی‌نهایت استفاده می‌کنند."
"enable_infinite_scroll_tip": "برای تابلوهای چندگانه اعمال نمی‌شود، که همیشه از اسکرول بی‌نهایت استفاده می‌کنند.",
"blotter": "اعلانات",
"date": "تاریخ",
"hide_blotter": "مخفی کردن بلوتر",
"message": "پیام",
"show_all": "نمایش همه",
"show_blotter": "نمایش بلوتر"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "Whitepaper",
"token": "Token",
"start_new_thread": "Aloita uusi lanka",
"show_stats": "Näytä tilastot",
"hide": "Piilota",
"board_stats_hour": "Edellisen tunnin aikana <1>{{userCount}}</1> käyttäjää on lähettänyt <1>{{postCount}}</1> viestiä",
"board_stats_day": "Edellisenä päivänä <1>{{userCount}}</1> käyttäjää on lähettänyt <1>{{postCount}}</1> viestiä",
"board_stats_week": "Edellisellä viikolla <1>{{userCount}}</1> käyttäjää on lähettänyt <1>{{postCount}}</1> viestiä",
"board_stats_month": "Edellisellä kuukaudella <1>{{userCount}}</1> käyttäjää on lähettänyt <1>{{postCount}}</1> viestiä",
"board_stats_all": "Siitä lähtien <1>{{userCount}}</1> käyttäjää on lähettänyt <1>{{postCount}}</1> viestiä",
"board_created": "Luotu foorumi",
"settings": "Asetukset",
"home": "Koti",
"subscribe": "Tilaa",
@@ -280,5 +273,11 @@
"prev": "Edell.",
"next": "Seuraava",
"bottom": "Pohja",
"enable_infinite_scroll_tip": "Ei koske monilautalistauksia, jotka aina käyttävät ääretöntä vieritystä."
"enable_infinite_scroll_tip": "Ei koske monilautalistauksia, jotka aina käyttävät ääretöntä vieritystä.",
"blotter": "Ilmoitukset",
"date": "Päivämäärä",
"hide_blotter": "Piilota Blotter",
"message": "Viesti",
"show_all": "Näytä kaikki",
"show_blotter": "Näytä Blotter"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "Whitepaper",
"token": "Token",
"start_new_thread": "Magsimula ng Bagong Thread",
"show_stats": "Ipakita ang mga Estadistika",
"hide": "Itago",
"board_stats_hour": "Sa nakaraang oras, <1>{{userCount}}</1> mga gumagamit ang gumawa ng <1>{{postCount}}</1> na mga post",
"board_stats_day": "Sa nakaraang araw, <1>{{userCount}}</1> mga gumagamit ang gumawa ng <1>{{postCount}}</1> na mga post",
"board_stats_week": "Sa nakaraang linggo, <1>{{userCount}}</1> mga gumagamit ang gumawa ng <1>{{postCount}}</1> na mga post",
"board_stats_month": "Sa nakaraang buwan, <1>{{userCount}}</1> mga gumagamit ang gumawa ng <1>{{postCount}}</1> na mga post",
"board_stats_all": "Mula noon, <1>{{userCount}}</1> mga gumagamit ang gumawa ng <1>{{postCount}}</1> na mga post",
"board_created": "Board na nilikha",
"settings": "Mga Setting",
"home": "Bahay",
"subscribe": "Mag-subscribe",
@@ -280,5 +273,11 @@
"prev": "Nakaraan",
"next": "Susunod",
"bottom": "Ibaba",
"enable_infinite_scroll_tip": "Hindi naaangkop sa mga multiboard, na palaging gumagamit ng walang hanggang scroll."
"enable_infinite_scroll_tip": "Hindi naaangkop sa mga multiboard, na palaging gumagamit ng walang hanggang scroll.",
"blotter": "Mga anunsyo",
"date": "Petsa",
"hide_blotter": "Itago ang Blotter",
"message": "Mensahe",
"show_all": "Ipakita lahat",
"show_blotter": "Ipakita ang Blotter"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "Livre blanc",
"token": "Jeton",
"start_new_thread": "Démarrer un nouveau fil",
"show_stats": "Afficher les statistiques",
"hide": "masquer",
"board_stats_hour": "Au cours de la dernière heure, <1>{{userCount}}</1> utilisateurs ont publié <1>{{postCount}}</1> messages",
"board_stats_day": "Au cours de la dernière journée, <1>{{userCount}}</1> utilisateurs ont publié <1>{{postCount}}</1> messages",
"board_stats_week": "Au cours de la dernière semaine, <1>{{userCount}}</1> utilisateurs ont publié <1>{{postCount}}</1> messages",
"board_stats_month": "Au cours du dernier mois, <1>{{userCount}}</1> utilisateurs ont publié <1>{{postCount}}</1> messages",
"board_stats_all": "Depuis lors, <1>{{userCount}}</1> utilisateurs ont publié <1>{{postCount}}</1> messages",
"board_created": "Forum créé",
"settings": "Paramètres",
"home": "Accueil",
"subscribe": "S'abonner",
@@ -280,5 +273,11 @@
"prev": "Précédent",
"next": "Suivant",
"bottom": "Bas",
"enable_infinite_scroll_tip": "Ne s'applique pas aux multiboards, qui utilisent toujours le défilement infini."
"enable_infinite_scroll_tip": "Ne s'applique pas aux multiboards, qui utilisent toujours le défilement infini.",
"blotter": "Annonces",
"date": "Date",
"hide_blotter": "Masquer le Blotter",
"message": "Message",
"show_all": "Tout afficher",
"show_blotter": "Afficher le Blotter"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "מסמך לבן",
"token": "אסימון",
"start_new_thread": "התחלה של פוסט חדש",
"show_stats": "הצג סטטיסטיקות",
"hide": "הסתר",
"board_stats_hour": "בשעה האחרונה, <1>{{userCount}}</1> משתמשים פרסמו <1>{{postCount}}</1> הודעות",
"board_stats_day": "ביום האחרון, <1>{{userCount}}</1> משתמשים פרסמו <1>{{postCount}}</1> הודעות",
"board_stats_week": "בשבוע האחרון, <1>{{userCount}}</1> משתמשים פרסמו <1>{{postCount}}</1> הודעות",
"board_stats_month": "בחודש האחרון, <1>{{userCount}}</1> משתמשים פרסמו <1>{{postCount}}</1> הודעות",
"board_stats_all": "מאז, <1>{{userCount}}</1> משתמשים פרסמו <1>{{postCount}}</1> הודעות",
"board_created": "פורום נוצר",
"settings": "הגדרות",
"home": "בית",
"subscribe": "הירשם",
@@ -280,5 +273,11 @@
"prev": "הקודם",
"next": "הבא",
"bottom": "תחתית",
"enable_infinite_scroll_tip": "לא חל על לוחות מרובים, שתמיד משתמשים בגלילה אינסופית."
"enable_infinite_scroll_tip": "לא חל על לוחות מרובים, שתמיד משתמשים בגלילה אינסופית.",
"blotter": "הודעות",
"date": "תאריך",
"hide_blotter": "הסתר Blotter",
"message": "הודעה",
"show_all": "הצג הכל",
"show_blotter": "הצג Blotter"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "व्हाइटपेपर",
"token": "टोकन",
"start_new_thread": "नया थ्रेड शुरू करें",
"show_stats": "आँकड़े दिखाएं",
"hide": "छिपाएं",
"board_stats_hour": "पिछले घंटे में, <1>{{userCount}}</1> उपयोगकर्ता <1>{{postCount}}</1> पोस्ट की",
"board_stats_day": "पिछले दिन, <1>{{userCount}}</1> उपयोगकर्ता <1>{{postCount}}</1> पोस्ट की",
"board_stats_week": "पिछले सप्ताह, <1>{{userCount}}</1> उपयोगकर्ता <1>{{postCount}}</1> पोस्ट की",
"board_stats_month": "पिछले महीने, <1>{{userCount}}</1> उपयोगकर्ता <1>{{postCount}}</1> पोस्ट की",
"board_stats_all": "उस समय से, <1>{{userCount}}</1> उपयोगकर्ता <1>{{postCount}}</1> पोस्ट की",
"board_created": "बोर्ड बनाया गया",
"settings": "सेटिंग्स",
"home": "होम",
"subscribe": "सब्सक्राइब करें",
@@ -280,5 +273,11 @@
"prev": "पिछला",
"next": "अगला",
"bottom": "नीचे",
"enable_infinite_scroll_tip": "मल्टीबोर्ड पर लागू नहीं होता, जो हमेशा अनंत स्क्रॉल का उपयोग करते हैं।"
"enable_infinite_scroll_tip": "मल्टीबोर्ड पर लागू नहीं होता, जो हमेशा अनंत स्क्रॉल का उपयोग करते हैं।",
"blotter": "सूचनाएं",
"date": "तारीख",
"hide_blotter": "ब्लॉटर छिपाएं",
"message": "संदेश",
"show_all": "सभी दिखाएं",
"show_blotter": "ब्लॉटर दिखाएं"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "Whitepaper",
"token": "Token",
"start_new_thread": "Új téma indítása",
"show_stats": "Statisztikák megjelenítése",
"hide": "Elrejtés",
"board_stats_hour": "Az elmúlt órában <1>{{userCount}}</1> felhasználó <1>{{postCount}}</1> hozzászólást küldött",
"board_stats_day": "Az elmúlt napon <1>{{userCount}}</1> felhasználó <1>{{postCount}}</1> hozzászólást küldött",
"board_stats_week": "Az elmúlt héten <1>{{userCount}}</1> felhasználó <1>{{postCount}}</1> hozzászólást küldött",
"board_stats_month": "Az elmúlt hónapban <1>{{userCount}}</1> felhasználó <1>{{postCount}}</1> hozzászólást küldött",
"board_stats_all": "Azóta <1>{{userCount}}</1> felhasználó <1>{{postCount}}</1> hozzászólást küldött",
"board_created": "Fórum létrehozva",
"settings": "Beállítások",
"home": "Kezdőlap",
"subscribe": "Feliratkozás",
@@ -280,5 +273,11 @@
"prev": "Előző",
"next": "Következő",
"bottom": "Alul",
"enable_infinite_scroll_tip": "A többtáblás nézetekre nem vonatkozik, amelyek mindig végtelen görgetést használnak."
"enable_infinite_scroll_tip": "A többtáblás nézetekre nem vonatkozik, amelyek mindig végtelen görgetést használnak.",
"blotter": "Közlemények",
"date": "Dátum",
"hide_blotter": "Blotter elrejtése",
"message": "Üzenet",
"show_all": "Összes megjelenítése",
"show_blotter": "Blotter megjelenítése"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "Whitepaper",
"token": "Token",
"start_new_thread": "Mulai Thread Baru",
"show_stats": "Tampilkan Statistik",
"hide": "Sembunyikan",
"board_stats_hour": "Dalam satu jam terakhir, <1>{{userCount}}</1> pengguna telah membuat <1>{{postCount}}</1> postingan",
"board_stats_day": "Dalam satu hari terakhir, <1>{{userCount}}</1> pengguna telah membuat <1>{{postCount}}</1> postingan",
"board_stats_week": "Dalam seminggu terakhir, <1>{{userCount}}</1> pengguna telah membuat <1>{{postCount}}</1> postingan",
"board_stats_month": "Dalam sebulan terakhir, <1>{{userCount}}</1> pengguna telah membuat <1>{{postCount}}</1> postingan",
"board_stats_all": "Sejak saat itu, <1>{{userCount}}</1> pengguna telah membuat <1>{{postCount}}</1> postingan",
"board_created": "Papan pesan dibuat",
"settings": "Pengaturan",
"home": "Beranda",
"subscribe": "Berlangganan",
@@ -280,5 +273,11 @@
"prev": "Sebelumnya",
"next": "Berikutnya",
"bottom": "Bawah",
"enable_infinite_scroll_tip": "Tidak berlaku untuk papan ganda, yang selalu menggunakan scroll tak terbatas."
"enable_infinite_scroll_tip": "Tidak berlaku untuk papan ganda, yang selalu menggunakan scroll tak terbatas.",
"blotter": "Pengumuman",
"date": "Tanggal",
"hide_blotter": "Sembunyikan Blotter",
"message": "Pesan",
"show_all": "Tampilkan Semua",
"show_blotter": "Tampilkan Blotter"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "Libro bianco",
"token": "Token",
"start_new_thread": "Invia un Nuovo Thread",
"show_stats": "Mostra Statistiche",
"hide": "nascondi",
"board_stats_hour": "Nell'ultima ora, <1>{{userCount}}</1> utenti hanno inviato <1>{{postCount}}</1> post",
"board_stats_day": "Nell'ultimo giorno, <1>{{userCount}}</1> utenti hanno inviato <1>{{postCount}}</1> post",
"board_stats_week": "Nell'ultima settimana, <1>{{userCount}}</1> utenti hanno inviato <1>{{postCount}}</1> post",
"board_stats_month": "Nell'ultimo mese, <1>{{userCount}}</1> utenti hanno inviato <1>{{postCount}}</1> post",
"board_stats_all": "Da allora, <1>{{userCount}}</1> utenti hanno inviato <1>{{postCount}}</1> post",
"board_created": "board creata",
"settings": "Impostazioni",
"home": "Home",
"subscribe": "Iscriviti",
@@ -280,5 +273,11 @@
"prev": "Prec.",
"next": "Avanti",
"bottom": "In basso",
"enable_infinite_scroll_tip": "Non si applica ai multiboard, che usano sempre lo scroll infinito."
"enable_infinite_scroll_tip": "Non si applica ai multiboard, che usano sempre lo scroll infinito.",
"blotter": "Annunci",
"date": "Data",
"hide_blotter": "Nascondi Blotter",
"message": "Messaggio",
"show_all": "Mostra tutto",
"show_blotter": "Mostra Blotter"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "ホワイトペーパー",
"token": "トークン",
"start_new_thread": "新しいスレッドを開始",
"show_stats": "統計を表示",
"hide": "非表示",
"board_stats_hour": "直近1時間で<1>{{userCount}}</1>人のユーザーが<1>{{postCount}}</1>件の投稿をしました",
"board_stats_day": "直近1日間で<1>{{userCount}}</1>人のユーザーが<1>{{postCount}}</1>件の投稿をしました",
"board_stats_week": "直近1週間で<1>{{userCount}}</1>人のユーザーが<1>{{postCount}}</1>件の投稿をしました",
"board_stats_month": "直近1ヶ月で<1>{{userCount}}</1>人のユーザーが<1>{{postCount}}</1>件の投稿をしました",
"board_stats_all": "その後、<1>{{userCount}}</1>人のユーザーが<1>{{postCount}}</1>件の投稿をしました",
"board_created": "掲示板が作成されました",
"settings": "設定",
"home": "ホーム",
"subscribe": "購読",
@@ -280,5 +273,11 @@
"prev": "前へ",
"next": "次へ",
"bottom": "下",
"enable_infinite_scroll_tip": "マルチボードには適用されません。マルチボードは常に無限スクロールを使用します。"
"enable_infinite_scroll_tip": "マルチボードには適用されません。マルチボードは常に無限スクロールを使用します。",
"blotter": "お知らせ",
"date": "日付",
"hide_blotter": "ブロッターを非表示",
"message": "メッセージ",
"show_all": "すべて表示",
"show_blotter": "ブロッターを表示"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "화이트페이퍼",
"token": "토큰",
"start_new_thread": "새로운 쓰레드 시작",
"show_stats": "통계 표시",
"hide": "숨기기",
"board_stats_hour": "지난 1시간 동안 <1>{{userCount}}</1>명의 사용자가 <1>{{postCount}}</1>개의 게시물을 작성했습니다",
"board_stats_day": "지난 1일 동안 <1>{{userCount}}</1>명의 사용자가 <1>{{postCount}}</1>개의 게시물을 작성했습니다",
"board_stats_week": "지난 1주간 <1>{{userCount}}</1>명의 사용자가 <1>{{postCount}}</1>개의 게시물을 작성했습니다",
"board_stats_month": "지난 1개월 동안 <1>{{userCount}}</1>명의 사용자가 <1>{{postCount}}</1>개의 게시물을 작성했습니다",
"board_stats_all": "그 후 <1>{{userCount}}</1>명의 사용자가 <1>{{postCount}}</1>개의 게시물을 작성했습니다",
"board_created": "게시판이 생성되었습니다",
"settings": "설정",
"home": "홈",
"subscribe": "구독",
@@ -280,5 +273,11 @@
"prev": "이전",
"next": "다음",
"bottom": "아래",
"enable_infinite_scroll_tip": "항상 무한 스크롤을 사용하는 멀티보드에는 적용되지 않습니다."
"enable_infinite_scroll_tip": "항상 무한 스크롤을 사용하는 멀티보드에는 적용되지 않습니다.",
"blotter": "공지",
"date": "날짜",
"hide_blotter": "블로터 숨기기",
"message": "메시지",
"show_all": "모두 보기",
"show_blotter": "블로터 표시"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "व्हायटपेपर",
"token": "टोकन",
"start_new_thread": "नवीन थ्रेड सुरू करा",
"show_stats": "आँकडे दर्शवा",
"hide": "लपवा",
"board_stats_hour": "गेल्या एक तासात, <1>{{userCount}}</1> वापरकर्ते <1>{{postCount}}</1> पोस्ट्स केले",
"board_stats_day": "मागील दिवसात, <1>{{userCount}}</1> वापरकर्ते <1>{{postCount}}</1> पोस्ट्स केले",
"board_stats_week": "मागील सप्ताहात, <1>{{userCount}}</1> वापरकर्ते <1>{{postCount}}</1> पोस्ट्स केले",
"board_stats_month": "मागील महिन्यात, <1>{{userCount}}</1> वापरकर्ते <1>{{postCount}}</1> पोस्ट्स केले",
"board_stats_all": "त्यानंतर, <1>{{userCount}}</1> वापरकर्ते <1>{{postCount}}</1> पोस्ट्स केले",
"board_created": "संदेश फलक तयार केला",
"settings": "सेटिंग्ज",
"home": "होम",
"subscribe": "सब्स्क्राईब",
@@ -280,5 +273,11 @@
"prev": "मागील",
"next": "पुढे",
"bottom": "तळ",
"enable_infinite_scroll_tip": "मल्टीबोर्डवर लागू होत नाही, जे नेहमी अंतहीन स्क्रॉल वापरतात."
"enable_infinite_scroll_tip": "मल्टीबोर्डवर लागू होत नाही, जे नेहमी अंतहीन स्क्रॉल वापरतात.",
"blotter": "जाहिराती",
"date": "तारीख",
"hide_blotter": "ब्लॉटर लपवा",
"message": "संदेश",
"show_all": "सर्व दाखवा",
"show_blotter": "ब्लॉटर दाखवा"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "Whitepaper",
"token": "Token",
"start_new_thread": "Start een nieuw onderwerp",
"show_stats": "Toon statistieken",
"hide": "Verbergen",
"board_stats_hour": "In het afgelopen uur hebben <1>{{userCount}}</1> gebruikers <1>{{postCount}}</1> berichten gemaakt",
"board_stats_day": "In de afgelopen dag hebben <1>{{userCount}}</1> gebruikers <1>{{postCount}}</1> berichten gemaakt",
"board_stats_week": "In de afgelopen week hebben <1>{{userCount}}</1> gebruikers <1>{{postCount}}</1> berichten gemaakt",
"board_stats_month": "In de afgelopen maand hebben <1>{{userCount}}</1> gebruikers <1>{{postCount}}</1> berichten gemaakt",
"board_stats_all": "Sindsdien hebben <1>{{userCount}}</1> gebruikers <1>{{postCount}}</1> berichten gemaakt",
"board_created": "Forum aangemaakt",
"settings": "Instellingen",
"home": "Home",
"subscribe": "Abonneren",
@@ -280,5 +273,11 @@
"prev": "Vorige",
"next": "Volgende",
"bottom": "Onder",
"enable_infinite_scroll_tip": "Geldt niet voor multiboards, die altijd oneindig scrollen gebruiken."
"enable_infinite_scroll_tip": "Geldt niet voor multiboards, die altijd oneindig scrollen gebruiken.",
"blotter": "Mededelingen",
"date": "Datum",
"hide_blotter": "Blotter verbergen",
"message": "Bericht",
"show_all": "Alles tonen",
"show_blotter": "Blotter tonen"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "Whitepaper",
"token": "Token",
"start_new_thread": "Start en ny tråd",
"show_stats": "Vis statistikk",
"hide": "Skjul",
"board_stats_hour": "I løpet av den siste timen har <1>{{userCount}}</1> brukere laget <1>{{postCount}}</1> innlegg",
"board_stats_day": "I løpet av den siste dagen har <1>{{userCount}}</1> brukere laget <1>{{postCount}}</1> innlegg",
"board_stats_week": "I løpet av den siste uken har <1>{{userCount}}</1> brukere laget <1>{{postCount}}</1> innlegg",
"board_stats_month": "I løpet av den siste måneden har <1>{{userCount}}</1> brukere laget <1>{{postCount}}</1> innlegg",
"board_stats_all": "Siden den gang har <1>{{userCount}}</1> brukere laget <1>{{postCount}}</1> innlegg",
"board_created": "Forum opprettet",
"settings": "Innstillinger",
"home": "Hjem",
"subscribe": "Abonner",
@@ -280,5 +273,11 @@
"prev": "Forrige",
"next": "Neste",
"bottom": "Bunn",
"enable_infinite_scroll_tip": "Gjelder ikke for multiboard, som alltid bruker uendelig scroll."
"enable_infinite_scroll_tip": "Gjelder ikke for multiboard, som alltid bruker uendelig scroll.",
"blotter": "Kunngjøringer",
"date": "Dato",
"hide_blotter": "Skjul Blotter",
"message": "Melding",
"show_all": "Vis alle",
"show_blotter": "Vis Blotter"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "Whitepaper",
"token": "Token",
"start_new_thread": "Rozpocznij nowy wątek",
"show_stats": "Pokaż statystyki",
"hide": "Ukryj",
"board_stats_hour": "W ciągu ostatniej godziny <1>{{userCount}}</1> użytkowników napisało <1>{{postCount}}</1> postów",
"board_stats_day": "W ciągu ostatniego dnia <1>{{userCount}}</1> użytkowników napisało <1>{{postCount}}</1> postów",
"board_stats_week": "W ciągu ostatniego tygodnia <1>{{userCount}}</1> użytkowników napisało <1>{{postCount}}</1> postów",
"board_stats_month": "W ciągu ostatniego miesiąca <1>{{userCount}}</1> użytkowników napisało <1>{{postCount}}</1> postów",
"board_stats_all": "Od tego czasu <1>{{userCount}}</1> użytkowników napisało <1>{{postCount}}</1> postów",
"board_created": "Forum utworzone",
"settings": "Ustawienia",
"home": "Strona główna",
"subscribe": "Subskrybuj",
@@ -280,5 +273,11 @@
"prev": "Poprz.",
"next": "Dalej",
"bottom": "Dół",
"enable_infinite_scroll_tip": "Nie dotyczy multiboardów, które zawsze używają nieskończonego przewijania."
"enable_infinite_scroll_tip": "Nie dotyczy multiboardów, które zawsze używają nieskończonego przewijania.",
"blotter": "Ogłoszenia",
"date": "Data",
"hide_blotter": "Ukryj Blotter",
"message": "Wiadomość",
"show_all": "Pokaż wszystko",
"show_blotter": "Pokaż Blotter"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "Whitepaper",
"token": "Token",
"start_new_thread": "Iniciar um Novo Tópico",
"show_stats": "Mostrar Estatísticas",
"hide": "ocultar",
"board_stats_hour": "Nas últimas horas, <1>{{userCount}}</1> usuários fizeram <1>{{postCount}}</1> postagens",
"board_stats_day": "Nas últimas horas, <1>{{userCount}}</1> usuários fizeram <1>{{postCount}}</1> postagens",
"board_stats_week": "Na última semana, <1>{{userCount}}</1> usuários fizeram <1>{{postCount}}</1> postagens",
"board_stats_month": "No último mês, <1>{{userCount}}</1> usuários fizeram <1>{{postCount}}</1> postagens",
"board_stats_all": "Desde então, <1>{{userCount}}</1> usuários fizeram <1>{{postCount}}</1> postagens",
"board_created": "Fórum criado",
"settings": "Configurações",
"home": "Início",
"subscribe": "Subscrever",
@@ -280,5 +273,11 @@
"prev": "Anterior",
"next": "Próximo",
"bottom": "Inferior",
"enable_infinite_scroll_tip": "Não se aplica a multiboard, que sempre usam rolagem infinita."
"enable_infinite_scroll_tip": "Não se aplica a multiboard, que sempre usam rolagem infinita.",
"blotter": "Anúncios",
"date": "Data",
"hide_blotter": "Ocultar Blotter",
"message": "Mensagem",
"show_all": "Mostrar tudo",
"show_blotter": "Mostrar Blotter"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "Whitepaper",
"token": "Token",
"start_new_thread": "Începeți un nou fir de discuție",
"show_stats": "Afișează statistici",
"hide": "Ascunde",
"board_stats_hour": "În ultima oră, <1>{{userCount}}</1> utilizatori au făcut <1>{{postCount}}</1> postări",
"board_stats_day": "În ultima zi, <1>{{userCount}}</1> utilizatori au făcut <1>{{postCount}}</1> postări",
"board_stats_week": "În ultima săptămână, <1>{{userCount}}</1> utilizatori au făcut <1>{{postCount}}</1> postări",
"board_stats_month": "În ultima lună, <1>{{userCount}}</1> utilizatori au făcut <1>{{postCount}}</1> postări",
"board_stats_all": "De atunci, <1>{{userCount}}</1> utilizatori au făcut <1>{{postCount}}</1> postări",
"board_created": "Forum creat",
"settings": "Setări",
"home": "Acasă",
"subscribe": "Abonare",
@@ -280,5 +273,11 @@
"prev": "Anterior",
"next": "Următorul",
"bottom": "Jos",
"enable_infinite_scroll_tip": "Nu se aplică la multiboard-urile, care folosesc întotdeauna defilarea infinită."
"enable_infinite_scroll_tip": "Nu se aplică la multiboard-urile, care folosesc întotdeauna defilarea infinită.",
"blotter": "Anunțuri",
"date": "Dată",
"hide_blotter": "Ascunde Blotter",
"message": "Mesaj",
"show_all": "Afișează toate",
"show_blotter": "Afișează Blotter"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "Белая бумага",
"token": "Токен",
"start_new_thread": "Начать новую тему",
"show_stats": "Показать статистику",
"hide": "Скрыть",
"board_stats_hour": "За последний час <1>{{userCount}}</1> пользователей опубликовали <1>{{postCount}}</1> сообщений",
"board_stats_day": "За прошлый день <1>{{userCount}}</1> пользователей опубликовали <1>{{postCount}}</1> сообщений",
"board_stats_week": "За прошлую неделю <1>{{userCount}}</1> пользователей опубликовали <1>{{postCount}}</1> сообщений",
"board_stats_month": "За прошлый месяц <1>{{userCount}}</1> пользователей опубликовали <1>{{postCount}}</1> сообщений",
"board_stats_all": "С тех пор <1>{{userCount}}</1> пользователей опубликовали <1>{{postCount}}</1> сообщений",
"board_created": "Форум создан",
"settings": "Настройки",
"home": "Главная",
"subscribe": "Подписаться",
@@ -280,5 +273,11 @@
"prev": "Пред.",
"next": "Далее",
"bottom": "Вниз",
"enable_infinite_scroll_tip": "Не применяется к мультибордам, которые всегда используют бесконечную прокрутку."
"enable_infinite_scroll_tip": "Не применяется к мультибордам, которые всегда используют бесконечную прокрутку.",
"blotter": "Объявления",
"date": "Дата",
"hide_blotter": "Скрыть Blotter",
"message": "Сообщение",
"show_all": "Показать всё",
"show_blotter": "Показать Blotter"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "Whitepaper",
"token": "Token",
"start_new_thread": "Filloni një temë të re",
"show_stats": "Shfaq Statistikat",
"hide": "Fshih",
"board_stats_hour": "Në orën e fundit, <1>{{userCount}}</1> përdorues ka bërë <1>{{postCount}}</1> postime",
"board_stats_day": "Në ditën e fundit, <1>{{userCount}}</1> përdorues ka bërë <1>{{postCount}}</1> postime",
"board_stats_week": "Në javën e kaluar, <1>{{userCount}}</1> përdorues ka bërë <1>{{postCount}}</1> postime",
"board_stats_month": "Në muajin e kaluar, <1>{{userCount}}</1> përdorues ka bërë <1>{{postCount}}</1> postime",
"board_stats_all": "Që atëherë, <1>{{userCount}}</1> përdorues ka bërë <1>{{postCount}}</1> postime",
"board_created": "Forumi është krijuar",
"settings": "Parametrat",
"home": "Faqja kryesore",
"subscribe": "Pajtohu",
@@ -280,5 +273,11 @@
"prev": "Paraprake",
"next": "Tjetra",
"bottom": "Poshtë",
"enable_infinite_scroll_tip": "Nuk aplikohet për multiboard-et, që gjithmonë përdorin rrëshqitje të pafundme."
"enable_infinite_scroll_tip": "Nuk aplikohet për multiboard-et, që gjithmonë përdorin rrëshqitje të pafundme.",
"blotter": "Njoftimet",
"date": "Data",
"hide_blotter": "Fsheh Blotterin",
"message": "Mesazh",
"show_all": "Shfaq të gjitha",
"show_blotter": "Shfaq Blotterin"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "Whitepaper",
"token": "Token",
"start_new_thread": "Starta en ny tråd",
"show_stats": "Visa statistik",
"hide": "Dölj",
"board_stats_hour": "Under den senaste timmen har <1>{{userCount}}</1> användare gjort <1>{{postCount}}</1> inlägg",
"board_stats_day": "Under den senaste dagen har <1>{{userCount}}</1> användare gjort <1>{{postCount}}</1> inlägg",
"board_stats_week": "Under den senaste veckan har <1>{{userCount}}</1> användare gjort <1>{{postCount}}</1> inlägg",
"board_stats_month": "Under den senaste månaden har <1>{{userCount}}</1> användare gjort <1>{{postCount}}</1> inlägg",
"board_stats_all": "Sedan dess har <1>{{userCount}}</1> användare gjort <1>{{postCount}}</1> inlägg",
"board_created": "Forum skapat",
"settings": "Inställningar",
"home": "Hem",
"subscribe": "Prenumerera",
@@ -280,5 +273,11 @@
"prev": "Föreg.",
"next": "Nästa",
"bottom": "Botten",
"enable_infinite_scroll_tip": "Gäller inte multiboard, som alltid använder oändlig scroll."
"enable_infinite_scroll_tip": "Gäller inte multiboard, som alltid använder oändlig scroll.",
"blotter": "Meddelanden",
"date": "Datum",
"hide_blotter": "Dölj Blotter",
"message": "Meddelande",
"show_all": "Visa alla",
"show_blotter": "Visa Blotter"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "వైట్‌పేపర్",
"token": "టోకెన్",
"start_new_thread": "కొత్త థ్రెడ్‌ను ప్రారంభించండి",
"show_stats": "సాంకేతికాలను చూపించు",
"hide": "దాచు",
"board_stats_hour": "గత గంటలో, <1>{{userCount}}</1> యూజర్‌లు <1>{{postCount}}</1> పోస్టులు చేశారు",
"board_stats_day": "గత రోజు, <1>{{userCount}}</1> యూజర్‌లు <1>{{postCount}}</1> పోస్టులు చేశారు",
"board_stats_week": "గత వారం, <1>{{userCount}}</1> వాడుకరులు <1>{{postCount}}</1> పోస్టులు చేశారు",
"board_stats_month": "గత నెలలో, <1>{{userCount}}</1> వాడుకరులు <1>{{postCount}}</1> పోస్టులు చేశారు",
"board_stats_all": "అప్పుడు నుండి, <1>{{userCount}}</1> వాడుకరులు <1>{{postCount}}</1> పోస్టులు చేశారు",
"board_created": "బోర్డు సృష్టించబడింది",
"settings": "అమరికలు",
"home": "హోమ్",
"subscribe": "చేరండి",
@@ -280,5 +273,11 @@
"prev": "మునుపటి",
"next": "తదుపరి",
"bottom": "క్రింద",
"enable_infinite_scroll_tip": "మల్టీబోర్డ్‌లకు వర్తించదు, అవి ఎల్లప్పుడూ అనంత స్క్రాల్ ఉపయోగిస్తాయి."
"enable_infinite_scroll_tip": "మల్టీబోర్డ్‌లకు వర్తించదు, అవి ఎల్లప్పుడూ అనంత స్క్రాల్ ఉపయోగిస్తాయి.",
"blotter": "ప్రకటనలు",
"date": "తేదీ",
"hide_blotter": "బ్లాటర్ దాచండి",
"message": "సందేశం",
"show_all": "అన్నీ చూపించు",
"show_blotter": "బ్లాటర్ చూపించు"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "เอกสารขาว",
"token": "โทเค็น",
"start_new_thread": "เริ่มต้นสร้างกระทู้ใหม่",
"show_stats": "แสดงสถิติ",
"hide": "ซ่อน",
"board_stats_hour": "ในชั่วโมงที่ผ่านมา <1>{{userCount}}</1> ผู้ใช้งานได้สร้าง <1>{{postCount}}</1> โพสต์",
"board_stats_day": "ในวันที่ผ่านมา <1>{{userCount}}</1> ผู้ใช้งานได้สร้าง <1>{{postCount}}</1> โพสต์",
"board_stats_week": "ในช่วงสัปดาห์ที่ผ่านมา <1>{{userCount}}</1> ผู้ใช้งานได้สร้าง <1>{{postCount}}</1> โพสต์",
"board_stats_month": "ในช่วงเดือนที่ผ่านมา <1>{{userCount}}</1> ผู้ใช้งานได้สร้าง <1>{{postCount}}</1> โพสต์",
"board_stats_all": "ตั้งแต่นั้นมา <1>{{userCount}}</1> ผู้ใช้งานได้สร้าง <1>{{postCount}}</1> โพสต์",
"board_created": "สร้างกระดานข่าวแล้ว",
"settings": "การตั้งค่า",
"home": "หน้าแรก",
"subscribe": "สมัครสมาชิก",
@@ -280,5 +273,11 @@
"prev": "ก่อนหน้า",
"next": "ถัดไป",
"bottom": "ล่าง",
"enable_infinite_scroll_tip": "ไม่ใช้กับมัลติบอร์ด ซึ่งใช้การเลื่อนแบบไม่สิ้นสุดเสมอ"
"enable_infinite_scroll_tip": "ไม่ใช้กับมัลติบอร์ด ซึ่งใช้การเลื่อนแบบไม่สิ้นสุดเสมอ",
"blotter": "ประกาศ",
"date": "วันที่",
"hide_blotter": "ซ่อน Blotter",
"message": "ข้อความ",
"show_all": "แสดงทั้งหมด",
"show_blotter": "แสดง Blotter"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "Beyaz kağıt",
"token": "Jeton",
"start_new_thread": "Yeni bir Konu Başlat",
"show_stats": "İstatistikleri Göster",
"hide": "Gizle",
"board_stats_hour": "Son saatte <1>{{userCount}}</1> kullanıcı <1>{{postCount}}</1> gönderi yaptı",
"board_stats_day": "Son gün içinde <1>{{userCount}}</1> kullanıcı <1>{{postCount}}</1> gönderi yaptı",
"board_stats_week": "Geçen hafta <1>{{userCount}}</1> kullanıcı <1>{{postCount}}</1> gönderi yaptı",
"board_stats_month": "Geçen ay <1>{{userCount}}</1> kullanıcı <1>{{postCount}}</1> gönderi yaptı",
"board_stats_all": "O zamandan beri <1>{{userCount}}</1> kullanıcı <1>{{postCount}}</1> gönderi yaptı",
"board_created": "Forum oluşturuldu",
"settings": "Ayarlar",
"home": "Anasayfa",
"subscribe": "Abone Ol",
@@ -280,5 +273,11 @@
"prev": "Önceki",
"next": "İleri",
"bottom": "Alt",
"enable_infinite_scroll_tip": "Her zaman sonsuz kaydırma kullanan çoklu panolar için geçerli değildir."
"enable_infinite_scroll_tip": "Her zaman sonsuz kaydırma kullanan çoklu panolar için geçerli değildir.",
"blotter": "Duyurular",
"date": "Tarih",
"hide_blotter": "Blotter'ı Gizle",
"message": "Mesaj",
"show_all": "Tümünü göster",
"show_blotter": "Blotter'ı Göster"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "Біла книга",
"token": "Токен",
"start_new_thread": "Почати нову тему",
"show_stats": "Показати статистику",
"hide": "Приховати",
"board_stats_hour": "За останню годину <1>{{userCount}}</1> користувачів зробили <1>{{postCount}}</1> повідомлень",
"board_stats_day": "За останній день <1>{{userCount}}</1> користувачів зробили <1>{{postCount}}</1> повідомлень",
"board_stats_week": "За минулий тиждень <1>{{userCount}}</1> користувачів зробили <1>{{postCount}}</1> повідомлень",
"board_stats_month": "За минулий місяць <1>{{userCount}}</1> користувачів зробили <1>{{postCount}}</1> повідомлень",
"board_stats_all": "З того часу <1>{{userCount}}</1> користувачів зробили <1>{{postCount}}</1> повідомлень",
"board_created": "Форум створено",
"settings": "Налаштування",
"home": "Головна",
"subscribe": "Підписатися",
@@ -280,5 +273,11 @@
"prev": "Попередн.",
"next": "Далі",
"bottom": "Вниз",
"enable_infinite_scroll_tip": "Не застосовується до мультибордів, які завжди використовують нескінченне прокручування."
"enable_infinite_scroll_tip": "Не застосовується до мультибордів, які завжди використовують нескінченне прокручування.",
"blotter": "Оголошення",
"date": "Дата",
"hide_blotter": "Приховати Blotter",
"message": "Повідомлення",
"show_all": "Показати все",
"show_blotter": "Показати Blotter"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "سفید پیپر",
"token": "ٹوکن",
"start_new_thread": "نئی دھاگہ شروع کریں",
"show_stats": "احصائیہ دکھائیں",
"hide": "چھپائیں",
"board_stats_hour": "گذشتہ گھنٹے میں، <1>{{userCount}}</1> صارفین نے <1>{{postCount}}</1> مواد ارسال کیا ہے",
"board_stats_day": "گذشتہ دن، <1>{{userCount}}</1> صارفین نے <1>{{postCount}}</1> مواد ارسال کیا ہے",
"board_stats_week": "گذشتہ ہفتے، <1>{{userCount}}</1> صارفین نے <1>{{postCount}}</1> مواد ارسال کیا ہے",
"board_stats_month": "گذشتہ مہینے، <1>{{userCount}}</1> صارفین نے <1>{{postCount}}</1> مواد ارسال کیا ہے",
"board_stats_all": "اس کے بعد، <1>{{userCount}}</1> صارفین نے <1>{{postCount}}</1> مواد ارسال کیا ہے",
"board_created": "پیغام بورڈز بنایا گیا",
"settings": "ترتیبات",
"home": "ہوم",
"subscribe": "سبسکرائب کریں",
@@ -280,5 +273,11 @@
"prev": "پچھلا",
"next": "اگلا",
"bottom": "نیچے",
"enable_infinite_scroll_tip": "ملٹی بورڈز پر لاگو نہیں ہوتا، جو ہمیشہ لامحدود سکرول استعمال کرتے ہیں۔"
"enable_infinite_scroll_tip": "ملٹی بورڈز پر لاگو نہیں ہوتا، جو ہمیشہ لامحدود سکرول استعمال کرتے ہیں۔",
"blotter": "اعلانات",
"date": "تاریخ",
"hide_blotter": "بلوٹر چھپائیں",
"message": "پیغام",
"show_all": "سب دکھائیں",
"show_blotter": "بلاٹر دکھائیں"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "Bản trắng",
"token": "Token",
"start_new_thread": "Bắt đầu một chủ đề mới",
"show_stats": "Hiển thị Thống kê",
"hide": "Ẩn",
"board_stats_hour": "Trong giờ qua, <1>{{userCount}}</1> người dùng đã đăng <1>{{postCount}}</1> bài",
"board_stats_day": "Trong ngày qua, <1>{{userCount}}</1> người dùng đã đăng <1>{{postCount}}</1> bài",
"board_stats_week": "Trong tuần qua, <1>{{userCount}}</1> người dùng đã đăng <1>{{postCount}}</1> bài",
"board_stats_month": "Trong tháng qua, <1>{{userCount}}</1> người dùng đã đăng <1>{{postCount}}</1> bài",
"board_stats_all": "Từ đó, <1>{{userCount}}</1> người dùng đã đăng <1>{{postCount}}</1> bài",
"board_created": "Diễn đàn được tạo",
"settings": "Cài đặt",
"home": "Trang chủ",
"subscribe": "Đăng ký",
@@ -280,5 +273,11 @@
"prev": "Trước",
"next": "Tiếp theo",
"bottom": "Dưới cùng",
"enable_infinite_scroll_tip": "Không áp dụng cho đa bảng, luôn sử dụng cuộn vô hạn."
"enable_infinite_scroll_tip": "Không áp dụng cho đa bảng, luôn sử dụng cuộn vô hạn.",
"blotter": "Thông báo",
"date": "Ngày",
"hide_blotter": "Ẩn Blotter",
"message": "Tin nhắn",
"show_all": "Hiển thị tất cả",
"show_blotter": "Hiển thị Blotter"
}
+7 -8
View File
@@ -11,14 +11,7 @@
"whitepaper": "白皮书",
"token": "代币",
"start_new_thread": "开始新主题",
"show_stats": "显示统计",
"hide": "隐藏",
"board_stats_hour": "在过去的一小时内,<1>{{userCount}}</1> 用户发布了<1>{{postCount}}</1>个帖子",
"board_stats_day": "在过去的一天内,<1>{{userCount}}</1> 用户发布了<1>{{postCount}}</1>个帖子",
"board_stats_week": "在过去的一周内,<1>{{userCount}}</1> 用户发布了<1>{{postCount}}</1>个帖子",
"board_stats_month": "在过去的一个月内,<1>{{userCount}}</1> 用户发布了<1>{{postCount}}</1>个帖子",
"board_stats_all": "从那时起,<1>{{userCount}}</1> 用户发布了<1>{{postCount}}</1>个帖子",
"board_created": "论坛已创建",
"settings": "设置",
"home": "首页",
"subscribe": "订阅",
@@ -280,5 +273,11 @@
"prev": "上一页",
"next": "下一步",
"bottom": "底部",
"enable_infinite_scroll_tip": "不适用于多板视图,后者始终使用无限滚动。"
"enable_infinite_scroll_tip": "不适用于多板视图,后者始终使用无限滚动。",
"blotter": "公告",
"date": "日期",
"hide_blotter": "隐藏 Blotter",
"message": "消息",
"show_all": "显示全部",
"show_blotter": "显示公告"
}
+308
View File
@@ -0,0 +1,308 @@
#!/usr/bin/env node
/*
Blotter maintenance CLI
Path: src/data/5chan-blotter.json
Shape: { "entries": BlotterEntry[] }
BlotterEntry: id, kind (release|manual), timestamp (unix seconds), message;
version required when kind=release.
Release message format (strict): v{version}: {one-liner}
Modes:
- check Validate blotter: required fields, timestamp-desc order,
changelog coverage, release format.
- release Upsert release entry. Version from package.json (or --version).
Date from CHANGELOG.md heading. One-liner from --message.
- manual Add manual entry. Message required, timestamp defaults to now.
- interactive Interactive prompt for devs (yarn blotter).
Usage:
node scripts/update-blotter.js check
node scripts/update-blotter.js release --message "One-liner description" [--version 0.6.6]
node scripts/update-blotter.js manual --message "Manual dev message" [--timestamp 1739721600]
node scripts/update-blotter.js [interactive]
Release coverage ignores manual entries.
*/
import fs from 'fs/promises';
import { readFileSync } from 'fs';
import path from 'path';
import readline from 'readline';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.join(__dirname, '..');
const BLOTTER_PATH = path.join(ROOT, 'src', 'data', '5chan-blotter.json');
const CHANGELOG_PATH = path.join(ROOT, 'CHANGELOG.md');
const PACKAGE_JSON_PATH = path.join(ROOT, 'package.json');
const RELEASE_PREFIX_RE = /^v[\d.]+: .+$/;
function parseArgs(argv) {
const out = { flags: new Set(), positional: [] };
for (let i = 0; i < argv.length; i++) {
const arg = argv[i];
const next = i + 1 < argv.length ? argv[i + 1] : undefined;
if (arg.startsWith('--')) {
const key = arg;
let value;
if (next && !next.startsWith('--')) {
value = next;
i++;
}
out[key] = value ?? '';
} else {
out.positional.push(arg);
}
}
return out;
}
function usage(exitCode = 1, msg) {
if (msg) console.error(msg);
console.error(`
Blotter maintenance CLI
yarn blotter Interactive mode
yarn blotter:check Validate (non-mutating)
yarn blotter:release --message "One-liner" [--version X.Y.Z]
yarn blotter:manual --message "Message" [--timestamp unix]
Path: src/data/5chan-blotter.json
Release format: v{version}: {one-liner}
`);
process.exit(exitCode);
}
async function loadBlotter() {
try {
const text = await fs.readFile(BLOTTER_PATH, 'utf8');
const data = JSON.parse(text);
let entries;
if (data && typeof data.entries === 'object' && Array.isArray(data.entries)) {
entries = data.entries;
} else if (Array.isArray(data)) {
entries = data;
} else {
throw new Error('Blotter must be { entries: BlotterEntry[] }');
}
return entries;
} catch (err) {
if (err.code === 'ENOENT') return [];
throw err;
}
}
async function writeBlotter(entries) {
const sorted = [...entries].sort((a, b) => b.timestamp - a.timestamp);
const payload = { entries: sorted };
const json = JSON.stringify(payload, null, 2) + '\n';
await fs.mkdir(path.dirname(BLOTTER_PATH), { recursive: true });
await fs.writeFile(BLOTTER_PATH, json, 'utf8');
}
function getPackageVersion() {
const pkg = JSON.parse(readFileSync(PACKAGE_JSON_PATH, 'utf8'));
return pkg.version;
}
async function getChangelogVersions() {
const text = await fs.readFile(CHANGELOG_PATH, 'utf8');
const re = /#+ \[([^\]]+)\].*?\((\d{4}-\d{2}-\d{2})\)/g;
const versions = [];
let m;
while ((m = re.exec(text))) {
versions.push({ version: m[1], date: m[2] });
}
return versions;
}
function getReleaseDateForVersion(versions, version) {
const found = versions.find((v) => v.version === version);
return found ? found.date : null;
}
function dateToUnix(dateStr) {
const d = new Date(dateStr + 'T12:00:00Z');
return Math.floor(d.getTime() / 1000);
}
function runCheck(entries) {
const errors = [];
for (let i = 0; i < entries.length; i++) {
const e = entries[i];
if (typeof e.id !== 'string' || !e.id.trim()) {
errors.push(`Entry ${i}: missing or invalid id`);
}
if (e.kind !== 'release' && e.kind !== 'manual') {
errors.push(`Entry ${i}: kind must be "release" or "manual"`);
}
if (typeof e.timestamp !== 'number' || typeof e.message !== 'string') {
errors.push(`Entry ${i}: missing timestamp or message`);
}
if (e.kind === 'release') {
if (typeof e.version !== 'string' || !e.version.trim()) {
errors.push(`Entry ${i}: release requires version field`);
} else if (!RELEASE_PREFIX_RE.test(e.message)) {
errors.push(`Entry ${i}: release message must match "vX.Y.Z: one-liner"`);
}
}
if (i > 0 && entries[i - 1].timestamp < e.timestamp) {
errors.push(`Entry ${i}: must be ordered timestamp-desc`);
}
}
return errors;
}
async function runChangelogCoverageCheck(entries) {
const releaseEntries = entries.filter((e) => e.kind === 'release');
const releaseVersions = new Set();
for (const e of releaseEntries) {
const v = e.version || (e.message && e.message.match(/^v([\d.]+):/)?.[1]);
if (v) releaseVersions.add(v);
}
const changelogVersions = await getChangelogVersions();
const missing = changelogVersions.filter((v) => !releaseVersions.has(v.version)).map((v) => v.version);
return missing;
}
async function modeCheck() {
const entries = await loadBlotter();
const errors = runCheck(entries);
if (errors.length) {
errors.forEach((e) => console.error(`Error: ${e}`));
process.exit(1);
}
const missingVersions = await runChangelogCoverageCheck(entries);
if (missingVersions.length) {
console.error(`Changelog versions missing release entries: ${missingVersions.join(', ')}`);
process.exit(1);
}
console.log('Blotter check passed.');
}
function formatReleaseMessage(version, oneLiner) {
const trimmed = (oneLiner || '').trim();
if (!trimmed) throw new Error('Release message (one-liner) is required');
return `v${version}: ${trimmed}`;
}
async function modeRelease(args) {
const message = args['--message'];
const versionArg = args['--version'];
const version = versionArg || getPackageVersion();
const changelogVersions = await getChangelogVersions();
const releaseDate = getReleaseDateForVersion(changelogVersions, version);
if (!releaseDate) {
console.error(`Version ${version} not found in CHANGELOG.md headings.`);
process.exit(1);
}
const timestamp = dateToUnix(releaseDate);
const fullMessage = formatReleaseMessage(version, message);
if (!message) {
console.error('--message is required for release mode');
process.exit(1);
}
const entries = await loadBlotter();
const id = `release-${version}`;
const existingIdx = entries.findIndex((e) => e.kind === 'release' && (e.version === version || e.id === id));
const newEntry = { id, kind: 'release', timestamp, message: fullMessage, version };
let updated;
if (existingIdx >= 0) {
updated = [...entries];
updated[existingIdx] = newEntry;
} else {
updated = [newEntry, ...entries];
}
await writeBlotter(updated);
console.log(existingIdx >= 0 ? `Updated release entry for v${version}` : `Added release entry for v${version}`);
}
async function modeManual(args) {
const message = args['--message'];
const tsArg = args['--timestamp'];
if (!message || !message.trim()) {
console.error('--message is required for manual mode');
process.exit(1);
}
const timestamp = tsArg ? parseInt(tsArg, 10) : Math.floor(Date.now() / 1000);
if (Number.isNaN(timestamp)) {
console.error('--timestamp must be a valid Unix timestamp');
process.exit(1);
}
const entries = await loadBlotter();
const id = `manual-${timestamp}`;
const newEntry = { id, timestamp, message: message.trim(), kind: 'manual' };
const updated = [newEntry, ...entries];
await writeBlotter(updated);
console.log('Added manual entry.');
}
async function prompt(question) {
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
return new Promise((resolve) => {
rl.question(question, (answer) => {
rl.close();
resolve(answer?.trim() ?? '');
});
});
}
async function modeInteractive() {
console.log('Blotter interactive mode');
console.log(' 1) Add manual entry');
console.log(' 2) Add/update release entry');
console.log(' 3) Run check only');
const choice = await prompt('Choice [1-3]: ');
if (choice === '1') {
const message = await prompt('Message: ');
if (!message) {
console.error('Message is required');
process.exit(1);
}
await modeManual({ '--message': message });
} else if (choice === '2') {
const version = getPackageVersion();
const oneLiner = await prompt(`One-liner for v${version} [auto-from package.json]: `);
if (!oneLiner) {
console.error('One-liner is required');
process.exit(1);
}
await modeRelease({ '--message': oneLiner, '--version': version });
} else if (choice === '3') {
await modeCheck();
} else {
console.error('Invalid choice');
process.exit(1);
}
}
async function main() {
const args = parseArgs(process.argv.slice(2));
const mode = args.positional[0] || 'interactive';
if (mode === 'check') {
await modeCheck();
} else if (mode === 'release') {
await modeRelease(args);
} else if (mode === 'manual') {
await modeManual(args);
} else if (mode === 'interactive' || !mode) {
await modeInteractive();
} else {
usage(1, `Unknown mode: ${mode}`);
}
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
+4 -2
View File
@@ -16,6 +16,7 @@ import { useDirectories } from './hooks/use-directories';
import { useResolvedSubplebbitAddress } from './hooks/use-resolved-subplebbit-address';
import { getSubplebbitAddress, isPostRoute, isPendingPostRoute, isModQueueRoute } from './lib/utils/route-utils';
import styles from './app.module.css';
import Blotter from './views/blotter';
import FAQ from './views/faq';
import Home from './views/home';
import Rules from './views/rules';
@@ -33,7 +34,7 @@ import CreateBoardModal from './components/create-board-modal';
import FeedCacheContainer from './components/feed-cache-container';
import ReplyModal from './components/reply-modal';
import PostForm from './components/post-form';
import SubplebbitStats from './components/subplebbit-stats';
import BoardBlotter from './components/board-blotter';
import TopBar from './components/topbar';
import TopbarEditModal from './components/topbar-edit-modal';
import DirectoryModal from './components/directory-modal';
@@ -111,7 +112,7 @@ const BoardLayout = () => {
: (subplebbitAddress || isInAllView || isInModView || isInSubscriptionsView || pendingPost?.subplebbitAddress || isOnModQueueRoute) && (
<>
<PostForm key={key} />
{!(isInAllView || isInSubscriptionsView || isInModView) && !isOnModQueueRoute && <SubplebbitStats />}
{!(isInAllView || isInSubscriptionsView || isInModView) && !isOnModQueueRoute && <BoardBlotter />}
<DesktopBoardButtons />
</>
)}
@@ -242,6 +243,7 @@ const App = () => {
<Route path='/' element={<Home />} />
<Route path='/faq' element={<FAQ />} />
<Route path='/rules/:boardIdentifier?' element={<Rules />} />
<Route path='/blotter' element={<Blotter />} />
<Route element={<BoardLayout />}>
{/* Canonical multiboard routes (no time filter) */}
<Route path='/all' element={boardFeedElement} />
@@ -0,0 +1,9 @@
.versionLink {
color: var(--button-desktop-text-color);
text-decoration: var(--button-text-decoration);
text-transform: none;
}
.versionLink:hover {
color: var(--button-desktop-text-color-hover);
}
@@ -0,0 +1,26 @@
import type { BlotterEntry } from '../../lib/utils/blotter-utils';
import styles from './blotter-message.module.css';
const RELEASES_BASE = 'https://github.com/bitsocialhq/5chan/releases/tag/v';
function normalizeMessage(text: string): string {
return text.replace(/subplebbit/gi, 'board').replace(/plebchan/gi, '5chan');
}
const BlotterMessage = ({ entry }: { entry: BlotterEntry }) => {
if (entry.kind === 'release' && entry.version) {
const idx = entry.message.indexOf(': ');
const oneLiner = idx >= 0 ? entry.message.slice(idx + 2) : entry.message;
return (
<>
<a href={`${RELEASES_BASE}${entry.version}`} className={styles.versionLink} target='_blank' rel='noopener noreferrer'>
v{entry.version}
</a>
: {normalizeMessage(oneLiner)}
</>
);
}
return <>{normalizeMessage(entry.message)}</>;
};
export default BlotterMessage;
+1
View File
@@ -0,0 +1 @@
export { default } from './blotter-message';
@@ -27,28 +27,21 @@
text-align: right;
}
.hideButton {
.hideButton,
.actionLink {
color: var(--button-desktop-text-color);
text-decoration: var(--button-text-decoration);
text-transform: capitalize;
}
.hideButton:hover {
.hideButton:hover,
.actionLink:hover {
cursor: pointer;
color: var(--button-desktop-text-color-hover);
}
.statValue {
color: var(--button-desktop-text-color);
}
.lowercase {
text-transform: lowercase;
}
@media (max-width: 640px) {
.content {
display: none;
}
}
}
@@ -0,0 +1,65 @@
import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import blotterData from '../../data/5chan-blotter.json';
import BlotterMessage from '../blotter-message';
import { formatBlotterDate, getBlotterPreview, isBlotterEntry, sortBlotterEntries } from '../../lib/utils/blotter-utils';
import useBlotterVisibilityStore from '../../stores/use-blotter-visibility-store';
import styles from './board-blotter.module.css';
const BoardBlotter = () => {
const { t } = useTranslation();
const { isHidden, toggleVisibility } = useBlotterVisibilityStore();
const entries = (blotterData as { entries?: unknown[] }).entries ?? [];
const validEntries = entries.filter(isBlotterEntry);
const sorted = sortBlotterEntries(validEntries);
const preview = getBlotterPreview(sorted);
return (
<div className={`${styles.content} ${styles.show}`}>
<table className={styles.blotter}>
<thead>
<tr>
<td>
<hr />
</td>
</tr>
</thead>
{!isHidden && (
<tbody>
{preview.map((entry) => (
<tr key={entry.id}>
<td>
{formatBlotterDate(entry.timestamp)} <BlotterMessage entry={entry} />
</td>
</tr>
))}
</tbody>
)}
<tfoot>
<tr>
<td colSpan={2}>
[
<span className={styles.hideButton} onClick={() => toggleVisibility()}>
{isHidden ? t('show_blotter') : t('hide')}
</span>
]
{!isHidden && (
<>
{' '}
[
<Link to='/blotter' className={styles.actionLink}>
{t('show_all')}
</Link>
]
</>
)}
</td>
</tr>
</tfoot>
</table>
</div>
);
};
export default BoardBlotter;
+1
View File
@@ -0,0 +1 @@
export { default } from './board-blotter';
-1
View File
@@ -1 +0,0 @@
export { default } from './subplebbit-stats';
@@ -1,108 +0,0 @@
import { useParams } from 'react-router-dom';
import { Trans, useTranslation } from 'react-i18next';
import { useAccountComment, useSubplebbit, useSubplebbitStats } from '@plebbit/plebbit-react-hooks';
import useSubplebbitsPagesStore from '@plebbit/plebbit-react-hooks/dist/stores/subplebbits-pages';
import useSubplebbitStatsVisibilityStore from '../../stores/use-subplebbit-stats-visibility-store';
import { useResolvedSubplebbitAddress } from '../../hooks/use-resolved-subplebbit-address';
import styles from './subplebbit-stats.module.css';
const SubplebbitStats = () => {
const { t } = useTranslation();
const params = useParams();
const accountComment = useAccountComment({ commentIndex: params?.accountCommentIndex as any });
const resolvedAddress = useResolvedSubplebbitAddress();
const subplebbitAddress = resolvedAddress || accountComment?.subplebbitAddress;
const subplebbit = useSubplebbit({ subplebbitAddress });
const { address, createdAt } = subplebbit || {};
const stats = useSubplebbitStats({ subplebbitAddress: address });
const { hiddenStats, toggleVisibility } = useSubplebbitStatsVisibilityStore();
const isHidden = hiddenStats[address];
const comment = useSubplebbitsPagesStore((state) => state.comments[params?.commentCid as string]);
const { deleted, locked, removed } = comment || {};
const hideStats = deleted || locked || removed;
const unixToMMDDYYYY = (timestamp: number) => {
const date = new Date(timestamp * 1000);
const month = ('0' + (date.getMonth() + 1)).slice(-2);
const day = ('0' + date.getDate()).slice(-2);
const year = date.getFullYear().toString().slice(-2);
return month + '/' + day + '/' + year;
};
return (
<div className={`${styles.content} ${hideStats ? styles.hide : styles.show}`}>
<table className={styles.blotter}>
<thead>
<tr>
<td>
<hr />
</td>
</tr>
</thead>
{!isHidden && (
<tbody>
<tr>
<td>
<Trans
i18nKey='board_stats_hour'
values={{ userCount: stats.hourActiveUserCount ?? '?', postCount: stats.hourPostCount ?? '?' }}
components={{ 1: <span key='hour-stat-value' className={styles.statValue} /> }}
/>
{' / '}
<Trans
i18nKey='board_stats_day'
values={{ userCount: stats.dayActiveUserCount ?? '?', postCount: stats.dayPostCount ?? '?' }}
components={{ 1: <span key='day-stat-value' className={styles.statValue} /> }}
/>
</td>
</tr>
<tr>
<td>
<Trans
i18nKey='board_stats_week'
values={{ userCount: stats.weekActiveUserCount ?? '?', postCount: stats.weekPostCount ?? '?' }}
components={{ 1: <span key='week-stat-value' className={styles.statValue} /> }}
/>
{' / '}
<Trans
i18nKey='board_stats_month'
values={{ userCount: stats.monthActiveUserCount ?? '?', postCount: stats.monthPostCount ?? '?' }}
components={{ 1: <span key='month-stat-value' className={styles.statValue} /> }}
/>
</td>
</tr>
<tr>
<td className={styles.lowercase}>
{createdAt && unixToMMDDYYYY(createdAt)} {t('board_created')}
{' / '}
<Trans
i18nKey='board_stats_all'
values={{ userCount: stats.allActiveUserCount ?? '?', postCount: stats.allPostCount ?? '?' }}
components={{ 1: <span key='all-stat-value' className={styles.statValue} /> }}
/>
</td>
</tr>
</tbody>
)}
<tfoot>
<tr>
<td colSpan={2}>
[
<span className={styles.hideButton} onClick={() => toggleVisibility(address)}>
{isHidden ? t('show_stats') : t('hide')}
</span>
]
</td>
</tr>
</tfoot>
</table>
</div>
);
};
export default SubplebbitStats;
+298
View File
@@ -0,0 +1,298 @@
{
"entries": [
{
"id": "release-0.6.6",
"kind": "release",
"timestamp": 1771243200,
"message": "v0.6.6: Syncs board dirs from GitHub, macOS icon, reply perf",
"version": "0.6.6"
},
{
"id": "release-0.6.5",
"kind": "release",
"timestamp": 1770811200,
"message": "v0.6.5: quotedCids in replies, OP backlinks, dependabot fix",
"version": "0.6.5"
},
{
"id": "release-0.6.4",
"kind": "release",
"timestamp": 1770724800,
"message": "v0.6.4: Quote links, backlinks, pseudonymityMode, release artifacts",
"version": "0.6.4"
},
{
"id": "release-0.6.3",
"kind": "release",
"timestamp": 1769774400,
"message": "v0.6.3: Electron icon config, production build fixes",
"version": "0.6.3"
},
{
"id": "release-0.6.2",
"kind": "release",
"timestamp": 1769428800,
"message": "v0.6.2: Mod-queue compact view, pending approval features",
"version": "0.6.2"
},
{
"id": "release-0.6.1",
"kind": "release",
"timestamp": 1769342400,
"message": "v0.6.1: Major CI, Android, mod-queue, and routing fixes",
"version": "0.6.1"
},
{
"id": "release-0.5.3",
"kind": "release",
"timestamp": 1753790400,
"message": "v0.5.3: Pending post redirect, challenge verification, libp2p deps",
"version": "0.5.3"
},
{
"id": "release-0.5.2",
"kind": "release",
"timestamp": 1749038400,
"message": "v0.5.2: Board search, board address length, markdown 5chan links",
"version": "0.5.2"
},
{
"id": "release-0.5.1",
"kind": "release",
"timestamp": 1748952000,
"message": "v0.5.1: Android fullscreen fix, electron clipboard, reply filtering",
"version": "0.5.1"
},
{
"id": "release-0.5.0",
"kind": "release",
"timestamp": 1747915200,
"message": "v0.5.0: Electron IPFS retry, markdown spoiler, pending post redirect",
"version": "0.5.0"
},
{
"id": "release-0.4.0",
"kind": "release",
"timestamp": 1741348800,
"message": "v0.4.0: Catalog filters, p/mod feed, catalog search, schema adapters",
"version": "0.4.0"
},
{
"id": "release-0.3.6",
"kind": "release",
"timestamp": 1740312000,
"message": "v0.3.6: Catalog instant posts, feed no-posts fix, spoiler visibility",
"version": "0.3.6"
},
{
"id": "release-0.3.5",
"kind": "release",
"timestamp": 1740052800,
"message": "v0.3.5: Auto-subscribe new accounts, markdown list fix, topbar links",
"version": "0.3.5"
},
{
"id": "release-0.3.4",
"kind": "release",
"timestamp": 1738756800,
"message": "v0.3.4: Boards list vichan-style, spoiler text, challenge modal, settings hash routing",
"version": "0.3.4"
},
{
"id": "release-0.3.3",
"kind": "release",
"timestamp": 1735128000,
"message": "v0.3.3: Snow effect mobile fix",
"version": "0.3.3"
},
{
"id": "release-0.3.2",
"kind": "release",
"timestamp": 1735041600,
"message": "v0.3.2: Christmas theme, avatar timestamp, electron routers",
"version": "0.3.2"
},
{
"id": "release-0.3.1",
"kind": "release",
"timestamp": 1731240000,
"message": "v0.3.1: Moderation API schema, post quote hover fix",
"version": "0.3.1"
},
{
"id": "release-0.3.0",
"kind": "release",
"timestamp": 1731067200,
"message": "v0.3.0: Android choose file, interface settings, reply modal errors",
"version": "0.3.0"
},
{
"id": "release-0.2.9",
"kind": "release",
"timestamp": 1730203200,
"message": "v0.2.9: Catalog bump order, Reddit embed, p/all time filter, Java APK fix",
"version": "0.2.9"
},
{
"id": "release-0.2.8",
"kind": "release",
"timestamp": 1729339200,
"message": "v0.2.8: Account storage alert, catalog thumbnails, embed support",
"version": "0.2.8"
},
{
"id": "release-0.2.7",
"kind": "release",
"timestamp": 1726920000,
"message": "v0.2.7: Deleted posts collapse styling, edit reason display",
"version": "0.2.7"
},
{
"id": "release-0.2.6",
"kind": "release",
"timestamp": 1726833600,
"message": "v0.2.6: Anon mode refresh fix, catalog filters save, feed instant posts",
"version": "0.2.6"
},
{
"id": "release-0.2.5",
"kind": "release",
"timestamp": 1725624000,
"message": "v0.2.5: Board buttons, catalog, edit menu, markdown fixes",
"version": "0.2.5"
},
{
"id": "release-0.2.4",
"kind": "release",
"timestamp": 1721736000,
"message": "v0.2.4: Avatars, board header offline icon, post loading states",
"version": "0.2.4"
},
{
"id": "release-0.2.3",
"kind": "release",
"timestamp": 1720008000,
"message": "v0.2.3: Feed description, mod menu fixes, catalog filters",
"version": "0.2.3"
},
{
"id": "release-0.2.2",
"kind": "release",
"timestamp": 1719057600,
"message": "v0.2.2: Catalog filter label, large image size fix",
"version": "0.2.2"
},
{
"id": "release-0.2.1",
"kind": "release",
"timestamp": 1718884800,
"message": "v0.2.1: Incorrect assets path fix",
"version": "0.2.1"
},
{
"id": "release-0.2.0",
"kind": "release",
"timestamp": 1718884800,
"message": "v0.2.0: Major rewrite with p/all, catalog, multiboards, mod menu",
"version": "0.2.0"
},
{
"id": "release-0.1.17",
"kind": "release",
"timestamp": 1703073600,
"message": "v0.1.17: Electron IPFS errors, SettingsModal signer fix",
"version": "0.1.17"
},
{
"id": "release-0.1.16",
"kind": "release",
"timestamp": 1702900800,
"message": "v0.1.16: SettingsModal signer preview fix",
"version": "0.1.16"
},
{
"id": "release-0.1.15",
"kind": "release",
"timestamp": 1702641600,
"message": "v0.1.15: multisub.json, share link copy, view on seedit",
"version": "0.1.15"
},
{
"id": "release-0.1.14",
"kind": "release",
"timestamp": 1697976000,
"message": "v0.1.14: Anon mode per-thread, home redesign, popular threads",
"version": "0.1.14"
},
{
"id": "release-0.1.12",
"kind": "release",
"timestamp": 1695038400,
"message": "v0.1.12: New version toast once fix",
"version": "0.1.12"
},
{
"id": "release-0.1.10",
"kind": "release",
"timestamp": 1691668800,
"message": "v0.1.10: Revert refresh button",
"version": "0.1.10"
},
{
"id": "release-0.1.8",
"kind": "release",
"timestamp": 1686312000,
"message": "v0.1.8: Maintenance release",
"version": "0.1.8"
},
{
"id": "release-0.1.7",
"kind": "release",
"timestamp": 1686225600,
"message": "v0.1.7: Revert toasts refactor",
"version": "0.1.7"
},
{
"id": "release-0.1.5",
"kind": "release",
"timestamp": 1683892800,
"message": "v0.1.5: Maintenance release",
"version": "0.1.5"
},
{
"id": "release-0.1.4",
"kind": "release",
"timestamp": 1683806400,
"message": "v0.1.4: Maintenance release",
"version": "0.1.4"
},
{
"id": "release-0.1.3",
"kind": "release",
"timestamp": 1683374400,
"message": "v0.1.3: Maintenance release",
"version": "0.1.3"
},
{
"id": "release-0.1.2",
"kind": "release",
"timestamp": 1682769600,
"message": "v0.1.2: Maintenance release",
"version": "0.1.2"
},
{
"id": "release-0.1.1",
"kind": "release",
"timestamp": 1682596800,
"message": "v0.1.1: Maintenance release",
"version": "0.1.1"
},
{
"id": "release-0.1.0",
"kind": "release",
"timestamp": 1682337600,
"message": "v0.1.0: Initial release",
"version": "0.1.0"
}
]
}
+62
View File
@@ -0,0 +1,62 @@
/**
* Blotter utilities: sorting, preview slicing, date formatting, and entry guards.
* Board preview count is fixed at 3 entries to preserve compact block height.
*/
export type BlotterEntryKind = 'release' | 'manual';
export interface BlotterEntry {
id: string;
kind: BlotterEntryKind;
timestamp: number;
message: string;
version?: string;
}
/** Number of entries shown in the board blotter preview block. */
export const BLOTTER_PREVIEW_COUNT = 3;
/**
* Guards that an entry satisfies the BlotterEntry contract.
* Returns false for invalid entries (missing id, kind, timestamp, or message;
* kind=release without version).
*/
export function isBlotterEntry(entry: unknown): entry is BlotterEntry {
if (!entry || typeof entry !== 'object') return false;
const e = entry as Record<string, unknown>;
if (typeof e.id !== 'string' || !e.id.trim()) return false;
if (e.kind !== 'release' && e.kind !== 'manual') return false;
const ts = e.timestamp;
if (typeof ts !== 'number' || !Number.isFinite(ts) || ts < 0) return false;
if (typeof e.message !== 'string' || !e.message.trim()) return false;
if (e.kind === 'release' && (typeof e.version !== 'string' || !e.version.trim())) return false;
return true;
}
/**
* Filters and sorts entries by timestamp descending.
* Invalid entries are skipped. Ensures stable descending order.
*/
export function sortBlotterEntries<T extends { timestamp: number }>(entries: T[]): T[] {
return [...entries].sort((a, b) => b.timestamp - a.timestamp);
}
/**
* Returns the first N entries for board preview.
* Use BLOTTER_PREVIEW_COUNT (3) to preserve compact block height.
*/
export function getBlotterPreview<T>(entries: T[], count: number = BLOTTER_PREVIEW_COUNT): T[] {
return entries.slice(0, count);
}
/**
* Formats a unix timestamp (seconds) as MM/DD/YY.
*/
export function formatBlotterDate(timestamp: number): string {
if (!Number.isFinite(timestamp) || timestamp < 0) return '';
const date = new Date(timestamp * 1000);
const month = String(date.getUTCMonth() + 1).padStart(2, '0');
const day = String(date.getUTCDate()).padStart(2, '0');
const year = String(date.getUTCFullYear()).slice(-2);
return `${month}/${day}/${year}`;
}
@@ -0,0 +1,24 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface BlotterVisibilityState {
isHidden: boolean;
toggleVisibility: () => void;
}
const useBlotterVisibilityStore = create<BlotterVisibilityState>()(
persist(
(set) => ({
isHidden: false,
toggleVisibility: () =>
set((state) => ({
isHidden: !state.isHidden,
})),
}),
{
name: 'blotter-visibility',
},
),
);
export default useBlotterVisibilityStore;
@@ -1,27 +0,0 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface SubplebbitStatsVisibilityState {
hiddenStats: { [subplebbitAddress: string]: boolean };
toggleVisibility: (subplebbitAddress: string) => void;
}
const useSubplebbitStatsVisibilityStore = create<SubplebbitStatsVisibilityState>()(
persist(
(set) => ({
hiddenStats: {},
toggleVisibility: (subplebbitAddress) =>
set((state) => ({
hiddenStats: {
...state.hiddenStats,
[subplebbitAddress]: !state.hiddenStats[subplebbitAddress],
},
})),
}),
{
name: 'subplebbit-stats-visibility',
},
),
);
export default useSubplebbitStatsVisibilityStore;
+72
View File
@@ -0,0 +1,72 @@
.wrapper {
width: 100%;
}
.content {
margin: auto;
text-align: center;
padding: 0 10px;
}
.content h1 {
font-size: 28px;
font-weight: 700;
text-align: center;
margin: 20px 0 0;
font-family: 'Helvetica Neue', arial, sans-serif;
letter-spacing: -2px;
}
.table {
width: 80%;
max-width: 1000px;
margin: 25px auto;
border-collapse: collapse;
border: 1px solid #606060;
background-color: #fff;
color: #000;
}
.table th {
padding: 5px;
font-weight: 700;
background-color: #ffccaa;
vertical-align: middle;
}
.table .dateHeader {
text-align: center;
white-space: nowrap;
width: 90px;
}
.table .messageHeader {
text-align: left;
border-left: 1px solid #606060;
}
.table .dateCell {
white-space: nowrap;
width: 90px;
text-align: center;
}
.table .messageCell {
text-align: left;
border-left: 1px solid #606060;
}
.table td {
padding: 5px;
vertical-align: top;
}
.table tbody tr {
border-bottom: 1px solid #606060;
}
@media (max-width: 640px) {
.table {
width: calc(100% - 20px);
}
}
+48
View File
@@ -0,0 +1,48 @@
import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import blotterData from '../../data/5chan-blotter.json';
import BlotterMessage from '../../components/blotter-message';
import { formatBlotterDate, isBlotterEntry, sortBlotterEntries } from '../../lib/utils/blotter-utils';
import { Footer } from '../home';
import styles from './blotter.module.css';
const Blotter = () => {
const { t } = useTranslation();
const entries = (blotterData as { entries?: unknown[] }).entries ?? [];
const validEntries = entries.filter(isBlotterEntry);
const sorted = sortBlotterEntries(validEntries);
useEffect(() => {
window.scrollTo(0, 0);
document.title = 'Blotter - 5chan';
}, []);
return (
<div className={styles.wrapper}>
<div className={styles.content}>
<h1>5chan Blotter</h1>
<table className={styles.table}>
<thead>
<tr>
<th className={styles.dateHeader}>{t('date')}</th>
<th className={styles.messageHeader}>{t('message')}</th>
</tr>
</thead>
<tbody>
{sorted.map((entry) => (
<tr key={entry.id}>
<td className={styles.dateCell}>{formatBlotterDate(entry.timestamp)}</td>
<td className={styles.messageCell}>
<BlotterMessage entry={entry} />
</td>
</tr>
))}
</tbody>
</table>
<Footer />
</div>
</div>
);
};
export default Blotter;
+1
View File
@@ -0,0 +1 @@
export { default } from './blotter';