import { ClockIcon, DatabaseIcon, PencilIcon, TrendingUpIcon, UserGroupIcon, } from '@heroicons/react/outline' import { Row } from '../layout/row' import { formatMoney } from 'common/util/format' import { UserLink } from '../user-page' import { Contract, contractMetrics, contractPool, updateContract, } from 'web/lib/firebase/contracts' import dayjs from 'dayjs' import { DateTimeTooltip } from '../datetime-tooltip' import { fromNow } from 'web/lib/util/time' import { Avatar } from '../avatar' import { useState } from 'react' import { ContractInfoDialog } from './contract-info-dialog' import { Bet } from 'common/bet' import NewContractBadge from '../new-contract-badge' import { CATEGORY_LIST } from 'common/categories' import { TagsList } from '../tags-list' import { UserFollowButton } from '../follow-button' import { groupPath } from 'web/lib/firebase/groups' import { SiteLink } from 'web/components/site-link' import { DAY_MS } from 'common/util/time' import { useGroupsWithContract } from 'web/hooks/use-group' import { ShareIconButton } from 'web/components/share-icon-button' import { useUser } from 'web/hooks/use-user' export type ShowTime = 'resolve-date' | 'close-date' export function MiscDetails(props: { contract: Contract showHotVolume?: boolean showTime?: ShowTime }) { const { contract, showHotVolume, showTime } = props const { volume, volume24Hours, closeTime, tags, isResolved, createdTime, resolutionTime, } = contract // Show at most one category that this contract is tagged by const categories = CATEGORY_LIST.filter((category) => tags.map((t) => t.toLowerCase()).includes(category) ).slice(0, 1) const isNew = createdTime > Date.now() - DAY_MS && !isResolved return ( {showHotVolume ? ( {formatMoney(volume24Hours)} ) : showTime === 'close-date' ? ( {(closeTime || 0) < Date.now() ? 'Closed' : 'Closes'}{' '} {fromNow(closeTime || 0)} ) : showTime === 'resolve-date' && resolutionTime !== undefined ? ( {'Resolved '} {fromNow(resolutionTime || 0)} ) : volume > 0 || !isNew ? ( {contractPool(contract)} pool ) : ( )} {categories.length > 0 && ( )} ) } export function AvatarDetails(props: { contract: Contract }) { const { contract } = props const { creatorName, creatorUsername } = contract return ( ) } export function AbbrContractDetails(props: { contract: Contract showHotVolume?: boolean showTime?: ShowTime }) { const { contract, showHotVolume, showTime } = props return ( ) } export function ContractDetails(props: { contract: Contract bets: Bet[] isCreator?: boolean disabled?: boolean }) { const { contract, bets, isCreator, disabled } = props const { closeTime, creatorName, creatorUsername, creatorId } = contract const { volumeLabel, resolvedDate } = contractMetrics(contract) const groups = (useGroupsWithContract(contract.id) ?? []).sort((g1, g2) => { return g2.createdTime - g1.createdTime }) const user = useUser() const groupsUserIsMemberOf = groups ? groups.filter((g) => g.memberIds.includes(contract.creatorId)) : [] const groupsUserIsCreatorOf = groups ? groups.filter((g) => g.creatorId === contract.creatorId) : [] // Priorities for which group the contract belongs to: // In order of created most recently // Group that the contract owner created // Group the contract owner is a member of // Any group the contract is in const groupToDisplay = groupsUserIsCreatorOf.length > 0 ? groupsUserIsCreatorOf[0] : groupsUserIsMemberOf.length > 0 ? groupsUserIsMemberOf[0] : groups ? groups[0] : undefined return ( {disabled ? ( creatorName ) : ( )} {!disabled && } {groupToDisplay ? ( {groupToDisplay.name} ) : (
)} {(!!closeTime || !!resolvedDate) && ( {resolvedDate && contract.resolutionTime ? ( <> {resolvedDate} ) : null} {!resolvedDate && closeTime && ( <> )} )}
{volumeLabel}
{!disabled && } ) } // String version of the above, to send to the OpenGraph image generator export function contractTextDetails(contract: Contract) { const { closeTime, tags } = contract const { createdDate, resolvedDate, volumeLabel } = contractMetrics(contract) const hashtags = tags.map((tag) => `#${tag}`) return ( `${resolvedDate ? `${createdDate} - ${resolvedDate}` : createdDate}` + (closeTime ? ` • ${closeTime > Date.now() ? 'Closes' : 'Closed'} ${dayjs( closeTime ).format('MMM D, h:mma')}` : '') + ` • ${volumeLabel}` + (hashtags.length > 0 ? ` • ${hashtags.join(' ')}` : '') ) } function EditableCloseDate(props: { closeTime: number contract: Contract isCreator: boolean }) { const { closeTime, contract, isCreator } = props const [isEditingCloseTime, setIsEditingCloseTime] = useState(false) const [closeDate, setCloseDate] = useState( closeTime && dayjs(closeTime).format('YYYY-MM-DDTHH:mm') ) const isSameYear = dayjs(closeTime).isSame(dayjs(), 'year') const isSameDay = dayjs(closeTime).isSame(dayjs(), 'day') const onSave = () => { const newCloseTime = dayjs(closeDate).valueOf() if (newCloseTime === closeTime) setIsEditingCloseTime(false) else if (newCloseTime > Date.now()) { const { description } = contract const formattedCloseDate = dayjs(newCloseTime).format('YYYY-MM-DD h:mm a') const newDescription = `${description}\n\nClose date updated to ${formattedCloseDate}` updateContract(contract.id, { closeTime: newCloseTime, description: newDescription, }) setIsEditingCloseTime(false) } } return ( <> {isEditingCloseTime ? (
e.stopPropagation()} onChange={(e) => setCloseDate(e.target.value || '')} min={Date.now()} value={closeDate} />
) : ( Date.now() ? 'Trading ends:' : 'Trading ended:'} time={closeTime} > {isSameYear ? dayjs(closeTime).format('MMM D') : dayjs(closeTime).format('MMM D, YYYY')} {isSameDay && <> ({fromNow(closeTime)})} )} {isCreator && (isEditingCloseTime ? ( ) : ( ))} ) }