chore(git): merge pull request #239 from issues/238-mods-fetching-fallback into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m11s
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m11s
Reviewed-on: #239
This commit is contained in:
commit
7783371297
@ -31,7 +31,7 @@ import {
|
||||
timeout
|
||||
} from 'utils'
|
||||
|
||||
type FetchModsOptions = {
|
||||
export type FetchModsOptions = {
|
||||
source?: string
|
||||
until?: number
|
||||
since?: number
|
||||
@ -170,16 +170,25 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
|
||||
const filter: NDKFilter = {
|
||||
kinds: [NDKKind.Classified], // Specify the kind of events to fetch
|
||||
limit: limit || MOD_FILTER_LIMIT, // Limit the number of events fetched to 20
|
||||
'#t': [T_TAG_VALUE],
|
||||
until, // Optional filter to fetch events until this timestamp
|
||||
since, // Optional filter to fetch events from this timestamp
|
||||
authors: author ? [author] : undefined // Optional filter to fetch events from only this author
|
||||
'#t': [T_TAG_VALUE]
|
||||
}
|
||||
|
||||
// If the source matches the current window location, add a filter condition
|
||||
if (source === window.location.host) {
|
||||
filter['#r'] = [window.location.host] // Add a tag filter for the current host
|
||||
}
|
||||
// Optional filter to fetch events until this timestamp
|
||||
if (until) {
|
||||
filter['until'] = until
|
||||
}
|
||||
// Optional filter to fetch events from this timestamp
|
||||
if (since) {
|
||||
filter['since'] = since
|
||||
}
|
||||
// Optional filter to fetch events from only this author
|
||||
if (author) {
|
||||
filter['authors'] = [author]
|
||||
}
|
||||
|
||||
return ndk
|
||||
.fetchEvents(filter, {
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { ModFilter } from 'components/Filters/ModsFilter'
|
||||
import { Pagination } from 'components/Pagination'
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import {
|
||||
createSearchParams,
|
||||
useLoaderData,
|
||||
@ -8,7 +7,7 @@ import {
|
||||
} from 'react-router-dom'
|
||||
import { LoadingSpinner } from '../../components/LoadingSpinner'
|
||||
import { ModCard } from '../../components/ModCard'
|
||||
import { MOD_FILTER_LIMIT } from '../../constants'
|
||||
import { MOD_FILTER_LIMIT, T_TAG_VALUE } from '../../constants'
|
||||
import {
|
||||
useAppSelector,
|
||||
useFilteredMods,
|
||||
@ -20,77 +19,156 @@ import '../../styles/filters.css'
|
||||
import '../../styles/pagination.css'
|
||||
import '../../styles/search.css'
|
||||
import '../../styles/styles.css'
|
||||
import { FilterOptions, ModDetails } from '../../types'
|
||||
import { DEFAULT_FILTER_OPTIONS, scrollIntoView } from 'utils'
|
||||
import { FilterOptions, ModDetails, SortBy } from '../../types'
|
||||
import {
|
||||
DEFAULT_FILTER_OPTIONS,
|
||||
extractModData,
|
||||
isModDataComplete
|
||||
} from 'utils'
|
||||
import { SearchInput } from 'components/SearchInput'
|
||||
import { ModsPageLoaderResult } from './loader'
|
||||
import { FetchModsOptions } from 'contexts/NDKContext'
|
||||
import {
|
||||
NDKFilter,
|
||||
NDKKind,
|
||||
NDKSubscription,
|
||||
NDKSubscriptionCacheUsage
|
||||
} from '@nostr-dev-kit/ndk'
|
||||
|
||||
export const ModsPage = () => {
|
||||
const scrollTargetRef = useRef<HTMLDivElement>(null)
|
||||
const { repostList, muteLists, nsfwList } =
|
||||
useLoaderData() as ModsPageLoaderResult
|
||||
const { fetchMods } = useNDKContext()
|
||||
const { ndk, fetchMods } = useNDKContext()
|
||||
const [isFetching, setIsFetching] = useState(false)
|
||||
const [mods, setMods] = useState<ModDetails[]>([])
|
||||
|
||||
const [isLoadMoreVisible, setIsLoadMoreVisible] = useState(true)
|
||||
const [filterOptions] = useLocalStorage<FilterOptions>(
|
||||
'filter',
|
||||
DEFAULT_FILTER_OPTIONS
|
||||
)
|
||||
|
||||
const [page, setPage] = useState(1)
|
||||
|
||||
const userState = useAppSelector((state) => state.user)
|
||||
|
||||
useEffect(() => {
|
||||
setIsFetching(true)
|
||||
fetchMods({ source: filterOptions.source })
|
||||
.then((res) => {
|
||||
setMods(res)
|
||||
if (filterOptions.sort === SortBy.Latest) {
|
||||
res.sort((a, b) => b.published_at - a.published_at)
|
||||
} else if (filterOptions.sort === SortBy.Oldest) {
|
||||
res.sort((a, b) => a.published_at - b.published_at)
|
||||
}
|
||||
setIsLoadMoreVisible(res.length >= MOD_FILTER_LIMIT)
|
||||
setMods(res.slice(0, MOD_FILTER_LIMIT))
|
||||
})
|
||||
.finally(() => {
|
||||
setIsFetching(false)
|
||||
})
|
||||
}, [filterOptions.source, fetchMods])
|
||||
}, [filterOptions.source, fetchMods, filterOptions.sort])
|
||||
|
||||
const handleNext = useCallback(() => {
|
||||
const lastMod: ModDetails | undefined = useMemo(() => {
|
||||
// For the latest sort find oldest mod
|
||||
// for the oldest sort find newest mod
|
||||
return mods.reduce((prev, current) => {
|
||||
if (!prev) return current
|
||||
if (filterOptions.sort === SortBy.Latest) {
|
||||
return current.edited_at < prev.edited_at ? current : prev
|
||||
} else if (filterOptions.sort === SortBy.Oldest) {
|
||||
return current.edited_at > prev.edited_at ? current : prev
|
||||
}
|
||||
return prev
|
||||
}, undefined as ModDetails | undefined)
|
||||
}, [mods, filterOptions.sort])
|
||||
|
||||
// Add missing mods to the list
|
||||
useEffect(() => {
|
||||
let sub: NDKSubscription
|
||||
if (lastMod) {
|
||||
const filter: NDKFilter = {
|
||||
kinds: [NDKKind.Classified],
|
||||
'#t': [T_TAG_VALUE]
|
||||
}
|
||||
if (filterOptions.sort === SortBy.Latest) {
|
||||
filter.since = lastMod.edited_at + 1
|
||||
} else if (filterOptions.sort === SortBy.Oldest) {
|
||||
filter.until = lastMod.edited_at - 1
|
||||
}
|
||||
if (filterOptions.source === window.location.host) {
|
||||
filter['#r'] = [window.location.host]
|
||||
}
|
||||
sub = ndk.subscribe(
|
||||
filter,
|
||||
{
|
||||
closeOnEose: false,
|
||||
cacheUsage: NDKSubscriptionCacheUsage.PARALLEL
|
||||
},
|
||||
undefined,
|
||||
{
|
||||
onEvent: (ndkEvent) => {
|
||||
setMods((prevMods) => {
|
||||
// Skip if not valid
|
||||
if (!isModDataComplete(ndkEvent)) {
|
||||
return prevMods
|
||||
}
|
||||
|
||||
// Skip existing
|
||||
if (
|
||||
prevMods.find(
|
||||
(e) =>
|
||||
e.id === ndkEvent.id ||
|
||||
prevMods.findIndex((n) => n.id === ndkEvent.id) !== -1
|
||||
)
|
||||
) {
|
||||
return prevMods
|
||||
}
|
||||
const newMod = extractModData(ndkEvent)
|
||||
return [...prevMods, newMod]
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
return () => {
|
||||
if (sub) sub.stop()
|
||||
}
|
||||
}, [filterOptions.sort, filterOptions.source, lastMod, ndk])
|
||||
|
||||
const handleLoadMore = useCallback(() => {
|
||||
setIsFetching(true)
|
||||
|
||||
const until =
|
||||
mods.length > 0 ? mods[mods.length - 1].published_at - 1 : undefined
|
||||
const fetchModsOptions: FetchModsOptions = {
|
||||
source: filterOptions.source
|
||||
}
|
||||
|
||||
fetchMods({
|
||||
source: filterOptions.source,
|
||||
until
|
||||
})
|
||||
if (lastMod) {
|
||||
if (filterOptions.sort === SortBy.Latest) {
|
||||
fetchModsOptions.until = lastMod.edited_at - 1
|
||||
} else if (filterOptions.sort === SortBy.Oldest) {
|
||||
fetchModsOptions.since = lastMod.edited_at + 1
|
||||
}
|
||||
}
|
||||
|
||||
fetchMods(fetchModsOptions)
|
||||
.then((res) => {
|
||||
setMods(res)
|
||||
setPage((prev) => prev + 1)
|
||||
scrollIntoView(scrollTargetRef.current)
|
||||
setMods((prevMods) => {
|
||||
const newMods = res
|
||||
const combinedMods = [...prevMods, ...newMods]
|
||||
const uniqueMods = Array.from(
|
||||
new Set(combinedMods.map((mod) => mod.id))
|
||||
)
|
||||
.map((id) => combinedMods.find((mod) => mod.id === id))
|
||||
.filter((mod): mod is ModDetails => mod !== undefined)
|
||||
|
||||
setIsLoadMoreVisible(newMods.length >= MOD_FILTER_LIMIT)
|
||||
|
||||
return uniqueMods
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
setIsFetching(false)
|
||||
})
|
||||
}, [filterOptions.source, mods, fetchMods])
|
||||
|
||||
const handlePrev = useCallback(() => {
|
||||
setIsFetching(true)
|
||||
|
||||
const since = mods.length > 0 ? mods[0].published_at + 1 : undefined
|
||||
|
||||
fetchMods({
|
||||
source: filterOptions.source,
|
||||
since
|
||||
})
|
||||
.then((res) => {
|
||||
setMods(res)
|
||||
setPage((prev) => prev - 1)
|
||||
scrollIntoView(scrollTargetRef.current)
|
||||
})
|
||||
.finally(() => {
|
||||
setIsFetching(false)
|
||||
})
|
||||
}, [filterOptions.source, mods, fetchMods])
|
||||
}, [fetchMods, filterOptions, lastMod])
|
||||
|
||||
const filteredModList = useFilteredMods(
|
||||
mods,
|
||||
@ -121,12 +199,17 @@ export const ModsPage = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Pagination
|
||||
page={page}
|
||||
disabledNext={mods.length < MOD_FILTER_LIMIT}
|
||||
handlePrev={handlePrev}
|
||||
handleNext={handleNext}
|
||||
/>
|
||||
{!isFetching && isLoadMoreVisible && filteredModList.length > 0 && (
|
||||
<div className='IBMSMListFeedLoadMore'>
|
||||
<button
|
||||
className='btn btnMain IBMSMListFeedLoadMoreBtn'
|
||||
type='button'
|
||||
onClick={handleLoadMore}
|
||||
>
|
||||
Load More
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user