feat: game page search, cached filters #105
@ -1,16 +1,20 @@
|
||||
import { useAppSelector } from 'hooks'
|
||||
import { useAppSelector, useLocalStorage } from 'hooks'
|
||||
import React from 'react'
|
||||
import { Dispatch, SetStateAction } from 'react'
|
||||
import { FilterOptions, ModeratedFilter, NSFWFilter, SortBy } from 'types'
|
||||
import { DEFAULT_FILTER_OPTIONS } from 'utils'
|
||||
|
||||
type Props = {
|
||||
filterOptions: FilterOptions
|
||||
setFilterOptions: Dispatch<SetStateAction<FilterOptions>>
|
||||
author?: string | undefined
|
||||
filterKey?: string | undefined
|
||||
}
|
||||
|
||||
export const ModFilter = React.memo(
|
||||
({ filterOptions, setFilterOptions }: Props) => {
|
||||
({ author, filterKey = 'filter' }: Props) => {
|
||||
const userState = useAppSelector((state) => state.user)
|
||||
const [filterOptions, setFilterOptions] = useLocalStorage<FilterOptions>(
|
||||
filterKey,
|
||||
DEFAULT_FILTER_OPTIONS
|
||||
)
|
||||
|
||||
return (
|
||||
<div className='IBMSecMain'>
|
||||
@ -62,9 +66,9 @@ export const ModFilter = React.memo(
|
||||
import.meta.env.VITE_REPORTING_NPUB
|
||||
|
||||
const isOwnProfile =
|
||||
filterOptions.author &&
|
||||
author &&
|
||||
userState.auth &&
|
||||
userState.user?.pubkey === filterOptions.author
|
||||
userState.user?.pubkey === author
|
||||
|
||||
if (!(isAdmin || isOwnProfile)) return null
|
||||
}
|
||||
|
@ -18,7 +18,8 @@ export const useFilteredMods = (
|
||||
muteLists: {
|
||||
admin: MuteLists
|
||||
user: MuteLists
|
||||
}
|
||||
},
|
||||
author?: string | undefined
|
||||
) => {
|
||||
return useMemo(() => {
|
||||
const nsfwFilter = (mods: ModDetails[]) => {
|
||||
@ -50,7 +51,7 @@ export const useFilteredMods = (
|
||||
const isAdmin = userState.user?.npub === import.meta.env.VITE_REPORTING_NPUB
|
||||
const isOwner =
|
||||
userState.user?.npub &&
|
||||
npubToHex(userState.user.npub as string) === filterOptions.author
|
||||
npubToHex(userState.user.npub as string) === author
|
||||
const isUnmoderatedFully =
|
||||
filterOptions.moderated === ModeratedFilter.Unmoderated_Fully
|
||||
|
||||
@ -84,7 +85,7 @@ export const useFilteredMods = (
|
||||
filterOptions.sort,
|
||||
filterOptions.moderated,
|
||||
filterOptions.nsfw,
|
||||
filterOptions.author,
|
||||
author,
|
||||
mods,
|
||||
muteLists,
|
||||
nsfwList
|
||||
|
@ -11,20 +11,20 @@ import { MAX_MODS_PER_PAGE, T_TAG_VALUE } from 'constants.ts'
|
||||
import {
|
||||
useAppSelector,
|
||||
useFilteredMods,
|
||||
useLocalStorage,
|
||||
useMuteLists,
|
||||
useNDKContext,
|
||||
useNSFWList
|
||||
} from 'hooks'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useParams, useSearchParams } from 'react-router-dom'
|
||||
import { FilterOptions, ModDetails } from 'types'
|
||||
import {
|
||||
FilterOptions,
|
||||
ModDetails,
|
||||
ModeratedFilter,
|
||||
NSFWFilter,
|
||||
SortBy
|
||||
} from 'types'
|
||||
import { extractModData, isModDataComplete, scrollIntoView } from 'utils'
|
||||
DEFAULT_FILTER_OPTIONS,
|
||||
extractModData,
|
||||
isModDataComplete,
|
||||
scrollIntoView
|
||||
} from 'utils'
|
||||
|
||||
export const GamePage = () => {
|
||||
const scrollTargetRef = useRef<HTMLDivElement>(null)
|
||||
@ -34,12 +34,10 @@ export const GamePage = () => {
|
||||
const muteLists = useMuteLists()
|
||||
const nsfwList = useNSFWList()
|
||||
|
||||
const [filterOptions, setFilterOptions] = useState<FilterOptions>({
|
||||
sort: SortBy.Latest,
|
||||
nsfw: NSFWFilter.Hide_NSFW,
|
||||
source: window.location.host,
|
||||
moderated: ModeratedFilter.Moderated
|
||||
})
|
||||
const [filterOptions] = useLocalStorage<FilterOptions>(
|
||||
'filter',
|
||||
DEFAULT_FILTER_OPTIONS
|
||||
)
|
||||
const [mods, setMods] = useState<ModDetails[]>([])
|
||||
|
||||
const [currentPage, setCurrentPage] = useState(1)
|
||||
@ -186,10 +184,7 @@ export const GamePage = () => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ModFilter
|
||||
filterOptions={filterOptions}
|
||||
setFilterOptions={setFilterOptions}
|
||||
/>
|
||||
<ModFilter />
|
||||
<div className='IBMSecMain IBMSMListWrapper'>
|
||||
<div className='IBMSMList'>
|
||||
{currentMods.map((mod) => (
|
||||
|
@ -8,6 +8,7 @@ import { MOD_FILTER_LIMIT } from '../constants'
|
||||
import {
|
||||
useAppSelector,
|
||||
useFilteredMods,
|
||||
useLocalStorage,
|
||||
useMuteLists,
|
||||
useNDKContext,
|
||||
useNSFWList
|
||||
@ -17,14 +18,8 @@ import '../styles/filters.css'
|
||||
import '../styles/pagination.css'
|
||||
import '../styles/search.css'
|
||||
import '../styles/styles.css'
|
||||
import {
|
||||
FilterOptions,
|
||||
ModDetails,
|
||||
ModeratedFilter,
|
||||
NSFWFilter,
|
||||
SortBy
|
||||
} from '../types'
|
||||
import { scrollIntoView } from 'utils'
|
||||
import { FilterOptions, ModDetails } from '../types'
|
||||
import { DEFAULT_FILTER_OPTIONS, scrollIntoView } from 'utils'
|
||||
import { SearchInput } from 'components/SearchInput'
|
||||
|
||||
export const ModsPage = () => {
|
||||
@ -32,12 +27,11 @@ export const ModsPage = () => {
|
||||
const { fetchMods } = useNDKContext()
|
||||
const [isFetching, setIsFetching] = useState(false)
|
||||
const [mods, setMods] = useState<ModDetails[]>([])
|
||||
const [filterOptions, setFilterOptions] = useState<FilterOptions>({
|
||||
sort: SortBy.Latest,
|
||||
nsfw: NSFWFilter.Hide_NSFW,
|
||||
source: window.location.host,
|
||||
moderated: ModeratedFilter.Moderated
|
||||
})
|
||||
|
||||
const [filterOptions] = useLocalStorage<FilterOptions>(
|
||||
'filter',
|
||||
DEFAULT_FILTER_OPTIONS
|
||||
)
|
||||
const muteLists = useMuteLists()
|
||||
const nsfwList = useNSFWList()
|
||||
|
||||
@ -113,10 +107,7 @@ export const ModsPage = () => {
|
||||
ref={scrollTargetRef}
|
||||
>
|
||||
<PageTitleRow />
|
||||
<ModFilter
|
||||
filterOptions={filterOptions}
|
||||
setFilterOptions={setFilterOptions}
|
||||
/>
|
||||
<ModFilter />
|
||||
|
||||
<div className='IBMSecMain IBMSMListWrapper'>
|
||||
<div className='IBMSMList'>
|
||||
|
@ -9,6 +9,7 @@ import { MOD_FILTER_LIMIT } from '../constants'
|
||||
import {
|
||||
useAppSelector,
|
||||
useFilteredMods,
|
||||
useLocalStorage,
|
||||
useMuteLists,
|
||||
useNDKContext,
|
||||
useNSFWList
|
||||
@ -18,16 +19,10 @@ import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { useParams, Navigate, Link } from 'react-router-dom'
|
||||
import { toast } from 'react-toastify'
|
||||
import { appRoutes, getProfilePageRoute } from 'routes'
|
||||
import {
|
||||
FilterOptions,
|
||||
ModDetails,
|
||||
ModeratedFilter,
|
||||
NSFWFilter,
|
||||
SortBy,
|
||||
UserRelaysType
|
||||
} from 'types'
|
||||
import { FilterOptions, ModDetails, UserRelaysType } from 'types'
|
||||
import {
|
||||
copyTextToClipboard,
|
||||
DEFAULT_FILTER_OPTIONS,
|
||||
now,
|
||||
npubToHex,
|
||||
scrollIntoView,
|
||||
@ -230,12 +225,9 @@ export const ProfilePage = () => {
|
||||
|
||||
// Mods
|
||||
const [mods, setMods] = useState<ModDetails[]>([])
|
||||
const [filterOptions, setFilterOptions] = useState<FilterOptions>({
|
||||
sort: SortBy.Latest,
|
||||
nsfw: NSFWFilter.Hide_NSFW,
|
||||
source: window.location.host,
|
||||
moderated: ModeratedFilter.Moderated,
|
||||
author: profilePubkey
|
||||
const filterKey = 'filter-profile'
|
||||
const [filterOptions] = useLocalStorage<FilterOptions>(filterKey, {
|
||||
...DEFAULT_FILTER_OPTIONS
|
||||
})
|
||||
const muteLists = useMuteLists()
|
||||
const nsfwList = useNSFWList()
|
||||
@ -304,7 +296,8 @@ export const ProfilePage = () => {
|
||||
userState,
|
||||
filterOptions,
|
||||
nsfwList,
|
||||
muteLists
|
||||
muteLists,
|
||||
profilePubkey
|
||||
)
|
||||
|
||||
// Redirect route
|
||||
@ -470,10 +463,7 @@ export const ProfilePage = () => {
|
||||
{/* Tabs Content */}
|
||||
{tab === 0 && (
|
||||
<>
|
||||
<ModFilter
|
||||
filterOptions={filterOptions}
|
||||
setFilterOptions={setFilterOptions}
|
||||
/>
|
||||
<ModFilter filterKey={filterKey} author={profilePubkey} />
|
||||
|
||||
<div className='IBMSMList IBMSMListAlt'>
|
||||
{filteredModList.map((mod) => (
|
||||
|
@ -23,28 +23,16 @@ import {
|
||||
useAppSelector,
|
||||
useFilteredMods,
|
||||
useGames,
|
||||
useLocalStorage,
|
||||
useMuteLists,
|
||||
useNDKContext,
|
||||
useNSFWList
|
||||
} from 'hooks'
|
||||
import React, {
|
||||
Dispatch,
|
||||
SetStateAction,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState
|
||||
} from 'react'
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useSearchParams } from 'react-router-dom'
|
||||
import { FilterOptions, ModDetails, ModeratedFilter, MuteLists } from 'types'
|
||||
import {
|
||||
FilterOptions,
|
||||
ModDetails,
|
||||
ModeratedFilter,
|
||||
MuteLists,
|
||||
NSFWFilter,
|
||||
SortBy
|
||||
} from 'types'
|
||||
import {
|
||||
DEFAULT_FILTER_OPTIONS,
|
||||
extractModData,
|
||||
isModDataComplete,
|
||||
log,
|
||||
@ -69,12 +57,10 @@ export const SearchPage = () => {
|
||||
const searchKind =
|
||||
(searchParams.get('kind') as SearchKindEnum) || SearchKindEnum.Mods
|
||||
|
||||
const [filterOptions, setFilterOptions] = useState<FilterOptions>({
|
||||
sort: SortBy.Latest,
|
||||
nsfw: NSFWFilter.Hide_NSFW,
|
||||
source: window.location.host,
|
||||
moderated: ModeratedFilter.Moderated
|
||||
})
|
||||
const [filterOptions] = useLocalStorage<FilterOptions>(
|
||||
'filter',
|
||||
DEFAULT_FILTER_OPTIONS
|
||||
)
|
||||
|
||||
const [searchTerm, setSearchTerm] = useState(searchParams.get('q') || '')
|
||||
|
||||
@ -124,10 +110,7 @@ export const SearchPage = () => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Filters
|
||||
filterOptions={filterOptions}
|
||||
setFilterOptions={setFilterOptions}
|
||||
/>
|
||||
<Filters />
|
||||
{searchKind === SearchKindEnum.Mods && (
|
||||
<ModsResult
|
||||
searchTerm={searchTerm}
|
||||
@ -153,75 +136,29 @@ export const SearchPage = () => {
|
||||
)
|
||||
}
|
||||
|
||||
type FiltersProps = {
|
||||
filterOptions: FilterOptions
|
||||
setFilterOptions: Dispatch<SetStateAction<FilterOptions>>
|
||||
}
|
||||
const Filters = React.memo(() => {
|
||||
const [filterOptions, setFilterOptions] = useLocalStorage<FilterOptions>(
|
||||
'filter',
|
||||
DEFAULT_FILTER_OPTIONS
|
||||
)
|
||||
|
||||
const Filters = React.memo(
|
||||
({ filterOptions, setFilterOptions }: FiltersProps) => {
|
||||
const userState = useAppSelector((state) => state.user)
|
||||
const [searchParams, setSearchParams] = useSearchParams()
|
||||
const searchKind =
|
||||
(searchParams.get('kind') as SearchKindEnum) || SearchKindEnum.Mods
|
||||
const handleChangeSearchKind = (kind: SearchKindEnum) => {
|
||||
searchParams.set('kind', kind)
|
||||
setSearchParams(searchParams, {
|
||||
replace: true
|
||||
})
|
||||
}
|
||||
const userState = useAppSelector((state) => state.user)
|
||||
const [searchParams, setSearchParams] = useSearchParams()
|
||||
const searchKind =
|
||||
(searchParams.get('kind') as SearchKindEnum) || SearchKindEnum.Mods
|
||||
const handleChangeSearchKind = (kind: SearchKindEnum) => {
|
||||
searchParams.set('kind', kind)
|
||||
setSearchParams(searchParams, {
|
||||
replace: true
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='IBMSecMain'>
|
||||
<div className='FiltersMain'>
|
||||
{searchKind === SearchKindEnum.Mods && (
|
||||
<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>
|
||||
)}
|
||||
return (
|
||||
<div className='IBMSecMain'>
|
||||
<div className='FiltersMain'>
|
||||
{searchKind === SearchKindEnum.Mods && <ModFilter />}
|
||||
|
||||
{searchKind === SearchKindEnum.Users && (
|
||||
<div className='FiltersMainElement'>
|
||||
<div className='dropdown dropdownMain'>
|
||||
<button
|
||||
@ -230,26 +167,65 @@ const Filters = React.memo(
|
||||
data-bs-toggle='dropdown'
|
||||
type='button'
|
||||
>
|
||||
Searching: {searchKind}
|
||||
{filterOptions.moderated}
|
||||
</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>
|
||||
))}
|
||||
{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>
|
||||
)}
|
||||
|
||||
<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>
|
||||
)
|
||||
})
|
||||
|
||||
type ModsResultProps = {
|
||||
filterOptions: FilterOptions
|
||||
|
@ -22,5 +22,4 @@ export interface FilterOptions {
|
||||
nsfw: NSFWFilter
|
||||
source: string
|
||||
moderated: ModeratedFilter
|
||||
author?: string
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user