Move tweet, embed buttons plus communities and tags into a market info dialog

This commit is contained in:
James Grugett 2022-04-07 15:52:54 -05:00
parent 7e6545a669
commit 5e8decfa4e
8 changed files with 125 additions and 83 deletions

View File

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

View 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}`
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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 ?? ''
)}`
}

View File

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