import clsx from 'clsx' import React from 'react' import { formatMoney } from 'common/util/format' import { Col } from 'web/components/layout/col' import { Row } from 'web/components/layout/row' import { Page } from 'web/components/page' import { SEO } from 'web/components/SEO' import { Title } from 'web/components/title' import { useUser } from 'web/hooks/use-user' import { fromNow } from 'web/lib/util/time' import dayjs from 'dayjs' import customParseFormat from 'dayjs/plugin/customParseFormat' import { getChallengeUrl, useAcceptedChallenges, useUserChallenges, } from 'web/lib/firebase/challenges' import { Challenge, CHALLENGES_ENABLED } from 'common/challenge' import { Tabs } from 'web/components/layout/tabs' import { SiteLink } from 'web/components/site-link' import { Avatar } from 'web/components/avatar' import Router from 'next/router' import { contractPathWithoutContract } from 'web/lib/firebase/contracts' import { Button } from 'web/components/button' import { ClipboardCopyIcon, QrcodeIcon } from '@heroicons/react/outline' import { copyToClipboard } from 'web/lib/util/copy' import toast from 'react-hot-toast' import { Modal } from 'web/components/layout/modal' import { QRCode } from 'web/components/qr-code' import { CreateChallengeModal } from 'web/components/challenges/create-challenge-modal' import { UserLink } from 'web/components/user-link' dayjs.extend(customParseFormat) const columnClass = 'sm:px-5 px-2 py-3.5 max-w-[100px] truncate' const amountClass = columnClass + ' max-w-[75px] font-bold' export default function ChallengesListPage() { const user = useUser() const challenges = useAcceptedChallenges() const [open, setOpen] = React.useState(false) const userChallenges = useUserChallenges(user?.id) .concat( user ? challenges.filter((c) => c.acceptances[0].userId === user.id) : [] ) .sort((a, b) => b.createdTime - a.createdTime) const userTab = user ? [ { content: , title: 'Your Challenges', }, ] : [] const publicTab = [ { content: , title: 'Public Challenges', }, ] return ( {CHALLENGES_ENABLED && ( <Button size="lg" color="gradient" onClick={() => setOpen(true)}> Create Challenge <CreateChallengeModal isOpen={open} setOpen={setOpen} user={user} /> </Button> )} </Row> <p> Want to create your own challenge? <SiteLink className={'mx-1 font-bold'} href={'/home'}> Find </SiteLink> a market you and a friend disagree on and hit the challenge button, or tap the button above to create a new market & challenge in one. </p> <Tabs className="mb-4" tabs={[...userTab, ...publicTab]} /> </Col> </Page> ) } function YourChallengesTable(props: { links: Challenge[] }) { const { links } = props return links.length == 0 ? ( <p>There aren't currently any challenges.</p> ) : ( <div className="overflow-scroll"> <table className="w-full divide-y divide-gray-300 rounded-lg border border-gray-200"> <thead className="bg-gray-50 text-left text-sm font-semibold text-gray-900"> <tr> <th className={amountClass}>Amount</th> <th className={clsx( columnClass, 'text-center sm:pl-10 sm:text-start' )} > Link </th> <th className={columnClass}>Accepted By</th> </tr> </thead> <tbody className={'divide-y divide-gray-200 bg-white'}> {links.map((link) => ( <YourLinkSummaryRow challenge={link} /> ))} </tbody> </table> </div> ) } function YourLinkSummaryRow(props: { challenge: Challenge }) { const { challenge } = props const { acceptances } = challenge const [open, setOpen] = React.useState(false) const className = clsx( 'whitespace-nowrap text-sm hover:cursor-pointer text-gray-500 hover:bg-sky-50 bg-white' ) return ( <> <Modal open={open} setOpen={setOpen} size={'sm'}> <Col className={ 'items-center justify-center gap-4 rounded-md bg-white p-8 py-8 ' } > <span className={'mb-4 text-center text-xl text-indigo-700'}> Have your friend scan this to accept the challenge! </span> <QRCode url={getChallengeUrl(challenge)} /> </Col> </Modal> <tr id={challenge.slug} key={challenge.slug} className={className}> <td className={amountClass}> <SiteLink href={getChallengeUrl(challenge)}> {formatMoney(challenge.creatorAmount)} </SiteLink> </td> <td className={clsx( columnClass, 'text-center sm:max-w-[200px] sm:text-start' )} > <Row className="items-center gap-2"> <Button color="gray-white" size="xs" onClick={() => { copyToClipboard(getChallengeUrl(challenge)) toast('Link copied to clipboard!') }} > <ClipboardCopyIcon className={'h-5 w-5 sm:h-4 sm:w-4'} /> </Button> <Button color="gray-white" size="xs" onClick={() => { setOpen(true) }} > <QrcodeIcon className="h-5 w-5 sm:h-4 sm:w-4" /> </Button> <SiteLink href={getChallengeUrl(challenge)} className={'mx-1 mb-1 hidden sm:inline-block'} > {`...${challenge.contractSlug}/${challenge.slug}`} </SiteLink> </Row> </td> <td className={columnClass}> <Row className={'items-center justify-start gap-1'}> {acceptances.length > 0 ? ( <> <Avatar username={acceptances[0].userUsername} avatarUrl={acceptances[0].userAvatarUrl} size={'sm'} /> <UserLink name={acceptances[0].userName} username={acceptances[0].userUsername} /> </> ) : ( <span> No one - {challenge.expiresTime && ` (expires ${fromNow(challenge.expiresTime)})`} </span> )} </Row> </td> </tr> </> ) } function PublicChallengesTable(props: { links: Challenge[] }) { const { links } = props return links.length == 0 ? ( <p>There aren't currently any challenges.</p> ) : ( <div className="overflow-scroll"> <table className="w-full divide-y divide-gray-300 rounded-lg border border-gray-200"> <thead className="bg-gray-50 text-left text-sm font-semibold text-gray-900"> <tr> <th className={amountClass}>Amount</th> <th className={columnClass}>Creator</th> <th className={columnClass}>Acceptor</th> <th className={columnClass}>Market</th> </tr> </thead> <tbody className={'divide-y divide-gray-200 bg-white'}> {links.map((link) => ( <PublicLinkSummaryRow challenge={link} /> ))} </tbody> </table> </div> ) } function PublicLinkSummaryRow(props: { challenge: Challenge }) { const { challenge } = props const { acceptances, creatorUsername, creatorName, creatorAvatarUrl, contractCreatorUsername, contractQuestion, contractSlug, } = challenge const className = clsx( 'whitespace-nowrap text-sm hover:cursor-pointer text-gray-500 hover:bg-sky-50 bg-white' ) return ( <tr id={challenge.slug + '-public'} key={challenge.slug + '-public'} className={className} onClick={() => Router.push(getChallengeUrl(challenge))} > <td className={amountClass}> <SiteLink href={getChallengeUrl(challenge)}> {formatMoney(challenge.creatorAmount)} </SiteLink> </td> <td className={clsx(columnClass)}> <Row className={'items-center justify-start gap-1'}> <Avatar username={creatorUsername} avatarUrl={creatorAvatarUrl} size={'sm'} noLink={true} /> <UserLink name={creatorName} username={creatorUsername} /> </Row> </td> <td className={clsx(columnClass)}> <Row className={'items-center justify-start gap-1'}> {acceptances.length > 0 ? ( <> <Avatar username={acceptances[0].userUsername} avatarUrl={acceptances[0].userAvatarUrl} size={'sm'} noLink={true} /> <UserLink name={acceptances[0].userName} username={acceptances[0].userUsername} /> </> ) : ( <span> No one - {challenge.expiresTime && ` (expires ${fromNow(challenge.expiresTime)})`} </span> )} </Row> </td> <td className={clsx(columnClass, 'font-bold')}> <SiteLink href={contractPathWithoutContract( contractCreatorUsername, contractSlug )} > {contractQuestion} </SiteLink> </td> </tr> ) }