* Added radio buttons to market creation (non functional) * Ignoring vs code files Should this be done in the repo or should everyone using VS Code do that himself globally on his machine(s)? * Removed 'automatic' resolution * added union type for resolution * revert: resolution could be anything here (non binary markets) * Expanded ChoicesToggleGroup for string choices * Added combined resolution and required buttons to market creation * restricted automatic resolution to binary markets * added automatic resolution to contract * added automatic resolution to contract overview * string or number array to mixed array * created const for resolutions * Added comments for leading semicolons * configuration of auto resolution on market creation * v1.22.19 * v1.0.0 * v0.0.0 * v1.0.0 * v1.22.19 * Mock display automatic resolution * Revert changes to market creation * Revert "v1.22.19" This reverts commit22f59adc9c. * Removed resolutiontype from contract creation * Added auto resolution time to contract * Auto resolution date editable * refactoring * Editable interface for auto resolution * New edit interface for auto resolution * Setting of auto resolve date when changing close date * prohibited changing other peoples markets * removed unnecessary export * refactoring (cherry picked from commit4de86d5b08) * Added comments for leading semicolons (cherry picked from commit60739c7853) * Ignoring vs code files Should this be done in the repo or should everyone using VS Code do that himself globally on his machine(s)? (cherry picked from commit944de9398a) * removed unused imports and variables * added type for binary resolution * Prettier * const for binary resolutions * using the type "resolution" * Prettier * Re-added comment * Update functions/src/create-contract.ts * Revert "Ignoring vs code files" This reverts commit09aea5c207. * launch config for debugging with vs code WIP * "Launch Chrome" does not work since login via google is not possible in debugger-chrome * Breakpoints are unbound when attached to chrome * Revert "Added comments for leading semicolons" * prettier * linebreak crlf * vscode settings * correct linebreaks * search exclusion * automatic prettifier * vscode settings * correct linebreaks * search exclusion * automatic prettifier * Working debugger config * fix merge * Removed comments, default resolution MKT * removed vscode from gitignore * refactoring description update * Added auto resolution to LiteMarket * fix date, setDate mutates object * fixed firestore.rules * script to add auto resolution to all markets * regularely auto resolve markets * fix description error * moved calculate ts for access in firebase * Revert "moved calculate ts for access in firebase" This reverts commit8380bf4f72. * fix reference to calculate for firebase * fixed references to time * renamed function * added description * added auto resolution to description * direct bool check instead of != null * direct bool check instead of != undefined * remove explicit type * Fix free response markets * removed contract from functionname * interval set to 1h * query instead of filter * folds ~> contracts * query instead of filter * promise.all instead of foreach * removed contractDoc from function header * removed autoResolution from function header * batchedWaitAll instead of promise.all * removed unused parameter * replaced auto resolution with constant * suggestions from PR * fix comment * removed unused imports * added scripts to add close dates on prod * optimization * removed test script * security: only auto resolve markets which are closed * consistency checks * re-added type check for binary markets * moved check of probability into switch case block * removed unused import * auto resolution every minute * auto resolution time optional * pr fixes
		
			
				
	
	
		
			275 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			275 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import {
 | |
|   DotsHorizontalIcon,
 | |
|   PencilIcon,
 | |
|   CheckIcon,
 | |
| } from '@heroicons/react/outline'
 | |
| import clsx from 'clsx'
 | |
| import dayjs from 'dayjs'
 | |
| import { uniqBy } from 'lodash'
 | |
| import { useState } from 'react'
 | |
| import { Bet } from 'common/bet'
 | |
| 
 | |
| import { Contract } from 'common/contract'
 | |
| import { formatMoney } from 'common/util/format'
 | |
| import {
 | |
|   contractPath,
 | |
|   contractPool,
 | |
|   getBinaryProbPercent,
 | |
|   updateContract,
 | |
| } from 'web/lib/firebase/contracts'
 | |
| import { LiquidityPanel } from '../liquidity-panel'
 | |
| import { CopyLinkButton } from '../copy-link-button'
 | |
| 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 { Title } from '../title'
 | |
| import { TweetButton } from '../tweet-button'
 | |
| import { InfoTooltip } from '../info-tooltip'
 | |
| 
 | |
| const formatTime = (dt: number) => dayjs(dt).format('MMM DD, YYYY hh:mm a z')
 | |
| 
 | |
| export function ContractInfoDialog(props: {
 | |
|   contract: Contract
 | |
|   bets: Bet[]
 | |
|   isCreator: boolean
 | |
| }) {
 | |
|   const { contract, bets, isCreator } = props
 | |
| 
 | |
|   const [open, setOpen] = useState(false)
 | |
| 
 | |
|   const {
 | |
|     createdTime,
 | |
|     closeTime,
 | |
|     resolutionTime,
 | |
|     mechanism,
 | |
|     outcomeType,
 | |
|     autoResolutionTime,
 | |
|   } = contract
 | |
| 
 | |
|   const tradersCount = uniqBy(
 | |
|     bets.filter((bet) => !bet.isAnte),
 | |
|     'userId'
 | |
|   ).length
 | |
| 
 | |
|   const typeDisplay =
 | |
|     outcomeType === 'BINARY'
 | |
|       ? 'YES / NO'
 | |
|       : outcomeType === 'FREE_RESPONSE'
 | |
|       ? 'Free response'
 | |
|       : 'Numeric'
 | |
| 
 | |
|   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>Share</div>
 | |
| 
 | |
|           <Row className="justify-start gap-4">
 | |
|             <CopyLinkButton
 | |
|               contract={contract}
 | |
|               toastClassName={'sm:-left-10 -left-4 min-w-[250%]'}
 | |
|             />
 | |
|             <TweetButton
 | |
|               className="self-start"
 | |
|               tweetText={getTweetText(contract, false)}
 | |
|             />
 | |
|             <ShareEmbedButton contract={contract} toastClassName={'-left-20'} />
 | |
|           </Row>
 | |
|           <div />
 | |
| 
 | |
|           <div>Stats</div>
 | |
| 
 | |
|           <table className="table-compact table-zebra table w-full text-gray-500">
 | |
|             <tbody>
 | |
|               <tr>
 | |
|                 <td>Type</td>
 | |
|                 <td>{typeDisplay}</td>
 | |
|               </tr>
 | |
| 
 | |
|               <tr>
 | |
|                 <td>Payout</td>
 | |
|                 <td>
 | |
|                   {mechanism === 'cpmm-1' ? (
 | |
|                     <>
 | |
|                       Fixed{' '}
 | |
|                       <InfoTooltip text="Each YES share is worth M$1 if YES wins." />
 | |
|                     </>
 | |
|                   ) : (
 | |
|                     <div>
 | |
|                       Parimutuel{' '}
 | |
|                       <InfoTooltip text="Each share is a fraction of the pool. " />
 | |
|                     </div>
 | |
|                   )}
 | |
|                 </td>
 | |
|               </tr>
 | |
| 
 | |
|               <tr>
 | |
|                 <td>Market created</td>
 | |
|                 <td>{formatTime(createdTime)}</td>
 | |
|               </tr>
 | |
| 
 | |
|               {closeTime && (
 | |
|                 <tr>
 | |
|                   <td>Market close{closeTime > Date.now() ? 's' : 'd'}</td>
 | |
|                   <td>{formatTime(closeTime)}</td>
 | |
|                 </tr>
 | |
|               )}
 | |
| 
 | |
|               {autoResolutionTime && !resolutionTime && (
 | |
|                 <EditableResolutionTime
 | |
|                   time={autoResolutionTime}
 | |
|                   contract={contract}
 | |
|                   isCreator={isCreator}
 | |
|                 />
 | |
|               )}
 | |
| 
 | |
|               {resolutionTime && (
 | |
|                 <tr>
 | |
|                   <td>Market resolved</td>
 | |
|                   <td>{formatTime(resolutionTime)}</td>
 | |
|                 </tr>
 | |
|               )}
 | |
| 
 | |
|               <tr>
 | |
|                 <td>Volume</td>
 | |
|                 <td>{formatMoney(contract.volume)}</td>
 | |
|               </tr>
 | |
| 
 | |
|               <tr>
 | |
|                 <td>Creator earnings</td>
 | |
|                 <td>{formatMoney(contract.collectedFees.creatorFee)}</td>
 | |
|               </tr>
 | |
| 
 | |
|               <tr>
 | |
|                 <td>Traders</td>
 | |
|                 <td>{tradersCount}</td>
 | |
|               </tr>
 | |
| 
 | |
|               <tr>
 | |
|                 <td>
 | |
|                   {mechanism === 'cpmm-1' ? 'Liquidity pool' : 'Betting pool'}
 | |
|                 </td>
 | |
|                 <td>{contractPool(contract)}</td>
 | |
|               </tr>
 | |
|             </tbody>
 | |
|           </table>
 | |
| 
 | |
|           <div>Tags</div>
 | |
|           <TagsInput contract={contract} />
 | |
|           <div />
 | |
| 
 | |
|           {contract.mechanism === 'cpmm-1' && !contract.resolution && (
 | |
|             <LiquidityPanel contract={contract} />
 | |
|           )}
 | |
|         </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}`
 | |
| }
 | |
| 
 | |
| export function EditableResolutionTime(props: {
 | |
|   time: number
 | |
|   contract: Contract
 | |
|   isCreator: boolean
 | |
| }) {
 | |
|   const { time, contract, isCreator } = props
 | |
| 
 | |
|   const [isEditing, setIsEditing] = useState(false)
 | |
|   const [timeString, setTimeString] = useState(time && formatTime(time))
 | |
| 
 | |
|   const onSave = () => {
 | |
|     const newTime = dayjs(timeString).valueOf()
 | |
|     if (newTime === time) setIsEditing(false)
 | |
|     else if (
 | |
|       contract.closeTime &&
 | |
|       newTime > (contract.closeTime ?? Date.now())
 | |
|     ) {
 | |
|       const formattedTime = dayjs(newTime).format('YYYY-MM-DD h:mm a')
 | |
|       const newDescription = `${contract.description}\n\nAuto resolution date updated to ${formattedTime}`
 | |
| 
 | |
|       updateContract(contract.id, {
 | |
|         autoResolutionTime: newTime,
 | |
|         description: newDescription,
 | |
|       })
 | |
| 
 | |
|       setIsEditing(false)
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return (
 | |
|     <tr>
 | |
|       <td>
 | |
|         Market autoresolves
 | |
|         {isCreator &&
 | |
|           (isEditing ? (
 | |
|             <button className="btn btn-xs btn-ghost" onClick={onSave}>
 | |
|               <CheckIcon className="inline h-4 w-4" />
 | |
|             </button>
 | |
|           ) : (
 | |
|             <button
 | |
|               className="btn btn-xs btn-ghost"
 | |
|               onClick={() => setIsEditing(true)}
 | |
|             >
 | |
|               <PencilIcon className="inline h-4 w-4" />
 | |
|             </button>
 | |
|           ))}
 | |
|       </td>
 | |
|       <td>
 | |
|         {isEditing ? (
 | |
|           <div className="form-control mr-1 items-start">
 | |
|             <input
 | |
|               type="datetime-local"
 | |
|               className="input input-xs"
 | |
|               onClick={(e) => e.stopPropagation()}
 | |
|               onChange={(e) => setTimeString(e.target.value || '')}
 | |
|               min={contract.closeTime}
 | |
|               value={timeString}
 | |
|             />
 | |
|           </div>
 | |
|         ) : (
 | |
|           <div className="form-control mr-1 items-start">
 | |
|             {formatTime(time)}
 | |
|           </div>
 | |
|         )}
 | |
|       </td>
 | |
|     </tr>
 | |
|   )
 | |
| }
 |