refactor: use filter storage state, separate profile page filter

This commit is contained in:
enes 2024-10-29 13:38:13 +01:00
parent 6e07f4b8be
commit efad0f44f5
7 changed files with 126 additions and 170 deletions

View File

@ -1,16 +1,20 @@
import { useAppSelector } from 'hooks' import { useAppSelector, useLocalStorage } from 'hooks'
import React from 'react' import React from 'react'
import { Dispatch, SetStateAction } from 'react'
import { FilterOptions, ModeratedFilter, NSFWFilter, SortBy } from 'types' import { FilterOptions, ModeratedFilter, NSFWFilter, SortBy } from 'types'
import { DEFAULT_FILTER_OPTIONS } from 'utils'
type Props = { type Props = {
filterOptions: FilterOptions author?: string | undefined
setFilterOptions: Dispatch<SetStateAction<FilterOptions>> filterKey?: string | undefined
} }
export const ModFilter = React.memo( export const ModFilter = React.memo(
({ filterOptions, setFilterOptions }: Props) => { ({ author, filterKey = 'filter' }: Props) => {
const userState = useAppSelector((state) => state.user) const userState = useAppSelector((state) => state.user)
const [filterOptions, setFilterOptions] = useLocalStorage<FilterOptions>(
filterKey,
DEFAULT_FILTER_OPTIONS
)
return ( return (
<div className='IBMSecMain'> <div className='IBMSecMain'>
@ -62,9 +66,9 @@ export const ModFilter = React.memo(
import.meta.env.VITE_REPORTING_NPUB import.meta.env.VITE_REPORTING_NPUB
const isOwnProfile = const isOwnProfile =
filterOptions.author && author &&
userState.auth && userState.auth &&
userState.user?.pubkey === filterOptions.author userState.user?.pubkey === author
if (!(isAdmin || isOwnProfile)) return null if (!(isAdmin || isOwnProfile)) return null
} }

View File

@ -18,7 +18,8 @@ export const useFilteredMods = (
muteLists: { muteLists: {
admin: MuteLists admin: MuteLists
user: MuteLists user: MuteLists
} },
author?: string | undefined
) => { ) => {
return useMemo(() => { return useMemo(() => {
const nsfwFilter = (mods: ModDetails[]) => { const nsfwFilter = (mods: ModDetails[]) => {
@ -50,7 +51,7 @@ export const useFilteredMods = (
const isAdmin = userState.user?.npub === import.meta.env.VITE_REPORTING_NPUB const isAdmin = userState.user?.npub === import.meta.env.VITE_REPORTING_NPUB
const isOwner = const isOwner =
userState.user?.npub && userState.user?.npub &&
npubToHex(userState.user.npub as string) === filterOptions.author npubToHex(userState.user.npub as string) === author
const isUnmoderatedFully = const isUnmoderatedFully =
filterOptions.moderated === ModeratedFilter.Unmoderated_Fully filterOptions.moderated === ModeratedFilter.Unmoderated_Fully
@ -84,7 +85,7 @@ export const useFilteredMods = (
filterOptions.sort, filterOptions.sort,
filterOptions.moderated, filterOptions.moderated,
filterOptions.nsfw, filterOptions.nsfw,
filterOptions.author, author,
mods, mods,
muteLists, muteLists,
nsfwList nsfwList

View File

@ -11,20 +11,20 @@ import { MAX_MODS_PER_PAGE, T_TAG_VALUE } from 'constants.ts'
import { import {
useAppSelector, useAppSelector,
useFilteredMods, useFilteredMods,
useLocalStorage,
useMuteLists, useMuteLists,
useNDKContext, useNDKContext,
useNSFWList useNSFWList
} from 'hooks' } from 'hooks'
import { useEffect, useMemo, useRef, useState } from 'react' import { useEffect, useMemo, useRef, useState } from 'react'
import { useParams, useSearchParams } from 'react-router-dom' import { useParams, useSearchParams } from 'react-router-dom'
import { FilterOptions, ModDetails } from 'types'
import { import {
FilterOptions, DEFAULT_FILTER_OPTIONS,
ModDetails, extractModData,
ModeratedFilter, isModDataComplete,
NSFWFilter, scrollIntoView
SortBy } from 'utils'
} from 'types'
import { extractModData, isModDataComplete, scrollIntoView } from 'utils'
export const GamePage = () => { export const GamePage = () => {
const scrollTargetRef = useRef<HTMLDivElement>(null) const scrollTargetRef = useRef<HTMLDivElement>(null)
@ -34,12 +34,10 @@ export const GamePage = () => {
const muteLists = useMuteLists() const muteLists = useMuteLists()
const nsfwList = useNSFWList() const nsfwList = useNSFWList()
const [filterOptions, setFilterOptions] = useState<FilterOptions>({ const [filterOptions] = useLocalStorage<FilterOptions>(
sort: SortBy.Latest, 'filter',
nsfw: NSFWFilter.Hide_NSFW, DEFAULT_FILTER_OPTIONS
source: window.location.host, )
moderated: ModeratedFilter.Moderated
})
const [mods, setMods] = useState<ModDetails[]>([]) const [mods, setMods] = useState<ModDetails[]>([])
const [currentPage, setCurrentPage] = useState(1) const [currentPage, setCurrentPage] = useState(1)
@ -186,10 +184,7 @@ export const GamePage = () => {
/> />
</div> </div>
</div> </div>
<ModFilter <ModFilter />
filterOptions={filterOptions}
setFilterOptions={setFilterOptions}
/>
<div className='IBMSecMain IBMSMListWrapper'> <div className='IBMSecMain IBMSMListWrapper'>
<div className='IBMSMList'> <div className='IBMSMList'>
{currentMods.map((mod) => ( {currentMods.map((mod) => (

View File

@ -8,6 +8,7 @@ import { MOD_FILTER_LIMIT } from '../constants'
import { import {
useAppSelector, useAppSelector,
useFilteredMods, useFilteredMods,
useLocalStorage,
useMuteLists, useMuteLists,
useNDKContext, useNDKContext,
useNSFWList useNSFWList
@ -17,14 +18,8 @@ import '../styles/filters.css'
import '../styles/pagination.css' import '../styles/pagination.css'
import '../styles/search.css' import '../styles/search.css'
import '../styles/styles.css' import '../styles/styles.css'
import { import { FilterOptions, ModDetails } from '../types'
FilterOptions, import { DEFAULT_FILTER_OPTIONS, scrollIntoView } from 'utils'
ModDetails,
ModeratedFilter,
NSFWFilter,
SortBy
} from '../types'
import { scrollIntoView } from 'utils'
import { SearchInput } from 'components/SearchInput' import { SearchInput } from 'components/SearchInput'
export const ModsPage = () => { export const ModsPage = () => {
@ -32,12 +27,11 @@ export const ModsPage = () => {
const { fetchMods } = useNDKContext() const { fetchMods } = useNDKContext()
const [isFetching, setIsFetching] = useState(false) const [isFetching, setIsFetching] = useState(false)
const [mods, setMods] = useState<ModDetails[]>([]) const [mods, setMods] = useState<ModDetails[]>([])
const [filterOptions, setFilterOptions] = useState<FilterOptions>({
sort: SortBy.Latest, const [filterOptions] = useLocalStorage<FilterOptions>(
nsfw: NSFWFilter.Hide_NSFW, 'filter',
source: window.location.host, DEFAULT_FILTER_OPTIONS
moderated: ModeratedFilter.Moderated )
})
const muteLists = useMuteLists() const muteLists = useMuteLists()
const nsfwList = useNSFWList() const nsfwList = useNSFWList()
@ -113,10 +107,7 @@ export const ModsPage = () => {
ref={scrollTargetRef} ref={scrollTargetRef}
> >
<PageTitleRow /> <PageTitleRow />
<ModFilter <ModFilter />
filterOptions={filterOptions}
setFilterOptions={setFilterOptions}
/>
<div className='IBMSecMain IBMSMListWrapper'> <div className='IBMSecMain IBMSMListWrapper'>
<div className='IBMSMList'> <div className='IBMSMList'>

View File

@ -9,6 +9,7 @@ import { MOD_FILTER_LIMIT } from '../constants'
import { import {
useAppSelector, useAppSelector,
useFilteredMods, useFilteredMods,
useLocalStorage,
useMuteLists, useMuteLists,
useNDKContext, useNDKContext,
useNSFWList useNSFWList
@ -18,16 +19,10 @@ import { useCallback, useEffect, useRef, useState } from 'react'
import { useParams, Navigate, Link } from 'react-router-dom' import { useParams, Navigate, Link } from 'react-router-dom'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { appRoutes, getProfilePageRoute } from 'routes' import { appRoutes, getProfilePageRoute } from 'routes'
import { import { FilterOptions, ModDetails, UserRelaysType } from 'types'
FilterOptions,
ModDetails,
ModeratedFilter,
NSFWFilter,
SortBy,
UserRelaysType
} from 'types'
import { import {
copyTextToClipboard, copyTextToClipboard,
DEFAULT_FILTER_OPTIONS,
now, now,
npubToHex, npubToHex,
scrollIntoView, scrollIntoView,
@ -230,12 +225,9 @@ export const ProfilePage = () => {
// Mods // Mods
const [mods, setMods] = useState<ModDetails[]>([]) const [mods, setMods] = useState<ModDetails[]>([])
const [filterOptions, setFilterOptions] = useState<FilterOptions>({ const filterKey = 'filter-profile'
sort: SortBy.Latest, const [filterOptions] = useLocalStorage<FilterOptions>(filterKey, {
nsfw: NSFWFilter.Hide_NSFW, ...DEFAULT_FILTER_OPTIONS
source: window.location.host,
moderated: ModeratedFilter.Moderated,
author: profilePubkey
}) })
const muteLists = useMuteLists() const muteLists = useMuteLists()
const nsfwList = useNSFWList() const nsfwList = useNSFWList()
@ -304,7 +296,8 @@ export const ProfilePage = () => {
userState, userState,
filterOptions, filterOptions,
nsfwList, nsfwList,
muteLists muteLists,
profilePubkey
) )
// Redirect route // Redirect route
@ -470,10 +463,7 @@ export const ProfilePage = () => {
{/* Tabs Content */} {/* Tabs Content */}
{tab === 0 && ( {tab === 0 && (
<> <>
<ModFilter <ModFilter filterKey={filterKey} author={profilePubkey} />
filterOptions={filterOptions}
setFilterOptions={setFilterOptions}
/>
<div className='IBMSMList IBMSMListAlt'> <div className='IBMSMList IBMSMListAlt'>
{filteredModList.map((mod) => ( {filteredModList.map((mod) => (

View File

@ -23,28 +23,16 @@ import {
useAppSelector, useAppSelector,
useFilteredMods, useFilteredMods,
useGames, useGames,
useLocalStorage,
useMuteLists, useMuteLists,
useNDKContext, useNDKContext,
useNSFWList useNSFWList
} from 'hooks' } from 'hooks'
import React, { import React, { useEffect, useMemo, useRef, useState } from 'react'
Dispatch,
SetStateAction,
useEffect,
useMemo,
useRef,
useState
} from 'react'
import { useSearchParams } from 'react-router-dom' import { useSearchParams } from 'react-router-dom'
import { FilterOptions, ModDetails, ModeratedFilter, MuteLists } from 'types'
import { import {
FilterOptions, DEFAULT_FILTER_OPTIONS,
ModDetails,
ModeratedFilter,
MuteLists,
NSFWFilter,
SortBy
} from 'types'
import {
extractModData, extractModData,
isModDataComplete, isModDataComplete,
log, log,
@ -69,12 +57,10 @@ export const SearchPage = () => {
const searchKind = const searchKind =
(searchParams.get('kind') as SearchKindEnum) || SearchKindEnum.Mods (searchParams.get('kind') as SearchKindEnum) || SearchKindEnum.Mods
const [filterOptions, setFilterOptions] = useState<FilterOptions>({ const [filterOptions] = useLocalStorage<FilterOptions>(
sort: SortBy.Latest, 'filter',
nsfw: NSFWFilter.Hide_NSFW, DEFAULT_FILTER_OPTIONS
source: window.location.host, )
moderated: ModeratedFilter.Moderated
})
const [searchTerm, setSearchTerm] = useState(searchParams.get('q') || '') const [searchTerm, setSearchTerm] = useState(searchParams.get('q') || '')
@ -124,10 +110,7 @@ export const SearchPage = () => {
/> />
</div> </div>
</div> </div>
<Filters <Filters />
filterOptions={filterOptions}
setFilterOptions={setFilterOptions}
/>
{searchKind === SearchKindEnum.Mods && ( {searchKind === SearchKindEnum.Mods && (
<ModsResult <ModsResult
searchTerm={searchTerm} searchTerm={searchTerm}
@ -153,75 +136,29 @@ export const SearchPage = () => {
) )
} }
type FiltersProps = { const Filters = React.memo(() => {
filterOptions: FilterOptions const [filterOptions, setFilterOptions] = useLocalStorage<FilterOptions>(
setFilterOptions: Dispatch<SetStateAction<FilterOptions>> 'filter',
} DEFAULT_FILTER_OPTIONS
)
const Filters = React.memo( const userState = useAppSelector((state) => state.user)
({ filterOptions, setFilterOptions }: FiltersProps) => { const [searchParams, setSearchParams] = useSearchParams()
const userState = useAppSelector((state) => state.user) const searchKind =
const [searchParams, setSearchParams] = useSearchParams() (searchParams.get('kind') as SearchKindEnum) || SearchKindEnum.Mods
const searchKind = const handleChangeSearchKind = (kind: SearchKindEnum) => {
(searchParams.get('kind') as SearchKindEnum) || SearchKindEnum.Mods searchParams.set('kind', kind)
const handleChangeSearchKind = (kind: SearchKindEnum) => { setSearchParams(searchParams, {
searchParams.set('kind', kind) replace: true
setSearchParams(searchParams, { })
replace: true }
})
}
return ( return (
<div className='IBMSecMain'> <div className='IBMSecMain'>
<div className='FiltersMain'> <div className='FiltersMain'>
{searchKind === SearchKindEnum.Mods && ( {searchKind === SearchKindEnum.Mods && <ModFilter />}
<ModFilter
filterOptions={filterOptions}
setFilterOptions={setFilterOptions}
/>
)}
{searchKind === SearchKindEnum.Users && (
<div className='FiltersMainElement'>
<div className='dropdown dropdownMain'>
<button
className='btn dropdown-toggle btnMain btnMainDropdown'
aria-expanded='false'
data-bs-toggle='dropdown'
type='button'
>
{filterOptions.moderated}
</button>
<div className='dropdown-menu dropdownMainMenu'>
{Object.values(ModeratedFilter).map((item, index) => {
if (item === ModeratedFilter.Unmoderated_Fully) {
const isAdmin =
userState.user?.npub ===
import.meta.env.VITE_REPORTING_NPUB
if (!isAdmin) return null
}
return (
<div
key={`moderatedFilterItem-${index}`}
className='dropdown-item dropdownMainMenuItem'
onClick={() =>
setFilterOptions((prev) => ({
...prev,
moderated: item
}))
}
>
{item}
</div>
)
})}
</div>
</div>
</div>
)}
{searchKind === SearchKindEnum.Users && (
<div className='FiltersMainElement'> <div className='FiltersMainElement'>
<div className='dropdown dropdownMain'> <div className='dropdown dropdownMain'>
<button <button
@ -230,26 +167,65 @@ const Filters = React.memo(
data-bs-toggle='dropdown' data-bs-toggle='dropdown'
type='button' type='button'
> >
Searching: {searchKind} {filterOptions.moderated}
</button> </button>
<div className='dropdown-menu dropdownMainMenu'> <div className='dropdown-menu dropdownMainMenu'>
{Object.values(SearchKindEnum).map((item, index) => ( {Object.values(ModeratedFilter).map((item, index) => {
<div if (item === ModeratedFilter.Unmoderated_Fully) {
key={`searchingFilterItem-${index}`} const isAdmin =
className='dropdown-item dropdownMainMenuItem' userState.user?.npub ===
onClick={() => handleChangeSearchKind(item)} import.meta.env.VITE_REPORTING_NPUB
>
{item} if (!isAdmin) return null
</div> }
))}
return (
<div
key={`moderatedFilterItem-${index}`}
className='dropdown-item dropdownMainMenuItem'
onClick={() =>
setFilterOptions((prev) => ({
...prev,
moderated: item
}))
}
>
{item}
</div>
)
})}
</div> </div>
</div> </div>
</div> </div>
)}
<div className='FiltersMainElement'>
<div className='dropdown dropdownMain'>
<button
className='btn dropdown-toggle btnMain btnMainDropdown'
aria-expanded='false'
data-bs-toggle='dropdown'
type='button'
>
Searching: {searchKind}
</button>
<div className='dropdown-menu dropdownMainMenu'>
{Object.values(SearchKindEnum).map((item, index) => (
<div
key={`searchingFilterItem-${index}`}
className='dropdown-item dropdownMainMenuItem'
onClick={() => handleChangeSearchKind(item)}
>
{item}
</div>
))}
</div>
</div>
</div> </div>
</div> </div>
) </div>
} )
) })
type ModsResultProps = { type ModsResultProps = {
filterOptions: FilterOptions filterOptions: FilterOptions

View File

@ -22,5 +22,4 @@ export interface FilterOptions {
nsfw: NSFWFilter nsfw: NSFWFilter
source: string source: string
moderated: ModeratedFilter moderated: ModeratedFilter
author?: string
} }