import { ClockIcon } from '@heroicons/react/outline' import { ExclamationIcon, PencilIcon, PlusCircleIcon, UserGroupIcon, } from '@heroicons/react/solid' import clsx from 'clsx' import { Editor } from '@tiptap/react' import dayjs from 'dayjs' import Link from 'next/link' import { Row } from '../layout/row' import { formatMoney } from 'common/util/format' import { Contract, updateContract } from 'web/lib/firebase/contracts' import { DateTimeTooltip } from '../datetime-tooltip' import { fromNow } from 'web/lib/util/time' import { Avatar } from '../avatar' import { useState } from 'react' import NewContractBadge from '../new-contract-badge' import { MiniUserFollowButton } from '../follow-button' import { DAY_MS } from 'common/util/time' import { useUser, useUserById } from 'web/hooks/use-user' import { Button } from 'web/components/button' import { Modal } from 'web/components/layout/modal' import { Col } from 'web/components/layout/col' import { ContractGroupsList } from 'web/components/groups/contract-groups-list' import { linkClass } from 'web/components/site-link' import { getGroupLinkToDisplay, groupPath } from 'web/lib/firebase/groups' import { insertContent } from '../editor/utils' import { contractMetrics } from 'common/contract-details' import { UserLink } from 'web/components/user-link' import { FeaturedContractBadge } from 'web/components/contract/featured-contract-badge' import { Tooltip } from 'web/components/tooltip' import { ExtraContractActionsRow } from './extra-contract-actions-row' import { GroupLink } from 'common/group' import { Subtitle } from '../subtitle' import { useIsMobile } from 'web/hooks/use-is-mobile' import { useIsClient } from 'web/hooks/use-is-client' import { BountiedContractBadge, BountiedContractSmallBadge, } from 'web/components/contract/bountied-contract-badge' import { Input } from '../input' import { editorExtensions } from '../editor' export type ShowTime = 'resolve-date' | 'close-date' export function MiscDetails(props: { contract: Contract showTime?: ShowTime hideGroupLink?: boolean }) { const { contract, showTime, hideGroupLink } = props const { closeTime, isResolved, createdTime, resolutionTime, uniqueBettorCount, } = contract const isClient = useIsClient() const isNew = createdTime > Date.now() - DAY_MS && !isResolved const groupToDisplay = getGroupLinkToDisplay(contract) return ( {isClient && showTime === 'close-date' ? ( {(closeTime || 0) < Date.now() ? 'Closed' : 'Closes'}{' '} {fromNow(closeTime || 0)} ) : isClient && showTime === 'resolve-date' && resolutionTime ? ( {'Resolved '} {fromNow(resolutionTime)} ) : (contract?.featuredOnHomeRank ?? 0) > 0 ? ( ) : (contract.openCommentBounties ?? 0) > 0 ? ( ) : !isNew || (uniqueBettorCount ?? 0) > 1 ? ( {uniqueBettorCount} trader{uniqueBettorCount !== 1 ? 's' : ''} ) : ( )} {!hideGroupLink && groupToDisplay && ( {groupToDisplay.name} )} ) } export function AvatarDetails(props: { contract: Contract className?: string short?: boolean noLink?: boolean }) { const { contract, short, className, noLink } = props const { creatorName, creatorUsername, creatorAvatarUrl } = contract return ( ) } export function ContractDetails(props: { contract: Contract disabled?: boolean }) { const { contract, disabled } = props const isMobile = useIsMobile() return (
{/* GROUPS */} {isMobile && ( )} ) } export function MarketSubheader(props: { contract: Contract disabled?: boolean }) { const { contract, disabled } = props const { creatorName, creatorUsername, creatorId, creatorAvatarUrl } = contract const { resolvedDate } = contractMetrics(contract) const user = useUser() const creator = useUserById(creatorId) const correctResolutionPercentage = creator?.fractionResolvedCorrectly const isCreator = user?.id === creatorId const isMobile = useIsMobile() return ( {!disabled && (
)} {disabled ? ( creatorName ) : ( {/**/} )} {correctResolutionPercentage != null && correctResolutionPercentage < BAD_CREATOR_THRESHOLD && ( )} {!isMobile && ( )}
) } export function CloseOrResolveTime(props: { contract: Contract resolvedDate: any isCreator: boolean disabled?: boolean }) { const { contract, resolvedDate, isCreator, disabled } = props const { resolutionTime, closeTime } = contract if (!!closeTime || !!resolvedDate) { return ( {resolvedDate && resolutionTime ? ( <>
resolved 
{resolvedDate}
) : null} {!resolvedDate && closeTime && ( {dayjs().isBefore(closeTime) &&
closes 
} {!dayjs().isBefore(closeTime) &&
closed 
}
)}
) } else return <> } export function MarketGroups(props: { contract: Contract disabled?: boolean }) { const [open, setOpen] = useState(false) const user = useUser() const { contract, disabled } = props const groupToDisplay = getGroupLinkToDisplay(contract) return ( <> {!disabled && user && ( )} ) } export function ExtraMobileContractDetails(props: { contract: Contract forceShowVolume?: boolean }) { const { contract, forceShowVolume } = props const { volume, resolutionTime, closeTime, creatorId, uniqueBettorCount } = contract const user = useUser() const uniqueBettors = uniqueBettorCount ?? 0 const { resolvedDate } = contractMetrics(contract) const volumeTranslation = volume > 800 || uniqueBettors >= 20 ? 'High' : volume > 300 || uniqueBettors >= 10 ? 'Medium' : 'Low' return ( {resolvedDate && resolutionTime ? ( {resolvedDate} Ended ) : ( !resolvedDate && closeTime && ( Closes  ) )} {(user || forceShowVolume) && ( {volumeTranslation} Activity )} ) } export function GroupDisplay(props: { groupToDisplay?: GroupLink | null disabled?: boolean }) { const { groupToDisplay, disabled } = props if (groupToDisplay) { const groupSection = ( {groupToDisplay.name} ) return disabled ? ( groupSection ) : ( {groupSection} ) } else return (
No Group
) } function EditableCloseDate(props: { closeTime: number contract: Contract isCreator: boolean disabled?: boolean }) { const { closeTime, contract, isCreator, disabled } = props const isClient = useIsClient() const dayJsCloseTime = dayjs(closeTime) const dayJsNow = dayjs() const [isEditingCloseTime, setIsEditingCloseTime] = useState(false) const [closeDate, setCloseDate] = useState( closeTime && dayJsCloseTime.format('YYYY-MM-DD') ) const [closeHoursMinutes, setCloseHoursMinutes] = useState( closeTime && dayJsCloseTime.format('HH:mm') ) const isSameYear = dayJsCloseTime.isSame(dayJsNow, 'year') const isSameDay = dayJsCloseTime.isSame(dayJsNow, 'day') let newCloseTime = closeDate ? dayjs(`${closeDate}T${closeHoursMinutes}`).valueOf() : undefined function onSave(customTime?: number) { if (customTime) { newCloseTime = customTime setCloseDate(dayjs(newCloseTime).format('YYYY-MM-DD')) setCloseHoursMinutes(dayjs(newCloseTime).format('HH:mm')) } if (!newCloseTime) return if (newCloseTime === closeTime) setIsEditingCloseTime(false) else { const content = contract.description const formattedCloseDate = dayjs(newCloseTime).format('YYYY-MM-DD h:mm a') const editor = new Editor({ content, extensions: editorExtensions() }) editor.commands.focus('end') insertContent( editor, `

Close date updated to ${formattedCloseDate}

` ) updateContract(contract.id, { closeTime: newCloseTime, description: editor.getJSON(), }) setIsEditingCloseTime(false) } } return ( <> e.stopPropagation()} onChange={(e) => setCloseDate(e.target.value)} min={isClient ? Date.now() : undefined} value={closeDate} /> e.stopPropagation()} onChange={(e) => setCloseHoursMinutes(e.target.value)} min="00:00" value={closeHoursMinutes} /> !disabled && isCreator && setIsEditingCloseTime(true)} > {isSameDay && isClient ? ( {fromNow(closeTime)} ) : isSameYear ? ( dayJsCloseTime.format('MMM D') ) : ( dayJsCloseTime.format('MMM D, YYYY') )} {isCreator && !disabled && } ) } const BAD_CREATOR_THRESHOLD = 0.8