degmods.com/src/pages/mods.tsx

194 lines
5.4 KiB
TypeScript
Raw Normal View History

2024-10-07 15:45:21 +05:00
import { ModFilter } from 'components/ModsFilter'
2024-09-18 21:40:34 +05:00
import { Pagination } from 'components/Pagination'
2024-10-07 15:45:21 +05:00
import React, { useCallback, useEffect, useRef, useState } from 'react'
import { createSearchParams, useNavigate } from 'react-router-dom'
2024-08-06 15:46:38 +05:00
import { LoadingSpinner } from '../components/LoadingSpinner'
import { ModCard } from '../components/ModCard'
2024-09-18 21:40:34 +05:00
import { MOD_FILTER_LIMIT } from '../constants'
2024-10-07 15:45:21 +05:00
import {
useAppSelector,
useFilteredMods,
useMuteLists,
useNSFWList
} from '../hooks'
import { appRoutes } from '../routes'
2024-07-11 17:52:48 +05:00
import '../styles/filters.css'
import '../styles/pagination.css'
import '../styles/search.css'
2024-08-06 15:46:38 +05:00
import '../styles/styles.css'
2024-10-07 15:45:21 +05:00
import {
FilterOptions,
ModDetails,
ModeratedFilter,
NSFWFilter,
SortBy
} from '../types'
2024-08-06 15:46:38 +05:00
import { fetchMods } from '../utils'
2024-07-11 17:52:48 +05:00
export const ModsPage = () => {
2024-08-06 15:46:38 +05:00
const [isFetching, setIsFetching] = useState(false)
const [mods, setMods] = useState<ModDetails[]>([])
2024-08-06 15:46:38 +05:00
const [filterOptions, setFilterOptions] = useState<FilterOptions>({
sort: SortBy.Latest,
nsfw: NSFWFilter.Hide_NSFW,
source: window.location.host,
moderated: ModeratedFilter.Moderated
})
2024-09-18 21:40:34 +05:00
const muteLists = useMuteLists()
const nsfwList = useNSFWList()
2024-08-06 15:46:38 +05:00
const [page, setPage] = useState(1)
const userState = useAppSelector((state) => state.user)
2024-08-06 15:46:38 +05:00
useEffect(() => {
setIsFetching(true)
2024-09-02 13:47:16 +05:00
fetchMods({ source: filterOptions.source })
2024-08-06 15:46:38 +05:00
.then((res) => {
setMods(res)
})
2024-08-06 15:46:38 +05:00
.finally(() => {
setIsFetching(false)
})
}, [filterOptions.source])
const handleNext = useCallback(() => {
setIsFetching(true)
const until =
mods.length > 0 ? mods[mods.length - 1].published_at - 1 : undefined
2024-08-06 15:46:38 +05:00
2024-09-02 13:47:16 +05:00
fetchMods({
source: filterOptions.source,
until
})
2024-08-06 15:46:38 +05:00
.then((res) => {
setMods(res)
setPage((prev) => prev + 1)
})
.finally(() => {
setIsFetching(false)
})
2024-08-06 15:46:38 +05:00
}, [filterOptions.source, mods])
const handlePrev = useCallback(() => {
setIsFetching(true)
const since = mods.length > 0 ? mods[0].published_at + 1 : undefined
2024-08-06 15:46:38 +05:00
2024-09-02 13:47:16 +05:00
fetchMods({
source: filterOptions.source,
since
})
2024-08-06 15:46:38 +05:00
.then((res) => {
setMods(res)
setPage((prev) => prev - 1)
})
.finally(() => {
setIsFetching(false)
})
}, [filterOptions.source, mods])
2024-10-07 15:45:21 +05:00
const filteredModList = useFilteredMods(
2024-08-06 15:46:38 +05:00
mods,
2024-10-07 15:45:21 +05:00
userState,
filterOptions,
nsfwList,
muteLists
)
2024-07-11 17:52:48 +05:00
return (
2024-08-06 15:46:38 +05:00
<>
{isFetching && <LoadingSpinner desc='Fetching mod details from relays' />}
<div className='InnerBodyMain'>
<div className='ContainerMain'>
<div className='IBMSecMainGroup IBMSecMainGroupAlt'>
<PageTitleRow />
2024-10-07 15:45:21 +05:00
<ModFilter
2024-08-06 15:46:38 +05:00
filterOptions={filterOptions}
setFilterOptions={setFilterOptions}
/>
<div className='IBMSecMain IBMSMListWrapper'>
<div className='IBMSMList'>
{filteredModList.map((mod) => (
<ModCard key={mod.id} {...mod} />
))}
2024-08-06 15:46:38 +05:00
</div>
2024-07-11 17:52:48 +05:00
</div>
2024-08-06 15:46:38 +05:00
<Pagination
page={page}
2024-08-08 21:56:30 +05:00
disabledNext={mods.length < MOD_FILTER_LIMIT}
2024-08-06 15:46:38 +05:00
handlePrev={handlePrev}
handleNext={handleNext}
/>
</div>
</div>
</div>
2024-08-06 15:46:38 +05:00
</>
)
}
2024-08-06 15:46:38 +05:00
const PageTitleRow = React.memo(() => {
const navigate = useNavigate()
const searchTermRef = useRef<HTMLInputElement>(null)
const handleSearch = () => {
const value = searchTermRef.current?.value || '' // Access the input value from the ref
if (value !== '') {
const searchParams = createSearchParams({
searchTerm: value,
searching: 'Mods'
})
navigate({ pathname: appRoutes.search, search: `?${searchParams}` })
}
}
// Handle "Enter" key press inside the input
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Enter') {
handleSearch()
}
}
return (
<div className='IBMSecMain'>
<div className='SearchMainWrapper'>
<div className='IBMSMTitleMain'>
<h2 className='IBMSMTitleMainHeading'>Mods</h2>
</div>
<div className='SearchMain'>
<div className='SearchMainInside'>
<div className='SearchMainInsideWrapper'>
<input
type='text'
className='SMIWInput'
ref={searchTermRef}
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>
2024-07-11 17:52:48 +05:00
</div>
</div>
</div>
</div>
</div>
)
2024-08-06 15:46:38 +05:00
})