import { NDKFilter, NDKKind, NDKSubscriptionCacheUsage } from '@nostr-dev-kit/ndk' import { ModCard } from 'components/ModCard' import { ModFilter } from 'components/ModsFilter' import { PaginationWithPageNumbers } from 'components/Pagination' import { SearchInput } from 'components/SearchInput' import { MAX_MODS_PER_PAGE, T_TAG_VALUE } from 'constants.ts' import { useAppSelector, useFilteredMods, useMuteLists, useNDKContext, useNSFWList } from 'hooks' import { useEffect, useMemo, useRef, useState } from 'react' import { useParams, useSearchParams } from 'react-router-dom' import { FilterOptions, ModDetails, ModeratedFilter, NSFWFilter, SortBy } from 'types' import { extractModData, isModDataComplete, scrollIntoView } from 'utils' export const GamePage = () => { const scrollTargetRef = useRef(null) const params = useParams() const { name: gameName } = params const { ndk } = useNDKContext() const muteLists = useMuteLists() const nsfwList = useNSFWList() const [filterOptions, setFilterOptions] = useState({ sort: SortBy.Latest, nsfw: NSFWFilter.Hide_NSFW, source: window.location.host, moderated: ModeratedFilter.Moderated }) const [mods, setMods] = useState([]) const [currentPage, setCurrentPage] = useState(1) const userState = useAppSelector((state) => state.user) // Search const searchTermRef = useRef(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) => { 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, filterOptions, nsfwList, muteLists ) // Pagination logic const totalGames = filteredModList.length const totalPages = Math.ceil(totalGames / MAX_MODS_PER_PAGE) const startIndex = (currentPage - 1) * MAX_MODS_PER_PAGE const endIndex = startIndex + MAX_MODS_PER_PAGE const currentMods = filteredModList.slice(startIndex, endIndex) const handlePageChange = (page: number) => { if (page >= 1 && page <= totalPages) { scrollIntoView(scrollTargetRef.current) setCurrentPage(page) } } useEffect(() => { const filter: NDKFilter = { kinds: [NDKKind.Classified], '#t': [T_TAG_VALUE] } const subscription = ndk.subscribe(filter, { cacheUsage: NDKSubscriptionCacheUsage.PARALLEL, closeOnEose: true }) subscription.on('event', (ndkEvent) => { if (isModDataComplete(ndkEvent)) { const mod = extractModData(ndkEvent) if (mod.game === gameName) setMods((prev) => { if (prev.find((e) => e.aTag === mod.aTag)) return [...prev] return [...prev, mod] }) } }) subscription.start() // Cleanup function to stop subscription return () => { subscription.stop() } }, [gameName, ndk]) if (!gameName) return null return ( <>

Game:  {gameName} {searchTerm !== '' && ( <>  —  {searchTerm} )}

{currentMods.map((mod) => ( ))}
) }