mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
feat(all-feed): add NSFW/SFW filter
This commit is contained in:
@@ -10,6 +10,7 @@ import useCatalogFiltersStore from '../../stores/use-catalog-filters-store';
|
||||
import useCatalogStyleStore from '../../stores/use-catalog-style-store';
|
||||
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
||||
import useSortingStore from '../../stores/use-sorting-store';
|
||||
import useAllFeedFilterStore from '../../stores/use-all-feed-filter-store';
|
||||
import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
|
||||
import useIsMobile from '../../hooks/use-is-mobile';
|
||||
import useTimeFilter from '../../hooks/use-time-filter';
|
||||
@@ -247,14 +248,14 @@ export const TimeFilter = ({ isInAllView, isInCatalogView, isInSubscriptionsView
|
||||
? `/all/catalog/${timeFilterName}`
|
||||
: `/all/${timeFilterName}`
|
||||
: isInSubscriptionsView
|
||||
? isInCatalogView
|
||||
? `/subs/catalog/${timeFilterName}`
|
||||
: `/subs/${timeFilterName}`
|
||||
: isInModView
|
||||
? isInCatalogView
|
||||
? `/mod/catalog/${timeFilterName}`
|
||||
: `/mod/${timeFilterName}`
|
||||
: null;
|
||||
? isInCatalogView
|
||||
? `/subs/catalog/${timeFilterName}`
|
||||
: `/subs/${timeFilterName}`
|
||||
: isInModView
|
||||
? isInCatalogView
|
||||
? `/mod/catalog/${timeFilterName}`
|
||||
: `/mod/${timeFilterName}`
|
||||
: null;
|
||||
link && navigate(link);
|
||||
};
|
||||
|
||||
@@ -282,6 +283,22 @@ export const TimeFilter = ({ isInAllView, isInCatalogView, isInSubscriptionsView
|
||||
);
|
||||
};
|
||||
|
||||
const AllFeedFilter = () => {
|
||||
const { t } = useTranslation();
|
||||
const { filter, setFilter } = useAllFeedFilterStore();
|
||||
|
||||
return (
|
||||
<>
|
||||
<span>{t('show')}</span>:
|
||||
<select className='capitalize' value={filter} onChange={(e) => setFilter(e.target.value as 'all' | 'nsfw' | 'sfw')}>
|
||||
<option value='all'>{t('show_all_boards')}</option>
|
||||
<option value='nsfw'>{t('show_nsfw_boards_only')}</option>
|
||||
<option value='sfw'>{t('show_worksafe_boards_only')}</option>
|
||||
</select>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const MobileBoardButtons = () => {
|
||||
const { t } = useTranslation();
|
||||
const params = useParams();
|
||||
@@ -341,6 +358,14 @@ export const MobileBoardButtons = () => {
|
||||
</span>
|
||||
)
|
||||
)}
|
||||
{isInAllView && (
|
||||
<>
|
||||
<hr />
|
||||
<div className={styles.options}>
|
||||
<AllFeedFilter />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{isInCatalogView && (
|
||||
<>
|
||||
<hr />
|
||||
@@ -409,11 +434,9 @@ export const DesktopBoardButtons = () => {
|
||||
{isInPostView || isInPendingPostPage ? (
|
||||
<>
|
||||
[
|
||||
<ReturnButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />
|
||||
] [
|
||||
<ReturnButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />] [
|
||||
<CatalogButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />] [
|
||||
<UpdateButton />
|
||||
] [
|
||||
<UpdateButton />] [
|
||||
<AutoButton />]
|
||||
<span className={styles.rightSideButtons}>
|
||||
<PostPageStats />
|
||||
@@ -462,6 +485,7 @@ export const DesktopBoardButtons = () => {
|
||||
<ShowOPCommentOption />
|
||||
</>
|
||||
)}
|
||||
{isInAllView && <AllFeedFilter />}
|
||||
{(isInAllView || isInSubscriptionsView || isInModView) && (
|
||||
<TimeFilter isInAllView={isInAllView} isInCatalogView={isInCatalogView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useDefaultSubplebbits } from './use-default-subplebbits';
|
||||
import useAllFeedFilterStore from '../stores/use-all-feed-filter-store';
|
||||
|
||||
export const useFilteredDefaultSubplebbitAddresses = () => {
|
||||
const defaultSubplebbits = useDefaultSubplebbits();
|
||||
const { filter } = useAllFeedFilterStore();
|
||||
|
||||
return useMemo(() => {
|
||||
if (filter === 'all') {
|
||||
return defaultSubplebbits.map((subplebbit) => subplebbit.address);
|
||||
}
|
||||
if (filter === 'nsfw') {
|
||||
return defaultSubplebbits.filter((subplebbit) => subplebbit.nsfw === true).map((subplebbit) => subplebbit.address);
|
||||
}
|
||||
// filter === 'sfw'
|
||||
return defaultSubplebbits.filter((subplebbit) => subplebbit.nsfw !== true).map((subplebbit) => subplebbit.address);
|
||||
}, [defaultSubplebbits, filter]);
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
export type AllFeedFilter = 'all' | 'nsfw' | 'sfw';
|
||||
|
||||
interface AllFeedFilterStore {
|
||||
filter: AllFeedFilter;
|
||||
setFilter: (value: AllFeedFilter) => void;
|
||||
}
|
||||
|
||||
const getStoredFilter = (): AllFeedFilter => {
|
||||
try {
|
||||
const stored = localStorage.getItem('5chan-all-feed-filter') as AllFeedFilter;
|
||||
return stored === 'nsfw' || stored === 'sfw' ? stored : 'all';
|
||||
} catch {
|
||||
return 'all';
|
||||
}
|
||||
};
|
||||
|
||||
const useAllFeedFilterStore = create<AllFeedFilterStore>((set) => ({
|
||||
filter: getStoredFilter(),
|
||||
setFilter: (value: AllFeedFilter) => {
|
||||
set({ filter: value });
|
||||
try {
|
||||
localStorage.setItem('5chan-all-feed-filter', value);
|
||||
} catch (error) {
|
||||
console.warn('Failed to save allFeedFilter to localStorage:', error);
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
export default useAllFeedFilterStore;
|
||||
@@ -7,6 +7,7 @@ import styles from './board.module.css';
|
||||
import { shouldShowSnow } from '../../lib/snow';
|
||||
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
|
||||
import { useDefaultSubplebbitAddresses, useDefaultSubplebbits } from '../../hooks/use-default-subplebbits';
|
||||
import { useFilteredDefaultSubplebbitAddresses } from '../../hooks/use-filtered-default-subplebbit-addresses';
|
||||
import { useResolvedSubplebbitAddress, useBoardPath } from '../../hooks/use-resolved-subplebbit-address';
|
||||
import { useFeedStateString } from '../../hooks/use-state-string';
|
||||
import useTimeFilter, { timeFilterNameToSeconds } from '../../hooks/use-time-filter';
|
||||
@@ -60,6 +61,7 @@ const Board = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp, t
|
||||
|
||||
const boardPath = useBoardPath(subplebbitAddress);
|
||||
const defaultSubplebbitAddresses = useDefaultSubplebbitAddresses();
|
||||
const filteredDefaultSubplebbitAddresses = useFilteredDefaultSubplebbitAddresses();
|
||||
|
||||
const account = useAccount();
|
||||
const subscriptions = account?.subscriptions;
|
||||
@@ -69,7 +71,7 @@ const Board = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp, t
|
||||
|
||||
const subplebbitAddresses = useMemo(() => {
|
||||
if (isInAllView) {
|
||||
return defaultSubplebbitAddresses;
|
||||
return filteredDefaultSubplebbitAddresses;
|
||||
}
|
||||
if (isInSubscriptionsView) {
|
||||
return subscriptions || [];
|
||||
@@ -78,7 +80,16 @@ const Board = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp, t
|
||||
return accountSubplebbitAddresses;
|
||||
}
|
||||
return [subplebbitAddress];
|
||||
}, [isInAllView, isInSubscriptionsView, isInModView, subplebbitAddress, defaultSubplebbitAddresses, subscriptions, accountSubplebbitAddresses]);
|
||||
}, [
|
||||
isInAllView,
|
||||
isInSubscriptionsView,
|
||||
isInModView,
|
||||
subplebbitAddress,
|
||||
defaultSubplebbitAddresses,
|
||||
filteredDefaultSubplebbitAddresses,
|
||||
subscriptions,
|
||||
accountSubplebbitAddresses,
|
||||
]);
|
||||
|
||||
const { sortType } = useSortingStore();
|
||||
const { timeFilterSeconds: timeFilterSecondsFromHook, timeFilterName: timeFilterNameFromHook } = useTimeFilter();
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso';
|
||||
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
|
||||
import useCatalogFeedRows from '../../hooks/use-catalog-feed-rows';
|
||||
import { useDefaultSubplebbits } from '../../hooks/use-default-subplebbits';
|
||||
import { useFilteredDefaultSubplebbitAddresses } from '../../hooks/use-filtered-default-subplebbit-addresses';
|
||||
import { useResolvedSubplebbitAddress, useBoardPath } from '../../hooks/use-resolved-subplebbit-address';
|
||||
import { useFeedStateString } from '../../hooks/use-state-string';
|
||||
import useTimeFilter, { timeFilterNameToSeconds } from '../../hooks/use-time-filter';
|
||||
@@ -152,19 +153,18 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
||||
|
||||
const account = useAccount();
|
||||
const subscriptions = account?.subscriptions;
|
||||
const filteredDefaultSubplebbitAddresses = useFilteredDefaultSubplebbitAddresses();
|
||||
|
||||
const subplebbitAddresses = useMemo(() => {
|
||||
const filteredDefaultSubplebbits = defaultSubplebbits.map((subplebbit) => subplebbit.address).filter(Boolean); // Filter out any undefined/null values
|
||||
|
||||
if (isInAllView) {
|
||||
return filteredDefaultSubplebbits;
|
||||
return filteredDefaultSubplebbitAddresses;
|
||||
}
|
||||
if (isInSubscriptionsView) {
|
||||
return (subscriptions || []).filter(Boolean); // Filter out any undefined/null values
|
||||
}
|
||||
// Only include subplebbitAddress if it's defined
|
||||
return subplebbitAddress ? [subplebbitAddress] : [];
|
||||
}, [isInAllView, isInSubscriptionsView, subplebbitAddress, defaultSubplebbits, subscriptions]);
|
||||
}, [isInAllView, isInSubscriptionsView, subplebbitAddress, filteredDefaultSubplebbitAddresses, subscriptions]);
|
||||
|
||||
const { imageSize } = useCatalogStyleStore();
|
||||
const columnWidth = imageSize === 'Large' ? 270 : 180;
|
||||
|
||||
Reference in New Issue
Block a user