2024-10-14 19:20:43 +05:00
|
|
|
import {
|
|
|
|
NDKEvent,
|
|
|
|
NDKFilter,
|
|
|
|
NDKKind,
|
|
|
|
NDKSubscriptionCacheUsage,
|
|
|
|
NDKUserProfile,
|
|
|
|
profileFromEvent
|
|
|
|
} from '@nostr-dev-kit/ndk'
|
2024-09-18 08:20:32 +05:00
|
|
|
import { ErrorBoundary } from 'components/ErrorBoundary'
|
|
|
|
import { GameCard } from 'components/GameCard'
|
|
|
|
import { LoadingSpinner } from 'components/LoadingSpinner'
|
|
|
|
import { ModCard } from 'components/ModCard'
|
2024-10-07 15:45:21 +05:00
|
|
|
import { ModFilter } from 'components/ModsFilter'
|
2024-09-18 08:20:32 +05:00
|
|
|
import { Pagination } from 'components/Pagination'
|
|
|
|
import { Profile } from 'components/ProfileSection'
|
2024-10-29 09:39:30 +01:00
|
|
|
import { SearchInput } from 'components/SearchInput'
|
2024-09-18 21:40:34 +05:00
|
|
|
import {
|
|
|
|
MAX_GAMES_PER_PAGE,
|
|
|
|
MAX_MODS_PER_PAGE,
|
|
|
|
T_TAG_VALUE
|
|
|
|
} from 'constants.ts'
|
2024-10-07 15:45:21 +05:00
|
|
|
import {
|
|
|
|
useAppSelector,
|
|
|
|
useFilteredMods,
|
|
|
|
useGames,
|
2024-10-29 13:38:13 +01:00
|
|
|
useLocalStorage,
|
2024-10-07 15:45:21 +05:00
|
|
|
useMuteLists,
|
2024-10-14 19:20:43 +05:00
|
|
|
useNDKContext,
|
2024-10-07 15:45:21 +05:00
|
|
|
useNSFWList
|
|
|
|
} from 'hooks'
|
2024-10-29 13:38:13 +01:00
|
|
|
import React, { useEffect, useMemo, useRef, useState } from 'react'
|
2024-09-18 22:40:41 +05:00
|
|
|
import { useSearchParams } from 'react-router-dom'
|
2024-10-29 13:38:13 +01:00
|
|
|
import { FilterOptions, ModDetails, ModeratedFilter, MuteLists } from 'types'
|
2024-10-07 15:45:21 +05:00
|
|
|
import {
|
2024-10-29 13:38:13 +01:00
|
|
|
DEFAULT_FILTER_OPTIONS,
|
2024-10-21 15:21:09 +02:00
|
|
|
extractModData,
|
|
|
|
isModDataComplete,
|
|
|
|
log,
|
|
|
|
LogType,
|
2024-10-29 15:44:41 +01:00
|
|
|
scrollIntoView,
|
|
|
|
timeout
|
2024-10-21 15:21:09 +02:00
|
|
|
} from 'utils'
|
2024-09-18 08:20:32 +05:00
|
|
|
|
2024-10-07 15:45:21 +05:00
|
|
|
enum SearchKindEnum {
|
2024-09-18 08:20:32 +05:00
|
|
|
Mods = 'Mods',
|
|
|
|
Games = 'Games',
|
|
|
|
Users = 'Users'
|
|
|
|
}
|
|
|
|
|
2024-09-16 12:36:35 +05:00
|
|
|
export const SearchPage = () => {
|
2024-10-21 15:21:09 +02:00
|
|
|
const scrollTargetRef = useRef<HTMLDivElement>(null)
|
2024-10-29 09:39:30 +01:00
|
|
|
const [searchParams, setSearchParams] = useSearchParams()
|
2024-09-18 22:40:41 +05:00
|
|
|
|
2024-09-18 21:40:34 +05:00
|
|
|
const muteLists = useMuteLists()
|
2024-10-07 15:45:21 +05:00
|
|
|
const nsfwList = useNSFWList()
|
2024-09-18 08:20:32 +05:00
|
|
|
const searchTermRef = useRef<HTMLInputElement>(null)
|
2024-10-07 15:45:21 +05:00
|
|
|
|
2024-10-29 09:39:30 +01:00
|
|
|
const searchKind =
|
|
|
|
(searchParams.get('kind') as SearchKindEnum) || SearchKindEnum.Mods
|
2024-10-07 15:45:21 +05:00
|
|
|
|
2024-10-29 13:38:13 +01:00
|
|
|
const [filterOptions] = useLocalStorage<FilterOptions>(
|
|
|
|
'filter',
|
|
|
|
DEFAULT_FILTER_OPTIONS
|
|
|
|
)
|
2024-10-07 15:45:21 +05:00
|
|
|
|
2024-10-29 09:39:30 +01:00
|
|
|
const [searchTerm, setSearchTerm] = useState(searchParams.get('q') || '')
|
2024-09-18 08:20:32 +05:00
|
|
|
|
|
|
|
const handleSearch = () => {
|
|
|
|
const value = searchTermRef.current?.value || '' // Access the input value from the ref
|
|
|
|
setSearchTerm(value)
|
2024-10-29 09:39:30 +01:00
|
|
|
|
|
|
|
if (value) {
|
|
|
|
searchParams.set('q', value)
|
|
|
|
} else {
|
|
|
|
searchParams.delete('q')
|
|
|
|
}
|
|
|
|
|
|
|
|
setSearchParams(searchParams, {
|
|
|
|
replace: true
|
|
|
|
})
|
2024-09-18 08:20:32 +05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Handle "Enter" key press inside the input
|
|
|
|
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
|
|
if (event.key === 'Enter') {
|
|
|
|
handleSearch()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className='InnerBodyMain'>
|
|
|
|
<div className='ContainerMain'>
|
2024-10-21 15:21:09 +02:00
|
|
|
<div
|
|
|
|
className='IBMSecMainGroup IBMSecMainGroupAlt'
|
|
|
|
ref={scrollTargetRef}
|
|
|
|
>
|
2024-09-18 08:20:32 +05:00
|
|
|
<div className='IBMSecMain'>
|
|
|
|
<div className='SearchMainWrapper'>
|
|
|
|
<div className='IBMSMTitleMain'>
|
|
|
|
<h2 className='IBMSMTitleMainHeading'>
|
|
|
|
Search:
|
|
|
|
<span className='IBMSMTitleMainHeadingSpan'>
|
|
|
|
{searchTerm}
|
|
|
|
</span>
|
|
|
|
</h2>
|
|
|
|
</div>
|
2024-10-29 09:39:30 +01:00
|
|
|
<SearchInput
|
|
|
|
handleKeyDown={handleKeyDown}
|
|
|
|
handleSearch={handleSearch}
|
|
|
|
ref={searchTermRef}
|
|
|
|
/>
|
2024-09-18 08:20:32 +05:00
|
|
|
</div>
|
|
|
|
</div>
|
2024-10-29 13:38:13 +01:00
|
|
|
<Filters />
|
2024-10-07 15:45:21 +05:00
|
|
|
{searchKind === SearchKindEnum.Mods && (
|
2024-09-18 08:20:32 +05:00
|
|
|
<ModsResult
|
|
|
|
searchTerm={searchTerm}
|
|
|
|
filterOptions={filterOptions}
|
|
|
|
muteLists={muteLists}
|
2024-10-07 15:45:21 +05:00
|
|
|
nsfwList={nsfwList}
|
2024-10-21 15:21:09 +02:00
|
|
|
el={scrollTargetRef.current}
|
2024-09-18 08:20:32 +05:00
|
|
|
/>
|
|
|
|
)}
|
2024-10-07 15:45:21 +05:00
|
|
|
{searchKind === SearchKindEnum.Users && (
|
2024-09-18 08:20:32 +05:00
|
|
|
<UsersResult
|
|
|
|
searchTerm={searchTerm}
|
|
|
|
muteLists={muteLists}
|
|
|
|
moderationFilter={filterOptions.moderated}
|
|
|
|
/>
|
|
|
|
)}
|
2024-10-07 15:45:21 +05:00
|
|
|
{searchKind === SearchKindEnum.Games && (
|
2024-09-18 08:20:32 +05:00
|
|
|
<GamesResult searchTerm={searchTerm} />
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-10-29 13:38:13 +01:00
|
|
|
const Filters = React.memo(() => {
|
|
|
|
const [filterOptions, setFilterOptions] = useLocalStorage<FilterOptions>(
|
|
|
|
'filter',
|
|
|
|
DEFAULT_FILTER_OPTIONS
|
|
|
|
)
|
2024-09-23 14:56:14 +05:00
|
|
|
|
2024-10-29 13:38:13 +01:00
|
|
|
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
|
|
|
|
})
|
|
|
|
}
|
2024-09-23 14:56:14 +05:00
|
|
|
|
2024-10-29 13:38:13 +01:00
|
|
|
return (
|
|
|
|
<div className='IBMSecMain'>
|
|
|
|
<div className='FiltersMain'>
|
|
|
|
{searchKind === SearchKindEnum.Mods && <ModFilter />}
|
2024-09-23 14:56:14 +05:00
|
|
|
|
2024-10-29 13:38:13 +01:00
|
|
|
{searchKind === SearchKindEnum.Users && (
|
2024-09-18 08:20:32 +05:00
|
|
|
<div className='FiltersMainElement'>
|
|
|
|
<div className='dropdown dropdownMain'>
|
|
|
|
<button
|
|
|
|
className='btn dropdown-toggle btnMain btnMainDropdown'
|
|
|
|
aria-expanded='false'
|
|
|
|
data-bs-toggle='dropdown'
|
|
|
|
type='button'
|
|
|
|
>
|
2024-10-29 13:38:13 +01:00
|
|
|
{filterOptions.moderated}
|
2024-09-18 08:20:32 +05:00
|
|
|
</button>
|
|
|
|
<div className='dropdown-menu dropdownMainMenu'>
|
2024-10-29 13:38:13 +01:00
|
|
|
{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>
|
|
|
|
)
|
|
|
|
})}
|
2024-09-18 08:20:32 +05:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
2024-10-29 13:38:13 +01:00
|
|
|
)}
|
|
|
|
|
|
|
|
<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>
|
2024-09-18 08:20:32 +05:00
|
|
|
</div>
|
|
|
|
</div>
|
2024-10-29 13:38:13 +01:00
|
|
|
</div>
|
|
|
|
)
|
|
|
|
})
|
2024-09-18 08:20:32 +05:00
|
|
|
|
|
|
|
type ModsResultProps = {
|
|
|
|
filterOptions: FilterOptions
|
|
|
|
searchTerm: string
|
|
|
|
muteLists: {
|
|
|
|
admin: MuteLists
|
|
|
|
user: MuteLists
|
|
|
|
}
|
2024-10-07 15:45:21 +05:00
|
|
|
nsfwList: string[]
|
2024-10-21 15:21:09 +02:00
|
|
|
el: HTMLElement | null
|
2024-09-18 08:20:32 +05:00
|
|
|
}
|
|
|
|
|
|
|
|
const ModsResult = ({
|
|
|
|
filterOptions,
|
|
|
|
searchTerm,
|
2024-10-07 15:45:21 +05:00
|
|
|
muteLists,
|
2024-10-21 15:21:09 +02:00
|
|
|
nsfwList,
|
|
|
|
el
|
2024-09-18 08:20:32 +05:00
|
|
|
}: ModsResultProps) => {
|
2024-10-14 19:20:43 +05:00
|
|
|
const { ndk } = useNDKContext()
|
2024-09-18 08:20:32 +05:00
|
|
|
const [mods, setMods] = useState<ModDetails[]>([])
|
|
|
|
const [page, setPage] = useState(1)
|
|
|
|
const userState = useAppSelector((state) => state.user)
|
|
|
|
|
|
|
|
useEffect(() => {
|
2024-10-14 19:20:43 +05:00
|
|
|
const filter: NDKFilter = {
|
|
|
|
kinds: [NDKKind.Classified],
|
|
|
|
'#t': [T_TAG_VALUE]
|
2024-09-18 08:20:32 +05:00
|
|
|
}
|
|
|
|
|
2024-10-14 19:20:43 +05:00
|
|
|
const subscription = ndk.subscribe(filter, {
|
|
|
|
cacheUsage: NDKSubscriptionCacheUsage.PARALLEL,
|
|
|
|
closeOnEose: true
|
|
|
|
})
|
2024-09-18 08:20:32 +05:00
|
|
|
|
2024-10-14 19:20:43 +05:00
|
|
|
subscription.on('event', (ndkEvent) => {
|
|
|
|
if (isModDataComplete(ndkEvent)) {
|
|
|
|
const mod = extractModData(ndkEvent)
|
|
|
|
setMods((prev) => {
|
|
|
|
if (prev.find((e) => e.aTag === mod.aTag)) return [...prev]
|
2024-09-18 08:20:32 +05:00
|
|
|
|
2024-10-14 19:20:43 +05:00
|
|
|
return [...prev, mod]
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
2024-09-18 08:20:32 +05:00
|
|
|
|
|
|
|
// Cleanup function to stop all subscriptions
|
|
|
|
return () => {
|
2024-10-14 19:20:43 +05:00
|
|
|
subscription.stop()
|
2024-09-18 08:20:32 +05:00
|
|
|
}
|
2024-10-14 19:20:43 +05:00
|
|
|
}, [ndk])
|
2024-09-18 08:20:32 +05:00
|
|
|
|
|
|
|
useEffect(() => {
|
2024-10-21 15:21:09 +02:00
|
|
|
scrollIntoView(el)
|
2024-09-18 08:20:32 +05:00
|
|
|
setPage(1)
|
2024-10-21 15:21:09 +02:00
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
2024-09-18 08:20:32 +05:00
|
|
|
}, [searchTerm])
|
|
|
|
|
|
|
|
const filteredMods = useMemo(() => {
|
2024-10-29 09:39:30 +01:00
|
|
|
// Search page requires search term
|
2024-09-18 08:20:32 +05:00
|
|
|
if (searchTerm === '') return []
|
|
|
|
|
|
|
|
const lowerCaseSearchTerm = searchTerm.toLowerCase()
|
|
|
|
|
|
|
|
const filterFn = (mod: ModDetails) =>
|
|
|
|
mod.title.toLowerCase().includes(lowerCaseSearchTerm) ||
|
|
|
|
mod.game.toLowerCase().includes(lowerCaseSearchTerm) ||
|
|
|
|
mod.summary.toLowerCase().includes(lowerCaseSearchTerm) ||
|
|
|
|
mod.body.toLowerCase().includes(lowerCaseSearchTerm) ||
|
|
|
|
mod.tags.findIndex((tag) =>
|
|
|
|
tag.toLowerCase().includes(lowerCaseSearchTerm)
|
|
|
|
) > -1
|
|
|
|
|
2024-10-28 14:49:36 +01:00
|
|
|
const filterSourceFn = (mod: ModDetails) => {
|
2024-10-29 09:39:30 +01:00
|
|
|
// Filter by source if selected
|
2024-10-28 14:49:36 +01:00
|
|
|
if (filterOptions.source === window.location.host) {
|
|
|
|
return mod.rTag === filterOptions.source
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
return mods.filter(filterFn).filter(filterSourceFn)
|
|
|
|
}, [filterOptions.source, mods, searchTerm])
|
2024-09-18 08:20:32 +05:00
|
|
|
|
2024-10-07 15:45:21 +05:00
|
|
|
const filteredModList = useFilteredMods(
|
2024-09-18 08:20:32 +05:00
|
|
|
filteredMods,
|
2024-10-07 15:45:21 +05:00
|
|
|
userState,
|
|
|
|
filterOptions,
|
|
|
|
nsfwList,
|
2024-09-18 08:20:32 +05:00
|
|
|
muteLists
|
2024-10-07 15:45:21 +05:00
|
|
|
)
|
2024-09-18 08:20:32 +05:00
|
|
|
|
|
|
|
const handleNext = () => {
|
2024-10-21 15:21:09 +02:00
|
|
|
scrollIntoView(el)
|
2024-09-18 08:20:32 +05:00
|
|
|
setPage((prev) => prev + 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
const handlePrev = () => {
|
2024-10-21 15:21:09 +02:00
|
|
|
scrollIntoView(el)
|
2024-09-18 08:20:32 +05:00
|
|
|
setPage((prev) => prev - 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<div className='IBMSecMain IBMSMListWrapper'>
|
|
|
|
<div className='IBMSMList'>
|
|
|
|
{filteredModList
|
|
|
|
.slice((page - 1) * MAX_MODS_PER_PAGE, page * MAX_MODS_PER_PAGE)
|
2024-09-23 20:58:50 +05:00
|
|
|
.map((mod) => (
|
|
|
|
<ModCard key={mod.id} {...mod} />
|
|
|
|
))}
|
2024-09-18 08:20:32 +05:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<Pagination
|
|
|
|
page={page}
|
|
|
|
disabledNext={filteredModList.length <= page * MAX_MODS_PER_PAGE}
|
|
|
|
handlePrev={handlePrev}
|
|
|
|
handleNext={handleNext}
|
|
|
|
/>
|
|
|
|
</>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
type UsersResultProps = {
|
|
|
|
searchTerm: string
|
2024-10-07 15:45:21 +05:00
|
|
|
moderationFilter: ModeratedFilter
|
2024-09-18 08:20:32 +05:00
|
|
|
muteLists: {
|
|
|
|
admin: MuteLists
|
|
|
|
user: MuteLists
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const UsersResult = ({
|
|
|
|
searchTerm,
|
|
|
|
moderationFilter,
|
|
|
|
muteLists
|
|
|
|
}: UsersResultProps) => {
|
2024-10-14 19:20:43 +05:00
|
|
|
const { fetchEvents } = useNDKContext()
|
2024-09-18 08:20:32 +05:00
|
|
|
const [isFetching, setIsFetching] = useState(false)
|
|
|
|
const [profiles, setProfiles] = useState<NDKUserProfile[]>([])
|
|
|
|
|
|
|
|
const userState = useAppSelector((state) => state.user)
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (searchTerm === '') {
|
|
|
|
setProfiles([])
|
|
|
|
} else {
|
2024-10-29 15:44:41 +01:00
|
|
|
const fetchProfiles = async () => {
|
|
|
|
setIsFetching(true)
|
|
|
|
|
|
|
|
const filter: NDKFilter = {
|
|
|
|
kinds: [NDKKind.Metadata],
|
|
|
|
search: searchTerm
|
|
|
|
}
|
|
|
|
|
|
|
|
const profiles = await Promise.race([
|
|
|
|
fetchEvents(filter),
|
|
|
|
timeout(10 * 1000)
|
|
|
|
])
|
|
|
|
.then((events) => {
|
|
|
|
const results = events.map((event) => {
|
|
|
|
const ndkEvent = new NDKEvent(undefined, event)
|
|
|
|
const profile = profileFromEvent(ndkEvent)
|
|
|
|
return profile
|
|
|
|
})
|
|
|
|
return results
|
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
log(true, LogType.Error, 'An error occurred in fetching users', err)
|
|
|
|
return []
|
|
|
|
})
|
|
|
|
|
|
|
|
setProfiles(profiles)
|
|
|
|
setIsFetching(false)
|
2024-09-18 08:20:32 +05:00
|
|
|
}
|
|
|
|
|
2024-10-29 15:44:41 +01:00
|
|
|
fetchProfiles()
|
2024-09-18 08:20:32 +05:00
|
|
|
}
|
2024-10-29 15:44:41 +01:00
|
|
|
}, [fetchEvents, searchTerm])
|
2024-09-18 08:20:32 +05:00
|
|
|
|
|
|
|
const filteredProfiles = useMemo(() => {
|
|
|
|
let filtered = [...profiles]
|
|
|
|
const isAdmin = userState.user?.npub === import.meta.env.VITE_REPORTING_NPUB
|
|
|
|
const isUnmoderatedFully =
|
2024-10-07 15:45:21 +05:00
|
|
|
moderationFilter === ModeratedFilter.Unmoderated_Fully
|
2024-09-18 08:20:32 +05:00
|
|
|
|
|
|
|
// Only apply filtering if the user is not an admin or the admin has not selected "Unmoderated Fully"
|
|
|
|
if (!(isAdmin && isUnmoderatedFully)) {
|
|
|
|
filtered = filtered.filter(
|
|
|
|
(profile) => !muteLists.admin.authors.includes(profile.pubkey as string)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-10-07 15:45:21 +05:00
|
|
|
if (moderationFilter === ModeratedFilter.Moderated) {
|
2024-09-18 08:20:32 +05:00
|
|
|
filtered = filtered.filter(
|
|
|
|
(profile) => !muteLists.user.authors.includes(profile.pubkey as string)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
return filtered
|
|
|
|
}, [userState.user?.npub, moderationFilter, profiles, muteLists])
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
{isFetching && <LoadingSpinner desc='Fetching Profiles' />}
|
|
|
|
<div className='IBMSecMain IBMSMListWrapper'>
|
|
|
|
<div className='IBMSMList'>
|
|
|
|
{filteredProfiles.map((profile) => {
|
|
|
|
if (profile.pubkey) {
|
|
|
|
return (
|
|
|
|
<ErrorBoundary key={profile.pubkey}>
|
2024-10-28 12:43:26 +01:00
|
|
|
<Profile pubkey={profile.pubkey as string} />
|
2024-09-18 08:20:32 +05:00
|
|
|
</ErrorBoundary>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
return null
|
|
|
|
})}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
type GamesResultProps = {
|
|
|
|
searchTerm: string
|
|
|
|
}
|
|
|
|
|
|
|
|
const GamesResult = ({ searchTerm }: GamesResultProps) => {
|
2024-09-18 21:40:34 +05:00
|
|
|
const games = useGames()
|
2024-09-18 08:20:32 +05:00
|
|
|
const [page, setPage] = useState(1)
|
|
|
|
|
|
|
|
// Reset the page to 1 whenever searchTerm changes
|
|
|
|
useEffect(() => {
|
|
|
|
setPage(1)
|
|
|
|
}, [searchTerm])
|
|
|
|
|
|
|
|
const filteredGames = useMemo(() => {
|
|
|
|
if (searchTerm === '') return []
|
|
|
|
|
|
|
|
const lowerCaseSearchTerm = searchTerm.toLowerCase()
|
|
|
|
|
|
|
|
return games.filter((game) =>
|
|
|
|
game['Game Name'].toLowerCase().includes(lowerCaseSearchTerm)
|
|
|
|
)
|
|
|
|
}, [searchTerm, games])
|
|
|
|
|
|
|
|
const handleNext = () => {
|
|
|
|
setPage((prev) => prev + 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
const handlePrev = () => {
|
|
|
|
setPage((prev) => prev - 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<div className='IBMSecMain IBMSMListWrapper'>
|
2024-09-18 13:53:55 +00:00
|
|
|
<div className='IBMSMList IBMSMListFeaturedAlt'>
|
2024-09-18 08:20:32 +05:00
|
|
|
{filteredGames
|
|
|
|
.slice((page - 1) * MAX_GAMES_PER_PAGE, page * MAX_GAMES_PER_PAGE)
|
|
|
|
.map((game) => (
|
|
|
|
<GameCard
|
|
|
|
key={game['Game Name']}
|
|
|
|
title={game['Game Name']}
|
|
|
|
imageUrl={game['Boxart image']}
|
|
|
|
/>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<Pagination
|
|
|
|
page={page}
|
|
|
|
disabledNext={filteredGames.length <= page * MAX_GAMES_PER_PAGE}
|
|
|
|
handlePrev={handlePrev}
|
|
|
|
handleNext={handleNext}
|
|
|
|
/>
|
|
|
|
</>
|
|
|
|
)
|
2024-09-16 12:36:35 +05:00
|
|
|
}
|