diff --git a/src/components/board-buttons/board-buttons.tsx b/src/components/board-buttons/board-buttons.tsx index 4068ac30..3a6c53fc 100644 --- a/src/components/board-buttons/board-buttons.tsx +++ b/src/components/board-buttons/board-buttons.tsx @@ -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 ( + <> + {t('show')}:  + + + ); +}; + export const MobileBoardButtons = () => { const { t } = useTranslation(); const params = useParams(); @@ -341,6 +358,14 @@ export const MobileBoardButtons = () => { ) )} + {isInAllView && ( + <> +
+
+ +
+ + )} {isInCatalogView && ( <>
@@ -409,11 +434,9 @@ export const DesktopBoardButtons = () => { {isInPostView || isInPendingPostPage ? ( <> [ - - ] [ + ] [ ] [ - - ] [ + ] [ ] @@ -462,6 +485,7 @@ export const DesktopBoardButtons = () => { )} + {isInAllView && } {(isInAllView || isInSubscriptionsView || isInModView) && ( )} diff --git a/src/hooks/use-filtered-default-subplebbit-addresses.ts b/src/hooks/use-filtered-default-subplebbit-addresses.ts new file mode 100644 index 00000000..54fe5c79 --- /dev/null +++ b/src/hooks/use-filtered-default-subplebbit-addresses.ts @@ -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]); +}; diff --git a/src/stores/use-all-feed-filter-store.ts b/src/stores/use-all-feed-filter-store.ts new file mode 100644 index 00000000..6385b38e --- /dev/null +++ b/src/stores/use-all-feed-filter-store.ts @@ -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((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; diff --git a/src/views/board/board.tsx b/src/views/board/board.tsx index 6c34ac54..dd924127 100644 --- a/src/views/board/board.tsx +++ b/src/views/board/board.tsx @@ -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(); diff --git a/src/views/catalog/catalog.tsx b/src/views/catalog/catalog.tsx index 42de23d4..a4612ff0 100644 --- a/src/views/catalog/catalog.tsx +++ b/src/views/catalog/catalog.tsx @@ -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;