* Add TipTap editor and renderer components * Change market description editor to rich text * Type description as JSON, fix string-based logic - Delete make-predictions.tsx - Delete feed logic that showed descriptions * wip Fix API validation * fix type error * fix extension import (backend) In firebase, typescript compiles imports into common js imports like `const StarterKit = require("@tiptap/starter-kit")` Even though StarterKit is exported from the cjs file, it gets imported as undefined. But it magically works if we import * If you're reading this in the future, consider replacing StarterKit with the entire list of extensions. * Stop load on fail create market, improve warning * Refactor editor as hook / fix infinite submit bug Move state of editor back up to parent We have to do this later anyways to allow parent to edit * Add images - display, paste + uploading * add uploading state of image * Fix placeholder, misc styling min height, quote * Fix appending to description * code review fixes: rename, refactor, chop carets * Add hint & upload button on new lines - bump to Tailwind 3.1 for arbitrary variants * clean up, run prettier * rename FileButton to FileUploadButton * add image extension as functions dependency
231 lines
4.3 KiB
TypeScript
231 lines
4.3 KiB
TypeScript
import { Bet } from 'common/bet'
|
|
import { Answer } from 'common/answer'
|
|
import { getOutcomeProbability, getProbability } from 'common/calculate'
|
|
import { Comment } from 'common/comment'
|
|
import { Contract } from 'common/contract'
|
|
import { User } from 'common/user'
|
|
import { removeUndefinedProps } from 'common/util/object'
|
|
import { ENV_CONFIG } from 'common/envs/constants'
|
|
import { JSONContent } from '@tiptap/core'
|
|
|
|
export type LiteMarket = {
|
|
// Unique identifer for this market
|
|
id: string
|
|
|
|
// Attributes about the creator
|
|
creatorUsername: string
|
|
creatorName: string
|
|
createdTime: number
|
|
creatorAvatarUrl?: string
|
|
|
|
// Market attributes. All times are in milliseconds since epoch
|
|
closeTime?: number
|
|
question: string
|
|
description: string | JSONContent
|
|
tags: string[]
|
|
url: string
|
|
outcomeType: string
|
|
mechanism: string
|
|
|
|
pool: { [outcome: string]: number }
|
|
probability?: number
|
|
p?: number
|
|
totalLiquidity?: number
|
|
|
|
volume: number
|
|
volume7Days: number
|
|
volume24Hours: number
|
|
|
|
isResolved: boolean
|
|
resolution?: string
|
|
resolutionTime?: number
|
|
resolutionProbability?: number
|
|
}
|
|
|
|
export type ApiAnswer = Answer & {
|
|
probability?: number
|
|
}
|
|
|
|
export type FullMarket = LiteMarket & {
|
|
bets: Bet[]
|
|
comments: Comment[]
|
|
answers?: ApiAnswer[]
|
|
}
|
|
|
|
export type ApiError = {
|
|
error: string
|
|
}
|
|
|
|
type ValidationErrorDetail = {
|
|
field: string | null
|
|
error: string
|
|
}
|
|
export class ValidationError {
|
|
details: ValidationErrorDetail[]
|
|
|
|
constructor(details: ValidationErrorDetail[]) {
|
|
this.details = details
|
|
}
|
|
}
|
|
|
|
export function toLiteMarket(contract: Contract): LiteMarket {
|
|
const {
|
|
id,
|
|
creatorUsername,
|
|
creatorName,
|
|
createdTime,
|
|
creatorAvatarUrl,
|
|
closeTime,
|
|
question,
|
|
description,
|
|
tags,
|
|
slug,
|
|
pool,
|
|
outcomeType,
|
|
mechanism,
|
|
volume,
|
|
volume7Days,
|
|
volume24Hours,
|
|
isResolved,
|
|
resolution,
|
|
resolutionTime,
|
|
resolutionProbability,
|
|
} = contract
|
|
|
|
const { p, totalLiquidity } = contract as any
|
|
|
|
const probability =
|
|
contract.outcomeType === 'BINARY' ? getProbability(contract) : undefined
|
|
|
|
return removeUndefinedProps({
|
|
id,
|
|
creatorUsername,
|
|
creatorName,
|
|
createdTime,
|
|
creatorAvatarUrl,
|
|
closeTime:
|
|
resolutionTime && closeTime
|
|
? Math.min(resolutionTime, closeTime)
|
|
: closeTime,
|
|
question,
|
|
description,
|
|
tags,
|
|
url: `https://manifold.markets/${creatorUsername}/${slug}`,
|
|
pool,
|
|
probability,
|
|
p,
|
|
totalLiquidity,
|
|
outcomeType,
|
|
mechanism,
|
|
volume,
|
|
volume7Days,
|
|
volume24Hours,
|
|
isResolved,
|
|
resolution,
|
|
resolutionTime,
|
|
resolutionProbability,
|
|
})
|
|
}
|
|
|
|
export function toFullMarket(
|
|
contract: Contract,
|
|
comments: Comment[],
|
|
bets: Bet[]
|
|
): FullMarket {
|
|
const liteMarket = toLiteMarket(contract)
|
|
const answers =
|
|
contract.outcomeType === 'FREE_RESPONSE'
|
|
? contract.answers.map((answer) =>
|
|
augmentAnswerWithProbability(contract, answer)
|
|
)
|
|
: undefined
|
|
|
|
return {
|
|
...liteMarket,
|
|
answers,
|
|
comments,
|
|
bets,
|
|
}
|
|
}
|
|
|
|
function augmentAnswerWithProbability(
|
|
contract: Contract,
|
|
answer: Answer
|
|
): ApiAnswer {
|
|
const probability = getOutcomeProbability(contract, answer.id)
|
|
return {
|
|
...answer,
|
|
probability,
|
|
}
|
|
}
|
|
|
|
export type LiteUser = {
|
|
id: string
|
|
createdTime: number
|
|
|
|
name: string
|
|
username: string
|
|
url: string
|
|
avatarUrl?: string
|
|
|
|
bio?: string
|
|
bannerUrl?: string
|
|
website?: string
|
|
twitterHandle?: string
|
|
discordHandle?: string
|
|
|
|
balance: number
|
|
totalDeposits: number
|
|
|
|
profitCached: {
|
|
daily: number
|
|
weekly: number
|
|
monthly: number
|
|
allTime: number
|
|
}
|
|
|
|
creatorVolumeCached: {
|
|
daily: number
|
|
weekly: number
|
|
monthly: number
|
|
allTime: number
|
|
}
|
|
}
|
|
|
|
export function toLiteUser(user: User): LiteUser {
|
|
const {
|
|
id,
|
|
createdTime,
|
|
name,
|
|
username,
|
|
avatarUrl,
|
|
bio,
|
|
bannerUrl,
|
|
website,
|
|
twitterHandle,
|
|
discordHandle,
|
|
balance,
|
|
totalDeposits,
|
|
profitCached,
|
|
creatorVolumeCached,
|
|
} = user
|
|
|
|
return removeUndefinedProps({
|
|
id,
|
|
createdTime,
|
|
name,
|
|
username,
|
|
url: `https://${ENV_CONFIG.domain}/${username}`,
|
|
avatarUrl,
|
|
bio,
|
|
bannerUrl,
|
|
website,
|
|
twitterHandle,
|
|
discordHandle,
|
|
balance,
|
|
totalDeposits,
|
|
profitCached,
|
|
creatorVolumeCached,
|
|
})
|
|
}
|