Inga/tip button (#1043)
* added tip jar * made market actions/comments and manalink buttons IconButtons
This commit is contained in:
parent
4e5b78f4ee
commit
5ba4a9dce7
|
@ -82,3 +82,39 @@ export function Button(props: {
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function IconButton(props: {
|
||||||
|
className?: string
|
||||||
|
onClick?: MouseEventHandler<any> | undefined
|
||||||
|
children?: ReactNode
|
||||||
|
size?: SizeType
|
||||||
|
type?: 'button' | 'reset' | 'submit'
|
||||||
|
disabled?: boolean
|
||||||
|
loading?: boolean
|
||||||
|
}) {
|
||||||
|
const {
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
onClick,
|
||||||
|
size = 'md',
|
||||||
|
type = 'button',
|
||||||
|
disabled = false,
|
||||||
|
loading,
|
||||||
|
} = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type={type}
|
||||||
|
className={clsx(
|
||||||
|
'inline-flex items-center justify-center transition-colors disabled:cursor-not-allowed',
|
||||||
|
sizeClasses[size],
|
||||||
|
'disabled:text-greyscale-2 text-greyscale-6 hover:text-indigo-600',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
disabled={disabled || loading}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -19,11 +19,9 @@ import ShortToggle from '../widgets/short-toggle'
|
||||||
import { DuplicateContractButton } from '../duplicate-contract-button'
|
import { DuplicateContractButton } from '../duplicate-contract-button'
|
||||||
import { Row } from '../layout/row'
|
import { Row } from '../layout/row'
|
||||||
import { BETTORS, User } from 'common/user'
|
import { BETTORS, User } from 'common/user'
|
||||||
import { Button } from '../button'
|
import { IconButton } from '../button'
|
||||||
import { AddLiquidityButton } from './add-liquidity-button'
|
import { AddLiquidityButton } from './add-liquidity-button'
|
||||||
|
import { Tooltip } from '../tooltip'
|
||||||
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'
|
|
||||||
|
|
||||||
export function ContractInfoDialog(props: {
|
export function ContractInfoDialog(props: {
|
||||||
contract: Contract
|
contract: Contract
|
||||||
|
@ -84,171 +82,173 @@ export function ContractInfoDialog(props: {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Tooltip text="Market details" placement="bottom" noTap noFade>
|
||||||
size="sm"
|
<IconButton
|
||||||
color="gray-white"
|
size="2xs"
|
||||||
className={clsx(contractDetailsButtonClassName, className)}
|
className={clsx(className)}
|
||||||
onClick={() => setOpen(true)}
|
onClick={() => setOpen(true)}
|
||||||
>
|
>
|
||||||
<DotsHorizontalIcon
|
<DotsHorizontalIcon
|
||||||
className={clsx('h-5 w-5 flex-shrink-0')}
|
className={clsx('h-5 w-5 flex-shrink-0')}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
</Button>
|
</IconButton>
|
||||||
|
|
||||||
<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="This Market" />
|
<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>
|
||||||
<tr>
|
|
||||||
<td>Type</td>
|
|
||||||
<td>{typeDisplay}</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>Payout</td>
|
|
||||||
<td className="flex gap-1">
|
|
||||||
{mechanism === 'cpmm-1' ? (
|
|
||||||
<>
|
|
||||||
Fixed{' '}
|
|
||||||
<InfoTooltip text="Each YES share is worth M$1 if YES wins." />
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
Parimutuel{' '}
|
|
||||||
<InfoTooltip text="Each share is a fraction of the pool. " />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>Market created</td>
|
|
||||||
<td>{formatTime(createdTime)}</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
{closeTime && (
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Market close{closeTime > Date.now() ? 's' : 'd'}</td>
|
<td>Type</td>
|
||||||
<td>{formatTime(closeTime)}</td>
|
<td>{typeDisplay}</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
|
||||||
|
|
||||||
{resolutionTime && (
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Market resolved</td>
|
<td>Payout</td>
|
||||||
<td>{formatTime(resolutionTime)}</td>
|
<td className="flex gap-1">
|
||||||
</tr>
|
{mechanism === 'cpmm-1' ? (
|
||||||
)}
|
<>
|
||||||
|
Fixed{' '}
|
||||||
<tr>
|
<InfoTooltip text="Each YES share is worth M$1 if YES wins." />
|
||||||
<td>
|
</>
|
||||||
<span className="mr-1">Volume</span>
|
) : (
|
||||||
<InfoTooltip text="Total amount bought or sold" />
|
<>
|
||||||
</td>
|
Parimutuel{' '}
|
||||||
<td>{formatMoney(contract.volume)}</td>
|
<InfoTooltip text="Each share is a fraction of the pool. " />
|
||||||
</tr>
|
</>
|
||||||
|
)}
|
||||||
<tr>
|
|
||||||
<td>{capitalize(BETTORS)}</td>
|
|
||||||
<td>{uniqueBettorCount ?? '0'}</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<Row>
|
|
||||||
<span className="mr-1">Elasticity</span>
|
|
||||||
<InfoTooltip
|
|
||||||
text={
|
|
||||||
mechanism === 'cpmm-1'
|
|
||||||
? 'Probability change between a M$50 bet on YES and NO'
|
|
||||||
: 'Probability change from a M$100 bet'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Row>
|
|
||||||
</td>
|
|
||||||
<td>{formatPercent(elasticity)}</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>Liquidity subsidies</td>
|
|
||||||
<td>
|
|
||||||
{mechanism === 'cpmm-1'
|
|
||||||
? formatMoney(contract.totalLiquidity)
|
|
||||||
: formatMoney(100)}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>Pool</td>
|
|
||||||
<td>
|
|
||||||
{mechanism === 'cpmm-1' && outcomeType === 'BINARY'
|
|
||||||
? `${Math.round(pool.YES)} YES, ${Math.round(pool.NO)} NO`
|
|
||||||
: mechanism === 'cpmm-1' && outcomeType === 'PSEUDO_NUMERIC'
|
|
||||||
? `${Math.round(pool.YES)} HIGHER, ${Math.round(
|
|
||||||
pool.NO
|
|
||||||
)} LOWER`
|
|
||||||
: contractPool(contract)}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
{/* Show a path to Firebase if user is an admin, or we're on localhost */}
|
|
||||||
{(isAdmin || isDev) && (
|
|
||||||
<tr>
|
|
||||||
<td>[ADMIN] Firestore</td>
|
|
||||||
<td>
|
|
||||||
<SiteLink href={firestoreConsolePath(id)}>
|
|
||||||
Console link
|
|
||||||
</SiteLink>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
|
||||||
{isAdmin && (
|
|
||||||
<tr>
|
|
||||||
<td>[ADMIN] Featured</td>
|
|
||||||
<td>
|
|
||||||
<ShortToggle
|
|
||||||
on={featured}
|
|
||||||
setOn={setFeatured}
|
|
||||||
onChange={onFeaturedToggle}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
)}
|
|
||||||
{user && (
|
|
||||||
<tr>
|
|
||||||
<td>{isAdmin ? '[ADMIN]' : ''} Unlisted</td>
|
|
||||||
<td>
|
|
||||||
<ShortToggle
|
|
||||||
disabled={
|
|
||||||
isUnlisted
|
|
||||||
? !(isAdmin || (isCreator && wasUnlistedByCreator))
|
|
||||||
: !(isCreator || isAdmin)
|
|
||||||
}
|
|
||||||
on={contract.visibility === 'unlisted'}
|
|
||||||
setOn={(b) =>
|
|
||||||
updateContract(id, {
|
|
||||||
visibility: b ? 'unlisted' : 'public',
|
|
||||||
unlistedById: b ? user.id : '',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
)}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<Row className="flex-wrap">
|
<tr>
|
||||||
{mechanism === 'cpmm-1' && (
|
<td>Market created</td>
|
||||||
<AddLiquidityButton contract={contract} className="mr-2" />
|
<td>{formatTime(createdTime)}</td>
|
||||||
)}
|
</tr>
|
||||||
<DuplicateContractButton contract={contract} />
|
|
||||||
</Row>
|
{closeTime && (
|
||||||
</Col>
|
<tr>
|
||||||
</Modal>
|
<td>Market close{closeTime > Date.now() ? 's' : 'd'}</td>
|
||||||
|
<td>{formatTime(closeTime)}</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{resolutionTime && (
|
||||||
|
<tr>
|
||||||
|
<td>Market resolved</td>
|
||||||
|
<td>{formatTime(resolutionTime)}</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span className="mr-1">Volume</span>
|
||||||
|
<InfoTooltip text="Total amount bought or sold" />
|
||||||
|
</td>
|
||||||
|
<td>{formatMoney(contract.volume)}</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>{capitalize(BETTORS)}</td>
|
||||||
|
<td>{uniqueBettorCount ?? '0'}</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<Row>
|
||||||
|
<span className="mr-1">Elasticity</span>
|
||||||
|
<InfoTooltip
|
||||||
|
text={
|
||||||
|
mechanism === 'cpmm-1'
|
||||||
|
? 'Probability change between a M$50 bet on YES and NO'
|
||||||
|
: 'Probability change from a M$100 bet'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Row>
|
||||||
|
</td>
|
||||||
|
<td>{formatPercent(elasticity)}</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>Liquidity subsidies</td>
|
||||||
|
<td>
|
||||||
|
{mechanism === 'cpmm-1'
|
||||||
|
? formatMoney(contract.totalLiquidity)
|
||||||
|
: formatMoney(100)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>Pool</td>
|
||||||
|
<td>
|
||||||
|
{mechanism === 'cpmm-1' && outcomeType === 'BINARY'
|
||||||
|
? `${Math.round(pool.YES)} YES, ${Math.round(pool.NO)} NO`
|
||||||
|
: mechanism === 'cpmm-1' &&
|
||||||
|
outcomeType === 'PSEUDO_NUMERIC'
|
||||||
|
? `${Math.round(pool.YES)} HIGHER, ${Math.round(
|
||||||
|
pool.NO
|
||||||
|
)} LOWER`
|
||||||
|
: contractPool(contract)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{/* Show a path to Firebase if user is an admin, or we're on localhost */}
|
||||||
|
{(isAdmin || isDev) && (
|
||||||
|
<tr>
|
||||||
|
<td>[ADMIN] Firestore</td>
|
||||||
|
<td>
|
||||||
|
<SiteLink href={firestoreConsolePath(id)}>
|
||||||
|
Console link
|
||||||
|
</SiteLink>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
{isAdmin && (
|
||||||
|
<tr>
|
||||||
|
<td>[ADMIN] Featured</td>
|
||||||
|
<td>
|
||||||
|
<ShortToggle
|
||||||
|
on={featured}
|
||||||
|
setOn={setFeatured}
|
||||||
|
onChange={onFeaturedToggle}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
{user && (
|
||||||
|
<tr>
|
||||||
|
<td>{isAdmin ? '[ADMIN]' : ''} Unlisted</td>
|
||||||
|
<td>
|
||||||
|
<ShortToggle
|
||||||
|
disabled={
|
||||||
|
isUnlisted
|
||||||
|
? !(isAdmin || (isCreator && wasUnlistedByCreator))
|
||||||
|
: !(isCreator || isAdmin)
|
||||||
|
}
|
||||||
|
on={contract.visibility === 'unlisted'}
|
||||||
|
setOn={(b) =>
|
||||||
|
updateContract(id, {
|
||||||
|
visibility: b ? 'unlisted' : 'public',
|
||||||
|
unlistedById: b ? user.id : '',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<Row className="flex-wrap">
|
||||||
|
{mechanism === 'cpmm-1' && (
|
||||||
|
<AddLiquidityButton contract={contract} className="mr-2" />
|
||||||
|
)}
|
||||||
|
<DuplicateContractButton contract={contract} />
|
||||||
|
</Row>
|
||||||
|
</Col>
|
||||||
|
</Modal>
|
||||||
|
</Tooltip>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { ShareIcon } from '@heroicons/react/outline'
|
||||||
import { Row } from '../layout/row'
|
import { Row } from '../layout/row'
|
||||||
import { Contract } from 'web/lib/firebase/contracts'
|
import { Contract } from 'web/lib/firebase/contracts'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { Button } from 'web/components/button'
|
import { IconButton } from 'web/components/button'
|
||||||
import { useUser } from 'web/hooks/use-user'
|
import { useUser } from 'web/hooks/use-user'
|
||||||
import { ShareModal } from './share-modal'
|
import { ShareModal } from './share-modal'
|
||||||
import { FollowMarketButton } from 'web/components/follow-market-button'
|
import { FollowMarketButton } from 'web/components/follow-market-button'
|
||||||
|
@ -16,15 +16,14 @@ export function ExtraContractActionsRow(props: { contract: Contract }) {
|
||||||
const [isShareOpen, setShareOpen] = useState(false)
|
const [isShareOpen, setShareOpen] = useState(false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row>
|
<Row className="gap-1">
|
||||||
<FollowMarketButton contract={contract} user={user} />
|
<FollowMarketButton contract={contract} user={user} />
|
||||||
|
|
||||||
<LikeMarketButton contract={contract} user={user} />
|
<LikeMarketButton contract={contract} user={user} />
|
||||||
|
|
||||||
<Tooltip text="Share" placement="bottom" noTap noFade>
|
<Tooltip text="Share" placement="bottom" noTap noFade>
|
||||||
<Button
|
<IconButton
|
||||||
size="sm"
|
size="2xs"
|
||||||
color="gray-white"
|
|
||||||
className={'flex'}
|
className={'flex'}
|
||||||
onClick={() => setShareOpen(true)}
|
onClick={() => setShareOpen(true)}
|
||||||
>
|
>
|
||||||
|
@ -35,7 +34,7 @@ export function ExtraContractActionsRow(props: { contract: Contract }) {
|
||||||
contract={contract}
|
contract={contract}
|
||||||
user={user}
|
user={user}
|
||||||
/>
|
/>
|
||||||
</Button>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
<ContractInfoDialog contract={contract} user={user} />
|
<ContractInfoDialog contract={contract} user={user} />
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import { HeartIcon } from '@heroicons/react/outline'
|
|
||||||
|
|
||||||
import { Button } from 'web/components/button'
|
|
||||||
import { formatMoney, shortFormatNumber } from 'common/util/format'
|
import { formatMoney, shortFormatNumber } from 'common/util/format'
|
||||||
import { Col } from 'web/components/layout/col'
|
import { Col } from 'web/components/layout/col'
|
||||||
import { Tooltip } from '../tooltip'
|
import { Tooltip } from '../tooltip'
|
||||||
|
import TipJar from 'web/public/custom-components/tipJar'
|
||||||
|
import { useState } from 'react'
|
||||||
|
|
||||||
export function TipButton(props: {
|
export function TipButton(props: {
|
||||||
tipAmount: number
|
tipAmount: number
|
||||||
|
@ -14,11 +13,12 @@ export function TipButton(props: {
|
||||||
isCompact?: boolean
|
isCompact?: boolean
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
}) {
|
}) {
|
||||||
const { tipAmount, totalTipped, userTipped, isCompact, onClick, disabled } =
|
const { tipAmount, totalTipped, userTipped, onClick, disabled } = props
|
||||||
props
|
|
||||||
|
|
||||||
const tipDisplay = shortFormatNumber(Math.ceil(totalTipped / 10))
|
const tipDisplay = shortFormatNumber(Math.ceil(totalTipped / 10))
|
||||||
|
|
||||||
|
const [hover, setHover] = useState(false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
text={
|
text={
|
||||||
|
@ -30,39 +30,40 @@ export function TipButton(props: {
|
||||||
noTap
|
noTap
|
||||||
noFade
|
noFade
|
||||||
>
|
>
|
||||||
<Button
|
<button
|
||||||
size={'sm'}
|
|
||||||
className={clsx(
|
|
||||||
'max-w-xs self-center',
|
|
||||||
isCompact && 'px-0 py-0',
|
|
||||||
disabled && 'hover:bg-inherit'
|
|
||||||
)}
|
|
||||||
color={'gray-white'}
|
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
className={clsx(
|
||||||
|
'px-2 py-1 text-xs', //2xs button
|
||||||
|
'text-greyscale-6 transition-transform hover:text-indigo-600 disabled:cursor-not-allowed',
|
||||||
|
!disabled ? 'hover:rotate-12' : ''
|
||||||
|
)}
|
||||||
|
onMouseOver={() => setHover(true)}
|
||||||
|
onMouseLeave={() => setHover(false)}
|
||||||
>
|
>
|
||||||
<Col className={'relative items-center sm:flex-row'}>
|
<Col className={clsx('relative', disabled ? 'opacity-30' : '')}>
|
||||||
<HeartIcon
|
<TipJar
|
||||||
className={clsx(
|
size={18}
|
||||||
'h-5 w-5',
|
color={userTipped || (hover && !disabled) ? '#4f46e5' : '#66667C'}
|
||||||
totalTipped > 0 ? 'mr-2' : '',
|
fill={userTipped ? '#4f46e5' : 'none'}
|
||||||
userTipped ? 'fill-teal-500 text-teal-500' : ''
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
{totalTipped > 0 && (
|
<div
|
||||||
<div
|
className={clsx(
|
||||||
className={clsx(
|
' absolute top-[2px] text-[0.5rem]',
|
||||||
'bg-greyscale-5 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',
|
userTipped ? 'text-white' : '',
|
||||||
tipDisplay.length > 2
|
tipDisplay.length === 1
|
||||||
? 'text-[0.4rem] sm:text-[0.5rem]'
|
? 'left-[7px]'
|
||||||
: 'sm:text-2xs text-[0.5rem]'
|
: tipDisplay.length === 2
|
||||||
)}
|
? 'left-[4.5px]'
|
||||||
>
|
: tipDisplay.length > 2
|
||||||
{tipDisplay}
|
? 'left-[4px] top-[2.5px] text-[0.35rem]'
|
||||||
</div>
|
: ''
|
||||||
)}
|
)}
|
||||||
|
>
|
||||||
|
{totalTipped > 0 ? tipDisplay : ''}
|
||||||
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
</Button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ import { UserLink } from 'web/components/user-link'
|
||||||
import { CommentInput } from '../comment-input'
|
import { CommentInput } from '../comment-input'
|
||||||
import { AwardBountyButton } from 'web/components/award-bounty-button'
|
import { AwardBountyButton } from 'web/components/award-bounty-button'
|
||||||
import { ReplyIcon } from '@heroicons/react/solid'
|
import { ReplyIcon } from '@heroicons/react/solid'
|
||||||
import { Button } from '../button'
|
import { IconButton } from '../button'
|
||||||
import { ReplyToggle } from '../comments/reply-toggle'
|
import { ReplyToggle } from '../comments/reply-toggle'
|
||||||
|
|
||||||
export type ReplyTo = { id: string; username: string }
|
export type ReplyTo = { id: string; username: string }
|
||||||
|
@ -154,36 +154,46 @@ export function ParentFeedComment(props: {
|
||||||
numComments={numComments}
|
numComments={numComments}
|
||||||
onClick={onSeeReplyClick}
|
onClick={onSeeReplyClick}
|
||||||
/>
|
/>
|
||||||
<Row className="grow justify-end gap-2">
|
<CommentActions
|
||||||
{onReplyClick && (
|
onReplyClick={onReplyClick}
|
||||||
<Button
|
comment={comment}
|
||||||
size={'sm'}
|
showTip={showTip}
|
||||||
className={clsx(
|
myTip={myTip}
|
||||||
'hover:bg-greyscale-2 mt-0 mb-1 max-w-xs px-0 py-0'
|
totalTip={totalTip}
|
||||||
)}
|
contract={contract}
|
||||||
color={'gray-white'}
|
/>
|
||||||
onClick={() => onReplyClick(comment)}
|
|
||||||
>
|
|
||||||
<ReplyIcon className="h-5 w-5" />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{showTip && (
|
|
||||||
<Tipper
|
|
||||||
comment={comment}
|
|
||||||
myTip={myTip ?? 0}
|
|
||||||
totalTip={totalTip ?? 0}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{(contract.openCommentBounties ?? 0) > 0 && (
|
|
||||||
<AwardBountyButton comment={comment} contract={contract} />
|
|
||||||
)}
|
|
||||||
</Row>
|
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function CommentActions(props: {
|
||||||
|
onReplyClick?: (comment: ContractComment) => void
|
||||||
|
comment: ContractComment
|
||||||
|
showTip?: boolean
|
||||||
|
myTip?: number
|
||||||
|
totalTip?: number
|
||||||
|
contract: Contract
|
||||||
|
}) {
|
||||||
|
const { onReplyClick, comment, showTip, myTip, totalTip, contract } = props
|
||||||
|
return (
|
||||||
|
<Row className="grow justify-end">
|
||||||
|
{onReplyClick && (
|
||||||
|
<IconButton size={'xs'} onClick={() => onReplyClick(comment)}>
|
||||||
|
<ReplyIcon className="h-5 w-5" />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
{showTip && (
|
||||||
|
<Tipper comment={comment} myTip={myTip ?? 0} totalTip={totalTip ?? 0} />
|
||||||
|
)}
|
||||||
|
{(contract.openCommentBounties ?? 0) > 0 && (
|
||||||
|
<AwardBountyButton comment={comment} contract={contract} />
|
||||||
|
)}
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export const FeedComment = memo(function FeedComment(props: {
|
export const FeedComment = memo(function FeedComment(props: {
|
||||||
contract: Contract
|
contract: Contract
|
||||||
comment: ContractComment
|
comment: ContractComment
|
||||||
|
@ -233,30 +243,14 @@ export const FeedComment = memo(function FeedComment(props: {
|
||||||
content={content || text}
|
content={content || text}
|
||||||
smallImage
|
smallImage
|
||||||
/>
|
/>
|
||||||
<Row className="grow justify-end gap-2">
|
<CommentActions
|
||||||
{onReplyClick && (
|
onReplyClick={onReplyClick}
|
||||||
<Button
|
comment={comment}
|
||||||
size={'sm'}
|
showTip={showTip}
|
||||||
className={clsx(
|
myTip={myTip}
|
||||||
'hover:bg-greyscale-2 mt-0 mb-1 max-w-xs px-0 py-0'
|
totalTip={totalTip}
|
||||||
)}
|
contract={contract}
|
||||||
color={'gray-white'}
|
/>
|
||||||
onClick={() => onReplyClick(comment)}
|
|
||||||
>
|
|
||||||
<ReplyIcon className="h-5 w-5" />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{showTip && (
|
|
||||||
<Tipper
|
|
||||||
comment={comment}
|
|
||||||
myTip={myTip ?? 0}
|
|
||||||
totalTip={totalTip ?? 0}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{(contract.openCommentBounties ?? 0) > 0 && (
|
|
||||||
<AwardBountyButton comment={comment} contract={contract} />
|
|
||||||
)}
|
|
||||||
</Row>
|
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Button } from 'web/components/button'
|
import { IconButton } from 'web/components/button'
|
||||||
import {
|
import {
|
||||||
Contract,
|
Contract,
|
||||||
followContract,
|
followContract,
|
||||||
|
@ -33,9 +33,8 @@ export const FollowMarketButton = (props: {
|
||||||
noTap
|
noTap
|
||||||
noFade
|
noFade
|
||||||
>
|
>
|
||||||
<Button
|
<IconButton
|
||||||
size={'sm'}
|
size="2xs"
|
||||||
color={'gray-white'}
|
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (!user) return firebaseLogin()
|
if (!user) return firebaseLogin()
|
||||||
if (followers?.includes(user.id)) {
|
if (followers?.includes(user.id)) {
|
||||||
|
@ -65,18 +64,12 @@ export const FollowMarketButton = (props: {
|
||||||
>
|
>
|
||||||
{watching ? (
|
{watching ? (
|
||||||
<Col className={'items-center gap-x-2 sm:flex-row'}>
|
<Col className={'items-center gap-x-2 sm:flex-row'}>
|
||||||
<EyeOffIcon
|
<EyeOffIcon className={clsx('h-5 w-5')} aria-hidden="true" />
|
||||||
className={clsx('h-5 w-5 sm:h-6 sm:w-6')}
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
{/* Unwatch */}
|
{/* Unwatch */}
|
||||||
</Col>
|
</Col>
|
||||||
) : (
|
) : (
|
||||||
<Col className={'items-center gap-x-2 sm:flex-row'}>
|
<Col className={'items-center gap-x-2 sm:flex-row'}>
|
||||||
<EyeIcon
|
<EyeIcon className={clsx('h-5 w-5')} aria-hidden="true" />
|
||||||
className={clsx('h-5 w-5 sm:h-6 sm:w-6')}
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
{/* Watch */}
|
{/* Watch */}
|
||||||
</Col>
|
</Col>
|
||||||
)}
|
)}
|
||||||
|
@ -87,7 +80,7 @@ export const FollowMarketButton = (props: {
|
||||||
followers?.includes(user?.id ?? 'nope') ? 'watched' : 'unwatched'
|
followers?.includes(user?.id ?? 'nope') ? 'watched' : 'unwatched'
|
||||||
} a question!`}
|
} a question!`}
|
||||||
/>
|
/>
|
||||||
</Button>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,9 @@ import { Col } from 'web/components/layout/col'
|
||||||
import { Row } from 'web/components/layout/row'
|
import { Row } from 'web/components/layout/row'
|
||||||
import { Claim, Manalink } from 'common/manalink'
|
import { Claim, Manalink } from 'common/manalink'
|
||||||
import { ShareIconButton } from './share-icon-button'
|
import { ShareIconButton } from './share-icon-button'
|
||||||
import { contractDetailsButtonClassName } from './contract/contract-info-dialog'
|
|
||||||
import { useUserById } from 'web/hooks/use-user'
|
import { useUserById } from 'web/hooks/use-user'
|
||||||
import getManalinkUrl from 'web/get-manalink-url'
|
import getManalinkUrl from 'web/get-manalink-url'
|
||||||
|
import { IconButton } from './button'
|
||||||
|
|
||||||
export type ManalinkInfo = {
|
export type ManalinkInfo = {
|
||||||
expiresTime: number | null
|
expiresTime: number | null
|
||||||
|
@ -123,7 +123,7 @@ export function ManalinkCardFromView(props: {
|
||||||
src="/logo-white.svg"
|
src="/logo-white.svg"
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Row className="relative w-full gap-1 rounded-b-lg bg-white px-4 py-2 text-lg">
|
<Row className="relative w-full rounded-b-lg bg-white px-4 py-2 align-middle text-lg">
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'my-auto mb-1 w-full',
|
'my-auto mb-1 w-full',
|
||||||
|
@ -133,32 +133,23 @@ export function ManalinkCardFromView(props: {
|
||||||
{formatMoney(amount)}
|
{formatMoney(amount)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<IconButton size="2xs" onClick={() => (window.location.href = qrUrl)}>
|
||||||
onClick={() => (window.location.href = qrUrl)}
|
|
||||||
className={clsx(contractDetailsButtonClassName)}
|
|
||||||
>
|
|
||||||
<QrcodeIcon className="h-6 w-6" />
|
<QrcodeIcon className="h-6 w-6" />
|
||||||
</button>
|
</IconButton>
|
||||||
|
|
||||||
<ShareIconButton
|
<ShareIconButton
|
||||||
toastClassName={'-left-48 min-w-[250%]'}
|
toastClassName={'-left-48 min-w-[250%]'}
|
||||||
buttonClassName={'transition-colors'}
|
|
||||||
onCopyButtonClassName={
|
|
||||||
'bg-gray-200 text-gray-600 transition-none hover:bg-gray-200 hover:text-gray-600'
|
|
||||||
}
|
|
||||||
copyPayload={getManalinkUrl(link.slug)}
|
copyPayload={getManalinkUrl(link.slug)}
|
||||||
/>
|
/>
|
||||||
<button
|
<IconButton
|
||||||
|
size="xs"
|
||||||
onClick={() => setShowDetails(!showDetails)}
|
onClick={() => setShowDetails(!showDetails)}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
contractDetailsButtonClassName,
|
showDetails ? ' text-indigo-600 hover:text-indigo-700' : ''
|
||||||
showDetails
|
|
||||||
? 'bg-gray-200 text-gray-600 hover:bg-gray-200 hover:text-gray-600'
|
|
||||||
: ''
|
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<DotsHorizontalIcon className="h-[24px] w-5" />
|
<DotsHorizontalIcon className="h-5 w-5" />
|
||||||
</button>
|
</IconButton>
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
<div className="mt-2 mb-4 text-xs text-gray-500 md:text-sm">
|
<div className="mt-2 mb-4 text-xs text-gray-500 md:text-sm">
|
||||||
|
|
|
@ -5,34 +5,22 @@ import clsx from 'clsx'
|
||||||
import { copyToClipboard } from 'web/lib/util/copy'
|
import { copyToClipboard } from 'web/lib/util/copy'
|
||||||
import { ToastClipboard } from 'web/components/toast-clipboard'
|
import { ToastClipboard } from 'web/components/toast-clipboard'
|
||||||
import { track } from 'web/lib/service/analytics'
|
import { track } from 'web/lib/service/analytics'
|
||||||
import { contractDetailsButtonClassName } from 'web/components/contract/contract-info-dialog'
|
import { IconButton } from './button'
|
||||||
|
|
||||||
export function ShareIconButton(props: {
|
export function ShareIconButton(props: {
|
||||||
buttonClassName?: string
|
|
||||||
onCopyButtonClassName?: string
|
|
||||||
toastClassName?: string
|
toastClassName?: string
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
iconClassName?: string
|
iconClassName?: string
|
||||||
copyPayload: string
|
copyPayload: string
|
||||||
}) {
|
}) {
|
||||||
const {
|
const { toastClassName, children, iconClassName, copyPayload } = props
|
||||||
buttonClassName,
|
|
||||||
onCopyButtonClassName,
|
|
||||||
toastClassName,
|
|
||||||
children,
|
|
||||||
iconClassName,
|
|
||||||
copyPayload,
|
|
||||||
} = props
|
|
||||||
const [showToast, setShowToast] = useState(false)
|
const [showToast, setShowToast] = useState(false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative z-10 flex-shrink-0">
|
<div className="relative z-10 flex-shrink-0">
|
||||||
<button
|
<IconButton
|
||||||
className={clsx(
|
size="2xs"
|
||||||
contractDetailsButtonClassName,
|
className={clsx('mt-1', showToast ? 'text-indigo-600' : '')}
|
||||||
buttonClassName,
|
|
||||||
showToast ? onCopyButtonClassName : ''
|
|
||||||
)}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
copyToClipboard(copyPayload)
|
copyToClipboard(copyPayload)
|
||||||
track('copy share link')
|
track('copy share link')
|
||||||
|
@ -41,11 +29,11 @@ export function ShareIconButton(props: {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<LinkIcon
|
<LinkIcon
|
||||||
className={clsx(iconClassName ? iconClassName : 'h-[24px] w-5')}
|
className={clsx(iconClassName ? iconClassName : 'h-5 w-5')}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
{children}
|
{children}
|
||||||
</button>
|
</IconButton>
|
||||||
|
|
||||||
{showToast && <ToastClipboard className={toastClassName} />}
|
{showToast && <ToastClipboard className={toastClassName} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
23
web/public/custom-components/tipJar.tsx
Normal file
23
web/public/custom-components/tipJar.tsx
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
export default function TipJar({
|
||||||
|
size = 18,
|
||||||
|
color = '#66667C',
|
||||||
|
strokeWidth = 1.5,
|
||||||
|
fill = 'none',
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 18 18"
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
fill={fill}
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth={strokeWidth}
|
||||||
|
opacity={50}
|
||||||
|
>
|
||||||
|
<path d="M15.5,8.1v5.8c0,1.43-1.16,2.6-2.6,2.6H5.1c-1.44,0-2.6-1.16-2.6-2.6v-5.8c0-1.04,.89-3.25,1.5-4.1h0v-2c0-.55,.45-1,1-1H13c.55,0,1,.45,1,1v2h0c.61,.85,1.5,3.06,1.5,4.1Z" />
|
||||||
|
<line x1="4" y1="4" x2="9" y2="4" />
|
||||||
|
<line x1="11.26" y1="4" x2="14" y2="4" />
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user