Update SEO for non-binary markets

This commit is contained in:
James Grugett 2022-02-17 18:34:11 -06:00
parent 8e33c2b639
commit 0a3b14883c
4 changed files with 64 additions and 58 deletions

View File

@ -1,10 +1,10 @@
import { IncomingMessage } from "http"; import { IncomingMessage } from 'http'
import { parse } from "url"; import { parse } from 'url'
import { ParsedRequest } from "./types"; import { ParsedRequest } from './types'
export function parseRequest(req: IncomingMessage) { export function parseRequest(req: IncomingMessage) {
console.log("HTTP " + req.url); console.log('HTTP ' + req.url)
const { pathname, query } = parse(req.url || "/", true); const { pathname, query } = parse(req.url || '/', true)
const { const {
fontSize, fontSize,
images, images,
@ -20,73 +20,73 @@ export function parseRequest(req: IncomingMessage) {
creatorName, creatorName,
creatorUsername, creatorUsername,
creatorAvatarUrl, creatorAvatarUrl,
} = query || {}; } = query || {}
if (Array.isArray(fontSize)) { if (Array.isArray(fontSize)) {
throw new Error("Expected a single fontSize"); throw new Error('Expected a single fontSize')
} }
if (Array.isArray(theme)) { if (Array.isArray(theme)) {
throw new Error("Expected a single theme"); throw new Error('Expected a single theme')
} }
const arr = (pathname || "/").slice(1).split("."); const arr = (pathname || '/').slice(1).split('.')
let extension = ""; let extension = ''
let text = ""; let text = ''
if (arr.length === 0) { if (arr.length === 0) {
text = ""; text = ''
} else if (arr.length === 1) { } else if (arr.length === 1) {
text = arr[0]; text = arr[0]
} else { } else {
extension = arr.pop() as string; extension = arr.pop() as string
text = arr.join("."); text = arr.join('.')
} }
// Take a url query param and return a single string // Take a url query param and return a single string
const getString = (stringOrArray: string[] | string | undefined): string => { const getString = (stringOrArray: string[] | string | undefined): string => {
if (Array.isArray(stringOrArray)) { if (Array.isArray(stringOrArray)) {
// If the query param is an array, return the first element // If the query param is an array, return the first element
return stringOrArray[0]; return stringOrArray[0]
} }
return stringOrArray || ""; return stringOrArray || ''
}; }
const parsedRequest: ParsedRequest = { const parsedRequest: ParsedRequest = {
fileType: extension === "jpeg" ? extension : "png", fileType: extension === 'jpeg' ? extension : 'png',
text: decodeURIComponent(text), text: decodeURIComponent(text),
theme: theme === "dark" ? "dark" : "light", theme: theme === 'dark' ? 'dark' : 'light',
md: md === "1" || md === "true", md: md === '1' || md === 'true',
fontSize: fontSize || "96px", fontSize: fontSize || '96px',
images: getArray(images), images: getArray(images),
widths: getArray(widths), widths: getArray(widths),
heights: getArray(heights), heights: getArray(heights),
question: question:
getString(question) || "Will you create a prediction market on Manifold?", getString(question) || 'Will you create a prediction market on Manifold?',
probability: getString(probability) || "85%", probability: getString(probability),
metadata: getString(metadata) || "Jan 1  •  M$ 123 pool", metadata: getString(metadata) || 'Jan 1  •  M$ 123 pool',
creatorName: getString(creatorName) || "Manifold Markets", creatorName: getString(creatorName) || 'Manifold Markets',
creatorUsername: getString(creatorUsername) || "ManifoldMarkets", creatorUsername: getString(creatorUsername) || 'ManifoldMarkets',
creatorAvatarUrl: getString(creatorAvatarUrl) || "", creatorAvatarUrl: getString(creatorAvatarUrl) || '',
}; }
parsedRequest.images = getDefaultImages(parsedRequest.images); parsedRequest.images = getDefaultImages(parsedRequest.images)
return parsedRequest; return parsedRequest
} }
function getArray(stringOrArray: string[] | string | undefined): string[] { function getArray(stringOrArray: string[] | string | undefined): string[] {
if (typeof stringOrArray === "undefined") { if (typeof stringOrArray === 'undefined') {
return []; return []
} else if (Array.isArray(stringOrArray)) { } else if (Array.isArray(stringOrArray)) {
return stringOrArray; return stringOrArray
} else { } else {
return [stringOrArray]; return [stringOrArray]
} }
} }
function getDefaultImages(images: string[]): string[] { function getDefaultImages(images: string[]): string[] {
const defaultImage = "https://manifold.markets/logo.png"; const defaultImage = 'https://manifold.markets/logo.png'
if (!images || !images[0]) { if (!images || !images[0]) {
return [defaultImage]; return [defaultImage]
} }
return images; return images
} }

View File

@ -1,15 +1,15 @@
import { sanitizeHtml } from "./sanitizer"; import { sanitizeHtml } from './sanitizer'
import { ParsedRequest } from "./types"; import { ParsedRequest } from './types'
function getCss(theme: string, fontSize: string) { function getCss(theme: string, fontSize: string) {
let background = "white"; let background = 'white'
let foreground = "black"; let foreground = 'black'
let radial = "lightgray"; let radial = 'lightgray'
if (theme === "dark") { if (theme === 'dark') {
background = "black"; background = 'black'
foreground = "white"; foreground = 'white'
radial = "dimgray"; radial = 'dimgray'
} }
// To use Readex Pro: `font-family: 'Readex Pro', sans-serif;` // To use Readex Pro: `font-family: 'Readex Pro', sans-serif;`
return ` return `
@ -78,7 +78,7 @@ function getCss(theme: string, fontSize: string) {
.text-primary { .text-primary {
color: #11b981; color: #11b981;
} }
`; `
} }
export function getHtml(parsedReq: ParsedRequest) { export function getHtml(parsedReq: ParsedRequest) {
@ -92,8 +92,8 @@ export function getHtml(parsedReq: ParsedRequest) {
creatorName, creatorName,
creatorUsername, creatorUsername,
creatorAvatarUrl, creatorAvatarUrl,
} = parsedReq; } = parsedReq
const hideAvatar = creatorAvatarUrl ? "" : "hidden"; const hideAvatar = creatorAvatarUrl ? '' : 'hidden'
return `<!DOCTYPE html> return `<!DOCTYPE html>
<html> <html>
<head> <head>
@ -145,7 +145,7 @@ export function getHtml(parsedReq: ParsedRequest) {
</div> </div>
<div class="flex flex-col text-primary"> <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 class="text-4xl">${probability !== '' ? 'chance' : ''}</div>
</div> </div>
</div> </div>
@ -157,5 +157,5 @@ export function getHtml(parsedReq: ParsedRequest) {
</div> </div>
</div> </div>
</body> </body>
</html>`; </html>`
} }

View File

@ -2,7 +2,7 @@ import Head from 'next/head'
export type OgCardProps = { export type OgCardProps = {
question: string question: string
probability: string probability?: string
metadata: string metadata: string
creatorName: string creatorName: string
creatorUsername: string creatorUsername: string
@ -11,11 +11,16 @@ export type OgCardProps = {
} }
function buildCardUrl(props: OgCardProps) { function buildCardUrl(props: OgCardProps) {
const probabilityParam =
props.probability === undefined
? ''
: `&probability=${encodeURIComponent(props.probability ?? '')}`
// URL encode each of the props, then add them as query params // URL encode each of the props, then add them as query params
return ( return (
`https://manifold-og-image.vercel.app/m.png` + `https://manifold-og-image.vercel.app/m.png` +
`?question=${encodeURIComponent(props.question)}` + `?question=${encodeURIComponent(props.question)}` +
`&probability=${encodeURIComponent(props.probability)}` + probabilityParam +
`&metadata=${encodeURIComponent(props.metadata)}` + `&metadata=${encodeURIComponent(props.metadata)}` +
`&creatorName=${encodeURIComponent(props.creatorName)}` + `&creatorName=${encodeURIComponent(props.creatorName)}` +
`&creatorUsername=${encodeURIComponent(props.creatorUsername)}` `&creatorUsername=${encodeURIComponent(props.creatorUsername)}`

View File

@ -102,8 +102,7 @@ export default function ContractPage(props: {
const allowResolve = !isResolved && isCreator && !!user const allowResolve = !isResolved && isCreator && !!user
const hasSidePanel = isBinary && (allowTrade || allowResolve) const hasSidePanel = isBinary && (allowTrade || allowResolve)
// TODO(James): Create SEO props for non-binary contracts. const ogCardProps = getOpenGraphProps(contract)
const ogCardProps = isBinary ? getOpenGraphProps(contract) : undefined
return ( return (
<Page wide={hasSidePanel}> <Page wide={hasSidePanel}>
@ -191,8 +190,10 @@ function BetsSection(props: {
} }
const getOpenGraphProps = (contract: Contract) => { const getOpenGraphProps = (contract: Contract) => {
const { resolution, question, creatorName, creatorUsername } = contract const { resolution, question, creatorName, creatorUsername, outcomeType } =
const probPercent = getBinaryProbPercent(contract) contract
const probPercent =
outcomeType === 'BINARY' ? getBinaryProbPercent(contract) : undefined
const description = resolution const description = resolution
? `Resolved ${resolution}. ${contract.description}` ? `Resolved ${resolution}. ${contract.description}`