import { nip19 } from 'nostr-tools' import { useMemo, useState } from 'react' import { useNavigate } from 'react-router-dom' import { A11y, Autoplay, Navigation, Pagination } from 'swiper/modules' import { Swiper, SwiperSlide } from 'swiper/react' import { BlogCard } from '../components/BlogCard' import { GameCard } from '../components/GameCard' import { ModCard } from '../components/ModCard' import { LANDING_PAGE_DATA } from '../constants' import { useAppSelector, useDidMount, useGames, useMuteLists, useNDKContext, useNSFWList } from '../hooks' import { appRoutes, getModPageRoute } from '../routes' import { ModDetails } from '../types' import { extractModData, handleModImageError, log, LogType } from '../utils' import '../styles/cardLists.css' import '../styles/SimpleSlider.css' import '../styles/styles.css' // Import Swiper styles import { NDKFilter } from '@nostr-dev-kit/ndk' import 'swiper/css' import 'swiper/css/navigation' import 'swiper/css/pagination' import placeholder from '../assets/img/DEGMods Placeholder Img.png' export const HomePage = () => { const navigate = useNavigate() const games = useGames() const featuredGames = useMemo(() => { return games.filter((game) => LANDING_PAGE_DATA.featuredGames.includes(game['Game Name']) ) }, [games]) return ( <div className='InnerBodyMain'> <div className='SliderWrapper'> <div className='ContainerMain'> <div className='IBMSecMain'> <div className='simple-slider IBMSMSlider'> <Swiper className='swiper-container IBMSMSliderContainer' wrapperClass='swiper-wrapper IBMSMSliderContainerWrapper' modules={[Navigation, Pagination, A11y, Autoplay]} pagination={{ clickable: true, dynamicBullets: true }} slidesPerView={1} autoplay={{ delay: 5000 }} speed={1000} navigation loop > {LANDING_PAGE_DATA.featuredSlider.map((naddr) => ( <SwiperSlide key={naddr} className='swiper-slide IBMSMSliderContainerWrapperSlider' > <SlideContent naddr={naddr} /> </SwiperSlide> ))} </Swiper> </div> </div> </div> </div> <div className='ContainerMain'> <div className='IBMSecMainGroup IBMSecMainGroupAlt'> <div className='IBMSecMain IBMSMListWrapper'> <div className='IBMSMTitleMain'> <h2 className='IBMSMTitleMainHeading'>Cool Games</h2> </div> <div className='IBMSMList IBMSMListFeaturedAlt'> {featuredGames.map((game) => ( <GameCard key={game['Game Name']} title={game['Game Name']} imageUrl={game['Boxart image']} /> ))} </div> <div className='IBMSMAction'> <a className='btn btnMain IBMSMActionBtn' role='button' onClick={() => navigate(appRoutes.games)} > View All </a> </div> </div> <div className='IBMSecMain IBMSMListWrapper'> <div className='IBMSMTitleMain'> <h2 className='IBMSMTitleMainHeading'>Awesome Mods</h2> </div> <div className='IBMSMList IBMSMListAlt'> {LANDING_PAGE_DATA.awesomeMods.map((naddr) => ( <DisplayMod key={naddr} naddr={naddr} /> ))} </div> <div className='IBMSMAction'> <a className='btn btnMain IBMSMActionBtn' role='button' onClick={() => navigate(appRoutes.mods)} > View All </a> </div> </div> <DisplayLatestMods /> <div className='IBMSecMain IBMSMListWrapper'> <div className='IBMSMTitleMain'> <h2 className='IBMSMTitleMainHeading'>Blog Posts (WIP)</h2> </div> <div className='IBMSMList'> <BlogCard backgroundLink={placeholder} /> <BlogCard backgroundLink={placeholder} /> <BlogCard backgroundLink={placeholder} /> <BlogCard backgroundLink={placeholder} /> </div> <div className='IBMSMAction'> <a className='btn btnMain IBMSMActionBtn' role='button' href='blog.html' > View All </a> </div> </div> </div> </div> </div> ) } type SlideContentProps = { naddr: string } const SlideContent = ({ naddr }: SlideContentProps) => { const navigate = useNavigate() const { fetchEvent } = useNDKContext() const [mod, setMod] = useState<ModDetails>() useDidMount(() => { const decoded = nip19.decode<'naddr'>(naddr as `naddr1${string}`) const { identifier, kind, pubkey } = decoded.data const ndkFilter: NDKFilter = { '#a': [identifier], authors: [pubkey], kinds: [kind] } fetchEvent(ndkFilter) .then((ndkEvent) => { if (ndkEvent) { const extracted = extractModData(ndkEvent) setMod(extracted) } }) .catch((err) => { log( true, LogType.Error, 'An error occurred in fetching mod details from relays', err ) }) }) if (!mod) return <Spinner /> return ( <> <div className='IBMSMSCWSPicWrapper'> <img src={mod.featuredImageUrl} onError={handleModImageError} className='IBMSMSCWSPic' /> </div> <div className='IBMSMSCWSInfo'> <h3 className='IBMSMSCWSInfoHeading'>{mod.title}</h3> <div className='IBMSMSCWSInfoTextWrapper'> <p className='IBMSMSCWSInfoText'> {mod.summary} <br /> </p> </div> <p className='IBMSMSCWSInfoText IBMSMSCWSInfoText2'> {mod.game} <br /> </p> <div className='IBMSMSliderContainerWrapperSliderAction'> <a className='btn btnMain IBMSMSliderContainerWrapperSliderActionbtn' role='button' onClick={() => navigate(getModPageRoute(naddr))} > Check it out </a> </div> </div> </> ) } type DisplayModProps = { naddr: string } const DisplayMod = ({ naddr }: DisplayModProps) => { const [mod, setMod] = useState<ModDetails>() const { fetchEvent } = useNDKContext() useDidMount(() => { const decoded = nip19.decode<'naddr'>(naddr as `naddr1${string}`) const { identifier, kind, pubkey } = decoded.data const ndkFilter: NDKFilter = { '#a': [identifier], authors: [pubkey], kinds: [kind] } fetchEvent(ndkFilter) .then((ndkEvent) => { if (ndkEvent) { const extracted = extractModData(ndkEvent) setMod(extracted) } }) .catch((err) => { log( true, LogType.Error, 'An error occurred in fetching mod details from relays', err ) }) }) if (!mod) return <Spinner /> return <ModCard {...mod} /> } const DisplayLatestMods = () => { const navigate = useNavigate() const { fetchMods } = useNDKContext() const { siteWot, userWot } = useAppSelector((state) => state.wot) const [isFetchingLatestMods, setIsFetchingLatestMods] = useState(true) const [latestMods, setLatestMods] = useState<ModDetails[]>([]) const muteLists = useMuteLists() const nsfwList = useNSFWList() useDidMount(() => { fetchMods({ source: window.location.host }) .then((mods) => { const wotFilteredMods = mods.filter( (mod) => siteWot.includes(mod.author) || userWot.includes(mod.author) ) setLatestMods(wotFilteredMods) }) .finally(() => { setIsFetchingLatestMods(false) }) }) const filteredMods = useMemo(() => { const mutedAuthors = [...muteLists.admin.authors, ...muteLists.user.authors] const mutedEvents = [ ...muteLists.admin.replaceableEvents, ...muteLists.user.replaceableEvents ] const filtered = latestMods.filter( (mod) => !mutedAuthors.includes(mod.author) && !mutedEvents.includes(mod.aTag) && !nsfwList.includes(mod.aTag) && !mod.nsfw ) return filtered.slice(0, 4) }, [muteLists, nsfwList, latestMods]) return ( <div className='IBMSecMain IBMSMListWrapper'> <div className='IBMSMTitleMain'> <h2 className='IBMSMTitleMainHeading'>Latest Mods</h2> </div> <div className='IBMSMList'> {isFetchingLatestMods ? ( <Spinner /> ) : ( filteredMods.map((mod) => { return <ModCard key={mod.id} {...mod} /> }) )} </div> <div className='IBMSMAction'> <a className='btn btnMain IBMSMActionBtn' role='button' onClick={() => navigate(appRoutes.mods)} > View All </a> </div> </div> ) } const Spinner = () => { return ( <div className='spinner'> <div className='spinnerCircle'></div> </div> ) }