From aa63ab0f1b69e0e4a6f7a2d685dd1ade9f53586d Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Sat, 15 Jun 2024 16:42:30 +0200 Subject: [PATCH] feat(catalog): add filters modal for text-only posts, nsfw boards in p/all --- .../board-buttons/board-buttons.tsx | 21 +--- .../settings-modal/settings-modal.module.css | 4 +- .../settings-modal/settings-modal.tsx | 2 +- src/stores/use-catalog-filters-store.ts | 14 +++ .../catalog-filters.module.css | 99 +++++++++++++++++++ .../catalog-filters/catalog-filters.tsx | 67 +++++++++++++ src/views/catalog/catalog-filters/index.ts | 1 + src/views/catalog/catalog.tsx | 26 ++++- 8 files changed, 210 insertions(+), 24 deletions(-) create mode 100644 src/views/catalog/catalog-filters/catalog-filters.module.css create mode 100644 src/views/catalog/catalog-filters/catalog-filters.tsx create mode 100644 src/views/catalog/catalog-filters/index.ts diff --git a/src/components/board-buttons/board-buttons.tsx b/src/components/board-buttons/board-buttons.tsx index 4d481e00..b2f6c5e1 100644 --- a/src/components/board-buttons/board-buttons.tsx +++ b/src/components/board-buttons/board-buttons.tsx @@ -2,9 +2,10 @@ import { useTranslation } from 'react-i18next'; import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'; import { useAccountComment, useSubscribe } from '@plebbit/plebbit-react-hooks'; import { isAllView, isCatalogView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils'; -import useCatalogFiltersStore from '../../stores/use-catalog-filters-store'; +import useSortingStore from '../../stores/use-sorting-store'; import useFeedResetStore from '../../stores/use-feed-reset-store'; import useTimeFilter from '../../hooks/use-time-filter'; +import CatalogFilters from '../../views/catalog/catalog-filters/'; import styles from './board-buttons.module.css'; interface BoardButtonsProps { @@ -82,7 +83,7 @@ const RefreshButton = () => { const SortOptions = () => { const { t } = useTranslation(); - const { sortType, setSortType } = useCatalogFiltersStore(); + const { sortType, setSortType } = useSortingStore(); const handleSortChange = (event: React.ChangeEvent) => { const type = event.target.value as 'active' | 'new'; @@ -183,20 +184,6 @@ export const MobileBoardButtons = () => { ); }; -const TextOnlyPostsFilter = () => { - const { showTextOnlyThreads, setShowTextOnlyThreads } = useCatalogFiltersStore(); - - return ( -
- Text-Only Posts:{' '} - -
- ); -}; - export const DesktopBoardButtons = () => { const params = useParams(); const location = useLocation(); @@ -244,7 +231,7 @@ export const DesktopBoardButtons = () => { {(isInAllView || isInSubscriptionsView) && ( )} - {isInCatalogView && } + {(isInCatalogView || isInAllView || isInSubscriptionsView) && }{' '} {!(isInAllView || isInSubscriptionsView) && ( <> [ diff --git a/src/components/settings-modal/settings-modal.module.css b/src/components/settings-modal/settings-modal.module.css index abbe3cc0..a0461b47 100644 --- a/src/components/settings-modal/settings-modal.module.css +++ b/src/components/settings-modal/settings-modal.module.css @@ -24,12 +24,12 @@ .settingsModal { top: 25px; + left: 50%; + transform: translateX(-50%); width: 394px; height: auto; max-height: 80%; - left: calc(50% - 22px); overflow-y: auto; - margin-left: -178px; position: fixed; z-index: 5; padding: 2px; diff --git a/src/components/settings-modal/settings-modal.tsx b/src/components/settings-modal/settings-modal.tsx index bf07379c..ff5317a7 100644 --- a/src/components/settings-modal/settings-modal.tsx +++ b/src/components/settings-modal/settings-modal.tsx @@ -35,7 +35,7 @@ const SettingsModal = () => { return ( <> -
+
{t('settings')} diff --git a/src/stores/use-catalog-filters-store.ts b/src/stores/use-catalog-filters-store.ts index 4e1885fa..92c5a718 100644 --- a/src/stores/use-catalog-filters-store.ts +++ b/src/stores/use-catalog-filters-store.ts @@ -1,6 +1,10 @@ import { create } from 'zustand'; interface CatalogFiltersStore { + showAdultBoards: boolean; + setShowAdultBoards: (value: boolean) => void; + showGoreBoards: boolean; + setShowGoreBoards: (value: boolean) => void; showTextOnlyThreads: boolean; setShowTextOnlyThreads: (value: boolean) => void; } @@ -11,6 +15,16 @@ const useCatalogFiltersStore = create((set) => ({ set({ showTextOnlyThreads: value }); localStorage.setItem('showTextOnlyThreads', value.toString()); }, + showAdultBoards: localStorage.getItem('showAdultBoards') === 'true' ? true : false, + setShowAdultBoards: (value: boolean) => { + set({ showAdultBoards: value }); + localStorage.setItem('showAdultBoards', value.toString()); + }, + showGoreBoards: localStorage.getItem('showGoreBoards') === 'true' ? true : false, + setShowGoreBoards: (value: boolean) => { + set({ showGoreBoards: value }); + localStorage.setItem('showGoreBoards', value.toString()); + }, })); export default useCatalogFiltersStore; diff --git a/src/views/catalog/catalog-filters/catalog-filters.module.css b/src/views/catalog/catalog-filters/catalog-filters.module.css new file mode 100644 index 00000000..d66a128e --- /dev/null +++ b/src/views/catalog/catalog-filters/catalog-filters.module.css @@ -0,0 +1,99 @@ +.overlay { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(0, 0, 0, 0.25); + z-index: 4; +} + +.button { + text-transform: capitalize; + color: var(--button-desktop-text-color); + text-decoration: var(--button-text-decoration); +} + +.button:hover { + color: var(--button-desktop-text-color-hover); + cursor: pointer; +} + +.modal label, .modal button, .modal select { + cursor: pointer; +} + +.modal button { + text-transform: capitalize; +} + +.modal { + top: 60px; + left: 50%; + transform: translateX(-50%); + width: 340px; + height: auto; + max-height: 80%; + overflow-y: auto; + position: fixed; + z-index: 5; + padding: 2px; + font-size: var(--settings-modal-font-size); + box-shadow: 0 0 5px rgba(0, 0, 0, 0.25); + background-color: var(--settings-modal-background-color); + border-top: var(--settings-modal-border-top); + border-right: var(--settings-modal-border-right); + border-bottom: var(--settings-modal-border-bottom); + border-left: var(--settings-modal-border-left); +} + + +.header { + font-size: 16px; + font-weight: 700; + margin-bottom: 5px; + margin-top: 5px; + padding-bottom: 5px; + text-align: center; + line-height: 14px; + border-bottom: var(--settings-modal-header-border-bottom); +} + +.title { + text-transform: capitalize; +} + +.closeButton { + right: 5px; + width: 18px; + height: 18px; + display: block; + background-size: 100%; + position: absolute; + top: 5px; + image-rendering: pixelated; + background-image: var(--settings-modal-close-button-background-image); +} + +.filters { + padding: 5px; +} + +.filters label { + display: block; + margin-bottom: 7px; +} + +.filters label input[type='checkbox'] { + margin-right: 5px; +} + +.categoryTitle { + font-size: 14px; + font-weight: 700; + padding-bottom: 5px; +} + +.paddingBottom { + padding-bottom: 5px; +} \ No newline at end of file diff --git a/src/views/catalog/catalog-filters/catalog-filters.tsx b/src/views/catalog/catalog-filters/catalog-filters.tsx new file mode 100644 index 00000000..d1129291 --- /dev/null +++ b/src/views/catalog/catalog-filters/catalog-filters.tsx @@ -0,0 +1,67 @@ +import { useState } from 'react'; +import { useLocation, useParams } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; +import { isAllView, isCatalogView } from '../../../lib/utils/view-utils'; +import useCatalogFiltersStore from '../../../stores/use-catalog-filters-store'; +import styles from './catalog-filters.module.css'; + +const FiltersModal = ({ closeModal }: { closeModal: () => void }) => { + const { t } = useTranslation(); + const { showAdultBoards, setShowAdultBoards, showGoreBoards, setShowGoreBoards, showTextOnlyThreads, setShowTextOnlyThreads } = useCatalogFiltersStore(); + const location = useLocation(); + const isInCatalogView = isCatalogView(location.pathname, useParams()); + const isInAllView = isAllView(location.pathname); + + return ( + <> +
+
+
+ {t('filters')} + +
+
+ {isInCatalogView && ( + + )} + {isInAllView && ( +
+
NSFW Boards
+ + +
+ )} +
+
+ + ); +}; + +const CatalogFilters = () => { + const [showModal, setShowModal] = useState(false); + + const closeModal = () => { + setShowModal(false); + }; + + return ( + <> + [ + setShowModal(true)}> + Filters + + ]{showModal && } + + ); +}; + +export default CatalogFilters; diff --git a/src/views/catalog/catalog-filters/index.ts b/src/views/catalog/catalog-filters/index.ts new file mode 100644 index 00000000..14706590 --- /dev/null +++ b/src/views/catalog/catalog-filters/index.ts @@ -0,0 +1 @@ +export { default } from './catalog-filters'; diff --git a/src/views/catalog/catalog.tsx b/src/views/catalog/catalog.tsx index 93c270cc..11da68b4 100644 --- a/src/views/catalog/catalog.tsx +++ b/src/views/catalog/catalog.tsx @@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'; import { useAccount, Subplebbit, useFeed, useSubplebbit, Comment } from '@plebbit/plebbit-react-hooks'; import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso'; import { isAllView, isSubscriptionsView } from '../../lib/utils/view-utils'; -import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits'; +import useDefaultSubplebbits from '../../hooks/use-default-subplebbits'; import useFeedStateString from '../../hooks/use-feed-state-string'; import { useMultisubMetadata } from '../../hooks/use-default-subplebbits'; import useTimeFilter from '../../hooks/use-time-filter'; @@ -91,21 +91,39 @@ const Catalog = () => { const { subplebbitAddress } = useParams<{ subplebbitAddress: string }>(); const isInAllView = isAllView(location.pathname); - const defaultSubplebbitAddresses = useDefaultSubplebbitAddresses(); + const defaultSubplebbits = useDefaultSubplebbits(); + const { showAdultBoards, showGoreBoards } = useCatalogFiltersStore(); const account = useAccount(); const subscriptions = account?.subscriptions; const isInSubscriptionsView = isSubscriptionsView(location.pathname); const subplebbitAddresses = useMemo(() => { + const filteredDefaultSubplebbits = defaultSubplebbits + .filter((subplebbit) => { + const { tags } = subplebbit; + const hasGoreTag = tags?.includes('gore'); + const hasAdultTag = tags?.includes('adult'); + + if ((hasGoreTag && !showGoreBoards) || (hasAdultTag && !showAdultBoards)) { + return false; + } + return true; + }) + .map((subplebbit) => subplebbit.address); + if (isInAllView) { - return defaultSubplebbitAddresses; + return filteredDefaultSubplebbits; } if (isInSubscriptionsView) { return subscriptions || []; } return [subplebbitAddress]; - }, [isInAllView, isInSubscriptionsView, subplebbitAddress, defaultSubplebbitAddresses, subscriptions]); + }, [isInAllView, isInSubscriptionsView, subplebbitAddress, defaultSubplebbits, subscriptions, showAdultBoards, showGoreBoards]); + + useEffect(() => { + console.log(showAdultBoards, showGoreBoards); + }, [showAdultBoards, showGoreBoards]); const columnCount = Math.floor(useWindowWidth() / columnWidth); // postPerPage based on columnCount for optimized feed, dont change value after first render