2024-11-05 16:22:08 +01:00
|
|
|
import { kinds, nip19 } from 'nostr-tools'
|
2024-09-23 13:56:51 +05:00
|
|
|
import { useMemo, useState } from 'react'
|
2024-11-13 14:30:58 +01:00
|
|
|
import { Link, useNavigate, useNavigation } from 'react-router-dom'
|
2024-09-23 20:58:50 +05:00
|
|
|
import { A11y, Autoplay, Navigation, Pagination } from 'swiper/modules'
|
2024-09-02 13:47:16 +05:00
|
|
|
import { Swiper, SwiperSlide } from 'swiper/react'
|
2024-07-11 18:34:12 +05:00
|
|
|
import { BlogCard } from '../components/BlogCard'
|
2024-07-11 17:15:03 +05:00
|
|
|
import { GameCard } from '../components/GameCard'
|
2024-07-11 17:52:48 +05:00
|
|
|
import { ModCard } from '../components/ModCard'
|
2024-11-12 20:15:27 +01:00
|
|
|
import { LANDING_PAGE_DATA, PROFILE_BLOG_FILTER_LIMIT } from '../constants'
|
2024-10-14 13:24:43 +05:00
|
|
|
import {
|
2024-11-11 22:37:49 +05:00
|
|
|
useAppSelector,
|
2024-10-14 13:24:43 +05:00
|
|
|
useDidMount,
|
|
|
|
useGames,
|
2024-11-06 13:12:19 +01:00
|
|
|
useLocalStorage,
|
2024-10-14 13:24:43 +05:00
|
|
|
useMuteLists,
|
|
|
|
useNDKContext,
|
|
|
|
useNSFWList
|
|
|
|
} from '../hooks'
|
2024-09-03 15:13:51 +05:00
|
|
|
import { appRoutes, getModPageRoute } from '../routes'
|
2024-11-06 13:12:19 +01:00
|
|
|
import { BlogCardDetails, ModDetails, NSFWFilter, SortBy } from '../types'
|
2024-11-05 16:22:08 +01:00
|
|
|
import {
|
2024-11-06 13:12:19 +01:00
|
|
|
extractBlogCardDetails,
|
2024-11-05 16:22:08 +01:00
|
|
|
extractModData,
|
|
|
|
handleModImageError,
|
|
|
|
log,
|
|
|
|
LogType,
|
|
|
|
npubToHex
|
|
|
|
} from '../utils'
|
2024-09-02 13:47:16 +05:00
|
|
|
|
2024-07-11 16:19:12 +05:00
|
|
|
import '../styles/cardLists.css'
|
|
|
|
import '../styles/SimpleSlider.css'
|
|
|
|
import '../styles/styles.css'
|
|
|
|
|
2024-09-02 13:47:16 +05:00
|
|
|
// Import Swiper styles
|
2024-11-13 10:58:30 +01:00
|
|
|
import { NDKFilter } from '@nostr-dev-kit/ndk'
|
2024-09-02 13:47:16 +05:00
|
|
|
import 'swiper/css'
|
|
|
|
import 'swiper/css/navigation'
|
|
|
|
import 'swiper/css/pagination'
|
2024-11-13 14:30:58 +01:00
|
|
|
import { LoadingSpinner } from 'components/LoadingSpinner'
|
2024-11-14 11:29:01 +01:00
|
|
|
import { Spinner } from 'components/Spinner'
|
2024-11-18 23:21:05 +05:00
|
|
|
import { isInWoT } from 'utils/wot'
|
2024-09-02 13:47:16 +05:00
|
|
|
|
2024-07-11 16:19:12 +05:00
|
|
|
export const HomePage = () => {
|
2024-09-02 13:47:16 +05:00
|
|
|
const navigate = useNavigate()
|
2024-09-23 13:56:51 +05:00
|
|
|
const games = useGames()
|
|
|
|
|
|
|
|
const featuredGames = useMemo(() => {
|
|
|
|
return games.filter((game) =>
|
|
|
|
LANDING_PAGE_DATA.featuredGames.includes(game['Game Name'])
|
|
|
|
)
|
|
|
|
}, [games])
|
|
|
|
|
2024-07-11 16:19:12 +05:00
|
|
|
return (
|
|
|
|
<div className='InnerBodyMain'>
|
|
|
|
<div className='SliderWrapper'>
|
|
|
|
<div className='ContainerMain'>
|
|
|
|
<div className='IBMSecMain'>
|
|
|
|
<div className='simple-slider IBMSMSlider'>
|
2024-09-02 13:47:16 +05:00
|
|
|
<Swiper
|
|
|
|
className='swiper-container IBMSMSliderContainer'
|
|
|
|
wrapperClass='swiper-wrapper IBMSMSliderContainerWrapper'
|
2024-09-02 16:04:18 +00:00
|
|
|
modules={[Navigation, Pagination, A11y, Autoplay]}
|
2024-09-02 13:47:16 +05:00
|
|
|
pagination={{ clickable: true, dynamicBullets: true }}
|
|
|
|
slidesPerView={1}
|
|
|
|
autoplay={{ delay: 5000 }}
|
|
|
|
speed={1000}
|
|
|
|
navigation
|
|
|
|
loop
|
|
|
|
>
|
|
|
|
{LANDING_PAGE_DATA.featuredSlider.map((naddr) => (
|
2024-09-10 22:38:14 +05:00
|
|
|
<SwiperSlide
|
|
|
|
key={naddr}
|
|
|
|
className='swiper-slide IBMSMSliderContainerWrapperSlider'
|
|
|
|
>
|
2024-09-02 13:47:16 +05:00
|
|
|
<SlideContent naddr={naddr} />
|
|
|
|
</SwiperSlide>
|
|
|
|
))}
|
|
|
|
</Swiper>
|
2024-07-11 16:19:12 +05:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className='ContainerMain'>
|
|
|
|
<div className='IBMSecMainGroup IBMSecMainGroupAlt'>
|
|
|
|
<div className='IBMSecMain IBMSMListWrapper'>
|
|
|
|
<div className='IBMSMTitleMain'>
|
2024-09-02 13:47:16 +05:00
|
|
|
<h2 className='IBMSMTitleMainHeading'>Cool Games</h2>
|
2024-07-11 16:19:12 +05:00
|
|
|
</div>
|
|
|
|
<div className='IBMSMList IBMSMListFeaturedAlt'>
|
2024-09-23 13:56:51 +05:00
|
|
|
{featuredGames.map((game) => (
|
2024-09-10 22:38:14 +05:00
|
|
|
<GameCard
|
2024-09-23 13:56:51 +05:00
|
|
|
key={game['Game Name']}
|
|
|
|
title={game['Game Name']}
|
|
|
|
imageUrl={game['Boxart image']}
|
2024-09-10 22:38:14 +05:00
|
|
|
/>
|
2024-09-02 13:47:16 +05:00
|
|
|
))}
|
2024-07-11 16:19:12 +05:00
|
|
|
</div>
|
|
|
|
<div className='IBMSMAction'>
|
|
|
|
<a
|
|
|
|
className='btn btnMain IBMSMActionBtn'
|
|
|
|
role='button'
|
2024-09-02 13:47:16 +05:00
|
|
|
onClick={() => navigate(appRoutes.games)}
|
2024-07-11 16:19:12 +05:00
|
|
|
>
|
|
|
|
View All
|
|
|
|
</a>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className='IBMSecMain IBMSMListWrapper'>
|
|
|
|
<div className='IBMSMTitleMain'>
|
2024-09-02 13:47:16 +05:00
|
|
|
<h2 className='IBMSMTitleMainHeading'>Awesome Mods</h2>
|
2024-07-11 16:19:12 +05:00
|
|
|
</div>
|
|
|
|
<div className='IBMSMList IBMSMListAlt'>
|
2024-09-02 13:47:16 +05:00
|
|
|
{LANDING_PAGE_DATA.awesomeMods.map((naddr) => (
|
|
|
|
<DisplayMod key={naddr} naddr={naddr} />
|
|
|
|
))}
|
2024-07-11 16:19:12 +05:00
|
|
|
</div>
|
|
|
|
<div className='IBMSMAction'>
|
|
|
|
<a
|
|
|
|
className='btn btnMain IBMSMActionBtn'
|
|
|
|
role='button'
|
2024-09-02 13:47:16 +05:00
|
|
|
onClick={() => navigate(appRoutes.mods)}
|
2024-07-11 16:19:12 +05:00
|
|
|
>
|
|
|
|
View All
|
|
|
|
</a>
|
|
|
|
</div>
|
|
|
|
</div>
|
2024-09-02 13:47:16 +05:00
|
|
|
<DisplayLatestMods />
|
2024-11-05 16:22:08 +01:00
|
|
|
<DisplayLatestBlogs />
|
2024-07-11 16:19:12 +05:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
2024-09-02 13:47:16 +05:00
|
|
|
|
|
|
|
type SlideContentProps = {
|
|
|
|
naddr: string
|
|
|
|
}
|
|
|
|
|
|
|
|
const SlideContent = ({ naddr }: SlideContentProps) => {
|
|
|
|
const navigate = useNavigate()
|
2024-10-14 19:20:43 +05:00
|
|
|
const { fetchEvent } = useNDKContext()
|
2024-09-02 13:47:16 +05:00
|
|
|
const [mod, setMod] = useState<ModDetails>()
|
|
|
|
|
|
|
|
useDidMount(() => {
|
|
|
|
const decoded = nip19.decode<'naddr'>(naddr as `naddr1${string}`)
|
2024-10-21 17:51:00 +05:00
|
|
|
const { identifier, kind, pubkey } = decoded.data
|
2024-09-02 13:47:16 +05:00
|
|
|
|
2024-10-14 13:24:43 +05:00
|
|
|
const ndkFilter: NDKFilter = {
|
2024-09-02 13:47:16 +05:00
|
|
|
'#a': [identifier],
|
|
|
|
authors: [pubkey],
|
|
|
|
kinds: [kind]
|
|
|
|
}
|
|
|
|
|
2024-10-21 17:51:00 +05:00
|
|
|
fetchEvent(ndkFilter)
|
2024-10-14 19:20:43 +05:00
|
|
|
.then((ndkEvent) => {
|
|
|
|
if (ndkEvent) {
|
2024-10-14 13:24:43 +05:00
|
|
|
const extracted = extractModData(ndkEvent)
|
2024-09-02 13:47:16 +05:00
|
|
|
setMod(extracted)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
log(
|
|
|
|
true,
|
|
|
|
LogType.Error,
|
|
|
|
'An error occurred in fetching mod details from relays',
|
|
|
|
err
|
|
|
|
)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
if (!mod) return <Spinner />
|
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
2024-09-02 09:03:41 +00:00
|
|
|
<div className='IBMSMSCWSPicWrapper'>
|
|
|
|
<img
|
|
|
|
src={mod.featuredImageUrl}
|
|
|
|
onError={handleModImageError}
|
|
|
|
className='IBMSMSCWSPic'
|
|
|
|
/>
|
|
|
|
</div>
|
2024-09-02 13:47:16 +05:00
|
|
|
<div className='IBMSMSCWSInfo'>
|
|
|
|
<h3 className='IBMSMSCWSInfoHeading'>{mod.title}</h3>
|
2024-09-17 11:45:05 +00:00
|
|
|
<div className='IBMSMSCWSInfoTextWrapper'>
|
|
|
|
<p className='IBMSMSCWSInfoText'>
|
|
|
|
{mod.summary}
|
|
|
|
<br />
|
|
|
|
</p>
|
|
|
|
</div>
|
2024-09-03 17:56:33 +00:00
|
|
|
<p className='IBMSMSCWSInfoText IBMSMSCWSInfoText2'>
|
2024-09-05 12:46:14 +05:00
|
|
|
{mod.game}
|
2024-09-03 17:56:33 +00:00
|
|
|
<br />
|
|
|
|
</p>
|
2024-09-02 13:47:16 +05:00
|
|
|
<div className='IBMSMSliderContainerWrapperSliderAction'>
|
|
|
|
<a
|
|
|
|
className='btn btnMain IBMSMSliderContainerWrapperSliderActionbtn'
|
|
|
|
role='button'
|
2024-09-03 15:13:51 +05:00
|
|
|
onClick={() => navigate(getModPageRoute(naddr))}
|
2024-09-02 13:47:16 +05:00
|
|
|
>
|
|
|
|
Check it out
|
|
|
|
</a>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
type DisplayModProps = {
|
|
|
|
naddr: string
|
|
|
|
}
|
|
|
|
|
|
|
|
const DisplayMod = ({ naddr }: DisplayModProps) => {
|
|
|
|
const [mod, setMod] = useState<ModDetails>()
|
|
|
|
|
2024-10-14 19:20:43 +05:00
|
|
|
const { fetchEvent } = useNDKContext()
|
2024-10-14 13:24:43 +05:00
|
|
|
|
2024-09-02 13:47:16 +05:00
|
|
|
useDidMount(() => {
|
|
|
|
const decoded = nip19.decode<'naddr'>(naddr as `naddr1${string}`)
|
2024-10-21 17:51:00 +05:00
|
|
|
const { identifier, kind, pubkey } = decoded.data
|
2024-09-02 13:47:16 +05:00
|
|
|
|
2024-10-14 13:24:43 +05:00
|
|
|
const ndkFilter: NDKFilter = {
|
2024-09-02 13:47:16 +05:00
|
|
|
'#a': [identifier],
|
|
|
|
authors: [pubkey],
|
|
|
|
kinds: [kind]
|
|
|
|
}
|
|
|
|
|
2024-10-21 17:51:00 +05:00
|
|
|
fetchEvent(ndkFilter)
|
2024-10-14 19:20:43 +05:00
|
|
|
.then((ndkEvent) => {
|
|
|
|
if (ndkEvent) {
|
2024-10-14 13:24:43 +05:00
|
|
|
const extracted = extractModData(ndkEvent)
|
2024-09-02 13:47:16 +05:00
|
|
|
setMod(extracted)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
log(
|
|
|
|
true,
|
|
|
|
LogType.Error,
|
|
|
|
'An error occurred in fetching mod details from relays',
|
|
|
|
err
|
|
|
|
)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
if (!mod) return <Spinner />
|
|
|
|
|
2024-09-23 20:58:50 +05:00
|
|
|
return <ModCard {...mod} />
|
2024-09-02 13:47:16 +05:00
|
|
|
}
|
|
|
|
|
|
|
|
const DisplayLatestMods = () => {
|
|
|
|
const navigate = useNavigate()
|
2024-10-14 13:24:43 +05:00
|
|
|
const { fetchMods } = useNDKContext()
|
2024-11-18 23:21:05 +05:00
|
|
|
const { siteWot, siteWotLevel, userWot, userWotLevel } = useAppSelector(
|
|
|
|
(state) => state.wot
|
|
|
|
)
|
2024-09-02 13:47:16 +05:00
|
|
|
const [isFetchingLatestMods, setIsFetchingLatestMods] = useState(true)
|
|
|
|
const [latestMods, setLatestMods] = useState<ModDetails[]>([])
|
|
|
|
|
2024-09-30 15:13:57 +05:00
|
|
|
const muteLists = useMuteLists()
|
2024-10-04 00:40:36 +05:00
|
|
|
const nsfwList = useNSFWList()
|
2024-09-30 15:13:57 +05:00
|
|
|
|
2024-09-02 13:47:16 +05:00
|
|
|
useDidMount(() => {
|
2024-09-03 15:23:07 +05:00
|
|
|
fetchMods({ source: window.location.host })
|
2024-10-14 13:24:43 +05:00
|
|
|
.then((mods) => {
|
2024-11-13 10:43:49 +01:00
|
|
|
// Sort by the latest (published_at descending)
|
|
|
|
mods.sort((a, b) => b.published_at - a.published_at)
|
2024-11-11 22:37:49 +05:00
|
|
|
const wotFilteredMods = mods.filter(
|
2024-11-18 23:21:05 +05:00
|
|
|
(mod) =>
|
|
|
|
isInWoT(siteWot, siteWotLevel, mod.author) ||
|
|
|
|
isInWoT(userWot, userWotLevel, mod.author)
|
2024-11-11 22:37:49 +05:00
|
|
|
)
|
|
|
|
setLatestMods(wotFilteredMods)
|
2024-09-02 13:47:16 +05:00
|
|
|
})
|
|
|
|
.finally(() => {
|
|
|
|
setIsFetchingLatestMods(false)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2024-09-30 15:13:57 +05:00
|
|
|
const filteredMods = useMemo(() => {
|
|
|
|
const mutedAuthors = [...muteLists.admin.authors, ...muteLists.user.authors]
|
|
|
|
const mutedEvents = [
|
|
|
|
...muteLists.admin.replaceableEvents,
|
|
|
|
...muteLists.user.replaceableEvents
|
|
|
|
]
|
|
|
|
|
|
|
|
const filtered = latestMods.filter(
|
|
|
|
(mod) =>
|
2024-10-04 00:40:36 +05:00
|
|
|
!mutedAuthors.includes(mod.author) &&
|
|
|
|
!mutedEvents.includes(mod.aTag) &&
|
|
|
|
!nsfwList.includes(mod.aTag) &&
|
|
|
|
!mod.nsfw
|
2024-09-30 15:13:57 +05:00
|
|
|
)
|
|
|
|
|
|
|
|
return filtered.slice(0, 4)
|
2024-10-04 00:40:36 +05:00
|
|
|
}, [muteLists, nsfwList, latestMods])
|
2024-09-30 15:13:57 +05:00
|
|
|
|
2024-09-02 13:47:16 +05:00
|
|
|
return (
|
|
|
|
<div className='IBMSecMain IBMSMListWrapper'>
|
|
|
|
<div className='IBMSMTitleMain'>
|
|
|
|
<h2 className='IBMSMTitleMainHeading'>Latest Mods</h2>
|
|
|
|
</div>
|
|
|
|
<div className='IBMSMList'>
|
|
|
|
{isFetchingLatestMods ? (
|
|
|
|
<Spinner />
|
|
|
|
) : (
|
2024-09-30 15:13:57 +05:00
|
|
|
filteredMods.map((mod) => {
|
2024-09-23 20:58:50 +05:00
|
|
|
return <ModCard key={mod.id} {...mod} />
|
2024-09-02 13:47:16 +05:00
|
|
|
})
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className='IBMSMAction'>
|
|
|
|
<a
|
|
|
|
className='btn btnMain IBMSMActionBtn'
|
|
|
|
role='button'
|
|
|
|
onClick={() => navigate(appRoutes.mods)}
|
|
|
|
>
|
|
|
|
View All
|
|
|
|
</a>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-11-05 16:22:08 +01:00
|
|
|
const DisplayLatestBlogs = () => {
|
|
|
|
const [blogs, setBlogs] = useState<Partial<BlogCardDetails>[]>()
|
|
|
|
const { fetchEvents } = useNDKContext()
|
2024-11-06 13:12:19 +01:00
|
|
|
const [filterOptions] = useLocalStorage('filter-blog-curated', {
|
|
|
|
sort: SortBy.Latest,
|
|
|
|
nsfw: NSFWFilter.Hide_NSFW
|
|
|
|
})
|
2024-11-13 14:30:58 +01:00
|
|
|
const navigation = useNavigation()
|
2024-11-05 16:22:08 +01:00
|
|
|
useDidMount(() => {
|
|
|
|
const fetchBlogs = async () => {
|
|
|
|
try {
|
|
|
|
// Show maximum of 4 blog posts
|
|
|
|
// 2 should be featured and the most recent 2 from blog npubs
|
|
|
|
// Populate the filter from known naddr (constants.ts)
|
2024-11-12 20:15:27 +01:00
|
|
|
const filter: NDKFilter = {
|
|
|
|
kinds: [kinds.LongFormArticle],
|
|
|
|
authors: [],
|
|
|
|
'#d': []
|
|
|
|
}
|
2024-11-05 16:22:08 +01:00
|
|
|
for (let i = 0; i < LANDING_PAGE_DATA.featuredBlogPosts.length; i++) {
|
|
|
|
try {
|
|
|
|
const naddr = LANDING_PAGE_DATA.featuredBlogPosts[i]
|
2024-11-12 20:15:27 +01:00
|
|
|
const decoded = nip19.decode<'naddr'>(naddr as `naddr1${string}`)
|
|
|
|
const { pubkey, identifier } = decoded.data
|
|
|
|
if (!filter.authors?.includes(pubkey)) {
|
|
|
|
filter.authors?.push(pubkey)
|
|
|
|
}
|
|
|
|
if (!filter.authors?.includes(identifier)) {
|
|
|
|
filter['#d']?.push(identifier)
|
2024-11-05 16:22:08 +01:00
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
// Silently ignore
|
|
|
|
}
|
|
|
|
}
|
2024-11-12 20:15:27 +01:00
|
|
|
|
2024-11-06 13:12:19 +01:00
|
|
|
// Prepare filter for the latest
|
2024-11-05 16:22:08 +01:00
|
|
|
const blogNpubs = import.meta.env.VITE_BLOG_NPUBS.split(',')
|
|
|
|
const blogHexkeys = blogNpubs
|
|
|
|
.map(npubToHex)
|
|
|
|
.filter((hexkey) => hexkey !== null)
|
|
|
|
|
2024-11-12 20:15:27 +01:00
|
|
|
// We fetch more posts in case of duplicates (from featured)
|
2024-11-06 13:12:19 +01:00
|
|
|
const latestFilter: NDKFilter = {
|
2024-11-05 16:22:08 +01:00
|
|
|
authors: blogHexkeys,
|
|
|
|
kinds: [kinds.LongFormArticle],
|
2024-11-13 10:55:51 +01:00
|
|
|
limit: PROFILE_BLOG_FILTER_LIMIT
|
2024-11-06 13:12:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Filter by NSFW tag
|
2024-11-12 20:15:27 +01:00
|
|
|
if (filterOptions.nsfw === NSFWFilter.Only_NSFW) {
|
|
|
|
latestFilter['#L'] = ['content-warning']
|
2024-11-06 13:12:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const results = await Promise.allSettled([
|
2024-11-12 20:15:27 +01:00
|
|
|
fetchEvents(filter),
|
2024-11-06 13:12:19 +01:00
|
|
|
fetchEvents(latestFilter)
|
|
|
|
])
|
|
|
|
|
2024-11-13 10:55:51 +01:00
|
|
|
const events: Partial<BlogCardDetails>[] = []
|
2024-11-06 13:12:19 +01:00
|
|
|
// Get featured blogs posts result
|
|
|
|
results.forEach((r) => {
|
|
|
|
// Add events from both promises to the array
|
|
|
|
if (r.status === 'fulfilled' && r.value) {
|
2024-11-13 10:55:51 +01:00
|
|
|
events.push(
|
|
|
|
...r.value
|
|
|
|
.map(extractBlogCardDetails) // Extract the blog card details
|
|
|
|
.sort(
|
|
|
|
// Sort each result by published_at in descending order
|
|
|
|
// We can't sort everything at once we'd lose prefered
|
|
|
|
(a, b) =>
|
|
|
|
a.published_at && b.published_at
|
|
|
|
? b.published_at - a.published_at
|
|
|
|
: 0
|
|
|
|
)
|
|
|
|
)
|
2024-11-06 13:12:19 +01:00
|
|
|
}
|
2024-11-05 16:22:08 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
// Remove duplicates
|
|
|
|
const unique = Array.from(
|
2024-11-06 13:12:19 +01:00
|
|
|
events
|
2024-11-13 10:55:51 +01:00
|
|
|
.filter((b) => b.id)
|
2024-11-05 16:22:08 +01:00
|
|
|
.reduce((map, obj) => {
|
2024-11-13 10:55:51 +01:00
|
|
|
map.set(obj.id!, obj)
|
2024-11-05 16:22:08 +01:00
|
|
|
return map
|
2024-11-13 10:55:51 +01:00
|
|
|
}, new Map<string, Partial<BlogCardDetails>>())
|
2024-11-05 16:22:08 +01:00
|
|
|
.values()
|
2024-11-13 10:55:51 +01:00
|
|
|
).filter(
|
|
|
|
(b) => !(b.nsfw && filterOptions.nsfw === NSFWFilter.Hide_NSFW)
|
2024-11-05 16:22:08 +01:00
|
|
|
)
|
|
|
|
|
2024-11-12 20:15:27 +01:00
|
|
|
const latest = unique.slice(0, 4)
|
|
|
|
setBlogs(latest)
|
2024-11-05 16:22:08 +01:00
|
|
|
} catch (error) {
|
|
|
|
log(
|
|
|
|
true,
|
|
|
|
LogType.Error,
|
|
|
|
'An error occurred in fetching blog details from relays',
|
|
|
|
error
|
|
|
|
)
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fetchBlogs()
|
|
|
|
})
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className='IBMSecMain IBMSMListWrapper'>
|
2024-11-28 17:21:35 +01:00
|
|
|
{navigation.state !== 'idle' && <LoadingSpinner desc={'Fetching...'} />}
|
2024-11-05 16:22:08 +01:00
|
|
|
<div className='IBMSMTitleMain'>
|
|
|
|
<h2 className='IBMSMTitleMainHeading'>Blog Posts</h2>
|
|
|
|
</div>
|
|
|
|
<div className='IBMSMList'>
|
|
|
|
{blogs?.map((b) => (
|
|
|
|
<BlogCard key={b.id} {...b} />
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className='IBMSMAction'>
|
|
|
|
<Link
|
|
|
|
className='btn btnMain IBMSMActionBtn'
|
|
|
|
role='button'
|
|
|
|
to={appRoutes.blogs}
|
|
|
|
>
|
|
|
|
View All
|
|
|
|
</Link>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|