user profile btn in social nav now only is active for current logged in user, added NSFW tag for admin tagged ones, mod search under a specific game, search term and some filters added to url, filter state is saved locally in cache, user search now works #108
39
src/components/SearchInput.tsx
Normal file
39
src/components/SearchInput.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
)
|
@ -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 !== '' && (
|
||||||
|
<>
|
||||||
|
—
|
||||||
|
<span className='IBMSMTitleMainHeadingSpan'>
|
||||||
|
{searchTerm}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
<SearchInput
|
||||||
|
handleKeyDown={handleKeyDown}
|
||||||
|
handleSearch={handleSearch}
|
||||||
|
ref={searchTermRef}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ModFilter
|
<ModFilter
|
||||||
|
Loading…
Reference in New Issue
Block a user