178 lines
5.8 KiB
TypeScript
Raw Normal View History

import { filterForEventsTaggingId, NDKFilter } from '@nostr-dev-kit/ndk'
2024-10-31 20:14:29 +01:00
import { NDKContextType } from 'contexts/NDKContext'
import { kinds, nip19 } from 'nostr-tools'
2024-10-31 20:14:29 +01:00
import { LoaderFunctionArgs, redirect } from 'react-router-dom'
import { toast } from 'react-toastify'
import { appRoutes } from 'routes'
import { store } from 'store'
import { BlogPageLoaderResult, FilterOptions, NSFWFilter } from 'types'
import {
DEFAULT_FILTER_OPTIONS,
getLocalStorageItem,
log,
LogType
} from 'utils'
import { extractBlogCardDetails, extractBlogDetails } from 'utils/blog'
2024-10-31 20:14:29 +01:00
export const blogRouteLoader =
(ndkContext: NDKContextType) =>
async ({ params }: LoaderFunctionArgs) => {
const { naddr } = params
if (!naddr) {
log(true, LogType.Error, 'Required naddr.')
return redirect(appRoutes.blogs)
}
// Decode author from naddr
const decoded = nip19.decode<'naddr'>(naddr as `naddr1${string}`)
const { pubkey } = decoded.data
2024-10-31 20:14:29 +01:00
try {
// Get the filter with #a from naddr for the main blog content
2024-10-31 20:14:29 +01:00
const filter = filterForEventsTaggingId(naddr)
if (!filter) {
log(true, LogType.Error, 'Unable to create filter from blog naddr.')
return redirect(appRoutes.blogs)
}
// Update kinds to make sure we fetch correct event kind
filter.kinds = [kinds.LongFormArticle]
const userState = store.getState().user
2024-11-05 16:22:08 +01:00
// Get the blog filter options for latest blogs
const filterOptions = JSON.parse(
getLocalStorageItem('filter-blog', DEFAULT_FILTER_OPTIONS)
) as FilterOptions
// Fetch 4 in case the current blog is included in the latest
const latestModsFilter: NDKFilter = {
authors: [pubkey],
kinds: [kinds.LongFormArticle],
limit: 4
}
// Add source filter
if (filterOptions.source === window.location.host) {
latestModsFilter['#r'] = [filterOptions.source]
}
// Filter by NSFW tag
// NSFWFilter.Show_NSFW -> filter not needed
// NSFWFilter.Only_NSFW -> true
// NSFWFilter.Hide_NSFW -> false
if (filterOptions.nsfw !== NSFWFilter.Show_NSFW) {
latestModsFilter['#nsfw'] = [
(filterOptions.nsfw === NSFWFilter.Only_NSFW).toString()
]
}
// Parallel fetch blog event, latest events, mute, and nsfw lists in parallel
const settled = await Promise.allSettled([
ndkContext.fetchEvent(filter),
ndkContext.fetchEvents(latestModsFilter),
ndkContext.getMuteLists(userState?.user?.pubkey as string),
ndkContext.getNSFWList()
])
const result: BlogPageLoaderResult = {
blog: undefined,
latest: [],
isAddedToNSFW: false,
isBlocked: false
}
// Check the blog event result
const fetchEventResult = settled[0]
if (fetchEventResult.status === 'fulfilled' && fetchEventResult.value) {
// Extract the blog details from the event
result.blog = extractBlogDetails(fetchEventResult.value)
} else if (fetchEventResult.status === 'rejected') {
log(
true,
LogType.Error,
'Unable to fetch the blog event.',
fetchEventResult.reason
)
}
// Check the lateast blog events
const fetchEventsResult = settled[1]
if (fetchEventsResult.status === 'fulfilled' && fetchEventsResult.value) {
// Extract the blog card details from the events
result.latest = fetchEventsResult.value
.map(extractBlogCardDetails)
.filter((b) => b.id !== result.blog?.id) // Filter out current blog if present
.slice(0, 3) // Take only three
} else if (fetchEventsResult.status === 'rejected') {
log(
true,
LogType.Error,
'Unable to fetch the latest blog events.',
fetchEventsResult.reason
)
2024-10-31 20:14:29 +01:00
}
const muteList = settled[2]
if (muteList.status === 'fulfilled' && muteList.value) {
if (muteList && muteList.value) {
if (result.blog && result.blog.aTag) {
if (
muteList.value.admin.replaceableEvents.includes(
result.blog.aTag
) ||
muteList.value.user.replaceableEvents.includes(result.blog.aTag)
) {
result.isBlocked = true
}
}
}
} else if (muteList.status === 'rejected') {
log(true, LogType.Error, 'Issue fetching mute list', muteList.reason)
}
const nsfwList = settled[3]
if (nsfwList.status === 'fulfilled' && nsfwList.value) {
// Check if the blog is marked as NSFW
// Mark it as NSFW only if it's missing the tag
if (result.blog) {
const isMissingNsfwTag =
!result.blog.nsfw &&
result.blog.aTag &&
nsfwList.value.includes(result.blog.aTag)
if (isMissingNsfwTag) {
result.blog.nsfw = true
}
if (result.blog.aTag && nsfwList.value.includes(result.blog.aTag)) {
result.isAddedToNSFW = true
}
}
// Check if the the latest blogs too
result.latest = result.latest.map((b) => {
if (b) {
const isMissingNsfwTag =
!b.nsfw && b.aTag && nsfwList.value.includes(b.aTag)
if (isMissingNsfwTag) {
b.nsfw = true
}
}
return b
})
} else if (nsfwList.status === 'rejected') {
log(true, LogType.Error, 'Issue fetching nsfw list', nsfwList.reason)
}
return result
2024-10-31 20:14:29 +01:00
} catch (error) {
log(
true,
LogType.Error,
'An error occurred in fetching blog details from relays',
error
)
toast.error('An error occurred in fetching blog details from relays')
2024-10-31 20:14:29 +01:00
return redirect(appRoutes.blogs)
}
}