Merge pull request #382 from plebbit/development

Development
This commit is contained in:
Tom (plebeius.eth)
2024-06-03 20:25:09 +02:00
committed by GitHub
66 changed files with 1318 additions and 832 deletions
+1 -1
View File
@@ -7,7 +7,7 @@
"dependencies": {
"@capacitor/app": "1.1.1",
"@floating-ui/react": "0.26.1",
"@plebbit/plebbit-react-hooks": "https://github.com/plebbit/plebbit-react-hooks.git#f0ae8cd285f282d82eafd290e3b73525faa8ec6b",
"@plebbit/plebbit-react-hooks": "https://github.com/plebbit/plebbit-react-hooks.git#d7fa27920f8ec21c9fa08bc216054083e1d59fc0",
"@testing-library/jest-dom": "5.14.1",
"@testing-library/react": "13.0.0",
"@testing-library/user-event": "13.2.1",
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "مشرفو المنتدى",
"this_post_was_removed": "تمت إزالة هذا المنشور",
"user_deleted_this_post": "حذف المستخدم هذا المنشور.",
"reason": "السبب"
"reason": "السبب",
"search": "بحث",
"ban_user_for": "حظر المستخدم لمدة <1></1> يوم(أيام)",
"optional": "اختياري",
"close_thread": "اغلق الموضوع",
"view_spoiler": "عرض الحاضر",
"filter": "تصفية"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "বোর্ড মডস",
"this_post_was_removed": "এই পোস্টটি সরানো হয়েছে",
"user_deleted_this_post": "ব্যবহারকারী এই পোস্টটি মুছে ফেলেছে।",
"reason": "কারণ"
"reason": "কারণ",
"search": "অনুসন্ধান",
"ban_user_for": "ব্যবহারকারীকে <1></1> দিনের জন্য বন্ধ করুন",
"optional": "ঐচ্ছিক",
"close_thread": "থ্রেড বন্ধ করুন",
"view_spoiler": "স্পোয়ালার দেখুন",
"filter": "ফিল্টার"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "Nástěnka Mods",
"this_post_was_removed": "tento příspěvek byl odstraněn",
"user_deleted_this_post": "Uživatel smazal tento příspěvek.",
"reason": "důvod"
"reason": "důvod",
"search": "vyhledat",
"ban_user_for": "Zakázat uživatele na <1></1> den (dní)",
"optional": "volitelný",
"close_thread": "uzavřít vlákno",
"view_spoiler": "zobrazit spoiler",
"filter": "filtr"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "Board Mods",
"this_post_was_removed": "dette indlæg blev fjernet",
"user_deleted_this_post": "Brugeren slettede dette indlæg.",
"reason": "grund"
"reason": "grund",
"search": "søg",
"ban_user_for": "Udeluk brugeren i <1></1> dag(e)",
"optional": "valgfri",
"close_thread": "luk tråd",
"view_spoiler": "vis spoiler",
"filter": "filtrér"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "Board-Moderatoren",
"this_post_was_removed": "Dieser Beitrag wurde entfernt",
"user_deleted_this_post": "Benutzer hat diesen Beitrag gelöscht.",
"reason": "Grund"
"reason": "Grund",
"search": "suche",
"ban_user_for": "Benutzer für <1></1> Tag(e) sperren",
"optional": "optional",
"close_thread": "Thread schließen",
"view_spoiler": "Spoiler anzeigen",
"filter": "filtern"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "Mods του φόρουμ",
"this_post_was_removed": "αυτή η ανάρτηση αφαιρέθηκε",
"user_deleted_this_post": "Ο χρήστης διέγραψε αυτή την ανάρτηση.",
"reason": "λόγος"
"reason": "λόγος",
"search": "αναζήτηση",
"ban_user_for": "Αποκλεισμός χρήστη για <1></1> ημέρα(ες)",
"optional": "προαιρετικό",
"close_thread": "κλείσιμο νήματος",
"view_spoiler": "προβολή spoiler",
"filter": "φίλτρο"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "Board Mods",
"this_post_was_removed": "this post was removed",
"user_deleted_this_post": "User deleted this post.",
"reason": "reason"
"reason": "reason",
"search": "search",
"ban_user_for": "Ban user for <1></1> day(s)",
"optional": "optional",
"close_thread": "close thread",
"view_spoiler": "view spoiler",
"filter": "filter"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "Mods del foro",
"this_post_was_removed": "este mensaje fue eliminado",
"user_deleted_this_post": "El usuario eliminó este mensaje.",
"reason": "razón"
"reason": "razón",
"search": "buscar",
"ban_user_for": "Prohibir al usuario durante <1></1> día(s)",
"optional": "opcional",
"close_thread": "cerrar hilo",
"view_spoiler": "ver spoiler",
"filter": "filtrar"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "مدیران تالار",
"this_post_was_removed": "این پست حذف شد",
"user_deleted_this_post": "کاربر این پست را حذف کرده است.",
"reason": "دلیل"
"reason": "دلیل",
"search": "جستجو",
"ban_user_for": "بستن کاربر برای <1></1> روز",
"optional": "اختیاری",
"close_thread": "بستن موضوع",
"view_spoiler": "نمایش اسپویلر",
"filter": "فیلتر"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "Hallituksen Mods",
"this_post_was_removed": "tämä viesti on poistettu",
"user_deleted_this_post": "Käyttäjä poisti tämän viestin.",
"reason": "syy"
"reason": "syy",
"search": "haku",
"ban_user_for": "Estä käyttäjä <1></1> päiväksi",
"optional": "valinnainen",
"close_thread": "sulje keskustelu",
"view_spoiler": "näytä spoiler",
"filter": "suodatin"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "Board Mods",
"this_post_was_removed": "tinanggal ang post na ito",
"user_deleted_this_post": "Tinanggal ng gumagamit ang post na ito.",
"reason": "dahilan"
"reason": "dahilan",
"search": "maghanap",
"ban_user_for": "I-ban ang user para sa <1></1> araw",
"optional": "opsyonal",
"close_thread": "isara ang thread",
"view_spoiler": "tingnan ang spoiler",
"filter": "salain"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "Mods de la table",
"this_post_was_removed": "ce post a été supprimé",
"user_deleted_this_post": "L'utilisateur a supprimé ce post.",
"reason": "raison"
"reason": "raison",
"search": "chercher",
"ban_user_for": "Bannir l'utilisateur pendant <1></1> jour(s)",
"optional": "facultatif",
"close_thread": "fermer le fil",
"view_spoiler": "voir le spoiler",
"filter": "filtre"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "מנהלי הפורום",
"this_post_was_removed": "הודעה זו הוסרה",
"user_deleted_this_post": "המשתמש מחק את הפוסט הזה.",
"reason": "סיבה"
"reason": "סיבה",
"search": "חיפוש",
"ban_user_for": "אסור למשתמש למשך <1></1> יום",
"optional": "אופציונלי",
"close_thread": "סגור את הנושא",
"view_spoiler": "הצג ספוילר",
"filter": "סנן"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "बोर्ड मॉड्स",
"this_post_was_removed": "यह पोस्ट हटा दी गई है",
"user_deleted_this_post": "उपयोगकर्ता ने इस पोस्ट को हटा दिया।",
"reason": "कारण"
"reason": "कारण",
"search": "खोज",
"ban_user_for": "उपयोगकर्ता को <1></1> दिनों के लिए प्रतिबंधित करें",
"optional": "वैकल्पिक",
"close_thread": "थ्रेड बंद करें",
"view_spoiler": "स्पॉइलर देखें",
"filter": "फ़िल्टर"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "Tábla Mods",
"this_post_was_removed": "ez a bejegyzés eltávolításra került",
"user_deleted_this_post": "A felhasználó törölte ezt a bejegyzést.",
"reason": "indok"
"reason": "indok",
"search": "keresés",
"ban_user_for": "Felhasználó letiltása <1></1> napra",
"optional": "opcionális",
"close_thread": "szál bezárása",
"view_spoiler": "spoiler megtekintése",
"filter": "szűrő"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "Papan Pesan Mods",
"this_post_was_removed": "posting ini telah dihapus",
"user_deleted_this_post": "Pengguna menghapus posting ini.",
"reason": "alasan"
"reason": "alasan",
"search": "cari",
"ban_user_for": "Blokir pengguna selama <1></1> hari",
"optional": "opsional",
"close_thread": "tutup utas",
"view_spoiler": "lihat spoiler",
"filter": "menyaring"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "Moderatori del Forum",
"this_post_was_removed": "questo post è stato rimosso",
"user_deleted_this_post": "L'utente ha eliminato questo post.",
"reason": "motivo"
"reason": "motivo",
"search": "cerca",
"ban_user_for": "Banna l'utente per <1></1> giorno/i",
"optional": "opzionale",
"close_thread": "chiudi il thread",
"view_spoiler": "mostra spoiler",
"filter": "filtro"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "ボードの管理者",
"this_post_was_removed": "この投稿は削除されました",
"user_deleted_this_post": "ユーザーがこの投稿を削除しました。",
"reason": "理由"
"reason": "理由",
"search": "検索",
"ban_user_for": "ユーザーを<1></1>日間禁止する",
"optional": "オプショナル",
"close_thread": "スレッドを閉じる",
"view_spoiler": "スポイラーを表示",
"filter": "フィルター"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "게시판 모드",
"this_post_was_removed": "이 게시물은 삭제되었습니다",
"user_deleted_this_post": "사용자가이 게시물을 삭제했습니다.",
"reason": "이유"
"reason": "이유",
"search": "검색",
"ban_user_for": "사용자를 <1></1>일 동안 차단하기",
"optional": "선택",
"close_thread": "스레드 닫기",
"view_spoiler": "스포일러 보기",
"filter": "필터"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "बोर्ड मॉड्स",
"this_post_was_removed": "हा पोस्ट काढून टाकला गेला आहे",
"user_deleted_this_post": "वापरकर्ता हे पोस्ट हटवलं आहे.",
"reason": "कारण"
"reason": "कारण",
"search": "शोध",
"ban_user_for": "वापरकर्ता ब्लॉक करा <1></1> दिवस",
"optional": "वैकल्पिक",
"close_thread": "थ्रेड बंद करा",
"view_spoiler": "स्पॉईलर पहा",
"filter": "फिल्टर"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "Board Mods",
"this_post_was_removed": "deze post is verwijderd",
"user_deleted_this_post": "Gebruiker heeft deze post verwijderd.",
"reason": "reden"
"reason": "reden",
"search": "zoeken",
"ban_user_for": "Gebruiker verbannen voor <1></1> dag(en)",
"optional": "optioneel",
"close_thread": "draad sluiten",
"view_spoiler": "bekijk spoiler",
"filter": "filter"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "Board Mods",
"this_post_was_removed": "dette innlegget ble fjernet",
"user_deleted_this_post": "Brukeren slettet dette innlegget.",
"reason": "grunn"
"reason": "grunn",
"search": "søk",
"ban_user_for": "Blokker bruker i <1></1> dag(er)",
"optional": "valgfritt",
"close_thread": "lukk tråd",
"view_spoiler": "se spoiler",
"filter": "filter"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "Tablica Mods",
"this_post_was_removed": "ten post został usunięty",
"user_deleted_this_post": "Użytkownik usunął ten post.",
"reason": "powód"
"reason": "powód",
"search": "szukaj",
"ban_user_for": "Zablokuj użytkownika na <1></1> dzień (dni)",
"optional": "opcjonalnie",
"close_thread": "zamknij wątek",
"view_spoiler": "pokaż spoiler",
"filter": "filtr"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "Mods do fórum",
"this_post_was_removed": "esta postagem foi removida",
"user_deleted_this_post": "O usuário excluiu esta postagem.",
"reason": "razão"
"reason": "razão",
"search": "pesquisar",
"ban_user_for": "Banir usuário por <1></1> dia(s)",
"optional": "opcional",
"close_thread": "fechar tópico",
"view_spoiler": "ver spoiler",
"filter": "filtro"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "Mods de la bord",
"this_post_was_removed": "acest mesaj a fost eliminat",
"user_deleted_this_post": "Utilizatorul a șters acest mesaj.",
"reason": "motiv"
"reason": "motiv",
"search": "căutare",
"ban_user_for": "Interzice utilizatorul pentru <1></1> zi(zile)",
"optional": "opțional",
"close_thread": "închide firul",
"view_spoiler": "vezi spoilerul",
"filter": "filtru"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "Модераторы форума",
"this_post_was_removed": "этот пост был удален",
"user_deleted_this_post": "Пользователь удалил этот пост.",
"reason": "причина"
"reason": "причина",
"search": "поиск",
"ban_user_for": "Заблокировать пользователя на <1></1> день(дней)",
"optional": "необязательный",
"close_thread": "закрыть тему",
"view_spoiler": "посмотреть спойлер",
"filter": "фильтр"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "Tabela Mods",
"this_post_was_removed": "kjo postim u hoq",
"user_deleted_this_post": "Përdoruesi fshiu këtë post.",
"reason": "arsye"
"reason": "arsye",
"search": "kërkim",
"ban_user_for": "Ndalo përdoruesin për <1></1> ditë",
"optional": "opsionale",
"close_thread": "mbyll temën",
"view_spoiler": "shikoni spoiler",
"filter": "filtruar"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "Styrelse Mods",
"this_post_was_removed": "detta inlägg togs bort",
"user_deleted_this_post": "Användaren tog bort detta inlägg.",
"reason": "anledning"
"reason": "anledning",
"search": "sök",
"ban_user_for": "Blockera användaren i <1></1> dag(ar)",
"optional": "valfritt",
"close_thread": "stäng tråd",
"view_spoiler": "visa spoiler",
"filter": "filter"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "సందేశ బోర్డుల మాడ్స్",
"this_post_was_removed": "ఈ పోస్టు తొలగించబడింది",
"user_deleted_this_post": "వాడు ఈ పోస్టును తొలగించాడు.",
"reason": "కారణం"
"reason": "కారణం",
"search": "శోధన",
"ban_user_for": "<1></1> రోజుల కోసం వాడుకరినీ బ్యాన్ చేయండి",
"optional": "ఐచ్ఛికం",
"close_thread": "థ్రెడ్‌ను మూసేయండి",
"view_spoiler": "స్పోయ్లర్ వీక్షించండి",
"filter": "వడపోత"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "บอร์ด Mods",
"this_post_was_removed": "โพสต์นี้ถูกลบ",
"user_deleted_this_post": "ผู้ใช้ได้ลบโพสต์นี้",
"reason": "เหตุผล"
"reason": "เหตุผล",
"search": "ค้นหา",
"ban_user_for": "แบนผู้ใช้ <1></1> วัน",
"optional": "ไม่จำเป็น",
"close_thread": "ปิดกระทู้",
"view_spoiler": "ดูสปอยล์เลอร์",
"filter": "ตัวกรอง"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "Forum Mods",
"this_post_was_removed": "bu mesaj kaldırıldı",
"user_deleted_this_post": "Kullanıcı bu mesajı sildi.",
"reason": "neden"
"reason": "neden",
"search": "arama",
"ban_user_for": "Kullanıcıyı <1></1> gün boyunca yasakla",
"optional": "isteğe bağlı",
"close_thread": "konuyu kapat",
"view_spoiler": "spoileri görüntüle",
"filter": "filtre"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "Дошка Моди",
"this_post_was_removed": "цей пост було видалено",
"user_deleted_this_post": "Користувач видалив цей пост.",
"reason": "причина"
"reason": "причина",
"search": "пошук",
"ban_user_for": "Заборонити користувача на <1></1> день (днів)",
"optional": "необов’язково",
"close_thread": "закрити тему",
"view_spoiler": "переглянути спойлер",
"filter": "фільтр"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "بورڈ موڈس",
"this_post_was_removed": "یہ پوسٹ ہٹا دی گئی ہے",
"user_deleted_this_post": "صارف نے یہ پوسٹ حذف کردی.",
"reason": "وجہ"
"reason": "وجہ",
"search": "تلاش کریں",
"ban_user_for": "<1></1> دنوں کے لیے صارف کو پابندی عائد کریں",
"optional": "اختیاری",
"close_thread": "موضوع بند کریں",
"view_spoiler": "اسپوائلر دیکھیں",
"filter": "فلٹر"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "Bảng Mods",
"this_post_was_removed": "bài đăng này đã bị xóa",
"user_deleted_this_post": "Người dùng đã xóa bài đăng này.",
"reason": "lý do"
"reason": "lý do",
"search": "tìm kiếm",
"ban_user_for": "Cấm người dùng trong <1></1> ngày",
"optional": "tùy chọn",
"close_thread": "đóng chủ đề",
"view_spoiler": "xem spoiler",
"filter": "bộ lọc"
}
+7 -1
View File
@@ -141,5 +141,11 @@
"board_mods": "董事會版主",
"this_post_was_removed": "此帖已被删除",
"user_deleted_this_post": "用户删除了这个帖子。",
"reason": "原因"
"reason": "原因",
"search": "搜索",
"ban_user_for": "封禁用户 <1></1> 天",
"optional": "可选的",
"close_thread": "关闭主题",
"view_spoiler": "查看剧透",
"filter": "过滤器"
}
+3 -5
View File
@@ -18,15 +18,13 @@ import SubplebbitStats from './components/subplebbit-stats';
import TopBar from './components/topbar';
const BoardLayout = () => {
const { accountCommentIndex, commentCid, subplebbitAddress } = useParams();
const { accountCommentIndex, subplebbitAddress } = useParams();
const location = useLocation();
const isMobile = useIsMobile();
const isValidAccountCommentIndex = !accountCommentIndex || (!isNaN(parseInt(accountCommentIndex)) && parseInt(accountCommentIndex) >= 0);
const isValidCommentCid = !commentCid || /^Qm[a-zA-Z0-9]{44}$/.test(commentCid);
const isValidSubplebbitAddress = !subplebbitAddress || subplebbitAddress.includes('.') || /^12D3KooW[a-zA-Z0-9]{44}$/.test(subplebbitAddress);
if (!isValidAccountCommentIndex || !isValidCommentCid || !isValidSubplebbitAddress) {
if (!isValidAccountCommentIndex) {
return <NotFound />;
}
@@ -45,7 +43,7 @@ const BoardLayout = () => {
) : (
<>
<PostForm key={key} />
{subplebbitAddress && isValidSubplebbitAddress && <SubplebbitStats />}
{subplebbitAddress && <SubplebbitStats />}
<DesktopBoardButtons />
</>
)}
+2 -13
View File
@@ -13,11 +13,6 @@ interface BoardButtonsProps {
isInSubscriptionsView?: boolean;
}
const OptionsButton = () => {
const { t } = useTranslation();
return <button className='button'>{t('options')}</button>;
};
const CatalogButton = ({ address, isInAllView, isInSubscriptionsView }: BoardButtonsProps) => {
const { t } = useTranslation();
const link = isInAllView ? `/p/all/catalog` : isInSubscriptionsView ? `/p/subscriptions/catalog` : `/p/${address}/catalog`;
@@ -96,7 +91,6 @@ export const MobileBoardButtons = () => {
<>
<ReturnButton address={subplebbitAddress} />
<CatalogButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} />
<OptionsButton />
<SubscribeButton address={subplebbitAddress} />
</>
) : (
@@ -106,7 +100,6 @@ export const MobileBoardButtons = () => {
) : (
<CatalogButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} />
)}
<OptionsButton />
<SubscribeButton address={subplebbitAddress} />
<RefreshButton />
{isInCatalogView && (
@@ -143,9 +136,7 @@ export const DesktopBoardButtons = () => {
[
<ReturnButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} />
] [
<CatalogButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} />
] [
<OptionsButton />]
<CatalogButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} />]
{isInPostView && (
<span className={styles.rightSideButtons}>
[
@@ -166,9 +157,7 @@ export const DesktopBoardButtons = () => {
<CatalogButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} />]{' '}
</>
)}
[
<OptionsButton />] [
<RefreshButton />]
[<RefreshButton />]
<span className={styles.rightSideButtons}>
{isInCatalogView && <SortOptions />}
{!(isInAllView || isInSubscriptionsView) && (
+1 -1
View File
@@ -39,7 +39,7 @@ const BoardHeader = () => {
<div className={styles.bannerCnt}>
<ImageBanner key={isInAllView ? 'all' : isInSubscriptionsView ? 'subscriptions' : address} />
</div>
<div className={styles.boardTitle}>{title || `p/${shortAddress}`}</div>
<div className={styles.boardTitle}>{title || `p/${shortAddress || subplebbitAddress}`}</div>
<div className={styles.boardSubtitle}>{subtitle}</div>
<hr />
</div>
+2 -2
View File
@@ -55,7 +55,7 @@ export const CatalogPostMedia = ({ commentMediaInfo, isOutOfFeed, linkWidth, lin
<img src={thumbnail} alt='' onLoad={handleLoad} onError={handleError} style={loadingStyle} />
) : (
// show first frame of the video, as a workaround for Safari not loading thumbnails
<video src={`${url}#t=0.001`} onLoad={handleLoad} onError={handleError} style={loadingStyle} />
<video src={`${url}#t=0.001`} onError={handleError} />
);
} else if (type === 'webpage' && !hasError) {
thumbnailComponent = <img src={thumbnail} alt='' onLoad={handleLoad} onError={handleError} style={loadingStyle} />;
@@ -69,7 +69,7 @@ export const CatalogPostMedia = ({ commentMediaInfo, isOutOfFeed, linkWidth, lin
return (
<div className={hasError ? '' : styles.mediaWrapper} style={thumbnailDimensions}>
{!isLoaded && !hasError && <span className={styles.loadingSkeleton} />}
{!isLoaded && !hasError && type !== 'video' && <span className={styles.loadingSkeleton} />}
{hasError ? <img className={styles.fileDeleted} src='/assets/filedeleted-res.gif' alt='File deleted' /> : thumbnailComponent}
</div>
);
@@ -18,6 +18,17 @@
display: inline-block;
}
.spoiler {
display: inline-block;
margin: 3px 20px 5px 20px;
background-color: black;
color: white;
display: flex;
cursor: pointer;
justify-content: center;
align-items: center;
}
@media (max-width: 640px) {
.subplebbitAvatar {
max-width: 125px;
@@ -52,6 +63,10 @@
float: left;
}
.thumbnail video {
cursor: pointer;
}
.hide {
display: none !important;
}
+18 -18
View File
@@ -1,5 +1,6 @@
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Comment } from '@plebbit/plebbit-react-hooks';
import styles from './comment-media.module.css';
import { CommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail } from '../../lib/utils/media-utils';
import { getHostname } from '../../lib/utils/url-utils';
@@ -9,16 +10,22 @@ import Embed, { canEmbed } from '../embed';
interface MediaProps {
commentMediaInfo?: CommentMediaInfo;
isDeleted?: boolean;
isOutOfFeed?: boolean; // virtuoso wrapper unneeded
isReply: boolean;
post?: Comment;
isReply?: boolean;
linkHeight?: number;
linkWidth?: number;
showThumbnail?: boolean;
setShowThumbnail: (showThumbnail: boolean) => void;
}
const Thumbnail = ({ commentMediaInfo, isDeleted, isOutOfFeed, isReply, linkHeight, linkWidth, setShowThumbnail }: MediaProps) => {
const Thumbnail = ({ commentMediaInfo, 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 isReply = parentCid;
const isDeleted = deleted || removed;
const { patternThumbnailUrl, thumbnail, type, url } = commentMediaInfo || {};
const [hasError, setHasError] = useState(false);
const handleError = () => setHasError(true);
@@ -72,6 +79,10 @@ const Thumbnail = ({ commentMediaInfo, isDeleted, isOutOfFeed, isReply, linkHeig
return hasError || isDeleted ? (
<img className={styles.fileDeleted} src='/assets/filedeleted-res.gif' alt='File deleted' />
) : spoiler ? (
<span className={styles.spoiler} style={{ width: maxThumbnailSize, height: maxThumbnailSize }} onClick={() => setShowThumbnail(false)}>
<span className={styles.spoilerText}>[{t('view_spoiler')}]</span>
</span>
) : isOutOfFeed ? (
<span className={styles.subplebbitAvatar}>{thumbnailComponent}</span>
) : isMobile || isReply ? (
@@ -133,7 +144,7 @@ const Media = ({ commentMediaInfo, isReply, setShowThumbnail }: MediaProps) => {
);
};
const CommentMedia = ({ commentMediaInfo, isDeleted, isOutOfFeed, isReply, linkHeight, linkWidth, showThumbnail, setShowThumbnail }: MediaProps) => {
const CommentMedia = ({ commentMediaInfo, post, showThumbnail, setShowThumbnail }: MediaProps) => {
const { t } = useTranslation();
const isMobile = useIsMobile();
const { type, url } = commentMediaInfo || {};
@@ -141,21 +152,10 @@ const CommentMedia = ({ commentMediaInfo, isDeleted, isOutOfFeed, isReply, linkH
return (
<span className={styles.content}>
<span className={`${showThumbnail ? styles.show : styles.hide} ${styles.thumbnail}`}>
{url && (
<Thumbnail
commentMediaInfo={commentMediaInfo}
isDeleted={isDeleted}
isOutOfFeed={isOutOfFeed}
isReply={isReply}
linkHeight={linkHeight}
linkWidth={linkWidth}
showThumbnail={showThumbnail}
setShowThumbnail={setShowThumbnail}
/>
)}
{url && <Thumbnail commentMediaInfo={commentMediaInfo} post={post} setShowThumbnail={setShowThumbnail} />}
{isMobile && type && <div className={styles.fileInfo}>{getDisplayMediaInfoType(type, t)}</div>}
</span>
{!showThumbnail && <Media commentMediaInfo={commentMediaInfo} isReply={isReply} setShowThumbnail={setShowThumbnail} />}
{!showThumbnail && <Media commentMediaInfo={commentMediaInfo} isReply={post?.parentCid} setShowThumbnail={setShowThumbnail} />}
</span>
);
};
@@ -28,4 +28,14 @@
.embedButton:hover {
cursor: pointer;
color: var(--button-desktop-text-color-hover);
}
.spoiler {
background-color: black;
color: black;
width: fit-content;
}
.spoiler:hover {
color: white;
}
+3 -1
View File
@@ -10,6 +10,7 @@ import CommentMedia from '../comment-media';
interface MarkdownProps {
content: string;
spoiler?: boolean;
}
const MAX_LENGTH_FOR_GFM = 10000; // remarkGfm lags with large content
@@ -37,7 +38,7 @@ const blockquoteToGreentext = () => (tree: any) => {
});
};
const Markdown = ({ content }: MarkdownProps) => {
const Markdown = ({ content, spoiler }: MarkdownProps) => {
const remarkPlugins: any[] = [[supersub]];
if (content && content.length <= MAX_LENGTH_FOR_GFM) {
@@ -68,6 +69,7 @@ const Markdown = ({ content }: MarkdownProps) => {
remarkPlugins={remarkPlugins}
rehypePlugins={[[rehypeSanitize, customSchema]]}
components={{
p: ({ children }) => <p className=''>{spoiler ? <span className={styles.spoiler}>{children}</span> : children}</p>,
img: ({ src }) => <span>{src}</span>,
video: ({ src }) => <span>{src}</span>,
iframe: ({ src }) => <span>{src}</span>,
+1
View File
@@ -0,0 +1 @@
export { default } from './mod-menu';
@@ -0,0 +1,84 @@
.checkbox {
display: inline-block;
padding: 3px 7px 3px 4px;
filter: var(--filter80);
}
.button {
color: var(--text-info);
font-weight: bold;
text-decoration: none;
padding: 0 4px 0 4px;
cursor: pointer;
}
.modal {
z-index: 7;
background-color: var(--challenge-modal-background-color);
padding: 1px 0;
padding-top: 2px;
color: var(--text);
border: var(--challenge-modal-border);
min-width: 200px;
}
.modal input[type="checkbox"] {
margin-right: 3px;
outline: none;
cursor: pointer;
margin: 0 3px;
vertical-align: middle;
transform: translateY(-2px);
}
.menuItem {
padding: 2px 3px 1px 3px;
}
.menuItem label {
cursor: pointer;
}
.menuReason {
padding-bottom: 2px;
}
.menuItem input[type="text"] {
padding: 2px;
box-shadow: var(--box-shadow-input);
margin-top: 2px;
width: calc(100% - 6px);
margin-bottom: 2px;
}
.menuItem input[type="text"], .menuItem input[type='number'] {
border: var(--post-form-field-input-border);
outline: none;
}
.menuItem input[type="text"]:focus, .menuItem input[type='number']:focus {
border: var(--post-form-field-input-focus-border);
}
.bottom {
clear: both;
display: block;
}
.bottom button {
text-transform: capitalize;
cursor: pointer;
padding: 2px 6px 3px;
margin-left: 2px;
margin-bottom: 2px;
}
.optional {
color: var(--text-info);
}
@supports (-moz-appearance: none) {
.banInput {
width: 3.5em;
}
}
+175
View File
@@ -0,0 +1,175 @@
import { useState } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { autoUpdate, flip, FloatingFocusManager, offset, shift, useClick, useDismiss, useFloating, useId, useInteractions, useRole } from '@floating-ui/react';
import { PublishCommentEditOptions, useComment, useEditedComment, usePublishCommentEdit } from '@plebbit/plebbit-react-hooks';
import styles from './mod-menu.module.css';
import { alertChallengeVerificationFailed } from '../../../lib/utils/challenge-utils';
import useChallengesStore from '../../../stores/use-challenges-store';
import _ from 'lodash';
const { addChallenge } = useChallengesStore.getState();
type ModMenuProps = {
cid: string;
isCommentAuthorMod?: boolean;
};
const ModMenu = ({ cid, isCommentAuthorMod }: ModMenuProps) => {
const { t } = useTranslation();
let post: any;
const comment = useComment({ commentCid: cid });
const { editedComment } = useEditedComment({ comment });
if (editedComment) {
post = editedComment;
} else if (comment) {
post = comment;
}
const isReply = post?.parentCid;
const [isModMenuOpen, setIsModMenuOpen] = useState(false);
const defaultPublishOptions: PublishCommentEditOptions = {
removed: post?.removed,
locked: post?.locked,
spoiler: post?.spoiler,
pinned: post?.pinned,
commentAuthor: { banExpiresAt: post?.banExpiresAt },
commentCid: post?.cid,
subplebbitAddress: post?.subplebbitAddress,
onChallenge: (...args: any) => addChallenge([...args, post]),
onChallengeVerification: alertChallengeVerificationFailed,
onError: (error: Error) => {
console.warn(error);
alert(error.message);
},
};
const [publishCommentEditOptions, setPublishCommentEditOptions] = useState(defaultPublishOptions);
const { publishCommentEdit } = usePublishCommentEdit(publishCommentEditOptions);
const [banDuration, setBanDuration] = useState(1);
const daysToTimestampInSeconds = (days: number) => {
const now = new Date();
now.setDate(now.getDate() + days);
return Math.floor(now.getTime() / 1000);
};
const onCheckbox = (e: React.ChangeEvent<HTMLInputElement>) => {
const { id, checked } = e.target;
if (id === 'banUser') {
setPublishCommentEditOptions((state) => ({
...state,
commentAuthor: { ...state.commentAuthor, banExpiresAt: checked ? daysToTimestampInSeconds(banDuration) : undefined },
}));
} else {
setPublishCommentEditOptions((state) => ({ ...state, [id]: checked }));
}
};
const onBanDurationChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const days = parseInt(e.target.value, 10) || 1;
setBanDuration(days);
setPublishCommentEditOptions((state) => ({
...state,
commentAuthor: { ...state.commentAuthor, banExpiresAt: daysToTimestampInSeconds(days) },
}));
};
const onReason = (e: React.ChangeEvent<HTMLInputElement>) =>
setPublishCommentEditOptions((state) => ({ ...state, reason: e.target.value ? e.target.value : undefined }));
const { refs, floatingStyles, context } = useFloating({
placement: 'bottom-start',
open: isModMenuOpen,
onOpenChange: setIsModMenuOpen,
middleware: [offset(2), flip({ fallbackAxisSideDirection: 'end' }), shift()],
whileElementsMounted: autoUpdate,
});
const click = useClick(context);
const dismiss = useDismiss(context);
const role = useRole(context);
const { getReferenceProps, getFloatingProps } = useInteractions([click, dismiss, role]);
const headingId = useId();
const handleSaveClick = async () => {
await publishCommentEdit();
setIsModMenuOpen(false);
};
return (
<>
<span className={styles.checkbox} ref={refs.setReference} {...(cid && getReferenceProps())}>
<input type='checkbox' onChange={() => cid && setIsModMenuOpen(!isModMenuOpen)} checked={isModMenuOpen} />
</span>
{isModMenuOpen && (
<FloatingFocusManager context={context} modal={false}>
<div className={styles.modal} ref={refs.setFloating} style={floatingStyles} aria-labelledby={headingId} {...getFloatingProps()}>
<div className={styles.ModMenu}>
<div className={styles.menuItem}>
<label>
[
<input onChange={onCheckbox} checked={publishCommentEditOptions.removed} type='checkbox' id='removed' />
{_.capitalize(t('remove'))}? ]
</label>
</div>
{!isReply && (
<div className={styles.menuItem}>
[
<label>
<input onChange={onCheckbox} checked={publishCommentEditOptions.locked} type='checkbox' id='locked' />
{_.capitalize(t('close_thread'))}?
</label>
]
</div>
)}
<div className={styles.menuItem}>
[
<label>
<input onChange={onCheckbox} checked={publishCommentEditOptions.spoiler} type='checkbox' id='spoiler' />
{_.capitalize(t('spoiler'))}?
</label>
]
</div>
<div className={styles.menuItem}>
[
<label>
<input onChange={onCheckbox} checked={publishCommentEditOptions.pinned} type='checkbox' id='pinned' />
{_.capitalize(t('sticky'))}?
</label>
]
</div>
{!isCommentAuthorMod && (
<div className={styles.menuItem}>
[
<label>
<input onChange={onCheckbox} checked={!!publishCommentEditOptions.commentAuthor?.banExpiresAt} type='checkbox' id='banUser' />
<Trans
i18nKey='ban_user_for'
shouldUnescape={true}
components={{ 1: <input className={styles.banInput} onChange={onBanDurationChange} type='number' min={1} max={100} defaultValue={banDuration} /> }}
/>
?
</label>
]
</div>
)}
<div className={`${styles.menuItem} ${styles.menuReason}`}>
{_.capitalize(t('reason'))}?
<input type='text' onChange={onReason} defaultValue={post?.reason} size={14} />
</div>
<div className={styles.bottom}>
<button onClick={handleSaveClick}>{t('save')}</button>
</div>
</div>
</div>
</FloatingFocusManager>
)}
</>
);
};
export default ModMenu;
@@ -19,6 +19,7 @@ import Markdown from '../../markdown';
import PostMenuDesktop from './post-menu-desktop/';
import { PostProps } from '../post';
import _ from 'lodash';
import ModMenu from '../mod-menu';
const PostInfo = ({ openReplyModal, post, roles, isHidden }: PostProps) => {
const { t } = useTranslation();
@@ -44,11 +45,7 @@ const PostInfo = ({ openReplyModal, post, roles, isHidden }: PostProps) => {
return (
<div className={styles.postInfo}>
{!isHidden && (
<span className={styles.checkbox}>
<input type='checkbox' />
</span>
)}
{!isHidden && <ModMenu cid={cid} />}
{title && <span className={styles.subject}>{displayTitle} </span>}
<span className={styles.nameBlock}>
<span className={`${styles.name} ${(isDescription || isRules || authorRole) && styles.capcodeMod}`}>
@@ -111,16 +108,12 @@ const PostInfo = ({ openReplyModal, post, roles, isHidden }: PostProps) => {
const PostMedia = ({ post }: PostProps) => {
const { t } = useTranslation();
const { deleted, link, linkHeight, linkWidth, parentCid, removed } = post || {};
const { isDescription, isRules } = post || {}; // custom properties, not from api
const commentMediaInfo = getCommentMediaInfo(post);
const { type, url } = commentMediaInfo || {};
const embedUrl = url && new URL(url);
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
const hasThumbnail = getHasThumbnail(commentMediaInfo, post?.link);
const [showThumbnail, setShowThumbnail] = useState(true);
const isReply = parentCid;
return (
<div className={styles.file}>
<div className={styles.fileText}>
@@ -152,16 +145,7 @@ const PostMedia = ({ post }: PostProps) => {
</div>
{(hasThumbnail || (!hasThumbnail && !showThumbnail)) && (
<div className={styles.fileThumbnail}>
<CommentMedia
commentMediaInfo={commentMediaInfo}
isOutOfFeed={isDescription || isRules} // virtuoso wrapper unneeded
isDeleted={removed || deleted}
isReply={isReply}
linkHeight={linkHeight}
linkWidth={linkWidth}
showThumbnail={showThumbnail}
setShowThumbnail={setShowThumbnail}
/>
<CommentMedia commentMediaInfo={commentMediaInfo} post={post} showThumbnail={showThumbnail} setShowThumbnail={setShowThumbnail} />
</div>
)}
</div>
@@ -169,7 +153,7 @@ const PostMedia = ({ post }: PostProps) => {
};
const PostMessage = ({ post }: PostProps) => {
const { cid, content, deleted, parentCid, postCid, reason, removed, state, subplebbitAddress } = post || {};
const { cid, content, deleted, parentCid, postCid, reason, removed, spoiler, state, subplebbitAddress } = post || {};
const { t } = useTranslation();
const params = useParams();
const location = useLocation();
@@ -201,7 +185,7 @@ const PostMessage = ({ post }: PostProps) => {
) : deleted ? (
<span className={styles.removedContent}>{t('user_deleted_this_post')}</span>
) : (
<Markdown content={displayContent} />
<Markdown content={displayContent} spoiler={spoiler} />
)}
{(removed || deleted) && reason && (
<span>
@@ -19,7 +19,7 @@ import _ from 'lodash';
const PostInfoAndMedia = ({ openReplyModal, post, roles }: PostProps) => {
const { t } = useTranslation();
const { author, cid, deleted, link, linkHeight, linkWidth, locked, parentCid, pinned, removed, shortCid, state, subplebbitAddress, timestamp, title } = post || {};
const { author, cid, link, locked, parentCid, pinned, shortCid, state, subplebbitAddress, timestamp, title } = post || {};
const { isDescription, isRules } = post || {}; // custom properties, not from api
const { address, displayName, shortAddress } = author || {};
@@ -94,18 +94,7 @@ const PostInfoAndMedia = ({ openReplyModal, post, roles }: PostProps) => {
)}
</span>
</div>
{(hasThumbnail || link) && (
<CommentMedia
commentMediaInfo={commentMediaInfo}
isOutOfFeed={isDescription || isRules} // virtuoso wrapper unneeded
isDeleted={removed || deleted}
isReply={isReply}
linkHeight={linkHeight}
linkWidth={linkWidth}
showThumbnail={showThumbnail}
setShowThumbnail={setShowThumbnail}
/>
)}
{(hasThumbnail || link) && <CommentMedia commentMediaInfo={commentMediaInfo} post={post} showThumbnail={showThumbnail} setShowThumbnail={setShowThumbnail} />}
</>
);
};
-6
View File
@@ -61,12 +61,6 @@
width: 100%;
}
.postDesktop .checkbox, .replyDesktop .checkbox {
display: inline-block;
padding: 3px 7px 3px 4px;
filter: var(--filter80);
}
.postDesktop .subject {
color: var(--post-subject-text-color);
font-weight: var(--post-subject-font-weight);
+28 -5
View File
@@ -2,16 +2,18 @@
.boardNavDesktop {
font-size: var(--topbar-font-size);
color: var(--topbar-separator-color);
position: relative;
}
.boardNavDesktop a {
.boardNavDesktop a, .navTopRight span {
color: var(--topbar-desktop-link-text-color);
text-decoration: var(--topbar-desktop-link-text-decoration);
}
.boardNavDesktop a:hover {
.boardNavDesktop a:hover, .navTopRight span:hover {
color: var(--topbar-desktop-link-text-color-hover);
text-decoration: var(--topbar-desktop-link-text-decoration-hover);
cursor: pointer;
}
.navTopRight {
@@ -19,6 +21,24 @@
text-transform: capitalize;
}
.searchBar {
position: absolute;
right: 0;
padding: 3px 5px 0 0;
}
.boardNavDesktop .searchBar {
margin-right: -7px;
}
.searchBar input[type="text"] {
outline: none;
margin: 2px;
padding: 1px;
border: 1px solid #000;
width: 220px;
}
.boardNavMobile {
padding: 2px 4px;
background-color: var(--topbar-mobile-background-color);
@@ -32,11 +52,15 @@
justify-content: space-around;
}
.boardNavMobile .pageJump a {
.boardNavMobile .pageJump a, .boardNavMobile .pageJump span {
color: var(--topbar-mobile-button-color);
text-decoration: var(--button-text-decoration-mobile);
}
.pageJump {
position: relative;
}
.boardNavMobile strong {
text-transform: capitalize;
padding-right: 5px;
@@ -55,11 +79,10 @@
padding: 3px 5px 0 0;
}
.pageJump a, .mobileSearchButton {
.pageJump a, .pageJump span {
padding-right: 5px;
color: var(--homepage-box-link-text-color);
text-decoration: var(--homepage-box-link-text-decoration);
text-transform: capitalize;
}
@media (max-width: 640px) {
+53 -3
View File
@@ -1,17 +1,63 @@
import { useEffect, useRef, useState } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useAccountComment } from '@plebbit/plebbit-react-hooks';
import { Link, useLocation, useNavigate, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { isAllView, isCatalogView, isSubscriptionsView } from '../../lib/utils/view-utils';
import styles from './topbar.module.css';
import useDefaultSubplebbits, { categorizeSubplebbits, useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
import { debounce } from 'lodash';
import _, { debounce } from 'lodash';
const SearchBar = ({ setShowSearchBar }: { setShowSearchBar: (show: boolean) => void }) => {
const navigate = useNavigate();
const searchBarRef = useRef<HTMLDivElement>(null);
const searchInputRef = useRef<HTMLInputElement>(null);
const placeholder = `"community.eth/.sol" / "12D3KooW..."`;
useEffect(() => {
searchInputRef.current?.focus();
}, []);
const handleClickOutside = useCallback(
(event: MouseEvent) => {
if (searchBarRef.current && !searchBarRef.current.contains(event.target as Node)) {
setShowSearchBar(false);
}
},
[searchBarRef, setShowSearchBar],
);
useEffect(() => {
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [handleClickOutside]);
const handleSearchSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const searchInput = searchInputRef.current?.value;
if (searchInput) {
searchInputRef.current.value = '';
navigate(`/p/${searchInput}`);
setShowSearchBar(false);
}
};
return (
<div className={styles.searchBar} ref={searchBarRef}>
<form onSubmit={handleSearchSubmit}>
<input type='text' autoCorrect='off' autoComplete='off' spellCheck='false' autoCapitalize='off' placeholder={placeholder} ref={searchInputRef} />
</form>
</div>
);
};
const TopBarDesktop = () => {
const { t } = useTranslation();
const location = useLocation();
const isInCatalogView = isCatalogView(location.pathname);
const subplebbits = useDefaultSubplebbits();
const [showSearchBar, setShowSearchBar] = useState(false);
const { plebbitSubs, interestsSubs, randomSubs, internationalSubs, projectsSubs } = categorizeSubplebbits(subplebbits);
@@ -33,8 +79,9 @@ const TopBarDesktop = () => {
</span>
<span className={styles.navTopRight}>
[<Link to={!location.pathname.endsWith('settings') ? location.pathname.replace(/\/$/, '') + '/settings' : location.pathname}>{t('settings')}</Link>] [
<Link to='/'>{t('home')}</Link>]
<span onClick={() => setShowSearchBar(!showSearchBar)}>{t('search')}</span>] [<Link to='/'>{t('home')}</Link>]
</span>
{showSearchBar && <SearchBar setShowSearchBar={setShowSearchBar} />}
</div>
);
};
@@ -44,6 +91,7 @@ const TopBarMobile = ({ subplebbitAddress }: { subplebbitAddress: string }) => {
const navigate = useNavigate();
const subplebbitAddresses = useDefaultSubplebbitAddresses();
const displaySubplebbitAddress = subplebbitAddress && subplebbitAddress.length > 30 ? subplebbitAddress.slice(0, 30).concat('...') : subplebbitAddress;
const [showSearchBar, setShowSearchBar] = useState(false);
const currentSubplebbitIsInList = subplebbitAddresses.some((address: string) => address === subplebbitAddress);
@@ -95,7 +143,9 @@ const TopBarMobile = ({ subplebbitAddress }: { subplebbitAddress: string }) => {
</div>
<div className={styles.pageJump}>
<Link to={useLocation().pathname.replace(/\/$/, '') + '/settings'}>{t('settings')}</Link>
<span onClick={() => setShowSearchBar(!showSearchBar)}>{_.capitalize(t('search'))}</span>
<Link to='/'>{t('home')}</Link>
{showSearchBar && <SearchBar setShowSearchBar={setShowSearchBar} />}
</div>
</div>
);
+17 -14
View File
@@ -6,12 +6,20 @@ const usePopularPosts = (subplebbits: Subplebbit[]) => {
const [popularPosts, setPopularPosts] = useState<Comment[]>([]);
useEffect(() => {
const subplebbitToPost: { [key: string]: any } = {};
const uniqueLinks: Set<string> = new Set();
const allPosts: Comment[] = [];
let postsPerSub = 1;
if (subplebbits.length == 2) {
postsPerSub = 4;
} else if (subplebbits.length == 3) {
postsPerSub = 3;
} else if (subplebbits.length >= 4 && subplebbits.length < 8) {
postsPerSub = 2;
}
subplebbits.forEach((subplebbit: any) => {
let maxTimestamp = -Infinity;
let mostRecentPost = null;
let subplebbitPosts: Comment[] = [];
if (subplebbit?.posts?.pages?.hot?.comments) {
for (const post of Object.values(subplebbit.posts.pages.hot.comments as Comment)) {
@@ -21,32 +29,27 @@ const usePopularPosts = (subplebbits: Subplebbit[]) => {
if (
isMediaShowed &&
replyCount > 0 &&
(replyCount > 0 || postsPerSub > 1) &&
!deleted &&
!removed &&
!locked &&
!pinned &&
timestamp > Date.now() / 1000 - 60 * 60 * 24 * 30 &&
timestamp > maxTimestamp &&
!uniqueLinks.has(link)
) {
maxTimestamp = post.timestamp;
mostRecentPost = post;
subplebbitPosts.push(post);
uniqueLinks.add(link);
}
}
if (mostRecentPost) {
subplebbitToPost[subplebbit.address] = { post: mostRecentPost, timestamp: maxTimestamp };
}
subplebbitPosts.sort((a: any, b: any) => b.timestamp - a.timestamp);
allPosts.push(...subplebbitPosts.slice(0, postsPerSub));
}
});
const sortedPosts = Object.values(subplebbitToPost)
.sort((a, b) => b.timestamp - a.timestamp)
.slice(0, 8);
const sortedPosts = allPosts.sort((a: any, b: any) => b.timestamp - a.timestamp).slice(0, 8);
setPopularPosts(sortedPosts.map((item) => item.post));
setPopularPosts(sortedPosts);
}, [subplebbits]);
return popularPosts;
+44
View File
@@ -0,0 +1,44 @@
import { create } from 'zustand';
interface HomeFiltersStore {
showNsfwBoardsOnly: boolean;
setShowNsfwBoardsOnly: (value: boolean) => void;
showWorksafeBoardsOnly: boolean;
setShowWorksafeBoardsOnly: (value: boolean) => void;
useCatalog: boolean;
setUseCatalog: (value: boolean) => void;
showWorksafeContentOnly: boolean;
setShowWorksafeContentOnly: (value: boolean) => void;
showNsfwContentOnly: boolean;
setShowNsfwContentOnly: (value: boolean) => void;
}
const useHomeFiltersStore = create<HomeFiltersStore>((set) => ({
showNsfwBoardsOnly: localStorage.getItem('showNsfwBoardsOnly') === 'true' ? true : false,
setShowNsfwBoardsOnly: (value: boolean) => {
set({ showNsfwBoardsOnly: value });
localStorage.setItem('showNsfwBoardsOnly', value.toString());
},
showWorksafeBoardsOnly: localStorage.getItem('showWorksafeBoardsOnly') === 'true' ? true : false,
setShowWorksafeBoardsOnly: (value: boolean) => {
set({ showWorksafeBoardsOnly: value });
localStorage.setItem('showWorksafeBoardsOnly', value.toString());
},
useCatalog: localStorage.getItem('useCatalog') === 'true' ? true : false,
setUseCatalog: (value: boolean) => {
set({ useCatalog: value });
localStorage.setItem('useCatalog', value.toString());
},
showWorksafeContentOnly: localStorage.getItem('showWorksafeContentOnly') === 'true' ? true : false,
setShowWorksafeContentOnly: (value: boolean) => {
set({ showWorksafeContentOnly: value });
localStorage.setItem('showWorksafeContentOnly', value.toString());
},
showNsfwContentOnly: localStorage.getItem('showNsfwContentOnly') === 'true' ? true : false,
setShowNsfwContentOnly: (value: boolean) => {
set({ showNsfwContentOnly: value });
localStorage.setItem('showNsfwContentOnly', value.toString());
},
}));
export default useHomeFiltersStore;
+9 -3
View File
@@ -49,7 +49,7 @@ const Board = () => {
setResetFunction(reset);
}, [reset, setResetFunction]);
const subplebbit = useSubplebbit({ subplebbitAddress });
const { subplebbit, error } = useSubplebbit({ subplebbitAddress });
const { createdAt, description, rules, shortAddress, state, suggested } = subplebbit || {};
const title = isInAllView ? t('all') : isInSubscriptionsView ? t('subscriptions') : subplebbit?.title;
@@ -65,6 +65,12 @@ const Board = () => {
) : (
<LoadingEllipsis string={loadingStateString} />
)}
{error && (
<div style={{ color: 'red' }}>
<br />
{error.message}
</div>
)}
</div>
);
@@ -96,8 +102,8 @@ const Board = () => {
const lastVirtuosoState = lastVirtuosoStates?.[location.pathname];
useEffect(() => {
document.title = title ? title : shortAddress;
}, [title, shortAddress]);
document.title = title ? title : shortAddress || subplebbitAddress;
}, [title, shortAddress, subplebbitAddress]);
return (
<div className={styles.content}>
+296
View File
@@ -0,0 +1,296 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Subplebbit, useAccount, useAccountSubplebbits } from '@plebbit/plebbit-react-hooks';
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
import useHomeFiltersStore from '../../../stores/use-home-filters';
import { useMultisubMetadata } from '../../../hooks/use-default-subplebbits';
import styles from '../home.module.css';
import { nsfwTags } from '../home';
const BoxModal = ({ isBoardsBoxModal }: { isBoardsBoxModal: boolean }) => {
const { t } = useTranslation();
const [showFilterModal, setShowFilterModal] = useState(false);
const modalRef = useRef<HTMLDivElement>(null);
const {
showNsfwBoardsOnly,
setShowNsfwBoardsOnly,
showWorksafeBoardsOnly,
setShowWorksafeBoardsOnly,
useCatalog,
setUseCatalog,
showWorksafeContentOnly,
setShowWorksafeContentOnly,
showNsfwContentOnly,
setShowNsfwContentOnly,
} = useHomeFiltersStore();
const handleClickOutside = useCallback(
(event: MouseEvent) => {
if (modalRef.current && !modalRef.current.contains(event.target as Node)) {
setShowFilterModal(false);
}
},
[modalRef, setShowFilterModal],
);
useEffect(() => {
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [handleClickOutside]);
return (
<>
<span ref={modalRef} onClick={() => setShowFilterModal(true)}>
{isBoardsBoxModal ? t('filter') : t('options')} ▼
</span>
{showFilterModal && (
<div ref={modalRef} className={styles.filterModal}>
{isBoardsBoxModal ? (
<>
<div
className={`${styles.option} ${!showNsfwBoardsOnly && !showWorksafeBoardsOnly && styles.selected}`}
onClick={() => {
setShowNsfwBoardsOnly(false);
setShowWorksafeBoardsOnly(false);
setShowFilterModal(false);
}}
>
Show All Boards
</div>
<div
className={`${styles.option} ${showNsfwBoardsOnly && styles.selected}`}
onClick={() => {
if (showWorksafeBoardsOnly) {
setShowWorksafeBoardsOnly(false);
}
setShowNsfwBoardsOnly(!showNsfwBoardsOnly);
setShowFilterModal(false);
}}
>
Show NSFW Boards Only
</div>
<div
className={`${styles.option} ${showWorksafeBoardsOnly && styles.selected}`}
onClick={() => {
if (showNsfwBoardsOnly) {
setShowNsfwBoardsOnly(false);
}
setShowWorksafeBoardsOnly(!showWorksafeBoardsOnly);
setShowFilterModal(false);
}}
>
Show Worksafe Boards Only
</div>
<div className={styles.separator} />
<div
className={`${styles.option} ${useCatalog && styles.selected}`}
onClick={() => {
setUseCatalog(!useCatalog);
setShowFilterModal(false);
}}
>
Use Catalog
</div>
</>
) : (
<>
<div
className={`${styles.option} ${showWorksafeContentOnly && styles.selected}`}
onClick={() => {
if (showNsfwContentOnly) {
setShowNsfwContentOnly(false);
}
setShowWorksafeContentOnly(!showWorksafeContentOnly);
setShowFilterModal(false);
}}
>
Show Worksafe Content Only
</div>
<div
className={`${styles.option} ${showNsfwContentOnly && styles.selected}`}
onClick={() => {
if (showWorksafeContentOnly) {
setShowWorksafeContentOnly(false);
}
setShowNsfwContentOnly(!showNsfwContentOnly);
setShowFilterModal(false);
}}
>
Show NSFW Content Only
</div>
<div
className={`${styles.option} ${!showWorksafeContentOnly && !showNsfwContentOnly && styles.selected}`}
onClick={() => {
setShowWorksafeContentOnly(false);
setShowNsfwContentOnly(false);
setShowFilterModal(false);
}}
>
Show All Content
</div>
</>
)}
</div>
)}
</>
);
};
const Board = ({ isOffline, subplebbit }: { isOffline: boolean; subplebbit: Subplebbit }) => {
const { t } = useTranslation();
const { address, title, tags } = subplebbit || {};
const nsfwTag = tags.find((tag: string) => nsfwTags.includes(tag));
const { useCatalog } = useHomeFiltersStore();
const boardLink = useCatalog ? `/p/${address}/catalog` : `/p/${address}`;
return (
<div className={styles.subplebbit} key={address}>
{isOffline && <span className={styles.offlineIcon} />}
<Link to={boardLink}>{title || address}</Link>
{nsfwTag && <span className={styles.nsfw}> ({t(nsfwTag)})</span>}
</div>
);
};
const BoardsBox = ({ multisub, subplebbits }: { multisub: Subplebbit[]; subplebbits: any }) => {
const { t } = useTranslation();
const account = useAccount();
const subscriptions = account?.subscriptions || [];
const { accountSubplebbits } = useAccountSubplebbits();
const accountSubplebbitAddresses = Object.keys(accountSubplebbits);
const { showNsfwBoardsOnly, showWorksafeBoardsOnly } = useHomeFiltersStore();
const filterSubs = (subs: Subplebbit[], includeTags: string[], excludeTags: string[] = []) =>
subs.filter((sub) => includeTags.every((tag) => sub.tags?.includes(tag)) && excludeTags.every((tag) => !sub.tags?.includes(tag)));
let plebbitSubs = filterSubs(multisub, ['plebbit']);
let interestsSubs = filterSubs(multisub, ['topic'], ['plebbit', 'country', 'international']);
let randomSubs = filterSubs(multisub, ['random'], ['plebbit']);
let internationalSubs = filterSubs(multisub, ['international']);
let projectsSubs = filterSubs(multisub, ['project'], ['plebbit', 'topic']);
const filterByNsfw = (subs: Subplebbit[], includeNsfw: boolean) => subs.filter((sub) => sub.tags.some((tag: string) => nsfwTags.includes(tag)) === includeNsfw);
const filterCategoriesByNsfw = (subsArray: Subplebbit[][], includeNsfw: boolean) => subsArray.map((subs) => filterByNsfw(subs, includeNsfw));
const includeNsfw = showNsfwBoardsOnly ? true : showWorksafeBoardsOnly ? false : null;
if (includeNsfw !== null) {
[plebbitSubs, interestsSubs, randomSubs, internationalSubs, projectsSubs] = filterCategoriesByNsfw(
[plebbitSubs, interestsSubs, randomSubs, internationalSubs, projectsSubs],
includeNsfw,
);
}
const isSubOffline = (address: string) => {
const subplebbit = subplebbits && subplebbits.find((sub: Subplebbit) => sub?.address === address);
const isOffline = subplebbit?.updatedAt && subplebbit.updatedAt < Date.now() / 1000 - 60 * 60;
return isOffline;
};
const multisubMetadata = useMultisubMetadata();
return (
<div className={`${styles.box} ${styles.boardsBox}`}>
<div className={styles.boxBar}>
<h2 className={styles.capitalize}>{t('boards')}</h2>
<BoxModal isBoardsBoxModal={true} />
</div>
<div className={styles.boardsBoxContent}>
<div className={styles.column}>
<h3>Multiboards</h3>
<div className={styles.list}>
<div className={styles.subplebbit}>
<Link to='/p/all'>{multisubMetadata?.title || 'All'}</Link>
</div>
<div className={styles.subplebbit}>
<Link to='/p/subscriptions'>Subscriptions</Link>
</div>
</div>
{plebbitSubs.length > 0 && (
<>
<h3>Plebbit</h3>
<div className={styles.list}>
{plebbitSubs.map((sub) => (
<Board key={sub.address} subplebbit={sub} isOffline={sub.address && isSubOffline(sub.address)} />
))}
</div>
</>
)}
{projectsSubs.length > 0 && (
<>
<h3>{t('projects')}</h3>
<div className={styles.list}>
{projectsSubs.map((sub) => (
<Board key={sub.address} subplebbit={sub} isOffline={sub.address && isSubOffline(sub.address)} />
))}
</div>
</>
)}
</div>
<div className={styles.column}>
{interestsSubs.length > 0 && (
<>
<h3>{t('interests')}</h3>
<div className={styles.list}>
{interestsSubs.map((sub) => (
<Board key={sub.address} subplebbit={sub} isOffline={sub.address && isSubOffline(sub.address)} />
))}
</div>
</>
)}
</div>
<div className={styles.column}>
{randomSubs.length > 0 && (
<>
<h3>{t('random')}</h3>
<div className={styles.list}>
{randomSubs.map((sub) => (
<Board key={sub.address} subplebbit={sub} isOffline={sub.address && isSubOffline(sub.address)} />
))}
</div>
</>
)}
{internationalSubs.length > 0 && (
<>
<h3>{t('international')}</h3>
<div className={styles.list}>
{internationalSubs.map((sub) => (
<Board key={sub.address} subplebbit={sub} isOffline={sub.address && isSubOffline(sub.address)} />
))}
</div>
</>
)}
</div>
<div className={styles.column}>
<div className={styles.list}>
<h3>{t('subscriptions')}</h3>
{subscriptions.length > 0
? subscriptions.map((address: string, index: number) => (
<div className={styles.subplebbit} key={index}>
<Link to={`/p/${address}`}>p/{address && Plebbit.getShortAddress(address)}</Link>
</div>
))
: t('not_subscribed')}
</div>
<div className={styles.list}>
<h3>{t('moderating')}</h3>
{accountSubplebbitAddresses.length > 0
? accountSubplebbitAddresses.map((address: string, index: number) => (
<div className={styles.subplebbit} key={index}>
<Link to={`/p/${address}`}>p/{address && Plebbit.getShortAddress(address)}</Link>
</div>
))
: t('not_moderating')}
</div>
</div>
</div>
</div>
);
};
export default BoardsBox;
+1
View File
@@ -0,0 +1 @@
export { default } from './boards-box';
+138
View File
@@ -0,0 +1,138 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import useHomeFiltersStore from '../../../stores/use-home-filters';
import styles from '../home.module.css';
const BoxModal = ({ isBoardsBoxModal }: { isBoardsBoxModal: boolean }) => {
const { t } = useTranslation();
const [showFilterModal, setShowFilterModal] = useState(false);
const modalRef = useRef<HTMLDivElement>(null);
const {
showNsfwBoardsOnly,
setShowNsfwBoardsOnly,
showWorksafeBoardsOnly,
setShowWorksafeBoardsOnly,
useCatalog,
setUseCatalog,
showWorksafeContentOnly,
setShowWorksafeContentOnly,
showNsfwContentOnly,
setShowNsfwContentOnly,
} = useHomeFiltersStore();
const handleClickOutside = useCallback(
(event: MouseEvent) => {
if (modalRef.current && !modalRef.current.contains(event.target as Node)) {
setShowFilterModal(false);
}
},
[modalRef, setShowFilterModal],
);
useEffect(() => {
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [handleClickOutside]);
return (
<>
<span ref={modalRef} onClick={() => setShowFilterModal(true)}>
{isBoardsBoxModal ? t('filter') : t('options')} ▼
</span>
{showFilterModal && (
<div ref={modalRef} className={styles.filterModal}>
{isBoardsBoxModal ? (
<>
<div
className={`${styles.option} ${!showNsfwBoardsOnly && !showWorksafeBoardsOnly && styles.selected}`}
onClick={() => {
setShowNsfwBoardsOnly(false);
setShowWorksafeBoardsOnly(false);
setShowFilterModal(false);
}}
>
Show All Boards
</div>
<div
className={`${styles.option} ${showNsfwBoardsOnly && styles.selected}`}
onClick={() => {
if (showWorksafeBoardsOnly) {
setShowWorksafeBoardsOnly(false);
}
setShowNsfwBoardsOnly(!showNsfwBoardsOnly);
setShowFilterModal(false);
}}
>
Show NSFW Boards Only
</div>
<div
className={`${styles.option} ${showWorksafeBoardsOnly && styles.selected}`}
onClick={() => {
if (showNsfwBoardsOnly) {
setShowNsfwBoardsOnly(false);
}
setShowWorksafeBoardsOnly(!showWorksafeBoardsOnly);
setShowFilterModal(false);
}}
>
Show Worksafe Boards Only
</div>
<div className={styles.separator} />
<div
className={`${styles.option} ${useCatalog && styles.selected}`}
onClick={() => {
setUseCatalog(!useCatalog);
setShowFilterModal(false);
}}
>
Use Catalog
</div>
</>
) : (
<>
<div
className={`${styles.option} ${showWorksafeContentOnly && styles.selected}`}
onClick={() => {
if (showNsfwContentOnly) {
setShowNsfwContentOnly(false);
}
setShowWorksafeContentOnly(!showWorksafeContentOnly);
setShowFilterModal(false);
}}
>
Show Worksafe Content Only
</div>
<div
className={`${styles.option} ${showNsfwContentOnly && styles.selected}`}
onClick={() => {
if (showWorksafeContentOnly) {
setShowWorksafeContentOnly(false);
}
setShowNsfwContentOnly(!showNsfwContentOnly);
setShowFilterModal(false);
}}
>
Show NSFW Content Only
</div>
<div
className={`${styles.option} ${!showWorksafeContentOnly && !showNsfwContentOnly && styles.selected}`}
onClick={() => {
setShowWorksafeContentOnly(false);
setShowNsfwContentOnly(false);
setShowFilterModal(false);
}}
>
Show All Content
</div>
</>
)}
</div>
)}
</>
);
};
export default BoxModal;
+1
View File
@@ -0,0 +1 @@
export { default } from './box-modal';
+38 -2
View File
@@ -27,6 +27,7 @@
.boardsBox {
overflow: hidden;
min-height: 150px;
}
.boxBar, .infoboxBar {
@@ -79,7 +80,7 @@
color: var(--homepage-infobox-link-text-color);
}
.searchBar {
.searchBar form {
display: flex;
text-align: center;
justify-content: center;
@@ -104,7 +105,7 @@
padding: 3px 10px;
}
.searchBar button {
.searchButton {
margin-left: 4px;
padding: 3px 5px;
display: inline-block;
@@ -112,6 +113,41 @@
font-size: 14px;
white-space: nowrap;
flex-shrink: 0;
cursor: pointer;
}
.filterModal {
position: absolute;
top: 30px;
right: 5px;
display: inline-block;
position: absolute;
text-align: right;
background: #fff;
border: 1px solid gray;
box-shadow: 2px 2px 0 1px rgba(0, 0, 0, .1);
padding: 4px 0;
line-height: 1.7em;
z-index: 100;
}
.filterModal .separator {
border-bottom: 1px solid #ccc;
margin: 6px 0 4px;
padding: 0;
}
.filterModal .option {
padding: 0 10px;
}
.filterModal .option:hover {
background-color: #fca;
cursor: pointer;
}
.filterModal .selected::before {
content: '✔ ';
}
.capitalize {
+24 -204
View File
@@ -1,23 +1,17 @@
import { useEffect, useMemo, useRef } from 'react';
import styles from './home.module.css';
import { Link, useNavigate } from 'react-router-dom';
import { Trans, useTranslation } from 'react-i18next';
import { Comment, Subplebbit, useAccount, useAccountSubplebbits, useSubplebbits } from '@plebbit/plebbit-react-hooks';
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
import { useSubplebbits } from '@plebbit/plebbit-react-hooks';
import styles from './home.module.css';
import packageJson from '../../../package.json';
import useDefaultSubplebbits, { useMultisubMetadata, useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
import usePopularPosts from '../../hooks/use-popular-posts';
import useDefaultSubplebbits, { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
import useSubplebbitsStats from '../../hooks/use-subplebbits-stats';
import { getCommentMediaInfo } from '../../lib/utils/media-utils';
import { CatalogPostMedia } from '../../components/catalog-row';
import LoadingEllipsis from '../../components/loading-ellipsis';
import PopularThreadsBox from './popular-threads-box';
import BoardsBox from './boards-box';
const isValidAddress = (address: string): boolean => {
if (address.includes('/') || address.includes('\\') || address.includes(' ')) {
return false;
}
return true;
};
// https://github.com/plebbit/temporary-default-subplebbits/blob/master/README.md
export const nsfwTags = ['adult', 'anti', 'gore'];
const SearchBar = () => {
const searchInputRef = useRef<HTMLInputElement>(null);
@@ -25,32 +19,26 @@ const SearchBar = () => {
const navigate = useNavigate();
const handleSearchSubmit = () => {
const inputValue = searchInputRef.current?.value;
if (inputValue && isValidAddress(inputValue)) {
navigate(`/p/${inputValue}`);
} else {
alert('invalid address');
const searchInput = searchInputRef.current?.value;
if (searchInput) {
navigate(`/p/${searchInput}`);
}
};
return (
<div className={styles.searchBar}>
<input
autoCorrect='off'
autoComplete='off'
spellCheck='false'
autoCapitalize='off'
type='text'
placeholder={`"board.eth/.sol" ${t('or')} "12D3KooW..."`}
ref={searchInputRef}
onSubmit={handleSearchSubmit}
onKeyDown={(e) => {
if (e.key === 'Enter') {
handleSearchSubmit();
}
}}
/>
<button onClick={handleSearchSubmit}>{t('go')}</button>
<form onSubmit={handleSearchSubmit}>
<input
autoCorrect='off'
autoComplete='off'
spellCheck='false'
autoCapitalize='off'
type='text'
placeholder={`"board.eth/.sol" ${t('or')} "12D3KooW..."`}
ref={searchInputRef}
/>
<button className={styles.searchButton}>{t('go')}</button>
</form>
</div>
);
};
@@ -72,171 +60,6 @@ const InfoBox = () => {
);
};
const Board = ({ isOffline, subplebbit }: { isOffline: boolean; subplebbit: Subplebbit }) => {
const { t } = useTranslation();
const { address, title, tags } = subplebbit || {};
const nsfwTags = ['adult', 'gore'];
const nsfwTag = tags.find((tag: string) => nsfwTags.includes(tag));
return (
<div className={styles.subplebbit} key={address}>
{isOffline && <span className={styles.offlineIcon} />}
<Link to={`/p/${address}`}>{title || address}</Link>
{nsfwTag && <span className={styles.nsfw}> ({t(nsfwTag)})</span>}
</div>
);
};
const Boards = ({ multisub, subplebbits }: { multisub: Subplebbit[]; subplebbits: any }) => {
const { t } = useTranslation();
const account = useAccount();
const subscriptions = account?.subscriptions || [];
const { accountSubplebbits } = useAccountSubplebbits();
const accountSubplebbitAddresses = Object.keys(accountSubplebbits);
const plebbitSubs = multisub.filter((sub) => sub.tags?.includes('plebbit'));
const interestsSubs = multisub.filter(
(sub) => sub.tags?.includes('topic') && !sub.tags?.includes('plebbit') && !sub.tags?.includes('country') && !sub.tags?.includes('international'),
);
const randomSubs = multisub.filter((sub) => sub.tags?.includes('random') && !sub.tags?.includes('plebbit'));
const internationalSubs = multisub.filter((sub) => sub.tags?.includes('international') || sub.tags?.includes('country'));
const projectsSubs = multisub.filter((sub) => sub.tags?.includes('project') && !sub.tags?.includes('plebbit') && !sub.tags?.includes('topic'));
const isSubOffline = (address: string) => {
const subplebbit = subplebbits && subplebbits.find((sub: Subplebbit) => sub?.address === address);
const isOffline = subplebbit?.updatedAt && subplebbit.updatedAt < Date.now() / 1000 - 60 * 60;
return isOffline;
};
const multisubMetadata = useMultisubMetadata();
return (
<div className={`${styles.box} ${styles.boardsBox}`}>
<div className={styles.boxBar}>
<h2 className={styles.capitalize}>{t('boards')}</h2>
<span>{t('options')} ▼</span>
</div>
<div className={styles.boardsBoxContent}>
<div className={styles.column}>
<h3>Multiboards</h3>
<div className={styles.list}>
<div className={styles.subplebbit}>
<Link to='/p/all'>{multisubMetadata?.title || 'All'}</Link>
</div>
<div className={styles.subplebbit}>
<Link to='/p/subscriptions'>Subscriptions</Link>
</div>
</div>
<h3>Plebbit</h3>
<div className={styles.list}>
{plebbitSubs.map((sub) => (
<Board key={sub.address} subplebbit={sub} isOffline={sub.address && isSubOffline(sub.address)} />
))}
</div>
<h3>{t('projects')}</h3>
<div className={styles.list}>
{projectsSubs.map((sub) => (
<Board key={sub.address} subplebbit={sub} isOffline={sub.address && isSubOffline(sub.address)} />
))}
</div>
</div>
<div className={styles.column}>
<h3>{t('interests')}</h3>
<div className={styles.list}>
{interestsSubs.map((sub) => (
<Board key={sub.address} subplebbit={sub} isOffline={sub.address && isSubOffline(sub.address)} />
))}
</div>
</div>
<div className={styles.column}>
<h3>{t('random')}</h3>
<div className={styles.list}>
{randomSubs.map((sub) => (
<Board key={sub.address} subplebbit={sub} isOffline={sub.address && isSubOffline(sub.address)} />
))}
</div>
<h3>{t('international')}</h3>
<div className={styles.list}>
{internationalSubs.map((sub) => (
<Board key={sub.address} subplebbit={sub} isOffline={sub.address && isSubOffline(sub.address)} />
))}
</div>
</div>
<div className={styles.column}>
<div className={styles.list}>
<h3>{t('subscriptions')}</h3>
{subscriptions.length > 0
? subscriptions.map((address: string, index: number) => (
<div className={styles.subplebbit} key={index}>
<Link to={`/p/${address}`}>p/{address && Plebbit.getShortAddress(address)}</Link>
</div>
))
: t('not_subscribed')}
</div>
<div className={styles.list}>
<h3>{t('moderating')}</h3>
{accountSubplebbitAddresses.length > 0
? accountSubplebbitAddresses.map((address: string, index: number) => (
<div className={styles.subplebbit} key={index}>
<Link to={`/p/${address}`}>p/{address && Plebbit.getShortAddress(address)}</Link>
</div>
))
: t('not_moderating')}
</div>
</div>
</div>
</div>
);
};
interface PopularThreadProps {
post: Comment;
boardTitle: string | undefined;
boardShortAddress: string;
}
const PopularThreadCard = ({ post, boardTitle, boardShortAddress }: PopularThreadProps) => {
const { cid, content, subplebbitAddress, title } = post || {};
const commentMediaInfo = getCommentMediaInfo(post);
return (
<div className={styles.popularThread} key={cid}>
<div className={styles.title}>{boardTitle || boardShortAddress}</div>
<div className={styles.mediaContainer}>
<Link to={`/p/${subplebbitAddress}/c/${cid}`}>
<CatalogPostMedia commentMediaInfo={commentMediaInfo} isOutOfFeed={true} />
</Link>
</div>
<div className={styles.threadContent}>
{title && <b>{title}</b>}
{content && (content.length > 99 ? `: ${content.substring(0, 99)}...` : `: ${content}`)}
</div>
</div>
);
};
const PopularThreads = ({ subplebbits }: { subplebbits: any }) => {
const { t } = useTranslation();
const popularPosts = usePopularPosts(subplebbits);
return (
<div className={styles.box}>
<div className={`${styles.boxBar} ${styles.color2ColorBar}`}>
<h2 className={styles.capitalize}>{t('popular_threads')}</h2>
<span>{t('options')} ▼</span>
</div>
<div className={`${styles.boxContent} ${popularPosts.length === 8 ? styles.popularThreads : ''}`}>
{popularPosts.length < 8 ? (
<LoadingEllipsis string={t('loading')} />
) : (
popularPosts.map((post: any) => (
<PopularThreadCard key={post.cid} post={post} boardTitle={post.subplebbitTitle || post.subplebbitAddress} boardShortAddress={post.subplebbitAddress} />
))
)}
</div>
</div>
);
};
const Stats = ({ multisub, subplebbitAddresses }: { multisub: any; subplebbitAddresses: string[] }) => {
const { t } = useTranslation();
const stats = useSubplebbitsStats({ subplebbitAddresses });
@@ -268,9 +91,6 @@ const Stats = ({ multisub, subplebbitAddresses }: { multisub: any; subplebbitAdd
<h2 className={styles.capitalize}>{t('stats')}</h2>
</div>
<div className={`${styles.boxContent} ${enoughStats ? styles.stats : ''}`}>
{/* <div className={styles.statsInfo}>
<p>{t('stats_info')}</p>
</div> */}
{enoughStats ? (
<>
<div className={styles.stat}>
@@ -403,8 +223,8 @@ const Home = () => {
<HomeLogo />
<SearchBar />
<InfoBox />
<Boards multisub={defaultSubplebbits} subplebbits={subplebbits} />
<PopularThreads subplebbits={subplebbits} />
<BoardsBox multisub={defaultSubplebbits} subplebbits={subplebbits} />
<PopularThreadsBox multisub={defaultSubplebbits} subplebbits={subplebbits} />
<Stats multisub={defaultSubplebbits} subplebbitAddresses={subplebbitAddresses} />
<Footer />
</div>
@@ -0,0 +1 @@
export { default } from './popular-threads-box';
@@ -0,0 +1,82 @@
import { useMemo } from 'react';
import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Comment, Subplebbit } from '@plebbit/plebbit-react-hooks';
import styles from '../home.module.css';
import usePopularPosts from '../../../hooks/use-popular-posts';
import useHomeFiltersStore from '../../../stores/use-home-filters';
import { getCommentMediaInfo } from '../../../lib/utils/media-utils';
import { CatalogPostMedia } from '../../../components/catalog-row';
import LoadingEllipsis from '../../../components/loading-ellipsis';
import BoxModal from '../box-modal';
import { nsfwTags } from '../home';
interface PopularThreadProps {
post: Comment;
boardTitle: string | undefined;
boardShortAddress: string;
}
const PopularThreadCard = ({ post, boardTitle, boardShortAddress }: PopularThreadProps) => {
const { cid, content, subplebbitAddress, title } = post || {};
const commentMediaInfo = getCommentMediaInfo(post);
return (
<div className={styles.popularThread} key={cid}>
<div className={styles.title}>{boardTitle || boardShortAddress}</div>
<div className={styles.mediaContainer}>
<Link to={`/p/${subplebbitAddress}/c/${cid}`}>
<CatalogPostMedia commentMediaInfo={commentMediaInfo} isOutOfFeed={true} />
</Link>
</div>
<div className={styles.threadContent}>
{title && <b>{title}</b>}
{content && (content.length > 99 ? `: ${content.substring(0, 99)}...` : `: ${content}`)}
</div>
</div>
);
};
const PopularThreadsBox = ({ multisub, subplebbits }: { multisub: Subplebbit[]; subplebbits: any }) => {
const { t } = useTranslation();
const { showWorksafeContentOnly, showNsfwContentOnly } = useHomeFiltersStore();
const getFilteredSubplebbits = () => {
if (showWorksafeContentOnly) {
return subplebbits.filter((sub: Subplebbit) => {
const multisubEntry = multisub.find((ms) => ms?.address === sub?.address);
return multisubEntry ? !multisubEntry.tags.some((tag: string) => nsfwTags.includes(tag)) : true;
});
}
if (showNsfwContentOnly) {
return subplebbits.filter((sub: Subplebbit) => {
const multisubEntry = multisub.find((ms) => ms?.address === sub?.address);
return multisubEntry ? multisubEntry.tags.some((tag: string) => nsfwTags.includes(tag)) : false;
});
}
return subplebbits;
};
const filteredSubplebbits = useMemo(getFilteredSubplebbits, [subplebbits, showWorksafeContentOnly, showNsfwContentOnly, multisub]);
const popularPosts = usePopularPosts(filteredSubplebbits);
return (
<div className={styles.box}>
<div className={`${styles.boxBar} ${styles.color2ColorBar}`}>
<h2 className={styles.capitalize}>{t('popular_threads')}</h2>
<BoxModal isBoardsBoxModal={false} />
</div>
<div className={`${styles.boxContent} ${popularPosts.length === 8 ? styles.popularThreads : ''}`}>
{popularPosts.length < 8 ? (
<LoadingEllipsis string={t('loading')} />
) : (
popularPosts.map((post: any) => (
<PopularThreadCard key={post.cid} post={post} boardTitle={post.subplebbitTitle || post.subplebbitAddress} boardShortAddress={post.subplebbitAddress} />
))
)}
</div>
</div>
);
};
export default PopularThreadsBox;
+1
View File
@@ -1,3 +1,4 @@
.content {
margin-bottom: 150px;
overflow-x: hidden;
}
+17 -484
View File
@@ -1529,11 +1529,6 @@
minimatch "^3.0.4"
plist "^3.0.4"
"@ensdomains/eth-ens-namehash@2.0.15":
version "2.0.15"
resolved "https://registry.yarnpkg.com/@ensdomains/eth-ens-namehash/-/eth-ens-namehash-2.0.15.tgz#5e5f2f24ba802aff8bc19edd822c9a11200cdf4a"
integrity sha512-JRDFP6+Hczb1E0/HhIg0PONgBYasfGfDheujmfxaZaAv/NAH4jE6Kf48WbqfRZdxt4IZI3jl3Ri7sZ1nP09lgw==
"@eslint-community/eslint-utils@^4.2.0":
version "4.4.0"
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
@@ -2165,11 +2160,6 @@
"@ethersproject/properties" "^5.7.0"
"@ethersproject/strings" "^5.7.0"
"@fastify/busboy@^2.0.0":
version "2.1.1"
resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d"
integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==
"@floating-ui/core@^1.0.0":
version "1.6.0"
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.0.tgz#fa41b87812a16bf123122bf945946bae3fdf7fc1"
@@ -2742,21 +2732,6 @@
uint8arraylist "^2.4.8"
uint8arrays "^5.0.3"
"@libp2p/crypto@^1.0.11":
version "1.0.17"
resolved "https://registry.yarnpkg.com/@libp2p/crypto/-/crypto-1.0.17.tgz#e64043328c0c866bf7f4cc8560b4f483e9c745dc"
integrity sha512-Oeg0Eb/EvAho0gVkOgemXEgrVxWaT3x/DpFgkBdZ9qGxwq75w/E/oPc7souqBz+l1swfz37GWnwV7bIb4Xv5Ag==
dependencies:
"@libp2p/interface-keys" "^1.0.2"
"@libp2p/interfaces" "^3.2.0"
"@noble/ed25519" "^1.6.0"
"@noble/secp256k1" "^1.5.4"
multiformats "^11.0.0"
node-forge "^1.1.0"
protons-runtime "^5.0.0"
uint8arraylist "^2.4.3"
uint8arrays "^4.0.2"
"@libp2p/crypto@^4.0.0", "@libp2p/crypto@^4.0.1", "@libp2p/crypto@^4.0.2", "@libp2p/crypto@^4.0.5":
version "4.0.5"
resolved "https://registry.yarnpkg.com/@libp2p/crypto/-/crypto-4.0.5.tgz#9f46ac445906a5a8f21246da75118254521382e6"
@@ -2788,17 +2763,6 @@
uint8arrays "^5.0.1"
wherearewe "^2.0.1"
"@libp2p/interface-connection@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@libp2p/interface-connection/-/interface-connection-4.0.0.tgz#fcc830ca891820fac89a4c6bd4fcc2df4874f49b"
integrity sha512-6xx/NmEc84HX7QmsjSC3hHredQYjHv4Dkf4G27adAPf+qN+vnPxmQ7gaTnk243a0++DOFTbZ2gKX/15G2B6SRg==
dependencies:
"@libp2p/interface-peer-id" "^2.0.0"
"@libp2p/interfaces" "^3.0.0"
"@multiformats/multiaddr" "^12.0.0"
it-stream-types "^1.0.4"
uint8arraylist "^2.1.2"
"@libp2p/interface-internal@^1.0.11", "@libp2p/interface-internal@^1.0.7", "@libp2p/interface-internal@^1.0.8":
version "1.0.11"
resolved "https://registry.yarnpkg.com/@libp2p/interface-internal/-/interface-internal-1.0.11.tgz#34e548400408e3518566e6d97f3c916f1ef05a9e"
@@ -2809,45 +2773,6 @@
"@multiformats/multiaddr" "^12.2.1"
uint8arraylist "^2.4.8"
"@libp2p/interface-keychain@^2.0.0":
version "2.0.5"
resolved "https://registry.yarnpkg.com/@libp2p/interface-keychain/-/interface-keychain-2.0.5.tgz#6ce104f38cf07ad72c9dfbe471a689f4ea4b4687"
integrity sha512-mb7QNgn9fIvC7CaJCi06GJ+a6DN6RVT9TmEi0NmedZGATeCArPeWWG7r7IfxNVXb9cVOOE1RzV1swK0ZxEJF9Q==
dependencies:
"@libp2p/interface-peer-id" "^2.0.0"
multiformats "^11.0.0"
"@libp2p/interface-keys@^1.0.2":
version "1.0.8"
resolved "https://registry.yarnpkg.com/@libp2p/interface-keys/-/interface-keys-1.0.8.tgz#2c6b55136113ae7cf78133d3c459cdf0455b29ae"
integrity sha512-CJ1SlrwuoHMquhEEWS77E+4vv7hwB7XORkqzGQrPQmA9MRdIEZRS64bA4JqCLUDa4ltH0l+U1vp0oZHLT67NEA==
"@libp2p/interface-peer-id@^2.0.0", "@libp2p/interface-peer-id@^2.0.2":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@libp2p/interface-peer-id/-/interface-peer-id-2.0.2.tgz#6302e70b6fc17c451bc3daa11447d059357bcc32"
integrity sha512-9pZp9zhTDoVwzRmp0Wtxw0Yfa//Yc0GqBCJi3EznBDE6HGIAVvppR91wSh2knt/0eYg0AQj7Y35VSesUTzMCUg==
dependencies:
multiformats "^11.0.0"
"@libp2p/interface-peer-info@^1.0.2":
version "1.0.10"
resolved "https://registry.yarnpkg.com/@libp2p/interface-peer-info/-/interface-peer-info-1.0.10.tgz#566026de95a0817b9e853c982b313541b7960c0b"
integrity sha512-HQlo8NwQjMyamCHJrnILEZz+YwEOXCB2sIIw3slIrhVUYeYlTaia1R6d9umaAeLHa255Zmdm4qGH8rJLRqhCcg==
dependencies:
"@libp2p/interface-peer-id" "^2.0.0"
"@multiformats/multiaddr" "^12.0.0"
"@libp2p/interface-pubsub@^3.0.0":
version "3.0.7"
resolved "https://registry.yarnpkg.com/@libp2p/interface-pubsub/-/interface-pubsub-3.0.7.tgz#cc1c7c47c883daddd2b84d83d719b3826943be3b"
integrity sha512-+c74EVUBTfw2sx1GE/z/IjsYO6dhur+ukF0knAppeZsRQ1Kgg6K5R3eECtT28fC6dBWLjFpAvW/7QGfiDAL4RA==
dependencies:
"@libp2p/interface-connection" "^4.0.0"
"@libp2p/interface-peer-id" "^2.0.0"
"@libp2p/interfaces" "^3.0.0"
it-pushable "^3.0.0"
uint8arraylist "^2.1.2"
"@libp2p/interface@^1.0.0", "@libp2p/interface@^1.1.2", "@libp2p/interface@^1.1.3", "@libp2p/interface@^1.1.6":
version "1.1.6"
resolved "https://registry.yarnpkg.com/@libp2p/interface/-/interface-1.1.6.tgz#379e3201f776ca13482ea60d31c4c5d40d88f7de"
@@ -2872,11 +2797,6 @@
progress-events "^1.0.0"
uint8arraylist "^2.4.8"
"@libp2p/interfaces@^3.0.0", "@libp2p/interfaces@^3.2.0":
version "3.3.2"
resolved "https://registry.yarnpkg.com/@libp2p/interfaces/-/interfaces-3.3.2.tgz#5d8079be845b0960939b5b18880e785a4714465a"
integrity sha512-p/M7plbrxLzuQchvNwww1Was7ZeGE2NaOFulMaZBYIihU8z3fhaV+a033OqnC/0NTX/yhfdNOG7znhYq3XoR/g==
"@libp2p/kad-dht@12.0.7":
version "12.0.7"
resolved "https://registry.yarnpkg.com/@libp2p/kad-dht/-/kad-dht-12.0.7.tgz#f4ee2d815f8e13976cb79ac7c32e4e34553918fb"
@@ -2914,17 +2834,6 @@
uint8arraylist "^2.4.8"
uint8arrays "^5.0.1"
"@libp2p/logger@^2.0.5":
version "2.1.1"
resolved "https://registry.yarnpkg.com/@libp2p/logger/-/logger-2.1.1.tgz#e12e6c320ea64252af954bcec996895098d1cd36"
integrity sha512-2UbzDPctg3cPupF6jrv6abQnAUTrbLybNOj0rmmrdGm1cN2HJ1o/hBu0sXuq4KF9P1h/eVRn1HIRbVIEKnEJrA==
dependencies:
"@libp2p/interface-peer-id" "^2.0.2"
"@multiformats/multiaddr" "^12.1.3"
debug "^4.3.4"
interface-datastore "^8.2.0"
multiformats "^11.0.2"
"@libp2p/logger@^4.0.10":
version "4.0.13"
resolved "https://registry.yarnpkg.com/@libp2p/logger/-/logger-4.0.13.tgz#9a32d933c79be8854726a1d3c69d95ed69e1f677"
@@ -3008,16 +2917,6 @@
uint8arraylist "^2.4.8"
uint8arrays "^5.0.3"
"@libp2p/peer-id@^2.0.0":
version "2.0.4"
resolved "https://registry.yarnpkg.com/@libp2p/peer-id/-/peer-id-2.0.4.tgz#d50d2ae4663ef79f6e31ce4eaf25e1f44e1667ab"
integrity sha512-gcOsN8Fbhj6izIK+ejiWsqiqKeJ2yWPapi/m55VjOvDa52/ptQzZszxQP8jUk93u36de92ATFXDfZR/Bi6eeUQ==
dependencies:
"@libp2p/interface-peer-id" "^2.0.0"
"@libp2p/interfaces" "^3.2.0"
multiformats "^11.0.0"
uint8arrays "^4.0.2"
"@libp2p/peer-id@^4.0.0", "@libp2p/peer-id@^4.0.5", "@libp2p/peer-id@^4.0.6", "@libp2p/peer-id@^4.0.9":
version "4.0.9"
resolved "https://registry.yarnpkg.com/@libp2p/peer-id/-/peer-id-4.0.9.tgz#2a08374f234e3d56c4b8a04ac434ab2f1d6cd143"
@@ -3227,26 +3126,7 @@
dependencies:
"@multiformats/multiaddr" "^12.0.0"
"@multiformats/multiaddr-to-uri@^9.0.1":
version "9.0.8"
resolved "https://registry.yarnpkg.com/@multiformats/multiaddr-to-uri/-/multiaddr-to-uri-9.0.8.tgz#c0b1491b26a4d52273ddc16024c59ba47ef8583f"
integrity sha512-4eiN5iEiQfy2A98BxekUfW410L/ivg0sgjYSgSqmklnrBhK+QyMz4yqgfkub8xDTXOc7O5jp4+LVyM3ZqMeWNw==
dependencies:
"@multiformats/multiaddr" "^12.0.0"
"@multiformats/multiaddr@^11.1.5":
version "11.6.1"
resolved "https://registry.yarnpkg.com/@multiformats/multiaddr/-/multiaddr-11.6.1.tgz#ec46984a298e715e27a398434c087856db5f3185"
integrity sha512-doST0+aB7/3dGK9+U5y3mtF3jq85KGbke1QiH0KE1F5mGQ9y56mFebTeu2D9FNOm+OT6UHb8Ss8vbSnpGjeLNw==
dependencies:
"@chainsafe/is-ip" "^2.0.1"
dns-over-http-resolver "^2.1.0"
err-code "^3.0.1"
multiformats "^11.0.0"
uint8arrays "^4.0.2"
varint "^6.0.0"
"@multiformats/multiaddr@^12.0.0", "@multiformats/multiaddr@^12.1.10", "@multiformats/multiaddr@^12.1.14", "@multiformats/multiaddr@^12.1.3", "@multiformats/multiaddr@^12.2.1":
"@multiformats/multiaddr@^12.0.0", "@multiformats/multiaddr@^12.1.10", "@multiformats/multiaddr@^12.1.14", "@multiformats/multiaddr@^12.2.1":
version "12.2.1"
resolved "https://registry.yarnpkg.com/@multiformats/multiaddr/-/multiaddr-12.2.1.tgz#d95d1590b17dbe39dcefbb4d832d14434d3fe075"
integrity sha512-UwjoArBbv64FlaetV4DDwh+PUMfzXUBltxQwdh+uTYnGFzVa8ZfJsn1vt1RJlJ6+Xtrm3RMekF/B+K338i2L5Q==
@@ -3305,7 +3185,7 @@
dependencies:
"@noble/hashes" "1.4.0"
"@noble/ed25519@^1.5.1", "@noble/ed25519@^1.6.0":
"@noble/ed25519@^1.5.1":
version "1.7.3"
resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.3.tgz#57e1677bf6885354b466c38e2b620c62f45a7123"
integrity sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==
@@ -3330,7 +3210,7 @@
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699"
integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==
"@noble/secp256k1@^1.3.0", "@noble/secp256k1@^1.5.4":
"@noble/secp256k1@^1.3.0":
version "1.7.1"
resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c"
integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==
@@ -3377,69 +3257,6 @@
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
"@plebbit/plebbit-js@https://github.com/plebbit/plebbit-js.git#3debda4034d18cf62ae46a4257a47284a8281e31":
version "0.0.3"
resolved "https://github.com/plebbit/plebbit-js.git#3debda4034d18cf62ae46a4257a47284a8281e31"
dependencies:
"@bonfida/spl-name-service" "2.4.2"
"@chainsafe/libp2p-gossipsub" "12.0.0"
"@chainsafe/libp2p-noise" "15.0.0"
"@chainsafe/libp2p-yamux" "6.0.2"
"@ensdomains/eth-ens-namehash" "2.0.15"
"@keyv/sqlite" "3.6.7"
"@libp2p/autonat" "1.0.12"
"@libp2p/bootstrap" "10.0.15"
"@libp2p/circuit-relay-v2" "^1.0.15"
"@libp2p/identify" "1.0.14"
"@libp2p/kad-dht" "12.0.7"
"@libp2p/mplex" "10.0.15"
"@libp2p/peer-id-factory" "4.0.6"
"@libp2p/webrtc" "4.0.19"
"@libp2p/webtransport" "4.0.19"
"@plebbit/plebbit-logger" "github:plebbit/plebbit-logger#355a96d7659ed820047980049dfa627d30d83a69"
"@plebbit/proper-lockfile" "github:plebbit/node-proper-lockfile#7fd6332117340c1d3d98dd0afee2d31cc06f72b8"
"@types/proper-lockfile" "4.1.2"
"@types/uuid" "8.3.4"
assert "2.1.0"
better-sqlite3 "9.3.0"
buffer "6.0.3"
captcha-canvas "3.2.1"
cbor "9.0.1"
debounce "1.2.1"
err-code "3.0.1"
ethers "6.10.0"
ext-name "5.0.0"
file-type "16.5.4"
hpagent "1.2.0"
ipfs-only-hash "4.0.0"
jose "4.11.0"
js-sha256 "0.9.0"
js-sha512 "0.9.0"
keyv "4.5.4"
knex "3.1.0"
kubo-rpc-client "3.0.3"
libp2p "1.2.1"
libp2p-crypto "0.21.2"
limiter-es6-compat "2.1.2"
localforage "1.10.0"
lodash-es "4.17.21"
lru-cache "10.1.0"
open-graph-scraper "6.3.3"
p-limit "4.0.0"
p-timeout "6.1.2"
peer-id "0.16.0"
probe-image-size "7.2.3"
retry "0.13.1"
rpc-websockets "7.10.0"
safe-stable-stringify "2.4.3"
sha1-uint8array "0.10.7"
skia-canvas "1.0.0"
tiny-typed-emitter "2.1.0"
tinycache "1.1.2"
ts-custom-error "3.3.1"
uuid "9.0.1"
viem "1.5.2"
"@plebbit/plebbit-js@https://github.com/plebbit/plebbit-js.git#b6f43c4b639b57622318c46a2e46792ae5fb01c3":
version "0.0.3"
resolved "https://github.com/plebbit/plebbit-js.git#b6f43c4b639b57622318c46a2e46792ae5fb01c3"
@@ -3515,11 +3332,11 @@
dependencies:
debug "4.3.4"
"@plebbit/plebbit-react-hooks@https://github.com/plebbit/plebbit-react-hooks.git#ebad94cad75020c6eef2516fffc87f5a7b7181ff":
"@plebbit/plebbit-react-hooks@https://github.com/plebbit/plebbit-react-hooks.git#d7fa27920f8ec21c9fa08bc216054083e1d59fc0":
version "0.0.1"
resolved "https://github.com/plebbit/plebbit-react-hooks.git#ebad94cad75020c6eef2516fffc87f5a7b7181ff"
resolved "https://github.com/plebbit/plebbit-react-hooks.git#d7fa27920f8ec21c9fa08bc216054083e1d59fc0"
dependencies:
"@plebbit/plebbit-js" "https://github.com/plebbit/plebbit-js.git#3debda4034d18cf62ae46a4257a47284a8281e31"
"@plebbit/plebbit-js" "https://github.com/plebbit/plebbit-js.git#b6f43c4b639b57622318c46a2e46792ae5fb01c3"
"@plebbit/plebbit-logger" "https://github.com/plebbit/plebbit-logger.git"
assert "2.0.0"
ethers "5.6.9"
@@ -4230,11 +4047,6 @@
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca"
integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==
"@types/minimatch@^3.0.4":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40"
integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==
"@types/minimist@^1.2.0":
version "1.2.5"
resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.5.tgz#ec10755e871497bcd83efe927e43ec46e8c0747e"
@@ -4279,13 +4091,6 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.96.tgz#eb0012d23ff53d14d64ec8a352bf89792de6aade"
integrity sha512-84iSqGXoO+Ha16j8pRZ/L90vDMKX04QTYMTfYeE1WrjWaZXuchBehGUZEpNgx7JnmlrIHdnABmpjrQjhCnNldQ==
"@types/node@^18.0.0":
version "18.19.31"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.31.tgz#b7d4a00f7cb826b60a543cebdbda5d189aaecdcd"
integrity sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA==
dependencies:
undici-types "~5.26.4"
"@types/normalize-package-data@^2.4.0", "@types/normalize-package-data@^2.4.1":
version "2.4.4"
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901"
@@ -4960,11 +4765,6 @@ any-promise@^1.0.0:
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==
any-signal@^3.0.0, any-signal@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/any-signal/-/any-signal-3.0.1.tgz#49cae34368187a3472e31de28fb5cb1430caa9a6"
integrity sha512-xgZgJtKEa9YmDqXodIgl7Fl1C8yNXr8w6gXjqK3LW4GcEiYT+6AQfJSE/8SPsEpLLmcvbv8YU+qet94UewHxqg==
any-signal@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/any-signal/-/any-signal-4.1.1.tgz#928416c355c66899e6b2a91cad4488f0324bae03"
@@ -5590,13 +5390,6 @@ blakejs@^1.1.0:
resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814"
integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==
blob-to-it@^2.0.0:
version "2.0.5"
resolved "https://registry.yarnpkg.com/blob-to-it/-/blob-to-it-2.0.5.tgz#5c7af31b139fde37b7f7d8e82dff8358ae38a63f"
integrity sha512-3VIFla8L4JuB+0WCdf+0POI7E1tTl8mhdGiwwwmnZEu6QjRJciS9fIvz8NgWY9URb0iagXYModGEYTcYeq9BMg==
dependencies:
browser-readablestream-to-it "^2.0.0"
blob-to-it@^2.0.5:
version "2.0.7"
resolved "https://registry.yarnpkg.com/blob-to-it/-/blob-to-it-2.0.7.tgz#637b8bb14963a7fce658ee758d9251dd1ee9fd3c"
@@ -5734,11 +5527,6 @@ browser-process-hrtime@^1.0.0:
resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626"
integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==
browser-readablestream-to-it@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/browser-readablestream-to-it/-/browser-readablestream-to-it-1.0.3.tgz#ac3e406c7ee6cdf0a502dd55db33bab97f7fba76"
integrity sha512-+12sHB+Br8HIh6VAMVEG5r3UXCyESIgDW7kzk3BjIXa43DVqVwL7GC5TW3jeh+72dtcH99pPVpw0X8i0jt+/kw==
browser-readablestream-to-it@^2.0.0:
version "2.0.5"
resolved "https://registry.yarnpkg.com/browser-readablestream-to-it/-/browser-readablestream-to-it-2.0.5.tgz#61539778c800930dfee1d4c361a060c99bbaa760"
@@ -5813,7 +5601,7 @@ buffer-from@^1.0.0:
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
buffer@6.0.3, buffer@^6.0.1, buffer@^6.0.3, buffer@~6.0.3:
buffer@6.0.3, buffer@^6.0.3, buffer@~6.0.3:
version "6.0.3"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
@@ -7045,14 +6833,6 @@ d@1, d@^1.0.1, d@^1.0.2:
es5-ext "^0.10.64"
type "^2.7.2"
dag-jose@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/dag-jose/-/dag-jose-4.0.0.tgz#4e65f62af58dd5203b2b094eb52142ffe0cdec1d"
integrity sha512-tw595L3UYoOUT9dSJPbBEG/qpRpw24kRZxa5SLRnlnr+g5L7O8oEs1d3W5TiVA1oJZbthVsf0Vi3zFN66qcEBA==
dependencies:
"@ipld/dag-cbor" "^9.0.0"
multiformats "^11.0.0"
dag-jose@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/dag-jose/-/dag-jose-5.1.0.tgz#1c87391b199444ce5fb78cb53e708b7d79e095d8"
@@ -7506,16 +7286,6 @@ dmg-license@^1.0.11:
smart-buffer "^4.0.2"
verror "^1.10.0"
dns-over-http-resolver@^2.1.0:
version "2.1.3"
resolved "https://registry.yarnpkg.com/dns-over-http-resolver/-/dns-over-http-resolver-2.1.3.tgz#bb7f2e10cc18d960339a6e30e21b8c1d99be7b38"
integrity sha512-zjRYFhq+CsxPAouQWzOsxNMvEN+SHisjzhX8EMxd2Y0EG3thvn6wXQgMJLnTDImkhe4jhLbOQpXtL10nALBOSA==
dependencies:
debug "^4.3.1"
native-fetch "^4.0.2"
receptacle "^1.3.2"
undici "^5.12.0"
dns-packet@^5.2.2, dns-packet@^5.6.1:
version "5.6.1"
resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.6.1.tgz#ae888ad425a9d1478a0674256ab866de1012cf2f"
@@ -7725,7 +7495,7 @@ electron-dl@^3.2.1:
pupa "^2.0.1"
unused-filename "^2.1.0"
electron-fetch@^1.7.2, electron-fetch@^1.9.1:
electron-fetch@^1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/electron-fetch/-/electron-fetch-1.9.1.tgz#e28bfe78d467de3f2dec884b1d72b8b05322f30f"
integrity sha512-M9qw6oUILGVrcENMSRRefE1MbHPIz0h79EKIeJWK9v563aT9Qkh8aEHPO1H5vi970wPirNY+jO9OpFoLiMsMGA==
@@ -9914,15 +9684,6 @@ inquirer@8.2.5:
through "^2.3.6"
wrap-ansi "^7.0.0"
interface-datastore@^7.0.0:
version "7.0.4"
resolved "https://registry.yarnpkg.com/interface-datastore/-/interface-datastore-7.0.4.tgz#f09ae4e2896f57f876d5d742a59e982fb3f42891"
integrity sha512-Q8LZS/jfFFHz6XyZazLTAc078SSCoa27ZPBOfobWdpDiFO7FqPA2yskitUJIhaCgxNK8C+/lMBUTBNfVIDvLiw==
dependencies:
interface-store "^3.0.0"
nanoid "^4.0.0"
uint8arrays "^4.0.2"
interface-datastore@^8.0.0, interface-datastore@^8.2.0, interface-datastore@^8.2.10, interface-datastore@^8.2.11:
version "8.2.11"
resolved "https://registry.yarnpkg.com/interface-datastore/-/interface-datastore-8.2.11.tgz#1d555ce6218ab6cba6291fc361debe9713590207"
@@ -9940,11 +9701,6 @@ interface-ipld-format@^1.0.0:
multicodec "^3.0.1"
multihashes "^4.0.2"
interface-store@^3.0.0:
version "3.0.4"
resolved "https://registry.yarnpkg.com/interface-store/-/interface-store-3.0.4.tgz#670d95ef45f3b7061d154c3cbfaf39a538167ad7"
integrity sha512-OjHUuGXbH4eXSBx1TF1tTySvjLldPLzRSYYXJwrEQI+XfH5JWYZofr0gVMV4F8XTwC+4V7jomDYkvGRmDSRKqQ==
interface-store@^5.0.0:
version "5.1.8"
resolved "https://registry.yarnpkg.com/interface-store/-/interface-store-5.1.8.tgz#94bf867d165b5c904cccf09adeba215a5b0f459e"
@@ -9987,49 +9743,7 @@ ipaddr.js@^2.0.1, ipaddr.js@^2.1.0:
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.1.0.tgz#2119bc447ff8c257753b196fc5f1ce08a4cdf39f"
integrity sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==
ipfs-core-types@^0.14.1:
version "0.14.1"
resolved "https://registry.yarnpkg.com/ipfs-core-types/-/ipfs-core-types-0.14.1.tgz#c69f4dd873324e975ef43c391c0692b96863b13c"
integrity sha512-4ujF8NlM9bYi2I6AIqPP9wfGGX0x/gRCkMoFdOQfxxrFg6HcAdfS+0/irK8mp4e7znOHWReOHeWqCGw+dAPwsw==
dependencies:
"@ipld/dag-pb" "^4.0.0"
"@libp2p/interface-keychain" "^2.0.0"
"@libp2p/interface-peer-id" "^2.0.0"
"@libp2p/interface-peer-info" "^1.0.2"
"@libp2p/interface-pubsub" "^3.0.0"
"@multiformats/multiaddr" "^11.1.5"
"@types/node" "^18.0.0"
interface-datastore "^7.0.0"
ipfs-unixfs "^9.0.0"
multiformats "^11.0.0"
ipfs-core-utils@^0.18.0:
version "0.18.1"
resolved "https://registry.yarnpkg.com/ipfs-core-utils/-/ipfs-core-utils-0.18.1.tgz#d5fae11bfdb511749c6f905b0d0da3174eb50909"
integrity sha512-P7jTpdfvlyBG3JR4o+Th3QJADlmXmwMxbkjszXry6VAjfSfLIIqXsdeYPoVRkV69GFEeQozuz2k/jR+U8cUH/Q==
dependencies:
"@libp2p/logger" "^2.0.5"
"@multiformats/multiaddr" "^11.1.5"
"@multiformats/multiaddr-to-uri" "^9.0.1"
any-signal "^3.0.0"
blob-to-it "^2.0.0"
browser-readablestream-to-it "^2.0.0"
err-code "^3.0.1"
ipfs-core-types "^0.14.1"
ipfs-unixfs "^9.0.0"
ipfs-utils "^9.0.13"
it-all "^2.0.0"
it-map "^2.0.0"
it-peekable "^2.0.0"
it-to-stream "^1.0.0"
merge-options "^3.0.4"
multiformats "^11.0.0"
nanoid "^4.0.0"
parse-duration "^1.0.0"
timeout-abort-controller "^3.0.0"
uint8arrays "^4.0.2"
ipfs-only-hash@4.0.0, ipfs-only-hash@^4.0.0:
ipfs-only-hash@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/ipfs-only-hash/-/ipfs-only-hash-4.0.0.tgz#b3bd60a244d9eb7394961aa9d812a2e5ac7c04d6"
integrity sha512-TE1DZCvfw8i3gcsTq3P4TFx3cKFJ3sluu/J3XINkJhIN9OwJgNMqKA+WnKx6ByCb1IoPXsTp1KM7tupElb6SyA==
@@ -10074,36 +9788,6 @@ ipfs-unixfs@^4.0.3:
err-code "^3.0.1"
protobufjs "^6.10.2"
ipfs-unixfs@^9.0.0:
version "9.0.1"
resolved "https://registry.yarnpkg.com/ipfs-unixfs/-/ipfs-unixfs-9.0.1.tgz#d06e688e07ef4ce08d610337ba2fe8c143c386e7"
integrity sha512-jh2CbXyxID+v3jLml9CqMwjdSS9ZRnsGfQGGPOfem0/hT/L48xUeTPvh7qLFWkZcIMhZtG+fnS1teei8x5uGBg==
dependencies:
err-code "^3.0.1"
protobufjs "^7.0.0"
ipfs-utils@^9.0.13, ipfs-utils@^9.0.7:
version "9.0.14"
resolved "https://registry.yarnpkg.com/ipfs-utils/-/ipfs-utils-9.0.14.tgz#24f5fda1f4567685eb32bca2543d518f95fd8704"
integrity sha512-zIaiEGX18QATxgaS0/EOQNoo33W0islREABAcxXE8n7y2MGAlB+hdsxXn4J0hGZge8IqVQhW8sWIb+oJz2yEvg==
dependencies:
any-signal "^3.0.0"
browser-readablestream-to-it "^1.0.0"
buffer "^6.0.1"
electron-fetch "^1.7.2"
err-code "^3.0.1"
is-electron "^2.2.0"
iso-url "^1.1.5"
it-all "^1.0.4"
it-glob "^1.0.1"
it-to-stream "^1.0.0"
merge-options "^3.0.4"
nanoid "^3.1.20"
native-fetch "^3.0.0"
node-fetch "^2.6.8"
react-native-fetch-api "^3.0.0"
stream-to-it "^0.2.2"
ipld-dag-pb@^0.22.2:
version "0.22.3"
resolved "https://registry.yarnpkg.com/ipld-dag-pb/-/ipld-dag-pb-0.22.3.tgz#6d5af28b5752236a5cb0e0a1888c87dd733b55cd"
@@ -10555,7 +10239,7 @@ iso-random-stream@^2.0.0:
events "^3.3.0"
readable-stream "^3.4.0"
iso-url@^1.1.5, iso-url@^1.2.1:
iso-url@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/iso-url/-/iso-url-1.2.1.tgz#db96a49d8d9a64a1c889fc07cc525d093afb1811"
integrity sha512-9JPDgCN4B7QPkLtYAAOrEuAWvP9rWvR5offAr0/SeF046wIkglqH3VXgYYP6NcsKslH80UIVgmPqNe3j7tG2ng==
@@ -10612,16 +10296,11 @@ istanbul-reports@^3.1.3:
html-escaper "^2.0.0"
istanbul-lib-report "^3.0.0"
it-all@^1.0.4, it-all@^1.0.5:
it-all@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/it-all/-/it-all-1.0.6.tgz#852557355367606295c4c3b7eff0136f07749335"
integrity sha512-3cmCc6Heqe3uWi3CVM/k51fa/XbMFpQVzFoDsV0IZNHSQDyAXl3c4MjHkFX5kF3922OGj7Myv1nSEUgRtcuM1A==
it-all@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/it-all/-/it-all-2.0.1.tgz#45d530ecf6e13fb81d7ba583cdfd55ffdb376b05"
integrity sha512-9UuJcCRZsboz+HBQTNOau80Dw+ryGaHYFP/cPYzFBJBFcfDathMYnhHk4t52en9+fcyDGPTdLB+lFc1wzQIroA==
it-all@^3.0.0, it-all@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/it-all/-/it-all-3.0.4.tgz#08f2e3eb3df04fa4525a343dcacfbdf91ffee162"
@@ -10659,11 +10338,6 @@ it-first@^1.0.6:
resolved "https://registry.yarnpkg.com/it-first/-/it-first-1.0.7.tgz#a4bef40da8be21667f7d23e44dae652f5ccd7ab1"
integrity sha512-nvJKZoBpZD/6Rtde6FXqwDqDZGF1sCADmr2Zoc0hZsIvnE449gRFnGctxDf09Bzc/FWnHXAdaHVIetY6lrE0/g==
it-first@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/it-first/-/it-first-2.0.1.tgz#75d66b254c385ae3a1906def060a69006a437cef"
integrity sha512-noC1oEQcWZZMUwq7VWxHNLML43dM+5bviZpfmkxkXlvBe60z7AFRqpZSga9uQBo792jKv9otnn1IjA4zwgNARw==
it-first@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/it-first/-/it-first-3.0.4.tgz#d68c8ae646ea402cd5e650c352da69988a310342"
@@ -10676,14 +10350,6 @@ it-foreach@^2.0.6:
dependencies:
it-peekable "^3.0.0"
it-glob@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/it-glob/-/it-glob-1.0.2.tgz#bab9b04d6aaac42884502f3a0bfee84c7a29e15e"
integrity sha512-Ch2Dzhw4URfB9L/0ZHyY+uqOnKvBNeS/SMcRiPmJfpHiM0TsUZn+GkpcZxAoF3dJVdPm/PuIk3A4wlV7SUo23Q==
dependencies:
"@types/minimatch" "^3.0.4"
minimatch "^3.0.4"
it-glob@^2.0.6:
version "2.0.7"
resolved "https://registry.yarnpkg.com/it-glob/-/it-glob-2.0.7.tgz#9c65a14dd89d3c19c0eaec50d382ee2b2396caba"
@@ -10691,11 +10357,6 @@ it-glob@^2.0.6:
dependencies:
minimatch "^9.0.4"
it-last@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/it-last/-/it-last-2.0.1.tgz#a6f3253459cb77e917aeada3b2c4ae9f3e66c64b"
integrity sha512-uVMedYW0wa2Cx0TAmcOCLbfuLLII7+vyURmhKa8Zovpd+aBTMsmINtsta2n364wJ5qsEDBH+akY1sUtAkaYBlg==
it-last@^3.0.4:
version "3.0.6"
resolved "https://registry.yarnpkg.com/it-last/-/it-last-3.0.6.tgz#53b1463e47fcaa950375968002598686101de6ab"
@@ -10728,11 +10389,6 @@ it-length@^3.0.4:
resolved "https://registry.yarnpkg.com/it-length/-/it-length-3.0.4.tgz#d178a18a6baf2cb448aeed53b6c854262abf38a1"
integrity sha512-RS3thYkvqtWksrV7SaAnTv+pgY7ozpS17HlRvWvcnoRjVyNJMuffdCkIKpKNPTq5uZw9zVnkVKLO077pJn5Yhg==
it-map@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/it-map/-/it-map-2.0.1.tgz#d5251fd6b222d6ee39293d406a3f8fce54fb9220"
integrity sha512-a2GcYDHiAh/eSU628xlvB56LA98luXZnniH2GlD0IdBzf15shEq9rBeb0Rg3o1SWtNILUAwqmQxEXcewGCdvmQ==
it-map@^3.0.5:
version "3.0.5"
resolved "https://registry.yarnpkg.com/it-map/-/it-map-3.0.5.tgz#30b1e1324cdb4aaadba29cd989485168d1dc4136"
@@ -10769,11 +10425,6 @@ it-parallel@^3.0.6:
dependencies:
p-defer "^4.0.0"
it-peekable@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/it-peekable/-/it-peekable-2.0.1.tgz#1e118610473aa511efc70f3bb7a7066178728dd3"
integrity sha512-fJ/YTU9rHRhGJOM2hhQKKEfRM6uKB9r4yGGFLBHqp72ACC8Yi6+7/FhuBAMG8cpN6mLoj9auVX7ZJ3ul6qFpTA==
it-peekable@^3.0.0:
version "3.0.3"
resolved "https://registry.yarnpkg.com/it-peekable/-/it-peekable-3.0.3.tgz#5f5741f34f3acd5735804f40d198652c54a3d8c1"
@@ -10803,7 +10454,7 @@ it-protobuf-stream@^1.1.2:
protons-runtime "^5.0.0"
uint8arraylist "^2.4.1"
it-pushable@^3.0.0, it-pushable@^3.1.2, it-pushable@^3.2.0, it-pushable@^3.2.3:
it-pushable@^3.1.2, it-pushable@^3.2.0, it-pushable@^3.2.3:
version "3.2.3"
resolved "https://registry.yarnpkg.com/it-pushable/-/it-pushable-3.2.3.tgz#e2b80aed90cfbcd54b620c0a0785e546d4e5f334"
integrity sha512-gzYnXYK8Y5t5b/BnJUr7glfQLO4U5vyb05gPx/TyTw+4Bv1zM9gFk4YsOrnulWefMewlphCjKkakFvj1y99Tcg==
@@ -10825,11 +10476,6 @@ it-sort@^3.0.4:
dependencies:
it-all "^3.0.0"
it-stream-types@^1.0.4:
version "1.0.5"
resolved "https://registry.yarnpkg.com/it-stream-types/-/it-stream-types-1.0.5.tgz#9c72e6adefdea9dac69d0a28fbea783deebd508d"
integrity sha512-I88Ka1nHgfX62e5mi5LLL+oueqz7Ltg0bUdtsUKDe9SoUqbQPf2Mp5kxDTe9pNhHQGs4pvYPAINwuZ1HAt42TA==
it-stream-types@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/it-stream-types/-/it-stream-types-2.0.1.tgz#69cb4d7e79e707b8257a8997e02751ccb6c3af32"
@@ -11714,31 +11360,6 @@ knex@3.1.0:
tarn "^3.0.2"
tildify "2.0.0"
kubo-rpc-client@3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/kubo-rpc-client/-/kubo-rpc-client-3.0.3.tgz#7b6e51e0e6be9b0c999af2456ad45398c456c517"
integrity sha512-PK+a1uOSYheu9y5+vh6WBfJT3pRl5LPUVdmaeg/wTpwSLL8VLgDmimtgybG7Z0SvM2UAA/cmrc32626/w+GS9Q==
dependencies:
"@ipld/dag-cbor" "^9.0.0"
"@ipld/dag-json" "^10.0.0"
"@ipld/dag-pb" "^4.0.0"
"@libp2p/crypto" "^1.0.11"
"@libp2p/logger" "^2.0.5"
"@libp2p/peer-id" "^2.0.0"
"@multiformats/multiaddr" "^12.1.10"
any-signal "^3.0.1"
dag-jose "^4.0.0"
err-code "^3.0.1"
ipfs-core-utils "^0.18.0"
ipfs-utils "^9.0.7"
it-first "^2.0.0"
it-last "^2.0.0"
merge-options "^3.0.4"
multiformats "^11.0.0"
parse-duration "^1.0.2"
stream-to-it "^0.2.4"
uint8arrays "^4.0.3"
kubo-rpc-client@4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/kubo-rpc-client/-/kubo-rpc-client-4.1.1.tgz#31ca09b24233948919be43bc3971708313c09bf1"
@@ -11997,11 +11618,6 @@ locate-path@^7.1.0:
dependencies:
p-locate "^6.0.0"
lodash-es@4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
lodash.debounce@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
@@ -12075,11 +11691,6 @@ long@^4.0.0:
resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28"
integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==
long@^5.0.0:
version "5.2.3"
resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1"
integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==
longest-streak@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4"
@@ -12984,16 +12595,6 @@ multicodec@^3.0.1:
uint8arrays "^3.0.0"
varint "^6.0.0"
multiformats@^11.0.0, multiformats@^11.0.2:
version "11.0.2"
resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-11.0.2.tgz#b14735efc42cd8581e73895e66bebb9752151b60"
integrity sha512-b5mYMkOkARIuVZCpvijFj9a6m5wMVLC7cf/jIPd5D/ARDOfLC5+IFkbgDXQgcU2goIsTD/O9NY4DI/Mt4OGvlg==
multiformats@^12.0.1:
version "12.1.3"
resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-12.1.3.tgz#cbf7a9861e11e74f8228b21376088cb43ba8754e"
integrity sha512-eajQ/ZH7qXZQR2AgtfpmSMizQzmyYVmCql7pdhldPuYQi4atACekbJaQplk6dWyIi10jCaFnd6pqvcEFXjbaJw==
multiformats@^13.0.0, multiformats@^13.0.1, multiformats@^13.1.0:
version "13.1.0"
resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-13.1.0.tgz#5aa9d2175108a448fc3bdb54ba8a3d0b6cab3ac3"
@@ -13044,16 +12645,11 @@ mz@^2.7.0:
object-assign "^4.0.1"
thenify-all "^1.0.0"
nanoid@^3.1.20, nanoid@^3.3.7:
nanoid@^3.3.7:
version "3.3.7"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
nanoid@^4.0.0:
version "4.0.2"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-4.0.2.tgz#140b3c5003959adbebf521c170f282c5e7f9fb9e"
integrity sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==
nanoid@^5.0.7:
version "5.0.7"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-5.0.7.tgz#6452e8c5a816861fd9d2b898399f7e5fd6944cc6"
@@ -13064,11 +12660,6 @@ napi-build-utils@^1.0.1:
resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806"
integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==
native-fetch@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/native-fetch/-/native-fetch-3.0.0.tgz#06ccdd70e79e171c365c75117959cf4fe14a09bb"
integrity sha512-G3Z7vx0IFb/FQ4JxvtqGABsOTIqRWvgQz6e+erkB+JJD6LrszQtMozEHI4EkmgZQvnGHrpLVzUWk7t4sJCIkVw==
native-fetch@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/native-fetch/-/native-fetch-4.0.2.tgz#75c8a44c5f3bb021713e5e24f2846750883e49af"
@@ -13173,14 +12764,14 @@ node-domexception@^2.0.1:
resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-2.0.1.tgz#83b0d101123b5bbf91018fd569a58b88ae985e5b"
integrity sha512-M85rnSC7WQ7wnfQTARPT4LrK7nwCHLdDFOCcItZMhTQjyCebJH8GciKqYJNgaOFZs9nFmTmd/VMyi3OW5jA47w==
node-fetch@^2.6.1, node-fetch@^2.6.11, node-fetch@^2.6.7, node-fetch@^2.6.8, node-fetch@^2.7.0:
node-fetch@^2.6.1, node-fetch@^2.6.11, node-fetch@^2.6.7, node-fetch@^2.7.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
dependencies:
whatwg-url "^5.0.0"
node-forge@^1, node-forge@^1.1.0, node-forge@^1.2.1:
node-forge@^1, node-forge@^1.2.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3"
integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==
@@ -13697,7 +13288,7 @@ parenthesis@^3.1.5:
resolved "https://registry.yarnpkg.com/parenthesis/-/parenthesis-3.1.8.tgz#3457fccb8f05db27572b841dad9d2630b912f125"
integrity sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw==
parse-duration@^1.0.0, parse-duration@^1.0.2:
parse-duration@^1.0.2:
version "1.1.0"
resolved "https://registry.yarnpkg.com/parse-duration/-/parse-duration-1.1.0.tgz#5192084c5d8f2a3fd676d04a451dbd2e05a1819c"
integrity sha512-z6t9dvSJYaPoQq7quMzdEagSFtpGu+utzHqqxmpVWNNZRIXnvqyCvn9XsTdh7c/w0Bqmdz3RB3YnRaKtpRtEXQ==
@@ -14655,24 +14246,6 @@ protobufjs@^6.10.2, protobufjs@^6.11.2:
"@types/node" ">=13.7.0"
long "^4.0.0"
protobufjs@^7.0.0:
version "7.2.6"
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.6.tgz#4a0ccd79eb292717aacf07530a07e0ed20278215"
integrity sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==
dependencies:
"@protobufjs/aspromise" "^1.1.2"
"@protobufjs/base64" "^1.1.2"
"@protobufjs/codegen" "^2.0.4"
"@protobufjs/eventemitter" "^1.1.0"
"@protobufjs/fetch" "^1.1.0"
"@protobufjs/float" "^1.0.2"
"@protobufjs/inquire" "^1.1.0"
"@protobufjs/path" "^1.1.2"
"@protobufjs/pool" "^1.1.0"
"@protobufjs/utf8" "^1.1.0"
"@types/node" ">=13.7.0"
long "^5.0.0"
protons-runtime@5.4.0, protons-runtime@^5.0.0, protons-runtime@^5.4.0:
version "5.4.0"
resolved "https://registry.yarnpkg.com/protons-runtime/-/protons-runtime-5.4.0.tgz#2751ce22cae6c35eebba89acfd9d783419ae3726"
@@ -15122,13 +14695,6 @@ readdirp@~3.6.0:
dependencies:
picomatch "^2.2.1"
receptacle@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/receptacle/-/receptacle-1.3.2.tgz#a7994c7efafc7a01d0e2041839dab6c4951360d2"
integrity sha512-HrsFvqZZheusncQRiEE7GatOAETrARKV/lnfYicIm8lbvp/JQOdADOfhjBd2DajvoszEyxSM6RlAAIZgEoeu/A==
dependencies:
ms "^2.1.1"
rechoir@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22"
@@ -15396,11 +14962,6 @@ restore-cursor@^3.1.0:
onetime "^5.1.0"
signal-exit "^3.0.2"
retimer@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/retimer/-/retimer-3.0.0.tgz#98b751b1feaf1af13eb0228f8ea68b8f9da530df"
integrity sha512-WKE0j11Pa0ZJI5YIk0nflGI7SQsfl2ljihVy7ogh7DeQSeYAUi0ubZ/yEueGtDfUPk6GH5LRw1hBdLq4IwUBWA==
retry@0.13.1, retry@^0.13.1:
version "0.13.1"
resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658"
@@ -16188,13 +15749,6 @@ stream-parser@~0.3.1:
dependencies:
debug "2"
stream-to-it@^0.2.2, stream-to-it@^0.2.4:
version "0.2.4"
resolved "https://registry.yarnpkg.com/stream-to-it/-/stream-to-it-0.2.4.tgz#d2fd7bfbd4a899b4c0d6a7e6a533723af5749bd0"
integrity sha512-4vEbkSs83OahpmBybNJXlJd7d6/RxzkkSdT3I0mnGt79Xd2Kk+e1JqbvAvsQfCeKj3aKb0QIWkyK3/n0j506vQ==
dependencies:
get-iterator "^1.0.2"
stream-to-it@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/stream-to-it/-/stream-to-it-1.0.1.tgz#7d5e1b04bab70facd48273279bfa49f0d0165950"
@@ -16761,13 +16315,6 @@ tildify@2.0.0:
resolved "https://registry.yarnpkg.com/tildify/-/tildify-2.0.0.tgz#f205f3674d677ce698b7067a99e949ce03b4754a"
integrity sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==
timeout-abort-controller@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/timeout-abort-controller/-/timeout-abort-controller-3.0.0.tgz#dd57ffca041652c03769904f8d95afd93fb95595"
integrity sha512-O3e+2B8BKrQxU2YRyEjC/2yFdb33slI22WRdUaDx6rvysfi9anloNZyR2q0l6LnePo5qH7gSM7uZtvvwZbc2yA==
dependencies:
retimer "^3.0.0"
timers-ext@^0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.7.tgz#6f57ad8578e07a3fb9f91d9387d65647555e25c6"
@@ -17125,7 +16672,7 @@ uint8-varint@^2.0.1, uint8-varint@^2.0.2, uint8-varint@^2.0.4:
uint8arraylist "^2.0.0"
uint8arrays "^5.0.0"
uint8arraylist@^2.0.0, uint8arraylist@^2.1.2, uint8arraylist@^2.4.1, uint8arraylist@^2.4.3, uint8arraylist@^2.4.8:
uint8arraylist@^2.0.0, uint8arraylist@^2.4.1, uint8arraylist@^2.4.3, uint8arraylist@^2.4.8:
version "2.4.8"
resolved "https://registry.yarnpkg.com/uint8arraylist/-/uint8arraylist-2.4.8.tgz#5a4d17f4defd77799cb38e93fd5db0f0dceddc12"
integrity sha512-vc1PlGOzglLF0eae1M8mLRTBivsvrGsdmJ5RbK3e+QRvRLOZfZhQROTwH/OfyF3+ZVUg9/8hE8bmKP2CvP9quQ==
@@ -17146,13 +16693,6 @@ uint8arrays@^2.0.5, uint8arrays@^2.1.2:
dependencies:
multiformats "^9.4.2"
uint8arrays@^4.0.2, uint8arrays@^4.0.3:
version "4.0.10"
resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-4.0.10.tgz#3ec5cde3348903c140e87532fc53f46b8f2e921f"
integrity sha512-AnJNUGGDJAgFw/eWu/Xb9zrVKEGlwJJCaeInlf3BkecE/zcTobk5YXYIPNQJO1q5Hh1QZrQQHf0JvcHqz2hqoA==
dependencies:
multiformats "^12.0.1"
uint8arrays@^5.0.0, uint8arrays@^5.0.1, uint8arrays@^5.0.2, uint8arrays@^5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-5.0.3.tgz#92b894d9c4269ba97c51544d6e1f279fe6f80d1f"
@@ -17195,13 +16735,6 @@ undici-types@~5.26.4:
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
undici@^5.12.0:
version "5.28.4"
resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068"
integrity sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==
dependencies:
"@fastify/busboy" "^2.0.0"
undici@^6.4.0:
version "6.12.0"
resolved "https://registry.yarnpkg.com/undici/-/undici-6.12.0.tgz#396d4dbd2ea2351094c00be44655d489afe4fb8a"