feat: mod search on game page

This commit is contained in:
enes 2024-10-29 09:35:39 +01:00
parent 6e4fa104c0
commit 72252d416b
2 changed files with 111 additions and 6 deletions

View File

@ -0,0 +1,39 @@
import { forwardRef } from 'react'
interface SearchInputProps {
handleKeyDown: (event: React.KeyboardEvent<HTMLInputElement>) => void
handleSearch: () => void
}
export const SearchInput = forwardRef<HTMLInputElement, SearchInputProps>(
({ handleKeyDown, handleSearch }, ref) => (
<div className='SearchMain'>
<div className='SearchMainInside'>
<div className='SearchMainInsideWrapper'>
<input
type='text'
className='SMIWInput'
ref={ref}
onKeyDown={handleKeyDown}
placeholder='Enter search term'
/>
<button
className='btn btnMain SMIWButton'
type='button'
onClick={handleSearch}
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M500.3 443.7l-119.7-119.7c27.22-40.41 40.65-90.9 33.46-144.7C401.8 87.79 326.8 13.32 235.2 1.723C99.01-15.51-15.51 99.01 1.724 235.2c11.6 91.64 86.08 166.7 177.6 178.9c53.8 7.189 104.3-6.236 144.7-33.46l119.7 119.7c15.62 15.62 40.95 15.62 56.57 0C515.9 484.7 515.9 459.3 500.3 443.7zM79.1 208c0-70.58 57.42-128 128-128s128 57.42 128 128c0 70.58-57.42 128-128 128S79.1 278.6 79.1 208z'></path>
</svg>
</button>
</div>
</div>
</div>
)
)

View File

@ -6,6 +6,7 @@ import {
import { ModCard } from 'components/ModCard' import { ModCard } from 'components/ModCard'
import { ModFilter } from 'components/ModsFilter' import { ModFilter } from 'components/ModsFilter'
import { PaginationWithPageNumbers } from 'components/Pagination' import { PaginationWithPageNumbers } from 'components/Pagination'
import { SearchInput } from 'components/SearchInput'
import { MAX_MODS_PER_PAGE, T_TAG_VALUE } from 'constants.ts' import { MAX_MODS_PER_PAGE, T_TAG_VALUE } from 'constants.ts'
import { import {
useAppSelector, useAppSelector,
@ -14,8 +15,8 @@ import {
useNDKContext, useNDKContext,
useNSFWList useNSFWList
} from 'hooks' } from 'hooks'
import { useEffect, useRef, useState } from 'react' import { useEffect, useMemo, useRef, useState } from 'react'
import { useParams } from 'react-router-dom' import { useParams, useSearchParams } from 'react-router-dom'
import { import {
FilterOptions, FilterOptions,
ModDetails, ModDetails,
@ -45,8 +46,60 @@ export const GamePage = () => {
const userState = useAppSelector((state) => state.user) const userState = useAppSelector((state) => state.user)
const filteredMods = useFilteredMods( // Search
mods, const searchTermRef = useRef<HTMLInputElement>(null)
const [searchParams, setSearchParams] = useSearchParams()
const [searchTerm, setSearchTerm] = useState(searchParams.get('q') || '')
const handleSearch = () => {
const value = searchTermRef.current?.value || '' // Access the input value from the ref
setSearchTerm(value)
if (value) {
searchParams.set('q', value)
} else {
searchParams.delete('q')
}
setSearchParams(searchParams, {
replace: true
})
}
// Handle "Enter" key press inside the input
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Enter') {
handleSearch()
}
}
const filteredMods = useMemo(() => {
const filterSourceFn = (mod: ModDetails) => {
if (filterOptions.source === window.location.host) {
return mod.rTag === filterOptions.source
}
return true
}
// If search term is missing, only filter by sources
if (searchTerm === '') return mods.filter(filterSourceFn)
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
return mods.filter(filterFn).filter(filterSourceFn)
}, [filterOptions.source, mods, searchTerm])
const filteredModList = useFilteredMods(
filteredMods,
userState, userState,
filterOptions, filterOptions,
nsfwList, nsfwList,
@ -54,11 +107,11 @@ export const GamePage = () => {
) )
// Pagination logic // Pagination logic
const totalGames = filteredMods.length const totalGames = filteredModList.length
const totalPages = Math.ceil(totalGames / MAX_MODS_PER_PAGE) const totalPages = Math.ceil(totalGames / MAX_MODS_PER_PAGE)
const startIndex = (currentPage - 1) * MAX_MODS_PER_PAGE const startIndex = (currentPage - 1) * MAX_MODS_PER_PAGE
const endIndex = startIndex + MAX_MODS_PER_PAGE const endIndex = startIndex + MAX_MODS_PER_PAGE
const currentMods = filteredMods.slice(startIndex, endIndex) const currentMods = filteredModList.slice(startIndex, endIndex)
const handlePageChange = (page: number) => { const handlePageChange = (page: number) => {
if (page >= 1 && page <= totalPages) { if (page >= 1 && page <= totalPages) {
@ -116,8 +169,21 @@ export const GamePage = () => {
<span className='IBMSMTitleMainHeadingSpan'> <span className='IBMSMTitleMainHeadingSpan'>
{gameName} {gameName}
</span> </span>
{searchTerm !== '' && (
<>
&nbsp;&mdash;&nbsp;
<span className='IBMSMTitleMainHeadingSpan'>
{searchTerm}
</span>
</>
)}
</h2> </h2>
</div> </div>
<SearchInput
handleKeyDown={handleKeyDown}
handleSearch={handleSearch}
ref={searchTermRef}
/>
</div> </div>
</div> </div>
<ModFilter <ModFilter