import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
import Badge from 'react-bootstrap/Badge'
import Dropdown from 'react-bootstrap/Dropdown'
import Countdown from './countdown'
import { abbrNum, numWithUnits } from '@/lib/format'
import { newComments, commentsViewedAt } from '@/lib/new-comments'
import { timeSince } from '@/lib/time'
import { DeleteDropdownItem } from './delete'
import styles from './item.module.css'
import { useMe } from './me'
import DontLikeThisDropdownItem, { OutlawDropdownItem } from './dont-link-this'
import BookmarkDropdownItem from './bookmark'
import SubscribeDropdownItem from './subscribe'
import { CopyLinkDropdownItem, CrosspostDropdownItem } from './share'
import Badges from './badge'
import { USER_ID } from '@/lib/constants'
import ActionDropdown from './action-dropdown'
import MuteDropdownItem from './mute'
import { DropdownItemUpVote } from './upvote'
import { useRoot } from './root'
import { MuteSubDropdownItem, PinSubDropdownItem } from './territory-header'
import UserPopover from './user-popover'
import { useQrPayment } from './payment'
import { useRetryCreateItem } from './use-item-submit'
import { useToast } from './toast'
import { useShowModal } from './modal'
import classNames from 'classnames'
export default function ItemInfo ({
item, full, commentsText = 'comments',
commentTextSingular = 'comment', className, embellishUser, extraInfo, edit, toggleEdit, editText,
onQuoteReply, extraBadges, nested, pinnable, showActionDropdown = true, showUser = true,
setDisableRetry, disableRetry
}) {
const editThreshold = new Date(item.invoice?.confirmedAt ?? item.createdAt).getTime() + 10 * 60000
const { me } = useMe()
const router = useRouter()
const [canEdit, setCanEdit] = useState(item.mine && !item.bio && (Date.now() < editThreshold))
const [hasNewComments, setHasNewComments] = useState(false)
const root = useRoot()
const sub = item?.sub || root?.sub
useEffect(() => {
if (!full) {
setHasNewComments(newComments(item))
}
}, [item])
useEffect(() => {
const authorEdit = item.mine && !item.bio
const invParams = window.localStorage.getItem(`item:${item.id}:hash:hmac`)
const hmacEdit = !!invParams && !me && Number(item.user.id) === USER_ID.anon
setCanEdit((authorEdit || hmacEdit) && (Date.now() < editThreshold))
}, [me, item.id, item.mine, editThreshold])
// territory founders can pin any post in their territory
// and OPs can pin any root reply in their post
const isPost = !item.parentId
const mySub = (me && sub && Number(me.id) === sub.userId)
const myPost = (me && root && Number(me.id) === Number(root.user.id))
const rootReply = item.path.split('.').length === 2
const canPin = (isPost && mySub) || (myPost && rootReply)
const meSats = (me ? item.meSats : item.meAnonSats) || 0
return (
{!(item.position && (pinnable || !item.subName)) && !(!item.parentId && Number(item.user?.id) === USER_ID.ad) &&
<>
{numWithUnits(item.sats)}
\
>}
{item.boost > 0 &&
<>
{abbrNum(item.boost)} boost
\
>}
{
const viewedAt = commentsViewedAt(item)
if (viewedAt) {
e.preventDefault()
router.push(
`/items/${item.id}?commentsViewedAt=${viewedAt}`,
`/items/${item.id}`)
}
}} title={numWithUnits(item.commentSats)} className='text-reset position-relative'
>
{numWithUnits(item.ncomments, {
abbreviate: false,
unitPlural: commentsText,
unitSingular: commentTextSingular
})}
{hasNewComments &&
{' '}
}
\
{showUser &&
@{item.user.name}
{embellishUser}
}
{timeSince(new Date(item.createdAt))}
{item.prior &&
<>
\
yesterday
>}
{item.subName &&
{' '}
{item.subName}
}
{sub?.nsfw &&
nsfw}
{(item.outlawed && !item.mine &&
{' '}
outlawed
) ||
(item.freebie && !item.position &&
{' '}
freebie
)}
{(item.apiKey &&
<>{' '}
bot>
)}
{extraBadges}
{
showActionDropdown &&
<>
{(item.parentId || item.text) && onQuoteReply &&
quote reply}
{me && }
{me && }
{item.otsHash &&
opentimestamp
}
{item?.noteId && (
window.open(`https://njump.me/${item.noteId}`, '_blank', 'noopener,noreferrer,nofollow')}>
nostr note
)}
{item && item.mine && !item.noteId && !item.isJob && !item.parentId &&
}
{me && !item.position &&
!item.mine && !item.deletedAt &&
(item.meDontLikeSats > meSats
?
: )}
{me && sub && !item.mine && !item.outlawed && Number(me.id) === Number(sub.userId) && sub.moderated &&
<>
>}
{item.mine && item.invoice?.id &&
<>
view invoice
>}
{me && !nested && !item.mine && sub && Number(me.id) !== Number(sub.userId) &&
<>
>}
{canPin &&
<>
>}
{item.mine && !item.position && !item.deletedAt && !item.bio &&
<>
>}
{me && !item.mine &&
<>
>}
>
}
{extraInfo}
)
}
function InfoDropdownItem ({ item }) {
const { me } = useMe()
const showModal = useShowModal()
const onClick = () => {
showModal((onClose) => {
return (
id
{item.id}
created at
{item.createdAt}
cost
{item.cost}
sats
{item.sats}
{me && (
<>
sats from me
{item.meSats}
>
)}
zappers
{item.upvotes}
)
})
}
return (
details
)
}
function PaymentInfo ({ item, disableRetry, setDisableRetry }) {
const { me } = useMe()
const toaster = useToast()
const retryCreateItem = useRetryCreateItem({ id: item.id })
const waitForQrPayment = useQrPayment()
const [disableInfoRetry, setDisableInfoRetry] = useState(disableRetry)
if (item.deletedAt) return null
const disableDualRetry = disableRetry || disableInfoRetry
function setDisableDualRetry (value) {
setDisableInfoRetry(value)
setDisableRetry?.(value)
}
let Component
let onClick
if (me && item.invoice?.actionState && item.invoice?.actionState !== 'PAID') {
if (item.invoice?.actionState === 'FAILED') {
Component = () => retry payment
onClick = async () => {
if (disableDualRetry) return
setDisableDualRetry(true)
try {
const { error } = await retryCreateItem({ variables: { invoiceId: parseInt(item.invoice?.id) } })
if (error) throw error
} catch (error) {
toaster.danger(error.message)
} finally {
setDisableDualRetry(false)
}
}
} else {
Component = () => (
pending
)
onClick = () => waitForQrPayment({ id: item.invoice?.id }, null, { cancelOnClose: false }).catch(console.error)
}
} else {
return null
}
return (
<>
\
>
)
}
function EditInfo ({ item, edit, canEdit, setCanEdit, toggleEdit, editText, editThreshold }) {
const router = useRouter()
if (canEdit) {
return (
<>
\
toggleEdit ? toggleEdit() : router.push(`/items/${item.id}/edit`)}
>
{editText || 'edit'}
{(!item.invoice?.actionState || item.invoice?.actionState === 'PAID')
? { setCanEdit(false) }}
/>
: 10:00}
>
)
}
if (edit && !canEdit) {
// if we're still editing after timer ran out
return (
<>
\
toggleEdit ? toggleEdit() : router.push(`/items/${item.id}`)}
>
cancel
00:00
>
)
}
return null
}