Move tweet, embed buttons plus communities and tags into a market info dialog
This commit is contained in:
parent
7e6545a669
commit
5e8decfa4e
|
@ -19,9 +19,8 @@ import { fromNow } from '../lib/util/time'
|
|||
import { Avatar } from './avatar'
|
||||
import { Spacer } from './layout/spacer'
|
||||
import { useState } from 'react'
|
||||
import { TweetButton } from './tweet-button'
|
||||
import { getProbability } from '../../common/calculate'
|
||||
import { ShareEmbedButton } from './share-embed-button'
|
||||
import { ContractInfoDialog } from './contract-info-dialog'
|
||||
|
||||
export function ContractCard(props: {
|
||||
contract: Contract
|
||||
|
@ -179,11 +178,9 @@ export function ContractDetails(props: {
|
|||
const { closeTime, creatorName, creatorUsername } = contract
|
||||
const { volumeLabel, createdDate, resolvedDate } = contractMetrics(contract)
|
||||
|
||||
const tweetText = getTweetText(contract, !!isCreator)
|
||||
|
||||
return (
|
||||
<Col className="gap-2 text-sm text-gray-500 sm:flex-row sm:flex-wrap">
|
||||
<Row className="flex-wrap items-center gap-x-4 gap-y-3">
|
||||
<Row className="flex-1 flex-wrap items-center gap-x-4 gap-y-3">
|
||||
<Row className="items-center gap-2">
|
||||
<Avatar
|
||||
username={creatorUsername}
|
||||
|
@ -236,8 +233,8 @@ export function ContractDetails(props: {
|
|||
|
||||
{!hideShareButtons && (
|
||||
<>
|
||||
<TweetButton className="self-end" tweetText={tweetText} />
|
||||
<ShareEmbedButton contract={contract} />
|
||||
<div className="flex-1" />
|
||||
<ContractInfoDialog contract={contract} />
|
||||
</>
|
||||
)}
|
||||
</Row>
|
||||
|
@ -331,24 +328,3 @@ function EditableCloseDate(props: {
|
|||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const getTweetText = (contract: Contract, isCreator: boolean) => {
|
||||
const { question, creatorName, resolution, outcomeType } = contract
|
||||
const isBinary = outcomeType === 'BINARY'
|
||||
|
||||
const tweetQuestion = isCreator
|
||||
? question
|
||||
: `${question} Asked by ${creatorName}.`
|
||||
const tweetDescription = resolution
|
||||
? `Resolved ${resolution}!`
|
||||
: isBinary
|
||||
? `Currently ${getBinaryProbPercent(
|
||||
contract
|
||||
)} chance, place your bets here:`
|
||||
: `Submit your own answer:`
|
||||
|
||||
const timeParam = `${Date.now()}`.substring(7)
|
||||
const url = `https://manifold.markets${contractPath(contract)}?t=${timeParam}`
|
||||
|
||||
return `${tweetQuestion}\n\n${tweetDescription}\n\n${url}`
|
||||
}
|
||||
|
|
88
web/components/contract-info-dialog.tsx
Normal file
88
web/components/contract-info-dialog.tsx
Normal file
|
@ -0,0 +1,88 @@
|
|||
import { DotsHorizontalIcon } from '@heroicons/react/outline'
|
||||
import clsx from 'clsx'
|
||||
import { useState } from 'react'
|
||||
import { Contract } from '../../common/contract'
|
||||
import { useFoldsWithTags } from '../hooks/use-fold'
|
||||
import { useUser } from '../hooks/use-user'
|
||||
import { contractPath, getBinaryProbPercent } from '../lib/firebase/contracts'
|
||||
import { Col } from './layout/col'
|
||||
import { Modal } from './layout/modal'
|
||||
import { Row } from './layout/row'
|
||||
import { ShareEmbedButton } from './share-embed-button'
|
||||
import { TagsInput } from './tags-input'
|
||||
import { FoldTagList } from './tags-list'
|
||||
import { Title } from './title'
|
||||
import { TweetButton } from './tweet-button'
|
||||
|
||||
export function ContractInfoDialog(props: { contract: Contract }) {
|
||||
const { contract } = props
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const user = useUser()
|
||||
|
||||
const folds = (useFoldsWithTags(contract.tags) ?? []).filter(
|
||||
(fold) => fold.followCount > 1 || user?.id === fold.curatorId
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
className="group flex items-center rounded-md px-3 py-2 text-sm font-medium text-gray-600 hover:cursor-pointer hover:bg-gray-100"
|
||||
onClick={() => setOpen(true)}
|
||||
>
|
||||
<DotsHorizontalIcon
|
||||
className={clsx(
|
||||
'h-6 w-6 flex-shrink-0 text-gray-400 group-hover:text-gray-500'
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<Modal open={open} setOpen={setOpen}>
|
||||
<Col className="gap-4 rounded bg-white p-6">
|
||||
<Title className="!mt-0 !mb-0" text="Market info" />
|
||||
|
||||
<div className="text-gray-500">Share</div>
|
||||
<Row className="justify-start gap-4">
|
||||
<TweetButton
|
||||
className="self-start"
|
||||
tweetText={getTweetText(contract, false)}
|
||||
/>
|
||||
<ShareEmbedButton contract={contract} />
|
||||
</Row>
|
||||
<div />
|
||||
<div className="text-gray-500">Communities</div>
|
||||
<FoldTagList folds={folds} noLabel />
|
||||
<div />
|
||||
|
||||
<div className="text-gray-500">Tags</div>
|
||||
<TagsInput contract={contract} />
|
||||
|
||||
<div />
|
||||
</Col>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const getTweetText = (contract: Contract, isCreator: boolean) => {
|
||||
const { question, creatorName, resolution, outcomeType } = contract
|
||||
const isBinary = outcomeType === 'BINARY'
|
||||
|
||||
const tweetQuestion = isCreator
|
||||
? question
|
||||
: `${question}\nAsked by ${creatorName}.`
|
||||
const tweetDescription = resolution
|
||||
? `Resolved ${resolution}!`
|
||||
: isBinary
|
||||
? `Currently ${getBinaryProbPercent(
|
||||
contract
|
||||
)} chance, place your bets here:`
|
||||
: `Submit your own answer:`
|
||||
|
||||
const timeParam = `${Date.now()}`.substring(7)
|
||||
const url = `https://manifold.markets${contractPath(contract)}?t=${timeParam}`
|
||||
|
||||
return `${tweetQuestion}\n\n${tweetDescription}\n\n${url}`
|
||||
}
|
|
@ -9,10 +9,7 @@ import clsx from 'clsx'
|
|||
import { ContractDetails, ResolutionOrChance } from './contract-card'
|
||||
import { Bet } from '../../common/bet'
|
||||
import { Comment } from '../../common/comment'
|
||||
import { RevealableTagsInput, TagsInput } from './tags-input'
|
||||
import BetRow from './bet-row'
|
||||
import { Fold } from '../../common/fold'
|
||||
import { FoldTagList } from './tags-list'
|
||||
import { ContractActivity } from './feed/contract-activity'
|
||||
import { AnswersGraph } from './answers/answers-graph'
|
||||
import { DPM, FreeResponse, FullContract } from '../../common/contract'
|
||||
|
@ -21,11 +18,10 @@ export const ContractOverview = (props: {
|
|||
contract: Contract
|
||||
bets: Bet[]
|
||||
comments: Comment[]
|
||||
folds: Fold[]
|
||||
children?: any
|
||||
className?: string
|
||||
}) => {
|
||||
const { contract, bets, comments, folds, children, className } = props
|
||||
const { contract, bets, comments, children, className } = props
|
||||
const { question, resolution, creatorId, outcomeType } = contract
|
||||
|
||||
const user = useUser()
|
||||
|
@ -75,26 +71,6 @@ export const ContractOverview = (props: {
|
|||
|
||||
{children}
|
||||
|
||||
<Row className="mt-6 hidden items-center justify-between gap-4 sm:flex">
|
||||
{folds.length === 0 ? (
|
||||
<TagsInput className={clsx('mx-4')} contract={contract} />
|
||||
) : (
|
||||
<FoldTagList folds={folds} />
|
||||
)}
|
||||
</Row>
|
||||
|
||||
<Col className="mt-6 gap-4 sm:hidden">
|
||||
{folds.length === 0 ? (
|
||||
<TagsInput contract={contract} />
|
||||
) : (
|
||||
<FoldTagList folds={folds} />
|
||||
)}
|
||||
</Col>
|
||||
|
||||
{folds.length > 0 && (
|
||||
<RevealableTagsInput className="mt-4" contract={contract} />
|
||||
)}
|
||||
|
||||
<Spacer h={12} />
|
||||
|
||||
<ContractActivity
|
||||
|
|
|
@ -6,23 +6,23 @@ import { contractPath } from '../lib/firebase/contracts'
|
|||
import { DOMAIN } from '../../common/envs/constants'
|
||||
import { copyToClipboard } from '../lib/util/copy'
|
||||
|
||||
function copyEmbedCode(contract: Contract) {
|
||||
const title = contract.question
|
||||
const src = `https://${DOMAIN}/embed${contractPath(contract)}`
|
||||
|
||||
const embedCode = `<iframe width="560" height="405" src="${src}" title="${title}" frameborder="0"></iframe>`
|
||||
|
||||
copyToClipboard(embedCode)
|
||||
}
|
||||
|
||||
export function ShareEmbedButton(props: { contract: Contract }) {
|
||||
const { contract } = props
|
||||
|
||||
const copyEmbed = () => {
|
||||
const title = contract.question
|
||||
const src = `https://${DOMAIN}/embed${contractPath(contract)}`
|
||||
|
||||
const embedCode = `<iframe width="560" height="405" src="${src}" title="${title}" frameborder="0"></iframe>`
|
||||
|
||||
copyToClipboard(embedCode)
|
||||
}
|
||||
|
||||
return (
|
||||
<Menu
|
||||
as="div"
|
||||
className="relative z-10 flex-shrink-0"
|
||||
onMouseUp={copyEmbed}
|
||||
onMouseUp={() => copyEmbedCode(contract)}
|
||||
>
|
||||
<Menu.Button
|
||||
className="btn btn-xs normal-case"
|
||||
|
|
|
@ -27,7 +27,7 @@ export function TagsInput(props: { contract: Contract; className?: string }) {
|
|||
|
||||
return (
|
||||
<Col className={clsx('gap-4', className)}>
|
||||
<TagsList tags={newTags} />
|
||||
<TagsList tags={newTags} noLabel />
|
||||
|
||||
<Row className="items-center gap-4">
|
||||
<input
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import clsx from 'clsx'
|
||||
import { Col } from './layout/col'
|
||||
|
||||
import { Row } from './layout/row'
|
||||
import { SiteLink } from './site-link'
|
||||
|
@ -70,15 +71,17 @@ export function FoldTagList(props: {
|
|||
}) {
|
||||
const { folds, noLabel, className } = props
|
||||
return (
|
||||
<Row className={clsx('flex-wrap items-center gap-2', className)}>
|
||||
{folds.length > 0 && (
|
||||
<>
|
||||
{!noLabel && <div className="mr-1 text-gray-500">Communities</div>}
|
||||
{folds.map((fold) => (
|
||||
<FoldTag key={fold.slug} fold={fold} />
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</Row>
|
||||
<Col className="gap-2">
|
||||
{!noLabel && <div className="mr-1 text-gray-500">Communities</div>}
|
||||
<Row className={clsx('flex-wrap items-center gap-2', className)}>
|
||||
{folds.length > 0 && (
|
||||
<>
|
||||
{folds.map((fold) => (
|
||||
<FoldTag key={fold.slug} fold={fold} />
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</Row>
|
||||
</Col>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import clsx from 'clsx'
|
||||
|
||||
export function TweetButton(props: { className?: string; tweetText?: string }) {
|
||||
export function TweetButton(props: { className?: string; tweetText: string }) {
|
||||
const { tweetText, className } = props
|
||||
|
||||
return (
|
||||
|
@ -11,9 +11,7 @@ export function TweetButton(props: { className?: string; tweetText?: string }) {
|
|||
border: '2px solid #1da1f2',
|
||||
color: '#1da1f2',
|
||||
}}
|
||||
href={`https://twitter.com/intent/tweet?text=${encodeURIComponent(
|
||||
tweetText ?? ''
|
||||
)}`}
|
||||
href={getTweetHref(tweetText)}
|
||||
target="_blank"
|
||||
>
|
||||
<img className="mr-2" src={'/twitter-logo.svg'} width={15} height={15} />
|
||||
|
@ -21,3 +19,9 @@ export function TweetButton(props: { className?: string; tweetText?: string }) {
|
|||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
function getTweetHref(tweetText: string) {
|
||||
return `https://twitter.com/intent/tweet?text=${encodeURIComponent(
|
||||
tweetText ?? ''
|
||||
)}`
|
||||
}
|
||||
|
|
|
@ -103,10 +103,6 @@ export default function ContractPage(props: {
|
|||
comments.sort((c1, c2) => c1.createdTime - c2.createdTime)
|
||||
bets.sort((bet1, bet2) => bet1.createdTime - bet2.createdTime)
|
||||
|
||||
const folds = (useFoldsWithTags(contract?.tags) ?? props.folds).filter(
|
||||
(fold) => fold.followCount > 1 || user?.id === fold.curatorId
|
||||
)
|
||||
|
||||
if (!contract) {
|
||||
return <Custom404 />
|
||||
}
|
||||
|
@ -146,7 +142,6 @@ export default function ContractPage(props: {
|
|||
contract={contract}
|
||||
bets={bets ?? []}
|
||||
comments={comments ?? []}
|
||||
folds={folds}
|
||||
>
|
||||
{contract.outcomeType === 'FREE_RESPONSE' && (
|
||||
<>
|
||||
|
|
Loading…
Reference in New Issue
Block a user