From 8922b370cc2e562e796ae3c58a2eb5e7f7609af1 Mon Sep 17 00:00:00 2001 From: Ian Philips Date: Thu, 1 Sep 2022 10:02:41 -0600 Subject: [PATCH] Show challenge on desktop, simplify modal --- .../challenges/create-challenge-modal.tsx | 111 +++++++----------- .../contract/extra-contract-actions-row.tsx | 28 ++++- web/components/contract/share-modal.tsx | 11 +- 3 files changed, 74 insertions(+), 76 deletions(-) diff --git a/web/components/challenges/create-challenge-modal.tsx b/web/components/challenges/create-challenge-modal.tsx index 6f91a6d4..72a8fd7b 100644 --- a/web/components/challenges/create-challenge-modal.tsx +++ b/web/components/challenges/create-challenge-modal.tsx @@ -18,7 +18,6 @@ import { NoLabel, YesLabel } from '../outcome-label' import { QRCode } from '../qr-code' import { copyToClipboard } from 'web/lib/util/copy' import { AmountInput } from '../amount-input' -import { getProbability } from 'common/calculate' import { createMarket } from 'web/lib/firebase/api' import { removeUndefinedProps } from 'common/util/object' import { FIXED_ANTE } from 'common/economy' @@ -26,6 +25,7 @@ import Textarea from 'react-expanding-textarea' import { useTextEditor } from 'web/components/editor' import { LoadingIndicator } from 'web/components/loading-indicator' import { track } from 'web/lib/service/analytics' +import { useWindowSize } from 'web/hooks/use-window-size' type challengeInfo = { amount: number @@ -110,8 +110,9 @@ function CreateChallengeForm(props: { const [isCreating, setIsCreating] = useState(false) const [finishedCreating, setFinishedCreating] = useState(false) const [error, setError] = useState('') - const [editingAcceptorAmount, setEditingAcceptorAmount] = useState(false) const defaultExpire = 'week' + const { width } = useWindowSize() + const isMobile = (width ?? 0) < 768 const [challengeInfo, setChallengeInfo] = useState({ expiresTime: dayjs().add(2, defaultExpire).valueOf(), @@ -147,7 +148,7 @@ function CreateChallengeForm(props: { setFinishedCreating(true) }} > - + <Title className="!mt-2 hidden sm:block" text="Challenge bet " /> <div className="mb-8"> Challenge a friend to bet on{' '} @@ -157,7 +158,7 @@ function CreateChallengeForm(props: { <Textarea placeholder="e.g. Will a Democrat be the next president?" className="input input-bordered mt-1 w-full resize-none" - autoFocus={true} + autoFocus={!isMobile} maxLength={MAX_QUESTION_LENGTH} value={challengeInfo.question} onChange={(e) => @@ -170,89 +171,59 @@ function CreateChallengeForm(props: { )} </div> - <div className="mt-2 flex flex-col flex-wrap justify-center gap-x-5 gap-y-2"> - <div>You'll bet:</div> - <Row - className={ - 'form-control w-full max-w-xs items-center justify-between gap-4 pr-3' - } - > - <AmountInput - amount={challengeInfo.amount || undefined} - onChange={(newAmount) => - setChallengeInfo((m: challengeInfo) => { - return { - ...m, - amount: newAmount ?? 0, - acceptorAmount: editingAcceptorAmount - ? m.acceptorAmount - : newAmount ?? 0, - } - }) - } - error={undefined} - label={'M$'} - inputClassName="w-24" - /> - <span className={''}>on</span> - {challengeInfo.outcome === 'YES' ? <YesLabel /> : <NoLabel />} - </Row> - <Row className={'mt-3 max-w-xs justify-end'}> - <Button - color={'gray-white'} - onClick={() => - setChallengeInfo((m: challengeInfo) => { - return { - ...m, - outcome: m.outcome === 'YES' ? 'NO' : 'YES', - } - }) + <Col className="mt-2 flex-wrap justify-center gap-x-5 gap-y-0 sm:gap-y-2"> + <Col> + <div>You'll bet:</div> + <Row + className={ + 'form-control w-full max-w-xs items-center justify-between gap-4 pr-3' } > - <SwitchVerticalIcon className={'h-6 w-6'} /> - </Button> - </Row> - <Row className={'items-center'}>If they bet:</Row> - <Row className={'max-w-xs items-center justify-between gap-4 pr-3'}> - <div className={'w-32 sm:mr-1'}> <AmountInput - amount={challengeInfo.acceptorAmount || undefined} - onChange={(newAmount) => { - setEditingAcceptorAmount(true) - + amount={challengeInfo.amount || undefined} + onChange={(newAmount) => setChallengeInfo((m: challengeInfo) => { return { ...m, + amount: newAmount ?? 0, acceptorAmount: newAmount ?? 0, } }) - }} + } error={undefined} label={'M$'} inputClassName="w-24" /> + <span className={''}>on</span> + {challengeInfo.outcome === 'YES' ? <YesLabel /> : <NoLabel />} + </Row> + <Row className={'max-w-xs justify-end'}> + <Button + color={'gray-white'} + onClick={() => + setChallengeInfo((m: challengeInfo) => { + return { + ...m, + outcome: m.outcome === 'YES' ? 'NO' : 'YES', + } + }) + } + > + <SwitchVerticalIcon className={'h-6 w-6'} /> + </Button> + </Row> + </Col> + <Row className={'items-center'}>If they bet:</Row> + <Row className={'max-w-xs items-center justify-between gap-4 pr-3'}> + <div className={'mt-1 w-32 sm:mr-1'}> + <span className={'ml-2 font-bold'}> + {formatMoney(challengeInfo.acceptorAmount)} + </span> </div> <span>on</span> {challengeInfo.outcome === 'YES' ? <NoLabel /> : <YesLabel />} </Row> - </div> - {contract && ( - <Button - size="2xs" - color="gray" - onClick={() => { - setEditingAcceptorAmount(true) - - const p = getProbability(contract) - const prob = challengeInfo.outcome === 'YES' ? p : 1 - p - const { amount } = challengeInfo - const acceptorAmount = Math.round(amount / prob - amount) - setChallengeInfo({ ...challengeInfo, acceptorAmount }) - }} - > - Use market odds - </Button> - )} + </Col> <div className="mt-8"> If the challenge is accepted, whoever is right will earn{' '} <span className="font-semibold"> diff --git a/web/components/contract/extra-contract-actions-row.tsx b/web/components/contract/extra-contract-actions-row.tsx index 4f362d84..2a5de1c0 100644 --- a/web/components/contract/extra-contract-actions-row.tsx +++ b/web/components/contract/extra-contract-actions-row.tsx @@ -11,14 +11,21 @@ import { FollowMarketButton } from 'web/components/follow-market-button' import { LikeMarketButton } from 'web/components/contract/like-market-button' import { ContractInfoDialog } from 'web/components/contract/contract-info-dialog' 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/lib/challenge' export function ExtraContractActionsRow(props: { contract: Contract user: User | undefined | null }) { const { user, contract } = props - + const { outcomeType, resolution } = contract const [isShareOpen, setShareOpen] = useState(false) + const [openCreateChallengeModal, setOpenCreateChallengeModal] = + useState(false) + const showChallenge = + user && outcomeType === 'BINARY' && !resolution && CHALLENGES_ENABLED return ( <Row className={'mt-0.5 justify-around sm:mt-2 lg:justify-start'}> @@ -45,6 +52,25 @@ export function ExtraContractActionsRow(props: { user={user} /> </Button> + {showChallenge && ( + <Button + size="lg" + color="gray-white" + className={'flex hidden max-w-xs self-center sm:inline-block'} + onClick={withTracking( + () => setOpenCreateChallengeModal(true), + 'click challenge button' + )} + > + <span>⚔️ Challenge</span> + <CreateChallengeModal + isOpen={openCreateChallengeModal} + setOpen={setOpenCreateChallengeModal} + user={user} + contract={contract} + /> + </Button> + )} <FollowMarketButton contract={contract} user={user} /> {user?.id !== contract.creatorId && ( diff --git a/web/components/contract/share-modal.tsx b/web/components/contract/share-modal.tsx index 5bae101d..ff3f41ae 100644 --- a/web/components/contract/share-modal.tsx +++ b/web/components/contract/share-modal.tsx @@ -45,7 +45,7 @@ export function ShareModal(props: { return ( <Modal open={isOpen} setOpen={setOpen} size="md"> - <Col className="gap-4 rounded bg-white p-4"> + <Col className="gap-2.5 rounded bg-white p-4 sm:gap-4"> <Title className="!mt-0 !mb-2" text="Share this market" /> <p> Earn{' '} @@ -57,7 +57,7 @@ export function ShareModal(props: { <Button size="2xl" color="gradient" - className={'mb-2 flex max-w-xs self-center'} + className={'flex max-w-xs self-center'} onClick={() => { copyToClipboard(shareUrl) toast.success('Link copied!', { @@ -68,17 +68,18 @@ export function ShareModal(props: { > {linkIcon} Copy link </Button> + <Row className={'justify-center'}>or</Row> {showChallenge && ( <Button - size="lg" - color="gray-white" + size="2xl" + color="gradient" className={'mb-2 flex max-w-xs self-center'} onClick={withTracking( () => setOpenCreateChallengeModal(true), 'click challenge button' )} > - <span>⚔️ Challenge a friend</span> + <span>⚔️ Challenge</span> <CreateChallengeModal isOpen={openCreateChallengeModal} setOpen={(open) => {