Merge branch 'main' into categories
This commit is contained in:
commit
1d600037db
|
@ -56,7 +56,9 @@ export function AddLiquidityPanel(props: { contract: Contract }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div>Subsidize this market by adding liquidity for traders.</div>
|
<div className="text-gray-500">
|
||||||
|
Subsidize this market by adding liquidity for traders.
|
||||||
|
</div>
|
||||||
|
|
||||||
<Row>
|
<Row>
|
||||||
<AmountInput
|
<AmountInput
|
||||||
|
|
|
@ -45,7 +45,7 @@ export function AmountInput(props: {
|
||||||
<span className="bg-gray-200 text-sm">{label}</span>
|
<span className="bg-gray-200 text-sm">{label}</span>
|
||||||
<input
|
<input
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'input input-bordered',
|
'input input-bordered max-w-[200px] text-lg',
|
||||||
error && 'input-error',
|
error && 'input-error',
|
||||||
inputClassName
|
inputClassName
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -116,7 +116,7 @@ export function AnswerBetPanel(props: {
|
||||||
</Row>
|
</Row>
|
||||||
<div className="my-3 text-left text-sm text-gray-500">Amount </div>
|
<div className="my-3 text-left text-sm text-gray-500">Amount </div>
|
||||||
<BuyAmountInput
|
<BuyAmountInput
|
||||||
inputClassName="w-full"
|
inputClassName="w-full max-w-none"
|
||||||
amount={betAmount}
|
amount={betAmount}
|
||||||
onChange={setBetAmount}
|
onChange={setBetAmount}
|
||||||
error={error}
|
error={error}
|
||||||
|
|
|
@ -62,7 +62,7 @@ export function BetPanel(props: {
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="mb-6 text-2xl text-gray-700">Place a trade</div>
|
<div className="mb-6 text-2xl">Place your bet</div>
|
||||||
{/* <Title className={clsx('!mt-0 text-neutral')} text="Place a trade" /> */}
|
{/* <Title className={clsx('!mt-0 text-neutral')} text="Place a trade" /> */}
|
||||||
|
|
||||||
<BuyPanel contract={contract} user={user} userBets={userBets ?? []} />
|
<BuyPanel contract={contract} user={user} userBets={userBets ?? []} />
|
||||||
|
@ -125,19 +125,23 @@ export function BetPanelSwitcher(props: {
|
||||||
<BinaryOutcomeLabel outcome={sharesOutcome} /> shares
|
<BinaryOutcomeLabel outcome={sharesOutcome} /> shares
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
{tradeType === 'BUY' && (
|
||||||
className="btn btn-sm"
|
<button
|
||||||
style={{
|
className="btn btn-sm"
|
||||||
backgroundColor: 'white',
|
style={{
|
||||||
border: '2px solid',
|
backgroundColor: 'white',
|
||||||
color: '#3D4451',
|
border: '2px solid',
|
||||||
}}
|
color: '#3D4451',
|
||||||
onClick={() =>
|
}}
|
||||||
tradeType === 'BUY' ? setTradeType('SELL') : setTradeType('BUY')
|
onClick={() =>
|
||||||
}
|
tradeType === 'BUY'
|
||||||
>
|
? setTradeType('SELL')
|
||||||
{tradeType === 'BUY' ? 'Sell' : 'Bet'}
|
: setTradeType('BUY')
|
||||||
</button>
|
}
|
||||||
|
>
|
||||||
|
{tradeType === 'BUY' ? 'Sell' : 'Bet'}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
)}
|
)}
|
||||||
|
@ -299,7 +303,7 @@ function BuyPanel(props: {
|
||||||
/>
|
/>
|
||||||
<div className="my-3 text-left text-sm text-gray-500">Amount</div>
|
<div className="my-3 text-left text-sm text-gray-500">Amount</div>
|
||||||
<BuyAmountInput
|
<BuyAmountInput
|
||||||
inputClassName="w-full"
|
inputClassName="w-full max-w-none"
|
||||||
amount={betAmount}
|
amount={betAmount}
|
||||||
onChange={onBetChange}
|
onChange={onBetChange}
|
||||||
error={error}
|
error={error}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import _ from 'lodash'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { Charity } from '../../../common/charity'
|
import { Charity } from '../../../common/charity'
|
||||||
import { useCharityTxns } from '../../hooks/use-charity-txns'
|
import { useCharityTxns } from '../../hooks/use-charity-txns'
|
||||||
|
import { manaToUSD } from '../../pages/charity/[charitySlug]'
|
||||||
import { Row } from '../layout/row'
|
import { Row } from '../layout/row'
|
||||||
|
|
||||||
export function CharityCard(props: { charity: Charity }) {
|
export function CharityCard(props: { charity: Charity }) {
|
||||||
|
@ -31,7 +32,9 @@ export function CharityCard(props: { charity: Charity }) {
|
||||||
{raised > 0 && (
|
{raised > 0 && (
|
||||||
<Row className="text-primary mt-4 flex-1 items-end justify-center gap-2">
|
<Row className="text-primary mt-4 flex-1 items-end justify-center gap-2">
|
||||||
<span className="text-3xl">
|
<span className="text-3xl">
|
||||||
${Math.floor((raised ?? 0) / 100)}
|
{raised < 100
|
||||||
|
? manaToUSD(raised)
|
||||||
|
: '$' + Math.floor(raised / 100)}
|
||||||
</span>
|
</span>
|
||||||
<span>raised</span>
|
<span>raised</span>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
34
web/components/charity/feed-items.tsx
Normal file
34
web/components/charity/feed-items.tsx
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import { Txn } from '../../../common/txn'
|
||||||
|
import { Avatar } from '../avatar'
|
||||||
|
import { useUserById } from '../../hooks/use-users'
|
||||||
|
import { UserLink } from '../user-page'
|
||||||
|
import { manaToUSD } from '../../pages/charity/[charitySlug]'
|
||||||
|
import { RelativeTimestamp } from '../feed/feed-items'
|
||||||
|
|
||||||
|
export function Donation(props: { txn: Txn }) {
|
||||||
|
const { txn } = props
|
||||||
|
const user = useUserById(txn.fromId)
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return <>Loading...</>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mb-2 flow-root pr-2 md:pr-0">
|
||||||
|
<div className="relative flex items-center space-x-3">
|
||||||
|
<Avatar username={user.name} avatarUrl={user.avatarUrl} size="sm" />
|
||||||
|
<div className="min-w-0 flex-1">
|
||||||
|
<p className="mt-0.5 text-sm text-gray-500">
|
||||||
|
<UserLink
|
||||||
|
className="text-gray-500"
|
||||||
|
username={user.username}
|
||||||
|
name={user.name}
|
||||||
|
/>{' '}
|
||||||
|
donated {manaToUSD(txn.amount)}
|
||||||
|
<RelativeTimestamp time={txn.createdTime} />
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -50,7 +50,19 @@ export function ContractInfoDialog(props: { contract: Contract; bets: Bet[] }) {
|
||||||
<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="Market info" />
|
||||||
|
|
||||||
<div className="text-gray-500">Stats</div>
|
<div>Share</div>
|
||||||
|
|
||||||
|
<Row className="justify-start gap-4">
|
||||||
|
<CopyLinkButton contract={contract} />
|
||||||
|
<TweetButton
|
||||||
|
className="self-start"
|
||||||
|
tweetText={getTweetText(contract, false)}
|
||||||
|
/>
|
||||||
|
<ShareEmbedButton contract={contract} />
|
||||||
|
</Row>
|
||||||
|
<div />
|
||||||
|
|
||||||
|
<div>Stats</div>
|
||||||
<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>
|
||||||
{category && (
|
{category && (
|
||||||
|
@ -105,19 +117,7 @@ export function ContractInfoDialog(props: { contract: Contract; bets: Bet[] }) {
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div className="text-gray-500">Share</div>
|
<div>Tags</div>
|
||||||
|
|
||||||
<Row className="justify-start gap-4">
|
|
||||||
<CopyLinkButton contract={contract} />
|
|
||||||
<TweetButton
|
|
||||||
className="self-start"
|
|
||||||
tweetText={getTweetText(contract, false)}
|
|
||||||
/>
|
|
||||||
<ShareEmbedButton contract={contract} />
|
|
||||||
</Row>
|
|
||||||
<div />
|
|
||||||
|
|
||||||
<div className="text-gray-500">Tags</div>
|
|
||||||
<TagsInput contract={contract} />
|
<TagsInput contract={contract} />
|
||||||
<div />
|
<div />
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ export function ContractInfoDialog(props: { contract: Contract; bets: Bet[] }) {
|
||||||
!contract.resolution &&
|
!contract.resolution &&
|
||||||
(!closeTime || closeTime > Date.now()) && (
|
(!closeTime || closeTime > Date.now()) && (
|
||||||
<>
|
<>
|
||||||
<div className="text-gray-500">Add liquidity</div>
|
<div className="">Add liquidity</div>
|
||||||
<AddLiquidityPanel contract={contract} />
|
<AddLiquidityPanel contract={contract} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { AnswersGraph } from '../answers/answers-graph'
|
||||||
import { DPM, FreeResponse, FullContract } from '../../../common/contract'
|
import { DPM, FreeResponse, FullContract } from '../../../common/contract'
|
||||||
import { ContractDescription } from './contract-description'
|
import { ContractDescription } from './contract-description'
|
||||||
import { ContractDetails } from './contract-details'
|
import { ContractDetails } from './contract-details'
|
||||||
|
import { ShareMarket } from '../share-market'
|
||||||
|
|
||||||
export const ContractOverview = (props: {
|
export const ContractOverview = (props: {
|
||||||
contract: Contract
|
contract: Contract
|
||||||
|
@ -84,7 +85,9 @@ export const ContractOverview = (props: {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{contract.description && <Spacer h={6} />}
|
{(contract.description || isCreator) && <Spacer h={6} />}
|
||||||
|
|
||||||
|
{isCreator && <ShareMarket className="px-2" contract={contract} />}
|
||||||
|
|
||||||
<ContractDescription
|
<ContractDescription
|
||||||
className="px-2"
|
className="px-2"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Fragment } from 'react'
|
import { Fragment } from 'react'
|
||||||
import { LinkIcon } from '@heroicons/react/outline'
|
import { LinkIcon } from '@heroicons/react/outline'
|
||||||
import { Menu, Transition } from '@headlessui/react'
|
import { Menu, Transition } from '@headlessui/react'
|
||||||
|
import clsx from 'clsx'
|
||||||
import { Contract } from '../../common/contract'
|
import { Contract } from '../../common/contract'
|
||||||
import { copyToClipboard } from '../lib/util/copy'
|
import { copyToClipboard } from '../lib/util/copy'
|
||||||
import { contractPath } from '../lib/firebase/contracts'
|
import { contractPath } from '../lib/firebase/contracts'
|
||||||
|
@ -10,8 +11,11 @@ function copyContractUrl(contract: Contract) {
|
||||||
copyToClipboard(`https://${ENV_CONFIG.domain}${contractPath(contract)}`)
|
copyToClipboard(`https://${ENV_CONFIG.domain}${contractPath(contract)}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CopyLinkButton(props: { contract: Contract }) {
|
export function CopyLinkButton(props: {
|
||||||
const { contract } = props
|
contract: Contract
|
||||||
|
buttonClassName?: string
|
||||||
|
}) {
|
||||||
|
const { contract, buttonClassName } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu
|
<Menu
|
||||||
|
@ -20,12 +24,10 @@ export function CopyLinkButton(props: { contract: Contract }) {
|
||||||
onMouseUp={() => copyContractUrl(contract)}
|
onMouseUp={() => copyContractUrl(contract)}
|
||||||
>
|
>
|
||||||
<Menu.Button
|
<Menu.Button
|
||||||
className="btn btn-xs normal-case"
|
className={clsx(
|
||||||
style={{
|
'btn btn-xs border-2 border-green-600 bg-white normal-case text-green-600 hover:border-green-600 hover:bg-white',
|
||||||
backgroundColor: 'white',
|
buttonClassName
|
||||||
border: '2px solid #16A34A',
|
)}
|
||||||
color: '#16A34A', // text-green-600
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<LinkIcon className="mr-1.5 h-4 w-4" aria-hidden="true" />
|
<LinkIcon className="mr-1.5 h-4 w-4" aria-hidden="true" />
|
||||||
Copy link
|
Copy link
|
||||||
|
|
|
@ -310,7 +310,7 @@ export function CommentInput(props: {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function RelativeTimestamp(props: { time: number }) {
|
export function RelativeTimestamp(props: { time: number }) {
|
||||||
const { time } = props
|
const { time } = props
|
||||||
return (
|
return (
|
||||||
<DateTimeTooltip time={time}>
|
<DateTimeTooltip time={time}>
|
||||||
|
@ -385,6 +385,7 @@ export function FeedBet(props: {
|
||||||
async function submitComment() {
|
async function submitComment() {
|
||||||
if (!user || !comment || !canComment) return
|
if (!user || !comment || !canComment) return
|
||||||
await createComment(contract.id, comment, user, id)
|
await createComment(contract.id, comment, user, id)
|
||||||
|
setComment('')
|
||||||
}
|
}
|
||||||
|
|
||||||
const bought = amount >= 0 ? 'bought' : 'sold'
|
const bought = amount >= 0 ? 'bought' : 'sold'
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import clsx from 'clsx'
|
||||||
import { Answer } from '../../common/answer'
|
import { Answer } from '../../common/answer'
|
||||||
import { getProbability } from '../../common/calculate'
|
import { getProbability } from '../../common/calculate'
|
||||||
import {
|
import {
|
||||||
|
@ -126,7 +127,14 @@ export function AnswerLabel(props: {
|
||||||
truncated = text.slice(0, 75) + '...'
|
truncated = text.slice(0, 75) + '...'
|
||||||
}
|
}
|
||||||
|
|
||||||
return <span className={className}>{truncated}</span>
|
return (
|
||||||
|
<span
|
||||||
|
style={{ wordBreak: 'break-word' }}
|
||||||
|
className={clsx('whitespace-pre-line break-words', className)}
|
||||||
|
>
|
||||||
|
{truncated}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function FreeResponseAnswerToolTip(props: {
|
function FreeResponseAnswerToolTip(props: {
|
||||||
|
|
|
@ -72,9 +72,9 @@ export function ResolutionPanel(props: {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Col className={clsx('rounded-md bg-white px-8 py-6', className)}>
|
<Col className={clsx('rounded-md bg-white px-8 py-6', className)}>
|
||||||
<Title className="!mt-0 whitespace-nowrap" text="Resolve market" />
|
<div className="mb-6 whitespace-nowrap text-2xl">Resolve market</div>
|
||||||
|
|
||||||
<div className="mb-2 text-sm text-gray-500">Outcome</div>
|
<div className="mb-3 text-sm text-gray-500">Outcome</div>
|
||||||
|
|
||||||
<YesNoCancelSelector
|
<YesNoCancelSelector
|
||||||
className="mx-auto my-2"
|
className="mx-auto my-2"
|
||||||
|
|
26
web/components/share-market.tsx
Normal file
26
web/components/share-market.tsx
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import clsx from 'clsx'
|
||||||
|
import { Contract, contractUrl } from '../lib/firebase/contracts'
|
||||||
|
import { CopyLinkButton } from './copy-link-button'
|
||||||
|
import { Col } from './layout/col'
|
||||||
|
import { Row } from './layout/row'
|
||||||
|
|
||||||
|
export function ShareMarket(props: { contract: Contract; className?: string }) {
|
||||||
|
const { contract, className } = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Col className={clsx(className, 'gap-3')}>
|
||||||
|
<div>Share your market</div>
|
||||||
|
<Row className="mb-6 items-center">
|
||||||
|
<input
|
||||||
|
className="input input-bordered flex-1 rounded-r-none text-gray-500"
|
||||||
|
type="text"
|
||||||
|
value={contractUrl(contract)}
|
||||||
|
/>
|
||||||
|
<CopyLinkButton
|
||||||
|
contract={contract}
|
||||||
|
buttonClassName="btn-md rounded-l-none"
|
||||||
|
/>
|
||||||
|
</Row>
|
||||||
|
</Col>
|
||||||
|
)
|
||||||
|
}
|
|
@ -26,10 +26,8 @@ export const useSaveShares = (
|
||||||
_.sumBy(noBets, (bet) => bet.shares),
|
_.sumBy(noBets, (bet) => bet.shares),
|
||||||
]
|
]
|
||||||
|
|
||||||
const [yesFloorShares, noFloorShares] = [
|
const yesFloorShares = Math.round(yesShares) === 0 ? 0 : Math.floor(yesShares)
|
||||||
Math.floor(yesShares),
|
const noFloorShares = Math.round(noShares) === 0 ? 0 : Math.floor(noShares)
|
||||||
Math.floor(noShares),
|
|
||||||
]
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Save yes and no shares to local storage.
|
// Save yes and no shares to local storage.
|
||||||
|
|
|
@ -26,6 +26,7 @@ import { DAY_MS } from '../../../common/util/time'
|
||||||
import { MAX_FEED_CONTRACTS } from '../../../common/recommended-contracts'
|
import { MAX_FEED_CONTRACTS } from '../../../common/recommended-contracts'
|
||||||
import { Bet } from '../../../common/bet'
|
import { Bet } from '../../../common/bet'
|
||||||
import { Comment } from '../../../common/comment'
|
import { Comment } from '../../../common/comment'
|
||||||
|
import { ENV_CONFIG } from '../../../common/envs/constants'
|
||||||
export type { Contract }
|
export type { Contract }
|
||||||
|
|
||||||
export function contractPath(contract: Contract) {
|
export function contractPath(contract: Contract) {
|
||||||
|
@ -36,6 +37,10 @@ export function homeContractPath(contract: Contract) {
|
||||||
return `/home?c=${contract.slug}`
|
return `/home?c=${contract.slug}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function contractUrl(contract: Contract) {
|
||||||
|
return `https://${ENV_CONFIG.domain}${contractPath(contract)}`
|
||||||
|
}
|
||||||
|
|
||||||
export function contractMetrics(contract: Contract) {
|
export function contractMetrics(contract: Contract) {
|
||||||
const { createdTime, resolutionTime, isResolved } = contract
|
const { createdTime, resolutionTime, isResolved } = contract
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,9 @@ import Custom404 from '../404'
|
||||||
import { useCharityTxns } from '../../hooks/use-charity-txns'
|
import { useCharityTxns } from '../../hooks/use-charity-txns'
|
||||||
import { useWindowSize } from '../../hooks/use-window-size'
|
import { useWindowSize } from '../../hooks/use-window-size'
|
||||||
import Confetti from 'react-confetti'
|
import Confetti from 'react-confetti'
|
||||||
|
import { Donation } from '../../components/charity/feed-items'
|
||||||
|
|
||||||
const manaToUSD = (mana: number) =>
|
export const manaToUSD = (mana: number) =>
|
||||||
(mana / 100).toLocaleString('en-US', { style: 'currency', currency: 'USD' })
|
(mana / 100).toLocaleString('en-US', { style: 'currency', currency: 'USD' })
|
||||||
|
|
||||||
export default function CharityPageWrapper() {
|
export default function CharityPageWrapper() {
|
||||||
|
@ -91,6 +92,9 @@ function CharityPage(props: { charity: Charity }) {
|
||||||
</Row>
|
</Row>
|
||||||
<h2 className="mt-7 mb-2 text-xl text-indigo-700">About</h2>
|
<h2 className="mt-7 mb-2 text-xl text-indigo-700">About</h2>
|
||||||
<Blurb text={description} />
|
<Blurb text={description} />
|
||||||
|
{txns.map((txn) => (
|
||||||
|
<Donation key={txn.id} txn={txn} />
|
||||||
|
))}
|
||||||
</Col>
|
</Col>
|
||||||
</Col>
|
</Col>
|
||||||
</Page>
|
</Page>
|
||||||
|
@ -98,8 +102,7 @@ function CharityPage(props: { charity: Charity }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function Blurb({ text }: { text: string }) {
|
function Blurb({ text }: { text: string }) {
|
||||||
// Default to open for now (aka don't actually hide any text yet.)
|
const [open, setOpen] = useState(false)
|
||||||
const [open, setOpen] = useState(true)
|
|
||||||
|
|
||||||
// Calculate whether the full blurb is already shown
|
// Calculate whether the full blurb is already shown
|
||||||
const ref = useRef<HTMLDivElement>(null)
|
const ref = useRef<HTMLDivElement>(null)
|
||||||
|
@ -125,7 +128,7 @@ function Blurb({ text }: { text: string }) {
|
||||||
onClick={() => setOpen(!open)}
|
onClick={() => setOpen(!open)}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'btn btn-link capitalize-none my-3 normal-case text-indigo-700',
|
'btn btn-link capitalize-none my-3 normal-case text-indigo-700',
|
||||||
hideExpander && 'hidden'
|
hideExpander && 'invisible'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{open ? 'Hide' : 'Read more'}
|
{open ? 'Hide' : 'Read more'}
|
||||||
|
@ -204,7 +207,7 @@ function DonationBox(props: {
|
||||||
Amount
|
Amount
|
||||||
</label>
|
</label>
|
||||||
<BuyAmountInput
|
<BuyAmountInput
|
||||||
inputClassName="w-full donate-input"
|
inputClassName="w-full max-w-none donate-input"
|
||||||
amount={amount}
|
amount={amount}
|
||||||
onChange={setAmount}
|
onChange={setAmount}
|
||||||
error={error}
|
error={error}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user