Inga/bettingfix (#879)
* making betting action panels much more minimal, particularly for mobile * added tiny follow button
This commit is contained in:
parent
ccf02bdba8
commit
8aaaf5e9e0
|
@ -11,6 +11,7 @@ export type ColorType =
|
||||||
| 'gray'
|
| 'gray'
|
||||||
| 'gradient'
|
| 'gradient'
|
||||||
| 'gray-white'
|
| 'gray-white'
|
||||||
|
| 'highlight-blue'
|
||||||
|
|
||||||
export function Button(props: {
|
export function Button(props: {
|
||||||
className?: string
|
className?: string
|
||||||
|
@ -56,7 +57,9 @@ export function Button(props: {
|
||||||
color === 'gradient' &&
|
color === 'gradient' &&
|
||||||
'border-none bg-gradient-to-r from-indigo-500 to-blue-500 text-white hover:from-indigo-700 hover:to-blue-700',
|
'border-none bg-gradient-to-r from-indigo-500 to-blue-500 text-white hover:from-indigo-700 hover:to-blue-700',
|
||||||
color === 'gray-white' &&
|
color === 'gray-white' &&
|
||||||
'border-none bg-white text-gray-500 shadow-none hover:bg-gray-200',
|
'text-greyscale-6 hover:bg-greyscale-2 border-none shadow-none',
|
||||||
|
color === 'highlight-blue' &&
|
||||||
|
'text-highlight-blue border-none shadow-none',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
import {
|
import { ClockIcon } from '@heroicons/react/outline'
|
||||||
ClockIcon,
|
|
||||||
DatabaseIcon,
|
|
||||||
PencilIcon,
|
|
||||||
UserGroupIcon,
|
|
||||||
} from '@heroicons/react/outline'
|
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import { Editor } from '@tiptap/react'
|
import { Editor } from '@tiptap/react'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
@ -16,9 +11,8 @@ import { DateTimeTooltip } from '../datetime-tooltip'
|
||||||
import { fromNow } from 'web/lib/util/time'
|
import { fromNow } from 'web/lib/util/time'
|
||||||
import { Avatar } from '../avatar'
|
import { Avatar } from '../avatar'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { ContractInfoDialog } from './contract-info-dialog'
|
|
||||||
import NewContractBadge from '../new-contract-badge'
|
import NewContractBadge from '../new-contract-badge'
|
||||||
import { UserFollowButton } from '../follow-button'
|
import { MiniUserFollowButton } from '../follow-button'
|
||||||
import { DAY_MS } from 'common/util/time'
|
import { DAY_MS } from 'common/util/time'
|
||||||
import { useUser } from 'web/hooks/use-user'
|
import { useUser } from 'web/hooks/use-user'
|
||||||
import { exhibitExts } from 'common/util/parse'
|
import { exhibitExts } from 'common/util/parse'
|
||||||
|
@ -34,6 +28,8 @@ import { UserLink } from 'web/components/user-link'
|
||||||
import { FeaturedContractBadge } from 'web/components/contract/featured-contract-badge'
|
import { FeaturedContractBadge } from 'web/components/contract/featured-contract-badge'
|
||||||
import { Tooltip } from 'web/components/tooltip'
|
import { Tooltip } from 'web/components/tooltip'
|
||||||
import { useWindowSize } from 'web/hooks/use-window-size'
|
import { useWindowSize } from 'web/hooks/use-window-size'
|
||||||
|
import { ExtraContractActionsRow } from './extra-contract-actions-row'
|
||||||
|
import { PlusCircleIcon } from '@heroicons/react/solid'
|
||||||
|
|
||||||
export type ShowTime = 'resolve-date' | 'close-date'
|
export type ShowTime = 'resolve-date' | 'close-date'
|
||||||
|
|
||||||
|
@ -104,90 +100,174 @@ export function AvatarDetails(props: {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useIsMobile() {
|
||||||
|
const { width } = useWindowSize()
|
||||||
|
return (width ?? 0) < 600
|
||||||
|
}
|
||||||
|
|
||||||
export function ContractDetails(props: {
|
export function ContractDetails(props: {
|
||||||
contract: Contract
|
contract: Contract
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
}) {
|
}) {
|
||||||
const { contract, disabled } = props
|
const { contract, disabled } = props
|
||||||
const {
|
const { creatorName, creatorUsername, creatorId, creatorAvatarUrl } = contract
|
||||||
closeTime,
|
const { resolvedDate } = contractMetrics(contract)
|
||||||
creatorName,
|
|
||||||
creatorUsername,
|
|
||||||
creatorId,
|
|
||||||
creatorAvatarUrl,
|
|
||||||
resolutionTime,
|
|
||||||
} = contract
|
|
||||||
const { volumeLabel, resolvedDate } = contractMetrics(contract)
|
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
const isCreator = user?.id === creatorId
|
const isCreator = user?.id === creatorId
|
||||||
const [open, setOpen] = useState(false)
|
const isMobile = useIsMobile()
|
||||||
const { width } = useWindowSize()
|
|
||||||
const isMobile = (width ?? 0) < 600
|
|
||||||
const groupToDisplay = getGroupLinkToDisplay(contract)
|
|
||||||
const groupInfo = groupToDisplay ? (
|
|
||||||
<Link prefetch={false} href={groupPath(groupToDisplay.slug)}>
|
|
||||||
<a
|
|
||||||
className={clsx(
|
|
||||||
linkClass,
|
|
||||||
'flex flex-row items-center truncate pr-0 sm:pr-2',
|
|
||||||
isMobile ? 'max-w-[140px]' : 'max-w-[250px]'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<UserGroupIcon className="mx-1 inline h-5 w-5 shrink-0" />
|
|
||||||
<span className="items-center truncate">{groupToDisplay.name}</span>
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
) : (
|
|
||||||
<Button
|
|
||||||
size={'xs'}
|
|
||||||
className={'max-w-[200px] pr-2'}
|
|
||||||
color={'gray-white'}
|
|
||||||
onClick={() => !groupToDisplay && setOpen(true)}
|
|
||||||
>
|
|
||||||
<Row>
|
|
||||||
<UserGroupIcon className="mx-1 inline h-5 w-5 shrink-0" />
|
|
||||||
<span className="truncate">No Group</span>
|
|
||||||
</Row>
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row className="flex-1 flex-wrap items-center gap-2 text-sm text-gray-500 md:gap-x-4 md:gap-y-2">
|
<Col>
|
||||||
<Row className="items-center gap-2">
|
<Row>
|
||||||
<Avatar
|
<Avatar
|
||||||
username={creatorUsername}
|
username={creatorUsername}
|
||||||
avatarUrl={creatorAvatarUrl}
|
avatarUrl={creatorAvatarUrl}
|
||||||
noLink={disabled}
|
noLink={disabled}
|
||||||
size={6}
|
size={9}
|
||||||
|
className="mr-1.5"
|
||||||
/>
|
/>
|
||||||
|
{!disabled && (
|
||||||
|
<div className="absolute mt-3 ml-[11px]">
|
||||||
|
<MiniUserFollowButton userId={creatorId} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<Col className="text-greyscale-6 ml-2 flex-1 flex-wrap text-sm">
|
||||||
|
<Row className="w-full justify-between ">
|
||||||
{disabled ? (
|
{disabled ? (
|
||||||
creatorName
|
creatorName
|
||||||
) : (
|
) : (
|
||||||
<UserLink
|
<UserLink
|
||||||
className="whitespace-nowrap"
|
className="my-auto whitespace-nowrap"
|
||||||
name={creatorName}
|
name={creatorName}
|
||||||
username={creatorUsername}
|
username={creatorUsername}
|
||||||
short={isMobile}
|
short={isMobile}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!disabled && <UserFollowButton userId={creatorId} small />}
|
|
||||||
</Row>
|
</Row>
|
||||||
|
<Row className="text-2xs text-greyscale-4 gap-2 sm:text-xs">
|
||||||
|
<CloseOrResolveTime
|
||||||
|
contract={contract}
|
||||||
|
resolvedDate={resolvedDate}
|
||||||
|
isCreator={isCreator}
|
||||||
|
/>
|
||||||
|
{!isMobile && (
|
||||||
|
<MarketGroups
|
||||||
|
contract={contract}
|
||||||
|
isMobile={isMobile}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Row>
|
||||||
|
</Col>
|
||||||
|
<div className="mt-0">
|
||||||
|
<ExtraContractActionsRow contract={contract} />
|
||||||
|
</div>
|
||||||
|
</Row>
|
||||||
|
{/* GROUPS */}
|
||||||
|
{isMobile && (
|
||||||
|
<div className="mt-2">
|
||||||
|
<MarketGroups
|
||||||
|
contract={contract}
|
||||||
|
isMobile={isMobile}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Col>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CloseOrResolveTime(props: {
|
||||||
|
contract: Contract
|
||||||
|
resolvedDate: any
|
||||||
|
isCreator: boolean
|
||||||
|
}) {
|
||||||
|
const { contract, resolvedDate, isCreator } = props
|
||||||
|
const { resolutionTime, closeTime } = contract
|
||||||
|
console.log(closeTime, resolvedDate)
|
||||||
|
if (!!closeTime || !!resolvedDate) {
|
||||||
|
return (
|
||||||
|
<Row className="select-none items-center gap-1">
|
||||||
|
{resolvedDate && resolutionTime ? (
|
||||||
|
<>
|
||||||
|
<DateTimeTooltip text="Market resolved:" time={resolutionTime}>
|
||||||
<Row>
|
<Row>
|
||||||
|
<div>resolved </div>
|
||||||
|
{resolvedDate}
|
||||||
|
</Row>
|
||||||
|
</DateTimeTooltip>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{!resolvedDate && closeTime && (
|
||||||
|
<Row>
|
||||||
|
{dayjs().isBefore(closeTime) && <div>closes </div>}
|
||||||
|
{!dayjs().isBefore(closeTime) && <div>closed </div>}
|
||||||
|
<EditableCloseDate
|
||||||
|
closeTime={closeTime}
|
||||||
|
contract={contract}
|
||||||
|
isCreator={isCreator ?? false}
|
||||||
|
/>
|
||||||
|
</Row>
|
||||||
|
)}
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
} else return <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MarketGroups(props: {
|
||||||
|
contract: Contract
|
||||||
|
isMobile: boolean | undefined
|
||||||
|
disabled: boolean | undefined
|
||||||
|
}) {
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
|
const user = useUser()
|
||||||
|
const { contract, isMobile, disabled } = props
|
||||||
|
const groupToDisplay = getGroupLinkToDisplay(contract)
|
||||||
|
const groupInfo = groupToDisplay ? (
|
||||||
|
<Link prefetch={false} href={groupPath(groupToDisplay.slug)}>
|
||||||
|
<a
|
||||||
|
className={clsx(
|
||||||
|
'flex flex-row items-center truncate pr-1',
|
||||||
|
isMobile ? 'max-w-[140px]' : 'max-w-[250px]'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="bg-greyscale-4 hover:bg-greyscale-3 text-2xs items-center truncate rounded-full px-2 text-white sm:text-xs">
|
||||||
|
{groupToDisplay.name}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<Row
|
||||||
|
className={clsx(
|
||||||
|
'cursor-default select-none items-center truncate pr-1',
|
||||||
|
isMobile ? 'max-w-[140px]' : 'max-w-[250px]'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
'bg-greyscale-4 text-2xs items-center truncate rounded-full px-2 text-white sm:text-xs'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
No Group
|
||||||
|
</div>
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Row className="align-middle">
|
||||||
{disabled ? (
|
{disabled ? (
|
||||||
groupInfo
|
{ groupInfo }
|
||||||
) : !groupToDisplay && !user ? (
|
|
||||||
<div />
|
|
||||||
) : (
|
) : (
|
||||||
<Row>
|
<Row>
|
||||||
{groupInfo}
|
{groupInfo}
|
||||||
{user && groupToDisplay && (
|
{user && (
|
||||||
<Button
|
<button
|
||||||
size={'xs'}
|
className="text-greyscale-4 hover:text-greyscale-3"
|
||||||
color={'gray-white'}
|
|
||||||
onClick={() => setOpen(!open)}
|
onClick={() => setOpen(!open)}
|
||||||
>
|
>
|
||||||
<PencilIcon className="mb-0.5 mr-0.5 inline h-4 w-4 shrink-0" />
|
<PlusCircleIcon className="mb-0.5 mr-0.5 inline h-4 w-4 shrink-0" />
|
||||||
</Button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
|
@ -201,45 +281,7 @@ export function ContractDetails(props: {
|
||||||
<ContractGroupsList contract={contract} user={user} />
|
<ContractGroupsList contract={contract} user={user} />
|
||||||
</Col>
|
</Col>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
{(!!closeTime || !!resolvedDate) && (
|
|
||||||
<Row className="hidden items-center gap-1 md:inline-flex">
|
|
||||||
{resolvedDate && resolutionTime ? (
|
|
||||||
<>
|
|
||||||
<ClockIcon className="h-5 w-5" />
|
|
||||||
<DateTimeTooltip text="Market resolved:" time={resolutionTime}>
|
|
||||||
{resolvedDate}
|
|
||||||
</DateTimeTooltip>
|
|
||||||
</>
|
</>
|
||||||
) : null}
|
|
||||||
|
|
||||||
{!resolvedDate && closeTime && user && (
|
|
||||||
<>
|
|
||||||
<ClockIcon className="h-5 w-5" />
|
|
||||||
<EditableCloseDate
|
|
||||||
closeTime={closeTime}
|
|
||||||
contract={contract}
|
|
||||||
isCreator={isCreator ?? false}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Row>
|
|
||||||
)}
|
|
||||||
{user && (
|
|
||||||
<>
|
|
||||||
<Row className="hidden items-center gap-1 md:inline-flex">
|
|
||||||
<DatabaseIcon className="h-5 w-5" />
|
|
||||||
<div className="whitespace-nowrap">{volumeLabel}</div>
|
|
||||||
</Row>
|
|
||||||
{!disabled && (
|
|
||||||
<ContractInfoDialog
|
|
||||||
contract={contract}
|
|
||||||
className={'hidden md:inline-flex'}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Row>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,12 +322,12 @@ export function ExtraMobileContractDetails(props: {
|
||||||
!resolvedDate &&
|
!resolvedDate &&
|
||||||
closeTime && (
|
closeTime && (
|
||||||
<Col className={'items-center text-sm text-gray-500'}>
|
<Col className={'items-center text-sm text-gray-500'}>
|
||||||
|
<Row className={'text-gray-400'}>Closes </Row>
|
||||||
<EditableCloseDate
|
<EditableCloseDate
|
||||||
closeTime={closeTime}
|
closeTime={closeTime}
|
||||||
contract={contract}
|
contract={contract}
|
||||||
isCreator={creatorId === user?.id}
|
isCreator={creatorId === user?.id}
|
||||||
/>
|
/>
|
||||||
<Row className={'text-gray-400'}>Ends</Row>
|
|
||||||
</Col>
|
</Col>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { deleteField } from 'firebase/firestore'
|
||||||
import ShortToggle from '../widgets/short-toggle'
|
import ShortToggle from '../widgets/short-toggle'
|
||||||
import { DuplicateContractButton } from '../copy-contract-button'
|
import { DuplicateContractButton } from '../copy-contract-button'
|
||||||
import { Row } from '../layout/row'
|
import { Row } from '../layout/row'
|
||||||
|
import { Button } from '../button'
|
||||||
|
|
||||||
export const contractDetailsButtonClassName =
|
export const contractDetailsButtonClassName =
|
||||||
'group flex items-center rounded-md px-3 py-2 text-sm font-medium cursor-pointer hover:bg-gray-100 text-gray-400 hover:text-gray-500'
|
'group flex items-center rounded-md px-3 py-2 text-sm font-medium cursor-pointer hover:bg-gray-100 text-gray-400 hover:text-gray-500'
|
||||||
|
@ -67,19 +68,21 @@ export function ContractInfoDialog(props: {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button
|
<Button
|
||||||
|
size="sm"
|
||||||
|
color="gray-white"
|
||||||
className={clsx(contractDetailsButtonClassName, className)}
|
className={clsx(contractDetailsButtonClassName, className)}
|
||||||
onClick={() => setOpen(true)}
|
onClick={() => setOpen(true)}
|
||||||
>
|
>
|
||||||
<DotsHorizontalIcon
|
<DotsHorizontalIcon
|
||||||
className={clsx('h-6 w-6 flex-shrink-0')}
|
className={clsx('h-5 w-5 flex-shrink-0')}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
</button>
|
</Button>
|
||||||
|
|
||||||
<Modal open={open} setOpen={setOpen}>
|
<Modal open={open} setOpen={setOpen}>
|
||||||
<Col className="gap-4 rounded bg-white p-6">
|
<Col className="gap-4 rounded bg-white p-6">
|
||||||
<Title className="!mt-0 !mb-0" text="Market info" />
|
<Title className="!mt-0 !mb-0" text="This Market" />
|
||||||
|
|
||||||
<table className="table-compact table-zebra table w-full text-gray-500">
|
<table className="table-compact table-zebra table w-full text-gray-500">
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
|
@ -25,11 +25,11 @@ import {
|
||||||
NumericContract,
|
NumericContract,
|
||||||
PseudoNumericContract,
|
PseudoNumericContract,
|
||||||
} from 'common/contract'
|
} from 'common/contract'
|
||||||
import { ContractDetails, ExtraMobileContractDetails } from './contract-details'
|
import { ContractDetails } from './contract-details'
|
||||||
import { NumericGraph } from './numeric-graph'
|
import { NumericGraph } from './numeric-graph'
|
||||||
|
|
||||||
const OverviewQuestion = (props: { text: string }) => (
|
const OverviewQuestion = (props: { text: string }) => (
|
||||||
<Linkify className="text-2xl text-indigo-700 md:text-3xl" text={props.text} />
|
<Linkify className="text-lg text-indigo-700 sm:text-2xl" text={props.text} />
|
||||||
)
|
)
|
||||||
|
|
||||||
const BetWidget = (props: { contract: CPMMContract }) => {
|
const BetWidget = (props: { contract: CPMMContract }) => {
|
||||||
|
@ -73,7 +73,7 @@ const BinaryOverview = (props: { contract: BinaryContract; bets: Bet[] }) => {
|
||||||
const { contract, bets } = props
|
const { contract, bets } = props
|
||||||
return (
|
return (
|
||||||
<Col className="gap-1 md:gap-2">
|
<Col className="gap-1 md:gap-2">
|
||||||
<Col className="gap-3 px-2 sm:gap-4">
|
<Col className="gap-1 px-2">
|
||||||
<ContractDetails contract={contract} />
|
<ContractDetails contract={contract} />
|
||||||
<Row className="justify-between gap-4">
|
<Row className="justify-between gap-4">
|
||||||
<OverviewQuestion text={contract.question} />
|
<OverviewQuestion text={contract.question} />
|
||||||
|
@ -85,7 +85,6 @@ const BinaryOverview = (props: { contract: BinaryContract; bets: Bet[] }) => {
|
||||||
</Row>
|
</Row>
|
||||||
<Row className="items-center justify-between gap-4 xl:hidden">
|
<Row className="items-center justify-between gap-4 xl:hidden">
|
||||||
<BinaryResolutionOrChance contract={contract} />
|
<BinaryResolutionOrChance contract={contract} />
|
||||||
<ExtraMobileContractDetails contract={contract} />
|
|
||||||
{tradingAllowed(contract) && (
|
{tradingAllowed(contract) && (
|
||||||
<BetWidget contract={contract as CPMMBinaryContract} />
|
<BetWidget contract={contract as CPMMBinaryContract} />
|
||||||
)}
|
)}
|
||||||
|
@ -113,10 +112,6 @@ const ChoiceOverview = (props: {
|
||||||
</Col>
|
</Col>
|
||||||
<Col className={'mb-1 gap-y-2'}>
|
<Col className={'mb-1 gap-y-2'}>
|
||||||
<AnswersGraph contract={contract} bets={[...bets].reverse()} />
|
<AnswersGraph contract={contract} bets={[...bets].reverse()} />
|
||||||
<ExtraMobileContractDetails
|
|
||||||
contract={contract}
|
|
||||||
forceShowVolume={true}
|
|
||||||
/>
|
|
||||||
</Col>
|
</Col>
|
||||||
</Col>
|
</Col>
|
||||||
)
|
)
|
||||||
|
@ -140,7 +135,6 @@ const PseudoNumericOverview = (props: {
|
||||||
</Row>
|
</Row>
|
||||||
<Row className="items-center justify-between gap-4 xl:hidden">
|
<Row className="items-center justify-between gap-4 xl:hidden">
|
||||||
<PseudoNumericResolutionOrExpectation contract={contract} />
|
<PseudoNumericResolutionOrExpectation contract={contract} />
|
||||||
<ExtraMobileContractDetails contract={contract} />
|
|
||||||
{tradingAllowed(contract) && <BetWidget contract={contract} />}
|
{tradingAllowed(contract) && <BetWidget contract={contract} />}
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
|
@ -11,38 +11,29 @@ import { FollowMarketButton } from 'web/components/follow-market-button'
|
||||||
import { LikeMarketButton } from 'web/components/contract/like-market-button'
|
import { LikeMarketButton } from 'web/components/contract/like-market-button'
|
||||||
import { ContractInfoDialog } from 'web/components/contract/contract-info-dialog'
|
import { ContractInfoDialog } from 'web/components/contract/contract-info-dialog'
|
||||||
import { Col } from 'web/components/layout/col'
|
import { Col } from 'web/components/layout/col'
|
||||||
import { withTracking } from 'web/lib/service/analytics'
|
|
||||||
import { CreateChallengeModal } from 'web/components/challenges/create-challenge-modal'
|
|
||||||
import { CHALLENGES_ENABLED } from 'common/challenge'
|
|
||||||
import ChallengeIcon from 'web/lib/icons/challenge-icon'
|
|
||||||
|
|
||||||
export function ExtraContractActionsRow(props: { contract: Contract }) {
|
export function ExtraContractActionsRow(props: { contract: Contract }) {
|
||||||
const { contract } = props
|
const { contract } = props
|
||||||
const { outcomeType, resolution } = contract
|
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
const [isShareOpen, setShareOpen] = useState(false)
|
const [isShareOpen, setShareOpen] = useState(false)
|
||||||
const [openCreateChallengeModal, setOpenCreateChallengeModal] =
|
|
||||||
useState(false)
|
|
||||||
const showChallenge =
|
|
||||||
user && outcomeType === 'BINARY' && !resolution && CHALLENGES_ENABLED
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row className={'mt-0.5 justify-around sm:mt-2 lg:justify-start'}>
|
<Row>
|
||||||
|
<FollowMarketButton contract={contract} user={user} />
|
||||||
|
{user?.id !== contract.creatorId && (
|
||||||
|
<LikeMarketButton contract={contract} user={user} />
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
size="lg"
|
size="sm"
|
||||||
color="gray-white"
|
color="gray-white"
|
||||||
className={'flex'}
|
className={'flex'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShareOpen(true)
|
setShareOpen(true)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Col className={'items-center sm:flex-row'}>
|
<Row>
|
||||||
<ShareIcon
|
<ShareIcon className={clsx('h-5 w-5')} aria-hidden="true" />
|
||||||
className={clsx('h-[24px] w-5 sm:mr-2')}
|
</Row>
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
<span>Share</span>
|
|
||||||
</Col>
|
|
||||||
<ShareModal
|
<ShareModal
|
||||||
isOpen={isShareOpen}
|
isOpen={isShareOpen}
|
||||||
setOpen={setShareOpen}
|
setOpen={setShareOpen}
|
||||||
|
@ -50,35 +41,7 @@ export function ExtraContractActionsRow(props: { contract: Contract }) {
|
||||||
user={user}
|
user={user}
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
|
<Col className={'justify-center'}>
|
||||||
{showChallenge && (
|
|
||||||
<Button
|
|
||||||
size="lg"
|
|
||||||
color="gray-white"
|
|
||||||
className="max-w-xs self-center"
|
|
||||||
onClick={withTracking(
|
|
||||||
() => setOpenCreateChallengeModal(true),
|
|
||||||
'click challenge button'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Col className="items-center sm:flex-row">
|
|
||||||
<ChallengeIcon className="mx-auto h-[24px] w-5 text-gray-500 sm:mr-2" />
|
|
||||||
<span>Challenge</span>
|
|
||||||
</Col>
|
|
||||||
<CreateChallengeModal
|
|
||||||
isOpen={openCreateChallengeModal}
|
|
||||||
setOpen={setOpenCreateChallengeModal}
|
|
||||||
user={user}
|
|
||||||
contract={contract}
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<FollowMarketButton contract={contract} user={user} />
|
|
||||||
{user?.id !== contract.creatorId && (
|
|
||||||
<LikeMarketButton contract={contract} user={user} />
|
|
||||||
)}
|
|
||||||
<Col className={'justify-center md:hidden'}>
|
|
||||||
<ContractInfoDialog contract={contract} />
|
<ContractInfoDialog contract={contract} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
|
@ -38,15 +38,16 @@ export function LikeMarketButton(props: {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
size={'lg'}
|
size={'sm'}
|
||||||
className={'max-w-xs self-center'}
|
className={'max-w-xs self-center'}
|
||||||
color={'gray-white'}
|
color={'gray-white'}
|
||||||
onClick={onLike}
|
onClick={onLike}
|
||||||
>
|
>
|
||||||
<Col className={'items-center sm:flex-row'}>
|
<Col className={'relative items-center sm:flex-row'}>
|
||||||
<HeartIcon
|
<HeartIcon
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'h-[24px] w-5 sm:mr-2',
|
'h-5 w-5 sm:h-6 sm:w-6',
|
||||||
|
totalTipped > 0 ? 'mr-2' : '',
|
||||||
user &&
|
user &&
|
||||||
(userLikedContractIds?.includes(contract.id) ||
|
(userLikedContractIds?.includes(contract.id) ||
|
||||||
(!likes && contract.likedByUserIds?.includes(user.id)))
|
(!likes && contract.likedByUserIds?.includes(user.id)))
|
||||||
|
@ -54,7 +55,18 @@ export function LikeMarketButton(props: {
|
||||||
: ''
|
: ''
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
Tip {totalTipped > 0 ? `(${formatMoney(totalTipped)})` : ''}
|
{totalTipped > 0 && (
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
'bg-greyscale-6 absolute ml-3.5 mt-2 h-4 w-4 rounded-full align-middle text-white sm:mt-3 sm:h-5 sm:w-5 sm:px-1',
|
||||||
|
totalTipped > 99
|
||||||
|
? 'text-[0.4rem] sm:text-[0.5rem]'
|
||||||
|
: 'sm:text-2xs text-[0.5rem]'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{totalTipped}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</Col>
|
</Col>
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
import { CheckCircleIcon, PlusCircleIcon } from '@heroicons/react/solid'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { useFollows } from 'web/hooks/use-follows'
|
import { useFollows } from 'web/hooks/use-follows'
|
||||||
import { useUser } from 'web/hooks/use-user'
|
import { useUser } from 'web/hooks/use-user'
|
||||||
import { follow, unfollow } from 'web/lib/firebase/users'
|
import { follow, unfollow } from 'web/lib/firebase/users'
|
||||||
|
@ -54,18 +56,73 @@ export function FollowButton(props: {
|
||||||
|
|
||||||
export function UserFollowButton(props: { userId: string; small?: boolean }) {
|
export function UserFollowButton(props: { userId: string; small?: boolean }) {
|
||||||
const { userId, small } = props
|
const { userId, small } = props
|
||||||
const currentUser = useUser()
|
const user = useUser()
|
||||||
const following = useFollows(currentUser?.id)
|
const following = useFollows(user?.id)
|
||||||
const isFollowing = following?.includes(userId)
|
const isFollowing = following?.includes(userId)
|
||||||
|
|
||||||
if (!currentUser || currentUser.id === userId) return null
|
if (!user || user.id === userId) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FollowButton
|
<FollowButton
|
||||||
isFollowing={isFollowing}
|
isFollowing={isFollowing}
|
||||||
onFollow={() => follow(currentUser.id, userId)}
|
onFollow={() => follow(user.id, userId)}
|
||||||
onUnfollow={() => unfollow(currentUser.id, userId)}
|
onUnfollow={() => unfollow(user.id, userId)}
|
||||||
small={small}
|
small={small}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function MiniUserFollowButton(props: { userId: string }) {
|
||||||
|
const { userId } = props
|
||||||
|
const user = useUser()
|
||||||
|
const following = useFollows(user?.id)
|
||||||
|
const isFollowing = following?.includes(userId)
|
||||||
|
const isFirstRender = useRef(true)
|
||||||
|
const [justFollowed, setJustFollowed] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isFirstRender.current) {
|
||||||
|
if (isFollowing != undefined) {
|
||||||
|
isFirstRender.current = false
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (isFollowing) {
|
||||||
|
setJustFollowed(true)
|
||||||
|
setTimeout(() => {
|
||||||
|
setJustFollowed(false)
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
}, [isFollowing])
|
||||||
|
|
||||||
|
if (justFollowed) {
|
||||||
|
return (
|
||||||
|
<CheckCircleIcon
|
||||||
|
className={clsx(
|
||||||
|
'text-highlight-blue ml-3 mt-2 h-5 w-5 rounded-full bg-white sm:mr-2'
|
||||||
|
)}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!user ||
|
||||||
|
user.id === userId ||
|
||||||
|
isFollowing ||
|
||||||
|
!user ||
|
||||||
|
isFollowing === undefined
|
||||||
|
)
|
||||||
|
return null
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<button onClick={withTracking(() => follow(user.id, userId), 'follow')}>
|
||||||
|
<PlusCircleIcon
|
||||||
|
className={clsx(
|
||||||
|
'text-highlight-blue hover:text-hover-blue mt-2 ml-3 h-5 w-5 rounded-full bg-white sm:mr-2'
|
||||||
|
)}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ export const FollowMarketButton = (props: {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
size={'lg'}
|
size={'sm'}
|
||||||
color={'gray-white'}
|
color={'gray-white'}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (!user) return firebaseLogin()
|
if (!user) return firebaseLogin()
|
||||||
|
@ -56,13 +56,19 @@ export const FollowMarketButton = (props: {
|
||||||
>
|
>
|
||||||
{followers?.includes(user?.id ?? 'nope') ? (
|
{followers?.includes(user?.id ?? 'nope') ? (
|
||||||
<Col className={'items-center gap-x-2 sm:flex-row'}>
|
<Col className={'items-center gap-x-2 sm:flex-row'}>
|
||||||
<EyeOffIcon className={clsx('h-6 w-6')} aria-hidden="true" />
|
<EyeOffIcon
|
||||||
Unwatch
|
className={clsx('h-5 w-5 sm:h-6 sm:w-6')}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
{/* Unwatch */}
|
||||||
</Col>
|
</Col>
|
||||||
) : (
|
) : (
|
||||||
<Col className={'items-center gap-x-2 sm:flex-row'}>
|
<Col className={'items-center gap-x-2 sm:flex-row'}>
|
||||||
<EyeIcon className={clsx('h-6 w-6')} aria-hidden="true" />
|
<EyeIcon
|
||||||
Watch
|
className={clsx('h-5 w-5 sm:h-6 sm:w-6')}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
{/* Watch */}
|
||||||
</Col>
|
</Col>
|
||||||
)}
|
)}
|
||||||
<WatchMarketModal
|
<WatchMarketModal
|
||||||
|
|
|
@ -37,7 +37,6 @@ import { User } from 'common/user'
|
||||||
import { ContractComment } from 'common/comment'
|
import { ContractComment } from 'common/comment'
|
||||||
import { getOpenGraphProps } from 'common/contract-details'
|
import { getOpenGraphProps } from 'common/contract-details'
|
||||||
import { ContractDescription } from 'web/components/contract/contract-description'
|
import { ContractDescription } from 'web/components/contract/contract-description'
|
||||||
import { ExtraContractActionsRow } from 'web/components/contract/extra-contract-actions-row'
|
|
||||||
import {
|
import {
|
||||||
ContractLeaderboard,
|
ContractLeaderboard,
|
||||||
ContractTopTrades,
|
ContractTopTrades,
|
||||||
|
@ -257,7 +256,6 @@ export function ContractPageContent(
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ContractOverview contract={contract} bets={nonChallengeBets} />
|
<ContractOverview contract={contract} bets={nonChallengeBets} />
|
||||||
<ExtraContractActionsRow contract={contract} />
|
|
||||||
<ContractDescription className="mb-6 px-2" contract={contract} />
|
<ContractDescription className="mb-6 px-2" contract={contract} />
|
||||||
|
|
||||||
{outcomeType === 'NUMERIC' && (
|
{outcomeType === 'NUMERIC' && (
|
||||||
|
|
|
@ -26,6 +26,8 @@ module.exports = {
|
||||||
'greyscale-5': '#9191A7',
|
'greyscale-5': '#9191A7',
|
||||||
'greyscale-6': '#66667C',
|
'greyscale-6': '#66667C',
|
||||||
'greyscale-7': '#111140',
|
'greyscale-7': '#111140',
|
||||||
|
'highlight-blue': '#5BCEFF',
|
||||||
|
'hover-blue': '#90DEFF',
|
||||||
},
|
},
|
||||||
typography: {
|
typography: {
|
||||||
quoteless: {
|
quoteless: {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user