mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
+1
-1
@@ -7,7 +7,7 @@
|
||||
"dependencies": {
|
||||
"@capacitor/app": "1.1.1",
|
||||
"@floating-ui/react": "0.26.1",
|
||||
"@plebbit/plebbit-react-hooks": "https://github.com/plebbit/plebbit-react-hooks.git#d7fa27920f8ec21c9fa08bc216054083e1d59fc0",
|
||||
"@plebbit/plebbit-react-hooks": "https://github.com/plebbit/plebbit-react-hooks.git#f0ae8cd285f282d82eafd290e3b73525faa8ec6b",
|
||||
"@testing-library/jest-dom": "5.14.1",
|
||||
"@testing-library/react": "13.0.0",
|
||||
"@testing-library/user-event": "13.2.1",
|
||||
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "منذ {{count}} شهور",
|
||||
"time_1_year_ago": "منذ عام واحد",
|
||||
"time_x_years_ago": "منذ {{count}} سنوات",
|
||||
"open": "فتح"
|
||||
"open": "فتح",
|
||||
"bump_order": "ترتيب الدفع",
|
||||
"creation_date": "تاريخ الإنشاء",
|
||||
"sort_by": "ترتيب حسب",
|
||||
"time_filter": "تصفية الوقت",
|
||||
"subplebbit_offline_info": "قد يكون الرد على الرسائل غير متاح حالياً وقد تفشل النشر.",
|
||||
"posts_last_synced_info": "آخر تزامن الرسائل {{time}}، قد يكون الرد على الرسائل غير متاح حالياً وقد تفشل النشر."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}} মাস আগে",
|
||||
"time_1_year_ago": "১ বছর আগে",
|
||||
"time_x_years_ago": "{{count}} বছর আগে",
|
||||
"open": "খুলুন"
|
||||
"open": "খুলুন",
|
||||
"bump_order": "বাম অর্ডার",
|
||||
"creation_date": "তৈরি তারিখ",
|
||||
"sort_by": "দ্বারা বাছাই করুন",
|
||||
"time_filter": "সময় ফিল্টার",
|
||||
"subplebbit_offline_info": "সাবপ্লেবিট অফলাইন হতে পারে এবং প্রকাশনা ব্যর্থ হতে পারে।",
|
||||
"posts_last_synced_info": "পোস্টগুলি সর্বশেষ সিঙ্ক হয়েছে {{time}}, সাবপ্লেবিট অফলাইন হতে পারে এবং প্রকাশনা ব্যর্থ হতে পারে।"
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "Před {{count}} měsíci",
|
||||
"time_1_year_ago": "Před 1 rokem",
|
||||
"time_x_years_ago": "Před {{count}} roky",
|
||||
"open": "otevřít"
|
||||
"open": "otevřít",
|
||||
"bump_order": "Pořadí bump",
|
||||
"creation_date": "Datum vytvoření",
|
||||
"sort_by": "Řadit podle",
|
||||
"time_filter": "Filtr času",
|
||||
"subplebbit_offline_info": "Subplebbit může být offline a zveřejnění může selhat.",
|
||||
"posts_last_synced_info": "Poslední synchronizace příspěvků {{time}}, subplebbit může být offline a zveřejňování může selhat."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}} måneder siden",
|
||||
"time_1_year_ago": "1 år siden",
|
||||
"time_x_years_ago": "{{count}} år siden",
|
||||
"open": "åben"
|
||||
"open": "åben",
|
||||
"bump_order": "Bump rækkefølge",
|
||||
"creation_date": "Oprettelsesdato",
|
||||
"sort_by": "Sorter efter",
|
||||
"time_filter": "Tidsfilter",
|
||||
"subplebbit_offline_info": "Subplebbiten kan være offline, og offentliggørelse kan mislykkes.",
|
||||
"posts_last_synced_info": "Seneste synkroniserede indlæg {{time}}, subplebbiten kan være offline, og offentliggørelse kan mislykkes."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "vor {{count}} Monaten",
|
||||
"time_1_year_ago": "vor 1 Jahr",
|
||||
"time_x_years_ago": "vor {{count}} Jahren",
|
||||
"open": "öffnen"
|
||||
"open": "öffnen",
|
||||
"bump_order": "Bump-Reihenfolge",
|
||||
"creation_date": "Erstellungsdatum",
|
||||
"sort_by": "Sortieren nach",
|
||||
"time_filter": "Zeitfilter",
|
||||
"subplebbit_offline_info": "Der Subplebbit könnte offline sein und das Veröffentlichen könnte fehlschlagen.",
|
||||
"posts_last_synced_info": "Beiträge zuletzt synchronisiert {{time}}, der Subplebbit könnte offline sein und das Veröffentlichen könnte fehlschlagen."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "Πριν από {{count}} μήνες",
|
||||
"time_1_year_ago": "Πριν από 1 έτος",
|
||||
"time_x_years_ago": "Πριν από {{count}} έτη",
|
||||
"open": "ανοικτό"
|
||||
"open": "ανοικτό",
|
||||
"bump_order": "Τάξη Bump",
|
||||
"creation_date": "Ημερομηνία δημιουργίας",
|
||||
"sort_by": "Ταξινόμηση κατά",
|
||||
"time_filter": "Φίλτρο χρόνου",
|
||||
"subplebbit_offline_info": "Το subplebbit ενδέχεται να είναι εκτός σύνδεσης και η δημοσίευση μπορεί να αποτύχει.",
|
||||
"posts_last_synced_info": "Τελευταία συγχρονισμένες αναρτήσεις {{time}}, το subplebbit ενδέχεται να είναι εκτός σύνδεσης και η δημοσίευση μπορεί να αποτύχει."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}} months ago",
|
||||
"time_1_year_ago": "1 year ago",
|
||||
"time_x_years_ago": "{{count}} years ago",
|
||||
"open": "open"
|
||||
"open": "open",
|
||||
"bump_order": "Bump order",
|
||||
"creation_date": "Creation date",
|
||||
"sort_by": "Sort by",
|
||||
"time_filter": "Time Filter",
|
||||
"subplebbit_offline_info": "The subplebbit might be offline and publishing might fail.",
|
||||
"posts_last_synced_info": "Posts last synced {{time}}, the subplebbit might be offline and publishing might fail."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "hace {{count}} meses",
|
||||
"time_1_year_ago": "hace 1 año",
|
||||
"time_x_years_ago": "hace {{count}} años",
|
||||
"open": "abrir"
|
||||
"open": "abrir",
|
||||
"bump_order": "Orden de Bump",
|
||||
"creation_date": "Fecha de creación",
|
||||
"sort_by": "Ordenar por",
|
||||
"time_filter": "Filtro de tiempo",
|
||||
"subplebbit_offline_info": "El subplebbit podría estar fuera de línea y la publicación podría fallar.",
|
||||
"posts_last_synced_info": "Últimas publicaciones sincronizadas {{time}}, el subplebbit podría estar fuera de línea y la publicación podría fallar."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}} ماه پیش",
|
||||
"time_1_year_ago": "1 سال پیش",
|
||||
"time_x_years_ago": "{{count}} سال پیش",
|
||||
"open": "باز کردن"
|
||||
"open": "باز کردن",
|
||||
"bump_order": "سفارش بامپ",
|
||||
"creation_date": "تاریخ ایجاد",
|
||||
"sort_by": "مرتبسازی بر اساس",
|
||||
"time_filter": "فیلتر زمانی",
|
||||
"subplebbit_offline_info": "زیرپلبیت ممکن است آفلاین باشد و انتشار ممکن است شکست خورده باشد.",
|
||||
"posts_last_synced_info": "آخرین پستها همگامسازی شدهاند {{time}}، زیرپلبیت ممکن است آفلاین باشد و انتشار ممکن است شکست خورده باشد."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}} kuukautta sitten",
|
||||
"time_1_year_ago": "1 vuosi sitten",
|
||||
"time_x_years_ago": "{{count}} vuotta sitten",
|
||||
"open": "avata"
|
||||
"open": "avata",
|
||||
"bump_order": "Bump-järjestys",
|
||||
"creation_date": "Luomispäivämäärä",
|
||||
"sort_by": "Järjestä",
|
||||
"time_filter": "Aikasuodatin",
|
||||
"subplebbit_offline_info": "Subplebbit voi olla offline-tilassa, ja julkaisu voi epäonnistua.",
|
||||
"posts_last_synced_info": "Viimeksi synkronoidut viestit {{time}}, subplebbit voi olla offline-tilassa, ja julkaisu voi epäonnistua."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}} buwan ang nakalipas",
|
||||
"time_1_year_ago": "1 taon ang nakalipas",
|
||||
"time_x_years_ago": "{{count}} taon ang nakalipas",
|
||||
"open": "buksan"
|
||||
"open": "buksan",
|
||||
"bump_order": "Bump order",
|
||||
"creation_date": "Petsa ng Paglikha",
|
||||
"sort_by": "Ayusin ayon sa",
|
||||
"time_filter": "Time Filter",
|
||||
"subplebbit_offline_info": "Maaaring offline ang subplebbit at maaaring mabigo ang paglalathala.",
|
||||
"posts_last_synced_info": "Huling naka-sync na mga post {{time}}, maaaring offline ang subplebbit at maaaring mabigo ang paglalathala."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "il y a {{count}} mois",
|
||||
"time_1_year_ago": "il y a 1 an",
|
||||
"time_x_years_ago": "il y a {{count}} ans",
|
||||
"open": "ouvrir"
|
||||
"open": "ouvrir",
|
||||
"bump_order": "Ordre de Bump",
|
||||
"creation_date": "Date de création",
|
||||
"sort_by": "Trier par",
|
||||
"time_filter": "Filtre temporel",
|
||||
"subplebbit_offline_info": "Le subplebbit pourrait être hors ligne et la publication pourrait échouer.",
|
||||
"posts_last_synced_info": "Dernières publications synchronisées {{time}}, le subplebbit pourrait être hors ligne et la publication pourrait échouer."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "לפני {{count}} חודשים",
|
||||
"time_1_year_ago": "לפני שנה",
|
||||
"time_x_years_ago": "לפני {{count}} שנים",
|
||||
"open": "לפתוח"
|
||||
"open": "לפתוח",
|
||||
"bump_order": "סדר הפעם",
|
||||
"creation_date": "תאריך יצירה",
|
||||
"sort_by": "מיין לפי",
|
||||
"time_filter": "סינון זמן",
|
||||
"subplebbit_offline_info": "הסאבפלביט עשוי להיות לא מקוון והפרסום עשוי להכשל.",
|
||||
"posts_last_synced_info": "הפוסטים סונכרנו לאחרונה {{time}}, הסאבפלביט עשוי להיות לא מקוון והפרסום עשוי להכשל."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}} महीने पहले",
|
||||
"time_1_year_ago": "1 साल पहले",
|
||||
"time_x_years_ago": "{{count}} साल पहले",
|
||||
"open": "खोलें"
|
||||
"open": "खोलें",
|
||||
"bump_order": "बंप आदेश",
|
||||
"creation_date": "निर्माण तिथि",
|
||||
"sort_by": "क्रमबद्ध करें द्वारा",
|
||||
"time_filter": "समय फ़िल्टर",
|
||||
"subplebbit_offline_info": "सबप्लेबिट ऑफलाइन हो सकता है और प्रकाशन विफल हो सकता है।",
|
||||
"posts_last_synced_info": "आखिरी बार सिंक की गई पोस्ट {{time}}, सबप्लेबिट ऑफलाइन हो सकता है और प्रकाशन विफल हो सकता है।"
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}} hónapja",
|
||||
"time_1_year_ago": "1 éve",
|
||||
"time_x_years_ago": "{{count}} éve",
|
||||
"open": "nyit"
|
||||
"open": "nyit",
|
||||
"bump_order": "Bump sorrend",
|
||||
"creation_date": "Létrehozás dátuma",
|
||||
"sort_by": "Rendezés",
|
||||
"time_filter": "Idő szűrő",
|
||||
"subplebbit_offline_info": "A subplebbit offline lehet, és a közzététel sikertelen lehet.",
|
||||
"posts_last_synced_info": "Legutóbb szinkronizált bejegyzések {{time}}, a subplebbit offline lehet, és a közzététel sikertelen lehet."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}} bulan yang lalu",
|
||||
"time_1_year_ago": "1 tahun yang lalu",
|
||||
"time_x_years_ago": "{{count}} tahun yang lalu",
|
||||
"open": "buka"
|
||||
"open": "buka",
|
||||
"bump_order": "Urutan Bump",
|
||||
"creation_date": "Tanggal pembuatan",
|
||||
"sort_by": "Urutkan berdasarkan",
|
||||
"time_filter": "Filter Waktu",
|
||||
"subplebbit_offline_info": "Subplebbit mungkin offline dan penerbitan mungkin gagal.",
|
||||
"posts_last_synced_info": "Posting terakhir disinkronkan {{time}}, subplebbit mungkin offline dan penerbitan mungkin gagal."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}} mesi fa",
|
||||
"time_1_year_ago": "1 anno fa",
|
||||
"time_x_years_ago": "{{count}} anni fa",
|
||||
"open": "apri"
|
||||
"open": "apri",
|
||||
"bump_order": "Ordine di Bump",
|
||||
"creation_date": "Data di creazione",
|
||||
"sort_by": "Ordina per",
|
||||
"time_filter": "Filtro temporale",
|
||||
"subplebbit_offline_info": "Il subplebbit potrebbe essere offline e la pubblicazione potrebbe fallire.",
|
||||
"posts_last_synced_info": "Ultimi post sincronizzati {{time}}, il subplebbit potrebbe essere offline e la pubblicazione potrebbe fallire."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}}ヶ月前",
|
||||
"time_1_year_ago": "1年前",
|
||||
"time_x_years_ago": "{{count}}年前",
|
||||
"open": "開く"
|
||||
"open": "開く",
|
||||
"bump_order": "バンプ順",
|
||||
"creation_date": "作成日",
|
||||
"sort_by": "並べ替え",
|
||||
"time_filter": "時間フィルター",
|
||||
"subplebbit_offline_info": "サブプレビットがオフラインになっている可能性があり、公開が失敗することがあります。",
|
||||
"posts_last_synced_info": "最後に同期された投稿 {{time}}, サブプレビットがオフラインになっている可能性があり、公開が失敗することがあります。"
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}}개월 전",
|
||||
"time_1_year_ago": "1년 전",
|
||||
"time_x_years_ago": "{{count}}년 전",
|
||||
"open": "열기"
|
||||
"open": "열기",
|
||||
"bump_order": "버프 순서",
|
||||
"creation_date": "작성 날짜",
|
||||
"sort_by": "정렬 기준",
|
||||
"time_filter": "시간 필터",
|
||||
"subplebbit_offline_info": "서브플레빗이 오프라인 일 수 있으며 게시가 실패할 수 있습니다.",
|
||||
"posts_last_synced_info": "최근 동기화된 게시물 {{time}}, 서브플레빗이 오프라인 일 수 있으며 게시가 실패할 수 있습니다."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}} महिन्यांपूर्वी",
|
||||
"time_1_year_ago": "१ वर्षांपूर्वी",
|
||||
"time_x_years_ago": "{{count}} वर्षांपूर्वी",
|
||||
"open": "उघडा"
|
||||
"open": "उघडा",
|
||||
"bump_order": "बंप क्रम",
|
||||
"creation_date": "निर्माण तारीख",
|
||||
"sort_by": "क्रमवार करा",
|
||||
"time_filter": "वेळ फिल्टर",
|
||||
"subplebbit_offline_info": "सबप्लेबिट मोबाइल असून प्रकाशन अयशस्वी होऊ शकतो.",
|
||||
"posts_last_synced_info": "शेवटच्या बारीस सिन्क केलेले पोस्ट {{time}}, सबप्लेबिट मोबाइल असून प्रकाशन अयशस्वी होऊ शकतो."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}} maanden geleden",
|
||||
"time_1_year_ago": "1 jaar geleden",
|
||||
"time_x_years_ago": "{{count}} jaar geleden",
|
||||
"open": "openen"
|
||||
"open": "openen",
|
||||
"bump_order": "Bump-volgorde",
|
||||
"creation_date": "Creatiedatum",
|
||||
"sort_by": "Sorteer op",
|
||||
"time_filter": "Tijdsfilter",
|
||||
"subplebbit_offline_info": "De subplebbit kan offline zijn en publiceren kan mislukken.",
|
||||
"posts_last_synced_info": "Laatste keer gesynchroniseerde berichten {{time}}, de subplebbit kan offline zijn en publiceren kan mislukken."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}} måneder siden",
|
||||
"time_1_year_ago": "1 år siden",
|
||||
"time_x_years_ago": "{{count}} år siden",
|
||||
"open": "åpne"
|
||||
"open": "åpne",
|
||||
"bump_order": "Bump rekkefølge",
|
||||
"creation_date": "Opprettelsesdato",
|
||||
"sort_by": "Sorter etter",
|
||||
"time_filter": "Tidsfilter",
|
||||
"subplebbit_offline_info": "Subplebbiten kan være offline, og publisering kan mislykkes.",
|
||||
"posts_last_synced_info": "Siste synkroniserte innlegg {{time}}, subplebbiten kan være offline, og publisering kan mislykkes."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}} miesięcy temu",
|
||||
"time_1_year_ago": "1 rok temu",
|
||||
"time_x_years_ago": "{{count}} lat temu",
|
||||
"open": "otwórz"
|
||||
"open": "otwórz",
|
||||
"bump_order": "Kolejność Bump",
|
||||
"creation_date": "Data utworzenia",
|
||||
"sort_by": "Sortuj według",
|
||||
"time_filter": "Filtr czasu",
|
||||
"subplebbit_offline_info": "Subplebbit może być offline, a publikacja może się nie powieść.",
|
||||
"posts_last_synced_info": "Ostatnio zsynchronizowane posty {{time}}, subplebbit może być offline, a publikacja może się nie powieść."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "há {{count}} meses",
|
||||
"time_1_year_ago": "há 1 ano",
|
||||
"time_x_years_ago": "há {{count}} anos",
|
||||
"open": "abrir"
|
||||
"open": "abrir",
|
||||
"bump_order": "Ordem de Bump",
|
||||
"creation_date": "Data de criação",
|
||||
"sort_by": "Ordenar por",
|
||||
"time_filter": "Filtro de tempo",
|
||||
"subplebbit_offline_info": "O subplebbit pode estar offline e a publicação pode falhar.",
|
||||
"posts_last_synced_info": "Posts sincronizados pela última vez {{time}}, o subplebbit pode estar offline e a publicação pode falhar."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "Acum {{count}} luni",
|
||||
"time_1_year_ago": "Acum 1 an",
|
||||
"time_x_years_ago": "Acum {{count}} ani",
|
||||
"open": "deschis"
|
||||
"open": "deschis",
|
||||
"bump_order": "Ordinea Bump",
|
||||
"creation_date": "Data de creare",
|
||||
"sort_by": "Sortează după",
|
||||
"time_filter": "Filtru de timp",
|
||||
"subplebbit_offline_info": "Subplebbitul ar putea fi offline și publicarea ar putea eșua.",
|
||||
"posts_last_synced_info": "Ultimele postări sincronizate {{time}}, subplebbitul ar putea fi offline și publicarea ar putea eșua."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}} месяцев назад",
|
||||
"time_1_year_ago": "1 год назад",
|
||||
"time_x_years_ago": "{{count}} лет назад",
|
||||
"open": "открыть"
|
||||
"open": "открыть",
|
||||
"bump_order": "Порядок Bump",
|
||||
"creation_date": "Дата создания",
|
||||
"sort_by": "Сортировать по",
|
||||
"time_filter": "Фильтр времени",
|
||||
"subplebbit_offline_info": "Сабплеббит может быть офлайн, и публикация может не удалиться.",
|
||||
"posts_last_synced_info": "Последние синхронизированные сообщения {{time}}, сабплеббит может быть офлайн, и публикация может не удалиться."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}} muaj më parë",
|
||||
"time_1_year_ago": "1 vit më parë",
|
||||
"time_x_years_ago": "{{count}} vite më parë",
|
||||
"open": "hap"
|
||||
"open": "hap",
|
||||
"bump_order": "Renditja e Bump",
|
||||
"creation_date": "Data e krijimit",
|
||||
"sort_by": "Rendit sipas",
|
||||
"time_filter": "Filtri i kohës",
|
||||
"subplebbit_offline_info": "Subplebbiti mund të jetë jashtë linje dhe publikimi mund të dështojë.",
|
||||
"posts_last_synced_info": "Postimet e fundit të sinkronizuara {{time}}, subplebbiti mund të jetë jashtë linje dhe publikimi mund të dështojë."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "För {{count}} månader sedan",
|
||||
"time_1_year_ago": "För 1 år sedan",
|
||||
"time_x_years_ago": "För {{count}} år sedan",
|
||||
"open": "öppna"
|
||||
"open": "öppna",
|
||||
"bump_order": "Bump ordning",
|
||||
"creation_date": "Skapelsedatum",
|
||||
"sort_by": "Sortera efter",
|
||||
"time_filter": "Tidsfilter",
|
||||
"subplebbit_offline_info": "Subplebbit kan vara offline och publicering kan misslyckas.",
|
||||
"posts_last_synced_info": "Inlägg senast synkroniserade {{time}}, subplebbit kan vara offline och publicering kan misslyckas."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}} నెలల క్రితం",
|
||||
"time_1_year_ago": "1 సంవత్సరం క్రితం",
|
||||
"time_x_years_ago": "{{count}} సంవత్సరాల క్రితం",
|
||||
"open": "తెరవండి"
|
||||
"open": "తెరవండి",
|
||||
"bump_order": "బంప్ క్రమం",
|
||||
"creation_date": "సృష్టి తేదీ",
|
||||
"sort_by": "గట్టిగా",
|
||||
"time_filter": "సమయ ఫిల్టర్",
|
||||
"subplebbit_offline_info": "సబ్ప్లెబిట్ ఆఫ్లైన్ ఉండవచ్చు మరియు ప్రచురణ విఫలమైనా ఉంటుంది.",
|
||||
"posts_last_synced_info": "ఇటీవలే సింక్ చేసిన పోస్ట్లు {{time}}, సబ్ప్లెబిట్ ఆఫ్లైన్ ఉండవచ్చు, మరియు ప్రకటన విఫలమవుతుంది."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}} เดือนที่ผ่านมา",
|
||||
"time_1_year_ago": "1 ปีที่ผ่านมา",
|
||||
"time_x_years_ago": "{{count}} ปีที่ผ่านมา",
|
||||
"open": "เปิด"
|
||||
"open": "เปิด",
|
||||
"bump_order": "ลำดับของ Bump",
|
||||
"creation_date": "วันที่สร้าง",
|
||||
"sort_by": "เรียงลำดับตาม",
|
||||
"time_filter": "ตัวกรองเวลา",
|
||||
"subplebbit_offline_info": "เซ็บเพล็บบิทอาจออฟไลน์และการเผยแพร่อาจล้มเหลว",
|
||||
"posts_last_synced_info": "โพสต์ล่าสุดที่ซิงค์ {{time}}, ซับเพลบบิทอาจออฟไลน์และการเผยแพร่อาจล้มเหลว"
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}} ay önce",
|
||||
"time_1_year_ago": "1 yıl önce",
|
||||
"time_x_years_ago": "{{count}} yıl önce",
|
||||
"open": "aç"
|
||||
"open": "aç",
|
||||
"bump_order": "Bump sırası",
|
||||
"creation_date": "Oluşturulma tarihi",
|
||||
"sort_by": "Sırala",
|
||||
"time_filter": "Zaman Filtresi",
|
||||
"subplebbit_offline_info": "Subplebbit çevrimdışı olabilir ve yayınlama başarısız olabilir.",
|
||||
"posts_last_synced_info": "Son senkronize edilen gönderiler {{time}}, subplebbit çevrimdışı olabilir ve yayınlama başarısız olabilir."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}} місяців тому",
|
||||
"time_1_year_ago": "1 рік тому",
|
||||
"time_x_years_ago": "{{count}} років тому",
|
||||
"open": "відкрити"
|
||||
"open": "відкрити",
|
||||
"bump_order": "Порядок Bump",
|
||||
"creation_date": "Дата створення",
|
||||
"sort_by": "Сортувати за",
|
||||
"time_filter": "Фільтр за часом",
|
||||
"subplebbit_offline_info": "Сабплебіт може бути офлайн, а публікація може не вдастися.",
|
||||
"posts_last_synced_info": "Останні синхронізовані повідомлення {{time}}, сабплебіт може бути офлайн, а публікація може не вдастися."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}} مہینے پہلے",
|
||||
"time_1_year_ago": "1 سال پہلے",
|
||||
"time_x_years_ago": "{{count}} سال پہلے",
|
||||
"open": "کھولیں"
|
||||
"open": "کھولیں",
|
||||
"bump_order": "بمپ آرڈر",
|
||||
"creation_date": "تخلیق کی تاریخ",
|
||||
"sort_by": "کے ذریعہ سارٹ کریں",
|
||||
"time_filter": "وقت فلٹر",
|
||||
"subplebbit_offline_info": "سب پلیبٹ آف لائن ہوسکتا ہے اور شائع ہونے میں ناکام ہوسکتا ہے۔",
|
||||
"posts_last_synced_info": "آخری بار پوسٹ سنک کی گئی {{time}}, سب پلیبٹ آف لائن ہوسکتا ہے اور شائع ہونے میں ناکام ہوسکتا ہے۔"
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}} tháng trước",
|
||||
"time_1_year_ago": "1 năm trước",
|
||||
"time_x_years_ago": "{{count}} năm trước",
|
||||
"open": "mở"
|
||||
"open": "mở",
|
||||
"bump_order": "Thứ tự Bump",
|
||||
"creation_date": "Ngày tạo",
|
||||
"sort_by": "Sắp xếp theo",
|
||||
"time_filter": "Bộ lọc thời gian",
|
||||
"subplebbit_offline_info": "Subplebbit có thể ngoại tuyến và việc xuất bản có thể thất bại.",
|
||||
"posts_last_synced_info": "Bài viết được đồng bộ lần cuối {{time}}, subplebbit có thể ngoại tuyến và việc xuất bản có thể thất bại."
|
||||
}
|
||||
@@ -159,5 +159,11 @@
|
||||
"time_x_months_ago": "{{count}}个月前",
|
||||
"time_1_year_ago": "1年前",
|
||||
"time_x_years_ago": "{{count}}年前",
|
||||
"open": "打开"
|
||||
"open": "打开",
|
||||
"bump_order": "顶贴顺序",
|
||||
"creation_date": "创造日期",
|
||||
"sort_by": "排序方式",
|
||||
"time_filter": "时间过滤器",
|
||||
"subplebbit_offline_info": "子维基可能离线,发布可能失败。",
|
||||
"posts_last_synced_info": "最后同步的帖子 {{time}}, 子维基可能离线,发布可能失败。"
|
||||
}
|
||||
+8
-8
@@ -92,16 +92,16 @@ const App = () => {
|
||||
<Route path='/p/:subplebbitAddress/rules' element={<PostPage />} />
|
||||
<Route path='/p/:subplebbitAddress/rules/settings' element={<PostPage />} />
|
||||
|
||||
<Route path='/p/all' element={<Board />} />
|
||||
<Route path='/p/all/:timeFilterName?' element={<Board />} />
|
||||
<Route path='/p/all/:timeFilterName?/settings' element={<Board />} />
|
||||
<Route path='/p/all/description' element={<PostPage />} />
|
||||
<Route path='/p/all/settings' element={<Board />} />
|
||||
<Route path='/p/all/catalog' element={<Catalog />} />
|
||||
<Route path='/p/all/catalog/settings' element={<Catalog />} />
|
||||
<Route path='/p/all/catalog/:timeFilterName?' element={<Catalog />} />
|
||||
<Route path='/p/all/catalog/:timeFilterName?/settings' element={<Catalog />} />
|
||||
|
||||
<Route path='/p/subscriptions' element={<Board />} />
|
||||
<Route path='/p/subscriptions/settings' element={<Board />} />
|
||||
<Route path='/p/subscriptions/catalog' element={<Catalog />} />
|
||||
<Route path='/p/subscriptions/catalog/settings' element={<Catalog />} />
|
||||
<Route path='/p/subscriptions/:timeFilterName?' element={<Board />} />
|
||||
<Route path='/p/subscriptions/:timeFilterName?/settings' element={<Board />} />
|
||||
<Route path='/p/subscriptions/catalog/:timeFilterName?' element={<Catalog />} />
|
||||
<Route path='/p/subscriptions/catalog/:timeFilterName?/settings' element={<Catalog />} />
|
||||
|
||||
<Route path='/profile/:accountCommentIndex' element={<PendingPost />} />
|
||||
<Route path='/profile/:accountCommentIndex/settings' element={<PendingPost />} />
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link, useLocation, useParams } from 'react-router-dom';
|
||||
import { Link, useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import { useAccountComment, useSubscribe } from '@plebbit/plebbit-react-hooks';
|
||||
import { isAllView, isCatalogView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
||||
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
||||
import useSortingStore from '../../stores/use-sorting-store';
|
||||
import useTimeFilter from '../../hooks/use-time-filter';
|
||||
import styles from './board-buttons.module.css';
|
||||
|
||||
interface BoardButtonsProps {
|
||||
isInAllView?: boolean;
|
||||
address?: string | undefined;
|
||||
isInAllView?: boolean;
|
||||
isInCatalogView?: boolean;
|
||||
isInSubscriptionsView?: boolean;
|
||||
isTopbar?: boolean;
|
||||
}
|
||||
|
||||
const CatalogButton = ({ address, isInAllView, isInSubscriptionsView }: BoardButtonsProps) => {
|
||||
@@ -55,6 +57,7 @@ const RefreshButton = () => {
|
||||
};
|
||||
|
||||
const SortOptions = () => {
|
||||
const { t } = useTranslation();
|
||||
const { sortType, setSortType } = useSortingStore();
|
||||
|
||||
const handleSortChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
@@ -63,21 +66,62 @@ const SortOptions = () => {
|
||||
};
|
||||
return (
|
||||
<>
|
||||
Sort by:
|
||||
{t('sort_by')}:
|
||||
<select value={sortType} onChange={handleSortChange}>
|
||||
<option value='active'>Bump order</option>
|
||||
<option value='new'>Creation date</option>
|
||||
<option value='active'>{t('bump_order')}</option>
|
||||
<option value='new'>{t('creation_date')}</option>
|
||||
</select>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const TimeFilter = ({ isInAllView, isInCatalogView, isInSubscriptionsView, isTopbar = false }: BoardButtonsProps) => {
|
||||
const { t } = useTranslation();
|
||||
const params = useParams();
|
||||
const navigate = useNavigate();
|
||||
const { timeFilterNames } = useTimeFilter();
|
||||
const selectedTimeFilterName = params.timeFilterName;
|
||||
|
||||
const changeTimeFilter = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const timeFilterName = event.target.value;
|
||||
const link = isInAllView
|
||||
? isInCatalogView
|
||||
? `/p/all/catalog/${timeFilterName}`
|
||||
: `/p/all/${timeFilterName}`
|
||||
: isInSubscriptionsView
|
||||
? isInCatalogView
|
||||
? `/p/subscriptions/catalog/${timeFilterName}`
|
||||
: `/p/subscriptions/${timeFilterName}`
|
||||
: null;
|
||||
link && navigate(link);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{!isTopbar ? (
|
||||
<>
|
||||
<span className='capitalize'>{t('time_filter')}</span>:
|
||||
</>
|
||||
) : (
|
||||
<> </>
|
||||
)}
|
||||
<select onChange={changeTimeFilter} className={[styles.feedName, styles.menuItem].join(' ')} value={selectedTimeFilterName}>
|
||||
{timeFilterNames.map((timeFilterName, i) => (
|
||||
<option key={timeFilterName + i} value={timeFilterName}>
|
||||
{timeFilterName}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const MobileBoardButtons = () => {
|
||||
const params = useParams();
|
||||
const location = useLocation();
|
||||
const isInAllView = isAllView(location.pathname);
|
||||
const isInCatalogView = isCatalogView(location.pathname);
|
||||
const isInCatalogView = isCatalogView(location.pathname, params);
|
||||
const isInPendingPostPage = isPendingPostView(location.pathname, params);
|
||||
const isInPostView = isPostPageView(location.pathname, params);
|
||||
const isInSubscriptionsView = isSubscriptionsView(location.pathname);
|
||||
@@ -106,10 +150,24 @@ export const MobileBoardButtons = () => {
|
||||
<>
|
||||
<hr />
|
||||
<div className={styles.options}>
|
||||
{(isInAllView || isInSubscriptionsView) && (
|
||||
<>
|
||||
<TimeFilter isInAllView={isInAllView} isInCatalogView={false} isInSubscriptionsView={isInSubscriptionsView} />
|
||||
|
||||
</>
|
||||
)}
|
||||
<SortOptions />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{(isInAllView || isInSubscriptionsView) && !isInCatalogView && (
|
||||
<>
|
||||
<hr />
|
||||
<div className={styles.options}>
|
||||
<TimeFilter isInAllView={isInAllView} isInCatalogView={false} isInSubscriptionsView={isInSubscriptionsView} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
@@ -121,7 +179,7 @@ export const DesktopBoardButtons = () => {
|
||||
const location = useLocation();
|
||||
const accountComment = useAccountComment({ commentIndex: params?.accountCommentIndex as any });
|
||||
const subplebbitAddress = params?.subplebbitAddress || accountComment?.subplebbitAddress;
|
||||
const isInCatalogView = isCatalogView(location.pathname);
|
||||
const isInCatalogView = isCatalogView(location.pathname, params);
|
||||
const isInAllView = isAllView(location.pathname);
|
||||
const isInPendingPostPage = isPendingPostView(location.pathname, params);
|
||||
const isInPostView = isPostPageView(location.pathname, params);
|
||||
@@ -166,6 +224,11 @@ export const DesktopBoardButtons = () => {
|
||||
<SubscribeButton address={subplebbitAddress} />]
|
||||
</>
|
||||
)}
|
||||
{(isInAllView || isInSubscriptionsView) && (
|
||||
<>
|
||||
<TimeFilter isInAllView={isInAllView} isInCatalogView={false} isInSubscriptionsView={isInSubscriptionsView} />
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { MobileBoardButtons, DesktopBoardButtons } from './board-buttons';
|
||||
export { TimeFilter, MobileBoardButtons, DesktopBoardButtons } from './board-buttons';
|
||||
|
||||
@@ -28,6 +28,16 @@
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.offlineIcon {
|
||||
background-image: url('/public/assets/icons/offline.png');
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.bannerCnt {
|
||||
|
||||
@@ -31,15 +31,20 @@ const BoardHeader = () => {
|
||||
|
||||
const multisubMetadata = useMultisubMetadata();
|
||||
|
||||
const title = isInAllView ? multisubMetadata?.title : isInSubscriptionsView ? 'Subscriptions' : subplebbit?.title;
|
||||
const title = isInAllView ? multisubMetadata?.title || 'all' : isInSubscriptionsView ? 'Subscriptions' : subplebbit?.title;
|
||||
const subtitle = isInAllView ? 'p/all' : isInSubscriptionsView ? 'p/subscriptions' : `p/${address}`;
|
||||
|
||||
const isBoardOffline = subplebbit?.updatedAt && subplebbit.updatedAt < Date.now() / 1000 - 60 * 60;
|
||||
|
||||
return (
|
||||
<div className={styles.content}>
|
||||
<div className={styles.bannerCnt}>
|
||||
<ImageBanner key={isInAllView ? 'all' : isInSubscriptionsView ? 'subscriptions' : address} />
|
||||
</div>
|
||||
<div className={styles.boardTitle}>{title || `p/${shortAddress || subplebbitAddress}`}</div>
|
||||
<div className={styles.boardTitle}>
|
||||
{title || `p/${shortAddress || subplebbitAddress}`}
|
||||
{isBoardOffline && <span className={styles.offlineIcon} />}
|
||||
</div>
|
||||
<div className={styles.boardSubtitle}>{subtitle}</div>
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
@@ -136,7 +136,7 @@ const Media = ({ commentMediaInfo, isReply, setShowThumbnail }: MediaProps) => {
|
||||
)}
|
||||
{isMobile && (type === 'iframe' || type === 'video' || type === 'audio') && (
|
||||
<div className={styles.closeButton}>
|
||||
<span className='button' onClick={() => setShowThumbnail(false)}>
|
||||
<span className='button' onClick={() => setShowThumbnail(true)}>
|
||||
{t('close')}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -23,8 +23,15 @@
|
||||
text-align: center;
|
||||
color: red;
|
||||
font-weight: 700;
|
||||
padding-top: 100px;
|
||||
padding-bottom: 100px;
|
||||
padding-top: 100px 0;
|
||||
}
|
||||
|
||||
.offlineBoard {
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
color: red;
|
||||
padding: 30px 0;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.postFormTable td:first-child {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import { PublishCommentOptions, setAccount, useAccount, useAccountComment, useComment, usePublishComment } from '@plebbit/plebbit-react-hooks';
|
||||
import { PublishCommentOptions, setAccount, useAccount, useAccountComment, useComment, usePublishComment, useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
||||
import { create } from 'zustand';
|
||||
import { alertChallengeVerificationFailed } from '../../lib/utils/challenge-utils';
|
||||
import { getLinkMediaInfo } from '../../lib/utils/media-utils';
|
||||
@@ -12,6 +12,7 @@ import useReply from '../../hooks/use-reply';
|
||||
import useChallengesStore from '../../stores/use-challenges-store';
|
||||
import styles from './post-form.module.css';
|
||||
import _ from 'lodash';
|
||||
import { getFormattedTimeAgo } from '../../lib/utils/time-utils';
|
||||
|
||||
type SubmitState = {
|
||||
subplebbitAddress: string | undefined;
|
||||
@@ -271,9 +272,24 @@ const PostForm = () => {
|
||||
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
|
||||
const subplebbit = useSubplebbit({ subplebbitAddress: params?.subplebbitAddress });
|
||||
const { updatedAt } = subplebbit || {};
|
||||
const isBoardOffline = subplebbit?.updatedAt && subplebbit.updatedAt < Date.now() / 1000 - 60 * 60;
|
||||
|
||||
const offlineAlert =
|
||||
showForm &&
|
||||
(updatedAt ? (
|
||||
isBoardOffline && (
|
||||
<div className={styles.offlineBoard}>{t('posts_last_synced_info', { time: getFormattedTimeAgo(updatedAt), interpolation: { escapeValue: false } })}</div>
|
||||
)
|
||||
) : (
|
||||
<div className={styles.offlineBoard}>{t('subplebbit_offline_info')}</div>
|
||||
));
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.postFormDesktop}>
|
||||
{offlineAlert}
|
||||
{isThreadClosed ? (
|
||||
<div className={styles.closed}>
|
||||
{t('thread_closed')}
|
||||
@@ -293,6 +309,7 @@ const PostForm = () => {
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.postFormMobile}>
|
||||
{offlineAlert}
|
||||
{isThreadClosed ? (
|
||||
<div className={styles.closed}>
|
||||
{t('thread_closed')}
|
||||
|
||||
@@ -114,7 +114,7 @@ const PostMenuDesktop = ({ post }: { post: Comment }) => {
|
||||
const location = useLocation();
|
||||
const params = useParams();
|
||||
const isInAllView = isAllView(location.pathname);
|
||||
const isInCatalogView = isCatalogView(location.pathname);
|
||||
const isInCatalogView = isCatalogView(location.pathname, params);
|
||||
const isInPostPageView = isPostPageView(location.pathname, params);
|
||||
const isInSubscriptionsView = isSubscriptionsView(location.pathname);
|
||||
|
||||
|
||||
@@ -91,6 +91,16 @@
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
.offlineBoard {
|
||||
width: 292px;
|
||||
font-family: monospace;
|
||||
background-color: #E62020;
|
||||
font-size: 12px;
|
||||
color: white;
|
||||
padding: 3px 7px 3px 5px;
|
||||
text-shadow: 0 1px rgba(0, 0, 0, 0.20);
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.container {
|
||||
position: absolute;
|
||||
|
||||
@@ -2,8 +2,9 @@ import { useEffect, useRef, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Draggable from 'react-draggable';
|
||||
import { setAccount, useAccount, useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
||||
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
|
||||
import { setAccount, useAccount } from '@plebbit/plebbit-react-hooks';
|
||||
import { getFormattedTimeAgo } from '../../lib/utils/time-utils';
|
||||
import { isValidURL } from '../../lib/utils/url-utils';
|
||||
import useReply from '../../hooks/use-reply';
|
||||
import useIsMobile from '../../hooks/use-is-mobile';
|
||||
@@ -75,6 +76,15 @@ const ReplyModal = ({ closeModal, parentCid, scrollY }: ReplyModalProps) => {
|
||||
}
|
||||
}, [parentCid]);
|
||||
|
||||
const subplebbit = useSubplebbit({ subplebbitAddress });
|
||||
const { updatedAt } = subplebbit || {};
|
||||
const isBoardOffline = subplebbit?.updatedAt && subplebbit.updatedAt < Date.now() / 1000 - 60 * 60;
|
||||
const offlineAlert = updatedAt
|
||||
? isBoardOffline && (
|
||||
<div className={styles.offlineBoard}>{`Posts last synced ${getFormattedTimeAgo(updatedAt)}, the subplebbit might be offline and publishing might fail.`}</div>
|
||||
)
|
||||
: `The subplebbit might be offline and publishing might fail.`;
|
||||
|
||||
const modalContent = (
|
||||
<div className={styles.container} ref={nodeRef}>
|
||||
<div className={`replyModalHandle ${styles.title}`}>
|
||||
@@ -124,6 +134,8 @@ const ReplyModal = ({ closeModal, parentCid, scrollY }: ReplyModalProps) => {
|
||||
autoFocus={!isMobile} // autofocus causes auto scroll to top on mobile
|
||||
/>
|
||||
</div>
|
||||
{offlineAlert}
|
||||
<div className={styles.offlineAlert}></div>
|
||||
<div className={styles.footer}>
|
||||
{url && (
|
||||
<>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { isAllView, isCatalogView, isSubscriptionsView } from '../../lib/utils/v
|
||||
import styles from './topbar.module.css';
|
||||
import useDefaultSubplebbits, { categorizeSubplebbits, useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
|
||||
import _, { debounce } from 'lodash';
|
||||
import { TimeFilter } from '../board-buttons';
|
||||
|
||||
const SearchBar = ({ setShowSearchBar }: { setShowSearchBar: (show: boolean) => void }) => {
|
||||
const navigate = useNavigate();
|
||||
@@ -55,7 +56,8 @@ const SearchBar = ({ setShowSearchBar }: { setShowSearchBar: (show: boolean) =>
|
||||
const TopBarDesktop = () => {
|
||||
const { t } = useTranslation();
|
||||
const location = useLocation();
|
||||
const isInCatalogView = isCatalogView(location.pathname);
|
||||
const params = useParams();
|
||||
const isInCatalogView = isCatalogView(location.pathname, params);
|
||||
const subplebbits = useDefaultSubplebbits();
|
||||
const [showSearchBar, setShowSearchBar] = useState(false);
|
||||
|
||||
@@ -74,8 +76,13 @@ const TopBarDesktop = () => {
|
||||
return (
|
||||
<div className={styles.boardNavDesktop}>
|
||||
<span className={styles.boardList}>
|
||||
[<Link to='/p/all'>all</Link> / <Link to='/p/subscriptions'>subscriptions</Link>] [{renderSubplebbits(plebbitSubs)}] [{renderSubplebbits(projectsSubs)}] [
|
||||
{renderSubplebbits(interestsSubs)}] [{renderSubplebbits(randomSubs)}] [{renderSubplebbits(internationalSubs)}]
|
||||
[<Link to='/p/all'>all</Link> / <Link to='/p/subscriptions'>subscriptions</Link>]{' '}
|
||||
{subplebbits && (
|
||||
<>
|
||||
[{renderSubplebbits(plebbitSubs)}] [{renderSubplebbits(projectsSubs)}] [{renderSubplebbits(interestsSubs)}] [{renderSubplebbits(randomSubs)}] [
|
||||
{renderSubplebbits(internationalSubs)}]
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
<span className={styles.navTopRight}>
|
||||
[<Link to={!location.pathname.endsWith('settings') ? location.pathname.replace(/\/$/, '') + '/settings' : location.pathname}>{t('settings')}</Link>] [
|
||||
@@ -96,8 +103,9 @@ const TopBarMobile = ({ subplebbitAddress }: { subplebbitAddress: string }) => {
|
||||
const currentSubplebbitIsInList = subplebbitAddresses.some((address: string) => address === subplebbitAddress);
|
||||
|
||||
const location = useLocation();
|
||||
const params = useParams();
|
||||
const isInAllView = isAllView(location.pathname);
|
||||
const isInCatalogView = isCatalogView(location.pathname);
|
||||
const isInCatalogView = isCatalogView(location.pathname, params);
|
||||
const isInSubscriptionsView = isSubscriptionsView(location.pathname);
|
||||
const selectValue = isInAllView ? 'all' : isInSubscriptionsView ? 'subscriptions' : subplebbitAddress;
|
||||
|
||||
@@ -140,6 +148,9 @@ const TopBarMobile = ({ subplebbitAddress }: { subplebbitAddress: string }) => {
|
||||
<div className={styles.boardSelect}>
|
||||
<strong>{t('board')}</strong>
|
||||
{boardSelect}
|
||||
{(isInAllView || isInSubscriptionsView) && (
|
||||
<TimeFilter isTopbar={true} isInAllView={isInAllView} isInCatalogView={false} isInSubscriptionsView={isInSubscriptionsView} />
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.pageJump}>
|
||||
<Link to={useLocation().pathname.replace(/\/$/, '') + '/settings'}>{t('settings')}</Link>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import localForageLru from '@plebbit/plebbit-react-hooks/dist/lib/localforage-lru/index.js';
|
||||
@@ -0,0 +1,60 @@
|
||||
import assert from 'assert';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
// the timestamp the last time the user visited
|
||||
const lastVisitTimestamp = localStorage.getItem('plebchanLastVisitTimestamp');
|
||||
|
||||
// update the last visited timestamp every n seconds
|
||||
setInterval(() => {
|
||||
localStorage.setItem('plebchanLastVisitTimestamp', Date.now());
|
||||
}, 60 * 1000);
|
||||
|
||||
const timeFilterNamesToSeconds = {
|
||||
'1h': 60 * 60,
|
||||
'12h': 60 * 60 * 12,
|
||||
'24h': 60 * 60 * 24,
|
||||
'48h': 60 * 60 * 24 * 2,
|
||||
week: 60 * 60 * 24 * 7,
|
||||
month: 60 * 60 * 24 * 30,
|
||||
year: 60 * 60 * 24 * 365,
|
||||
all: undefined,
|
||||
};
|
||||
|
||||
// calculate the last visit timeFilterNamesToSeconds
|
||||
const secondsSinceLastVisit = lastVisitTimestamp ? (Date.now() - lastVisitTimestamp) / 1000 : Infinity;
|
||||
const day = 24 * 60 * 60;
|
||||
let lastVisitTimeFilterName;
|
||||
if (secondsSinceLastVisit > 30 * day) {
|
||||
lastVisitTimeFilterName = 'month';
|
||||
timeFilterNamesToSeconds[lastVisitTimeFilterName] = timeFilterNamesToSeconds['month'];
|
||||
} else if (secondsSinceLastVisit > 7 * day) {
|
||||
const weeks = Math.ceil(secondsSinceLastVisit / day / 7);
|
||||
lastVisitTimeFilterName = `${weeks}w`;
|
||||
timeFilterNamesToSeconds[lastVisitTimeFilterName] = 60 * 60 * 24 * 7 * weeks;
|
||||
} else if (secondsSinceLastVisit > day) {
|
||||
const days = Math.ceil(secondsSinceLastVisit / day);
|
||||
lastVisitTimeFilterName = `${days}d`;
|
||||
timeFilterNamesToSeconds[lastVisitTimeFilterName] = 60 * 60 * 24 * days;
|
||||
} else {
|
||||
lastVisitTimeFilterName = '24h';
|
||||
timeFilterNamesToSeconds[lastVisitTimeFilterName] = timeFilterNamesToSeconds['24h'];
|
||||
}
|
||||
|
||||
const timeFilterNames = [lastVisitTimeFilterName, '1h', '12h', '24h', '48h', 'week', 'month', 'year', 'all'];
|
||||
|
||||
const useTimeFilter = () => {
|
||||
const params = useParams();
|
||||
let timeFilterName = params.timeFilterName;
|
||||
|
||||
// the default time filter is the last visit time filter
|
||||
if (!timeFilterName) {
|
||||
timeFilterName = lastVisitTimeFilterName;
|
||||
}
|
||||
|
||||
assert(!timeFilterName || typeof timeFilterName === 'string', `useTimeFilter timeFilterName argument '${timeFilterName}' not a string`);
|
||||
const timeFilterSeconds = timeFilterNamesToSeconds[timeFilterName];
|
||||
assert(!timeFilterName || timeFilterName === 'all' || timeFilterSeconds !== undefined, `useTimeFilter no filter for timeFilterName '${timeFilterName}'`);
|
||||
return { timeFilterSeconds, timeFilterNames };
|
||||
};
|
||||
|
||||
export default useTimeFilter;
|
||||
@@ -2,6 +2,7 @@ export type ParamsType = {
|
||||
accountCommentIndex?: string;
|
||||
commentCid?: string;
|
||||
subplebbitAddress?: string;
|
||||
timeFilterName?: string;
|
||||
};
|
||||
|
||||
export const isAllView = (pathname: string): boolean => {
|
||||
@@ -14,8 +15,17 @@ export const isBoardView = (pathname: string, params: ParamsType): boolean => {
|
||||
return params.subplebbitAddress ? decodedPathname.startsWith(`/p/${params.subplebbitAddress}`) : false;
|
||||
};
|
||||
|
||||
export const isCatalogView = (pathname: string): boolean => {
|
||||
return pathname.endsWith('/catalog') || pathname.endsWith('/catalog/settings');
|
||||
export const isCatalogView = (pathname: string, params: ParamsType): boolean => {
|
||||
const { subplebbitAddress, timeFilterName } = params;
|
||||
const decodedPathname = decodeURIComponent(pathname);
|
||||
|
||||
return (
|
||||
decodedPathname === `/p/${subplebbitAddress}/catalog` ||
|
||||
decodedPathname === `/p/all/catalog` ||
|
||||
decodedPathname === `/p/all/catalog/${timeFilterName}` ||
|
||||
decodedPathname === `/p/subscriptions/catalog` ||
|
||||
decodedPathname === `/p/subscriptions/catalog/${timeFilterName}`
|
||||
);
|
||||
};
|
||||
|
||||
export const isDescriptionView = (pathname: string, params: ParamsType): boolean => {
|
||||
@@ -63,7 +73,7 @@ export const isNotFoundView = (pathname: string, params: ParamsType): boolean =>
|
||||
return (
|
||||
!isAllView(pathname) &&
|
||||
!isBoardView(pathname, params) &&
|
||||
!isCatalogView(pathname) &&
|
||||
!isCatalogView(pathname, params) &&
|
||||
!isDescriptionView(pathname, params) &&
|
||||
!isHomeView(pathname) &&
|
||||
!isPendingPostView(pathname, params) &&
|
||||
|
||||
@@ -8,6 +8,7 @@ import { isAllView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
||||
import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
|
||||
import useFeedStateString from '../../hooks/use-feed-state-string';
|
||||
import useReplyModal from '../../hooks/use-reply-modal';
|
||||
import useTimeFilter from '../../hooks/use-time-filter';
|
||||
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
||||
import useSortingStore from '../../stores/use-sorting-store';
|
||||
import LoadingEllipsis from '../../components/loading-ellipsis';
|
||||
@@ -42,7 +43,19 @@ const Board = () => {
|
||||
}, [isInAllView, isInSubscriptionsView, subplebbitAddress, defaultSubplebbitAddresses, subscriptions]);
|
||||
|
||||
const { sortType } = useSortingStore();
|
||||
const { feed, hasMore, loadMore, reset } = useFeed({ subplebbitAddresses, sortType });
|
||||
const { timeFilterSeconds } = useTimeFilter();
|
||||
|
||||
const feedOptions: any = {
|
||||
subplebbitAddresses,
|
||||
sortType,
|
||||
postsPerPage: 10,
|
||||
};
|
||||
|
||||
if (isInAllView || isInSubscriptionsView) {
|
||||
feedOptions.newerThan = timeFilterSeconds;
|
||||
}
|
||||
|
||||
const { feed, hasMore, loadMore, reset } = useFeed(feedOptions);
|
||||
|
||||
const setResetFunction = useFeedResetStore((state) => state.setResetFunction);
|
||||
useEffect(() => {
|
||||
@@ -91,15 +104,15 @@ const Board = () => {
|
||||
const setLastVirtuosoState = () => {
|
||||
virtuosoRef.current?.getState((snapshot: StateSnapshot) => {
|
||||
if (snapshot?.ranges?.length) {
|
||||
lastVirtuosoStates[location.pathname] = snapshot;
|
||||
lastVirtuosoStates[sortType + timeFilterSeconds] = snapshot;
|
||||
}
|
||||
});
|
||||
};
|
||||
window.addEventListener('scroll', setLastVirtuosoState);
|
||||
return () => window.removeEventListener('scroll', setLastVirtuosoState);
|
||||
}, [location.pathname]);
|
||||
}, [sortType, timeFilterSeconds]);
|
||||
|
||||
const lastVirtuosoState = lastVirtuosoStates?.[location.pathname];
|
||||
const lastVirtuosoState = lastVirtuosoStates?.[sortType + timeFilterSeconds];
|
||||
|
||||
useEffect(() => {
|
||||
document.title = title ? title : shortAddress || subplebbitAddress;
|
||||
|
||||
@@ -7,6 +7,7 @@ import { isAllView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
||||
import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
|
||||
import useFeedStateString from '../../hooks/use-feed-state-string';
|
||||
import { useMultisubMetadata } from '../../hooks/use-default-subplebbits';
|
||||
import useTimeFilter from '../../hooks/use-time-filter';
|
||||
import useWindowWidth from '../../hooks/use-window-width';
|
||||
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
||||
import useSortingStore from '../../stores/use-sorting-store';
|
||||
@@ -104,7 +105,19 @@ const Catalog = () => {
|
||||
const postsPerPage = useMemo(() => (columnCount <= 2 ? 10 : columnCount === 3 ? 15 : columnCount === 4 ? 20 : 25), []);
|
||||
|
||||
const { sortType } = useSortingStore();
|
||||
const { feed, hasMore, loadMore, reset } = useFeed({ subplebbitAddresses, sortType, postsPerPage });
|
||||
const { timeFilterSeconds } = useTimeFilter();
|
||||
|
||||
const feedOptions: any = {
|
||||
subplebbitAddresses,
|
||||
sortType,
|
||||
postsPerPage,
|
||||
};
|
||||
|
||||
if (isInAllView || isInSubscriptionsView) {
|
||||
feedOptions.newerThan = timeFilterSeconds;
|
||||
}
|
||||
|
||||
const { feed, hasMore, loadMore, reset } = useFeed(feedOptions);
|
||||
|
||||
const setResetFunction = useFeedResetStore((state) => state.setResetFunction);
|
||||
useEffect(() => {
|
||||
@@ -147,14 +160,14 @@ const Catalog = () => {
|
||||
const setLastVirtuosoState = () =>
|
||||
virtuosoRef.current?.getState((snapshot: StateSnapshot) => {
|
||||
if (snapshot?.ranges?.length) {
|
||||
lastVirtuosoStates[location.pathname] = snapshot;
|
||||
lastVirtuosoStates[sortType + timeFilterSeconds + 'catalog'] = snapshot;
|
||||
}
|
||||
});
|
||||
window.addEventListener('scroll', setLastVirtuosoState);
|
||||
return () => window.removeEventListener('scroll', setLastVirtuosoState);
|
||||
}, [location.pathname]);
|
||||
}, [sortType, timeFilterSeconds]);
|
||||
|
||||
const lastVirtuosoState = lastVirtuosoStates?.[location.pathname];
|
||||
const lastVirtuosoState = lastVirtuosoStates?.[sortType + timeFilterSeconds + 'catalog'];
|
||||
|
||||
useEffect(() => {
|
||||
let documentTitle = title ? title : shortAddress;
|
||||
|
||||
@@ -192,6 +192,7 @@ const BoardsBox = ({ multisub, subplebbits }: { multisub: Subplebbit[]; subplebb
|
||||
const isOffline = subplebbit?.updatedAt && subplebbit.updatedAt < Date.now() / 1000 - 60 * 60;
|
||||
return isOffline;
|
||||
};
|
||||
|
||||
const multisubMetadata = useMultisubMetadata();
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user