Use the generated OpenGraph card in all markets (#23)
* Run OpenGraph server on dev port 3001 * Cut '%' since frontend already passes it * Use the generated card in markets * Rename to ogCardProps * Don't show creator avatar url, for now * Remove bad comment
This commit is contained in:
parent
ed37d93868
commit
feca042e47
|
@ -62,12 +62,11 @@ export function parseRequest(req: IncomingMessage) {
|
|||
|
||||
question:
|
||||
getString(question) || "Will you create a prediction market on Manifold?",
|
||||
probability: getString(probability) || "85",
|
||||
probability: getString(probability) || "85%",
|
||||
metadata: getString(metadata) || "Jan 1 • M$ 123 pool",
|
||||
creatorName: getString(creatorName) || "Manifold Markets",
|
||||
creatorUsername: getString(creatorUsername) || "ManifoldMarkets",
|
||||
creatorAvatarUrl:
|
||||
getString(creatorAvatarUrl) || "https://manifold.markets/logo.png",
|
||||
creatorAvatarUrl: getString(creatorAvatarUrl) || "",
|
||||
};
|
||||
parsedRequest.images = getDefaultImages(parsedRequest.images);
|
||||
return parsedRequest;
|
||||
|
|
|
@ -93,6 +93,7 @@ export function getHtml(parsedReq: ParsedRequest) {
|
|||
creatorUsername,
|
||||
creatorAvatarUrl,
|
||||
} = parsedReq;
|
||||
const hideAvatar = creatorAvatarUrl ? "" : "hidden";
|
||||
return `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
@ -110,11 +111,11 @@ export function getHtml(parsedReq: ParsedRequest) {
|
|||
<div class="absolute left-24 top-8">
|
||||
<div class="flex flex-row align-bottom gap-6">
|
||||
<img
|
||||
class="h-24 w-24 rounded-full bg-white flex items-center justify-center"
|
||||
class="h-24 w-24 rounded-full bg-white flex items-center justify-center ${hideAvatar}"
|
||||
src="${creatorAvatarUrl}"
|
||||
alt=""
|
||||
/>
|
||||
<div class="flex flex-col">
|
||||
<div class="flex flex-col gap-2">
|
||||
<p class="text-gray-900 text-3xl">${creatorName}</p>
|
||||
<p class="text-gray-500 text-3xl">@${creatorUsername}</p>
|
||||
</div>
|
||||
|
@ -139,11 +140,11 @@ export function getHtml(parsedReq: ParsedRequest) {
|
|||
</div>
|
||||
|
||||
<div class="flex flex-row justify-between gap-12 pt-36">
|
||||
<div class="text-indigo-700 text-6xl leading-snug">
|
||||
<div class="text-indigo-700 text-6xl leading-tight">
|
||||
${question}
|
||||
</div>
|
||||
<div class="flex flex-col text-primary">
|
||||
<div class="text-8xl">${probability}%</div>
|
||||
<div class="text-8xl">${probability}</div>
|
||||
<div class="text-4xl">chance</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"private": true,
|
||||
"scripts": {
|
||||
"build": "tsc -p api/tsconfig.json && tsc -p web/tsconfig.json",
|
||||
"start": "cd .. && vercel dev"
|
||||
"start": "cd .. && vercel dev --listen 3001"
|
||||
},
|
||||
"dependencies": {
|
||||
"chrome-aws-lambda": "7.0.0",
|
||||
|
|
|
@ -1,12 +1,35 @@
|
|||
import Head from 'next/head'
|
||||
|
||||
export type OgCardProps = {
|
||||
question: string
|
||||
probability: string
|
||||
metadata: string
|
||||
creatorName: string
|
||||
creatorUsername: string
|
||||
// TODO: Store creator avatar url in each contract, then enable this
|
||||
// creatorAvatarUrl: string
|
||||
}
|
||||
|
||||
function buildCardUrl(props: OgCardProps) {
|
||||
// URL encode each of the props, then add them as query params
|
||||
return (
|
||||
`https://manifold-og-image.vercel.app/m.png` +
|
||||
`?question=${encodeURIComponent(props.question)}` +
|
||||
`&probability=${encodeURIComponent(props.probability)}` +
|
||||
`&metadata=${encodeURIComponent(props.metadata)}` +
|
||||
`&creatorName=${encodeURIComponent(props.creatorName)}` +
|
||||
`&creatorUsername=${encodeURIComponent(props.creatorUsername)}`
|
||||
)
|
||||
}
|
||||
|
||||
export function SEO(props: {
|
||||
title: string
|
||||
description: string
|
||||
url?: string
|
||||
children?: any[]
|
||||
ogCardProps?: OgCardProps
|
||||
}) {
|
||||
const { title, description, url, children } = props
|
||||
const { title, description, url, children, ogCardProps } = props
|
||||
|
||||
return (
|
||||
<Head>
|
||||
|
@ -34,6 +57,22 @@ export function SEO(props: {
|
|||
/>
|
||||
)}
|
||||
|
||||
{ogCardProps && (
|
||||
<>
|
||||
<meta
|
||||
property="og:image"
|
||||
content={buildCardUrl(ogCardProps)}
|
||||
key="image1"
|
||||
/>
|
||||
<meta name="twitter:card" content="summary_large_image" key="card" />
|
||||
<meta
|
||||
name="twitter:image"
|
||||
content={buildCardUrl(ogCardProps)}
|
||||
key="image2"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{children}
|
||||
</Head>
|
||||
)
|
||||
|
|
|
@ -165,3 +165,24 @@ export function ContractDetails(props: { contract: Contract }) {
|
|||
</Col>
|
||||
)
|
||||
}
|
||||
|
||||
// String version of the above, to send to the OpenGraph image generator
|
||||
export function contractTextDetails(contract: Contract) {
|
||||
const { question, description, closeTime } = contract
|
||||
const { truePool, createdDate, resolvedDate } = compute(contract)
|
||||
|
||||
const tags = parseTags(`${question} ${description}`).map((tag) => `#${tag}`)
|
||||
|
||||
return (
|
||||
`${contract.creatorUsername} • ${
|
||||
resolvedDate ? `${createdDate} - ${resolvedDate}` : createdDate
|
||||
}` +
|
||||
(closeTime
|
||||
? ` • ${closeTime > Date.now() ? 'Closes' : 'Closed'} ${dayjs(
|
||||
closeTime
|
||||
).format('MMM D, h:mma')}`
|
||||
: '') +
|
||||
` • ${formatMoney(truePool)} pool` +
|
||||
(tags.length > 0 ? ` • ${tags.join(' ')}` : '')
|
||||
)
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
} from '../../lib/firebase/contracts'
|
||||
import { SEO } from '../../components/SEO'
|
||||
import { Page } from '../../components/page'
|
||||
import { contractTextDetails } from '../../components/contract-card'
|
||||
|
||||
export async function getStaticProps(props: { params: any }) {
|
||||
const { username, contractSlug } = props.params
|
||||
|
@ -63,12 +64,21 @@ export default function ContractPage(props: {
|
|||
? `Resolved ${resolution}. ${contract.description}`
|
||||
: `${probPercent} chance. ${contract.description}`
|
||||
|
||||
const ogCardProps = {
|
||||
question,
|
||||
probability: probPercent,
|
||||
metadata: contractTextDetails(contract),
|
||||
creatorName: contract.creatorName,
|
||||
creatorUsername: contract.creatorUsername,
|
||||
}
|
||||
|
||||
return (
|
||||
<Page wide={allowTrade}>
|
||||
<SEO
|
||||
title={question}
|
||||
description={description}
|
||||
url={`/${props.username}/${props.slug}`}
|
||||
ogCardProps={ogCardProps}
|
||||
/>
|
||||
|
||||
<Col className="w-full md:flex-row justify-between mt-6">
|
||||
|
|
|
@ -26,15 +26,17 @@ function MyApp({ Component, pageProps }: AppProps) {
|
|||
key="description2"
|
||||
/>
|
||||
<meta property="og:url" content="https://manifold.markets" key="url" />
|
||||
<meta name="twitter:card" content="summary" />
|
||||
<meta name="twitter:card" content="summary" key="card" />
|
||||
<meta name="twitter:site" content="@manifoldmarkets" />
|
||||
<meta
|
||||
property="og:image"
|
||||
content="https://manifold.markets/logo-cover.png"
|
||||
key="image1"
|
||||
/>
|
||||
<meta
|
||||
name="twitter:image"
|
||||
content="https://manifold.markets/logo-bg.png"
|
||||
key="image2"
|
||||
/>
|
||||
</Head>
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user