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'
|
2024-09-23 13:55:18 +05:00
|
|
|
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,
|
2024-10-14 13:24:43 +05:00
|
|
|
useNDKContext,
|
2024-10-07 15:45:21 +05:00
|
|
|
useNSFWList
|
|
|
|
} from '../hooks'
|
2024-09-23 20:58:50 +05:00
|
|
|
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-10-21 15:21:09 +02:00
|
|
|
import { scrollIntoView } from 'utils'
|
2024-10-29 09:39:30 +01:00
|
|
|
import { SearchInput } from 'components/SearchInput'
|
2024-07-29 11:26:26 +05:00
|
|
|
|
2024-07-11 17:52:48 +05:00
|
|
|
export const ModsPage = () => {
|
2024-10-21 15:21:09 +02:00
|
|
|
const scrollTargetRef = useRef<HTMLDivElement>(null)
|
2024-10-14 13:24:43 +05:00
|
|
|
const { fetchMods } = useNDKContext()
|
2024-08-06 15:46:38 +05:00
|
|
|
const [isFetching, setIsFetching] = useState(false)
|
2024-07-29 11:26:26 +05:00
|
|
|
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()
|
2024-10-04 00:40:36 +05:00
|
|
|
const nsfwList = useNSFWList()
|
2024-08-06 15:46:38 +05:00
|
|
|
|
|
|
|
const [page, setPage] = useState(1)
|
2024-07-29 11:26:26 +05:00
|
|
|
|
2024-08-28 22:03:43 +05:00
|
|
|
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-07-29 11:26:26 +05:00
|
|
|
})
|
2024-08-06 15:46:38 +05:00
|
|
|
.finally(() => {
|
|
|
|
setIsFetching(false)
|
|
|
|
})
|
2024-10-14 13:24:43 +05:00
|
|
|
}, [filterOptions.source, fetchMods])
|
2024-08-06 15:46:38 +05:00
|
|
|
|
|
|
|
const handleNext = useCallback(() => {
|
|
|
|
setIsFetching(true)
|
|
|
|
|
|
|
|
const until =
|
2024-08-27 20:46:42 +05:00
|
|
|
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)
|
2024-10-21 15:21:09 +02:00
|
|
|
scrollIntoView(scrollTargetRef.current)
|
2024-07-29 11:26:26 +05:00
|
|
|
})
|
|
|
|
.finally(() => {
|
|
|
|
setIsFetching(false)
|
|
|
|
})
|
2024-10-14 13:24:43 +05:00
|
|
|
}, [filterOptions.source, mods, fetchMods])
|
2024-08-06 15:46:38 +05:00
|
|
|
|
|
|
|
const handlePrev = useCallback(() => {
|
|
|
|
setIsFetching(true)
|
2024-07-29 11:26:26 +05:00
|
|
|
|
2024-08-27 20:46:42 +05:00
|
|
|
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)
|
2024-10-21 15:21:09 +02:00
|
|
|
scrollIntoView(scrollTargetRef.current)
|
2024-08-06 15:46:38 +05:00
|
|
|
})
|
|
|
|
.finally(() => {
|
|
|
|
setIsFetching(false)
|
|
|
|
})
|
2024-10-14 13:24:43 +05:00
|
|
|
}, [filterOptions.source, mods, fetchMods])
|
2024-08-06 15:46:38 +05:00
|
|
|
|
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-29 11:26:26 +05:00
|
|
|
|
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'>
|
2024-10-21 15:21:09 +02:00
|
|
|
<div
|
|
|
|
className='IBMSecMainGroup IBMSecMainGroupAlt'
|
|
|
|
ref={scrollTargetRef}
|
|
|
|
>
|
2024-08-06 15:46:38 +05:00
|
|
|
<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'>
|
2024-09-23 20:58:50 +05:00
|
|
|
{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>
|
2024-07-29 11:26:26 +05:00
|
|
|
</div>
|
|
|
|
</div>
|
2024-08-06 15:46:38 +05:00
|
|
|
</>
|
2024-07-29 11:26:26 +05:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-08-06 15:46:38 +05:00
|
|
|
const PageTitleRow = React.memo(() => {
|
2024-09-23 13:55:18 +05:00
|
|
|
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({
|
2024-10-29 09:39:30 +01:00
|
|
|
q: value,
|
|
|
|
kind: 'Mods'
|
2024-09-23 13:55:18 +05:00
|
|
|
})
|
|
|
|
navigate({ pathname: appRoutes.search, search: `?${searchParams}` })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle "Enter" key press inside the input
|
|
|
|
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
|
|
if (event.key === 'Enter') {
|
|
|
|
handleSearch()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-29 11:26:26 +05:00
|
|
|
return (
|
|
|
|
<div className='IBMSecMain'>
|
|
|
|
<div className='SearchMainWrapper'>
|
|
|
|
<div className='IBMSMTitleMain'>
|
|
|
|
<h2 className='IBMSMTitleMainHeading'>Mods</h2>
|
|
|
|
</div>
|
2024-10-29 09:39:30 +01:00
|
|
|
<SearchInput
|
|
|
|
ref={searchTermRef}
|
|
|
|
handleKeyDown={handleKeyDown}
|
|
|
|
handleSearch={handleSearch}
|
|
|
|
/>
|
2024-07-29 11:26:26 +05:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)
|
2024-08-06 15:46:38 +05:00
|
|
|
})
|