mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
feat(home): add filter options to boards box
This commit is contained in:
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "حظر المستخدم لمدة <1></1> يوم(أيام)",
|
||||
"optional": "اختياري",
|
||||
"close_thread": "اغلق الموضوع",
|
||||
"view_spoiler": "عرض الحاضر"
|
||||
"view_spoiler": "عرض الحاضر",
|
||||
"filter": "تصفية"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "ব্যবহারকারীকে <1></1> দিনের জন্য বন্ধ করুন",
|
||||
"optional": "ঐচ্ছিক",
|
||||
"close_thread": "থ্রেড বন্ধ করুন",
|
||||
"view_spoiler": "স্পোয়ালার দেখুন"
|
||||
"view_spoiler": "স্পোয়ালার দেখুন",
|
||||
"filter": "ফিল্টার"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "Zakázat uživatele na <1></1> den (dní)",
|
||||
"optional": "volitelný",
|
||||
"close_thread": "uzavřít vlákno",
|
||||
"view_spoiler": "zobrazit spoiler"
|
||||
"view_spoiler": "zobrazit spoiler",
|
||||
"filter": "filtr"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "Udeluk brugeren i <1></1> dag(e)",
|
||||
"optional": "valgfri",
|
||||
"close_thread": "luk tråd",
|
||||
"view_spoiler": "vis spoiler"
|
||||
"view_spoiler": "vis spoiler",
|
||||
"filter": "filtrér"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "Benutzer für <1></1> Tag(e) sperren",
|
||||
"optional": "optional",
|
||||
"close_thread": "Thread schließen",
|
||||
"view_spoiler": "Spoiler anzeigen"
|
||||
"view_spoiler": "Spoiler anzeigen",
|
||||
"filter": "filtern"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "Αποκλεισμός χρήστη για <1></1> ημέρα(ες)",
|
||||
"optional": "προαιρετικό",
|
||||
"close_thread": "κλείσιμο νήματος",
|
||||
"view_spoiler": "προβολή spoiler"
|
||||
"view_spoiler": "προβολή spoiler",
|
||||
"filter": "φίλτρο"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "Ban user for <1></1> day(s)",
|
||||
"optional": "optional",
|
||||
"close_thread": "close thread",
|
||||
"view_spoiler": "view spoiler"
|
||||
"view_spoiler": "view spoiler",
|
||||
"filter": "filter"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "Prohibir al usuario durante <1></1> día(s)",
|
||||
"optional": "opcional",
|
||||
"close_thread": "cerrar hilo",
|
||||
"view_spoiler": "ver spoiler"
|
||||
"view_spoiler": "ver spoiler",
|
||||
"filter": "filtrar"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "بستن کاربر برای <1></1> روز",
|
||||
"optional": "اختیاری",
|
||||
"close_thread": "بستن موضوع",
|
||||
"view_spoiler": "نمایش اسپویلر"
|
||||
"view_spoiler": "نمایش اسپویلر",
|
||||
"filter": "فیلتر"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "Estä käyttäjä <1></1> päiväksi",
|
||||
"optional": "valinnainen",
|
||||
"close_thread": "sulje keskustelu",
|
||||
"view_spoiler": "näytä spoiler"
|
||||
"view_spoiler": "näytä spoiler",
|
||||
"filter": "suodatin"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "I-ban ang user para sa <1></1> araw",
|
||||
"optional": "opsyonal",
|
||||
"close_thread": "isara ang thread",
|
||||
"view_spoiler": "tingnan ang spoiler"
|
||||
"view_spoiler": "tingnan ang spoiler",
|
||||
"filter": "salain"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "Bannir l'utilisateur pendant <1></1> jour(s)",
|
||||
"optional": "facultatif",
|
||||
"close_thread": "fermer le fil",
|
||||
"view_spoiler": "voir le spoiler"
|
||||
"view_spoiler": "voir le spoiler",
|
||||
"filter": "filtre"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "אסור למשתמש למשך <1></1> יום",
|
||||
"optional": "אופציונלי",
|
||||
"close_thread": "סגור את הנושא",
|
||||
"view_spoiler": "הצג ספוילר"
|
||||
"view_spoiler": "הצג ספוילר",
|
||||
"filter": "סנן"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "उपयोगकर्ता को <1></1> दिनों के लिए प्रतिबंधित करें",
|
||||
"optional": "वैकल्पिक",
|
||||
"close_thread": "थ्रेड बंद करें",
|
||||
"view_spoiler": "स्पॉइलर देखें"
|
||||
"view_spoiler": "स्पॉइलर देखें",
|
||||
"filter": "फ़िल्टर"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"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"
|
||||
"view_spoiler": "spoiler megtekintése",
|
||||
"filter": "szűrő"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "Blokir pengguna selama <1></1> hari",
|
||||
"optional": "opsional",
|
||||
"close_thread": "tutup utas",
|
||||
"view_spoiler": "lihat spoiler"
|
||||
"view_spoiler": "lihat spoiler",
|
||||
"filter": "menyaring"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "Banna l'utente per <1></1> giorno/i",
|
||||
"optional": "opzionale",
|
||||
"close_thread": "chiudi il thread",
|
||||
"view_spoiler": "mostra spoiler"
|
||||
"view_spoiler": "mostra spoiler",
|
||||
"filter": "filtro"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "ユーザーを<1></1>日間禁止する",
|
||||
"optional": "オプショナル",
|
||||
"close_thread": "スレッドを閉じる",
|
||||
"view_spoiler": "スポイラーを表示"
|
||||
"view_spoiler": "スポイラーを表示",
|
||||
"filter": "フィルター"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "사용자를 <1></1>일 동안 차단하기",
|
||||
"optional": "선택",
|
||||
"close_thread": "스레드 닫기",
|
||||
"view_spoiler": "스포일러 보기"
|
||||
"view_spoiler": "스포일러 보기",
|
||||
"filter": "필터"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "वापरकर्ता ब्लॉक करा <1></1> दिवस",
|
||||
"optional": "वैकल्पिक",
|
||||
"close_thread": "थ्रेड बंद करा",
|
||||
"view_spoiler": "स्पॉईलर पहा"
|
||||
"view_spoiler": "स्पॉईलर पहा",
|
||||
"filter": "फिल्टर"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "Gebruiker verbannen voor <1></1> dag(en)",
|
||||
"optional": "optioneel",
|
||||
"close_thread": "draad sluiten",
|
||||
"view_spoiler": "bekijk spoiler"
|
||||
"view_spoiler": "bekijk spoiler",
|
||||
"filter": "filter"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "Blokker bruker i <1></1> dag(er)",
|
||||
"optional": "valgfritt",
|
||||
"close_thread": "lukk tråd",
|
||||
"view_spoiler": "se spoiler"
|
||||
"view_spoiler": "se spoiler",
|
||||
"filter": "filter"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "Zablokuj użytkownika na <1></1> dzień (dni)",
|
||||
"optional": "opcjonalnie",
|
||||
"close_thread": "zamknij wątek",
|
||||
"view_spoiler": "pokaż spoiler"
|
||||
"view_spoiler": "pokaż spoiler",
|
||||
"filter": "filtr"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "Banir usuário por <1></1> dia(s)",
|
||||
"optional": "opcional",
|
||||
"close_thread": "fechar tópico",
|
||||
"view_spoiler": "ver spoiler"
|
||||
"view_spoiler": "ver spoiler",
|
||||
"filter": "filtro"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "Interzice utilizatorul pentru <1></1> zi(zile)",
|
||||
"optional": "opțional",
|
||||
"close_thread": "închide firul",
|
||||
"view_spoiler": "vezi spoilerul"
|
||||
"view_spoiler": "vezi spoilerul",
|
||||
"filter": "filtru"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "Заблокировать пользователя на <1></1> день(дней)",
|
||||
"optional": "необязательный",
|
||||
"close_thread": "закрыть тему",
|
||||
"view_spoiler": "посмотреть спойлер"
|
||||
"view_spoiler": "посмотреть спойлер",
|
||||
"filter": "фильтр"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "Ndalo përdoruesin për <1></1> ditë",
|
||||
"optional": "opsionale",
|
||||
"close_thread": "mbyll temën",
|
||||
"view_spoiler": "shikoni spoiler"
|
||||
"view_spoiler": "shikoni spoiler",
|
||||
"filter": "filtruar"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "Blockera användaren i <1></1> dag(ar)",
|
||||
"optional": "valfritt",
|
||||
"close_thread": "stäng tråd",
|
||||
"view_spoiler": "visa spoiler"
|
||||
"view_spoiler": "visa spoiler",
|
||||
"filter": "filter"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "<1></1> రోజుల కోసం వాడుకరినీ బ్యాన్ చేయండి",
|
||||
"optional": "ఐచ్ఛికం",
|
||||
"close_thread": "థ్రెడ్ను మూసేయండి",
|
||||
"view_spoiler": "స్పోయ్లర్ వీక్షించండి"
|
||||
"view_spoiler": "స్పోయ్లర్ వీక్షించండి",
|
||||
"filter": "వడపోత"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "แบนผู้ใช้ <1></1> วัน",
|
||||
"optional": "ไม่จำเป็น",
|
||||
"close_thread": "ปิดกระทู้",
|
||||
"view_spoiler": "ดูสปอยล์เลอร์"
|
||||
"view_spoiler": "ดูสปอยล์เลอร์",
|
||||
"filter": "ตัวกรอง"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"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"
|
||||
"view_spoiler": "spoileri görüntüle",
|
||||
"filter": "filtre"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "Заборонити користувача на <1></1> день (днів)",
|
||||
"optional": "необов’язково",
|
||||
"close_thread": "закрити тему",
|
||||
"view_spoiler": "переглянути спойлер"
|
||||
"view_spoiler": "переглянути спойлер",
|
||||
"filter": "фільтр"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "<1></1> دنوں کے لیے صارف کو پابندی عائد کریں",
|
||||
"optional": "اختیاری",
|
||||
"close_thread": "موضوع بند کریں",
|
||||
"view_spoiler": "اسپوائلر دیکھیں"
|
||||
"view_spoiler": "اسپوائلر دیکھیں",
|
||||
"filter": "فلٹر"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"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"
|
||||
"view_spoiler": "xem spoiler",
|
||||
"filter": "bộ lọc"
|
||||
}
|
||||
@@ -146,5 +146,6 @@
|
||||
"ban_user_for": "封禁用户 <1></1> 天",
|
||||
"optional": "可选的",
|
||||
"close_thread": "关闭主题",
|
||||
"view_spoiler": "查看剧透"
|
||||
"view_spoiler": "查看剧透",
|
||||
"filter": "过滤器"
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import useDefaultSubplebbits, { categorizeSubplebbits, useDefaultSubplebbitAddre
|
||||
import _, { debounce } from 'lodash';
|
||||
|
||||
const SearchBar = ({ setShowSearchBar }: { setShowSearchBar: (show: boolean) => void }) => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const searchBarRef = useRef<HTMLDivElement>(null);
|
||||
const searchInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+215
-73
@@ -1,9 +1,10 @@
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import styles from './home.module.css';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
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 { create } from 'zustand';
|
||||
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';
|
||||
@@ -12,12 +13,32 @@ import { getCommentMediaInfo } from '../../lib/utils/media-utils';
|
||||
import { CatalogPostMedia } from '../../components/catalog-row';
|
||||
import LoadingEllipsis from '../../components/loading-ellipsis';
|
||||
|
||||
const isValidAddress = (address: string): boolean => {
|
||||
if (address.includes('/') || address.includes('\\') || address.includes(' ')) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
interface BoxModalStore {
|
||||
showNsfwBoardsOnly: boolean;
|
||||
setShowNsfwBoardsOnly: (value: boolean) => void;
|
||||
showWorksafeBoardsOnly: boolean;
|
||||
setShowWorksafeBoardsOnly: (value: boolean) => void;
|
||||
useCatalog: boolean;
|
||||
setUseCatalog: (value: boolean) => void;
|
||||
}
|
||||
|
||||
const useBoxModalStore = create<BoxModalStore>((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());
|
||||
},
|
||||
}));
|
||||
|
||||
const SearchBar = () => {
|
||||
const searchInputRef = useRef<HTMLInputElement>(null);
|
||||
@@ -25,32 +46,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,35 +87,145 @@ const InfoBox = () => {
|
||||
);
|
||||
};
|
||||
|
||||
// https://github.com/plebbit/temporary-default-subplebbits/blob/master/README.md
|
||||
const nsfwTags = ['adult', 'anti', 'gore'];
|
||||
|
||||
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));
|
||||
const { useCatalog } = useBoxModalStore();
|
||||
|
||||
const boardLink = useCatalog ? `/p/${address}/catalog` : `/p/${address}`;
|
||||
|
||||
return (
|
||||
<div className={styles.subplebbit} key={address}>
|
||||
{isOffline && <span className={styles.offlineIcon} />}
|
||||
<Link to={`/p/${address}`}>{title || address}</Link>
|
||||
<Link to={boardLink}>{title || address}</Link>
|
||||
{nsfwTag && <span className={styles.nsfw}> ({t(nsfwTag)})</span>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
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 } = useBoxModalStore();
|
||||
|
||||
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}>Show Worksafe Content Only</div>
|
||||
<div className={styles.option}>Show NSFW Content Only</div>
|
||||
<div className={`${styles.option} ${styles.selected}`}>Show All Content</div>
|
||||
</>
|
||||
)}
|
||||
</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 { showNsfwBoardsOnly, showWorksafeBoardsOnly } = useBoxModalStore();
|
||||
|
||||
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 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);
|
||||
@@ -113,7 +238,7 @@ const Boards = ({ multisub, subplebbits }: { multisub: Subplebbit[]; subplebbits
|
||||
<div className={`${styles.box} ${styles.boardsBox}`}>
|
||||
<div className={styles.boxBar}>
|
||||
<h2 className={styles.capitalize}>{t('boards')}</h2>
|
||||
<span>{t('options')} ▼</span>
|
||||
<BoxModal isBoardsBoxModal={true} />
|
||||
</div>
|
||||
<div className={styles.boardsBoxContent}>
|
||||
<div className={styles.column}>
|
||||
@@ -126,40 +251,60 @@ const Boards = ({ multisub, subplebbits }: { multisub: Subplebbit[]; subplebbits
|
||||
<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>
|
||||
{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}>
|
||||
<h3>{t('interests')}</h3>
|
||||
<div className={styles.list}>
|
||||
{interestsSubs.map((sub) => (
|
||||
<Board key={sub.address} subplebbit={sub} isOffline={sub.address && isSubOffline(sub.address)} />
|
||||
))}
|
||||
</div>
|
||||
{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}>
|
||||
<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>
|
||||
{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}>
|
||||
@@ -222,7 +367,7 @@ const PopularThreads = ({ subplebbits }: { subplebbits: any }) => {
|
||||
<div className={styles.box}>
|
||||
<div className={`${styles.boxBar} ${styles.color2ColorBar}`}>
|
||||
<h2 className={styles.capitalize}>{t('popular_threads')}</h2>
|
||||
<span>{t('options')} ▼</span>
|
||||
<BoxModal isBoardsBoxModal={false} />
|
||||
</div>
|
||||
<div className={`${styles.boxContent} ${popularPosts.length === 8 ? styles.popularThreads : ''}`}>
|
||||
{popularPosts.length < 8 ? (
|
||||
@@ -268,9 +413,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}>
|
||||
|
||||
Reference in New Issue
Block a user