import FsLightbox from 'fslightbox-react'
import { nip19 } from 'nostr-tools'
import { useEffect, useRef, useState } from 'react'
import {
Link as ReactRouterLink,
useLoaderData,
useNavigation,
useParams,
useSubmit
} from 'react-router-dom'
import { toast } from 'react-toastify'
import { BlogCard } from '../../components/BlogCard'
import { ProfileSection } from '../../components/ProfileSection'
import { useAppSelector, useBodyScrollDisable, useDidMount } from '../../hooks'
import { getGamePageRoute, getModsEditPageRoute } from '../../routes'
import '../../styles/comments.css'
import '../../styles/downloads.css'
import '../../styles/innerPage.css'
import '../../styles/popup.css'
import '../../styles/post.css'
import '../../styles/reactions.css'
import '../../styles/styles.css'
import '../../styles/tabs.css'
import '../../styles/tags.css'
import '../../styles/write.css'
import { DownloadUrl, ModPageLoaderResult } from '../../types'
import {
capitalizeEachWord,
checkUrlForFile,
copyTextToClipboard,
downloadFile,
getFilenameFromUrl,
isValidUrl
} from '../../utils'
import { Comments } from '../../components/comment'
import { PublishDetails } from 'components/Internal/PublishDetails'
import { Interactions } from 'components/Internal/Interactions'
import { ReportPopup } from 'components/ReportPopup'
import { Spinner } from 'components/Spinner'
import { RouterLoadingSpinner } from 'components/LoadingSpinner'
import { OriginalAuthor } from 'components/OriginalAuthor'
import { Viewer } from 'components/Markdown/Viewer'
import { PostWarnings } from 'components/PostWarning'
const MOD_REPORT_REASONS = [
{ label: 'Actually CP', key: 'actuallyCP' },
{ label: 'Spam', key: 'spam' },
{ label: 'Scam', key: 'scam' },
{ label: 'Not a game mod', key: 'notAGameMod' },
{ label: 'Stolen game mod', key: 'stolenGameMod' },
{ label: `Repost of a game mod`, key: 'repost' },
{ label: `Wasn't tagged NSFW`, key: 'wasntTaggedNSFW' },
{ label: 'Other reason', key: 'otherReason' }
]
export const ModPage = () => {
const { mod, postWarning } = useLoaderData() as ModPageLoaderResult
// We can get author right away from naddr, no need to wait for mod data
const { naddr } = useParams()
let author = mod?.author
if (naddr && !author) {
try {
const decoded = nip19.decode<'naddr'>(naddr as `naddr1${string}`)
author = decoded.data.pubkey
} catch (error) {
// Silently ignore - we will get author eventually from mods
}
}
const [commentCount, setCommentCount] = useState(0)
return (
<>
Mod Download
{postWarning &&
}
{mod.downloadUrls.length > 0 && (
)}
{mod.downloadUrls.length > 1 && (
{mod.downloadUrls
.slice(1)
.map((download, index) => (
))}
)}
>
) : (
)}
{typeof author !== 'undefined' && (
)}
>
)
}
const Game = () => {
const { naddr } = useParams()
const navigation = useNavigation()
const { mod, isAddedToNSFW, isBlocked, isRepost } =
useLoaderData() as ModPageLoaderResult
const userState = useAppSelector((state) => state.user)
const isLoggedIn = userState.auth && userState.user?.pubkey !== 'undefined'
const [showReportPopUp, setShowReportPopUp] = useState()
useBodyScrollDisable(!!showReportPopUp)
const submit = useSubmit()
const handleBlock = () => {
if (navigation.state === 'idle') {
submit(
{
intent: 'block',
value: !isBlocked
},
{
method: 'post',
encType: 'application/json'
}
)
}
}
const handleNSFW = () => {
if (navigation.state === 'idle') {
submit(
{
intent: 'nsfw',
value: !isAddedToNSFW
},
{
method: 'post',
encType: 'application/json'
}
)
}
}
const handleRepost = () => {
if (navigation.state === 'idle') {
submit(
{
intent: 'repost',
value: !isRepost
},
{
method: 'post',
encType: 'application/json'
}
)
}
}
const isAdmin =
userState.user?.npub &&
userState.user.npub === import.meta.env.VITE_REPORTING_NPUB
const game = mod?.game || ''
const gameRoute = getGamePageRoute(game)
const editRoute = getModsEditPageRoute(naddr ? naddr : '')
return (
<>
{!!showReportPopUp && (
setShowReportPopUp(undefined)}
/>
)}
>
)
}
type BodyProps = {
featuredImageUrl: string
title: string
body: string
game: string
screenshotsUrls: string[]
tags: string[]
LTags: string[]
nsfw: boolean
repost: boolean
originalAuthor?: string
}
const Body = ({
featuredImageUrl,
game,
title,
body,
screenshotsUrls,
tags,
LTags,
nsfw,
repost,
originalAuthor
}: BodyProps) => {
const COLLAPSED_MAX_SIZE = 250
const postBodyRef = useRef(null)
const viewFullPostBtnRef = useRef(null)
const [lightBoxController, setLightBoxController] = useState({
toggler: false,
slide: 1
})
const openLightBoxOnSlide = (slide: number) => {
setLightBoxController((prev) => ({
toggler: !prev.toggler,
slide
}))
}
useEffect(() => {
if (postBodyRef.current) {
if (postBodyRef.current.scrollHeight <= COLLAPSED_MAX_SIZE) {
viewFullPost()
}
}
}, [])
const viewFullPost = () => {
if (postBodyRef.current && viewFullPostBtnRef.current) {
postBodyRef.current.style.maxHeight = 'unset'
postBodyRef.current.style.padding = 'unset'
viewFullPostBtnRef.current.style.display = 'none'
}
}
return (
<>
{title}
{screenshotsUrls.map((url, index) => (

openLightBoxOnSlide(index + 1)}
/>
))}
{nsfw && (
)}
{repost && (
REPOST
{originalAuthor && originalAuthor !== '' && (
<>
. Original Author:{' '}
>
)}
)}
{tags.map((tag, index) => (
{tag}
))}
{LTags.length > 0 && (
{LTags.map((hierarchy) => {
const hierarchicalCategories = hierarchy.split(`:`)
const categories = hierarchicalCategories
.map
((c, i) => {
const partialHierarchy = hierarchicalCategories
.slice(0, i + 1)
.join(':')
return (
{capitalizeEachWord(c)}
)
})
.reduce((prev, curr, i) => [
prev,
,
curr
])
return (
{categories}
)
})}
)}
>
)
}
const Download = ({
url,
title,
hash,
signatureKey,
malwareScanLink,
modVersion,
customNote,
mediaUrl
}: DownloadUrl) => {
const [showAuthDetails, setShowAuthDetails] = useState(false)
const [showNotice, setShowNotice] = useState(false)
const [showScanNotice, setShowCanNotice] = useState(false)
useDidMount(async () => {
const isFile = await checkUrlForFile(url)
setShowNotice(!isFile)
// Check the malware scan url
// if it's valid URL
// if it contains sha256
// if it differs from download link
setShowCanNotice(
!(
malwareScanLink &&
isValidUrl(malwareScanLink) &&
/\b[a-fA-F0-9]{64}\b/.test(malwareScanLink) &&
malwareScanLink !== url
)
)
})
const handleDownload = () => {
// Get the filename from the URL
const filename = getFilenameFromUrl(url)
downloadFile(url, filename)
}
return (
{typeof title !== 'undefined' && title !== '' && (
{title}
)}
{showNotice && (
Notice: The creator has provided a download link that doesn't
download the files immediately, but rather redirects you to a
different site.
)}
{showScanNotice && (
The mod poster hasn't provided a malware scan report for these
files. Be careful.
)}
{/*temporarily commented out the WoT rating for download links within a mod post
*/}
setShowAuthDetails((prev) => !prev)}
>
Authentication Details
{showAuthDetails && (
)}
)
}
const DisplayModAuthorBlogs = () => {
const { latest } = useLoaderData() as ModPageLoaderResult
if (!latest?.length) return null
return (
Creator's Blog Posts
{latest?.map((b) => (
))}
)
}