initial commit for UI mobile changes

This commit is contained in:
ingawei 2022-09-13 01:08:18 -07:00
parent f49cb9b399
commit 7d9a57bf86
11 changed files with 235 additions and 138 deletions

View File

@ -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,8 +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',
className color === 'highlight-blue' &&
'text-highlight-blue border-none shadow-none'
)} )}
disabled={disabled} disabled={disabled}
onClick={onClick} onClick={onClick}

View File

@ -106,7 +106,7 @@ export function ContractCard(props: {
<> <>
{outcomeType === 'BINARY' && ( {outcomeType === 'BINARY' && (
<BinaryResolutionOrChance <BinaryResolutionOrChance
className="items-center self-center pr-5" className="items-center self-center"
contract={contract} contract={contract}
/> />
)} )}

View File

@ -18,7 +18,7 @@ import { Avatar } from '../avatar'
import { useState } from 'react' import { useState } from 'react'
import { ContractInfoDialog } from './contract-info-dialog' 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, UserFollowButton } 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 +34,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'
@ -133,8 +135,10 @@ export function ContractDetails(props: {
isMobile ? 'max-w-[140px]' : 'max-w-[250px]' isMobile ? 'max-w-[140px]' : 'max-w-[250px]'
)} )}
> >
<UserGroupIcon className="mx-1 inline h-5 w-5 shrink-0" /> {/* <UserGroupIcon className="mx-1 inline h-5 w-5 shrink-0" /> */}
<span className="items-center truncate">{groupToDisplay.name}</span> <div className="bg-greyscale-6 items-center truncate rounded-full px-2 text-white">
{groupToDisplay.name}
</div>
</a> </a>
</Link> </Link>
) : ( ) : (
@ -145,100 +149,111 @@ export function ContractDetails(props: {
onClick={() => !groupToDisplay && setOpen(true)} onClick={() => !groupToDisplay && setOpen(true)}
> >
<Row> <Row>
<UserGroupIcon className="mx-1 inline h-5 w-5 shrink-0" /> {/* <UserGroupIcon className="mx-1 inline h-5 w-5 shrink-0" /> */}
<span className="truncate">No Group</span> <div className="bg-greyscale-6 items-center truncate rounded-full px-2 text-white">
No Group
</div>
</Row> </Row>
</Button> </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"> <Row>
<Row className="items-center gap-2"> <Avatar
<Avatar username={creatorUsername}
username={creatorUsername} avatarUrl={creatorAvatarUrl}
avatarUrl={creatorAvatarUrl} noLink={disabled}
noLink={disabled} size={9}
size={6} />
/> {!disabled && (
{disabled ? ( <div className="absolute mt-4">
creatorName <MiniUserFollowButton userId={creatorId} />
) : ( </div>
<UserLink
className="whitespace-nowrap"
name={creatorName}
username={creatorUsername}
short={isMobile}
/>
)}
{!disabled && <UserFollowButton userId={creatorId} small />}
</Row>
<Row>
{disabled ? (
groupInfo
) : !groupToDisplay && !user ? (
<div />
) : (
<Row>
{groupInfo}
{user && groupToDisplay && (
<Button
size={'xs'}
color={'gray-white'}
onClick={() => setOpen(!open)}
>
<PencilIcon className="mb-0.5 mr-0.5 inline h-4 w-4 shrink-0" />
</Button>
)}
</Row>
)}
</Row>
<Modal open={open} setOpen={setOpen} size={'md'}>
<Col
className={
'max-h-[70vh] min-h-[20rem] overflow-auto rounded bg-white p-6'
}
>
<ContractGroupsList contract={contract} user={user} />
</Col>
</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 && ( <Col className="text-greyscale-6 ml-2 flex-1 flex-wrap text-sm">
<> <Row className="w-full justify-between ">
<Row className="hidden items-center gap-1 md:inline-flex"> {disabled ? (
<DatabaseIcon className="h-5 w-5" /> creatorName
<div className="whitespace-nowrap">{volumeLabel}</div> ) : (
</Row> <UserLink
{!disabled && ( className="my-auto whitespace-nowrap"
<ContractInfoDialog name={creatorName}
contract={contract} username={creatorUsername}
className={'hidden md:inline-flex'} short={isMobile}
/> />
)} )}
</> </Row>
)} <Row className="text-2xs text-greyscale-4 sm:text-xs">
{(!!closeTime || !!resolvedDate) && (
<Row className="items-center gap-1">
{resolvedDate && resolutionTime ? (
<>
<DateTimeTooltip
text="Market resolved:"
time={resolutionTime}
>
<Row>
<div>resolved&nbsp;</div>
<b>{resolvedDate}</b>
</Row>
</DateTimeTooltip>
</>
) : null}
{!resolvedDate && closeTime && user && (
<Row>
<div>Closes&nbsp;</div>
<EditableCloseDate
closeTime={closeTime}
contract={contract}
isCreator={isCreator ?? false}
/>
</Row>
)}
</Row>
)}
{/* <Row>
{disabled ? (
groupInfo
) : !groupToDisplay && !user ? (
<div />
) : (
<Row>
{groupInfo}
{user && groupToDisplay && (
<Button
size={'xs'}
color={'gray-white'}
onClick={() => setOpen(!open)}
>
<PlusCircleIcon className="mb-0.5 mr-0.5 inline h-4 w-4 shrink-0" />
</Button>
)}
</Row>
)}
</Row>
<Modal open={open} setOpen={setOpen} size={'md'}>
<Col
className={
'max-h-[70vh] min-h-[20rem] overflow-auto rounded bg-white p-6'
}
>
<ContractGroupsList contract={contract} user={user} />
</Col>
</Modal> */}
{/* {user && (
<>
<Row className="hidden items-center gap-1 md:inline-flex">
<DatabaseIcon className="h-5 w-5" />
<div className="whitespace-nowrap">{volumeLabel}</div>
</Row>
</>
)} */}
</Row>
</Col>
<div className="mt-0">
<ExtraContractActionsRow contract={contract} />
</div>
</Row> </Row>
) )
} }
@ -280,12 +295,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&nbsp;</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>
) )
)} )}

View File

@ -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,15 +68,17 @@ 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">

View File

@ -29,7 +29,7 @@ import { ContractDetails, ExtraMobileContractDetails } 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 }) => {
@ -75,17 +75,15 @@ const BinaryOverview = (props: { contract: BinaryContract; bets: Bet[] }) => {
<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-3 px-2 sm:gap-4">
<ContractDetails contract={contract} /> <ContractDetails contract={contract} />
<Row className="justify-between gap-4"> <OverviewQuestion text={contract.question} />
<OverviewQuestion text={contract.question} /> <BinaryResolutionOrChance
<BinaryResolutionOrChance className="hidden items-end xl:flex"
className="hidden items-end xl:flex" contract={contract}
contract={contract} large
large />
/>
</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} /> {/* <ExtraMobileContractDetails contract={contract} /> */}
{tradingAllowed(contract) && ( {tradingAllowed(contract) && (
<BetWidget contract={contract as CPMMBinaryContract} /> <BetWidget contract={contract as CPMMBinaryContract} />
)} )}
@ -113,10 +111,10 @@ 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 {/* <ExtraMobileContractDetails
contract={contract} contract={contract}
forceShowVolume={true} forceShowVolume={true}
/> /> */}
</Col> </Col>
</Col> </Col>
) )
@ -140,7 +138,7 @@ 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} /> {/* <ExtraMobileContractDetails contract={contract} /> */}
{tradingAllowed(contract) && <BetWidget contract={contract} />} {tradingAllowed(contract) && <BetWidget contract={contract} />}
</Row> </Row>
</Col> </Col>

View File

@ -23,26 +23,27 @@ export function ExtraContractActionsRow(props: { contract: Contract }) {
const [isShareOpen, setShareOpen] = useState(false) const [isShareOpen, setShareOpen] = useState(false)
const [openCreateChallengeModal, setOpenCreateChallengeModal] = const [openCreateChallengeModal, setOpenCreateChallengeModal] =
useState(false) useState(false)
const showChallenge = // const showChallenge =
user && outcomeType === 'BINARY' && !resolution && CHALLENGES_ENABLED // 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')} {/* <span className="hidden sm:block">Share</span> */}
aria-hidden="true" </Row>
/>
<span>Share</span>
</Col>
<ShareModal <ShareModal
isOpen={isShareOpen} isOpen={isShareOpen}
setOpen={setShareOpen} setOpen={setShareOpen}
@ -51,7 +52,7 @@ export function ExtraContractActionsRow(props: { contract: Contract }) {
/> />
</Button> </Button>
{showChallenge && ( {/* {showChallenge && (
<Button <Button
size="lg" size="lg"
color="gray-white" color="gray-white"
@ -72,13 +73,8 @@ export function ExtraContractActionsRow(props: { contract: Contract }) {
contract={contract} contract={contract}
/> />
</Button> </Button>
)} )} */}
<Col className={'justify-center'}>
<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>

View File

@ -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,19 @@ 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>
)}
{/* Tip {totalTipped > 0 ? `(${formatMoney(totalTipped)})` : ''} */}
</Col> </Col>
</Button> </Button>
) )

View File

@ -1,8 +1,11 @@
import { PlusCircleIcon } from '@heroicons/react/solid'
import clsx from 'clsx' import clsx from 'clsx'
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'
import { withTracking } from 'web/lib/service/analytics' import { withTracking } from 'web/lib/service/analytics'
import { Button } from './button'
import { Col } from './layout/col'
export function FollowButton(props: { export function FollowButton(props: {
isFollowing: boolean | undefined isFollowing: boolean | undefined
@ -69,3 +72,64 @@ export function UserFollowButton(props: { userId: string; small?: boolean }) {
/> />
) )
} }
// export function MiniFollowButton(props: {
// isFollowing: boolean | undefined
// onFollow: () => void
// onUnfollow: () => void
// className?: string
// }) {
// const { isFollowing, onFollow, className } = props
// const user = useUser()
// if (isFollowing || !user || isFollowing === undefined) {
// return <></>
// }
// return (
// <Button
// size="sm"
// color="highlight-blue"
// onClick={withTracking(onFollow, 'follow')}
// className={className}
// >
// <PlusCircleIcon
// className={clsx('h-[24px] w-5 sm:mr-2')}
// aria-hidden="true"
// />
// </Button>
// )
// }
export function MiniUserFollowButton(props: { userId: string }) {
const { userId } = props
const currentUser = useUser()
const following = useFollows(currentUser?.id)
const isFollowing = following?.includes(userId)
const user = useUser()
if (
!currentUser ||
currentUser.id === userId ||
isFollowing ||
!user ||
isFollowing === undefined
)
return null
return (
<>
<Button
size="sm"
color="highlight-blue"
onClick={withTracking(() => follow(currentUser.id, userId), 'follow')}
>
<PlusCircleIcon
className={clsx('h-[24px] w-5 sm:mr-2')}
aria-hidden="true"
/>
</Button>
</>
)
}

View File

@ -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

View File

@ -238,7 +238,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' && (

View File

@ -26,6 +26,7 @@ module.exports = {
'greyscale-5': '#9191A7', 'greyscale-5': '#9191A7',
'greyscale-6': '#66667C', 'greyscale-6': '#66667C',
'greyscale-7': '#111140', 'greyscale-7': '#111140',
'highlight-blue': '#5BCEFF',
}, },
typography: { typography: {
quoteless: { quoteless: {