feat: implement 5chan-specific default board list

This commit is contained in:
plebeius
2025-11-03 22:06:18 +01:00
parent 4fd9a9c1a9
commit bae6faa929
13 changed files with 87 additions and 288 deletions
+4 -7
View File
@@ -129,7 +129,7 @@ const Catalog = () => {
const isInAllView = isAllView(location.pathname);
const defaultSubplebbits = useDefaultSubplebbits();
const { hideAdultBoards, hideGoreBoards } = useInterfaceSettingsStore();
const { hideAdultBoards } = useInterfaceSettingsStore();
const { showTextOnlyThreads, filterItems, searchText, clearMatchedFilters } = useCatalogFiltersStore();
const account = useAccount();
@@ -139,11 +139,8 @@ const Catalog = () => {
const subplebbitAddresses = useMemo(() => {
const filteredDefaultSubplebbits = defaultSubplebbits
.filter((subplebbit) => {
const { tags } = subplebbit;
const hasGoreTag = tags?.includes('gore');
const hasAdultTag = tags?.includes('adult');
if ((hasGoreTag && hideGoreBoards) || (hasAdultTag && hideAdultBoards)) {
// Hide NSFW boards if hideAdultBoards is enabled (treating NSFW as adult content)
if (subplebbit.nsfw && hideAdultBoards) {
return false;
}
return true;
@@ -159,7 +156,7 @@ const Catalog = () => {
}
// Only include subplebbitAddress if it's defined
return subplebbitAddress ? [subplebbitAddress] : [];
}, [isInAllView, isInSubscriptionsView, subplebbitAddress, defaultSubplebbits, subscriptions, hideAdultBoards, hideGoreBoards]);
}, [isInAllView, isInSubscriptionsView, subplebbitAddress, defaultSubplebbits, subscriptions, hideAdultBoards]);
const { imageSize } = useCatalogStyleStore();
const columnWidth = imageSize === 'Large' ? 270 : 180;
+12 -85
View File
@@ -1,95 +1,38 @@
import { useState, useEffect } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { useState } from 'react';
import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import Plebbit from '@plebbit/plebbit-js';
import { Subplebbit, useSubplebbitStats } from '@plebbit/plebbit-react-hooks';
import useSubplebbitsStore from '@plebbit/plebbit-react-hooks/dist/stores/subplebbits';
import { useDefaultSubplebbitsState, useDefaultSubplebbitTags } from '../../../hooks/use-default-subplebbits';
import { useDefaultSubplebbitsState, MultisubSubplebbit } from '../../../hooks/use-default-subplebbits';
import useIsMobile from '../../../hooks/use-is-mobile';
import useIsSubplebbitOffline from '../../../hooks/use-is-subplebbit-offline';
import LoadingEllipsis from '../../../components/loading-ellipsis';
import Tooltip from '../../../components/tooltip';
import styles from './boards-list.module.css';
import { nsfwTags } from '../../../constants/nsfwTags';
const Board = ({ subplebbit, isMobile }: { subplebbit: Subplebbit; isMobile: boolean }) => {
const Board = ({ subplebbit, isMobile }: { subplebbit: MultisubSubplebbit; isMobile: boolean }) => {
const { t } = useTranslation();
const { address, tags } = subplebbit || {};
const nsfwTag = tags?.find((tag: string) => nsfwTags.includes(tag));
let stats = useSubplebbitStats({ subplebbitAddress: address });
const subplebbitData = useSubplebbitsStore((state) => state.subplebbits[address]);
const { isOffline, isOnlineStatusLoading, offlineIconClass, offlineTitle } = useIsSubplebbitOffline(subplebbitData);
const { address, title, nsfw } = subplebbit || {};
const displayAddress = address && Plebbit.getShortAddress(address);
const showOfflineIcon = address && (isOffline || isOnlineStatusLoading);
const title =
subplebbitData?.title ||
(subplebbitData?.updatedAt ? (
displayAddress.endsWith('.eth') || displayAddress.endsWith('.sol') ? (
displayAddress.slice(0, -4)
) : (
displayAddress
)
) : (
<span className={styles.loadingBoardCellValue}>{isOffline ? t('board_not_reachable') : t('loading_board')}</span>
));
return (
<tr key={address}>
<td>
<p className={styles.boardCell}>
<Link to={`/p/${address}`}>{displayAddress}</Link>
{nsfwTag && <span className={styles.nsfw}> ({t(nsfwTag)})</span>}
{showOfflineIcon && (
<span className={styles.offlineIconContainer}>
<Tooltip content={offlineTitle}>
<span className={`${styles.offlineIcon} ${offlineIconClass}`} />
</Tooltip>
</span>
)}
{nsfw && <span className={styles.nsfw}> ({t('nsfw')})</span>}
</p>
</td>
<td>
<p className={styles.boardCell}>{title}</p>
<p className={styles.boardCell}>{title || displayAddress}</p>
</td>
{!isMobile && (
<>
<td className={styles.boardPPH}>
<p className={styles.boardCell}>{stats.hourPostCount ?? <span className={styles.loadingBoardCellValue}>?</span>}</p>
</td>
<td className={styles.boardTags}>
<p className={styles.boardCell}>
{tags.map((tag: string, index: number) => (
<span key={tag}>
<Link to={`/${tag}`}>{tag}</Link>
{index < tags.length - 1 && ', '}
</span>
))}
</p>
</td>
</>
)}
</tr>
);
};
const BoardsList = ({ multisub }: { multisub: Subplebbit[] }) => {
const BoardsList = ({ multisub }: { multisub: MultisubSubplebbit[] }) => {
const { t } = useTranslation();
const location = useLocation();
const [displayCount, setDisplayCount] = useState(15);
const isMobile = useIsMobile();
const { loading, error } = useDefaultSubplebbitsState();
const currentTag = location.pathname.split('/').filter(Boolean)[0];
const tags = useDefaultSubplebbitTags(multisub);
useEffect(() => {
setDisplayCount(15);
}, [currentTag]);
if (loading) {
return (
<div className={styles.boardsBox}>
@@ -108,12 +51,9 @@ const BoardsList = ({ multisub }: { multisub: Subplebbit[] }) => {
);
}
const filteredBoards = (currentTag && tags.includes(currentTag) ? multisub.filter((sub) => sub?.tags?.includes(currentTag)) : multisub).slice(0, displayCount);
const totalBoardCount = currentTag && tags.includes(currentTag) ? multisub.filter((sub) => sub?.tags?.includes(currentTag)).length : multisub.length;
const hasMoreBoards =
currentTag && tags.includes(currentTag) ? displayCount < multisub.filter((sub) => sub?.tags?.includes(currentTag)).length : displayCount < multisub.length;
const filteredBoards = multisub.slice(0, displayCount);
const totalBoardCount = multisub.length;
const hasMoreBoards = displayCount < multisub.length;
return (
<div className={styles.boardsBox}>
@@ -126,12 +66,6 @@ const BoardsList = ({ multisub }: { multisub: Subplebbit[] }) => {
<tr>
<th>{t('board')}</th>
<th>{t('title')}</th>
{!isMobile && (
<th className={styles.boardPPH} title={t('pph')}>
PPH
</th>
)}
{!isMobile && <th>{t('tags')}</th>}
</tr>
</thead>
<tbody>
@@ -141,14 +75,7 @@ const BoardsList = ({ multisub }: { multisub: Subplebbit[] }) => {
</tbody>
</table>
<div className={styles.displayCount}>
{currentTag && tags.includes(currentTag)
? t('displaying_boards_with_tag', {
filteredBoardsCount: filteredBoards.length,
totalBoardCount: totalBoardCount,
currentTag: `"${currentTag}"`,
interpolation: { escapeValue: false },
})
: t('displaying_boards', { filteredBoardsCount: filteredBoards.length, totalBoardCount: totalBoardCount, interpolation: { escapeValue: false } })}
{t('displaying_boards', { filteredBoardsCount: filteredBoards.length, totalBoardCount: totalBoardCount, interpolation: { escapeValue: false } })}
</div>
{hasMoreBoards && (
<div className={styles.loadMoreButton}>
+1 -1
View File
@@ -10,7 +10,7 @@ import BoardsList from './boards-list';
import Version from '../../components/version';
import _ from 'lodash';
// https://github.com/plebbit/temporary-default-subplebbits/blob/master/README.md
// https://github.com/plebbit/lists/blob/master/5chan-multisub.json
const SearchBar = () => {
const searchInputRef = useRef<HTMLInputElement>(null);
@@ -9,7 +9,7 @@ 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 '../../../constants/nsfwTags';
import { MultisubSubplebbit } from '../../../hooks/use-default-subplebbits';
import { removeMarkdown } from '../../../lib/utils/post-utils';
interface PopularThreadProps {
@@ -50,7 +50,7 @@ const PopularThreadCard = ({ post, boardTitle, boardShortAddress }: PopularThrea
);
};
const PopularThreadsBox = ({ multisub, subplebbits }: { multisub: Subplebbit[]; subplebbits: any }) => {
const PopularThreadsBox = ({ multisub, subplebbits }: { multisub: MultisubSubplebbit[]; subplebbits: any }) => {
const { t } = useTranslation();
const { showWorksafeContentOnly, showNsfwContentOnly } = usePopularThreadsOptionsStore();
@@ -58,13 +58,13 @@ const PopularThreadsBox = ({ multisub, subplebbits }: { multisub: Subplebbit[];
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;
return multisubEntry ? !multisubEntry.nsfw : 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 multisubEntry ? multisubEntry.nsfw : false;
});
}
return subplebbits;