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 useCatalogStyleStore from '../../stores/use-catalog-style-store';
|
||||||
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
||||||
import useSortingStore from '../../stores/use-sorting-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 useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
|
||||||
import useIsMobile from '../../hooks/use-is-mobile';
|
import useIsMobile from '../../hooks/use-is-mobile';
|
||||||
import useTimeFilter from '../../hooks/use-time-filter';
|
import useTimeFilter from '../../hooks/use-time-filter';
|
||||||
@@ -247,14 +248,14 @@ export const TimeFilter = ({ isInAllView, isInCatalogView, isInSubscriptionsView
|
|||||||
? `/all/catalog/${timeFilterName}`
|
? `/all/catalog/${timeFilterName}`
|
||||||
: `/all/${timeFilterName}`
|
: `/all/${timeFilterName}`
|
||||||
: isInSubscriptionsView
|
: isInSubscriptionsView
|
||||||
? isInCatalogView
|
? isInCatalogView
|
||||||
? `/subs/catalog/${timeFilterName}`
|
? `/subs/catalog/${timeFilterName}`
|
||||||
: `/subs/${timeFilterName}`
|
: `/subs/${timeFilterName}`
|
||||||
: isInModView
|
: isInModView
|
||||||
? isInCatalogView
|
? isInCatalogView
|
||||||
? `/mod/catalog/${timeFilterName}`
|
? `/mod/catalog/${timeFilterName}`
|
||||||
: `/mod/${timeFilterName}`
|
: `/mod/${timeFilterName}`
|
||||||
: null;
|
: null;
|
||||||
link && navigate(link);
|
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 = () => {
|
export const MobileBoardButtons = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
@@ -341,6 +358,14 @@ export const MobileBoardButtons = () => {
|
|||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
|
{isInAllView && (
|
||||||
|
<>
|
||||||
|
<hr />
|
||||||
|
<div className={styles.options}>
|
||||||
|
<AllFeedFilter />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
{isInCatalogView && (
|
{isInCatalogView && (
|
||||||
<>
|
<>
|
||||||
<hr />
|
<hr />
|
||||||
@@ -409,11 +434,9 @@ export const DesktopBoardButtons = () => {
|
|||||||
{isInPostView || isInPendingPostPage ? (
|
{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} />] [
|
<CatalogButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />] [
|
||||||
<UpdateButton />
|
<UpdateButton />] [
|
||||||
] [
|
|
||||||
<AutoButton />]
|
<AutoButton />]
|
||||||
<span className={styles.rightSideButtons}>
|
<span className={styles.rightSideButtons}>
|
||||||
<PostPageStats />
|
<PostPageStats />
|
||||||
@@ -462,6 +485,7 @@ export const DesktopBoardButtons = () => {
|
|||||||
<ShowOPCommentOption />
|
<ShowOPCommentOption />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{isInAllView && <AllFeedFilter />}
|
||||||
{(isInAllView || isInSubscriptionsView || isInModView) && (
|
{(isInAllView || isInSubscriptionsView || isInModView) && (
|
||||||
<TimeFilter isInAllView={isInAllView} isInCatalogView={isInCatalogView} isInSubscriptionsView={isInSubscriptionsView} isInModView={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 { shouldShowSnow } from '../../lib/snow';
|
||||||
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
|
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
|
||||||
import { useDefaultSubplebbitAddresses, useDefaultSubplebbits } from '../../hooks/use-default-subplebbits';
|
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 { useResolvedSubplebbitAddress, useBoardPath } from '../../hooks/use-resolved-subplebbit-address';
|
||||||
import { useFeedStateString } from '../../hooks/use-state-string';
|
import { useFeedStateString } from '../../hooks/use-state-string';
|
||||||
import useTimeFilter, { timeFilterNameToSeconds } from '../../hooks/use-time-filter';
|
import useTimeFilter, { timeFilterNameToSeconds } from '../../hooks/use-time-filter';
|
||||||
@@ -60,6 +61,7 @@ const Board = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp, t
|
|||||||
|
|
||||||
const boardPath = useBoardPath(subplebbitAddress);
|
const boardPath = useBoardPath(subplebbitAddress);
|
||||||
const defaultSubplebbitAddresses = useDefaultSubplebbitAddresses();
|
const defaultSubplebbitAddresses = useDefaultSubplebbitAddresses();
|
||||||
|
const filteredDefaultSubplebbitAddresses = useFilteredDefaultSubplebbitAddresses();
|
||||||
|
|
||||||
const account = useAccount();
|
const account = useAccount();
|
||||||
const subscriptions = account?.subscriptions;
|
const subscriptions = account?.subscriptions;
|
||||||
@@ -69,7 +71,7 @@ const Board = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp, t
|
|||||||
|
|
||||||
const subplebbitAddresses = useMemo(() => {
|
const subplebbitAddresses = useMemo(() => {
|
||||||
if (isInAllView) {
|
if (isInAllView) {
|
||||||
return defaultSubplebbitAddresses;
|
return filteredDefaultSubplebbitAddresses;
|
||||||
}
|
}
|
||||||
if (isInSubscriptionsView) {
|
if (isInSubscriptionsView) {
|
||||||
return subscriptions || [];
|
return subscriptions || [];
|
||||||
@@ -78,7 +80,16 @@ const Board = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp, t
|
|||||||
return accountSubplebbitAddresses;
|
return accountSubplebbitAddresses;
|
||||||
}
|
}
|
||||||
return [subplebbitAddress];
|
return [subplebbitAddress];
|
||||||
}, [isInAllView, isInSubscriptionsView, isInModView, subplebbitAddress, defaultSubplebbitAddresses, subscriptions, accountSubplebbitAddresses]);
|
}, [
|
||||||
|
isInAllView,
|
||||||
|
isInSubscriptionsView,
|
||||||
|
isInModView,
|
||||||
|
subplebbitAddress,
|
||||||
|
defaultSubplebbitAddresses,
|
||||||
|
filteredDefaultSubplebbitAddresses,
|
||||||
|
subscriptions,
|
||||||
|
accountSubplebbitAddresses,
|
||||||
|
]);
|
||||||
|
|
||||||
const { sortType } = useSortingStore();
|
const { sortType } = useSortingStore();
|
||||||
const { timeFilterSeconds: timeFilterSecondsFromHook, timeFilterName: timeFilterNameFromHook } = useTimeFilter();
|
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 { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
|
||||||
import useCatalogFeedRows from '../../hooks/use-catalog-feed-rows';
|
import useCatalogFeedRows from '../../hooks/use-catalog-feed-rows';
|
||||||
import { useDefaultSubplebbits } from '../../hooks/use-default-subplebbits';
|
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 { useResolvedSubplebbitAddress, useBoardPath } from '../../hooks/use-resolved-subplebbit-address';
|
||||||
import { useFeedStateString } from '../../hooks/use-state-string';
|
import { useFeedStateString } from '../../hooks/use-state-string';
|
||||||
import useTimeFilter, { timeFilterNameToSeconds } from '../../hooks/use-time-filter';
|
import useTimeFilter, { timeFilterNameToSeconds } from '../../hooks/use-time-filter';
|
||||||
@@ -152,19 +153,18 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
|||||||
|
|
||||||
const account = useAccount();
|
const account = useAccount();
|
||||||
const subscriptions = account?.subscriptions;
|
const subscriptions = account?.subscriptions;
|
||||||
|
const filteredDefaultSubplebbitAddresses = useFilteredDefaultSubplebbitAddresses();
|
||||||
|
|
||||||
const subplebbitAddresses = useMemo(() => {
|
const subplebbitAddresses = useMemo(() => {
|
||||||
const filteredDefaultSubplebbits = defaultSubplebbits.map((subplebbit) => subplebbit.address).filter(Boolean); // Filter out any undefined/null values
|
|
||||||
|
|
||||||
if (isInAllView) {
|
if (isInAllView) {
|
||||||
return filteredDefaultSubplebbits;
|
return filteredDefaultSubplebbitAddresses;
|
||||||
}
|
}
|
||||||
if (isInSubscriptionsView) {
|
if (isInSubscriptionsView) {
|
||||||
return (subscriptions || []).filter(Boolean); // Filter out any undefined/null values
|
return (subscriptions || []).filter(Boolean); // Filter out any undefined/null values
|
||||||
}
|
}
|
||||||
// Only include subplebbitAddress if it's defined
|
// Only include subplebbitAddress if it's defined
|
||||||
return subplebbitAddress ? [subplebbitAddress] : [];
|
return subplebbitAddress ? [subplebbitAddress] : [];
|
||||||
}, [isInAllView, isInSubscriptionsView, subplebbitAddress, defaultSubplebbits, subscriptions]);
|
}, [isInAllView, isInSubscriptionsView, subplebbitAddress, filteredDefaultSubplebbitAddresses, subscriptions]);
|
||||||
|
|
||||||
const { imageSize } = useCatalogStyleStore();
|
const { imageSize } = useCatalogStyleStore();
|
||||||
const columnWidth = imageSize === 'Large' ? 270 : 180;
|
const columnWidth = imageSize === 'Large' ? 270 : 180;
|
||||||
|
|||||||
Reference in New Issue
Block a user