From 37d40626e730c074ca7bff2cff98b00bb031ac7b Mon Sep 17 00:00:00 2001 From: plebeius Date: Wed, 25 Feb 2026 14:51:10 +0800 Subject: [PATCH 01/20] refactor(account-settings): simplify UI layout, rename backup buttons, remove u/ prefix --- public/translations/ar/default.json | 5 +- public/translations/bn/default.json | 5 +- public/translations/cs/default.json | 5 +- public/translations/da/default.json | 5 +- public/translations/de/default.json | 5 +- public/translations/el/default.json | 5 +- public/translations/en/default.json | 5 +- public/translations/es/default.json | 5 +- public/translations/fa/default.json | 5 +- public/translations/fi/default.json | 5 +- public/translations/fil/default.json | 5 +- public/translations/fr/default.json | 5 +- public/translations/he/default.json | 5 +- public/translations/hi/default.json | 5 +- public/translations/hu/default.json | 5 +- public/translations/id/default.json | 5 +- public/translations/it/default.json | 5 +- public/translations/ja/default.json | 5 +- public/translations/ko/default.json | 5 +- public/translations/mr/default.json | 5 +- public/translations/nl/default.json | 5 +- public/translations/no/default.json | 5 +- public/translations/pl/default.json | 5 +- public/translations/pt/default.json | 5 +- public/translations/ro/default.json | 5 +- public/translations/ru/default.json | 5 +- public/translations/sq/default.json | 5 +- public/translations/sv/default.json | 5 +- public/translations/te/default.json | 5 +- public/translations/th/default.json | 5 +- public/translations/tr/default.json | 5 +- public/translations/uk/default.json | 5 +- public/translations/ur/default.json | 5 +- public/translations/vi/default.json | 5 +- public/translations/zh/default.json | 5 +- .../__tests__/account-settings.test.tsx | 9 ++-- .../account-settings.module.css | 6 +-- .../account-settings/account-settings.tsx | 51 ++++--------------- 38 files changed, 155 insertions(+), 86 deletions(-) diff --git a/public/translations/ar/default.json b/public/translations/ar/default.json index a9b09416..3ddca149 100644 --- a/public/translations/ar/default.json +++ b/public/translations/ar/default.json @@ -291,5 +291,8 @@ "continue": "متابعة", "loading_editor": "جاري تحميل المحرر...", "editor_fallback_warning": "فشل تحميل محرر الأكواد. يتم استخدام حقل نص عادي بدلاً منه.", - "return_to_settings": "العودة إلى الإعدادات" + "return_to_settings": "العودة إلى الإعدادات", + "import_account_backup": "استيراد نسخة احتياطية للحساب", + "account_auto_generated": "تم إنشاء هذا الحساب تلقائياً بواسطة 5chan، دون الحاجة للتسجيل.", + "download_backup": "تحميل النسخة الاحتياطية" } diff --git a/public/translations/bn/default.json b/public/translations/bn/default.json index 29cbc248..5e817479 100644 --- a/public/translations/bn/default.json +++ b/public/translations/bn/default.json @@ -291,5 +291,8 @@ "continue": "চালিয়ে যান", "loading_editor": "এডিটর লোড হচ্ছে...", "editor_fallback_warning": "কোড এডিটর লোড করতে ব্যর্থ হয়েছে। পরিবর্তে একটি সাধারণ টেক্সট এরিয়া ব্যবহার করা হচ্ছে।", - "return_to_settings": "সেটিংসে ফিরে যান" + "return_to_settings": "সেটিংসে ফিরে যান", + "import_account_backup": "অ্যাকাউন্ট ব্যাকআপ আমদানি করুন", + "account_auto_generated": "এই অ্যাকাউন্টটি 5chan দ্বারা স্বয়ংক্রিয়ভাবে তৈরি হয়েছে, সাইনআপের প্রয়োজন নেই।", + "download_backup": "ব্যাকআপ ডাউনলোড করুন" } diff --git a/public/translations/cs/default.json b/public/translations/cs/default.json index a29121a8..e8ba0663 100644 --- a/public/translations/cs/default.json +++ b/public/translations/cs/default.json @@ -291,5 +291,8 @@ "continue": "Pokračovat", "loading_editor": "Načítání editoru...", "editor_fallback_warning": "Načtení editoru kódu selhalo. Místo toho se používá jednoduché textové pole.", - "return_to_settings": "Zpět na nastavení" + "return_to_settings": "Zpět na nastavení", + "import_account_backup": "Importovat zálohu účtu", + "account_auto_generated": "Tento účet byl automaticky vygenerován 5chanem, registrace není nutná.", + "download_backup": "Stáhnout zálohu" } diff --git a/public/translations/da/default.json b/public/translations/da/default.json index 7e75ebc4..ec4e113f 100644 --- a/public/translations/da/default.json +++ b/public/translations/da/default.json @@ -291,5 +291,8 @@ "continue": "Fortsæt", "loading_editor": "Indlæser editor...", "editor_fallback_warning": "Kodeeditoren kunne ikke indlæses. Bruger et simpelt tekstfelt i stedet.", - "return_to_settings": "Tilbage til indstillinger" + "return_to_settings": "Tilbage til indstillinger", + "import_account_backup": "Importer kontobackup", + "account_auto_generated": "Denne konto blev automatisk oprettet af 5chan, ingen tilmelding påkrævet.", + "download_backup": "Download sikkerhedskopi" } diff --git a/public/translations/de/default.json b/public/translations/de/default.json index 2406c603..d891f147 100644 --- a/public/translations/de/default.json +++ b/public/translations/de/default.json @@ -291,5 +291,8 @@ "continue": "Weiter", "loading_editor": "Editor wird geladen...", "editor_fallback_warning": "Der Code-Editor konnte nicht geladen werden. Es wird stattdessen ein einfaches Textfeld verwendet.", - "return_to_settings": "Zurück zu den Einstellungen" + "return_to_settings": "Zurück zu den Einstellungen", + "import_account_backup": "Kontosicherung importieren", + "account_auto_generated": "Dieses Konto wurde automatisch von 5chan erstellt, keine Anmeldung erforderlich.", + "download_backup": "Sicherung herunterladen" } diff --git a/public/translations/el/default.json b/public/translations/el/default.json index da68d693..0c640a85 100644 --- a/public/translations/el/default.json +++ b/public/translations/el/default.json @@ -291,5 +291,8 @@ "continue": "Συνέχεια", "loading_editor": "Φόρτωση του επεξεργαστή...", "editor_fallback_warning": "Αποτυχία φόρτωσης του επεξεργαστή κώδικα. Χρησιμοποιείται απλό πεδίο κειμένου αντί για αυτό.", - "return_to_settings": "Επιστροφή στις Ρυθμίσεις" + "return_to_settings": "Επιστροφή στις Ρυθμίσεις", + "import_account_backup": "Εισαγωγή αντιγράφου ασφαλείας λογαριασμού", + "account_auto_generated": "Αυτός ο λογαριασμός δημιουργήθηκε αυτόματα από το 5chan, χωρίς απαιτούμενη εγγραφή.", + "download_backup": "Λήψη αντιγράφου ασφαλείας" } diff --git a/public/translations/en/default.json b/public/translations/en/default.json index 04dacc85..39cfd68d 100644 --- a/public/translations/en/default.json +++ b/public/translations/en/default.json @@ -290,5 +290,8 @@ "continue": "Continue", "loading_editor": "Loading editor...", "editor_fallback_warning": "The code editor failed to load. Using a plain textarea instead.", - "return_to_settings": "Return to Settings" + "return_to_settings": "Return to Settings", + "account_auto_generated": "This account was automatically generated by 5chan, no signup required.", + "download_backup": "Download Backup", + "import_account_backup": "Import Account Backup" } diff --git a/public/translations/es/default.json b/public/translations/es/default.json index 647990dc..9ac94666 100644 --- a/public/translations/es/default.json +++ b/public/translations/es/default.json @@ -291,5 +291,8 @@ "continue": "Continuar", "loading_editor": "Cargando editor...", "editor_fallback_warning": "No se pudo cargar el editor de código. Se usa un campo de texto simple en su lugar.", - "return_to_settings": "Volver a Configuración" + "return_to_settings": "Volver a Configuración", + "import_account_backup": "Importar copia de seguridad de la cuenta", + "account_auto_generated": "Esta cuenta fue generada automáticamente por 5chan, no se requiere registro.", + "download_backup": "Descargar copia de seguridad" } diff --git a/public/translations/fa/default.json b/public/translations/fa/default.json index e85e91b0..821ebaa8 100644 --- a/public/translations/fa/default.json +++ b/public/translations/fa/default.json @@ -291,5 +291,8 @@ "continue": "ادامه", "loading_editor": "در حال بارگذاری ویرایشگر...", "editor_fallback_warning": "بارگذاری ویرایشگر کد ناموفق بود. در عوض از یک فیلد متنی ساده استفاده می‌شود.", - "return_to_settings": "بازگشت به تنظیمات" + "return_to_settings": "بازگشت به تنظیمات", + "import_account_backup": "وارد کردن پشتیبان حساب", + "account_auto_generated": "این حساب به‌طور خودکار توسط 5chan ایجاد شده، نیازی به ثبت‌نام نیست.", + "download_backup": "دانلود پشتیبان" } diff --git a/public/translations/fi/default.json b/public/translations/fi/default.json index 746f180c..004ced5a 100644 --- a/public/translations/fi/default.json +++ b/public/translations/fi/default.json @@ -291,5 +291,8 @@ "continue": "Jatka", "loading_editor": "Ladataan editoria...", "editor_fallback_warning": "Koodieditorin lataus epäonnistui. Käytetään sen sijaan yksinkertaista tekstikenttää.", - "return_to_settings": "Palaa asetuksiin" + "return_to_settings": "Palaa asetuksiin", + "import_account_backup": "Tuo tilin varmuuskopio", + "account_auto_generated": "Tämä tili luotiin automaattisesti 5chanissa, rekisteröitymistä ei tarvita.", + "download_backup": "Lataa varmuuskopio" } diff --git a/public/translations/fil/default.json b/public/translations/fil/default.json index a581cf23..8b420318 100644 --- a/public/translations/fil/default.json +++ b/public/translations/fil/default.json @@ -291,5 +291,8 @@ "continue": "Magpatuloy", "loading_editor": "Nilo-load ang editor...", "editor_fallback_warning": "Nabigo ang pag-load ng code editor. Gumagamit ng plain textarea sa halip.", - "return_to_settings": "Bumalik sa Mga Setting" + "return_to_settings": "Bumalik sa Mga Setting", + "import_account_backup": "I-import ang Backup ng Account", + "account_auto_generated": "Ang account na ito ay awtomatikong nilikha ng 5chan, walang kailangang signup.", + "download_backup": "I-download ang Backup" } diff --git a/public/translations/fr/default.json b/public/translations/fr/default.json index 9e1e6a75..22c67cc2 100644 --- a/public/translations/fr/default.json +++ b/public/translations/fr/default.json @@ -291,5 +291,8 @@ "continue": "Continuer", "loading_editor": "Chargement de l'éditeur...", "editor_fallback_warning": "Le chargement de l'éditeur de code a échoué. Utilisation d'un champ de texte simple à la place.", - "return_to_settings": "Retour aux paramètres" + "return_to_settings": "Retour aux paramètres", + "import_account_backup": "Importer la sauvegarde du compte", + "account_auto_generated": "Ce compte a été généré automatiquement par 5chan, aucune inscription requise.", + "download_backup": "Télécharger la sauvegarde" } diff --git a/public/translations/he/default.json b/public/translations/he/default.json index 8dc15130..19adc721 100644 --- a/public/translations/he/default.json +++ b/public/translations/he/default.json @@ -291,5 +291,8 @@ "continue": "המשך", "loading_editor": "טוען עורך...", "editor_fallback_warning": "טעינת עורך הקוד נכשלה. משתמשים בשדה טקסט פשוט במקום.", - "return_to_settings": "חזרה להגדרות" + "return_to_settings": "חזרה להגדרות", + "import_account_backup": "ייבוא גיבוי חשבון", + "account_auto_generated": "חשבון זה נוצר אוטומטית על ידי 5chan, אין צורך בהרשמה.", + "download_backup": "הורד גיבוי" } diff --git a/public/translations/hi/default.json b/public/translations/hi/default.json index 21408058..37539feb 100644 --- a/public/translations/hi/default.json +++ b/public/translations/hi/default.json @@ -291,5 +291,8 @@ "continue": "जारी रखें", "loading_editor": "संपादक लोड हो रहा है...", "editor_fallback_warning": "कोड एडिटर लोड नहीं हो सका। इसके बजाय एक सामान्य टेक्स्ट एरिया का उपयोग किया जा रहा है।", - "return_to_settings": "सेटिंग्स पर वापस जाएं" + "return_to_settings": "सेटिंग्स पर वापस जाएं", + "import_account_backup": "खाता बैकअप आयात करें", + "account_auto_generated": "यह खाता 5chan द्वारा स्वचालित रूप से बनाया गया था, साइनअप की आवश्यकता नहीं है।", + "download_backup": "बैकअप डाउनलोड करें" } diff --git a/public/translations/hu/default.json b/public/translations/hu/default.json index b115238d..ad283c4f 100644 --- a/public/translations/hu/default.json +++ b/public/translations/hu/default.json @@ -291,5 +291,8 @@ "continue": "Folytatás", "loading_editor": "Szerkesztő betöltése...", "editor_fallback_warning": "A kódszerkesztő betöltése sikertelen. Egyszerű szövegmező használata helyette.", - "return_to_settings": "Vissza a beállításokhoz" + "return_to_settings": "Vissza a beállításokhoz", + "import_account_backup": "Fiók biztonsági mentés importálása", + "account_auto_generated": "Ezt a fiókot a 5chan automatikusan hozta létre, regisztráció nem szükséges.", + "download_backup": "Biztonsági mentés letöltése" } diff --git a/public/translations/id/default.json b/public/translations/id/default.json index 0f829020..85aa51ef 100644 --- a/public/translations/id/default.json +++ b/public/translations/id/default.json @@ -291,5 +291,8 @@ "continue": "Lanjutkan", "loading_editor": "Memuat editor...", "editor_fallback_warning": "Editor kode gagal dimuat. Menggunakan textarea biasa sebagai gantinya.", - "return_to_settings": "Kembali ke Pengaturan" + "return_to_settings": "Kembali ke Pengaturan", + "import_account_backup": "Impor Cadangan Akun", + "account_auto_generated": "Akun ini dibuat secara otomatis oleh 5chan, tidak perlu mendaftar.", + "download_backup": "Unduh cadangan" } diff --git a/public/translations/it/default.json b/public/translations/it/default.json index 963e9e9e..4688a0bd 100644 --- a/public/translations/it/default.json +++ b/public/translations/it/default.json @@ -291,5 +291,8 @@ "continue": "Continua", "loading_editor": "Caricamento editor...", "editor_fallback_warning": "Il caricamento dell'editor di codice non è riuscito. Si usa un campo di testo semplice al suo posto.", - "return_to_settings": "Torna alle impostazioni" + "return_to_settings": "Torna alle impostazioni", + "import_account_backup": "Importa backup account", + "account_auto_generated": "Questo account è stato generato automaticamente da 5chan, nessuna registrazione richiesta.", + "download_backup": "Scarica backup" } diff --git a/public/translations/ja/default.json b/public/translations/ja/default.json index 8dabcdd9..d99728da 100644 --- a/public/translations/ja/default.json +++ b/public/translations/ja/default.json @@ -291,5 +291,8 @@ "continue": "続ける", "loading_editor": "エディターを読み込み中...", "editor_fallback_warning": "コードエディターの読み込みに失敗しました。代わりにプレーンテキストエリアを使用します。", - "return_to_settings": "設定に戻る" + "return_to_settings": "設定に戻る", + "import_account_backup": "アカウントバックアップをインポート", + "account_auto_generated": "このアカウントは5chanによって自動生成されました。サインアップは不要です。", + "download_backup": "バックアップをダウンロード" } diff --git a/public/translations/ko/default.json b/public/translations/ko/default.json index 7d88cbc2..bf98139e 100644 --- a/public/translations/ko/default.json +++ b/public/translations/ko/default.json @@ -291,5 +291,8 @@ "continue": "계속", "loading_editor": "편집기 로딩 중...", "editor_fallback_warning": "코드 편집기 로드에 실패했습니다. 대신 일반 텍스트 영역을 사용합니다.", - "return_to_settings": "설정으로 돌아가기" + "return_to_settings": "설정으로 돌아가기", + "import_account_backup": "계정 백업 가져오기", + "account_auto_generated": "이 계정은 5chan에서 자동으로 생성되었으며, 가입이 필요하지 않습니다.", + "download_backup": "백업 다운로드" } diff --git a/public/translations/mr/default.json b/public/translations/mr/default.json index 018479c5..3209a694 100644 --- a/public/translations/mr/default.json +++ b/public/translations/mr/default.json @@ -291,5 +291,8 @@ "continue": "सुरू ठेवा", "loading_editor": "संपादक लोड होत आहे...", "editor_fallback_warning": "कोड एडिटर लोड नाही झाला. त्याऐवजी साध्या टेक्स्ट एरियाचा वापर केला जात आहे.", - "return_to_settings": "सेटिंग्जवर परत जा" + "return_to_settings": "सेटिंग्जवर परत जा", + "import_account_backup": "खाते बॅकअप आयात करा", + "account_auto_generated": "हा खाता 5chan द्वारा स्वयंचलितपणे तयार केला गेला, साइनअप आवश्यक नाही.", + "download_backup": "बॅकअप डाउनलोड करा" } diff --git a/public/translations/nl/default.json b/public/translations/nl/default.json index 1dd0658b..77ab2ee7 100644 --- a/public/translations/nl/default.json +++ b/public/translations/nl/default.json @@ -291,5 +291,8 @@ "continue": "Doorgaan", "loading_editor": "Editor laden...", "editor_fallback_warning": "De code-editor kon niet worden geladen. Er wordt een eenvoudig tekstveld gebruikt.", - "return_to_settings": "Terug naar Instellingen" + "return_to_settings": "Terug naar Instellingen", + "import_account_backup": "Accountback-up importeren", + "account_auto_generated": "Dit account is automatisch gegenereerd door 5chan, geen aanmelding vereist.", + "download_backup": "Back-up downloaden" } diff --git a/public/translations/no/default.json b/public/translations/no/default.json index 58c94b71..3cb33b7f 100644 --- a/public/translations/no/default.json +++ b/public/translations/no/default.json @@ -291,5 +291,8 @@ "continue": "Fortsett", "loading_editor": "Laster editor...", "editor_fallback_warning": "Kodeeditoren kunne ikke lastes. Bruker et enkelt tekstfelt i stedet.", - "return_to_settings": "Tilbake til Innstillinger" + "return_to_settings": "Tilbake til Innstillinger", + "import_account_backup": "Importer kontobackup", + "account_auto_generated": "Denne kontoen ble automatisk opprettet av 5chan, ingen registrering nødvendig.", + "download_backup": "Last ned sikkerhetskopi" } diff --git a/public/translations/pl/default.json b/public/translations/pl/default.json index 24f1cf33..367f08ec 100644 --- a/public/translations/pl/default.json +++ b/public/translations/pl/default.json @@ -291,5 +291,8 @@ "continue": "Kontynuuj", "loading_editor": "Ładowanie edytora...", "editor_fallback_warning": "Nie udało się załadować edytora kodu. Używane jest zamiast tego prostego pola tekstowego.", - "return_to_settings": "Powrót do ustawień" + "return_to_settings": "Powrót do ustawień", + "import_account_backup": "Importuj kopię zapasową konta", + "account_auto_generated": "To konto zostało automatycznie wygenerowane przez 5chan, rejestracja nie jest wymagana.", + "download_backup": "Pobierz kopię zapasową" } diff --git a/public/translations/pt/default.json b/public/translations/pt/default.json index a064b202..4c4c3e21 100644 --- a/public/translations/pt/default.json +++ b/public/translations/pt/default.json @@ -291,5 +291,8 @@ "continue": "Continuar", "loading_editor": "Carregando editor...", "editor_fallback_warning": "O editor de código falhou ao carregar. Usando um campo de texto simples em vez disso.", - "return_to_settings": "Voltar às Configurações" + "return_to_settings": "Voltar às Configurações", + "import_account_backup": "Importar backup da conta", + "account_auto_generated": "Esta conta foi gerada automaticamente pelo 5chan, sem necessidade de cadastro.", + "download_backup": "Baixar backup" } diff --git a/public/translations/ro/default.json b/public/translations/ro/default.json index c2c7a7a6..55e350fb 100644 --- a/public/translations/ro/default.json +++ b/public/translations/ro/default.json @@ -291,5 +291,8 @@ "continue": "Continuă", "loading_editor": "Se încarcă editorul...", "editor_fallback_warning": "Editorul de cod nu s-a putut încărca. Se folosește un câmp de text simplu în loc.", - "return_to_settings": "Înapoi la Setări" + "return_to_settings": "Înapoi la Setări", + "import_account_backup": "Importă backup cont", + "account_auto_generated": "Acest cont a fost generat automat de 5chan, nu este nevoie de înregistrare.", + "download_backup": "Descarcă backup" } diff --git a/public/translations/ru/default.json b/public/translations/ru/default.json index 7436b9b2..1acd9d10 100644 --- a/public/translations/ru/default.json +++ b/public/translations/ru/default.json @@ -291,5 +291,8 @@ "continue": "Продолжить", "loading_editor": "Загрузка редактора...", "editor_fallback_warning": "Не удалось загрузить редактор кода. Используется вместо него обычное текстовое поле.", - "return_to_settings": "Вернуться к настройкам" + "return_to_settings": "Вернуться к настройкам", + "import_account_backup": "Импорт резервной копии аккаунта", + "account_auto_generated": "Этот аккаунт был автоматически создан 5chan, регистрация не требуется.", + "download_backup": "Скачать резервную копию" } diff --git a/public/translations/sq/default.json b/public/translations/sq/default.json index 2b11c712..9e97a199 100644 --- a/public/translations/sq/default.json +++ b/public/translations/sq/default.json @@ -291,5 +291,8 @@ "continue": "Vazhdo", "loading_editor": "Duke ngarkuar redaktuesin...", "editor_fallback_warning": "Redaktori i kodit dështoi të ngarkohet. Duke përdorur një fushë teksti të thjeshtë në vend.", - "return_to_settings": "Kthehu te Cilësimet" + "return_to_settings": "Kthehu te Cilësimet", + "import_account_backup": "Importo backup të llogarisë", + "account_auto_generated": "Ky llogari u krijua automatikisht nga 5chan, nuk kërkohet regjistrim.", + "download_backup": "Shkarko kopjen e rezervë" } diff --git a/public/translations/sv/default.json b/public/translations/sv/default.json index e02b785a..cd056092 100644 --- a/public/translations/sv/default.json +++ b/public/translations/sv/default.json @@ -291,5 +291,8 @@ "continue": "Fortsätt", "loading_editor": "Laddar editor...", "editor_fallback_warning": "Kodredigeraren kunde inte laddas. Använder ett enkelt textfält istället.", - "return_to_settings": "Tillbaka till inställningar" + "return_to_settings": "Tillbaka till inställningar", + "import_account_backup": "Importera kontobackup", + "account_auto_generated": "Detta konto skapades automatiskt av 5chan, ingen registrering krävs.", + "download_backup": "Ladda ner säkerhetskopia" } diff --git a/public/translations/te/default.json b/public/translations/te/default.json index 1f39c35b..df039cda 100644 --- a/public/translations/te/default.json +++ b/public/translations/te/default.json @@ -291,5 +291,8 @@ "continue": "కొనసాగించు", "loading_editor": "సంపాదకాన్ని లోడ్ చేస్తోంది...", "editor_fallback_warning": "కోడ్ ఎడిటర్ లోడ్ చేయడం విఫలమైంది. బదులుగా సాధారణ టెక్స్ట్ ఏరియా ఉపయోగిస్తున్నారు.", - "return_to_settings": "సెట్టింగ్‌లకు తిరిగి వెళ్ళండి" + "return_to_settings": "సెట్టింగ్‌లకు తిరిగి వెళ్ళండి", + "import_account_backup": "ఖాతా బ్యాకప్ దిగుమతి చేయండి", + "account_auto_generated": "ఈ ఖాతా 5chan చేత స్వయంచాలకంగా సృష్టించబడింది, సైన్ అప్ అవసరం లేదు.", + "download_backup": "బ్యాకప్ డౌన్‌లోడ్ చేయండి" } diff --git a/public/translations/th/default.json b/public/translations/th/default.json index b8d658ed..993cee23 100644 --- a/public/translations/th/default.json +++ b/public/translations/th/default.json @@ -291,5 +291,8 @@ "continue": "ดำเนินการต่อ", "loading_editor": "กำลังโหลดตัวแก้ไข...", "editor_fallback_warning": "โหลดตัวแก้ไขโค้ดไม่สำเร็จ กำลังใช้ช่องข้อความธรรมดาแทน", - "return_to_settings": "กลับไปที่การตั้งค่า" + "return_to_settings": "กลับไปที่การตั้งค่า", + "import_account_backup": "นำเข้ารายการสำรองของบัญชี", + "account_auto_generated": "บัญชีนี้ถูกสร้างโดยอัตโนมัติโดย 5chan ไม่ต้องสมัครสมาชิก", + "download_backup": "ดาวน์โหลดสำรอง" } diff --git a/public/translations/tr/default.json b/public/translations/tr/default.json index 3a290b51..f77848c3 100644 --- a/public/translations/tr/default.json +++ b/public/translations/tr/default.json @@ -291,5 +291,8 @@ "continue": "Devam", "loading_editor": "Düzenleyici yükleniyor...", "editor_fallback_warning": "Kod düzenleyici yüklenemedi. Bunun yerine düz bir metin alanı kullanılıyor.", - "return_to_settings": "Ayarlara Dön" + "return_to_settings": "Ayarlara Dön", + "import_account_backup": "Hesap yedeğini içe aktar", + "account_auto_generated": "Bu hesap 5chan tarafından otomatik olarak oluşturuldu, kayıt gerekmez.", + "download_backup": "Yedek indir" } diff --git a/public/translations/uk/default.json b/public/translations/uk/default.json index f52aeb2b..6166deea 100644 --- a/public/translations/uk/default.json +++ b/public/translations/uk/default.json @@ -291,5 +291,8 @@ "continue": "Продовжити", "loading_editor": "Завантаження редактора...", "editor_fallback_warning": "Не вдалося завантажити редактор коду. Використовується прості текстове поле замість нього.", - "return_to_settings": "Повернутися до налаштувань" + "return_to_settings": "Повернутися до налаштувань", + "import_account_backup": "Імпорт резервної копії облікового запису", + "account_auto_generated": "Цей обліковий запис було автоматично створено 5chan, реєстрація не потрібна.", + "download_backup": "Завантажити резервну копію" } diff --git a/public/translations/ur/default.json b/public/translations/ur/default.json index 942f2f4e..c51df5c2 100644 --- a/public/translations/ur/default.json +++ b/public/translations/ur/default.json @@ -291,5 +291,8 @@ "continue": "جاری رکھیں", "loading_editor": "ایڈیٹر لوڈ ہو رہا ہے...", "editor_fallback_warning": "کوڈ ایڈیٹر لوڈ نہیں ہو سکا۔ اس کے بجائے ایک سادہ ٹیکسٹ ایریا استعمال کیا جا رہا ہے۔", - "return_to_settings": "ترتیبات میں واپس جائیں" + "return_to_settings": "ترتیبات میں واپس جائیں", + "import_account_backup": "اکاؤنٹ بیک اپ درآمد کریں", + "account_auto_generated": "یہ اکاؤنٹ 5chan نے خودکار طور پر بنایا ہے، سائن اپ کی ضرورت نہیں۔", + "download_backup": "بیک اپ ڈاؤن لوڈ کریں" } diff --git a/public/translations/vi/default.json b/public/translations/vi/default.json index 7ff9a149..b5f17ebf 100644 --- a/public/translations/vi/default.json +++ b/public/translations/vi/default.json @@ -291,5 +291,8 @@ "continue": "Tiếp tục", "loading_editor": "Đang tải trình soạn thảo...", "editor_fallback_warning": "Không thể tải trình soạn thảo mã. Đang sử dụng ô văn bản thuần thay thế.", - "return_to_settings": "Quay lại Cài đặt" + "return_to_settings": "Quay lại Cài đặt", + "import_account_backup": "Nhập bản sao lưu tài khoản", + "account_auto_generated": "Tài khoản này được tạo tự động bởi 5chan, không cần đăng ký.", + "download_backup": "Tải xuống bản sao lưu" } diff --git a/public/translations/zh/default.json b/public/translations/zh/default.json index 9de57f6d..8ff2d88a 100644 --- a/public/translations/zh/default.json +++ b/public/translations/zh/default.json @@ -291,5 +291,8 @@ "continue": "继续", "loading_editor": "正在加载编辑器...", "editor_fallback_warning": "代码编辑器加载失败。改用纯文本输入框。", - "return_to_settings": "返回设置" + "return_to_settings": "返回设置", + "import_account_backup": "导入账户备份", + "account_auto_generated": "此账户由 5chan 自动生成,无需注册。", + "download_backup": "下载备份" } diff --git a/src/components/settings-modal/account-settings/__tests__/account-settings.test.tsx b/src/components/settings-modal/account-settings/__tests__/account-settings.test.tsx index 2a8cffce..ee9c1023 100644 --- a/src/components/settings-modal/account-settings/__tests__/account-settings.test.tsx +++ b/src/components/settings-modal/account-settings/__tests__/account-settings.test.tsx @@ -18,7 +18,6 @@ vi.mock('react-i18next', () => ({ vi.mock('@plebbit/plebbit-react-hooks', () => ({ useAccount: () => ({ id: 'test-id', name: 'Account 1', author: { address: '0x123', shortAddress: '0x1...3' } }), useAccounts: () => ({ accounts: [{ id: 'test-id', name: 'Account 1', author: { shortAddress: '0x1...3' } }] }), - createAccount: vi.fn(), deleteAccount: vi.fn(), exportAccount: vi.fn(), importAccount: vi.fn(), @@ -70,13 +69,13 @@ describe('AccountSettings', () => { expect(buttons.some((b) => (b.textContent ?? '').includes('edit'))).toBe(true); }); - it('renders create, import, export buttons', () => { + it('renders download_backup and import_account_backup buttons', () => { render(createElement(AccountSettings)); const buttons = Array.from(container.querySelectorAll('button')); const texts = buttons.map((b) => b.textContent ?? ''); - expect(texts.some((t) => t.includes('create'))).toBe(true); - expect(texts.some((t) => t.includes('import'))).toBe(true); - expect(texts.some((t) => t.includes('export'))).toBe(true); + expect(texts.some((t) => t.includes('download_backup'))).toBe(true); + expect(texts.some((t) => t.includes('import_account_backup'))).toBe(true); + expect(texts.some((t) => t.includes('create'))).toBe(false); }); it('renders delete_account button', () => { diff --git a/src/components/settings-modal/account-settings/account-settings.module.css b/src/components/settings-modal/account-settings/account-settings.module.css index fce6a1e9..115ce486 100644 --- a/src/components/settings-modal/account-settings/account-settings.module.css +++ b/src/components/settings-modal/account-settings/account-settings.module.css @@ -17,11 +17,7 @@ color: red; } -.setting .createAccount { - margin-left: 5px; -} - -.warning { +.info { font-size: 0.8em; padding: 5px 0; } diff --git a/src/components/settings-modal/account-settings/account-settings.tsx b/src/components/settings-modal/account-settings/account-settings.tsx index 22b007c1..54d43ffc 100644 --- a/src/components/settings-modal/account-settings/account-settings.tsx +++ b/src/components/settings-modal/account-settings/account-settings.tsx @@ -1,6 +1,5 @@ -import { useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; -import { createAccount, deleteAccount, exportAccount, importAccount, setActiveAccount, useAccount, useAccounts } from '@plebbit/plebbit-react-hooks'; +import { deleteAccount, exportAccount, importAccount, setActiveAccount, useAccount, useAccounts } from '@plebbit/plebbit-react-hooks'; import styles from './account-settings.module.css'; import { Capacitor } from '@capacitor/core'; import { useLocation, useNavigate } from 'react-router-dom'; @@ -33,35 +32,8 @@ const AccountSettingsEditor = ({ const { t } = useTranslation(); const location = useLocation(); const { accounts } = useAccounts(); - const switchToNewAccountRef = useRef(false); const navigate = useNavigate(); - useEffect(() => { - if (switchToNewAccountRef.current && accounts.length > 0) { - const lastAccount = accounts[accounts.length - 1]; - setActiveAccount(lastAccount.name); - switchToNewAccountRef.current = false; - } - }, [accounts]); - - const handleCreateAccount = async () => { - const result = await withErrorHandling( - async () => { - switchToNewAccountRef.current = true; - await createAccount(); - }, - (error) => { - if (error instanceof Error) { - alert(error.message); - console.log(error); - } else { - console.error('An unknown error occurred:', error); - } - }, - ); - void result; - }; - const _deleteAccount = (accountName: string) => { if (!accountName) { return; @@ -184,29 +156,26 @@ const AccountSettingsEditor = ({ const accountsOptions = accounts.map((account) => ( )); + const host = window.electronApi?.isElectron ? 'this desktop app' : isAndroid ? 'this mobile app' : window.location.hostname; + return (
- {' '} - -
- {t('stored_locally', { - location: window.electronApi?.isElectron ? 'this desktop app' : isAndroid ? 'this mobile app' : window.location.hostname, - interpolation: { escapeValue: false }, - })} + {' '} + {' '} + +
+ {t('account_auto_generated')} {t('stored_locally', { location: host, interpolation: { escapeValue: false } })}
- + From 9c0350ca244b53e3f180a499350fa3fdfcb8fd1e Mon Sep 17 00:00:00 2001 From: plebeius Date: Wed, 25 Feb 2026 15:19:13 +0800 Subject: [PATCH 02/20] fix(board-buttons): use postCid for page number in PostPageStats --- src/components/board-buttons/board-buttons.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/board-buttons/board-buttons.tsx b/src/components/board-buttons/board-buttons.tsx index de58c127..b5bae65e 100644 --- a/src/components/board-buttons/board-buttons.tsx +++ b/src/components/board-buttons/board-buttons.tsx @@ -465,7 +465,7 @@ export const PostPageStats = () => { const subplebbitAddress = resolvedAddress || accountComment?.subplebbitAddress; const comment = useComment({ commentCid }); - const postCid = comment?.parentCid ?? commentCid; + const postCid = comment?.postCid ?? commentCid; const post = useComment({ commentCid: postCid }); const { closed, pinned, replyCount } = post || {}; From e241125aacb8515202e9082616c0037b28831404 Mon Sep 17 00:00:00 2001 From: plebeius Date: Wed, 25 Feb 2026 15:57:13 +0800 Subject: [PATCH 03/20] fix(backlinks): hide backlinks from pending replies without comment number --- src/components/post-desktop/post-desktop.tsx | 6 +++++- src/components/post-mobile/post-mobile.tsx | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/post-desktop/post-desktop.tsx b/src/components/post-desktop/post-desktop.tsx index 70490cce..d8db269a 100644 --- a/src/components/post-desktop/post-desktop.tsx +++ b/src/components/post-desktop/post-desktop.tsx @@ -482,6 +482,7 @@ const ReplyBacklinks = ({ (reply: Comment) => reply?.parentCid === cid && reply?.cid && + reply?.number && !(reply?.deleted || reply?.removed) && , )} {quotedByMap @@ -490,6 +491,7 @@ const ReplyBacklinks = ({ (reply: Comment) => reply?.parentCid !== cid && reply?.cid && + reply?.number && !(reply?.deleted || reply?.removed) && , )} @@ -502,7 +504,9 @@ const OpBacklinks = ({ cid, quotedByMap }: { cid: string; quotedByMap?: Map - reply?.cid && !(reply?.deleted || reply?.removed) && , + reply?.cid && + reply?.number && + !(reply?.deleted || reply?.removed) && , )} ); diff --git a/src/components/post-mobile/post-mobile.tsx b/src/components/post-mobile/post-mobile.tsx index ea66adc9..3c219084 100644 --- a/src/components/post-mobile/post-mobile.tsx +++ b/src/components/post-mobile/post-mobile.tsx @@ -437,7 +437,9 @@ const ReplyBacklinks = ({ post, quotedByMap, directRepliesByParentCid }: ReplyBa ?.get(cid) ?.map( (reply: Comment) => - reply?.cid && !(reply?.deleted || reply?.removed) && , + reply?.cid && + reply?.number && + !(reply?.deleted || reply?.removed) && , ) .filter(Boolean); @@ -447,6 +449,7 @@ const ReplyBacklinks = ({ post, quotedByMap, directRepliesByParentCid }: ReplyBa (reply: Comment) => reply?.parentCid === cid && reply?.cid && + reply?.number && !(reply?.deleted || reply?.removed) && , )} {quotedByMap @@ -455,6 +458,7 @@ const ReplyBacklinks = ({ post, quotedByMap, directRepliesByParentCid }: ReplyBa (reply: Comment) => reply?.parentCid !== cid && reply?.cid && + reply?.number && !(reply?.deleted || reply?.removed) && , )} From 41f07124c6c7c7b2a2c6cffe65920fd519218454 Mon Sep 17 00:00:00 2001 From: plebeius Date: Wed, 25 Feb 2026 16:18:16 +0800 Subject: [PATCH 04/20] fix(backlinks): scroll to reply and persist highlight when clicking OP backlink --- .../reply-quote-preview/reply-quote-preview.tsx | 11 +++++++++++ src/index.css | 6 ++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/components/reply-quote-preview/reply-quote-preview.tsx b/src/components/reply-quote-preview/reply-quote-preview.tsx index 1aaf5ef7..4cec3b34 100644 --- a/src/components/reply-quote-preview/reply-quote-preview.tsx +++ b/src/components/reply-quote-preview/reply-quote-preview.tsx @@ -71,6 +71,15 @@ const scrollToThreadCardTop = (threadCid: string) => { return true; }; +const scrollToReplyOnPage = (cid: string) => { + const el = document.querySelector(`[data-cid="${cid}"][data-post-cid]`); + if (!el) return false; + el.scrollIntoView({ behavior: 'smooth', block: 'center' }); + el.classList.add('scroll-highlight'); + setTimeout(() => el.classList.remove('scroll-highlight'), 2000); + return true; +}; + const DesktopQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, isQuotelinkReply, isOP, showTrailingBreak = true }: ReplyQuotePreviewProps) => { const [hoveredCid, setHoveredCid] = useState(null); const [outOfViewCid, setOutOfViewCid] = useState(null); @@ -126,6 +135,7 @@ const DesktopQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, i scrollToThreadCardTop(cid); return; } + if (scrollToReplyOnPage(cid)) return; navigate(threadRoute); } }; @@ -242,6 +252,7 @@ const MobileQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, is scrollToThreadCardTop(cid); return; } + if (scrollToReplyOnPage(cid)) return; navigate(threadRoute); } }; diff --git a/src/index.css b/src/index.css index 97e46b19..0d53e141 100644 --- a/src/index.css +++ b/src/index.css @@ -82,7 +82,8 @@ hr { cursor: pointer; } - .highlight { + .highlight, + .scroll-highlight { background: var(--reply-highlight-background-color) !important; } @@ -104,7 +105,8 @@ hr { cursor: pointer; } - .highlight { + .highlight, + .scroll-highlight { background: var(--reply-highlight-background-color) !important; border: var(--reply-highlight-border) !important; border-left: var(--reply-highlight-border-left, revert) !important; From f6906e1ff8a90c7a044e8afbd833be647e926cae Mon Sep 17 00:00:00 2001 From: plebeius Date: Wed, 25 Feb 2026 16:43:51 +0800 Subject: [PATCH 05/20] feat(home): add Boards You Moderate link to Multiboards in boards box --- public/translations/ar/default.json | 3 ++- public/translations/bn/default.json | 3 ++- public/translations/cs/default.json | 3 ++- public/translations/da/default.json | 3 ++- public/translations/de/default.json | 3 ++- public/translations/el/default.json | 3 ++- public/translations/en/default.json | 3 ++- public/translations/es/default.json | 3 ++- public/translations/fa/default.json | 3 ++- public/translations/fi/default.json | 3 ++- public/translations/fil/default.json | 3 ++- public/translations/fr/default.json | 3 ++- public/translations/he/default.json | 3 ++- public/translations/hi/default.json | 3 ++- public/translations/hu/default.json | 3 ++- public/translations/id/default.json | 3 ++- public/translations/it/default.json | 3 ++- public/translations/ja/default.json | 3 ++- public/translations/ko/default.json | 3 ++- public/translations/mr/default.json | 3 ++- public/translations/nl/default.json | 3 ++- public/translations/no/default.json | 3 ++- public/translations/pl/default.json | 3 ++- public/translations/pt/default.json | 3 ++- public/translations/ro/default.json | 3 ++- public/translations/ru/default.json | 3 ++- public/translations/sq/default.json | 3 ++- public/translations/sv/default.json | 3 ++- public/translations/te/default.json | 3 ++- public/translations/th/default.json | 3 ++- public/translations/tr/default.json | 3 ++- public/translations/uk/default.json | 3 ++- public/translations/ur/default.json | 3 ++- public/translations/vi/default.json | 3 ++- public/translations/zh/default.json | 3 ++- src/views/home/boards-list/boards-list.tsx | 15 +++++++++++++++ 36 files changed, 85 insertions(+), 35 deletions(-) diff --git a/public/translations/ar/default.json b/public/translations/ar/default.json index 3ddca149..3c2fe48d 100644 --- a/public/translations/ar/default.json +++ b/public/translations/ar/default.json @@ -294,5 +294,6 @@ "return_to_settings": "العودة إلى الإعدادات", "import_account_backup": "استيراد نسخة احتياطية للحساب", "account_auto_generated": "تم إنشاء هذا الحساب تلقائياً بواسطة 5chan، دون الحاجة للتسجيل.", - "download_backup": "تحميل النسخة الاحتياطية" + "download_backup": "تحميل النسخة الاحتياطية", + "boards_you_moderate_nav": "لوحات تعمل كمشرف عليها" } diff --git a/public/translations/bn/default.json b/public/translations/bn/default.json index 5e817479..ec48ced7 100644 --- a/public/translations/bn/default.json +++ b/public/translations/bn/default.json @@ -294,5 +294,6 @@ "return_to_settings": "সেটিংসে ফিরে যান", "import_account_backup": "অ্যাকাউন্ট ব্যাকআপ আমদানি করুন", "account_auto_generated": "এই অ্যাকাউন্টটি 5chan দ্বারা স্বয়ংক্রিয়ভাবে তৈরি হয়েছে, সাইনআপের প্রয়োজন নেই।", - "download_backup": "ব্যাকআপ ডাউনলোড করুন" + "download_backup": "ব্যাকআপ ডাউনলোড করুন", + "boards_you_moderate_nav": "আপনি যা মডারেট করেন এমন বোর্ড" } diff --git a/public/translations/cs/default.json b/public/translations/cs/default.json index e8ba0663..89a6a315 100644 --- a/public/translations/cs/default.json +++ b/public/translations/cs/default.json @@ -294,5 +294,6 @@ "return_to_settings": "Zpět na nastavení", "import_account_backup": "Importovat zálohu účtu", "account_auto_generated": "Tento účet byl automaticky vygenerován 5chanem, registrace není nutná.", - "download_backup": "Stáhnout zálohu" + "download_backup": "Stáhnout zálohu", + "boards_you_moderate_nav": "Radce, které moderujete" } diff --git a/public/translations/da/default.json b/public/translations/da/default.json index ec4e113f..a309a843 100644 --- a/public/translations/da/default.json +++ b/public/translations/da/default.json @@ -294,5 +294,6 @@ "return_to_settings": "Tilbage til indstillinger", "import_account_backup": "Importer kontobackup", "account_auto_generated": "Denne konto blev automatisk oprettet af 5chan, ingen tilmelding påkrævet.", - "download_backup": "Download sikkerhedskopi" + "download_backup": "Download sikkerhedskopi", + "boards_you_moderate_nav": "Boards du modererer" } diff --git a/public/translations/de/default.json b/public/translations/de/default.json index d891f147..f395dc86 100644 --- a/public/translations/de/default.json +++ b/public/translations/de/default.json @@ -294,5 +294,6 @@ "return_to_settings": "Zurück zu den Einstellungen", "import_account_backup": "Kontosicherung importieren", "account_auto_generated": "Dieses Konto wurde automatisch von 5chan erstellt, keine Anmeldung erforderlich.", - "download_backup": "Sicherung herunterladen" + "download_backup": "Sicherung herunterladen", + "boards_you_moderate_nav": "Von dir moderierte Boards" } diff --git a/public/translations/el/default.json b/public/translations/el/default.json index 0c640a85..1f8ff51a 100644 --- a/public/translations/el/default.json +++ b/public/translations/el/default.json @@ -294,5 +294,6 @@ "return_to_settings": "Επιστροφή στις Ρυθμίσεις", "import_account_backup": "Εισαγωγή αντιγράφου ασφαλείας λογαριασμού", "account_auto_generated": "Αυτός ο λογαριασμός δημιουργήθηκε αυτόματα από το 5chan, χωρίς απαιτούμενη εγγραφή.", - "download_backup": "Λήψη αντιγράφου ασφαλείας" + "download_backup": "Λήψη αντιγράφου ασφαλείας", + "boards_you_moderate_nav": "Πίνακες που διαχειρίζεστε" } diff --git a/public/translations/en/default.json b/public/translations/en/default.json index 39cfd68d..a607790f 100644 --- a/public/translations/en/default.json +++ b/public/translations/en/default.json @@ -293,5 +293,6 @@ "return_to_settings": "Return to Settings", "account_auto_generated": "This account was automatically generated by 5chan, no signup required.", "download_backup": "Download Backup", - "import_account_backup": "Import Account Backup" + "import_account_backup": "Import Account Backup", + "boards_you_moderate_nav": "Boards You Moderate" } diff --git a/public/translations/es/default.json b/public/translations/es/default.json index 9ac94666..920448ce 100644 --- a/public/translations/es/default.json +++ b/public/translations/es/default.json @@ -294,5 +294,6 @@ "return_to_settings": "Volver a Configuración", "import_account_backup": "Importar copia de seguridad de la cuenta", "account_auto_generated": "Esta cuenta fue generada automáticamente por 5chan, no se requiere registro.", - "download_backup": "Descargar copia de seguridad" + "download_backup": "Descargar copia de seguridad", + "boards_you_moderate_nav": "Tableros que moderas" } diff --git a/public/translations/fa/default.json b/public/translations/fa/default.json index 821ebaa8..8285e47b 100644 --- a/public/translations/fa/default.json +++ b/public/translations/fa/default.json @@ -294,5 +294,6 @@ "return_to_settings": "بازگشت به تنظیمات", "import_account_backup": "وارد کردن پشتیبان حساب", "account_auto_generated": "این حساب به‌طور خودکار توسط 5chan ایجاد شده، نیازی به ثبت‌نام نیست.", - "download_backup": "دانلود پشتیبان" + "download_backup": "دانلود پشتیبان", + "boards_you_moderate_nav": "تابلوهایی که مدیریت می‌کنید" } diff --git a/public/translations/fi/default.json b/public/translations/fi/default.json index 004ced5a..7e2a791c 100644 --- a/public/translations/fi/default.json +++ b/public/translations/fi/default.json @@ -294,5 +294,6 @@ "return_to_settings": "Palaa asetuksiin", "import_account_backup": "Tuo tilin varmuuskopio", "account_auto_generated": "Tämä tili luotiin automaattisesti 5chanissa, rekisteröitymistä ei tarvita.", - "download_backup": "Lataa varmuuskopio" + "download_backup": "Lataa varmuuskopio", + "boards_you_moderate_nav": "Laudat, joita moderoit" } diff --git a/public/translations/fil/default.json b/public/translations/fil/default.json index 8b420318..935d517d 100644 --- a/public/translations/fil/default.json +++ b/public/translations/fil/default.json @@ -294,5 +294,6 @@ "return_to_settings": "Bumalik sa Mga Setting", "import_account_backup": "I-import ang Backup ng Account", "account_auto_generated": "Ang account na ito ay awtomatikong nilikha ng 5chan, walang kailangang signup.", - "download_backup": "I-download ang Backup" + "download_backup": "I-download ang Backup", + "boards_you_moderate_nav": "Mga board na minomodera mo" } diff --git a/public/translations/fr/default.json b/public/translations/fr/default.json index 22c67cc2..e36f91a1 100644 --- a/public/translations/fr/default.json +++ b/public/translations/fr/default.json @@ -294,5 +294,6 @@ "return_to_settings": "Retour aux paramètres", "import_account_backup": "Importer la sauvegarde du compte", "account_auto_generated": "Ce compte a été généré automatiquement par 5chan, aucune inscription requise.", - "download_backup": "Télécharger la sauvegarde" + "download_backup": "Télécharger la sauvegarde", + "boards_you_moderate_nav": "Tableaux que vous modérez" } diff --git a/public/translations/he/default.json b/public/translations/he/default.json index 19adc721..4aaf93cf 100644 --- a/public/translations/he/default.json +++ b/public/translations/he/default.json @@ -294,5 +294,6 @@ "return_to_settings": "חזרה להגדרות", "import_account_backup": "ייבוא גיבוי חשבון", "account_auto_generated": "חשבון זה נוצר אוטומטית על ידי 5chan, אין צורך בהרשמה.", - "download_backup": "הורד גיבוי" + "download_backup": "הורד גיבוי", + "boards_you_moderate_nav": "לוחות שאתה מנהל" } diff --git a/public/translations/hi/default.json b/public/translations/hi/default.json index 37539feb..133fc016 100644 --- a/public/translations/hi/default.json +++ b/public/translations/hi/default.json @@ -294,5 +294,6 @@ "return_to_settings": "सेटिंग्स पर वापस जाएं", "import_account_backup": "खाता बैकअप आयात करें", "account_auto_generated": "यह खाता 5chan द्वारा स्वचालित रूप से बनाया गया था, साइनअप की आवश्यकता नहीं है।", - "download_backup": "बैकअप डाउनलोड करें" + "download_backup": "बैकअप डाउनलोड करें", + "boards_you_moderate_nav": "बोर्ड जिन्हें आप मॉडरेट करते हैं" } diff --git a/public/translations/hu/default.json b/public/translations/hu/default.json index ad283c4f..44f5db45 100644 --- a/public/translations/hu/default.json +++ b/public/translations/hu/default.json @@ -294,5 +294,6 @@ "return_to_settings": "Vissza a beállításokhoz", "import_account_backup": "Fiók biztonsági mentés importálása", "account_auto_generated": "Ezt a fiókot a 5chan automatikusan hozta létre, regisztráció nem szükséges.", - "download_backup": "Biztonsági mentés letöltése" + "download_backup": "Biztonsági mentés letöltése", + "boards_you_moderate_nav": "A táblák, amelyeket moderálsz" } diff --git a/public/translations/id/default.json b/public/translations/id/default.json index 85aa51ef..5a513dd2 100644 --- a/public/translations/id/default.json +++ b/public/translations/id/default.json @@ -294,5 +294,6 @@ "return_to_settings": "Kembali ke Pengaturan", "import_account_backup": "Impor Cadangan Akun", "account_auto_generated": "Akun ini dibuat secara otomatis oleh 5chan, tidak perlu mendaftar.", - "download_backup": "Unduh cadangan" + "download_backup": "Unduh cadangan", + "boards_you_moderate_nav": "Papan yang Anda moderasi" } diff --git a/public/translations/it/default.json b/public/translations/it/default.json index 4688a0bd..54b9e838 100644 --- a/public/translations/it/default.json +++ b/public/translations/it/default.json @@ -294,5 +294,6 @@ "return_to_settings": "Torna alle impostazioni", "import_account_backup": "Importa backup account", "account_auto_generated": "Questo account è stato generato automaticamente da 5chan, nessuna registrazione richiesta.", - "download_backup": "Scarica backup" + "download_backup": "Scarica backup", + "boards_you_moderate_nav": "Board che moderi" } diff --git a/public/translations/ja/default.json b/public/translations/ja/default.json index d99728da..423d65a4 100644 --- a/public/translations/ja/default.json +++ b/public/translations/ja/default.json @@ -294,5 +294,6 @@ "return_to_settings": "設定に戻る", "import_account_backup": "アカウントバックアップをインポート", "account_auto_generated": "このアカウントは5chanによって自動生成されました。サインアップは不要です。", - "download_backup": "バックアップをダウンロード" + "download_backup": "バックアップをダウンロード", + "boards_you_moderate_nav": "あなたがモデレートする板" } diff --git a/public/translations/ko/default.json b/public/translations/ko/default.json index bf98139e..f0666877 100644 --- a/public/translations/ko/default.json +++ b/public/translations/ko/default.json @@ -294,5 +294,6 @@ "return_to_settings": "설정으로 돌아가기", "import_account_backup": "계정 백업 가져오기", "account_auto_generated": "이 계정은 5chan에서 자동으로 생성되었으며, 가입이 필요하지 않습니다.", - "download_backup": "백업 다운로드" + "download_backup": "백업 다운로드", + "boards_you_moderate_nav": "사용자가 관리하는 게시판" } diff --git a/public/translations/mr/default.json b/public/translations/mr/default.json index 3209a694..f7bbf9a6 100644 --- a/public/translations/mr/default.json +++ b/public/translations/mr/default.json @@ -294,5 +294,6 @@ "return_to_settings": "सेटिंग्जवर परत जा", "import_account_backup": "खाते बॅकअप आयात करा", "account_auto_generated": "हा खाता 5chan द्वारा स्वयंचलितपणे तयार केला गेला, साइनअप आवश्यक नाही.", - "download_backup": "बॅकअप डाउनलोड करा" + "download_backup": "बॅकअप डाउनलोड करा", + "boards_you_moderate_nav": "तुम्ही मॉडरेट करता ते बोर्ड" } diff --git a/public/translations/nl/default.json b/public/translations/nl/default.json index 77ab2ee7..d45f1a5d 100644 --- a/public/translations/nl/default.json +++ b/public/translations/nl/default.json @@ -294,5 +294,6 @@ "return_to_settings": "Terug naar Instellingen", "import_account_backup": "Accountback-up importeren", "account_auto_generated": "Dit account is automatisch gegenereerd door 5chan, geen aanmelding vereist.", - "download_backup": "Back-up downloaden" + "download_backup": "Back-up downloaden", + "boards_you_moderate_nav": "Boards die je beheert" } diff --git a/public/translations/no/default.json b/public/translations/no/default.json index 3cb33b7f..0ee5c25e 100644 --- a/public/translations/no/default.json +++ b/public/translations/no/default.json @@ -294,5 +294,6 @@ "return_to_settings": "Tilbake til Innstillinger", "import_account_backup": "Importer kontobackup", "account_auto_generated": "Denne kontoen ble automatisk opprettet av 5chan, ingen registrering nødvendig.", - "download_backup": "Last ned sikkerhetskopi" + "download_backup": "Last ned sikkerhetskopi", + "boards_you_moderate_nav": "Boards du modererer" } diff --git a/public/translations/pl/default.json b/public/translations/pl/default.json index 367f08ec..0a82808b 100644 --- a/public/translations/pl/default.json +++ b/public/translations/pl/default.json @@ -294,5 +294,6 @@ "return_to_settings": "Powrót do ustawień", "import_account_backup": "Importuj kopię zapasową konta", "account_auto_generated": "To konto zostało automatycznie wygenerowane przez 5chan, rejestracja nie jest wymagana.", - "download_backup": "Pobierz kopię zapasową" + "download_backup": "Pobierz kopię zapasową", + "boards_you_moderate_nav": "Tablice, które moderujesz" } diff --git a/public/translations/pt/default.json b/public/translations/pt/default.json index 4c4c3e21..12e413a6 100644 --- a/public/translations/pt/default.json +++ b/public/translations/pt/default.json @@ -294,5 +294,6 @@ "return_to_settings": "Voltar às Configurações", "import_account_backup": "Importar backup da conta", "account_auto_generated": "Esta conta foi gerada automaticamente pelo 5chan, sem necessidade de cadastro.", - "download_backup": "Baixar backup" + "download_backup": "Baixar backup", + "boards_you_moderate_nav": "Quadros que você modera" } diff --git a/public/translations/ro/default.json b/public/translations/ro/default.json index 55e350fb..5afe22fc 100644 --- a/public/translations/ro/default.json +++ b/public/translations/ro/default.json @@ -294,5 +294,6 @@ "return_to_settings": "Înapoi la Setări", "import_account_backup": "Importă backup cont", "account_auto_generated": "Acest cont a fost generat automat de 5chan, nu este nevoie de înregistrare.", - "download_backup": "Descarcă backup" + "download_backup": "Descarcă backup", + "boards_you_moderate_nav": "Forumuri pe care le moderăm" } diff --git a/public/translations/ru/default.json b/public/translations/ru/default.json index 1acd9d10..fb94fd14 100644 --- a/public/translations/ru/default.json +++ b/public/translations/ru/default.json @@ -294,5 +294,6 @@ "return_to_settings": "Вернуться к настройкам", "import_account_backup": "Импорт резервной копии аккаунта", "account_auto_generated": "Этот аккаунт был автоматически создан 5chan, регистрация не требуется.", - "download_backup": "Скачать резервную копию" + "download_backup": "Скачать резервную копию", + "boards_you_moderate_nav": "Доски, которые вы модерируете" } diff --git a/public/translations/sq/default.json b/public/translations/sq/default.json index 9e97a199..1e89eb94 100644 --- a/public/translations/sq/default.json +++ b/public/translations/sq/default.json @@ -294,5 +294,6 @@ "return_to_settings": "Kthehu te Cilësimet", "import_account_backup": "Importo backup të llogarisë", "account_auto_generated": "Ky llogari u krijua automatikisht nga 5chan, nuk kërkohet regjistrim.", - "download_backup": "Shkarko kopjen e rezervë" + "download_backup": "Shkarko kopjen e rezervë", + "boards_you_moderate_nav": "Bordet që moderoni" } diff --git a/public/translations/sv/default.json b/public/translations/sv/default.json index cd056092..d2193608 100644 --- a/public/translations/sv/default.json +++ b/public/translations/sv/default.json @@ -294,5 +294,6 @@ "return_to_settings": "Tillbaka till inställningar", "import_account_backup": "Importera kontobackup", "account_auto_generated": "Detta konto skapades automatiskt av 5chan, ingen registrering krävs.", - "download_backup": "Ladda ner säkerhetskopia" + "download_backup": "Ladda ner säkerhetskopia", + "boards_you_moderate_nav": "Boards du modererar" } diff --git a/public/translations/te/default.json b/public/translations/te/default.json index df039cda..9e95c6c5 100644 --- a/public/translations/te/default.json +++ b/public/translations/te/default.json @@ -294,5 +294,6 @@ "return_to_settings": "సెట్టింగ్‌లకు తిరిగి వెళ్ళండి", "import_account_backup": "ఖాతా బ్యాకప్ దిగుమతి చేయండి", "account_auto_generated": "ఈ ఖాతా 5chan చేత స్వయంచాలకంగా సృష్టించబడింది, సైన్ అప్ అవసరం లేదు.", - "download_backup": "బ్యాకప్ డౌన్‌లోడ్ చేయండి" + "download_backup": "బ్యాకప్ డౌన్‌లోడ్ చేయండి", + "boards_you_moderate_nav": "మీరు మోడరేట్ చేసే బోర్డ్లు" } diff --git a/public/translations/th/default.json b/public/translations/th/default.json index 993cee23..f465645b 100644 --- a/public/translations/th/default.json +++ b/public/translations/th/default.json @@ -294,5 +294,6 @@ "return_to_settings": "กลับไปที่การตั้งค่า", "import_account_backup": "นำเข้ารายการสำรองของบัญชี", "account_auto_generated": "บัญชีนี้ถูกสร้างโดยอัตโนมัติโดย 5chan ไม่ต้องสมัครสมาชิก", - "download_backup": "ดาวน์โหลดสำรอง" + "download_backup": "ดาวน์โหลดสำรอง", + "boards_you_moderate_nav": "บอร์ดที่คุณดูแล" } diff --git a/public/translations/tr/default.json b/public/translations/tr/default.json index f77848c3..df357e5d 100644 --- a/public/translations/tr/default.json +++ b/public/translations/tr/default.json @@ -294,5 +294,6 @@ "return_to_settings": "Ayarlara Dön", "import_account_backup": "Hesap yedeğini içe aktar", "account_auto_generated": "Bu hesap 5chan tarafından otomatik olarak oluşturuldu, kayıt gerekmez.", - "download_backup": "Yedek indir" + "download_backup": "Yedek indir", + "boards_you_moderate_nav": "Yönettiğiniz panolar" } diff --git a/public/translations/uk/default.json b/public/translations/uk/default.json index 6166deea..c083d254 100644 --- a/public/translations/uk/default.json +++ b/public/translations/uk/default.json @@ -294,5 +294,6 @@ "return_to_settings": "Повернутися до налаштувань", "import_account_backup": "Імпорт резервної копії облікового запису", "account_auto_generated": "Цей обліковий запис було автоматично створено 5chan, реєстрація не потрібна.", - "download_backup": "Завантажити резервну копію" + "download_backup": "Завантажити резервну копію", + "boards_you_moderate_nav": "Дошки, які ви модеруєте" } diff --git a/public/translations/ur/default.json b/public/translations/ur/default.json index c51df5c2..02ede244 100644 --- a/public/translations/ur/default.json +++ b/public/translations/ur/default.json @@ -294,5 +294,6 @@ "return_to_settings": "ترتیبات میں واپس جائیں", "import_account_backup": "اکاؤنٹ بیک اپ درآمد کریں", "account_auto_generated": "یہ اکاؤنٹ 5chan نے خودکار طور پر بنایا ہے، سائن اپ کی ضرورت نہیں۔", - "download_backup": "بیک اپ ڈاؤن لوڈ کریں" + "download_backup": "بیک اپ ڈاؤن لوڈ کریں", + "boards_you_moderate_nav": "بورڈز جنہیں آپ ماڈریٹ کرتے ہیں" } diff --git a/public/translations/vi/default.json b/public/translations/vi/default.json index b5f17ebf..78230752 100644 --- a/public/translations/vi/default.json +++ b/public/translations/vi/default.json @@ -294,5 +294,6 @@ "return_to_settings": "Quay lại Cài đặt", "import_account_backup": "Nhập bản sao lưu tài khoản", "account_auto_generated": "Tài khoản này được tạo tự động bởi 5chan, không cần đăng ký.", - "download_backup": "Tải xuống bản sao lưu" + "download_backup": "Tải xuống bản sao lưu", + "boards_you_moderate_nav": "Các bảng bạn điều hành" } diff --git a/public/translations/zh/default.json b/public/translations/zh/default.json index 8ff2d88a..d94c9ad9 100644 --- a/public/translations/zh/default.json +++ b/public/translations/zh/default.json @@ -294,5 +294,6 @@ "return_to_settings": "返回设置", "import_account_backup": "导入账户备份", "account_auto_generated": "此账户由 5chan 自动生成,无需注册。", - "download_backup": "下载备份" + "download_backup": "下载备份", + "boards_you_moderate_nav": "您管理的板块" } diff --git a/src/views/home/boards-list/boards-list.tsx b/src/views/home/boards-list/boards-list.tsx index 1972cad7..1410a8eb 100644 --- a/src/views/home/boards-list/boards-list.tsx +++ b/src/views/home/boards-list/boards-list.tsx @@ -1,5 +1,6 @@ import { Link, useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; +import useAccountsStore from '@plebbit/plebbit-react-hooks/dist/stores/accounts'; import { useDirectoriesState, useDirectories, DirectoryCommunity } from '../../../hooks/use-directories'; import { getBoardPath } from '../../../lib/utils/route-utils'; import useDisclaimerModalStore from '../../../stores/use-disclaimer-modal-store'; @@ -63,6 +64,15 @@ const BoardsList = ({ multisub }: { multisub: DirectoryCommunity[] }) => { const { useCatalogLinks, boardFilter } = useBoardsFilterStore(); const directories = useDirectories(); + const accountSubplebbitAddresses = useAccountsStore( + (state) => { + const activeAccountId = state.activeAccountId; + const activeAccount = activeAccountId ? state.accounts[activeAccountId] : undefined; + return Object.keys(activeAccount?.subplebbits || {}); + }, + (prev, next) => prev.length === next.length && prev.every((val, idx) => val === next[idx]), + ); + const handleLinkClick = (e: React.MouseEvent, address: string) => { e.preventDefault(); const boardPath = getBoardPath(address, directories); @@ -595,6 +605,11 @@ const BoardsList = ({ multisub }: { multisub: DirectoryCommunity[] }) => {
  • Subscriptions
  • + {accountSubplebbitAddresses.length > 0 && ( +
  • + {t('boards_you_moderate_nav')} +
  • + )}
    )} From 8cef8ce2a0a02e6546c26f37cf78ddd6a7048cda Mon Sep 17 00:00:00 2001 From: plebeius Date: Wed, 25 Feb 2026 16:49:43 +0800 Subject: [PATCH 06/20] fix(popular-threads): adaptive ranking with grow-only stability --- src/hooks/use-popular-posts.ts | 107 ++++++++++-------- src/views/home/box-modal/box-modal.tsx | 99 ++++++---------- .../popular-threads-box.tsx | 42 ++++--- 3 files changed, 117 insertions(+), 131 deletions(-) diff --git a/src/hooks/use-popular-posts.ts b/src/hooks/use-popular-posts.ts index 534e2cf4..63a21902 100644 --- a/src/hooks/use-popular-posts.ts +++ b/src/hooks/use-popular-posts.ts @@ -2,77 +2,92 @@ import { useMemo, useRef } from 'react'; import { Comment, Subplebbit } from '@plebbit/plebbit-react-hooks'; import { getCommentMediaInfo, getHasThumbnail } from '../lib/utils/media-utils'; +const MAX_POSTS = 8; +const MAX_PER_SUB = 3; + /** - * Extracts popular posts from subplebbits. - * Uses memoization to avoid recomputing when only updatingState changes. + * Ranked by replyCount instead of a static threshold so the box + * adapts to both low- and high-activity periods. + * + * Grow-only commit: once a post enters the grid it never shifts or + * disappears — new posts fill remaining slots until the cap is reached. */ const usePopularPosts = (subplebbits: Subplebbit[]) => { - // Track the previous CID list to detect actual content changes vs transient state changes - const prevCidsRef = useRef(''); + const committedRef = useRef<{ posts: Comment[]; cids: Set }>({ + posts: [], + cids: new Set(), + }); + const prevInputKeyRef = useRef(''); + + // Reset committed when the board set changes (e.g. NSFW filter toggle) + const inputKey = subplebbits + .map((s) => s?.address) + .filter(Boolean) + .sort() + .join(','); + if (prevInputKeyRef.current !== inputKey) { + prevInputKeyRef.current = inputKey; + committedRef.current = { posts: [], cids: new Set() }; + } + + const candidates = useMemo(() => { + if (committedRef.current.posts.length >= MAX_POSTS) return []; - const { popularPosts, error } = useMemo(() => { try { - const uniqueLinks: Set = new Set(); + const uniqueLinks = new Set(); const allPosts: Comment[] = []; - // Base quota on boards that currently have loaded hot comments. - // Using total directory count can underfill the list when many boards are empty/unavailable. - const loadedSubplebbitsCount = subplebbits.filter((sub) => sub?.posts?.pages?.hot?.comments).length; - const postsPerSub = [0, 8, 4, 3, 2, 2, 2, 2, 1][Math.min(loadedSubplebbitsCount, 8)]; + for (const sub of subplebbits) { + if (!sub?.posts?.pages?.hot?.comments) continue; - subplebbits.forEach((subplebbit: any) => { - let subplebbitPosts: Comment[] = []; + const subPosts: Comment[] = []; + for (const post of Object.values(sub.posts.pages.hot.comments as Comment)) { + const { deleted, link, linkHeight, linkWidth, locked, pinned, removed, thumbnailUrl } = post; - if (subplebbit?.posts?.pages?.hot?.comments) { - const rawPosts = Object.values(subplebbit.posts.pages.hot.comments as Comment); - for (const post of rawPosts) { - const { deleted, link, linkHeight, linkWidth, locked, pinned, removed, replyCount, thumbnailUrl } = post; + try { + const commentMediaInfo = getCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight); + const hasThumbnail = getHasThumbnail(commentMediaInfo, link); - try { - const commentMediaInfo = getCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight); - const hasThumbnail = getHasThumbnail(commentMediaInfo, link); - - if (hasThumbnail && replyCount > 1 && !deleted && !removed && !locked && !pinned && !uniqueLinks.has(link)) { - subplebbitPosts.push(post); - uniqueLinks.add(link); - } - } catch (err) { - console.error('Error processing post:', err); + if (hasThumbnail && !deleted && !removed && !locked && !pinned && !uniqueLinks.has(link)) { + subPosts.push(post); + uniqueLinks.add(link); } + } catch { + // skip posts with malformed media URLs } - - subplebbitPosts.sort((a: any, b: any) => b.timestamp - a.timestamp); - const selectedPosts = subplebbitPosts.slice(0, postsPerSub); - allPosts.push(...selectedPosts); } + + subPosts.sort((a, b) => (b.replyCount ?? 0) - (a.replyCount ?? 0)); + allPosts.push(...subPosts.slice(0, MAX_PER_SUB)); + } + + // Primary: most replies first. Tiebreaker: newest first. + allPosts.sort((a, b) => { + const diff = (b.replyCount ?? 0) - (a.replyCount ?? 0); + return diff !== 0 ? diff : (b.timestamp ?? 0) - (a.timestamp ?? 0); }); - const sortedPosts = allPosts.sort((a: any, b: any) => b.timestamp - a.timestamp).slice(0, 8); - - return { popularPosts: sortedPosts, error: null }; + return allPosts; } catch (err) { console.error('Error in usePopularPosts:', err); - return { popularPosts: [], error: 'Failed to fetch popular posts' }; + return []; } }, [subplebbits]); - // Create stable reference: only update if the post content actually changes - // Build a key from relevant mutable fields, not just CIDs - const currentKey = popularPosts.map((p) => `${p.cid}:${p.replyCount}:${p.timestamp}:${p.locked}:${p.pinned}`).join(','); - const stablePostsRef = useRef(popularPosts); - const keyChanged = currentKey !== prevCidsRef.current; - - if (keyChanged) { - prevCidsRef.current = currentKey; - stablePostsRef.current = popularPosts; + // Grow-only: committed posts keep their position, new ones fill empty slots + const { posts, cids } = committedRef.current; + for (const post of candidates) { + if (posts.length >= MAX_POSTS) break; + if (post.cid && !cids.has(post.cid)) { + posts.push(post); + cids.add(post.cid); + } } - // Derive loading state from subplebbit states rather than post count - // A subplebbit is still loading if it has no posts pages yet and isn't in a terminal state const hasLoadedData = subplebbits.some((sub) => sub?.posts?.pages?.hot?.comments); const isLoading = subplebbits.length > 0 && !hasLoadedData; - return { popularPosts: stablePostsRef.current, isLoading, error }; + return { popularPosts: posts, isLoading, error: null as string | null }; }; export default usePopularPosts; diff --git a/src/views/home/box-modal/box-modal.tsx b/src/views/home/box-modal/box-modal.tsx index 609770d1..92ad4e10 100644 --- a/src/views/home/box-modal/box-modal.tsx +++ b/src/views/home/box-modal/box-modal.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useRef, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import useHomeFiltersStore from '../../../stores/use-popular-threads-options-store'; import styles from '../home.module.css'; @@ -11,21 +11,37 @@ const BoxModal = () => { const { showWorksafeContentOnly, setShowWorksafeContentOnly, showNsfwContentOnly, setShowNsfwContentOnly } = useHomeFiltersStore(); - const handleClickOutside = useCallback( - (event: MouseEvent) => { + useEffect(() => { + if (!showFilterModal) return; + const handleClickOutside = (event: MouseEvent) => { if (modalRef.current && !modalRef.current.contains(event.target as Node) && buttonRef.current && !buttonRef.current.contains(event.target as Node)) { setShowFilterModal(false); } - }, - [modalRef, buttonRef, setShowFilterModal], - ); - - useEffect(() => { - document.addEventListener('mousedown', handleClickOutside); - return () => { - document.removeEventListener('mousedown', handleClickOutside); }; - }, [handleClickOutside]); + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, [showFilterModal]); + + const selectFilter = (filter: 'worksafe' | 'nsfw' | 'all') => { + if (filter === 'worksafe') { + if (showNsfwContentOnly) setShowNsfwContentOnly(false); + setShowWorksafeContentOnly(!showWorksafeContentOnly); + } else if (filter === 'nsfw') { + if (showWorksafeContentOnly) setShowWorksafeContentOnly(false); + setShowNsfwContentOnly(!showNsfwContentOnly); + } else { + setShowWorksafeContentOnly(false); + setShowNsfwContentOnly(false); + } + setShowFilterModal(false); + }; + + const handleKey = (e: React.KeyboardEvent, filter: 'worksafe' | 'nsfw' | 'all') => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + selectFilter(filter); + } + }; return ( <> @@ -46,70 +62,29 @@ const BoxModal = () => { {showFilterModal && (
    { - if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault(); - if (showNsfwContentOnly) { - setShowNsfwContentOnly(false); - } - setShowWorksafeContentOnly(!showWorksafeContentOnly); - setShowFilterModal(false); - } - }} - onClick={() => { - if (showNsfwContentOnly) { - setShowNsfwContentOnly(false); - } - setShowWorksafeContentOnly(!showWorksafeContentOnly); - setShowFilterModal(false); - }} + onKeyDown={(e) => handleKey(e, 'worksafe')} + onClick={() => selectFilter('worksafe')} > {t('show_worksafe_content_only')}
    { - if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault(); - if (showWorksafeContentOnly) { - setShowWorksafeContentOnly(false); - } - setShowNsfwContentOnly(!showNsfwContentOnly); - setShowFilterModal(false); - } - }} - onClick={() => { - if (showWorksafeContentOnly) { - setShowWorksafeContentOnly(false); - } - setShowNsfwContentOnly(!showNsfwContentOnly); - setShowFilterModal(false); - }} + onKeyDown={(e) => handleKey(e, 'nsfw')} + onClick={() => selectFilter('nsfw')} > {t('show_nsfw_content_only')}
    { - if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault(); - setShowWorksafeContentOnly(false); - setShowNsfwContentOnly(false); - setShowFilterModal(false); - } - }} - onClick={() => { - setShowWorksafeContentOnly(false); - setShowNsfwContentOnly(false); - setShowFilterModal(false); - }} + onKeyDown={(e) => handleKey(e, 'all')} + onClick={() => selectFilter('all')} > {t('show_all_content')}
    diff --git a/src/views/home/popular-threads-box/popular-threads-box.tsx b/src/views/home/popular-threads-box/popular-threads-box.tsx index 0e176a05..100788d2 100644 --- a/src/views/home/popular-threads-box/popular-threads-box.tsx +++ b/src/views/home/popular-threads-box/popular-threads-box.tsx @@ -15,7 +15,8 @@ import { removeMarkdown } from '../../../lib/utils/post-utils'; interface PopularThreadProps { post: Comment; - directories: DirectoryCommunity[]; + boardTitle: string; + boardPath: string; } export const ContentPreview = ({ content, maxLength = 99 }: { content: string; maxLength?: number }) => { @@ -25,17 +26,11 @@ export const ContentPreview = ({ content, maxLength = 99 }: { content: string; m return truncatedText; }; -// Memoize to prevent rerenders when parent rerenders due to updatingState const PopularThreadCard = memo( - ({ post, directories }: PopularThreadProps) => { - const { cid, content, link, linkHeight, linkWidth, subplebbitAddress, thumbnailUrl, title } = post || {}; + ({ post, boardTitle, boardPath }: PopularThreadProps) => { + const { cid, content, link, linkHeight, linkWidth, thumbnailUrl, title } = post || {}; const commentMediaInfo = getCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight); - // Find the matching DirectoryCommunity entry and get its title - const directoriesEntry = directories.find((ms) => ms?.address === subplebbitAddress); - const boardTitle = directoriesEntry?.title?.replace(/^\/[^/]+\/\s*-\s*/, '') || ''; - const boardPath = subplebbitAddress ? getBoardPath(subplebbitAddress, directories) : ''; - return (
    {boardTitle}
    @@ -56,35 +51,31 @@ const PopularThreadCard = memo(
    ); }, - // Custom equality: rerender if post.cid or directories entry title changes - (prevProps, nextProps) => { - if (prevProps.post?.cid !== nextProps.post?.cid) return false; - // Compare the relevant directories entry for this post's subplebbitAddress - const prevEntry = prevProps.directories.find((ms) => ms?.address === prevProps.post?.subplebbitAddress); - const nextEntry = nextProps.directories.find((ms) => ms?.address === nextProps.post?.subplebbitAddress); - return prevEntry?.title === nextEntry?.title; - }, + (prevProps, nextProps) => prevProps.post?.cid === nextProps.post?.cid && prevProps.boardTitle === nextProps.boardTitle, ); const PopularThreadsBox = ({ directories, subplebbits }: { directories: DirectoryCommunity[]; subplebbits: any }) => { const { t } = useTranslation(); const { showWorksafeContentOnly, showNsfwContentOnly } = usePopularThreadsOptionsStore(); + const directoryByAddress = useMemo(() => new Map(directories.map((d) => [d.address, d])), [directories]); + const filteredSubplebbits = useMemo(() => { if (showWorksafeContentOnly) { return subplebbits.filter((sub: Subplebbit) => { - const directoriesEntry = directories.find((ms) => ms?.address === sub?.address); - return directoriesEntry ? !directoriesEntry.nsfw : true; + const entry = directoryByAddress.get(sub?.address); + return entry ? !entry.nsfw : true; }); } if (showNsfwContentOnly) { return subplebbits.filter((sub: Subplebbit) => { - const directoriesEntry = directories.find((ms) => ms?.address === sub?.address); - return directoriesEntry ? directoriesEntry.nsfw : false; + const entry = directoryByAddress.get(sub?.address); + return entry ? entry.nsfw : false; }); } return subplebbits; - }, [subplebbits, showWorksafeContentOnly, showNsfwContentOnly, directories]); + }, [subplebbits, showWorksafeContentOnly, showNsfwContentOnly, directoryByAddress]); + const { popularPosts } = usePopularPosts(filteredSubplebbits); const isLoading = popularPosts.length === 0; @@ -98,7 +89,12 @@ const PopularThreadsBox = ({ directories, subplebbits }: { directories: Director {isLoading ? ( ) : ( - popularPosts.map((post: any) => ) + popularPosts.map((post: Comment) => { + const entry = directoryByAddress.get(post.subplebbitAddress); + const boardTitle = entry?.title?.replace(/^\/[^/]+\/\s*-\s*/, '') || ''; + const boardPath = post.subplebbitAddress ? getBoardPath(post.subplebbitAddress, directories) : ''; + return ; + }) )}
    From d1bac529b133209f2a66bccf982696f592e949e2 Mon Sep 17 00:00:00 2001 From: plebeius Date: Wed, 25 Feb 2026 17:07:39 +0800 Subject: [PATCH 07/20] chore: improve translations --- public/translations/cs/default.json | 2 +- public/translations/da/default.json | 2 +- public/translations/ko/default.json | 2 +- public/translations/ro/default.json | 2 +- public/translations/th/default.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/public/translations/cs/default.json b/public/translations/cs/default.json index 89a6a315..40541711 100644 --- a/public/translations/cs/default.json +++ b/public/translations/cs/default.json @@ -295,5 +295,5 @@ "import_account_backup": "Importovat zálohu účtu", "account_auto_generated": "Tento účet byl automaticky vygenerován 5chanem, registrace není nutná.", "download_backup": "Stáhnout zálohu", - "boards_you_moderate_nav": "Radce, které moderujete" + "boards_you_moderate_nav": "Fóra, která moderujete" } diff --git a/public/translations/da/default.json b/public/translations/da/default.json index a309a843..82c85288 100644 --- a/public/translations/da/default.json +++ b/public/translations/da/default.json @@ -295,5 +295,5 @@ "import_account_backup": "Importer kontobackup", "account_auto_generated": "Denne konto blev automatisk oprettet af 5chan, ingen tilmelding påkrævet.", "download_backup": "Download sikkerhedskopi", - "boards_you_moderate_nav": "Boards du modererer" + "boards_you_moderate_nav": "Dine boards" } diff --git a/public/translations/ko/default.json b/public/translations/ko/default.json index f0666877..ee1a4740 100644 --- a/public/translations/ko/default.json +++ b/public/translations/ko/default.json @@ -295,5 +295,5 @@ "import_account_backup": "계정 백업 가져오기", "account_auto_generated": "이 계정은 5chan에서 자동으로 생성되었으며, 가입이 필요하지 않습니다.", "download_backup": "백업 다운로드", - "boards_you_moderate_nav": "사용자가 관리하는 게시판" + "boards_you_moderate_nav": "당신이 관리하는 게시판" } diff --git a/public/translations/ro/default.json b/public/translations/ro/default.json index 5afe22fc..a66a68d2 100644 --- a/public/translations/ro/default.json +++ b/public/translations/ro/default.json @@ -295,5 +295,5 @@ "import_account_backup": "Importă backup cont", "account_auto_generated": "Acest cont a fost generat automat de 5chan, nu este nevoie de înregistrare.", "download_backup": "Descarcă backup", - "boards_you_moderate_nav": "Forumuri pe care le moderăm" + "boards_you_moderate_nav": "Forumuri pe care le moderezi" } diff --git a/public/translations/th/default.json b/public/translations/th/default.json index f465645b..abe9e0fd 100644 --- a/public/translations/th/default.json +++ b/public/translations/th/default.json @@ -295,5 +295,5 @@ "import_account_backup": "นำเข้ารายการสำรองของบัญชี", "account_auto_generated": "บัญชีนี้ถูกสร้างโดยอัตโนมัติโดย 5chan ไม่ต้องสมัครสมาชิก", "download_backup": "ดาวน์โหลดสำรอง", - "boards_you_moderate_nav": "บอร์ดที่คุณดูแล" + "boards_you_moderate_nav": "บอร์ด" } From 7a516cfbf94a86104363e29ba429acbbbcd810b3 Mon Sep 17 00:00:00 2001 From: plebeius Date: Wed, 25 Feb 2026 17:11:50 +0800 Subject: [PATCH 08/20] fix(popular-threads): pass linkWidth/linkHeight to CatalogPostMedia for correct aspect ratio --- src/views/home/popular-threads-box/popular-threads-box.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/home/popular-threads-box/popular-threads-box.tsx b/src/views/home/popular-threads-box/popular-threads-box.tsx index 100788d2..7cb215ff 100644 --- a/src/views/home/popular-threads-box/popular-threads-box.tsx +++ b/src/views/home/popular-threads-box/popular-threads-box.tsx @@ -36,7 +36,7 @@ const PopularThreadCard = memo(
    {boardTitle}
    - +
    From 63915f38c77e694c76f8e6f54666e8ec3fe463b1 Mon Sep 17 00:00:00 2001 From: plebeius Date: Wed, 25 Feb 2026 17:38:44 +0800 Subject: [PATCH 09/20] chore: improve translations --- public/translations/da/default.json | 2 +- public/translations/he/default.json | 2 +- public/translations/th/default.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/translations/da/default.json b/public/translations/da/default.json index 82c85288..a309a843 100644 --- a/public/translations/da/default.json +++ b/public/translations/da/default.json @@ -295,5 +295,5 @@ "import_account_backup": "Importer kontobackup", "account_auto_generated": "Denne konto blev automatisk oprettet af 5chan, ingen tilmelding påkrævet.", "download_backup": "Download sikkerhedskopi", - "boards_you_moderate_nav": "Dine boards" + "boards_you_moderate_nav": "Boards du modererer" } diff --git a/public/translations/he/default.json b/public/translations/he/default.json index 4aaf93cf..c904c9a2 100644 --- a/public/translations/he/default.json +++ b/public/translations/he/default.json @@ -295,5 +295,5 @@ "import_account_backup": "ייבוא גיבוי חשבון", "account_auto_generated": "חשבון זה נוצר אוטומטית על ידי 5chan, אין צורך בהרשמה.", "download_backup": "הורד גיבוי", - "boards_you_moderate_nav": "לוחות שאתה מנהל" + "boards_you_moderate_nav": "לוחות שאתה מפקח עליהם" } diff --git a/public/translations/th/default.json b/public/translations/th/default.json index abe9e0fd..f465645b 100644 --- a/public/translations/th/default.json +++ b/public/translations/th/default.json @@ -295,5 +295,5 @@ "import_account_backup": "นำเข้ารายการสำรองของบัญชี", "account_auto_generated": "บัญชีนี้ถูกสร้างโดยอัตโนมัติโดย 5chan ไม่ต้องสมัครสมาชิก", "download_backup": "ดาวน์โหลดสำรอง", - "boards_you_moderate_nav": "บอร์ด" + "boards_you_moderate_nav": "บอร์ดที่คุณดูแล" } From 13dba007789801797fbbcef8e919341e5401661f Mon Sep 17 00:00:00 2001 From: plebeius Date: Wed, 25 Feb 2026 17:39:07 +0800 Subject: [PATCH 10/20] fix(catalog): cap single-board catalog to maxGuiPages to prevent page 11+ posts --- src/views/catalog/catalog.tsx | 35 ++++++----------------------------- 1 file changed, 6 insertions(+), 29 deletions(-) diff --git a/src/views/catalog/catalog.tsx b/src/views/catalog/catalog.tsx index 9aacca7b..ca429838 100644 --- a/src/views/catalog/catalog.tsx +++ b/src/views/catalog/catalog.tsx @@ -12,7 +12,6 @@ import { useFeedStateString } from '../../hooks/use-state-string'; import useWindowWidth from '../../hooks/use-window-width'; import useCatalogStyleStore from '../../stores/use-catalog-style-store'; import useFeedResetStore from '../../stores/use-feed-reset-store'; -import useFeedViewSettingsStore from '../../stores/use-feed-view-settings-store'; import useSortingStore from '../../stores/use-sorting-store'; import useCatalogFiltersStore from '../../stores/use-catalog-filters-store'; import { getSubplebbitAddress, isDirectoryBoard, normalizeMultiboardFeedPath } from '../../lib/utils/route-utils'; @@ -204,9 +203,9 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp, const isInSubscriptionsView = viewType ? viewType === 'subs' : false; const isInModView = viewType ? viewType === 'mod' : false; - const enableInfiniteScroll = useFeedViewSettingsStore((state) => state.enableInfiniteScroll); - const isForcedInfiniteScroll = isInAllView || isInSubscriptionsView || isInModView; - const effectiveInfiniteScroll = enableInfiniteScroll || isForcedInfiniteScroll; + const isMultiboard = isInAllView || isInSubscriptionsView || isInModView; + // Single-board catalogs always cap at maxGuiPages (no infinite scroll beyond the board's page limit) + const effectiveInfiniteScroll = isMultiboard; const directories = useDirectories(); const resolvedAddressFromUrl = useResolvedSubplebbitAddress(); @@ -241,7 +240,7 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp, const postsPerPage = columnCount <= 2 ? 10 : columnCount === 3 ? 15 : columnCount === 4 ? 20 : 25; const community = useDirectoryByAddress(isInAllView || isInSubscriptionsView || isInModView ? undefined : subplebbitAddress); - const { guiPostsPerPage: boardPostsPerPage, maxGuiPages, paginationFeedPostsPerPage, infiniteFeedPostsPerPage } = useBoardFeedPageSize(community); + const { guiPostsPerPage: boardPostsPerPage, maxGuiPages, paginationFeedPostsPerPage } = useBoardFeedPageSize(community); // Canonical redirect for multiboard catalog paths with numeric page segment (e.g. /all/catalog/1w/5 -> /all/catalog/1w) useEffect(() => { @@ -269,35 +268,13 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp, }, [subplebbitAddress]); const feedOptions = useMemo(() => { - const catalogPostsPerPage = - isInAllView || isInSubscriptionsView || isInModView - ? effectiveInfiniteScroll - ? 10 - : 100 - : effectiveInfiniteScroll - ? infiniteFeedPostsPerPage - : paginationFeedPostsPerPage; - return { subplebbitAddresses, sortType: feedSortType, - postsPerPage: catalogPostsPerPage, + postsPerPage: isMultiboard ? 10 : paginationFeedPostsPerPage, filter: createCombinedFilter(filterItems, searchText, subplebbitAddress || 'all', handleFilterMatch), }; - }, [ - subplebbitAddresses, - feedSortType, - isInAllView, - isInSubscriptionsView, - isInModView, - effectiveInfiniteScroll, - infiniteFeedPostsPerPage, - paginationFeedPostsPerPage, - filterItems, - searchText, - subplebbitAddress, - handleFilterMatch, - ]); + }, [subplebbitAddresses, feedSortType, isMultiboard, paginationFeedPostsPerPage, filterItems, searchText, subplebbitAddress, handleFilterMatch]); const { feed, hasMore, loadMore, reset } = useFeed(feedOptions); const { accountComments } = useAccountComments(); From 1dfc75998d8ba6f2a80d3cd51de6c03761daffe9 Mon Sep 17 00:00:00 2001 From: plebeius Date: Wed, 25 Feb 2026 17:48:23 +0800 Subject: [PATCH 11/20] update board-pagination styling --- .../board-pagination/board-pagination.module.css | 7 +++++++ src/components/board-pagination/board-pagination.tsx | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/components/board-pagination/board-pagination.module.css b/src/components/board-pagination/board-pagination.module.css index c1b35e10..7d45ca25 100644 --- a/src/components/board-pagination/board-pagination.module.css +++ b/src/components/board-pagination/board-pagination.module.css @@ -119,6 +119,13 @@ text-decoration: var(--boardsbar-desktop-link-text-decoration-hover); } +.pagelistSeparatorLink { + composes: footerPageLink; + border-left: 1px solid var(--pagelist-item-border-color); + padding-left: 12px; + margin-left: 7px; +} + .footerPageCurrent { font-weight: bold; color: var(--boardsbar-desktop-link-text-color-hover); diff --git a/src/components/board-pagination/board-pagination.tsx b/src/components/board-pagination/board-pagination.tsx index 8224d7ef..d62730ce 100644 --- a/src/components/board-pagination/board-pagination.tsx +++ b/src/components/board-pagination/board-pagination.tsx @@ -85,10 +85,10 @@ const BoardPagination = ({ basePath, currentPage, totalPages, footerStyle = fals ) : ( {t('next')} )} - + {t('catalog')} - + {t('archive')}
    From 2c31120a7d5da9928f67c0ac93f8e13676456fc5 Mon Sep 17 00:00:00 2001 From: plebeius Date: Wed, 25 Feb 2026 17:58:03 +0800 Subject: [PATCH 12/20] style(board-pagination): complete styling --- .../board-pagination.module.css | 14 +++++----- .../board-pagination/board-pagination.tsx | 26 +++++++------------ 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/src/components/board-pagination/board-pagination.module.css b/src/components/board-pagination/board-pagination.module.css index 7d45ca25..625a8a0f 100644 --- a/src/components/board-pagination/board-pagination.module.css +++ b/src/components/board-pagination/board-pagination.module.css @@ -46,6 +46,8 @@ overflow: hidden; list-style: none; background-color: var(--pagelist-background-color); + border-right: 1px solid var(--pagelist-item-border-color); + border-bottom: 1px solid var(--pagelist-item-border-color); } .footerPageItem { @@ -97,7 +99,7 @@ background: transparent; list-style: none; overflow: hidden; - color: var(--boardsbar-desktop-link-text-color); + color: var(--button-desktop-text-color); } .footerPageLink { @@ -108,15 +110,14 @@ padding: 0; border: none; background: transparent; - color: var(--boardsbar-desktop-link-text-color); + color: var(--button-desktop-text-color); text-transform: capitalize; cursor: pointer; - text-decoration: var(--boardsbar-desktop-link-text-decoration); + text-decoration: var(--button-text-decoration); } .footerPageLink:hover { - color: var(--boardsbar-desktop-link-text-color-hover); - text-decoration: var(--boardsbar-desktop-link-text-decoration-hover); + color: var(--button-desktop-text-color-hover); } .pagelistSeparatorLink { @@ -127,7 +128,6 @@ } .footerPageCurrent { + composes: footerPageLink; font-weight: bold; - color: var(--boardsbar-desktop-link-text-color-hover); - text-decoration: var(--boardsbar-desktop-link-text-decoration); } diff --git a/src/components/board-pagination/board-pagination.tsx b/src/components/board-pagination/board-pagination.tsx index d62730ce..25ce137b 100644 --- a/src/components/board-pagination/board-pagination.tsx +++ b/src/components/board-pagination/board-pagination.tsx @@ -61,23 +61,15 @@ const BoardPagination = ({ basePath, currentPage, totalPages, footerStyle = fals ] )} - {pageNumbers.map((page) => - page === currentPage ? ( - - [ - {page} - ] - - ) : ( - - [ - - {page} - - ] - - ), - )} + {pageNumbers.map((page) => ( + + [ + + {page} + + ] + + ))} {currentPage < totalPages ? (
    From e12743f7832f141f3875166ff03827c084954105 Mon Sep 17 00:00:00 2001 From: plebeius Date: Wed, 25 Feb 2026 19:10:47 +0800 Subject: [PATCH 16/20] fix(account-data-editor): resolve Ace editor loading failure Load react-ace before mode/theme to avoid 'ace is not defined' race. Unwrap Vite CJS double-default export for correct component reference. Use save_changes translation key for Save button. --- src/views/account-data-editor/account-data-editor.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/views/account-data-editor/account-data-editor.tsx b/src/views/account-data-editor/account-data-editor.tsx index 8f61b806..b753b90d 100644 --- a/src/views/account-data-editor/account-data-editor.tsx +++ b/src/views/account-data-editor/account-data-editor.tsx @@ -8,8 +8,11 @@ import styles from './account-data-editor.module.css'; const DEFAULT_RETURN_TO = '/subs/settings#account-settings'; const loadAce = async () => { - const [aceModule] = await Promise.all([import('react-ace'), import('ace-builds/src-noconflict/mode-json'), import('ace-builds/src-noconflict/theme-monokai')]); - return aceModule.default; + const aceModule = await import('react-ace'); + await Promise.all([import('ace-builds/src-noconflict/mode-json'), import('ace-builds/src-noconflict/theme-monokai')]); + // Vite CJS interop can double-wrap the default export + const mod = aceModule.default; + return typeof mod === 'function' ? mod : (mod as unknown as { default: typeof mod }).default; }; const AccountDataEditor = () => { @@ -103,7 +106,7 @@ const AccountDataEditor = () => {
    )} - post?.cid || `post-${index}`} - itemContent={(index, post) => } - useWindowScroll={true} - components={footerComponents} - endReached={effectiveInfiniteScroll && hasMore ? loadMore : undefined} - ref={virtuosoRef} - restoreStateFrom={lastVirtuosoState} - initialScrollTop={lastVirtuosoState?.scrollTop} - /> + {effectiveInfiniteScroll ? ( + post?.cid || `post-${index}`} + itemContent={(index, post) => } + useWindowScroll={true} + components={footerComponents} + endReached={hasMore ? loadMore : undefined} + ref={virtuosoRef} + restoreStateFrom={lastVirtuosoState} + initialScrollTop={lastVirtuosoState?.scrollTop} + /> + ) : ( + <> + {displayFeed.map((post, index) => ( + + ))} + + + )} ); From 2dfd0b4603550ed87d6332c692d43918e95c1bf1 Mon Sep 17 00:00:00 2001 From: plebeius Date: Wed, 25 Feb 2026 19:30:10 +0800 Subject: [PATCH 19/20] chore: improve translations --- public/translations/ar/default.json | 2 +- public/translations/fi/default.json | 2 +- public/translations/fr/default.json | 2 +- public/translations/pt/default.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/translations/ar/default.json b/public/translations/ar/default.json index 266d2ea4..1bf818c2 100644 --- a/public/translations/ar/default.json +++ b/public/translations/ar/default.json @@ -295,5 +295,5 @@ "import_account_backup": "استيراد نسخة احتياطية للحساب", "account_auto_generated": "تم إنشاء هذا الحساب تلقائياً بواسطة 5chan، دون الحاجة للتسجيل.", "download_backup": "تحميل النسخة الاحتياطية", - "boards_you_moderate_nav": "لوحات تعمل كمشرف عليها" + "boards_you_moderate_nav": "المنتديات التي تقوم بالإشراف عليها" } diff --git a/public/translations/fi/default.json b/public/translations/fi/default.json index 020cde2f..1af4d63d 100644 --- a/public/translations/fi/default.json +++ b/public/translations/fi/default.json @@ -209,7 +209,7 @@ "help": "Apua", "filter_and_highlights_help": "Suodattimet ja korostukset -apu", "filter_and_highlights": "Suodattimet ja korostukset", - "boards_you_moderate": "lautakunnat, joita valvot", + "boards_you_moderate": "Laudat, joita moderoit", "moderator_of_this_board": "Tämä käyttäjä on tämän taulun moderaattori", "administrator_of_this_board": "Tämä käyttäjä on tämän taulun järjestelmänvalvoja", "copy_content_id": "kopioi sisältö-ID", diff --git a/public/translations/fr/default.json b/public/translations/fr/default.json index 9363bbdb..6a17ff1c 100644 --- a/public/translations/fr/default.json +++ b/public/translations/fr/default.json @@ -209,7 +209,7 @@ "help": "Aide", "filter_and_highlights_help": "Aide sur les filtres et surlignages", "filter_and_highlights": "Filtres et surlignages", - "boards_you_moderate": "boards que vous modérez", + "boards_you_moderate": "Tableaux que vous modérez", "moderator_of_this_board": "Cet utilisateur est modérateur de ce tableau", "administrator_of_this_board": "Cet utilisateur est administrateur de ce tableau", "copy_content_id": "Copier l'ID du contenu", diff --git a/public/translations/pt/default.json b/public/translations/pt/default.json index 3000d2f7..bfb2a287 100644 --- a/public/translations/pt/default.json +++ b/public/translations/pt/default.json @@ -209,7 +209,7 @@ "help": "Ajuda", "filter_and_highlights_help": "Ajuda de Filtros e Realces", "filter_and_highlights": "Filtros e Destaques", - "boards_you_moderate": "boards que você modera", + "boards_you_moderate": "Quadros que você modera", "moderator_of_this_board": "Este usuário é moderador deste quadro", "administrator_of_this_board": "Este usuário é administrador deste quadro", "copy_content_id": "Copiar ID do conteúdo", From e57f94c614118938facaf73825ead1b366880073 Mon Sep 17 00:00:00 2001 From: plebeius Date: Wed, 25 Feb 2026 19:33:30 +0800 Subject: [PATCH 20/20] Update reply-quote-preview.tsx --- src/components/reply-quote-preview/reply-quote-preview.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/reply-quote-preview/reply-quote-preview.tsx b/src/components/reply-quote-preview/reply-quote-preview.tsx index 4cec3b34..19932b00 100644 --- a/src/components/reply-quote-preview/reply-quote-preview.tsx +++ b/src/components/reply-quote-preview/reply-quote-preview.tsx @@ -74,9 +74,9 @@ const scrollToThreadCardTop = (threadCid: string) => { const scrollToReplyOnPage = (cid: string) => { const el = document.querySelector(`[data-cid="${cid}"][data-post-cid]`); if (!el) return false; - el.scrollIntoView({ behavior: 'smooth', block: 'center' }); + document.querySelectorAll('.scroll-highlight').forEach((prev) => prev.classList.remove('scroll-highlight')); + el.scrollIntoView({ behavior: 'auto', block: 'center' }); el.classList.add('scroll-highlight'); - setTimeout(() => el.classList.remove('scroll-highlight'), 2000); return true; };