diff --git a/public/translations/ar/default.json b/public/translations/ar/default.json
index 5468a70e..35143b13 100644
--- a/public/translations/ar/default.json
+++ b/public/translations/ar/default.json
@@ -148,5 +148,16 @@
"close_thread": "اغلق الموضوع",
"view_spoiler": "عرض الحاضر",
"filter": "تصفية",
- "edit": "تحرير"
+ "edit": "تحرير",
+ "time_1_minute_ago": "منذ دقيقة واحدة",
+ "time_x_minutes_ago": "منذ {{count}} دقائق",
+ "time_1_hour_ago": "منذ ساعة واحدة",
+ "time_x_hours_ago": "منذ {{count}} ساعات",
+ "time_1_day_ago": "منذ يوم واحد",
+ "time_x_days_ago": "منذ {{count}} أيام",
+ "time_1_month_ago": "منذ شهر واحد",
+ "time_x_months_ago": "منذ {{count}} شهور",
+ "time_1_year_ago": "منذ عام واحد",
+ "time_x_years_ago": "منذ {{count}} سنوات",
+ "open": "فتح"
}
\ No newline at end of file
diff --git a/public/translations/bn/default.json b/public/translations/bn/default.json
index 0e1bacf1..77ee0266 100644
--- a/public/translations/bn/default.json
+++ b/public/translations/bn/default.json
@@ -148,5 +148,16 @@
"close_thread": "থ্রেড বন্ধ করুন",
"view_spoiler": "স্পোয়ালার দেখুন",
"filter": "ফিল্টার",
- "edit": "সম্পাদনা"
+ "edit": "সম্পাদনা",
+ "time_1_minute_ago": "১ মিনিট আগে",
+ "time_x_minutes_ago": "{{count}} মিনিট আগে",
+ "time_1_hour_ago": "১ ঘণ্টা আগে",
+ "time_x_hours_ago": "{{count}} ঘণ্টা আগে",
+ "time_1_day_ago": "১ দিন আগে",
+ "time_x_days_ago": "{{count}} দিন আগে",
+ "time_1_month_ago": "১ মাস আগে",
+ "time_x_months_ago": "{{count}} মাস আগে",
+ "time_1_year_ago": "১ বছর আগে",
+ "time_x_years_ago": "{{count}} বছর আগে",
+ "open": "খুলুন"
}
\ No newline at end of file
diff --git a/public/translations/cs/default.json b/public/translations/cs/default.json
index d7513ff4..a8adf654 100644
--- a/public/translations/cs/default.json
+++ b/public/translations/cs/default.json
@@ -148,5 +148,16 @@
"close_thread": "uzavřít vlákno",
"view_spoiler": "zobrazit spoiler",
"filter": "filtr",
- "edit": "upravit"
+ "edit": "upravit",
+ "time_1_minute_ago": "Před 1 minutou",
+ "time_x_minutes_ago": "Před {{count}} minutami",
+ "time_1_hour_ago": "Před 1 hodinou",
+ "time_x_hours_ago": "Před {{count}} hodinami",
+ "time_1_day_ago": "Před 1 dnem",
+ "time_x_days_ago": "Před {{count}} dny",
+ "time_1_month_ago": "Před 1 měsícem",
+ "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"
}
\ No newline at end of file
diff --git a/public/translations/da/default.json b/public/translations/da/default.json
index 943201a0..8698e067 100644
--- a/public/translations/da/default.json
+++ b/public/translations/da/default.json
@@ -148,5 +148,16 @@
"close_thread": "luk tråd",
"view_spoiler": "vis spoiler",
"filter": "filtrér",
- "edit": "rediger"
+ "edit": "rediger",
+ "time_1_minute_ago": "1 minut siden",
+ "time_x_minutes_ago": "{{count}} minutter siden",
+ "time_1_hour_ago": "1 time siden",
+ "time_x_hours_ago": "{{count}} timer siden",
+ "time_1_day_ago": "1 dag siden",
+ "time_x_days_ago": "{{count}} dage siden",
+ "time_1_month_ago": "1 måned siden",
+ "time_x_months_ago": "{{count}} måneder siden",
+ "time_1_year_ago": "1 år siden",
+ "time_x_years_ago": "{{count}} år siden",
+ "open": "åben"
}
\ No newline at end of file
diff --git a/public/translations/de/default.json b/public/translations/de/default.json
index 6c071bc6..c2b93b71 100644
--- a/public/translations/de/default.json
+++ b/public/translations/de/default.json
@@ -148,5 +148,16 @@
"close_thread": "Thread schließen",
"view_spoiler": "Spoiler anzeigen",
"filter": "filtern",
- "edit": "bearbeiten"
+ "edit": "bearbeiten",
+ "time_1_minute_ago": "vor 1 Minute",
+ "time_x_minutes_ago": "vor {{count}} Minuten",
+ "time_1_hour_ago": "vor 1 Stunde",
+ "time_x_hours_ago": "vor {{count}} Stunden",
+ "time_1_day_ago": "vor 1 Tag",
+ "time_x_days_ago": "vor {{count}} Tagen",
+ "time_1_month_ago": "vor 1 Monat",
+ "time_x_months_ago": "vor {{count}} Monaten",
+ "time_1_year_ago": "vor 1 Jahr",
+ "time_x_years_ago": "vor {{count}} Jahren",
+ "open": "öffnen"
}
\ No newline at end of file
diff --git a/public/translations/el/default.json b/public/translations/el/default.json
index 7a4d04af..67c75a5a 100644
--- a/public/translations/el/default.json
+++ b/public/translations/el/default.json
@@ -148,5 +148,16 @@
"close_thread": "κλείσιμο νήματος",
"view_spoiler": "προβολή spoiler",
"filter": "φίλτρο",
- "edit": "επεξεργασία"
+ "edit": "επεξεργασία",
+ "time_1_minute_ago": "Πριν από 1 λεπτό",
+ "time_x_minutes_ago": "Πριν από {{count}} λεπτά",
+ "time_1_hour_ago": "Πριν από 1 ώρα",
+ "time_x_hours_ago": "Πριν από {{count}} ώρες",
+ "time_1_day_ago": "Πριν από 1 ημέρα",
+ "time_x_days_ago": "Πριν από {{count}} ημέρες",
+ "time_1_month_ago": "Πριν από 1 μήνα",
+ "time_x_months_ago": "Πριν από {{count}} μήνες",
+ "time_1_year_ago": "Πριν από 1 έτος",
+ "time_x_years_ago": "Πριν από {{count}} έτη",
+ "open": "ανοικτό"
}
\ No newline at end of file
diff --git a/public/translations/en/default.json b/public/translations/en/default.json
index 4f44f528..257c4e19 100644
--- a/public/translations/en/default.json
+++ b/public/translations/en/default.json
@@ -148,5 +148,16 @@
"close_thread": "close thread",
"view_spoiler": "view spoiler",
"filter": "filter",
- "edit": "edit"
+ "edit": "edit",
+ "time_1_minute_ago": "1 minute ago",
+ "time_x_minutes_ago": "{{count}} minutes ago",
+ "time_1_hour_ago": "1 hour ago",
+ "time_x_hours_ago": "{{count}} hours ago",
+ "time_1_day_ago": "1 day ago",
+ "time_x_days_ago": "{{count}} days ago",
+ "time_1_month_ago": "1 month ago",
+ "time_x_months_ago": "{{count}} months ago",
+ "time_1_year_ago": "1 year ago",
+ "time_x_years_ago": "{{count}} years ago",
+ "open": "open"
}
\ No newline at end of file
diff --git a/public/translations/es/default.json b/public/translations/es/default.json
index 02d0f10c..d00027f5 100644
--- a/public/translations/es/default.json
+++ b/public/translations/es/default.json
@@ -148,5 +148,16 @@
"close_thread": "cerrar hilo",
"view_spoiler": "ver spoiler",
"filter": "filtrar",
- "edit": "editar"
+ "edit": "editar",
+ "time_1_minute_ago": "hace 1 minuto",
+ "time_x_minutes_ago": "hace {{count}} minutos",
+ "time_1_hour_ago": "hace 1 hora",
+ "time_x_hours_ago": "hace {{count}} horas",
+ "time_1_day_ago": "hace 1 día",
+ "time_x_days_ago": "hace {{count}} días",
+ "time_1_month_ago": "hace 1 mes",
+ "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"
}
\ No newline at end of file
diff --git a/public/translations/fa/default.json b/public/translations/fa/default.json
index 15d8aac7..853da3b1 100644
--- a/public/translations/fa/default.json
+++ b/public/translations/fa/default.json
@@ -148,5 +148,16 @@
"close_thread": "بستن موضوع",
"view_spoiler": "نمایش اسپویلر",
"filter": "فیلتر",
- "edit": "ویرایش"
+ "edit": "ویرایش",
+ "time_1_minute_ago": "1 دقیقه پیش",
+ "time_x_minutes_ago": "{{count}} دقیقه پیش",
+ "time_1_hour_ago": "1 ساعت پیش",
+ "time_x_hours_ago": "{{count}} ساعت پیش",
+ "time_1_day_ago": "1 روز پیش",
+ "time_x_days_ago": "{{count}} روز پیش",
+ "time_1_month_ago": "1 ماه پیش",
+ "time_x_months_ago": "{{count}} ماه پیش",
+ "time_1_year_ago": "1 سال پیش",
+ "time_x_years_ago": "{{count}} سال پیش",
+ "open": "باز کردن"
}
\ No newline at end of file
diff --git a/public/translations/fi/default.json b/public/translations/fi/default.json
index efaf244a..2543a863 100644
--- a/public/translations/fi/default.json
+++ b/public/translations/fi/default.json
@@ -148,5 +148,16 @@
"close_thread": "sulje keskustelu",
"view_spoiler": "näytä spoiler",
"filter": "suodatin",
- "edit": "muokkaa"
+ "edit": "muokkaa",
+ "time_1_minute_ago": "1 minuutti sitten",
+ "time_x_minutes_ago": "{{count}} minuuttia sitten",
+ "time_1_hour_ago": "1 tunti sitten",
+ "time_x_hours_ago": "{{count}} tuntia sitten",
+ "time_1_day_ago": "1 päivä sitten",
+ "time_x_days_ago": "{{count}} päivää sitten",
+ "time_1_month_ago": "1 kuukausi sitten",
+ "time_x_months_ago": "{{count}} kuukautta sitten",
+ "time_1_year_ago": "1 vuosi sitten",
+ "time_x_years_ago": "{{count}} vuotta sitten",
+ "open": "avata"
}
\ No newline at end of file
diff --git a/public/translations/fil/default.json b/public/translations/fil/default.json
index 4e100d83..9513962e 100644
--- a/public/translations/fil/default.json
+++ b/public/translations/fil/default.json
@@ -148,5 +148,16 @@
"close_thread": "isara ang thread",
"view_spoiler": "tingnan ang spoiler",
"filter": "salain",
- "edit": "i-edit"
+ "edit": "i-edit",
+ "time_1_minute_ago": "1 minuto ang nakalipas",
+ "time_x_minutes_ago": "{{count}} minuto ang nakalipas",
+ "time_1_hour_ago": "1 oras ang nakalipas",
+ "time_x_hours_ago": "{{count}} oras ang nakalipas",
+ "time_1_day_ago": "1 araw ang nakalipas",
+ "time_x_days_ago": "{{count}} araw ang nakalipas",
+ "time_1_month_ago": "1 buwan ang nakalipas",
+ "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"
}
\ No newline at end of file
diff --git a/public/translations/fr/default.json b/public/translations/fr/default.json
index 181b41bd..b8d41811 100644
--- a/public/translations/fr/default.json
+++ b/public/translations/fr/default.json
@@ -148,5 +148,16 @@
"close_thread": "fermer le fil",
"view_spoiler": "voir le spoiler",
"filter": "filtre",
- "edit": "modifier"
+ "edit": "modifier",
+ "time_1_minute_ago": "il y a 1 minute",
+ "time_x_minutes_ago": "il y a {{count}} minutes",
+ "time_1_hour_ago": "il y a 1 heure",
+ "time_x_hours_ago": "il y a {{count}} heures",
+ "time_1_day_ago": "il y a 1 jour",
+ "time_x_days_ago": "il y a {{count}} jours",
+ "time_1_month_ago": "il y a 1 mois",
+ "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"
}
\ No newline at end of file
diff --git a/public/translations/he/default.json b/public/translations/he/default.json
index 53d94cc5..93748454 100644
--- a/public/translations/he/default.json
+++ b/public/translations/he/default.json
@@ -148,5 +148,16 @@
"close_thread": "סגור את הנושא",
"view_spoiler": "הצג ספוילר",
"filter": "סנן",
- "edit": "ערוך"
+ "edit": "ערוך",
+ "time_1_minute_ago": "לפני דקה",
+ "time_x_minutes_ago": "לפני {{count}} דקות",
+ "time_1_hour_ago": "לפני שעה",
+ "time_x_hours_ago": "לפני {{count}} שעות",
+ "time_1_day_ago": "לפני יום",
+ "time_x_days_ago": "לפני {{count}} ימים",
+ "time_1_month_ago": "לפני חודש",
+ "time_x_months_ago": "לפני {{count}} חודשים",
+ "time_1_year_ago": "לפני שנה",
+ "time_x_years_ago": "לפני {{count}} שנים",
+ "open": "לפתוח"
}
\ No newline at end of file
diff --git a/public/translations/hi/default.json b/public/translations/hi/default.json
index e7a8fd70..a9263349 100644
--- a/public/translations/hi/default.json
+++ b/public/translations/hi/default.json
@@ -148,5 +148,16 @@
"close_thread": "थ्रेड बंद करें",
"view_spoiler": "स्पॉइलर देखें",
"filter": "फ़िल्टर",
- "edit": "संपादित करें"
+ "edit": "संपादित करें",
+ "time_1_minute_ago": "1 मिनट पहले",
+ "time_x_minutes_ago": "{{count}} मिनट पहले",
+ "time_1_hour_ago": "1 घंटा पहले",
+ "time_x_hours_ago": "{{count}} घंटे पहले",
+ "time_1_day_ago": "1 दिन पहले",
+ "time_x_days_ago": "{{count}} दिन पहले",
+ "time_1_month_ago": "1 महीना पहले",
+ "time_x_months_ago": "{{count}} महीने पहले",
+ "time_1_year_ago": "1 साल पहले",
+ "time_x_years_ago": "{{count}} साल पहले",
+ "open": "खोलें"
}
\ No newline at end of file
diff --git a/public/translations/hu/default.json b/public/translations/hu/default.json
index d28f4bde..3f30df50 100644
--- a/public/translations/hu/default.json
+++ b/public/translations/hu/default.json
@@ -148,5 +148,16 @@
"close_thread": "szál bezárása",
"view_spoiler": "spoiler megtekintése",
"filter": "szűrő",
- "edit": "szerkesztés"
+ "edit": "szerkesztés",
+ "time_1_minute_ago": "1 perce",
+ "time_x_minutes_ago": "{{count}} perce",
+ "time_1_hour_ago": "1 órája",
+ "time_x_hours_ago": "{{count}} órája",
+ "time_1_day_ago": "1 napja",
+ "time_x_days_ago": "{{count}} napja",
+ "time_1_month_ago": "1 hónapja",
+ "time_x_months_ago": "{{count}} hónapja",
+ "time_1_year_ago": "1 éve",
+ "time_x_years_ago": "{{count}} éve",
+ "open": "nyit"
}
\ No newline at end of file
diff --git a/public/translations/id/default.json b/public/translations/id/default.json
index 057ae59f..17aef358 100644
--- a/public/translations/id/default.json
+++ b/public/translations/id/default.json
@@ -148,5 +148,16 @@
"close_thread": "tutup utas",
"view_spoiler": "lihat spoiler",
"filter": "menyaring",
- "edit": "sunting"
+ "edit": "sunting",
+ "time_1_minute_ago": "1 menit yang lalu",
+ "time_x_minutes_ago": "{{count}} menit yang lalu",
+ "time_1_hour_ago": "1 jam yang lalu",
+ "time_x_hours_ago": "{{count}} jam yang lalu",
+ "time_1_day_ago": "1 hari yang lalu",
+ "time_x_days_ago": "{{count}} hari yang lalu",
+ "time_1_month_ago": "1 bulan yang lalu",
+ "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"
}
\ No newline at end of file
diff --git a/public/translations/it/default.json b/public/translations/it/default.json
index acae16c1..58d54e49 100644
--- a/public/translations/it/default.json
+++ b/public/translations/it/default.json
@@ -148,5 +148,16 @@
"close_thread": "chiudi il thread",
"view_spoiler": "mostra spoiler",
"filter": "filtro",
- "edit": "modifica"
+ "edit": "modifica",
+ "time_1_minute_ago": "1 minuto fa",
+ "time_x_minutes_ago": "{{count}} minuti fa",
+ "time_1_hour_ago": "1 ora fa",
+ "time_x_hours_ago": "{{count}} ore fa",
+ "time_1_day_ago": "1 giorno fa",
+ "time_x_days_ago": "{{count}} giorni fa",
+ "time_1_month_ago": "1 mese fa",
+ "time_x_months_ago": "{{count}} mesi fa",
+ "time_1_year_ago": "1 anno fa",
+ "time_x_years_ago": "{{count}} anni fa",
+ "open": "apri"
}
\ No newline at end of file
diff --git a/public/translations/ja/default.json b/public/translations/ja/default.json
index 0fcec985..03b72bdf 100644
--- a/public/translations/ja/default.json
+++ b/public/translations/ja/default.json
@@ -148,5 +148,16 @@
"close_thread": "スレッドを閉じる",
"view_spoiler": "スポイラーを表示",
"filter": "フィルター",
- "edit": "編集"
+ "edit": "編集",
+ "time_1_minute_ago": "1分前",
+ "time_x_minutes_ago": "{{count}}分前",
+ "time_1_hour_ago": "1時間前",
+ "time_x_hours_ago": "{{count}}時間前",
+ "time_1_day_ago": "1日前",
+ "time_x_days_ago": "{{count}}日前",
+ "time_1_month_ago": "1ヶ月前",
+ "time_x_months_ago": "{{count}}ヶ月前",
+ "time_1_year_ago": "1年前",
+ "time_x_years_ago": "{{count}}年前",
+ "open": "開く"
}
\ No newline at end of file
diff --git a/public/translations/ko/default.json b/public/translations/ko/default.json
index e933a350..3e11fa24 100644
--- a/public/translations/ko/default.json
+++ b/public/translations/ko/default.json
@@ -148,5 +148,16 @@
"close_thread": "스레드 닫기",
"view_spoiler": "스포일러 보기",
"filter": "필터",
- "edit": "편집"
+ "edit": "편집",
+ "time_1_minute_ago": "1분 전",
+ "time_x_minutes_ago": "{{count}}분 전",
+ "time_1_hour_ago": "1시간 전",
+ "time_x_hours_ago": "{{count}}시간 전",
+ "time_1_day_ago": "1일 전",
+ "time_x_days_ago": "{{count}}일 전",
+ "time_1_month_ago": "1개월 전",
+ "time_x_months_ago": "{{count}}개월 전",
+ "time_1_year_ago": "1년 전",
+ "time_x_years_ago": "{{count}}년 전",
+ "open": "열기"
}
\ No newline at end of file
diff --git a/public/translations/mr/default.json b/public/translations/mr/default.json
index 4c8c9501..b4daa7bd 100644
--- a/public/translations/mr/default.json
+++ b/public/translations/mr/default.json
@@ -148,5 +148,16 @@
"close_thread": "थ्रेड बंद करा",
"view_spoiler": "स्पॉईलर पहा",
"filter": "फिल्टर",
- "edit": "संपादित करा"
+ "edit": "संपादित करा",
+ "time_1_minute_ago": "१ मिनिटांपूर्वी",
+ "time_x_minutes_ago": "{{count}} मिनिटांपूर्वी",
+ "time_1_hour_ago": "१ तासांपूर्वी",
+ "time_x_hours_ago": "{{count}} तासांपूर्वी",
+ "time_1_day_ago": "१ दिवसांपूर्वी",
+ "time_x_days_ago": "{{count}} दिवसांपूर्वी",
+ "time_1_month_ago": "१ महिन्यांपूर्वी",
+ "time_x_months_ago": "{{count}} महिन्यांपूर्वी",
+ "time_1_year_ago": "१ वर्षांपूर्वी",
+ "time_x_years_ago": "{{count}} वर्षांपूर्वी",
+ "open": "उघडा"
}
\ No newline at end of file
diff --git a/public/translations/nl/default.json b/public/translations/nl/default.json
index 9e1fa096..e7917b3b 100644
--- a/public/translations/nl/default.json
+++ b/public/translations/nl/default.json
@@ -148,5 +148,16 @@
"close_thread": "draad sluiten",
"view_spoiler": "bekijk spoiler",
"filter": "filter",
- "edit": "bewerken"
+ "edit": "bewerken",
+ "time_1_minute_ago": "1 minuut geleden",
+ "time_x_minutes_ago": "{{count}} minuten geleden",
+ "time_1_hour_ago": "1 uur geleden",
+ "time_x_hours_ago": "{{count}} uur geleden",
+ "time_1_day_ago": "1 dag geleden",
+ "time_x_days_ago": "{{count}} dagen geleden",
+ "time_1_month_ago": "1 maand geleden",
+ "time_x_months_ago": "{{count}} maanden geleden",
+ "time_1_year_ago": "1 jaar geleden",
+ "time_x_years_ago": "{{count}} jaar geleden",
+ "open": "openen"
}
\ No newline at end of file
diff --git a/public/translations/no/default.json b/public/translations/no/default.json
index 8f1d6f74..834919ca 100644
--- a/public/translations/no/default.json
+++ b/public/translations/no/default.json
@@ -148,5 +148,16 @@
"close_thread": "lukk tråd",
"view_spoiler": "se spoiler",
"filter": "filter",
- "edit": "rediger"
+ "edit": "rediger",
+ "time_1_minute_ago": "1 minutt siden",
+ "time_x_minutes_ago": "{{count}} minutter siden",
+ "time_1_hour_ago": "1 time siden",
+ "time_x_hours_ago": "{{count}} timer siden",
+ "time_1_day_ago": "1 dag siden",
+ "time_x_days_ago": "{{count}} dager siden",
+ "time_1_month_ago": "1 måned siden",
+ "time_x_months_ago": "{{count}} måneder siden",
+ "time_1_year_ago": "1 år siden",
+ "time_x_years_ago": "{{count}} år siden",
+ "open": "åpne"
}
\ No newline at end of file
diff --git a/public/translations/pl/default.json b/public/translations/pl/default.json
index 60396994..1fe47c61 100644
--- a/public/translations/pl/default.json
+++ b/public/translations/pl/default.json
@@ -148,5 +148,16 @@
"close_thread": "zamknij wątek",
"view_spoiler": "pokaż spoiler",
"filter": "filtr",
- "edit": "edytuj"
+ "edit": "edytuj",
+ "time_1_minute_ago": "1 minutę temu",
+ "time_x_minutes_ago": "{{count}} minut temu",
+ "time_1_hour_ago": "1 godzinę temu",
+ "time_x_hours_ago": "{{count}} godzin temu",
+ "time_1_day_ago": "1 dzień temu",
+ "time_x_days_ago": "{{count}} dni temu",
+ "time_1_month_ago": "1 miesiąc temu",
+ "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"
}
\ No newline at end of file
diff --git a/public/translations/pt/default.json b/public/translations/pt/default.json
index 8569b299..aa6e2925 100644
--- a/public/translations/pt/default.json
+++ b/public/translations/pt/default.json
@@ -148,5 +148,16 @@
"close_thread": "fechar tópico",
"view_spoiler": "ver spoiler",
"filter": "filtro",
- "edit": "editar"
+ "edit": "editar",
+ "time_1_minute_ago": "há 1 minuto",
+ "time_x_minutes_ago": "há {{count}} minutos",
+ "time_1_hour_ago": "há 1 hora",
+ "time_x_hours_ago": "há {{count}} horas",
+ "time_1_day_ago": "há 1 dia",
+ "time_x_days_ago": "há {{count}} dias",
+ "time_1_month_ago": "há 1 mês",
+ "time_x_months_ago": "há {{count}} meses",
+ "time_1_year_ago": "há 1 ano",
+ "time_x_years_ago": "há {{count}} anos",
+ "open": "abrir"
}
\ No newline at end of file
diff --git a/public/translations/ro/default.json b/public/translations/ro/default.json
index 9eb8de50..a04f133c 100644
--- a/public/translations/ro/default.json
+++ b/public/translations/ro/default.json
@@ -148,5 +148,16 @@
"close_thread": "închide firul",
"view_spoiler": "vezi spoilerul",
"filter": "filtru",
- "edit": "editați"
+ "edit": "editați",
+ "time_1_minute_ago": "Acum 1 minut",
+ "time_x_minutes_ago": "Acum {{count}} minute",
+ "time_1_hour_ago": "Acum 1 oră",
+ "time_x_hours_ago": "Acum {{count}} ore",
+ "time_1_day_ago": "Acum 1 zi",
+ "time_x_days_ago": "Acum {{count}} zile",
+ "time_1_month_ago": "Acum 1 lună",
+ "time_x_months_ago": "Acum {{count}} luni",
+ "time_1_year_ago": "Acum 1 an",
+ "time_x_years_ago": "Acum {{count}} ani",
+ "open": "deschis"
}
\ No newline at end of file
diff --git a/public/translations/ru/default.json b/public/translations/ru/default.json
index 3c3ba51e..5b66383c 100644
--- a/public/translations/ru/default.json
+++ b/public/translations/ru/default.json
@@ -148,5 +148,16 @@
"close_thread": "закрыть тему",
"view_spoiler": "посмотреть спойлер",
"filter": "фильтр",
- "edit": "редактировать"
+ "edit": "редактировать",
+ "time_1_minute_ago": "1 минуту назад",
+ "time_x_minutes_ago": "{{count}} минут назад",
+ "time_1_hour_ago": "1 час назад",
+ "time_x_hours_ago": "{{count}} часов назад",
+ "time_1_day_ago": "1 день назад",
+ "time_x_days_ago": "{{count}} дней назад",
+ "time_1_month_ago": "1 месяц назад",
+ "time_x_months_ago": "{{count}} месяцев назад",
+ "time_1_year_ago": "1 год назад",
+ "time_x_years_ago": "{{count}} лет назад",
+ "open": "открыть"
}
\ No newline at end of file
diff --git a/public/translations/sq/default.json b/public/translations/sq/default.json
index 5af61728..ce9fef24 100644
--- a/public/translations/sq/default.json
+++ b/public/translations/sq/default.json
@@ -148,5 +148,16 @@
"close_thread": "mbyll temën",
"view_spoiler": "shikoni spoiler",
"filter": "filtruar",
- "edit": "redakto"
+ "edit": "redakto",
+ "time_1_minute_ago": "1 minutë më parë",
+ "time_x_minutes_ago": "{{count}} minuta më parë",
+ "time_1_hour_ago": "1 orë më parë",
+ "time_x_hours_ago": "{{count}} orë më parë",
+ "time_1_day_ago": "1 ditë më parë",
+ "time_x_days_ago": "{{count}} ditë më parë",
+ "time_1_month_ago": "1 muaj më parë",
+ "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"
}
\ No newline at end of file
diff --git a/public/translations/sv/default.json b/public/translations/sv/default.json
index c62474e2..092e4336 100644
--- a/public/translations/sv/default.json
+++ b/public/translations/sv/default.json
@@ -148,5 +148,16 @@
"close_thread": "stäng tråd",
"view_spoiler": "visa spoiler",
"filter": "filter",
- "edit": "redigera"
+ "edit": "redigera",
+ "time_1_minute_ago": "För 1 minut sedan",
+ "time_x_minutes_ago": "För {{count}} minuter sedan",
+ "time_1_hour_ago": "För 1 timme sedan",
+ "time_x_hours_ago": "För {{count}} timmar sedan",
+ "time_1_day_ago": "För 1 dag sedan",
+ "time_x_days_ago": "För {{count}} dagar sedan",
+ "time_1_month_ago": "För 1 månad sedan",
+ "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"
}
\ No newline at end of file
diff --git a/public/translations/te/default.json b/public/translations/te/default.json
index 22a39f5f..214ff344 100644
--- a/public/translations/te/default.json
+++ b/public/translations/te/default.json
@@ -148,5 +148,16 @@
"close_thread": "థ్రెడ్ను మూసేయండి",
"view_spoiler": "స్పోయ్లర్ వీక్షించండి",
"filter": "వడపోత",
- "edit": "సవరించు"
+ "edit": "సవరించు",
+ "time_1_minute_ago": "1 నిమిషం క్రితం",
+ "time_x_minutes_ago": "{{count}} నిమిషాల క్రితం",
+ "time_1_hour_ago": "1 గంట క్రితం",
+ "time_x_hours_ago": "{{count}} గంటల క్రితం",
+ "time_1_day_ago": "1 రోజు క్రితం",
+ "time_x_days_ago": "{{count}} రోజుల క్రితం",
+ "time_1_month_ago": "1 నెల క్రితం",
+ "time_x_months_ago": "{{count}} నెలల క్రితం",
+ "time_1_year_ago": "1 సంవత్సరం క్రితం",
+ "time_x_years_ago": "{{count}} సంవత్సరాల క్రితం",
+ "open": "తెరవండి"
}
\ No newline at end of file
diff --git a/public/translations/th/default.json b/public/translations/th/default.json
index dcd1242c..ff3a3b1d 100644
--- a/public/translations/th/default.json
+++ b/public/translations/th/default.json
@@ -148,5 +148,16 @@
"close_thread": "ปิดกระทู้",
"view_spoiler": "ดูสปอยล์เลอร์",
"filter": "ตัวกรอง",
- "edit": "แก้ไข"
+ "edit": "แก้ไข",
+ "time_1_minute_ago": "1 นาทีที่ผ่านมา",
+ "time_x_minutes_ago": "{{count}} นาทีที่ผ่านมา",
+ "time_1_hour_ago": "1 ชั่วโมงที่ผ่านมา",
+ "time_x_hours_ago": "{{count}} ชั่วโมงที่ผ่านมา",
+ "time_1_day_ago": "1 วันที่ผ่านมา",
+ "time_x_days_ago": "{{count}} วันที่ผ่านมา",
+ "time_1_month_ago": "1 เดือนที่ผ่านมา",
+ "time_x_months_ago": "{{count}} เดือนที่ผ่านมา",
+ "time_1_year_ago": "1 ปีที่ผ่านมา",
+ "time_x_years_ago": "{{count}} ปีที่ผ่านมา",
+ "open": "เปิด"
}
\ No newline at end of file
diff --git a/public/translations/tr/default.json b/public/translations/tr/default.json
index 3db66cf7..3f250145 100644
--- a/public/translations/tr/default.json
+++ b/public/translations/tr/default.json
@@ -148,5 +148,16 @@
"close_thread": "konuyu kapat",
"view_spoiler": "spoileri görüntüle",
"filter": "filtre",
- "edit": "düzenle"
+ "edit": "düzenle",
+ "time_1_minute_ago": "1 dakika önce",
+ "time_x_minutes_ago": "{{count}} dakika önce",
+ "time_1_hour_ago": "1 saat önce",
+ "time_x_hours_ago": "{{count}} saat önce",
+ "time_1_day_ago": "1 gün önce",
+ "time_x_days_ago": "{{count}} gün önce",
+ "time_1_month_ago": "1 ay önce",
+ "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ç"
}
\ No newline at end of file
diff --git a/public/translations/uk/default.json b/public/translations/uk/default.json
index c0b4e7b0..1ffe4b5a 100644
--- a/public/translations/uk/default.json
+++ b/public/translations/uk/default.json
@@ -148,5 +148,16 @@
"close_thread": "закрити тему",
"view_spoiler": "переглянути спойлер",
"filter": "фільтр",
- "edit": "редагувати"
+ "edit": "редагувати",
+ "time_1_minute_ago": "1 хвилину тому",
+ "time_x_minutes_ago": "{{count}} хвилин тому",
+ "time_1_hour_ago": "1 годину тому",
+ "time_x_hours_ago": "{{count}} годин тому",
+ "time_1_day_ago": "1 день тому",
+ "time_x_days_ago": "{{count}} днів тому",
+ "time_1_month_ago": "1 місяць тому",
+ "time_x_months_ago": "{{count}} місяців тому",
+ "time_1_year_ago": "1 рік тому",
+ "time_x_years_ago": "{{count}} років тому",
+ "open": "відкрити"
}
\ No newline at end of file
diff --git a/public/translations/ur/default.json b/public/translations/ur/default.json
index 1d7038ba..71cc12bb 100644
--- a/public/translations/ur/default.json
+++ b/public/translations/ur/default.json
@@ -148,5 +148,16 @@
"close_thread": "موضوع بند کریں",
"view_spoiler": "اسپوائلر دیکھیں",
"filter": "فلٹر",
- "edit": "ترمیم کریں"
+ "edit": "ترمیم کریں",
+ "time_1_minute_ago": "1 منٹ پہلے",
+ "time_x_minutes_ago": "{{count}} منٹ پہلے",
+ "time_1_hour_ago": "1 گھنٹہ پہلے",
+ "time_x_hours_ago": "{{count}} گھنٹے پہلے",
+ "time_1_day_ago": "1 دن پہلے",
+ "time_x_days_ago": "{{count}} دن پہلے",
+ "time_1_month_ago": "1 مہینہ پہلے",
+ "time_x_months_ago": "{{count}} مہینے پہلے",
+ "time_1_year_ago": "1 سال پہلے",
+ "time_x_years_ago": "{{count}} سال پہلے",
+ "open": "کھولیں"
}
\ No newline at end of file
diff --git a/public/translations/vi/default.json b/public/translations/vi/default.json
index 0bc003fe..cd0d8ce9 100644
--- a/public/translations/vi/default.json
+++ b/public/translations/vi/default.json
@@ -148,5 +148,16 @@
"close_thread": "đóng chủ đề",
"view_spoiler": "xem spoiler",
"filter": "bộ lọc",
- "edit": "chỉnh sửa"
+ "edit": "chỉnh sửa",
+ "time_1_minute_ago": "1 phút trước",
+ "time_x_minutes_ago": "{{count}} phút trước",
+ "time_1_hour_ago": "1 giờ trước",
+ "time_x_hours_ago": "{{count}} giờ trước",
+ "time_1_day_ago": "1 ngày trước",
+ "time_x_days_ago": "{{count}} ngày trước",
+ "time_1_month_ago": "1 tháng trước",
+ "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ở"
}
\ No newline at end of file
diff --git a/public/translations/zh/default.json b/public/translations/zh/default.json
index 1a6d9e23..83a3dbe6 100644
--- a/public/translations/zh/default.json
+++ b/public/translations/zh/default.json
@@ -148,5 +148,16 @@
"close_thread": "关闭主题",
"view_spoiler": "查看剧透",
"filter": "过滤器",
- "edit": "编辑"
+ "edit": "编辑",
+ "time_1_minute_ago": "1分钟前",
+ "time_x_minutes_ago": "{{count}}分钟前",
+ "time_1_hour_ago": "1小时前",
+ "time_x_hours_ago": "{{count}}小时前",
+ "time_1_day_ago": "1天前",
+ "time_x_days_ago": "{{count}}天前",
+ "time_1_month_ago": "1个月前",
+ "time_x_months_ago": "{{count}}个月前",
+ "time_1_year_ago": "1年前",
+ "time_x_years_ago": "{{count}}年前",
+ "open": "打开"
}
\ No newline at end of file
diff --git a/src/app.tsx b/src/app.tsx
index 9d8fa691..4bc0d54e 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -1,6 +1,6 @@
import { useEffect } from 'react';
import { Outlet, Route, Routes, useLocation, useParams } from 'react-router-dom';
-import { isHomeView } from './lib/utils/view-utils';
+import { isAllView, isHomeView, isSubscriptionsView } from './lib/utils/view-utils';
import useIsMobile from './hooks/use-is-mobile';
import useTheme from './hooks/use-theme';
import styles from './app.module.css';
@@ -21,6 +21,8 @@ const BoardLayout = () => {
const { accountCommentIndex, subplebbitAddress } = useParams();
const location = useLocation();
const isMobile = useIsMobile();
+ const isInAllView = isAllView(location.pathname);
+ const isInSubscriptionsView = isSubscriptionsView(location.pathname);
const isValidAccountCommentIndex = !accountCommentIndex || (!isNaN(parseInt(accountCommentIndex)) && parseInt(accountCommentIndex) >= 0);
@@ -36,16 +38,16 @@ const BoardLayout = () => {
{isMobile
- ? subplebbitAddress && (
+ ? (subplebbitAddress || isInAllView || isInSubscriptionsView) && (
<>
>
)
- : subplebbitAddress && (
+ : (subplebbitAddress || isInAllView || isInSubscriptionsView) && (
<>
-
+ {!(isInAllView || isInSubscriptionsView) && }
>
)}
diff --git a/src/components/catalog-row/catalog-row.module.css b/src/components/catalog-row/catalog-row.module.css
index e97bb7c5..66259f7e 100644
--- a/src/components/catalog-row/catalog-row.module.css
+++ b/src/components/catalog-row/catalog-row.module.css
@@ -83,8 +83,8 @@
visibility: hidden;
}
-.post:hover .postMenu {
- visibility: visible;
+.postMenuVisible {
+ visibility: visible !important;
}
.loadingSkeleton {
@@ -95,4 +95,37 @@
.fileDeleted {
image-rendering: pixelated;
+}
+
+.postPreview {
+ background-color: var(--catalog-post-preview-background-color);
+ border-radius: 3px;
+ padding: 5px 8px 4px;
+ z-index: 100000;
+ word-wrap: break-word;
+ white-space: pre-line;
+ max-width: 500px;
+ color: var(--catalog-post-preview-color);
+ font-family: arial, helvetica, sans-serif;
+ font-size: 10pt;
+}
+
+.postPreview .postSubject {
+ font-weight: 700;
+ color: var(--catalog-post-preview-subject-color);
+}
+
+.postPreview .postAuthor {
+ color: var(--catalog-post-preview-author-color);
+ font-weight: 700;
+}
+
+.postPreview .postLast {
+ color: var(--catalog-post-preview-last-color);
+ font-size: 90%;
+ margin-top: 3px;
+}
+
+.postPreview .capcode {
+ color: var(--catalog-post-preview-author-capcode-color);
}
\ No newline at end of file
diff --git a/src/components/catalog-row/catalog-row.tsx b/src/components/catalog-row/catalog-row.tsx
index 85c92292..05f102f2 100644
--- a/src/components/catalog-row/catalog-row.tsx
+++ b/src/components/catalog-row/catalog-row.tsx
@@ -1,14 +1,18 @@
-import { Link, useLocation } from 'react-router-dom';
+import { useEffect, useRef, useState } from 'react';
+import { createPortal } from 'react-dom';
import { useTranslation } from 'react-i18next';
-import { Comment } from '@plebbit/plebbit-react-hooks';
-import styles from './catalog-row.module.css';
+import { Link, useLocation } from 'react-router-dom';
+import { Comment, useComment } from '@plebbit/plebbit-react-hooks';
+import { useFloating, offset, shift, size, autoUpdate, Placement } from '@floating-ui/react';
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
+import { getFormattedTimeAgo } from '../../lib/utils/time-utils';
import { isAllView } from '../../lib/utils/view-utils';
import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
+import useEditCommentPrivileges from '../../hooks/use-author-privileges';
import PostMenuDesktop from '../post/post-desktop/post-menu-desktop/';
-
-import React, { useState } from 'react';
+import styles from './catalog-row.module.css';
+import _ from 'lodash';
interface CatalogPostMediaProps {
commentMediaInfo: any;
@@ -69,7 +73,7 @@ export const CatalogPostMedia = ({ commentMediaInfo, isOutOfFeed, linkWidth, lin
return (
- {!isLoaded && !hasError && type !== 'video' &&
}
+ {!isLoaded && !hasError && type !== 'video' && type !== 'audio' &&
}
{hasError ?

: thumbnailComponent}
);
@@ -77,7 +81,8 @@ export const CatalogPostMedia = ({ commentMediaInfo, isOutOfFeed, linkWidth, lin
const CatalogPost = ({ post }: { post: Comment }) => {
const { t } = useTranslation();
- const { cid, content, isDescription, isRules, link, linkHeight, linkWidth, locked, pinned, replyCount, subplebbitAddress, title } = post || {};
+ const { author, cid, content, isDescription, isRules, lastChildCid, link, linkHeight, linkWidth, locked, pinned, replyCount, subplebbitAddress, timestamp, title } =
+ post || {};
const commentMediaInfo = getCommentMediaInfo(post);
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
@@ -95,38 +100,133 @@ const CatalogPost = ({ post }: { post: Comment }) => {
);
- return (
-
- {hasThumbnail ? (
-
-
- {threadIcons}
-
-
-
- ) : (
- threadIcons
- )}
-
- R:
{replyCount || '0'}
- {linkCount > 0 && (
-
- {' '}
- / L: {linkCount}
-
- )}
-
-
-
-
-
-
- {title && `${title}${content ? ': ' : ''}`}
- {content}
-
-
+ const [hoveredCid, setHoveredCid] = useState
(null);
+ const [showPortal, setShowPortal] = useState(false);
+ const placementRef = useRef('right-start');
+ const availableWidthRef = useRef(0);
+ const timeoutRef = useRef(null);
+
+ const { refs, floatingStyles, update } = useFloating({
+ open: showPortal,
+ placement: placementRef.current,
+ middleware: [
+ shift({ padding: 10 }),
+ offset({ mainAxis: 5 }),
+ size({
+ apply({ availableWidth, elements }) {
+ availableWidthRef.current = availableWidth;
+ if (availableWidth >= 250) {
+ elements.floating.style.maxWidth = `${availableWidth - 12}px`;
+ } else if (placementRef.current === 'right-start') {
+ placementRef.current = 'left-start';
+ }
+ },
+ }),
+ ],
+ whileElementsMounted: autoUpdate,
+ });
+
+ useEffect(() => {
+ const handleResize = () => {
+ const availableWidth = availableWidthRef.current;
+ if (availableWidth >= 250) {
+ placementRef.current = 'right-start';
+ } else {
+ placementRef.current = 'left-start';
+ }
+ update();
+ };
+
+ window.addEventListener('resize', handleResize);
+ return () => {
+ window.removeEventListener('resize', handleResize);
+ };
+ }, [update]);
+
+ const lastReply = useComment({ commentCid: lastChildCid });
+
+ const { isCommentAuthorMod: isCatalogPostAuthorMod, commentAuthorRole: catalogPostAuthorRole } = useEditCommentPrivileges({
+ commentAuthorAddress: author?.address,
+ subplebbitAddress,
+ });
+ const { isCommentAuthorMod: isLastReplyAuthorMod, commentAuthorRole: lastReplyAuthorRole } = useEditCommentPrivileges({
+ commentAuthorAddress: lastReply?.author?.address,
+ subplebbitAddress,
+ });
+
+ const postContent = (
+
+ {title && `${title}${content ? ': ' : ''}`}
+ {content}
);
+
+ return (
+ <>
+
+
setHoveredCid(cid)} onMouseLeave={() => setHoveredCid(null)}>
+ {hasThumbnail ? (
+
+
(timeoutRef.current = setTimeout(() => setShowPortal(true), 250))}
+ onMouseLeave={() => {
+ setShowPortal(false);
+ if (timeoutRef.current) {
+ clearTimeout(timeoutRef.current);
+ timeoutRef.current = null;
+ }
+ }}
+ >
+ {threadIcons}
+
+
+
+ ) : (
+ threadIcons
+ )}
+
+ R:
{replyCount || '0'}
+ {linkCount > 0 && (
+
+ {' '}
+ / L: {linkCount}
+
+ )}
+
+
+
+
+ {hasThumbnail ? postContent :
{postContent}}
+
+
+ {hoveredCid === cid &&
+ showPortal &&
+ createPortal(
+
+
{title}
+ {' by '}
+
+ {author?.displayName || _.capitalize(t('anonymous'))}
+ {isCatalogPostAuthorMod && {` ## Board ${catalogPostAuthorRole}`}}
+
+
{getFormattedTimeAgo(timestamp)}
+ {replyCount > 0 && (
+
+ Last reply by{' '}
+
+ {lastReply?.author?.displayName || _.capitalize(t('anonymous'))}
+ {isLastReplyAuthorMod && ` ## Board ${lastReplyAuthorRole}`}
+
+ {getFormattedTimeAgo(lastReply?.timestamp)}
+
+ )}
+
,
+ document.body,
+ )}
+ >
+ );
};
interface CatalogRowProps {
diff --git a/src/components/comment-media/comment-media.module.css b/src/components/comment-media/comment-media.module.css
index dc952788..749db0a5 100644
--- a/src/components/comment-media/comment-media.module.css
+++ b/src/components/comment-media/comment-media.module.css
@@ -11,13 +11,16 @@
overflow: hidden;
}
-.subplebbitAvatar {
+.floatingEmbed, .subplebbitAvatar {
max-width: 250px;
max-height: 250px;
- margin: 3px 20px 5px 0;
display: inline-block;
}
+.subplebbitAvatar {
+ margin: 3px 20px 5px 0;
+}
+
.spoiler {
display: inline-block;
margin: 3px 20px 5px 20px;
@@ -131,4 +134,11 @@
.fileDeleted {
image-rendering: pixelated;
+}
+
+@media (min-width: 640px) {
+ .thumbnail,
+ .content {
+ line-height: 0; /* Ensure no extra space due to line-height */
+ }
}
\ No newline at end of file
diff --git a/src/components/comment-media/comment-media.tsx b/src/components/comment-media/comment-media.tsx
index caa8715b..4852da36 100644
--- a/src/components/comment-media/comment-media.tsx
+++ b/src/components/comment-media/comment-media.tsx
@@ -11,6 +11,7 @@ import Embed, { canEmbed } from '../embed';
interface MediaProps {
commentMediaInfo?: CommentMediaInfo;
post?: Comment;
+ isFloatingEmbed?: boolean;
isReply?: boolean;
linkHeight?: number;
linkWidth?: number;
@@ -18,11 +19,11 @@ interface MediaProps {
setShowThumbnail: (showThumbnail: boolean) => void;
}
-const Thumbnail = ({ commentMediaInfo, post, setShowThumbnail }: MediaProps) => {
+const Thumbnail = ({ commentMediaInfo, isFloatingEmbed, post, setShowThumbnail }: MediaProps) => {
const { t } = useTranslation();
const { deleted, linkHeight, linkWidth, parentCid, removed, spoiler } = post || {};
const { isDescription, isRules } = post || {}; // custom properties, not from api
- const isOutOfFeed = isDescription || isRules; // virtuoso wrapper unneeded
+ const isOutOfFeed = isDescription || isRules || isFloatingEmbed; // virtuoso wrapper unneeded
const isReply = parentCid;
const isDeleted = deleted || removed;
@@ -84,7 +85,7 @@ const Thumbnail = ({ commentMediaInfo, post, setShowThumbnail }: MediaProps) =>
[{t('view_spoiler')}]
) : isOutOfFeed ? (
- {thumbnailComponent}
+ {thumbnailComponent}
) : isMobile || isReply ? (
{thumbnailComponent}
@@ -135,7 +136,7 @@ const Media = ({ commentMediaInfo, isReply, setShowThumbnail }: MediaProps) => {
)}
{isMobile && (type === 'iframe' || type === 'video' || type === 'audio') && (
- setShowThumbnail(true)}>
+ setShowThumbnail(false)}>
{t('close')}
@@ -144,7 +145,7 @@ const Media = ({ commentMediaInfo, isReply, setShowThumbnail }: MediaProps) => {
);
};
-const CommentMedia = ({ commentMediaInfo, post, showThumbnail, setShowThumbnail }: MediaProps) => {
+const CommentMedia = ({ commentMediaInfo, isFloatingEmbed, post, showThumbnail, setShowThumbnail }: MediaProps) => {
const { t } = useTranslation();
const isMobile = useIsMobile();
const { type, url } = commentMediaInfo || {};
@@ -152,7 +153,7 @@ const CommentMedia = ({ commentMediaInfo, post, showThumbnail, setShowThumbnail
return (
- {url && }
+ {url && }
{isMobile && type && {getDisplayMediaInfoType(type, t)}
}
{!showThumbnail && }
diff --git a/src/components/markdown/markdown.module.css b/src/components/markdown/markdown.module.css
index bfdb899c..21dcafb5 100644
--- a/src/components/markdown/markdown.module.css
+++ b/src/components/markdown/markdown.module.css
@@ -38,4 +38,10 @@
.spoiler:hover {
color: white;
+}
+
+.floatingEmbed {
+ background-color: #d6daf0;
+ border: 1px solid #b7c5d9;
+ padding: 2px;
}
\ No newline at end of file
diff --git a/src/components/markdown/markdown.tsx b/src/components/markdown/markdown.tsx
index 1ac29608..84a3ecf5 100644
--- a/src/components/markdown/markdown.tsx
+++ b/src/components/markdown/markdown.tsx
@@ -1,18 +1,100 @@
-import React, { useMemo, useState } from 'react';
+import React, { useEffect, useMemo, useRef, useState } from 'react';
+import { useTranslation } from 'react-i18next';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import supersub from 'remark-supersub';
import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';
-import styles from './markdown.module.css';
-import { useTranslation } from 'react-i18next';
+import { useDismiss, useFloating, useFocus, useHover, useInteractions, offset, shift, size, autoUpdate, Placement, FloatingPortal } from '@floating-ui/react';
import { getLinkMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
+import useIsMobile from '../../hooks/use-is-mobile';
import CommentMedia from '../comment-media';
+import styles from './markdown.module.css';
-interface MarkdownProps {
- content: string;
- spoiler?: boolean;
+interface ContentLinkEmbedProps {
+ children: any;
+ href: string;
+ linkMediaInfo: any;
}
+const ContentLinkEmbed = ({ children, href, linkMediaInfo }: ContentLinkEmbedProps) => {
+ const { t } = useTranslation();
+ const isMobile = useIsMobile();
+ const [isOpen, setIsOpen] = useState(false);
+ const [showMedia, setShowMedia] = useState(false);
+ const placementRef = useRef('right');
+ const availableWidthRef = useRef(0);
+
+ const { refs, floatingStyles, update, context } = useFloating({
+ open: isOpen,
+ onOpenChange: setIsOpen,
+ placement: placementRef.current,
+ middleware: [
+ shift({ padding: 10 }),
+ offset({ mainAxis: 5 }),
+ size({
+ apply({ availableWidth, elements }) {
+ availableWidthRef.current = availableWidth;
+ if (availableWidth >= 250) {
+ elements.floating.style.maxWidth = `${availableWidth - 12}px`;
+ } else if (placementRef.current === 'right') {
+ placementRef.current = 'left';
+ }
+ },
+ }),
+ ],
+ whileElementsMounted: autoUpdate,
+ });
+
+ const hover = useHover(context, { move: false });
+ const focus = useFocus(context);
+ const dismiss = useDismiss(context);
+
+ const { getReferenceProps, getFloatingProps } = useInteractions([hover, focus, dismiss]);
+
+ useEffect(() => {
+ const handleResize = () => {
+ const availableWidth = availableWidthRef.current;
+ if (availableWidth >= 250) {
+ placementRef.current = 'right';
+ } else {
+ placementRef.current = 'left';
+ }
+ update();
+ };
+
+ window.addEventListener('resize', handleResize);
+ return () => {
+ window.removeEventListener('resize', handleResize);
+ };
+ }, [update]);
+
+ return (
+ <>
+
+ {children}
+ {' '}
+ [
+ setShowMedia(!showMedia)} ref={refs.setReference} {...getReferenceProps()}>
+ {showMedia ? t('remove') : isMobile ? t('open') : t('embed')}
+
+ ]
+ {showMedia && (
+ <>
+
+
+ >
+ )}
+
+ {isOpen && !isMobile && (
+
+
+
+ )}
+
+ >
+ );
+};
+
const MAX_LENGTH_FOR_GFM = 10000; // remarkGfm lags with large content
const blockquoteToGreentext = () => (tree: any) => {
@@ -38,6 +120,11 @@ const blockquoteToGreentext = () => (tree: any) => {
});
};
+interface MarkdownProps {
+ content: string;
+ spoiler?: boolean;
+}
+
const Markdown = ({ content, spoiler }: MarkdownProps) => {
const remarkPlugins: any[] = [[supersub]];
@@ -59,9 +146,6 @@ const Markdown = ({ content, spoiler }: MarkdownProps) => {
remarkPlugins.push([blockquoteToGreentext]);
- const { t } = useTranslation();
- const [showMedia, setShowMedia] = useState(false);
-
return (
{
remarkPlugins={remarkPlugins}
rehypePlugins={[[rehypeSanitize, customSchema]]}
components={{
- p: ({ children }) => {spoiler ? {children} : children}
,
+ p: ({ children }) => {spoiler ? {children} : children}
,
img: ({ src }) => {src},
video: ({ src }) => {src},
iframe: ({ src }) => {src},
@@ -83,24 +167,7 @@ const Markdown = ({ content, spoiler }: MarkdownProps) => {
if (href) {
const linkMediaInfo = getLinkMediaInfo(href);
if (getHasThumbnail(linkMediaInfo, href)) {
- return (
- <>
-
- {children}
- {' '}
- [
- setShowMedia(!showMedia)}>
- {showMedia ? t('remove') : t('embed')}
-
- ]
- {showMedia && (
- <>
-
-
- >
- )}
- >
- );
+ return ;
} else {
return (
diff --git a/src/components/post/post-desktop/post-desktop.tsx b/src/components/post/post-desktop/post-desktop.tsx
index eca9442c..eab6903c 100644
--- a/src/components/post/post-desktop/post-desktop.tsx
+++ b/src/components/post/post-desktop/post-desktop.tsx
@@ -5,11 +5,10 @@ import { Comment, useAccount, useBlock, useComment } from '@plebbit/plebbit-reac
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
import styles from '../post.module.css';
import { getCommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail } from '../../../lib/utils/media-utils';
-import { getFormattedDate } from '../../../lib/utils/time-utils';
import { isValidURL } from '../../../lib/utils/url-utils';
import { isAllView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../../lib/utils/view-utils';
import useCountLinksInReplies from '../../../hooks/use-count-links-in-replies';
-import useEditCommentPrivileges from '../../../hooks/use-edit-comment-privileges';
+import useEditCommentPrivileges from '../../../hooks/use-author-privileges';
import useReplies from '../../../hooks/use-replies';
import useStateString from '../../../hooks/use-state-string';
import CommentMedia from '../../comment-media';
@@ -20,6 +19,7 @@ import PostMenuDesktop from './post-menu-desktop/';
import EditMenu from '../edit-menu/edit-menu';
import ReplyQuotePreview from '../reply-quote-preview';
import { PostProps } from '../post';
+import Timestamp from '../timestamp';
import _ from 'lodash';
const PostInfo = ({ openReplyModal, post, roles, isHidden }: PostProps) => {
@@ -58,7 +58,7 @@ const PostInfo = ({ openReplyModal, post, roles, isHidden }: PostProps) => {
{!(isDescription || isRules) && (u/{shortAddress || accountShortAddress}) }
- {getFormattedDate(timestamp)}
+
{isDescription || isRules ? '' : ' '}
@@ -185,7 +185,7 @@ const PostMessage = ({ post }: PostProps) => {
{removed ? (
({t('this_post_was_removed')})
) : deleted ? (
- {t('user_deleted_this_post')}
+ {t('user_deleted_this_post')}
) : (
)}
@@ -232,7 +232,7 @@ const PostDesktop = ({ openReplyModal, post, roles, showAllReplies, showReplies
// scroll to reply if pathname is reply permalink (backlink)
const replyRefs = useRef<(HTMLDivElement | null)[]>([]);
useEffect(() => {
- const replyIndex = replies.findIndex((reply) => location.pathname.startsWith(`/p/${subplebbitAddress}/c/${reply?.cid}`));
+ const replyIndex = replies.findIndex((reply) => location.pathname === `/p/${subplebbitAddress}/c/${reply?.cid}`);
if (replyIndex !== -1 && replyRefs.current[replyIndex]) {
replyRefs.current[replyIndex]?.scrollIntoView();
}
@@ -240,7 +240,7 @@ const PostDesktop = ({ openReplyModal, post, roles, showAllReplies, showReplies
return (
- {showAllReplies ? (
+ {showReplies ? (
@@ -248,7 +248,7 @@ const PostDesktop = ({ openReplyModal, post, roles, showAllReplies, showReplies
)}
- {!isInPostPageView && !isDescription && !isRules && (
+ {!isInPostPageView && !isDescription && !isRules && showReplies && (
@@ -287,7 +287,7 @@ const PostDesktop = ({ openReplyModal, post, roles, showAllReplies, showReplies
(replyRefs.current[index] = el)}>
{'>>'}
-
+
{reply.link && isValidURL(reply.link) &&
}
{reply.content &&
}
diff --git a/src/components/post/post-mobile/post-menu-mobile/post-menu-mobile.module.css b/src/components/post/post-mobile/post-menu-mobile/post-menu-mobile.module.css
index 3f962756..02d261a2 100644
--- a/src/components/post/post-mobile/post-menu-mobile/post-menu-mobile.module.css
+++ b/src/components/post/post-mobile/post-menu-mobile/post-menu-mobile.module.css
@@ -10,6 +10,7 @@
font-weight: 700;
font-size: 16px;
height: 100%;
+ cursor: pointer;
}
.postMenu {
diff --git a/src/components/post/post-mobile/post-menu-mobile/post-menu-mobile.tsx b/src/components/post/post-mobile/post-menu-mobile/post-menu-mobile.tsx
index 18d3aef6..75bd5c94 100644
--- a/src/components/post/post-mobile/post-menu-mobile/post-menu-mobile.tsx
+++ b/src/components/post/post-mobile/post-menu-mobile/post-menu-mobile.tsx
@@ -6,7 +6,7 @@ import { autoUpdate, flip, FloatingFocusManager, offset, shift, useClick, useDis
import styles from './post-menu-mobile.module.css';
import { getCommentMediaInfo } from '../../../../lib/utils/media-utils';
import { copyShareLinkToClipboard, isValidURL } from '../../../../lib/utils/url-utils';
-import useEditCommentPrivileges from '../../../../hooks/use-edit-comment-privileges';
+import useEditCommentPrivileges from '../../../../hooks/use-author-privileges';
import EditMenu from '../../edit-menu/edit-menu';
interface PostMenuMobileProps {
diff --git a/src/components/post/post-mobile/post-mobile.tsx b/src/components/post/post-mobile/post-mobile.tsx
index 61b19dc7..1bd0ac7f 100644
--- a/src/components/post/post-mobile/post-mobile.tsx
+++ b/src/components/post/post-mobile/post-mobile.tsx
@@ -5,7 +5,6 @@ import { Comment, useAccount, useComment } from '@plebbit/plebbit-react-hooks';
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
import styles from '../post.module.css';
import { getCommentMediaInfo, getHasThumbnail } from '../../../lib/utils/media-utils';
-import { getFormattedDate } from '../../../lib/utils/time-utils';
import { isAllView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../../lib/utils/view-utils';
import useCountLinksInReplies from '../../../hooks/use-count-links-in-replies';
import useReplies from '../../../hooks/use-replies';
@@ -16,6 +15,7 @@ import Markdown from '../../markdown';
import ReplyQuotePreview from '../reply-quote-preview';
import PostMenuMobile from './post-menu-mobile';
import { PostProps } from '../post';
+import Timestamp from '../timestamp';
import _ from 'lodash';
const PostInfoAndMedia = ({ openReplyModal, post, roles }: PostProps) => {
@@ -78,7 +78,7 @@ const PostInfoAndMedia = ({ openReplyModal, post, roles }: PostProps) => {
p/{subplebbitAddress && Plebbit.getShortAddress(subplebbitAddress)}
)}
- {getFormattedDate(timestamp)}{' '}
+
{' '}
{!(isDescription || isRules) && (
!cid && e.preventDefault()}>
@@ -117,7 +117,7 @@ const ReplyBacklinks = ({ post }: PostProps) => {
const PostMessageMobile = ({ post }: PostProps) => {
const { t } = useTranslation();
- const { cid, content, deleted, parentCid, postCid, reason, removed, state, subplebbitAddress } = post || {};
+ const { cid, content, deleted, parentCid, postCid, reason, removed, spoiler, state, subplebbitAddress } = post || {};
const params = useParams();
const location = useLocation();
@@ -145,7 +145,7 @@ const PostMessageMobile = ({ post }: PostProps) => {
) : deleted ? (
{t('user_deleted_this_post')}
) : (
-
+
)}
{(removed || deleted) && reason && (
@@ -183,11 +183,10 @@ const PostMobile = ({ openReplyModal, post, roles, showAllReplies, showReplies =
const linksCount = useCountLinksInReplies(post);
const replies = useReplies(post);
+ // scroll to reply if pathname is reply permalink (backlink)
const replyRefs = useRef<(HTMLDivElement | null)[]>([]);
-
useEffect(() => {
- const replyIndex = replies.findIndex((reply) => location.pathname.startsWith(`/p/${subplebbitAddress}/c/${reply?.cid}`));
-
+ const replyIndex = replies.findIndex((reply) => location.pathname === `/p/${subplebbitAddress}/c/${reply?.cid}`);
if (replyIndex !== -1 && replyRefs.current[replyIndex]) {
replyRefs.current[replyIndex]?.scrollIntoView();
}
@@ -195,18 +194,18 @@ const PostMobile = ({ openReplyModal, post, roles, showAllReplies, showReplies =
return (
- {showAllReplies && (
+ {showReplies && (
)}
-
+
- {!isInPostView && !isInPendingPostView && (
+ {!isInPostView && !isInPendingPostView && showReplies && (
{replyCount > 0 && `${replyCount} Replies`}
@@ -233,7 +232,7 @@ const PostMobile = ({ openReplyModal, post, roles, showAllReplies, showReplies =
(replyRefs.current[index] = el)}>
-
+
{reply.content &&
}
diff --git a/src/components/post/post.module.css b/src/components/post/post.module.css
index 25993960..0a9acdfa 100644
--- a/src/components/post/post.module.css
+++ b/src/components/post/post.module.css
@@ -392,12 +392,15 @@
cursor: pointer;
}
-.removedContent {
- text-transform: uppercase;
+.removedContent, .deletedContent {
font-weight: 700;
color: red;
}
+.removedContent {
+ text-transform: uppercase;
+}
+
.backlink, .backlinkHash {
font-size: 0.8em;
}
diff --git a/src/components/post/reply-quote-preview/reply-quote-preview.tsx b/src/components/post/reply-quote-preview/reply-quote-preview.tsx
index c01fa8b0..3873fca4 100644
--- a/src/components/post/reply-quote-preview/reply-quote-preview.tsx
+++ b/src/components/post/reply-quote-preview/reply-quote-preview.tsx
@@ -1,8 +1,7 @@
import { useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { Link } from 'react-router-dom';
-import { Comment } from '@plebbit/plebbit-react-hooks';
-import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
+import { Comment, useAccount } from '@plebbit/plebbit-react-hooks';
import { useFloating, offset, shift, size, autoUpdate, Placement } from '@floating-ui/react';
import useIsMobile from '../../../hooks/use-is-mobile';
import styles from '../post.module.css';
@@ -15,8 +14,32 @@ interface ReplyQuotePreviewProps {
quotelinkReply?: Comment;
}
+const handleQuoteHover = (cid: string, onElementOutOfView: () => void) => {
+ const targetElement = document.getElementById(cid);
+
+ if (!targetElement) return;
+
+ const isInViewport = (element: HTMLElement) => {
+ const bounding = element.getBoundingClientRect();
+ return (
+ bounding.top >= 0 &&
+ bounding.left >= 0 &&
+ bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
+ bounding.right <= (window.innerWidth || document.documentElement.clientWidth)
+ );
+ };
+
+ if (isInViewport(targetElement)) {
+ targetElement.classList.add('highlight');
+ } else {
+ targetElement.classList.remove('highlight');
+ onElementOutOfView();
+ }
+};
+
const DesktopQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, isQuotelinkReply }: ReplyQuotePreviewProps) => {
const [hoveredCid, setHoveredCid] = useState
(null);
+ const [outOfViewCid, setOutOfViewCid] = useState(null);
const placementRef = useRef('right');
const availableWidthRef = useRef(0);
@@ -56,18 +79,37 @@ const DesktopQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, i
};
}, [update]);
+ const handleMouseOver = (cid: string | undefined) => {
+ if (!cid) return;
+
+ handleQuoteHover(cid, () => setOutOfViewCid(cid));
+ setHoveredCid(cid);
+ };
+
+ const handleMouseLeave = (cid: string | null) => {
+ if (cid) {
+ const targetElement = document.getElementById(cid);
+ if (targetElement) {
+ targetElement.classList.remove('highlight');
+ }
+ }
+ setHoveredCid(null);
+ setOutOfViewCid(null);
+ };
+
const replyBacklink = (
<>
setHoveredCid(backlinkReply?.cid)}
- onMouseLeave={() => setHoveredCid(null)}
+ onMouseOver={() => handleMouseOver(backlinkReply?.cid)}
+ onMouseLeave={() => handleMouseLeave(backlinkReply?.cid)}
>
c/{backlinkReply?.shortCid}
{hoveredCid === backlinkReply?.cid &&
+ outOfViewCid === backlinkReply?.cid &&
createPortal(
@@ -77,19 +119,23 @@ const DesktopQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, i
>
);
+ const account = useAccount();
+
const replyQuotelink = (
<>
setHoveredCid(quotelinkReply?.cid)}
- onMouseLeave={() => setHoveredCid(null)}
+ onMouseOver={() => handleMouseOver(quotelinkReply?.cid)}
+ onMouseLeave={() => handleMouseLeave(quotelinkReply?.cid)}
>
- {`c/${quotelinkReply?.cid && Plebbit.getShortCid(quotelinkReply.cid)}`}
+ {`c/${quotelinkReply?.shortCid}`}
+ {quotelinkReply?.author?.address === account?.author?.address && ' (You)'}
{hoveredCid === quotelinkReply?.cid &&
+ outOfViewCid === quotelinkReply?.cid &&
createPortal(
@@ -104,6 +150,7 @@ const DesktopQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, i
const MobileQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, isQuotelinkReply }: ReplyQuotePreviewProps) => {
const [hoveredCid, setHoveredCid] = useState
(null);
+ const [outOfViewCid, setOutOfViewCid] = useState(null);
const { refs, floatingStyles, update } = useFloating({
placement: 'bottom',
@@ -118,15 +165,39 @@ const MobileQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, is
};
}, [update]);
+ const handleMouseOver = (cid: string | undefined) => {
+ if (!cid) return;
+
+ handleQuoteHover(cid, () => setOutOfViewCid(cid));
+ setHoveredCid(cid);
+ };
+
+ const handleMouseLeave = (cid: string | null) => {
+ if (cid) {
+ const targetElement = document.getElementById(cid);
+ if (targetElement) {
+ targetElement.classList.remove('highlight');
+ }
+ }
+ setHoveredCid(null);
+ setOutOfViewCid(null);
+ };
+
const replyBacklink = (
<>
- setHoveredCid(backlinkReply?.cid)} onMouseLeave={() => setHoveredCid(null)}>
+ handleMouseOver(backlinkReply?.cid)}
+ onMouseLeave={() => handleMouseLeave(backlinkReply?.cid)}
+ >
c/{backlinkReply?.shortCid}{' '}
#
{hoveredCid === backlinkReply?.cid &&
+ outOfViewCid === backlinkReply?.cid &&
createPortal(
@@ -136,16 +207,26 @@ const MobileQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, is
>
);
+ const account = useAccount();
+
const replyQuotelink = (
<>
-
setHoveredCid(quotelinkReply?.cid)} onMouseLeave={() => setHoveredCid(null)}>
- c/{quotelinkReply?.shortCid}{' '}
+ handleMouseOver(quotelinkReply?.cid)}
+ onMouseLeave={() => handleMouseLeave(quotelinkReply?.cid)}
+ >
+ c/{quotelinkReply?.shortCid}
+ {quotelinkReply?.author?.address === account?.author?.address && ' (You)'}
+ {' '}
#
{hoveredCid === quotelinkReply?.cid &&
+ outOfViewCid === quotelinkReply?.cid &&
createPortal(
diff --git a/src/components/post/timestamp/index.ts b/src/components/post/timestamp/index.ts
new file mode 100644
index 00000000..8fe28740
--- /dev/null
+++ b/src/components/post/timestamp/index.ts
@@ -0,0 +1 @@
+export { default } from './timestamp';
diff --git a/src/components/post/timestamp/timestamp.tsx b/src/components/post/timestamp/timestamp.tsx
new file mode 100644
index 00000000..d5b71828
--- /dev/null
+++ b/src/components/post/timestamp/timestamp.tsx
@@ -0,0 +1,45 @@
+import { useState } from 'react';
+import { getFormattedDate, getFormattedTimeAgo } from '../../../lib/utils/time-utils';
+import { useFloating, autoUpdate, offset, flip, shift, useHover, useFocus, useDismiss, useRole, useInteractions, FloatingPortal } from '@floating-ui/react';
+
+const Timestamp = ({ timestamp }: { timestamp: number }) => {
+ const [isOpen, setIsOpen] = useState(false);
+
+ const { refs, floatingStyles, context } = useFloating({
+ open: isOpen,
+ onOpenChange: setIsOpen,
+ placement: 'top',
+ whileElementsMounted: autoUpdate,
+ middleware: [
+ offset(5),
+ flip({
+ fallbackAxisSideDirection: 'start',
+ }),
+ shift(),
+ ],
+ });
+
+ const hover = useHover(context, { move: false, delay: { open: 250, close: 0 } });
+ const focus = useFocus(context);
+ const dismiss = useDismiss(context);
+ const role = useRole(context, { role: 'tooltip' });
+
+ const { getReferenceProps, getFloatingProps } = useInteractions([hover, focus, dismiss, role]);
+
+ return (
+ <>
+
+ {getFormattedDate(timestamp)}
+
+
+ {isOpen && (
+
+ {getFormattedTimeAgo(timestamp)}
+
+ )}
+
+ >
+ );
+};
+
+export default Timestamp;
diff --git a/src/hooks/use-edit-comment-privileges.ts b/src/hooks/use-author-privileges.ts
similarity index 78%
rename from src/hooks/use-edit-comment-privileges.ts
rename to src/hooks/use-author-privileges.ts
index 92b43b8c..09b40d08 100644
--- a/src/hooks/use-edit-comment-privileges.ts
+++ b/src/hooks/use-author-privileges.ts
@@ -1,11 +1,11 @@
import { useAccount, useSubplebbit } from '@plebbit/plebbit-react-hooks';
-interface EditCommentPrivileges {
+interface AuthorPrivilegesProps {
commentAuthorAddress: string;
subplebbitAddress: string;
}
-const useEditCommentPrivileges = ({ commentAuthorAddress, subplebbitAddress }: EditCommentPrivileges) => {
+const useAuthorPrivileges = ({ commentAuthorAddress, subplebbitAddress }: AuthorPrivilegesProps) => {
const accountAuthorAddress = useAccount()?.author?.address;
const { roles } = useSubplebbit({ subplebbitAddress }) || {};
@@ -15,7 +15,7 @@ const useEditCommentPrivileges = ({ commentAuthorAddress, subplebbitAddress }: E
const isAccountMod = accountAuthorRole === 'admin' || accountAuthorRole === 'owner' || accountAuthorRole === 'moderator';
const isAccountCommentAuthor = accountAuthorAddress === commentAuthorAddress;
- return { isCommentAuthorMod, isAccountMod, isAccountCommentAuthor };
+ return { isCommentAuthorMod, isAccountMod, isAccountCommentAuthor, commentAuthorRole, accountAuthorRole };
};
-export default useEditCommentPrivileges;
+export default useAuthorPrivileges;
diff --git a/src/hooks/use-popular-posts.ts b/src/hooks/use-popular-posts.ts
index 65360890..d8aa7e42 100644
--- a/src/hooks/use-popular-posts.ts
+++ b/src/hooks/use-popular-posts.ts
@@ -10,9 +10,9 @@ const usePopularPosts = (subplebbits: Subplebbit[]) => {
const allPosts: Comment[] = [];
let postsPerSub = 1;
- if (subplebbits.length == 2) {
+ if (subplebbits.length === 2) {
postsPerSub = 4;
- } else if (subplebbits.length == 3) {
+ } else if (subplebbits.length === 3) {
postsPerSub = 3;
} else if (subplebbits.length >= 4 && subplebbits.length < 8) {
postsPerSub = 2;
diff --git a/src/hooks/use-replies.ts b/src/hooks/use-replies.ts
index 6af6b66b..bc59fc0a 100644
--- a/src/hooks/use-replies.ts
+++ b/src/hooks/use-replies.ts
@@ -2,7 +2,7 @@ import { useMemo, useCallback } from 'react';
import { Comment, useAccountComments } from '@plebbit/plebbit-react-hooks';
import { flattenCommentsPages } from '@plebbit/plebbit-react-hooks/dist/lib/utils';
-const useRepliesAndAccountReplies = (comment: Comment) => {
+const useReplies = (comment: Comment) => {
// flatten all replies including nested ones from the original comment
const flattenedReplies = useMemo(() => flattenCommentsPages(comment.replies), [comment.replies]);
@@ -45,4 +45,4 @@ const useRepliesAndAccountReplies = (comment: Comment) => {
return repliesAndNotYetPublishedReplies;
};
-export default useRepliesAndAccountReplies;
+export default useReplies;
diff --git a/src/index.css b/src/index.css
index 938cfbd0..d62858cd 100644
--- a/src/index.css
+++ b/src/index.css
@@ -26,6 +26,42 @@ hr {
color: var(--post-greentext-color);
}
+.highlight {
+ background: var(--reply-highlight-background-color) !important;
+}
+
+.capitalize {
+ text-transform: capitalize;
+}
+
+.tooltip {
+ position: absolute;
+ background-color: #181f24;
+ font-size: 11px;
+ line-height: 13px;
+ padding: 3px 6px;
+ z-index: 100000;
+ word-wrap: break-word;
+ white-space: pre-line;
+ max-width: 400px;
+ color: #fff;
+ text-align: center;
+}
+
+.tooltip::before {
+ content: "";
+ display: block;
+ width: 0;
+ height: 0;
+ position: absolute;
+ border-left: 4px solid transparent;
+ border-right: 4px solid transparent;
+ border-top: 4px solid #181f24;
+ margin-left: -4px;
+ bottom: -4px;
+ left: 50%;
+}
+
@media (max-width: 640px) {
.button {
font-size: var(--button-font-size-mobile);
diff --git a/src/lib/utils/time-utils.ts b/src/lib/utils/time-utils.ts
index 05ca793b..5c332bd8 100644
--- a/src/lib/utils/time-utils.ts
+++ b/src/lib/utils/time-utils.ts
@@ -33,3 +33,38 @@ export const getFormattedDate = (commentTimestamp: number) => {
}
return string;
};
+
+export const getFormattedTimeAgo = (unixTimestamp: number): string => {
+ const currentTime = Date.now() / 1000;
+ const timeDifference = currentTime - unixTimestamp;
+ const t = i18next.t;
+
+ if (timeDifference < 120) {
+ return t('time_1_minute_ago');
+ }
+ if (timeDifference < 3600) {
+ return t('time_x_minutes_ago', { count: Math.floor(timeDifference / 60) });
+ }
+ if (timeDifference < 7200) {
+ return t('time_1_hour_ago');
+ }
+ if (timeDifference < 86400) {
+ return t('time_x_hours_ago', { count: Math.floor(timeDifference / 3600) });
+ }
+ if (timeDifference < 172800) {
+ return t('time_1_day_ago');
+ }
+ if (timeDifference < 2592000) {
+ return t('time_x_days_ago', { count: Math.floor(timeDifference / 86400) });
+ }
+ if (timeDifference < 5184000) {
+ return t('time_1_month_ago');
+ }
+ if (timeDifference < 31104000) {
+ return t('time_x_months_ago', { count: Math.floor(timeDifference / 2592000) });
+ }
+ if (timeDifference < 62208000) {
+ return t('time_1_year_ago');
+ }
+ return t('time_x_years_ago', { count: Math.floor(timeDifference / 31104000) });
+};
diff --git a/src/lib/utils/view-utils.ts b/src/lib/utils/view-utils.ts
index 080d19ec..a990c9c5 100644
--- a/src/lib/utils/view-utils.ts
+++ b/src/lib/utils/view-utils.ts
@@ -45,12 +45,13 @@ export const isRulesView = (pathname: string, params: ParamsType): boolean => {
};
export const isSettingsView = (pathname: string, params: ParamsType): boolean => {
- const { subplebbitAddress, commentCid } = params;
+ const { accountCommentIndex, commentCid, subplebbitAddress } = params;
const decodedPathname = decodeURIComponent(pathname);
return (
decodedPathname === `/p/${subplebbitAddress}/c/${commentCid}/settings` ||
decodedPathname === `/p/${subplebbitAddress}/description/settings` ||
- decodedPathname === `/p/${subplebbitAddress}/rules/settings`
+ decodedPathname === `/p/${subplebbitAddress}/rules/settings` ||
+ decodedPathname === `/profile/${accountCommentIndex}/settings`
);
};
diff --git a/src/themes.css b/src/themes.css
index 83197713..54c95b97 100644
--- a/src/themes.css
+++ b/src/themes.css
@@ -25,6 +25,12 @@
--catalog-post-menu-desktop-btn-hover-color: red;
--catalog-post-menu-desktop-btn-opacity: 0.5;
--catalog-post-menu-desktop-btn-hover-opacity: 1;
+ --catalog-post-preview-background-color: #181f24;
+ --catalog-post-preview-color: #dedede;
+ --catalog-post-preview-subject-color: #cc1105;
+ --catalog-post-preview-author-color: #00a550;
+ --catalog-post-preview-last-color: #bbbfbd;
+ --catalog-post-preview-author-capcode-color: purple;
/* challenge modal */
--challenge-modal-background-color: #f0e0d6;
@@ -226,6 +232,11 @@
--catalog-post-menu-desktop-btn-hover-color: #d00;
--catalog-post-menu-desktop-btn-opacity: 0.5;
--catalog-post-menu-desktop-btn-hover-opacity: 1;
+ --catalog-post-preview-background-color: #181f24;
+ --catalog-post-preview-color: #dedede;
+ --catalog-post-preview-author-color: #00a550;
+ --catalog-post-preview-last-color: #bbbfbd;
+ --catalog-post-preview-author-capcode-color: purple;
/* challenge modal */
--challenge-modal-background-color: #d6daf0;
@@ -421,6 +432,12 @@
--catalog-post-menu-desktop-btn-hover-color: red;
--catalog-post-menu-desktop-btn-opacity: 0.5;
--catalog-post-menu-desktop-btn-hover-opacity: 1;
+ --catalog-post-preview-background-color: #181f24;
+ --catalog-post-preview-color: #dedede;
+ --catalog-post-preview-subject-color: #cc1105;
+ --catalog-post-preview-author-color: #00a550;
+ --catalog-post-preview-last-color: #bbbfbd;
+ --catalog-post-preview-author-capcode-color: purple;
/* challenge modal */
--challenge-modal-background-color: #f0e0d6;
@@ -590,6 +607,11 @@
--catalog-post-menu-desktop-btn-hover-color: red;
--catalog-post-menu-desktop-btn-opacity: 0.5;
--catalog-post-menu-desktop-btn-hover-opacity: 1;
+ --catalog-post-preview-background-color: #181f24;
+ --catalog-post-preview-color: #dedede;
+ --catalog-post-preview-author-color: #00a550;
+ --catalog-post-preview-last-color: #bbbfbd;
+ --catalog-post-preview-author-capcode-color: purple;
/* challenge modal */
--challenge-modal-background-color: #d6daf0;
@@ -766,12 +788,18 @@
--catalog-post-menu-desktop-btn-hover-color: #5f89ac;
--catalog-post-menu-desktop-btn-opacity: 0.5;
--catalog-post-menu-desktop-btn-hover-opacity: 1;
+ --catalog-post-preview-background-color: #181f24;
+ --catalog-post-preview-color: #dedede;
+ --catalog-post-preview-subject-color: #b294bb;
+ --catalog-post-preview-author-color: #00a550;
+ --catalog-post-preview-last-color: #bbbfbd;
+ --catalog-post-preview-author-capcode-color: purple;
/* challenge modal */
--challenge-modal-background-color: #282a2e;
--challenge-modal-border: 1px solid #111;
--challenge-modal-title-background-color: #282a2e;
- --challenge-modal-title-text-color: #c5c8c6;
+ --challenge-modal-title-text-color: #b0cab9;
--challenge-modal-title-border: unset;
--challenge-modal-title-font-size: 10pt;
--challenge-modal-title-font-weight: 700;
@@ -969,6 +997,10 @@
--catalog-post-menu-desktop-btn-hover-color: #f30;
--catalog-post-menu-desktop-btn-opacity: 0.5;
--catalog-post-menu-desktop-btn-hover-opacity: 1;
+ --catalog-post-preview-background-color: #181f24;
+ --catalog-post-preview-color: #dedede;
+ --catalog-post-preview-last-color: #bbbfbd;
+ --catalog-post-preview-author-capcode-color: purple;
/* challenge modal */
--challenge-modal-background-color: #ddd;
diff --git a/src/views/home/boards-box/boards-box.tsx b/src/views/home/boards-box/boards-box.tsx
index ee7ce7aa..aef5bbed 100644
--- a/src/views/home/boards-box/boards-box.tsx
+++ b/src/views/home/boards-box/boards-box.tsx
@@ -197,7 +197,7 @@ const BoardsBox = ({ multisub, subplebbits }: { multisub: Subplebbit[]; subplebb
return (
-
{t('boards')}
+ {t('boards')}
diff --git a/src/views/home/home.module.css b/src/views/home/home.module.css
index 0bad3b19..a7855eba 100644
--- a/src/views/home/home.module.css
+++ b/src/views/home/home.module.css
@@ -150,10 +150,6 @@
content: '✔ ';
}
-.capitalize {
- text-transform: capitalize;
-}
-
.list a {
color: var(--homepage-box-link-text-color);
text-decoration: var(--homepage-box-link-text-decoration);
diff --git a/src/views/home/home.tsx b/src/views/home/home.tsx
index 84456ae4..3c61045d 100644
--- a/src/views/home/home.tsx
+++ b/src/views/home/home.tsx
@@ -88,7 +88,7 @@ const Stats = ({ multisub, subplebbitAddresses }: { multisub: any; subplebbitAdd
return (
-
{t('stats')}
+ {t('stats')}
{enoughStats ? (
@@ -143,7 +143,7 @@ const Footer = () => {
-
+
Twitter
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 76b13a1e..338dc4c8 100644
--- a/src/views/home/popular-threads-box/popular-threads-box.tsx
+++ b/src/views/home/popular-threads-box/popular-threads-box.tsx
@@ -63,7 +63,7 @@ const PopularThreadsBox = ({ multisub, subplebbits }: { multisub: Subplebbit[];
return (
-
{t('popular_threads')}
+ {t('popular_threads')}
diff --git a/src/views/pending-post/pending-post.tsx b/src/views/pending-post/pending-post.tsx
index 0aab5e3b..4b25fd9c 100644
--- a/src/views/pending-post/pending-post.tsx
+++ b/src/views/pending-post/pending-post.tsx
@@ -1,13 +1,18 @@
import { useEffect } from 'react';
-import { useNavigate, useParams } from 'react-router-dom';
+import { useLocation, useNavigate, useParams } from 'react-router-dom';
import { useAccountComment } from '@plebbit/plebbit-react-hooks';
+import { isSettingsView } from '../../lib/utils/view-utils';
import Post from '../../components/post';
+import SettingsModal from '../../components/settings-modal';
const PendingPost = () => {
const { accountCommentIndex } = useParams<{ accountCommentIndex?: string }>();
const commentIndex = accountCommentIndex ? parseInt(accountCommentIndex) : undefined;
const post = useAccountComment({ commentIndex });
const navigate = useNavigate();
+ const location = useLocation();
+ const params = useParams();
+ const isInSettingsView = isSettingsView(location.pathname, params);
useEffect(() => window.scrollTo(0, 0), []);
@@ -17,7 +22,12 @@ const PendingPost = () => {
}
}, [post, navigate]);
- return
;
+ return (
+ <>
+ {isInSettingsView &&
}
+
;
+ >
+ );
};
export default PendingPost;