mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "عرض الموضوع",
|
"view_thread": "عرض الموضوع",
|
||||||
"return": "العودة",
|
"return": "العودة",
|
||||||
"bottom": "القاع",
|
"bottom": "القاع",
|
||||||
"top": "أعلى"
|
"top": "أعلى",
|
||||||
|
"thread_closed": "تم إغلاق الموضوع.",
|
||||||
|
"may_not_reply": "قد لا تتم الرد في هذا الوقت."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "থ্রেড দেখুন",
|
"view_thread": "থ্রেড দেখুন",
|
||||||
"return": "ফিরে যাও",
|
"return": "ফিরে যাও",
|
||||||
"bottom": "নীচে",
|
"bottom": "নীচে",
|
||||||
"top": "শীর্ষ"
|
"top": "শীর্ষ",
|
||||||
|
"thread_closed": "থ্রেড বন্ধ হয়েছে।",
|
||||||
|
"may_not_reply": "এই সময়ে আপনি প্রতিক্রিয়া দিতে পারবেন না।"
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "Zobrazit Vlákno",
|
"view_thread": "Zobrazit Vlákno",
|
||||||
"return": "Návrat",
|
"return": "Návrat",
|
||||||
"bottom": "Dno",
|
"bottom": "Dno",
|
||||||
"top": "Nahoře"
|
"top": "Nahoře",
|
||||||
|
"thread_closed": "Vlákno uzavřeno.",
|
||||||
|
"may_not_reply": "V tuto chvíli nemůžete odpovědět."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "Vis Tråd",
|
"view_thread": "Vis Tråd",
|
||||||
"return": "Vende tilbage",
|
"return": "Vende tilbage",
|
||||||
"bottom": "Bund",
|
"bottom": "Bund",
|
||||||
"top": "Top"
|
"top": "Top",
|
||||||
|
"thread_closed": "Tråd lukket.",
|
||||||
|
"may_not_reply": "Du kan muligvis ikke svare på dette tidspunkt."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "Thread Anzeigen",
|
"view_thread": "Thread Anzeigen",
|
||||||
"return": "Zurück",
|
"return": "Zurück",
|
||||||
"bottom": "Unten",
|
"bottom": "Unten",
|
||||||
"top": "Oben"
|
"top": "Oben",
|
||||||
|
"thread_closed": "Thread geschlossen.",
|
||||||
|
"may_not_reply": "Sie können möglicherweise zu diesem Zeitpunkt nicht antworten."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "Προβολή Νήματος",
|
"view_thread": "Προβολή Νήματος",
|
||||||
"return": "Επιστροφή",
|
"return": "Επιστροφή",
|
||||||
"bottom": "Κάτω",
|
"bottom": "Κάτω",
|
||||||
"top": "Κορυφή"
|
"top": "Κορυφή",
|
||||||
|
"thread_closed": "Το νήμα έκλεισε.",
|
||||||
|
"may_not_reply": "Μπορεί να μην μπορείτε να απαντήσετε αυτή τη στιγμή."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "View Thread",
|
"view_thread": "View Thread",
|
||||||
"return": "Return",
|
"return": "Return",
|
||||||
"bottom": "Bottom",
|
"bottom": "Bottom",
|
||||||
"top": "Top"
|
"top": "Top",
|
||||||
|
"thread_closed": "Thread closed.",
|
||||||
|
"may_not_reply": "You may not reply at this time."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "Ver Hilo",
|
"view_thread": "Ver Hilo",
|
||||||
"return": "Regresar",
|
"return": "Regresar",
|
||||||
"bottom": "Fondo",
|
"bottom": "Fondo",
|
||||||
"top": "Arriba"
|
"top": "Arriba",
|
||||||
|
"thread_closed": "Hilo cerrado.",
|
||||||
|
"may_not_reply": "Es posible que no puedas responder en este momento."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "مشاهده نخ",
|
"view_thread": "مشاهده نخ",
|
||||||
"return": "برگشت",
|
"return": "برگشت",
|
||||||
"bottom": "پایین",
|
"bottom": "پایین",
|
||||||
"top": "بالا"
|
"top": "بالا",
|
||||||
|
"thread_closed": "موضوع بسته شده است.",
|
||||||
|
"may_not_reply": "شما ممکن است در این زمان پاسخ ندهید."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "Näytä Ketju",
|
"view_thread": "Näytä Ketju",
|
||||||
"return": "Palata",
|
"return": "Palata",
|
||||||
"bottom": "Pohja",
|
"bottom": "Pohja",
|
||||||
"top": "Ylös"
|
"top": "Ylös",
|
||||||
|
"thread_closed": "Keskustelu suljettu.",
|
||||||
|
"may_not_reply": "Et ehkä voi vastata tällä hetkellä."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "Tingnan ang Thread",
|
"view_thread": "Tingnan ang Thread",
|
||||||
"return": "Bumalik",
|
"return": "Bumalik",
|
||||||
"bottom": "Ilalim",
|
"bottom": "Ilalim",
|
||||||
"top": "Itaas"
|
"top": "Itaas",
|
||||||
|
"thread_closed": "Thread na isara.",
|
||||||
|
"may_not_reply": "Hindi ka maaaring mag-reply sa oras na ito."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "Voir le Fil",
|
"view_thread": "Voir le Fil",
|
||||||
"return": "Retour",
|
"return": "Retour",
|
||||||
"bottom": "Bas",
|
"bottom": "Bas",
|
||||||
"top": "Haut"
|
"top": "Haut",
|
||||||
|
"thread_closed": "Fil fermé.",
|
||||||
|
"may_not_reply": "Vous ne pouvez peut-être pas répondre en ce moment."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "הצג אשכול",
|
"view_thread": "הצג אשכול",
|
||||||
"return": "חזור",
|
"return": "חזור",
|
||||||
"bottom": "תחתית",
|
"bottom": "תחתית",
|
||||||
"top": "עליון"
|
"top": "עליון",
|
||||||
|
"thread_closed": "השרשור נסגר.",
|
||||||
|
"may_not_reply": "אולי אינך יכול להגיב כרגע."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "थ्रेड देखें",
|
"view_thread": "थ्रेड देखें",
|
||||||
"return": "वापस",
|
"return": "वापस",
|
||||||
"bottom": "नीचे",
|
"bottom": "नीचे",
|
||||||
"top": "ऊपर"
|
"top": "ऊपर",
|
||||||
|
"thread_closed": "थ्रेड बंद हो गया।",
|
||||||
|
"may_not_reply": "इस समय आप जवाब नहीं दे सकते।"
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "Téma Megtekintése",
|
"view_thread": "Téma Megtekintése",
|
||||||
"return": "Visszatérés",
|
"return": "Visszatérés",
|
||||||
"bottom": "Alja",
|
"bottom": "Alja",
|
||||||
"top": "Felső"
|
"top": "Felső",
|
||||||
|
"thread_closed": "A téma lezárult.",
|
||||||
|
"may_not_reply": "Jelenleg nem válaszolhatsz erre."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "Lihat Thread",
|
"view_thread": "Lihat Thread",
|
||||||
"return": "Kembali",
|
"return": "Kembali",
|
||||||
"bottom": "Bawah",
|
"bottom": "Bawah",
|
||||||
"top": "Atas"
|
"top": "Atas",
|
||||||
|
"thread_closed": "Benang ditutup.",
|
||||||
|
"may_not_reply": "Anda mungkin tidak bisa membalas pada saat ini."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "Visualizza Thread",
|
"view_thread": "Visualizza Thread",
|
||||||
"return": "Indietro",
|
"return": "Indietro",
|
||||||
"bottom": "Vai sotto",
|
"bottom": "Vai sotto",
|
||||||
"top": "Vai sopra"
|
"top": "Vai sopra",
|
||||||
|
"thread_closed": "Thread chiuso.",
|
||||||
|
"may_not_reply": "Non puoi rispondere in questo momento."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "スレッドを見る",
|
"view_thread": "スレッドを見る",
|
||||||
"return": "戻る",
|
"return": "戻る",
|
||||||
"bottom": "下へ",
|
"bottom": "下へ",
|
||||||
"top": "上へ"
|
"top": "上へ",
|
||||||
|
"thread_closed": "スレッドが閉じられました。",
|
||||||
|
"may_not_reply": "現在、返信することはできません。"
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "스레드 보기",
|
"view_thread": "스레드 보기",
|
||||||
"return": "돌아 가기",
|
"return": "돌아 가기",
|
||||||
"bottom": "하단",
|
"bottom": "하단",
|
||||||
"top": "맨 위로"
|
"top": "맨 위로",
|
||||||
|
"thread_closed": "스레드가 닫혔습니다.",
|
||||||
|
"may_not_reply": "현재 시간에는 답글을 달 수 없습니다."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "थ्रेड पहा",
|
"view_thread": "थ्रेड पहा",
|
||||||
"return": "परत जा",
|
"return": "परत जा",
|
||||||
"bottom": "खाली",
|
"bottom": "खाली",
|
||||||
"top": "वर"
|
"top": "वर",
|
||||||
|
"thread_closed": "सूत्र बंद झाला.",
|
||||||
|
"may_not_reply": "हा वेळ उत्तर देण्यास शक्य नाही."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "Bekijk Thread",
|
"view_thread": "Bekijk Thread",
|
||||||
"return": "Terugkeer",
|
"return": "Terugkeer",
|
||||||
"bottom": "Onderkant",
|
"bottom": "Onderkant",
|
||||||
"top": "Boven"
|
"top": "Boven",
|
||||||
|
"thread_closed": "Thread gesloten.",
|
||||||
|
"may_not_reply": "U kunt op dit moment mogelijk niet antwoorden."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "Vis Tråd",
|
"view_thread": "Vis Tråd",
|
||||||
"return": "Retur",
|
"return": "Retur",
|
||||||
"bottom": "Bunnen",
|
"bottom": "Bunnen",
|
||||||
"top": "Topp"
|
"top": "Topp",
|
||||||
|
"thread_closed": "Tråd stengt.",
|
||||||
|
"may_not_reply": "Du kan kanskje ikke svare på dette tidspunktet."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "Zobacz Wątek",
|
"view_thread": "Zobacz Wątek",
|
||||||
"return": "Powrót",
|
"return": "Powrót",
|
||||||
"bottom": "Dół",
|
"bottom": "Dół",
|
||||||
"top": "Góra"
|
"top": "Góra",
|
||||||
|
"thread_closed": "Wątek zamknięty.",
|
||||||
|
"may_not_reply": "Może nie możesz teraz odpowiedzieć."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "Ver Tópico",
|
"view_thread": "Ver Tópico",
|
||||||
"return": "Retorno",
|
"return": "Retorno",
|
||||||
"bottom": "Fundo",
|
"bottom": "Fundo",
|
||||||
"top": "Topo"
|
"top": "Topo",
|
||||||
|
"thread_closed": "Tópico fechado.",
|
||||||
|
"may_not_reply": "Você pode não conseguir responder neste momento."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "Vizualizați Firul",
|
"view_thread": "Vizualizați Firul",
|
||||||
"return": "Întoarcere",
|
"return": "Întoarcere",
|
||||||
"bottom": "Partea de jos",
|
"bottom": "Partea de jos",
|
||||||
"top": "Sus"
|
"top": "Sus",
|
||||||
|
"thread_closed": "Thread închis.",
|
||||||
|
"may_not_reply": "Poate că nu puteți răspunde în acest moment."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "Посмотреть Тему",
|
"view_thread": "Посмотреть Тему",
|
||||||
"return": "Возвращение",
|
"return": "Возвращение",
|
||||||
"bottom": "Низ",
|
"bottom": "Низ",
|
||||||
"top": "Верх"
|
"top": "Верх",
|
||||||
|
"thread_closed": "Тема закрыта.",
|
||||||
|
"may_not_reply": "Вы можете не ответить в это время."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "Shiko Thread",
|
"view_thread": "Shiko Thread",
|
||||||
"return": "Kthimi",
|
"return": "Kthimi",
|
||||||
"bottom": "Fund",
|
"bottom": "Fund",
|
||||||
"top": "Sipër"
|
"top": "Sipër",
|
||||||
|
"thread_closed": "Fjala është e mbyllur.",
|
||||||
|
"may_not_reply": "Nuk mund të përgjigjeni në këtë kohë."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "Visa Tråd",
|
"view_thread": "Visa Tråd",
|
||||||
"return": "Återgång",
|
"return": "Återgång",
|
||||||
"bottom": "Botten",
|
"bottom": "Botten",
|
||||||
"top": "Topp"
|
"top": "Topp",
|
||||||
|
"thread_closed": "Tråd stängd.",
|
||||||
|
"may_not_reply": "Du kanske inte kan svara vid den här tiden."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "థ్రెడ్ను చూడండి",
|
"view_thread": "థ్రెడ్ను చూడండి",
|
||||||
"return": "రిటర్న్",
|
"return": "రిటర్న్",
|
||||||
"bottom": "కిందకు",
|
"bottom": "కిందకు",
|
||||||
"top": "మేలే"
|
"top": "మేలే",
|
||||||
|
"thread_closed": "థ్రెడ్ మూసివేయబడింది.",
|
||||||
|
"may_not_reply": "ఈ సమయంలో మీరు ప్రతిసారం ఇవ్వలేరు."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "ดูกระทู้",
|
"view_thread": "ดูกระทู้",
|
||||||
"return": "กลับ",
|
"return": "กลับ",
|
||||||
"bottom": "ด้านล่าง",
|
"bottom": "ด้านล่าง",
|
||||||
"top": "ด้านบน"
|
"top": "ด้านบน",
|
||||||
|
"thread_closed": "เธรดถูกปิด",
|
||||||
|
"may_not_reply": "คุณอาจไม่สามารถตอบกลับได้ในขณะนี้"
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "Konuyu Görüntüle",
|
"view_thread": "Konuyu Görüntüle",
|
||||||
"return": "Dönüş",
|
"return": "Dönüş",
|
||||||
"bottom": "Alt",
|
"bottom": "Alt",
|
||||||
"top": "Üst"
|
"top": "Üst",
|
||||||
|
"thread_closed": "Konu kapatıldı.",
|
||||||
|
"may_not_reply": "Şu anda cevap veremeyebilirsiniz."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "Переглянути Тему",
|
"view_thread": "Переглянути Тему",
|
||||||
"return": "Повернення",
|
"return": "Повернення",
|
||||||
"bottom": "Низ",
|
"bottom": "Низ",
|
||||||
"top": "Верхній"
|
"top": "Верхній",
|
||||||
|
"thread_closed": "Тема закрита.",
|
||||||
|
"may_not_reply": "Можливо, ви не зможете відповісти зараз."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "تھریڈ دیکھیں",
|
"view_thread": "تھریڈ دیکھیں",
|
||||||
"return": "واپس",
|
"return": "واپس",
|
||||||
"bottom": "نیچے",
|
"bottom": "نیچے",
|
||||||
"top": "اوپر"
|
"top": "اوپر",
|
||||||
|
"thread_closed": "موضوع بند کر دیا گیا ہے۔",
|
||||||
|
"may_not_reply": "آپ اس وقت جواب نہیں دے سکتے۔"
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "Xem Chủ Đề",
|
"view_thread": "Xem Chủ Đề",
|
||||||
"return": "Trở lại",
|
"return": "Trở lại",
|
||||||
"bottom": "Đáy",
|
"bottom": "Đáy",
|
||||||
"top": "Đầu"
|
"top": "Đầu",
|
||||||
|
"thread_closed": "Chủ đề đã đóng.",
|
||||||
|
"may_not_reply": "Bạn có thể không thể trả lời vào thời điểm này."
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,7 @@
|
|||||||
"view_thread": "查看线程",
|
"view_thread": "查看线程",
|
||||||
"return": "返回",
|
"return": "返回",
|
||||||
"bottom": "底部",
|
"bottom": "底部",
|
||||||
"top": "顶部"
|
"top": "顶部",
|
||||||
|
"thread_closed": "主题已关闭。",
|
||||||
|
"may_not_reply": "您现在可能无法回复。"
|
||||||
}
|
}
|
||||||
+1
-1
@@ -14,4 +14,4 @@
|
|||||||
.app {
|
.app {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-30
@@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { Outlet, Route, Routes, useLocation, useParams } from 'react-router-dom';
|
import { Outlet, Route, Routes, useLocation } from 'react-router-dom';
|
||||||
import { isHomeView } from './lib/utils/view-utils';
|
import { isHomeView } from './lib/utils/view-utils';
|
||||||
import { Subplebbit as SubplebbitType, useSubplebbit, useSubplebbits } from '@plebbit/plebbit-react-hooks';
|
import { useSubplebbits } from '@plebbit/plebbit-react-hooks';
|
||||||
import { useDefaultSubplebbitAddresses } from './hooks/use-default-subplebbits';
|
import { useDefaultSubplebbitAddresses } from './hooks/use-default-subplebbits';
|
||||||
import useTheme from './hooks/use-theme';
|
import useTheme from './hooks/use-theme';
|
||||||
import styles from './app.module.css';
|
import styles from './app.module.css';
|
||||||
@@ -16,32 +16,6 @@ import { MobileBoardButtons } from './components/board-buttons';
|
|||||||
import BoardStats from './components/board-stats';
|
import BoardStats from './components/board-stats';
|
||||||
import PostForm from './components/post-form';
|
import PostForm from './components/post-form';
|
||||||
|
|
||||||
interface BoardLayoutProps {
|
|
||||||
subplebbits: (SubplebbitType | undefined)[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const BoardLayout = React.memo(({ subplebbits }: BoardLayoutProps) => {
|
|
||||||
const { subplebbitAddress } = useParams<{ subplebbitAddress: string }>();
|
|
||||||
const subplebbit = useSubplebbit({ subplebbitAddress });
|
|
||||||
const { address, createdAt, title } = subplebbit || {};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{address && (
|
|
||||||
<>
|
|
||||||
<BoardNav address={address} subplebbits={subplebbits} />
|
|
||||||
<BoardBanner title={title} address={address} />
|
|
||||||
<MobileBoardButtons address={address} />
|
|
||||||
<PostForm address={address} />
|
|
||||||
<BoardStats address={address} createdAt={createdAt} />
|
|
||||||
<DesktopBoardButtons address={address} />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<Outlet />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const [theme] = useTheme();
|
const [theme] = useTheme();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
@@ -62,13 +36,27 @@ const App = () => {
|
|||||||
// </>
|
// </>
|
||||||
// );
|
// );
|
||||||
|
|
||||||
|
const boardLayout = (
|
||||||
|
<>
|
||||||
|
<BoardNav subplebbits={subplebbits} />
|
||||||
|
<BoardBanner />
|
||||||
|
<MobileBoardButtons />
|
||||||
|
<PostForm />
|
||||||
|
<BoardStats />
|
||||||
|
<DesktopBoardButtons />
|
||||||
|
<Outlet />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${styles.app} ${isInHomeView ? 'yotsuba' : theme}`}>
|
<div className={`${styles.app} ${isInHomeView ? 'yotsuba' : theme}`}>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path='/' element={<Home subplebbits={subplebbits} />} />
|
<Route path='/' element={<Home subplebbits={subplebbits} />} />
|
||||||
<Route element={<BoardLayout subplebbits={subplebbits} />}>
|
<Route element={boardLayout}>
|
||||||
<Route path='/p/:subplebbitAddress' element={<Subplebbit />} />
|
<Route path='/p/:subplebbitAddress' element={<Subplebbit />} />
|
||||||
<Route path='/p/:subplebbitAddress/c/:commentCid' element={<PostPage />} />
|
<Route path='/p/:subplebbitAddress/c/:commentCid' element={<PostPage />} />
|
||||||
|
<Route path='/p/:subplebbitAddress/description' element={<PostPage />} />
|
||||||
|
<Route path='/p/:subplebbitAddress/rules' element={<PostPage />} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path='/settings' element={<Settings />} />
|
<Route path='/settings' element={<Settings />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
import { useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import styles from './board-banner.module.css';
|
import styles from './board-banner.module.css';
|
||||||
|
|
||||||
@@ -12,13 +14,12 @@ const ImageBanner = () => {
|
|||||||
return <img src={imagePath} alt='banner' />;
|
return <img src={imagePath} alt='banner' />;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface BoardBannerProps {
|
const BoardBanner = () => {
|
||||||
title: string | undefined;
|
const { subplebbitAddress } = useParams<{ subplebbitAddress: string }>();
|
||||||
address: string | undefined;
|
const subplebbit = useSubplebbit({ subplebbitAddress });
|
||||||
}
|
const { address, title } = subplebbit || {};
|
||||||
|
|
||||||
const BoardBanner = ({ address, title }: BoardBannerProps) => {
|
|
||||||
const displayAddress = address && address.length > 10 && !address.includes('.') ? address.slice(0, 13).concat('...') : address;
|
const displayAddress = address && address.length > 10 && !address.includes('.') ? address.slice(0, 13).concat('...') : address;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<div className={styles.bannerCnt}>
|
<div className={styles.bannerCnt}>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import styles from './board-buttons.module.css';
|
|||||||
import { isPostPageView } from '../../lib/utils/view-utils';
|
import { isPostPageView } from '../../lib/utils/view-utils';
|
||||||
|
|
||||||
interface BoardButtonsProps {
|
interface BoardButtonsProps {
|
||||||
address: string;
|
address: string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const OptionsButton = () => {
|
const OptionsButton = () => {
|
||||||
@@ -51,7 +51,8 @@ const BottomButton = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MobileBoardButtons = ({ address }: BoardButtonsProps) => {
|
export const MobileBoardButtons = () => {
|
||||||
|
const { subplebbitAddress: address } = useParams<{ subplebbitAddress: string }>();
|
||||||
const isInPostPage = isPostPageView(useLocation().pathname, useParams());
|
const isInPostPage = isPostPageView(useLocation().pathname, useParams());
|
||||||
return (
|
return (
|
||||||
<div className={styles.mobileBoardButtons}>
|
<div className={styles.mobileBoardButtons}>
|
||||||
@@ -76,7 +77,8 @@ export const MobileBoardButtons = ({ address }: BoardButtonsProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DesktopBoardButtons = ({ address }: BoardButtonsProps) => {
|
export const DesktopBoardButtons = () => {
|
||||||
|
const { subplebbitAddress: address } = useParams<{ subplebbitAddress: string }>();
|
||||||
const isInPostPage = isPostPageView(useLocation().pathname, useParams());
|
const isInPostPage = isPostPageView(useLocation().pathname, useParams());
|
||||||
return (
|
return (
|
||||||
<div className={styles.desktopBoardButtons}>
|
<div className={styles.desktopBoardButtons}>
|
||||||
|
|||||||
@@ -33,11 +33,6 @@
|
|||||||
text-decoration: var(--button-text-decoration-mobile);
|
text-decoration: var(--button-text-decoration-mobile);
|
||||||
}
|
}
|
||||||
|
|
||||||
.boardNavMobile .pageJump a:hover {
|
|
||||||
color: var(--button-text-color-hover-mobile);
|
|
||||||
text-decoration: var(--button-text-decoration-hover-mobile);
|
|
||||||
}
|
|
||||||
|
|
||||||
.boardNavMobile strong {
|
.boardNavMobile strong {
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import { Link, useNavigate } from 'react-router-dom';
|
import { Link, useNavigate, useParams } from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Subplebbit } from '@plebbit/plebbit-react-hooks';
|
import { Subplebbit } from '@plebbit/plebbit-react-hooks';
|
||||||
import styles from './board-nav.module.css';
|
import styles from './board-nav.module.css';
|
||||||
|
|
||||||
interface BoardNavProps {
|
interface BoardNavProps {
|
||||||
address?: string | undefined;
|
|
||||||
subplebbits: (Subplebbit | undefined)[];
|
subplebbits: (Subplebbit | undefined)[];
|
||||||
currentSubplebbit?: string | undefined;
|
currentSubplebbit?: string | undefined;
|
||||||
}
|
}
|
||||||
@@ -77,7 +76,8 @@ const BoardNavMobile = ({ subplebbits, currentSubplebbit }: BoardNavProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const BoardNav = ({ address, subplebbits }: BoardNavProps) => {
|
const BoardNav = ({ subplebbits }: BoardNavProps) => {
|
||||||
|
const { subplebbitAddress: address } = useParams();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<BoardNavDesktop subplebbits={subplebbits} />
|
<BoardNavDesktop subplebbits={subplebbits} />
|
||||||
|
|||||||
@@ -1,3 +1,11 @@
|
|||||||
|
.hide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
.content hr {
|
.content hr {
|
||||||
min-width: 468px;
|
min-width: 468px;
|
||||||
margin-left: -4px;
|
margin-left: -4px;
|
||||||
@@ -12,7 +20,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.blotter tr {
|
.blotter tr {
|
||||||
font-size: 11px;
|
font-size: var(--stats-font-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
.blotter tfoot {
|
.blotter tfoot {
|
||||||
|
|||||||
@@ -1,15 +1,26 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useSubplebbitStats } from '@plebbit/plebbit-react-hooks';
|
import { useLocation, useParams } from 'react-router-dom';
|
||||||
|
import { useComment, useSubplebbit, useSubplebbitStats } from '@plebbit/plebbit-react-hooks';
|
||||||
import styles from './board-stats.module.css';
|
import styles from './board-stats.module.css';
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
|
import { isDescriptionView, isRulesView } from '../../lib/utils/view-utils';
|
||||||
|
|
||||||
export interface BoardStatsProps {
|
const BoardStats = () => {
|
||||||
address: string | undefined;
|
|
||||||
createdAt: number | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const BoardStats = ({ address, createdAt }: BoardStatsProps) => {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { commentCid, subplebbitAddress } = useParams<{ subplebbitAddress: string; commentCid: string }>();
|
||||||
|
|
||||||
|
const subplebbit = useSubplebbit({ subplebbitAddress });
|
||||||
|
const { address, createdAt } = subplebbit || {};
|
||||||
|
|
||||||
|
const location = useLocation();
|
||||||
|
const params = useParams();
|
||||||
|
const isInDescriptionView = isDescriptionView(location.pathname, params);
|
||||||
|
const isInRulesView = isRulesView(location.pathname, params);
|
||||||
|
|
||||||
|
const comment = useComment({ commentCid });
|
||||||
|
const { deleted, locked, removed } = comment || {};
|
||||||
|
const hideStats = deleted || locked || removed || isInDescriptionView || isInRulesView;
|
||||||
|
|
||||||
const stats = useSubplebbitStats({ subplebbitAddress: address });
|
const stats = useSubplebbitStats({ subplebbitAddress: address });
|
||||||
const [showStats, setShowStats] = useState(true);
|
const [showStats, setShowStats] = useState(true);
|
||||||
|
|
||||||
@@ -22,7 +33,7 @@ const BoardStats = ({ address, createdAt }: BoardStatsProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.content}>
|
<div className={`${styles.content} ${hideStats ? styles.hide : styles.show}`}>
|
||||||
<table className={styles.blotter}>
|
<table className={styles.blotter}>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import styles from './media.module.css';
|
import styles from './media.module.css';
|
||||||
import { CommentMediaInfo } from '../../lib/utils/media-utils';
|
import { CommentMediaInfo } from '../../lib/utils/media-utils';
|
||||||
import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
|
import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
|
||||||
@@ -34,7 +34,7 @@ const ThumbnailSmall = ({ style, children, thumbnailSmallPadding }: ThumbnailPro
|
|||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|
||||||
const Thumbnail = ({ commentMediaInfo, isMobile, isReply, linkHeight, linkWidth }: MediaProps) => {
|
const Thumbnail = ({ commentMediaInfo, isMobile, isReply, linkHeight, linkWidth, setShowThumbnail }: MediaProps) => {
|
||||||
let displayWidth, displayHeight;
|
let displayWidth, displayHeight;
|
||||||
const maxThumbnailSize = isMobile || isReply ? 125 : 250;
|
const maxThumbnailSize = isMobile || isReply ? 125 : 250;
|
||||||
if (linkWidth && linkHeight) {
|
if (linkWidth && linkHeight) {
|
||||||
@@ -46,19 +46,30 @@ const Thumbnail = ({ commentMediaInfo, isMobile, isReply, linkHeight, linkWidth
|
|||||||
displayHeight = `${maxThumbnailSize}px`;
|
displayHeight = `${maxThumbnailSize}px`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (commentMediaInfo?.type === 'audio') {
|
||||||
|
displayWidth = '250px';
|
||||||
|
displayHeight = '75px';
|
||||||
|
}
|
||||||
|
|
||||||
let mediaComponent: React.ReactNode = null;
|
let mediaComponent: React.ReactNode = null;
|
||||||
const iframeThumbnail = commentMediaInfo?.patternThumbnailUrl || commentMediaInfo?.thumbnail;
|
const iframeThumbnail = commentMediaInfo?.patternThumbnailUrl || commentMediaInfo?.thumbnail;
|
||||||
const gifFrameUrl = useFetchGifFirstFrame(commentMediaInfo?.type === 'gif' ? commentMediaInfo.url : undefined);
|
const gifFrameUrl = useFetchGifFirstFrame(commentMediaInfo?.type === 'gif' ? commentMediaInfo.url : undefined);
|
||||||
if (commentMediaInfo?.type === 'image') {
|
if (commentMediaInfo?.type === 'image') {
|
||||||
mediaComponent = <img src={commentMediaInfo.url} alt='' />;
|
mediaComponent = <img src={commentMediaInfo.url} alt='' onClick={() => setShowThumbnail(false)} />;
|
||||||
} else if (commentMediaInfo?.type === 'video') {
|
} else if (commentMediaInfo?.type === 'video') {
|
||||||
mediaComponent = commentMediaInfo.thumbnail ? <img src={commentMediaInfo.thumbnail} alt='' /> : <video src={`${commentMediaInfo.url}#t=0.001`} />;
|
mediaComponent = commentMediaInfo.thumbnail ? (
|
||||||
|
<img src={commentMediaInfo.thumbnail} alt='' />
|
||||||
|
) : (
|
||||||
|
<video src={`${commentMediaInfo.url}#t=0.001`} onClick={() => setShowThumbnail(false)} />
|
||||||
|
);
|
||||||
} else if (commentMediaInfo?.type === 'webpage') {
|
} else if (commentMediaInfo?.type === 'webpage') {
|
||||||
mediaComponent = <img src={commentMediaInfo.thumbnail} alt='' />;
|
mediaComponent = <img src={commentMediaInfo.thumbnail} alt='' onClick={() => setShowThumbnail(false)} />;
|
||||||
} else if (commentMediaInfo?.type === 'iframe') {
|
} else if (commentMediaInfo?.type === 'iframe') {
|
||||||
mediaComponent = iframeThumbnail ? <img src={iframeThumbnail} alt='' /> : null;
|
mediaComponent = iframeThumbnail ? <img src={iframeThumbnail} alt='' onClick={() => setShowThumbnail(false)} /> : null;
|
||||||
} else if (commentMediaInfo?.type === 'gif' && gifFrameUrl) {
|
} else if (commentMediaInfo?.type === 'gif' && gifFrameUrl) {
|
||||||
mediaComponent = <img src={gifFrameUrl} alt='' />;
|
mediaComponent = <img src={gifFrameUrl} alt='' onClick={() => setShowThumbnail(false)} />;
|
||||||
|
} else if (commentMediaInfo?.type === 'audio') {
|
||||||
|
mediaComponent = <audio src={commentMediaInfo.url} controls />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const thumbnailSmallPadding = isMobile ? styles.thumbnailMobile : styles.thumbnailReplyDesktop;
|
const thumbnailSmallPadding = isMobile ? styles.thumbnailMobile : styles.thumbnailReplyDesktop;
|
||||||
@@ -79,7 +90,7 @@ const Media = ({ commentMediaInfo, isMobile, isReply, linkHeight, linkWidth, sho
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<span className={styles.content}>
|
<span className={styles.content}>
|
||||||
<span className={`${showThumbnail ? styles.show : styles.hide} ${styles.thumbnail}`} onClick={() => setShowThumbnail(false)}>
|
<span className={`${showThumbnail ? styles.show : styles.hide} ${styles.thumbnail}`}>
|
||||||
<Thumbnail
|
<Thumbnail
|
||||||
commentMediaInfo={commentMediaInfo}
|
commentMediaInfo={commentMediaInfo}
|
||||||
isMobile={isMobile}
|
isMobile={isMobile}
|
||||||
@@ -111,7 +122,7 @@ const Media = ({ commentMediaInfo, isMobile, isReply, linkHeight, linkWidth, sho
|
|||||||
({commentMediaInfo?.type})
|
({commentMediaInfo?.type})
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{isMobile && (commentMediaInfo?.type === 'iframe' || commentMediaInfo?.type === 'video') && (
|
{isMobile && (commentMediaInfo?.type === 'iframe' || commentMediaInfo?.type === 'video' || commentMediaInfo?.type === 'audio') && (
|
||||||
<div className={styles.closeButton}>
|
<div className={styles.closeButton}>
|
||||||
<span className='button' onClick={() => setShowThumbnail(true)}>
|
<span className='button' onClick={() => setShowThumbnail(true)}>
|
||||||
{t('close')}
|
{t('close')}
|
||||||
|
|||||||
@@ -10,6 +10,15 @@
|
|||||||
padding-bottom: 0.5em;
|
padding-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.closed {
|
||||||
|
font-size: x-large;
|
||||||
|
text-align: center;
|
||||||
|
color: red;
|
||||||
|
font-weight: 700;
|
||||||
|
padding-top: 100px;
|
||||||
|
padding-bottom: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
.postFormButtonDesktop {
|
.postFormButtonDesktop {
|
||||||
display: none;
|
display: none;
|
||||||
|
|||||||
@@ -1,31 +1,58 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import styles from './post-form.module.css';
|
|
||||||
import { isPostPageView } from '../../lib/utils/view-utils';
|
|
||||||
import { useLocation, useParams } from 'react-router-dom';
|
import { useLocation, useParams } from 'react-router-dom';
|
||||||
|
import { useComment } from '@plebbit/plebbit-react-hooks';
|
||||||
|
import { isDescriptionView, isPostPageView, isRulesView } from '../../lib/utils/view-utils';
|
||||||
|
import styles from './post-form.module.css';
|
||||||
|
|
||||||
export interface PostFormProps {
|
const PostForm = () => {
|
||||||
address: string | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PostForm = ({ address }: PostFormProps) => {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const location = useLocation();
|
||||||
|
const params = useParams();
|
||||||
|
const isInPostPage = isPostPageView(location.pathname, params);
|
||||||
|
const isInDescriptionView = isDescriptionView(location.pathname, params);
|
||||||
|
const isInRulesView = isRulesView(location.pathname, params);
|
||||||
|
|
||||||
|
const { subplebbitAddress, commentCid } = params || {};
|
||||||
|
|
||||||
|
const comment = useComment({ commentCid });
|
||||||
|
const { deleted, locked, removed } = comment || {};
|
||||||
|
const isThreadClosed = deleted || locked || removed || isInDescriptionView || isInRulesView;
|
||||||
|
|
||||||
const [showForm, setShowForm] = useState(false);
|
const [showForm, setShowForm] = useState(false);
|
||||||
const isInPostPage = isPostPageView(useLocation().pathname, useParams());
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.postFormButtonDesktop}>
|
<div className={styles.postFormButtonDesktop}>
|
||||||
[
|
{isThreadClosed ? (
|
||||||
<button className='button' onClick={() => setShowForm(true)}>
|
<div className={styles.closed}>
|
||||||
{isInPostPage ? t('post_a_reply') : t('start_new_thread')}
|
{t('thread_closed')}
|
||||||
</button>
|
<br />
|
||||||
]
|
{t('may_not_reply')}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
[
|
||||||
|
<button className='button' onClick={() => setShowForm(true)}>
|
||||||
|
{isInPostPage ? t('post_a_reply') : t('start_new_thread')}
|
||||||
|
</button>
|
||||||
|
]
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.postFormButtonMobile}>
|
<div className={styles.postFormButtonMobile}>
|
||||||
<button className='button' onClick={() => setShowForm(!showForm)}>
|
{isThreadClosed ? (
|
||||||
{showForm ? t('close_post_form') : isInPostPage ? t('post_a_reply') : t('start_new_thread')}
|
<div className={styles.closed}>
|
||||||
</button>
|
Thread closed.
|
||||||
|
<br />
|
||||||
|
You may not reply at this time.
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<button className='button' onClick={() => setShowForm(true)}>
|
||||||
|
{isInPostPage ? t('post_a_reply') : t('start_new_thread')}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -57,6 +57,7 @@
|
|||||||
|
|
||||||
.postDesktop .checkbox, .replyDesktop .checkbox {
|
.postDesktop .checkbox, .replyDesktop .checkbox {
|
||||||
padding: 3px 7px 3px 4px;
|
padding: 3px 7px 3px 4px;
|
||||||
|
filter: var(--filter80);
|
||||||
}
|
}
|
||||||
|
|
||||||
.postDesktop .subject {
|
.postDesktop .subject {
|
||||||
@@ -368,6 +369,11 @@
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.capcodeMod {
|
||||||
|
color: var(--post-capcode-mod-text-color) !important;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
.postDesktop {
|
.postDesktop {
|
||||||
display: none;
|
display: none;
|
||||||
|
|||||||
+243
-213
@@ -1,7 +1,7 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Link, useLocation, useParams } from 'react-router-dom';
|
import { Link, useLocation, useParams } from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Comment } from '@plebbit/plebbit-react-hooks';
|
import { Role, useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
||||||
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
|
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
|
||||||
import { getCommentMediaInfoMemoized, getHasThumbnail } from '../../lib/utils/media-utils';
|
import { getCommentMediaInfoMemoized, getHasThumbnail } from '../../lib/utils/media-utils';
|
||||||
import { getFormattedDate } from '../../lib/utils/time-utils';
|
import { getFormattedDate } from '../../lib/utils/time-utils';
|
||||||
@@ -12,10 +12,158 @@ import styles from './post.module.css';
|
|||||||
import Markdown from '../markdown';
|
import Markdown from '../markdown';
|
||||||
import Media from '../media';
|
import Media from '../media';
|
||||||
|
|
||||||
const ReplyDesktop = ({ reply }: Comment) => {
|
interface PostProps {
|
||||||
|
index?: number;
|
||||||
|
post?: any;
|
||||||
|
reply?: any;
|
||||||
|
roles?: Role[];
|
||||||
|
showAllReplies?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PostDesktop = ({ post, roles, showAllReplies }: PostProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { author, cid, content, link, linkHeight, linkWidth, locked, pinned, postCid, replyCount, shortCid, subplebbitAddress, timestamp, title } = post || {};
|
||||||
|
const { address, displayName, shortAddress } = author || {};
|
||||||
|
const authorRole = roles?.[address]?.role;
|
||||||
|
|
||||||
|
const { isDescription, isRules } = post || {}; // custom properties, not from api
|
||||||
|
|
||||||
|
const isInPostPage = isPostPageView(useLocation().pathname, useParams());
|
||||||
|
|
||||||
|
const displayTitle = title && title.length > 75 ? title?.slice(0, 75) + '...' : title;
|
||||||
|
const displayContent = content && !isInPostPage && content.length > 1000 ? content?.slice(0, 1000) + '(...)' : content;
|
||||||
|
|
||||||
|
const commentMediaInfo = getCommentMediaInfoMemoized(post);
|
||||||
|
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
|
||||||
|
const [showThumbnail, setShowThumbnail] = useState(true);
|
||||||
|
|
||||||
|
const replies = useReplies(post);
|
||||||
|
const visibleLinkCount = useCountLinksInReplies(post, 5);
|
||||||
|
const totalLinkCount = useCountLinksInReplies(post);
|
||||||
|
const repliesCount = pinned ? replyCount : replyCount - 5;
|
||||||
|
const linkCount = pinned ? totalLinkCount : totalLinkCount - visibleLinkCount;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.postDesktop}>
|
||||||
|
<div className={styles.hrWrapper}>
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
{!isInPostPage && (
|
||||||
|
<span className={styles.hideButtonWrapper}>
|
||||||
|
<span className={`${styles.hideButton} ${styles.hideThread}`} />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{commentMediaInfo?.url && (
|
||||||
|
<div className={styles.file}>
|
||||||
|
<div className={styles.fileText}>
|
||||||
|
{t('link')}:{' '}
|
||||||
|
<a href={commentMediaInfo?.url} target='_blank' rel='noopener noreferrer'>
|
||||||
|
{commentMediaInfo.url.length > 30 ? commentMediaInfo?.url?.slice(0, 30) + '...' : commentMediaInfo?.url}
|
||||||
|
</a>{' '}
|
||||||
|
({commentMediaInfo?.type})
|
||||||
|
{!showThumbnail && (commentMediaInfo?.type === 'iframe' || commentMediaInfo?.type === 'video' || commentMediaInfo?.type === 'audio') && (
|
||||||
|
<span>
|
||||||
|
{' '}
|
||||||
|
[
|
||||||
|
<span className={styles.closeMedia} onClick={() => setShowThumbnail(true)}>
|
||||||
|
{t('close')}
|
||||||
|
</span>
|
||||||
|
]
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{hasThumbnail && (
|
||||||
|
<Media
|
||||||
|
commentMediaInfo={commentMediaInfo}
|
||||||
|
isMobile={false}
|
||||||
|
isReply={false}
|
||||||
|
linkHeight={linkHeight}
|
||||||
|
linkWidth={linkWidth}
|
||||||
|
showThumbnail={showThumbnail}
|
||||||
|
setShowThumbnail={setShowThumbnail}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className={styles.postInfo}>
|
||||||
|
<span className={styles.checkbox}>
|
||||||
|
<input type='checkbox' />
|
||||||
|
</span>
|
||||||
|
{title && <span className={styles.subject}>{displayTitle} </span>}
|
||||||
|
<span className={styles.nameBlock}>
|
||||||
|
<span className={`${styles.name} ${(isDescription || isRules || authorRole) && styles.capcodeMod}`}>
|
||||||
|
{displayName || 'Anonymous'}
|
||||||
|
{authorRole && ` ## Board ${authorRole}`}{' '}
|
||||||
|
</span>
|
||||||
|
{!(isDescription || isRules) && <span className={styles.userAddress}>(u/{shortAddress}) </span>}
|
||||||
|
</span>
|
||||||
|
<span className={styles.dateTime}>{getFormattedDate(timestamp)} </span>
|
||||||
|
<span className={styles.postNum}>
|
||||||
|
{!(isDescription || isRules) && (
|
||||||
|
<span className={styles.postNumLink}>
|
||||||
|
<Link to={`/p/${subplebbitAddress}/${cid}`} className={styles.linkToPost} title={t('link_to_post')}>
|
||||||
|
c/
|
||||||
|
</Link>
|
||||||
|
<span className={styles.replyToPost} title={t('reply_to_post')}>
|
||||||
|
{shortCid}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{pinned && (
|
||||||
|
<span className={styles.stickyIconWrapper}>
|
||||||
|
<img src='assets/icons/sticky.gif' alt='' className={styles.stickyIcon} title={t('sticky')} />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{locked && (
|
||||||
|
<span className={styles.closedIconWrapper}>
|
||||||
|
<img src='assets/icons/closed.gif' alt='' className={styles.closedIcon} title={t('closed')} />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<span className={styles.replyButton}>
|
||||||
|
[<Link to={`/p/${subplebbitAddress}/${isDescription ? 'description' : isRules ? 'rules' : `c/${postCid}`}`}>{t('reply')}</Link>]
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span className={styles.postMenuBtn}>▶</span>
|
||||||
|
</div>
|
||||||
|
{!content && <div className={styles.spacer} />}
|
||||||
|
{content && (
|
||||||
|
<blockquote className={styles.postMessage}>
|
||||||
|
<Markdown content={displayContent} />
|
||||||
|
{content.length > 1000 && !isInPostPage && (
|
||||||
|
<span className={styles.abbr}>
|
||||||
|
<br />
|
||||||
|
Comment too long. <Link to={`/p/${subplebbitAddress}/c/${cid}`}>Click here</Link> to view the full text.
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</blockquote>
|
||||||
|
)}
|
||||||
|
{(replies.length > 5 || (pinned && replies.length > 0)) && !isInPostPage && (
|
||||||
|
<span className={styles.summary}>
|
||||||
|
<span className={styles.expandButtonWrapper}>
|
||||||
|
<span className={styles.expandButton} />
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
{repliesCount} replies {linkCount > 0 && `and ${linkCount} links`} omitted.{' '}
|
||||||
|
</span>
|
||||||
|
<Link to={`/p/${subplebbitAddress}/c/${cid}`}>Click here</Link> to view.
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{!(pinned && !isInPostPage) &&
|
||||||
|
replies &&
|
||||||
|
(showAllReplies ? replies : replies.slice(-5)).map((reply, index) => (
|
||||||
|
<div key={reply.cid} className={styles.replyContainer}>
|
||||||
|
<ReplyDesktop index={index} reply={reply} roles={roles} />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ReplyDesktop = ({ reply, roles }: PostProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { author, content, link, linkHeight, linkWidth, parentCid, pinned, postCid, shortCid, subplebbitAddress, timestamp } = reply || {};
|
const { author, content, link, linkHeight, linkWidth, parentCid, pinned, postCid, shortCid, subplebbitAddress, timestamp } = reply || {};
|
||||||
const { displayName, shortAddress } = author || {};
|
const { address, displayName, shortAddress } = author || {};
|
||||||
|
const authorRole = roles?.[address]?.role;
|
||||||
|
|
||||||
const commentMediaInfo = getCommentMediaInfoMemoized(reply);
|
const commentMediaInfo = getCommentMediaInfoMemoized(reply);
|
||||||
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
|
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
|
||||||
@@ -32,7 +180,10 @@ const ReplyDesktop = ({ reply }: Comment) => {
|
|||||||
<input type='checkbox' />
|
<input type='checkbox' />
|
||||||
</span>
|
</span>
|
||||||
<span className={styles.nameBlock}>
|
<span className={styles.nameBlock}>
|
||||||
<span className={styles.name}>{displayName || 'Anonymous'} </span>
|
<span className={`${styles.name} ${authorRole && styles.capcodeMod}`}>
|
||||||
|
{displayName || 'Anonymous'}
|
||||||
|
{authorRole && ` ## Board ${authorRole}`}{' '}
|
||||||
|
</span>
|
||||||
<span className={styles.userAddress}>(u/{shortAddress}) </span>
|
<span className={styles.userAddress}>(u/{shortAddress}) </span>
|
||||||
</span>
|
</span>
|
||||||
<span className={styles.dateTime}>{getFormattedDate(timestamp)} </span>
|
<span className={styles.dateTime}>{getFormattedDate(timestamp)} </span>
|
||||||
@@ -60,7 +211,7 @@ const ReplyDesktop = ({ reply }: Comment) => {
|
|||||||
<a href={link} target='_blank' rel='noopener noreferrer'>
|
<a href={link} target='_blank' rel='noopener noreferrer'>
|
||||||
{link.length > 30 ? link?.slice(0, 30) + '...' : link}
|
{link.length > 30 ? link?.slice(0, 30) + '...' : link}
|
||||||
</a>
|
</a>
|
||||||
{!showThumbnail && (commentMediaInfo?.type === 'iframe' || commentMediaInfo?.type === 'video') && (
|
{!showThumbnail && (commentMediaInfo?.type === 'iframe' || commentMediaInfo?.type === 'video' || commentMediaInfo?.type === 'audio') && (
|
||||||
<span>
|
<span>
|
||||||
{' '}
|
{' '}
|
||||||
[
|
[
|
||||||
@@ -102,140 +253,111 @@ const ReplyDesktop = ({ reply }: Comment) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const PostDesktop = ({ post, showAllReplies }: { post: Comment; showAllReplies: boolean }) => {
|
const PostMobile = ({ post, roles, showAllReplies }: PostProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { author, cid, content, link, linkHeight, linkWidth, locked, pinned, postCid, replyCount, shortCid, subplebbitAddress, timestamp, title } = post || {};
|
const { author, cid, content, link, linkHeight, linkWidth, pinned, replyCount, shortCid, subplebbitAddress, timestamp, title } = post || {};
|
||||||
const { displayName, shortAddress } = author || {};
|
const { address, displayName, shortAddress } = author || {};
|
||||||
|
const authorRole = roles?.[address]?.role;
|
||||||
|
|
||||||
|
const { isDescription, isRules } = post || {}; // custom properties, not from api
|
||||||
|
|
||||||
|
const linkCount = useCountLinksInReplies(post);
|
||||||
const isInPostPage = isPostPageView(useLocation().pathname, useParams());
|
const isInPostPage = isPostPageView(useLocation().pathname, useParams());
|
||||||
|
const displayTitle = title && title.length > 30 ? title?.slice(0, 30) + '(...)' : title;
|
||||||
const displayTitle = title && title.length > 75 ? title?.slice(0, 75) + '...' : title;
|
const displayContent = content && !isInPostPage && content.length > 1000 ? content?.slice(0, 1000) : content;
|
||||||
const displayContent = content && !isInPostPage && content.length > 1000 ? content?.slice(0, 1000) + '(...)' : content;
|
|
||||||
|
|
||||||
const commentMediaInfo = getCommentMediaInfoMemoized(post);
|
const commentMediaInfo = getCommentMediaInfoMemoized(post);
|
||||||
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
|
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
|
||||||
const [showThumbnail, setShowThumbnail] = useState(true);
|
const [showThumbnail, setShowThumbnail] = useState(true);
|
||||||
|
|
||||||
const replies = useReplies(post);
|
const replies = useReplies(post);
|
||||||
const visibleLinkCount = useCountLinksInReplies(post, 5);
|
|
||||||
const totalLinkCount = useCountLinksInReplies(post);
|
|
||||||
const repliesCount = pinned ? replyCount : replyCount - 5;
|
|
||||||
const linkCount = pinned ? totalLinkCount : totalLinkCount - visibleLinkCount;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.postDesktop}>
|
<div className={styles.postMobile}>
|
||||||
<div className={styles.hrWrapper}>
|
<div className={styles.hrWrapper}>
|
||||||
<hr />
|
<hr />
|
||||||
</div>
|
</div>
|
||||||
{!isInPostPage && (
|
<div className={styles.thread}>
|
||||||
<span className={styles.hideButtonWrapper}>
|
<div className={styles.postContainer}>
|
||||||
<span className={`${styles.hideButton} ${styles.hideThread}`} />
|
<div className={styles.postOp}>
|
||||||
</span>
|
<div className={styles.postInfo}>
|
||||||
)}
|
<span className={styles.postMenuBtn} title='Post menu'>
|
||||||
{commentMediaInfo?.url && (
|
...
|
||||||
<div className={styles.file}>
|
|
||||||
<div className={styles.fileText}>
|
|
||||||
{t('link')}:{' '}
|
|
||||||
<a href={commentMediaInfo?.url} target='_blank' rel='noopener noreferrer'>
|
|
||||||
{commentMediaInfo.url.length > 30 ? commentMediaInfo?.url?.slice(0, 30) + '...' : commentMediaInfo?.url}
|
|
||||||
</a>{' '}
|
|
||||||
({commentMediaInfo?.type})
|
|
||||||
{!showThumbnail && (commentMediaInfo?.type === 'iframe' || commentMediaInfo?.type === 'video') && (
|
|
||||||
<span>
|
|
||||||
{' '}
|
|
||||||
[
|
|
||||||
<span className={styles.closeMedia} onClick={() => setShowThumbnail(true)}>
|
|
||||||
{t('close')}
|
|
||||||
</span>
|
|
||||||
]
|
|
||||||
</span>
|
</span>
|
||||||
|
<span className={styles.nameBlock}>
|
||||||
|
<span className={`${styles.name} ${authorRole && styles.capcodeMod}`}>
|
||||||
|
{displayName || 'Anonymous'}
|
||||||
|
{authorRole && ` ## Board ${authorRole}`}{' '}
|
||||||
|
</span>
|
||||||
|
{!(isDescription || isRules) && <span className={styles.address}>(u/{shortAddress || address?.slice(0, 12) + '...'})</span>}
|
||||||
|
{title && (
|
||||||
|
<>
|
||||||
|
<br />
|
||||||
|
<span className={styles.subject}>{displayTitle}</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
<span className={styles.dateTimePostNum}>
|
||||||
|
{getFormattedDate(timestamp)}{' '}
|
||||||
|
{!(isDescription || isRules) && (
|
||||||
|
<>
|
||||||
|
<span className={styles.linkToPost}>c/</span>
|
||||||
|
<span className={styles.replyToPost}>{shortCid}</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{hasThumbnail && (
|
||||||
|
<Media
|
||||||
|
commentMediaInfo={commentMediaInfo}
|
||||||
|
isMobile={true}
|
||||||
|
isReply={false}
|
||||||
|
linkHeight={linkHeight}
|
||||||
|
linkWidth={linkWidth}
|
||||||
|
showThumbnail={showThumbnail}
|
||||||
|
setShowThumbnail={setShowThumbnail}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{content && (
|
||||||
|
<blockquote className={`${styles.postMessage} ${styles.clampLines}`}>
|
||||||
|
<Markdown content={displayContent} />
|
||||||
|
{content.length > 1000 && !isInPostPage && (
|
||||||
|
<span className={styles.abbr}>
|
||||||
|
<br />
|
||||||
|
Comment too long. <Link to={`/p/${subplebbitAddress}/c/${cid}`}>Click here</Link> to view the full text.
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</blockquote>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{hasThumbnail && (
|
{!isInPostPage && (
|
||||||
<Media
|
<div className={styles.postLink}>
|
||||||
commentMediaInfo={commentMediaInfo}
|
<span className={styles.info}>
|
||||||
isMobile={false}
|
{replyCount > 0 && `${replyCount} Replies`}
|
||||||
isReply={false}
|
{linkCount > 0 && ` / ${linkCount} Links`}
|
||||||
linkHeight={linkHeight}
|
</span>
|
||||||
linkWidth={linkWidth}
|
<Link to={`/p/${subplebbitAddress}/${isDescription ? 'description' : isRules ? 'rules' : `c/${cid}`}`} className='button'>
|
||||||
showThumbnail={showThumbnail}
|
{t('view_thread')}
|
||||||
setShowThumbnail={setShowThumbnail}
|
</Link>
|
||||||
/>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
{!(pinned && !isInPostPage) &&
|
||||||
<div className={styles.postInfo}>
|
replies &&
|
||||||
<span className={styles.checkbox}>
|
(showAllReplies ? replies : replies.slice(-5)).map((reply, index) => (
|
||||||
<input type='checkbox' />
|
<div key={reply.cid} className={styles.replyContainer}>
|
||||||
</span>
|
<ReplyMobile index={index} reply={reply} roles={roles} />
|
||||||
{title && <span className={styles.subject}>{displayTitle} </span>}
|
</div>
|
||||||
<span className={styles.nameBlock}>
|
))}
|
||||||
<span className={styles.name}>{displayName || 'Anonymous'} </span>
|
|
||||||
<span className={styles.userAddress}>(u/{shortAddress}) </span>
|
|
||||||
</span>
|
|
||||||
<span className={styles.dateTime}>{getFormattedDate(timestamp)} </span>
|
|
||||||
<span className={styles.postNum}>
|
|
||||||
<span className={styles.postNumLink}>
|
|
||||||
<Link to={`/p/${subplebbitAddress}/c/${cid}`} className={styles.linkToPost} title={t('link_to_post')}>
|
|
||||||
c/
|
|
||||||
</Link>
|
|
||||||
<span className={styles.replyToPost} title={t('reply_to_post')}>
|
|
||||||
{shortCid}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
{pinned && (
|
|
||||||
<span className={styles.stickyIconWrapper}>
|
|
||||||
<img src='assets/icons/sticky.gif' alt='' className={styles.stickyIcon} title={t('sticky')} />
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{locked && (
|
|
||||||
<span className={styles.closedIconWrapper}>
|
|
||||||
<img src='assets/icons/closed.gif' alt='' className={styles.closedIcon} title={t('closed')} />
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
<span className={styles.replyButton}>
|
|
||||||
[<Link to={`/p/${subplebbitAddress}/c/${postCid}`}>{t('reply')}</Link>]
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<span className={styles.postMenuBtn}>▶</span>
|
|
||||||
</div>
|
</div>
|
||||||
{!content && <div className={styles.spacer} />}
|
|
||||||
{content && (
|
|
||||||
<blockquote className={styles.postMessage}>
|
|
||||||
<Markdown content={displayContent} />
|
|
||||||
{content.length > 1000 && !isInPostPage && (
|
|
||||||
<span className={styles.abbr}>
|
|
||||||
<br />
|
|
||||||
Comment too long. <Link to={`/p/${subplebbitAddress}/c/${cid}`}>Click here</Link> to view the full text.
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</blockquote>
|
|
||||||
)}
|
|
||||||
{(replies.length > 5 || pinned) && !isInPostPage && (
|
|
||||||
<span className={styles.summary}>
|
|
||||||
<span className={styles.expandButtonWrapper}>
|
|
||||||
<span className={styles.expandButton} />
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
{repliesCount} replies {linkCount > 0 && `and ${linkCount} links`} omitted.{' '}
|
|
||||||
</span>
|
|
||||||
<Link to={`/p/${subplebbitAddress}/c/${cid}`}>Click here</Link> to view.
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{!(pinned && !isInPostPage) &&
|
|
||||||
replies &&
|
|
||||||
replies.slice(0, showAllReplies ? replies.length : 5).map((reply, index) => (
|
|
||||||
<div key={reply.cid} className={styles.replyContainer}>
|
|
||||||
<ReplyDesktop index={index} reply={reply} />
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ReplyMobile = ({ reply }: Comment) => {
|
const ReplyMobile = ({ reply, roles }: PostProps) => {
|
||||||
const { author, content, link, linkHeight, linkWidth, parentCid, pinned, postCid, shortCid, subplebbitAddress, timestamp } = reply || {};
|
const { author, content, link, linkHeight, linkWidth, parentCid, pinned, postCid, shortCid, subplebbitAddress, timestamp } = reply || {};
|
||||||
const { displayName, shortAddress } = author || {};
|
const { address, displayName, shortAddress } = author || {};
|
||||||
|
const authorRole = roles?.[address]?.role;
|
||||||
|
|
||||||
const commentMediaInfo = getCommentMediaInfoMemoized(reply);
|
const commentMediaInfo = getCommentMediaInfoMemoized(reply);
|
||||||
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
|
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
|
||||||
@@ -250,7 +372,10 @@ const ReplyMobile = ({ reply }: Comment) => {
|
|||||||
<div className={styles.postInfo}>
|
<div className={styles.postInfo}>
|
||||||
<span className={styles.postMenuBtn}>...</span>
|
<span className={styles.postMenuBtn}>...</span>
|
||||||
<span className={styles.nameBlock}>
|
<span className={styles.nameBlock}>
|
||||||
<span className={styles.name}>{displayName || 'Anonymous'} </span>
|
<span className={`${styles.name} ${authorRole && styles.capcodeMod}`}>
|
||||||
|
{displayName || 'Anonymous'}
|
||||||
|
{authorRole && ` ## Board ${authorRole}`}{' '}
|
||||||
|
</span>
|
||||||
<span className={styles.address}>(u/{shortAddress})</span>
|
<span className={styles.address}>(u/{shortAddress})</span>
|
||||||
</span>
|
</span>
|
||||||
<span className={styles.dateTimePostNum}>
|
<span className={styles.dateTimePostNum}>
|
||||||
@@ -288,108 +413,13 @@ const ReplyMobile = ({ reply }: Comment) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const PostMobile = ({ post, showAllReplies }: { post: Comment; showAllReplies: boolean }) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const { author, cid, content, link, linkHeight, linkWidth, pinned, replyCount, shortCid, subplebbitAddress, timestamp, title } = post || {};
|
|
||||||
const { address, displayName, shortAddress } = author || {};
|
|
||||||
|
|
||||||
const linkCount = useCountLinksInReplies(post);
|
|
||||||
const isInPostPage = isPostPageView(useLocation().pathname, useParams());
|
|
||||||
const displayTitle = title && title.length > 30 ? title?.slice(0, 30) + '(...)' : title;
|
|
||||||
const displayContent = content && !isInPostPage && content.length > 1000 ? content?.slice(0, 1000) : content;
|
|
||||||
|
|
||||||
const commentMediaInfo = getCommentMediaInfoMemoized(post);
|
|
||||||
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
|
|
||||||
const [showThumbnail, setShowThumbnail] = useState(true);
|
|
||||||
|
|
||||||
const replies = useReplies(post);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.postMobile}>
|
|
||||||
<div className={styles.hrWrapper}>
|
|
||||||
<hr />
|
|
||||||
</div>
|
|
||||||
<div className={styles.thread}>
|
|
||||||
<div className={styles.postContainer}>
|
|
||||||
<div className={styles.postOp}>
|
|
||||||
<div className={styles.postInfo}>
|
|
||||||
<span className={styles.postMenuBtn} title='Post menu'>
|
|
||||||
...
|
|
||||||
</span>
|
|
||||||
<span className={styles.nameBlock}>
|
|
||||||
<span className={styles.name}>{displayName || 'Anonymous'} </span>
|
|
||||||
<span className={styles.address}>(u/{shortAddress || address?.slice(0, 12) + '...'})</span>
|
|
||||||
{title && (
|
|
||||||
<>
|
|
||||||
<br />
|
|
||||||
<span className={styles.subject}>{displayTitle}</span>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
<span className={styles.dateTimePostNum}>
|
|
||||||
{getFormattedDate(timestamp)} <span className={styles.linkToPost}>c/</span>
|
|
||||||
<span className={styles.replyToPost}>{shortCid}</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{hasThumbnail && (
|
|
||||||
<Media
|
|
||||||
commentMediaInfo={commentMediaInfo}
|
|
||||||
isMobile={true}
|
|
||||||
isReply={false}
|
|
||||||
linkHeight={linkHeight}
|
|
||||||
linkWidth={linkWidth}
|
|
||||||
showThumbnail={showThumbnail}
|
|
||||||
setShowThumbnail={setShowThumbnail}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{content && (
|
|
||||||
<blockquote className={`${styles.postMessage} ${styles.clampLines}`}>
|
|
||||||
<Markdown content={displayContent} />
|
|
||||||
{content.length > 1000 && !isInPostPage && (
|
|
||||||
<span className={styles.abbr}>
|
|
||||||
<br />
|
|
||||||
Comment too long. <Link to={`/p/${subplebbitAddress}/c/${cid}`}>Click here</Link> to view the full text.
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</blockquote>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{!isInPostPage && (
|
|
||||||
<div className={styles.postLink}>
|
|
||||||
<span className={styles.info}>
|
|
||||||
{replyCount} Replies
|
|
||||||
{linkCount > 0 && ` / ${linkCount} Links`}
|
|
||||||
</span>
|
|
||||||
<Link to={`/p/${subplebbitAddress}/c/${cid}`} className='button'>
|
|
||||||
{t('view_thread')}
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{!(pinned && !isInPostPage) &&
|
|
||||||
replies &&
|
|
||||||
replies.slice(0, showAllReplies ? replies.length : 5).map((reply, index) => (
|
|
||||||
<div key={reply.cid} className={styles.replyContainer}>
|
|
||||||
<ReplyMobile index={index} reply={reply} />
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
interface PostProps {
|
|
||||||
index?: number;
|
|
||||||
post: Comment;
|
|
||||||
showAllReplies?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Post = ({ post, showAllReplies = false }: PostProps) => {
|
const Post = ({ post, showAllReplies = false }: PostProps) => {
|
||||||
|
const subplebbit = useSubplebbit({ subplebbitAddress: post?.subplebbitAddress });
|
||||||
return (
|
return (
|
||||||
<div className={styles.thread}>
|
<div className={styles.thread}>
|
||||||
<div className={styles.postContainer}>
|
<div className={styles.postContainer}>
|
||||||
<PostDesktop post={post} showAllReplies={showAllReplies} />
|
<PostDesktop post={post} roles={subplebbit?.roles} showAllReplies={showAllReplies} />
|
||||||
<PostMobile post={post} showAllReplies={showAllReplies} />
|
<PostMobile post={post} roles={subplebbit?.roles} showAllReplies={showAllReplies} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -38,11 +38,6 @@ hr {
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button:hover {
|
|
||||||
color: var(--button-text-color-hover-mobile);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 640px) {
|
@media (min-width: 640px) {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export const getHasThumbnail = (commentMediaInfo: CommentMediaInfo | undefined,
|
|||||||
commentMediaInfo &&
|
commentMediaInfo &&
|
||||||
(commentMediaInfo.type === 'image' ||
|
(commentMediaInfo.type === 'image' ||
|
||||||
commentMediaInfo.type === 'video' ||
|
commentMediaInfo.type === 'video' ||
|
||||||
|
commentMediaInfo.type === 'audio' ||
|
||||||
commentMediaInfo.type === 'gif' ||
|
commentMediaInfo.type === 'gif' ||
|
||||||
(commentMediaInfo.type === 'webpage' && commentMediaInfo.thumbnail) ||
|
(commentMediaInfo.type === 'webpage' && commentMediaInfo.thumbnail) ||
|
||||||
(commentMediaInfo.type === 'iframe' && iframeThumbnail))
|
(commentMediaInfo.type === 'iframe' && iframeThumbnail))
|
||||||
|
|||||||
@@ -7,8 +7,20 @@ export const isHomeView = (pathname: string): boolean => {
|
|||||||
return pathname === '/';
|
return pathname === '/';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isDescriptionView = (pathname: string, params: ParamsType): boolean => {
|
||||||
|
const decodedPathname = decodeURIComponent(pathname);
|
||||||
|
return params.subplebbitAddress ? decodedPathname.startsWith(`/p/${params.subplebbitAddress}/description`) : false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isRulesView = (pathname: string, params: ParamsType): boolean => {
|
||||||
|
const decodedPathname = decodeURIComponent(pathname);
|
||||||
|
return params.subplebbitAddress ? decodedPathname.startsWith(`/p/${params.subplebbitAddress}/rules`) : false;
|
||||||
|
};
|
||||||
|
|
||||||
export const isPostPageView = (pathname: string, params: ParamsType): boolean => {
|
export const isPostPageView = (pathname: string, params: ParamsType): boolean => {
|
||||||
// some subs might use emojis in their address, so we need to decode the pathname
|
if (isDescriptionView(pathname, params) || isRulesView(pathname, params)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
const decodedPathname = decodeURIComponent(pathname);
|
const decodedPathname = decodeURIComponent(pathname);
|
||||||
return params.subplebbitAddress && params.commentCid ? decodedPathname.startsWith(`/p/${params.subplebbitAddress}/c/${params.commentCid}`) : false;
|
return params.subplebbitAddress && params.commentCid ? decodedPathname.startsWith(`/p/${params.subplebbitAddress}/c/${params.commentCid}`) : false;
|
||||||
};
|
};
|
||||||
|
|||||||
+61
-27
@@ -100,6 +100,13 @@
|
|||||||
--post-mobile-file-info-text-color: #707070;
|
--post-mobile-file-info-text-color: #707070;
|
||||||
--post-mobile-content-font-size: 11pt;
|
--post-mobile-content-font-size: 11pt;
|
||||||
|
|
||||||
|
/* post capcodes */
|
||||||
|
--post-capcode-mod-text-color: purple;
|
||||||
|
|
||||||
|
/* post form */
|
||||||
|
--post-form-toggle-font-size: 22px;
|
||||||
|
--post-form-toggle-font-weight: 700;
|
||||||
|
|
||||||
/* quotelink */
|
/* quotelink */
|
||||||
--post-quotelink-text-color: navy;
|
--post-quotelink-text-color: navy;
|
||||||
--post-quotelink-text-color-hover: red;
|
--post-quotelink-text-color-hover: red;
|
||||||
@@ -111,9 +118,8 @@
|
|||||||
--reply-desktop-background-color: #f0e0d6;
|
--reply-desktop-background-color: #f0e0d6;
|
||||||
--reply-desktop-border: 1px solid #d9bfb7;
|
--reply-desktop-border: 1px solid #d9bfb7;
|
||||||
|
|
||||||
/* post form */
|
/* stats */
|
||||||
--post-form-toggle-font-size: 22px;
|
--stats-font-size: 11px;
|
||||||
--post-form-toggle-font-weight: 700;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:root .yotsuba-b {
|
:root .yotsuba-b {
|
||||||
@@ -218,6 +224,13 @@
|
|||||||
--post-mobile-abbr-font-size: 10pt;
|
--post-mobile-abbr-font-size: 10pt;
|
||||||
--post-mobile-content-font-size: 11pt;
|
--post-mobile-content-font-size: 11pt;
|
||||||
|
|
||||||
|
/* post capcodes */
|
||||||
|
--post-capcode-mod-text-color: purple;
|
||||||
|
|
||||||
|
/* post form */
|
||||||
|
--post-form-toggle-font-size: 22px;
|
||||||
|
--post-form-toggle-font-weight: 700;
|
||||||
|
|
||||||
/* quotelink */
|
/* quotelink */
|
||||||
--post-quotelink-text-color: #d00;
|
--post-quotelink-text-color: #d00;
|
||||||
--post-quotelink-text-color-hover: #d00;
|
--post-quotelink-text-color-hover: #d00;
|
||||||
@@ -229,9 +242,8 @@
|
|||||||
--reply-desktop-background-color: #d6daf0;
|
--reply-desktop-background-color: #d6daf0;
|
||||||
--reply-desktop-border: 1px solid #b7c5d9;
|
--reply-desktop-border: 1px solid #b7c5d9;
|
||||||
|
|
||||||
/* post form */
|
/* stats */
|
||||||
--post-form-toggle-font-size: 22px;
|
--stats-font-size: 11px;
|
||||||
--post-form-toggle-font-weight: 700;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:root .futaba {
|
:root .futaba {
|
||||||
@@ -264,15 +276,12 @@
|
|||||||
--button-text-decoration: underline;
|
--button-text-decoration: underline;
|
||||||
|
|
||||||
/* mobile buttons */
|
/* mobile buttons */
|
||||||
--button-font-size-mobile: 12pt;
|
--button-font-size-mobile: 11pt;
|
||||||
--button-font-weight-mobile: unset;
|
--button-font-weight-mobile: 700;
|
||||||
--button-text-color-mobile: #00e;
|
--button-text-color-mobile: #800;
|
||||||
--button-background-image-mobile: none;
|
--button-background-image-mobile: url("/public/assets/buttons/button-fade.png");
|
||||||
--button-background-color-mobile: none;
|
--button-background-color-mobile: #f0e0d6;
|
||||||
--button-border-mobile: none;
|
--button-border-mobile: 1px solid #c0a69d;
|
||||||
--button-text-decoration-mobile: underline;
|
|
||||||
--button-text-color-hover-mobile: red;
|
|
||||||
--button-text-decoration-hover-mobile: underline;
|
|
||||||
|
|
||||||
/* footer */
|
/* footer */
|
||||||
--footer-item-background-color-desktop: #fed;
|
--footer-item-background-color-desktop: #fed;
|
||||||
@@ -314,6 +323,13 @@
|
|||||||
--post-mobile-abbr-font-size: 12pt;
|
--post-mobile-abbr-font-size: 12pt;
|
||||||
--post-mobile-content-font-size: 13pt;
|
--post-mobile-content-font-size: 13pt;
|
||||||
|
|
||||||
|
/* post capcodes */
|
||||||
|
--post-capcode-mod-text-color: purple;
|
||||||
|
|
||||||
|
/* post form */
|
||||||
|
--post-form-toggle-font-size: 22px;
|
||||||
|
--post-form-toggle-font-weight: 700;
|
||||||
|
|
||||||
/* quotelink */
|
/* quotelink */
|
||||||
--post-quotelink-text-color: navy;
|
--post-quotelink-text-color: navy;
|
||||||
--post-quotelink-text-color-hover: red;
|
--post-quotelink-text-color-hover: red;
|
||||||
@@ -325,9 +341,8 @@
|
|||||||
--reply-desktop-background-color: #f0e0d6;
|
--reply-desktop-background-color: #f0e0d6;
|
||||||
--reply-desktop-border: 1px solid #d9bfb7;
|
--reply-desktop-border: 1px solid #d9bfb7;
|
||||||
|
|
||||||
/* post form */
|
/* stats */
|
||||||
--post-form-toggle-font-size: 22px;
|
--stats-font-size: 10pt;
|
||||||
--post-form-toggle-font-weight: 700;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:root .burichan {
|
:root .burichan {
|
||||||
@@ -380,8 +395,6 @@
|
|||||||
--button-background-image-mobile: url("/public/assets/buttons/button-fade-blue.png");
|
--button-background-image-mobile: url("/public/assets/buttons/button-fade-blue.png");
|
||||||
--button-background-color-mobile: #d6daf0;
|
--button-background-color-mobile: #d6daf0;
|
||||||
--button-border-mobile: 1px solid #b7c5d9;
|
--button-border-mobile: 1px solid #b7c5d9;
|
||||||
--button-text-color-hover-mobile: red;
|
|
||||||
--button-text-decoration-hover-mobile: underline;
|
|
||||||
|
|
||||||
/* footer */
|
/* footer */
|
||||||
--footer-item-background-color-desktop: #eef2ff;
|
--footer-item-background-color-desktop: #eef2ff;
|
||||||
@@ -423,6 +436,13 @@
|
|||||||
--post-mobile-abbr-font-size: 12pt;
|
--post-mobile-abbr-font-size: 12pt;
|
||||||
--post-mobile-content-font-size: 13pt;
|
--post-mobile-content-font-size: 13pt;
|
||||||
|
|
||||||
|
/* post capcodes */
|
||||||
|
--post-capcode-mod-text-color: purple;
|
||||||
|
|
||||||
|
/* post form */
|
||||||
|
--post-form-toggle-font-size: 22px;
|
||||||
|
--post-form-toggle-font-weight: 700;
|
||||||
|
|
||||||
/* quotelink */
|
/* quotelink */
|
||||||
--post-quotelink-text-color: #d00;
|
--post-quotelink-text-color: #d00;
|
||||||
--post-quotelink-text-color-hover: #d00;
|
--post-quotelink-text-color-hover: #d00;
|
||||||
@@ -433,10 +453,9 @@
|
|||||||
--side-arrows-color: #b7c5d9;
|
--side-arrows-color: #b7c5d9;
|
||||||
--reply-desktop-background-color: #d6daf0;
|
--reply-desktop-background-color: #d6daf0;
|
||||||
--reply-desktop-border: 1px solid #b7c5d9;
|
--reply-desktop-border: 1px solid #b7c5d9;
|
||||||
|
|
||||||
/* post form */
|
/* stats */
|
||||||
--post-form-toggle-font-size: 22px;
|
--stats-font-size: 10pt;
|
||||||
--post-form-toggle-font-weight: 700;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:root .tomorrow {
|
:root .tomorrow {
|
||||||
@@ -478,6 +497,9 @@
|
|||||||
--button-desktop-text-color: #81a2be;
|
--button-desktop-text-color: #81a2be;
|
||||||
--button-desktop-text-color-hover: #5f89ac;
|
--button-desktop-text-color-hover: #5f89ac;
|
||||||
|
|
||||||
|
/* filter for inputs */
|
||||||
|
--filter80: brightness(85%);
|
||||||
|
|
||||||
/* mobile buttons */
|
/* mobile buttons */
|
||||||
--button-font-size-mobile: 10pt;
|
--button-font-size-mobile: 10pt;
|
||||||
--button-font-weight-mobile: 700;
|
--button-font-weight-mobile: 700;
|
||||||
@@ -529,6 +551,9 @@
|
|||||||
--post-mobile-abbr-font-size: 10pt;
|
--post-mobile-abbr-font-size: 10pt;
|
||||||
--post-mobile-content-font-size: 11pt;
|
--post-mobile-content-font-size: 11pt;
|
||||||
|
|
||||||
|
/* post capcodes */
|
||||||
|
--post-capcode-mod-text-color: #81a2be;
|
||||||
|
|
||||||
/* quotelink */
|
/* quotelink */
|
||||||
--post-quotelink-text-color: #5f89ac;
|
--post-quotelink-text-color: #5f89ac;
|
||||||
--post-quotelink-text-color-hover: #81a2be;
|
--post-quotelink-text-color-hover: #81a2be;
|
||||||
@@ -548,6 +573,9 @@
|
|||||||
--select-border: 1px solid #575a60;
|
--select-border: 1px solid #575a60;
|
||||||
--select-background-color: #282a2e;
|
--select-background-color: #282a2e;
|
||||||
--select-color: #c5c8c6;
|
--select-color: #c5c8c6;
|
||||||
|
|
||||||
|
/* stats */
|
||||||
|
--stats-font-size: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root .photon {
|
:root .photon {
|
||||||
@@ -632,6 +660,13 @@
|
|||||||
--post-mobile-content-font-size: 11pt;
|
--post-mobile-content-font-size: 11pt;
|
||||||
--post-mobile-border-bottom: 1px solid #ccc;
|
--post-mobile-border-bottom: 1px solid #ccc;
|
||||||
|
|
||||||
|
/* post capcodes */
|
||||||
|
--post-capcode-mod-text-color: #f60;
|
||||||
|
|
||||||
|
/* post form */
|
||||||
|
--post-form-toggle-font-size: 22px;
|
||||||
|
--post-form-toggle-font-weight: 700;
|
||||||
|
|
||||||
/* quotelink */
|
/* quotelink */
|
||||||
--post-quotelink-text-color: #f60;
|
--post-quotelink-text-color: #f60;
|
||||||
--post-quotelink-text-color-hover: #f30;
|
--post-quotelink-text-color-hover: #f30;
|
||||||
@@ -643,7 +678,6 @@
|
|||||||
--reply-desktop-background-color: #ddd;
|
--reply-desktop-background-color: #ddd;
|
||||||
--reply-desktop-border: 1px solid #ccc;
|
--reply-desktop-border: 1px solid #ccc;
|
||||||
|
|
||||||
/* post form */
|
/* stats */
|
||||||
--post-form-toggle-font-size: 22px;
|
--stats-font-size: 11px;
|
||||||
--post-form-toggle-font-weight: 700;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,21 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useComment } from '@plebbit/plebbit-react-hooks';
|
import { useComment, useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useLocation, useParams } from 'react-router-dom';
|
||||||
import Post from '../../components/post/post';
|
import Post from '../../components/post/post';
|
||||||
import styles from './post-page.module.css';
|
import styles from './post-page.module.css';
|
||||||
|
import { DescriptionPost, RulesPost } from '../subplebbit/subplebbit';
|
||||||
|
import { isDescriptionView, isRulesView } from '../../lib/utils/view-utils';
|
||||||
|
|
||||||
const PostPage = () => {
|
const PostPage = () => {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const { commentCid } = params;
|
const { commentCid, subplebbitAddress } = params;
|
||||||
|
|
||||||
|
const subplebbit = useSubplebbit({ subplebbitAddress });
|
||||||
|
const { createdAt, description, rules, suggested, title } = subplebbit;
|
||||||
|
|
||||||
|
const location = useLocation();
|
||||||
|
const isInDescriptionView = isDescriptionView(location.pathname, params);
|
||||||
|
const isInRulesView = isRulesView(location.pathname, params);
|
||||||
|
|
||||||
const post = useComment({ commentCid });
|
const post = useComment({ commentCid });
|
||||||
|
|
||||||
@@ -16,7 +25,13 @@ const PostPage = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<Post post={post} showAllReplies={true} />
|
{isInDescriptionView ? (
|
||||||
|
<DescriptionPost avatarUrl={suggested?.avatarUrl} createdAt={createdAt} description={description} subplebbitAddress={subplebbitAddress} title={title} />
|
||||||
|
) : isInRulesView ? (
|
||||||
|
<RulesPost createdAt={createdAt} rules={rules} subplebbitAddress={subplebbitAddress} />
|
||||||
|
) : (
|
||||||
|
<Post post={post} showAllReplies={true} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
.debug {
|
.footer {
|
||||||
position: fixed;
|
padding-top: 10px;
|
||||||
top: 50px;
|
|
||||||
right: 0;
|
|
||||||
padding: 0.5em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.debug select {
|
@media (max-width: 768px) {
|
||||||
margin: 2px;
|
.footer {
|
||||||
border: var(--select-border);
|
padding-left: 5px;
|
||||||
background-color: var(--select-background-color);
|
}
|
||||||
color: var(--select-color);
|
}
|
||||||
}
|
|
||||||
@@ -10,6 +10,52 @@ import Post from '../../components/post';
|
|||||||
|
|
||||||
const lastVirtuosoStates: { [key: string]: StateSnapshot } = {};
|
const lastVirtuosoStates: { [key: string]: StateSnapshot } = {};
|
||||||
|
|
||||||
|
interface DescriptionPostProps {
|
||||||
|
subplebbitAddress: string | undefined;
|
||||||
|
createdAt: number;
|
||||||
|
description: string;
|
||||||
|
avatarUrl?: string;
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DescriptionPost = ({ subplebbitAddress, createdAt, description, avatarUrl, title }: DescriptionPostProps) => {
|
||||||
|
const post = {
|
||||||
|
isDescription: true,
|
||||||
|
subplebbitAddress,
|
||||||
|
timestamp: createdAt,
|
||||||
|
author: { displayName: '## Board Mods' },
|
||||||
|
content: description,
|
||||||
|
link: avatarUrl,
|
||||||
|
title: 'Welcome to ' + title,
|
||||||
|
pinned: true,
|
||||||
|
locked: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Post post={post} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface RulesPostProps {
|
||||||
|
subplebbitAddress: string | undefined;
|
||||||
|
createdAt: number;
|
||||||
|
rules: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RulesPost = ({ subplebbitAddress, createdAt, rules }: RulesPostProps) => {
|
||||||
|
const content = rules.map((rule, index) => `${index + 1}. ${rule}`).join('\n');
|
||||||
|
const post = {
|
||||||
|
isRules: true,
|
||||||
|
subplebbitAddress,
|
||||||
|
timestamp: createdAt,
|
||||||
|
author: { displayName: '## Board Mods' },
|
||||||
|
content,
|
||||||
|
title: 'Rules',
|
||||||
|
pinned: true,
|
||||||
|
locked: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Post post={post} />;
|
||||||
|
};
|
||||||
|
|
||||||
const Subplebbit = () => {
|
const Subplebbit = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { subplebbitAddress } = useParams<{ subplebbitAddress: string }>();
|
const { subplebbitAddress } = useParams<{ subplebbitAddress: string }>();
|
||||||
@@ -18,7 +64,7 @@ const Subplebbit = () => {
|
|||||||
const { feed, hasMore, loadMore } = useFeed({ subplebbitAddresses, sortType });
|
const { feed, hasMore, loadMore } = useFeed({ subplebbitAddresses, sortType });
|
||||||
|
|
||||||
const subplebbit = useSubplebbit({ subplebbitAddress });
|
const subplebbit = useSubplebbit({ subplebbitAddress });
|
||||||
const { shortAddress, state, title } = subplebbit || {};
|
const { createdAt, description, rules, shortAddress, state, suggested, title } = subplebbit || {};
|
||||||
|
|
||||||
const loadingStateString = useFeedStateString(subplebbitAddresses) || t('loading');
|
const loadingStateString = useFeedStateString(subplebbitAddresses) || t('loading');
|
||||||
const loadingString = <div className={styles.stateString}>{state === 'failed' ? state : <LoadingEllipsis string={loadingStateString} />}</div>;
|
const loadingString = <div className={styles.stateString}>{state === 'failed' ? state : <LoadingEllipsis string={loadingStateString} />}</div>;
|
||||||
@@ -56,6 +102,14 @@ const Subplebbit = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
|
{createdAt && (
|
||||||
|
<>
|
||||||
|
{rules && rules.length > 0 && <RulesPost subplebbitAddress={subplebbitAddress} createdAt={createdAt} rules={rules} />}
|
||||||
|
{description && description.length > 0 && (
|
||||||
|
<DescriptionPost avatarUrl={suggested?.avatarUrl} subplebbitAddress={subplebbitAddress} createdAt={createdAt} description={description} title={title} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<Virtuoso
|
<Virtuoso
|
||||||
increaseViewportBy={{ bottom: 1200, top: 1200 }}
|
increaseViewportBy={{ bottom: 1200, top: 1200 }}
|
||||||
totalCount={feed?.length || 0}
|
totalCount={feed?.length || 0}
|
||||||
|
|||||||
Reference in New Issue
Block a user