Merge branch 'main' into theoremone
This commit is contained in:
		
						commit
						a48755b283
					
				
							
								
								
									
										18
									
								
								common/util/promise.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								common/util/promise.ts
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| export const batchedWaitAll = async <T>( | ||||
|   createPromises: (() => Promise<T>)[], | ||||
|   batchSize = 10 | ||||
| ) => { | ||||
|   const numBatches = Math.ceil(createPromises.length / batchSize) | ||||
|   const result: T[] = [] | ||||
|   for (let batchIndex = 0; batchIndex < numBatches; batchIndex++) { | ||||
|     const from = batchIndex * batchSize | ||||
|     const to = from + batchSize | ||||
| 
 | ||||
|     const promises = createPromises.slice(from, to).map((f) => f()) | ||||
| 
 | ||||
|     const batch = await Promise.all(promises) | ||||
|     result.push(...batch) | ||||
|   } | ||||
| 
 | ||||
|   return result | ||||
| } | ||||
|  | @ -5,6 +5,7 @@ import * as _ from 'lodash' | |||
| import { getValues } from './utils' | ||||
| import { Contract } from '../../common/contract' | ||||
| import { Bet } from '../../common/bet' | ||||
| import { batchedWaitAll } from '../../common/util/promise' | ||||
| 
 | ||||
| const firestore = admin.firestore() | ||||
| 
 | ||||
|  | @ -17,8 +18,8 @@ export const updateContractMetrics = functions.pubsub | |||
|       firestore.collection('contracts') | ||||
|     ) | ||||
| 
 | ||||
|     await Promise.all( | ||||
|       contracts.map(async (contract) => { | ||||
|     await batchedWaitAll( | ||||
|       contracts.map((contract) => async () => { | ||||
|         const volume24Hours = await computeVolumeFrom(contract, oneDay) | ||||
|         const volume7Days = await computeVolumeFrom(contract, oneDay * 7) | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ import { Contract } from '../../common/contract' | |||
| import { Bet } from '../../common/bet' | ||||
| import { User } from '../../common/user' | ||||
| import { calculatePayout } from '../../common/calculate' | ||||
| import { batchedWaitAll } from '../../common/util/promise' | ||||
| 
 | ||||
| const firestore = admin.firestore() | ||||
| 
 | ||||
|  | @ -22,8 +23,8 @@ export const updateUserMetrics = functions.pubsub | |||
|       contracts.map((contract) => [contract.id, contract]) | ||||
|     ) | ||||
| 
 | ||||
|     await Promise.all( | ||||
|       users.map(async (user) => { | ||||
|     await batchedWaitAll( | ||||
|       users.map((user) => async () => { | ||||
|         const [investmentValue, creatorVolume] = await Promise.all([ | ||||
|           computeInvestmentValue(user, contractsDict), | ||||
|           computeTotalPool(user, contractsDict), | ||||
|  |  | |||
|  | @ -334,7 +334,9 @@ const getTweetText = (contract: Contract, isCreator: boolean) => { | |||
|         contract | ||||
|       )} chance, place your bets here:` | ||||
|     : `Submit your own answer:` | ||||
|   const url = `https://manifold.markets${contractPath(contract)}` | ||||
| 
 | ||||
|   const timeParam = `${Date.now()}`.substring(7) | ||||
|   const url = `https://manifold.markets${contractPath(contract)}?t=${timeParam}` | ||||
| 
 | ||||
|   return `${tweetQuestion}\n\n${tweetDescription}\n\n${url}` | ||||
| } | ||||
|  |  | |||
|  | @ -93,10 +93,12 @@ function Timestamp(props: { time: number }) { | |||
| 
 | ||||
| function FeedBet(props: { activityItem: any; feedType: FeedType }) { | ||||
|   const { activityItem, feedType } = props | ||||
|   const { id, contractId, amount, outcome, createdTime } = activityItem | ||||
|   const { id, contractId, amount, outcome, createdTime, contract } = | ||||
|     activityItem | ||||
|   const user = useUser() | ||||
|   const isSelf = user?.id == activityItem.userId | ||||
|   // The creator can comment if the bet was posted in the last hour
 | ||||
|   const isCreator = contract.creatorId == activityItem.userId | ||||
|   // You can comment if your bet was posted in the last hour
 | ||||
|   const canComment = isSelf && Date.now() - createdTime < 60 * 60 * 1000 | ||||
| 
 | ||||
|   const [comment, setComment] = useState('') | ||||
|  | @ -113,6 +115,8 @@ function FeedBet(props: { activityItem: any; feedType: FeedType }) { | |||
|       <div> | ||||
|         {isSelf ? ( | ||||
|           <Avatar avatarUrl={user?.avatarUrl} /> | ||||
|         ) : isCreator ? ( | ||||
|           <Avatar avatarUrl={contract.creatorAvatarUrl} /> | ||||
|         ) : ( | ||||
|           <div className="relative px-1"> | ||||
|             <div className="flex h-8 w-8 items-center justify-center rounded-full bg-gray-200"> | ||||
|  | @ -123,7 +127,10 @@ function FeedBet(props: { activityItem: any; feedType: FeedType }) { | |||
|       </div> | ||||
|       <div className="min-w-0 flex-1 py-1.5"> | ||||
|         <div className="text-sm text-gray-500"> | ||||
|           <span>{isSelf ? 'You' : 'A trader'}</span> {bought} {money} | ||||
|           <span> | ||||
|             {isSelf ? 'You' : isCreator ? contract.creatorName : 'A trader'} | ||||
|           </span>{' '} | ||||
|           {bought} {money} | ||||
|           <MaybeOutcomeLabel outcome={outcome} feedType={feedType} /> | ||||
|           <Timestamp time={createdTime} /> | ||||
|           {canComment && ( | ||||
|  | @ -494,7 +501,7 @@ function FeedClose(props: { contract: Contract }) { | |||
|   ) | ||||
| } | ||||
| 
 | ||||
| function toFeedBet(bet: Bet) { | ||||
| function toFeedBet(bet: Bet, contract: Contract) { | ||||
|   return { | ||||
|     id: bet.id, | ||||
|     contractId: bet.contractId, | ||||
|  | @ -504,6 +511,7 @@ function toFeedBet(bet: Bet) { | |||
|     outcome: bet.outcome, | ||||
|     createdTime: bet.createdTime, | ||||
|     date: fromNow(bet.createdTime), | ||||
|     contract, | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  | @ -533,12 +541,13 @@ const DAY_IN_MS = 24 * 60 * 60 * 1000 | |||
| // Group together bets that are:
 | ||||
| // - Within `windowMs` of the first in the group
 | ||||
| // - Do not have a comment
 | ||||
| // - Were not created by this user
 | ||||
| // - Were not created by this user or the contract creator
 | ||||
| // Return a list of ActivityItems
 | ||||
| function groupBets( | ||||
|   bets: Bet[], | ||||
|   comments: Comment[], | ||||
|   windowMs: number, | ||||
|   contract: Contract, | ||||
|   userId?: string | ||||
| ) { | ||||
|   const commentsMap = mapCommentsByBetId(comments) | ||||
|  | @ -548,25 +557,25 @@ function groupBets( | |||
|   // Turn the current group into an ActivityItem
 | ||||
|   function pushGroup() { | ||||
|     if (group.length == 1) { | ||||
|       items.push(toActivityItem(group[0])) | ||||
|       items.push(toActivityItem(group[0], false)) | ||||
|     } else if (group.length > 1) { | ||||
|       items.push({ type: 'betgroup', bets: [...group], id: group[0].id }) | ||||
|     } | ||||
|     group = [] | ||||
|   } | ||||
| 
 | ||||
|   function toActivityItem(bet: Bet) { | ||||
|   function toActivityItem(bet: Bet, isPublic: boolean) { | ||||
|     const comment = commentsMap[bet.id] | ||||
|     return comment ? toFeedComment(bet, comment) : toFeedBet(bet) | ||||
|     return comment ? toFeedComment(bet, comment) : toFeedBet(bet, contract) | ||||
|   } | ||||
| 
 | ||||
|   for (const bet of bets) { | ||||
|     const isCreator = userId === bet.userId | ||||
|     const isCreator = userId === bet.userId || contract.creatorId === bet.userId | ||||
| 
 | ||||
|     if (commentsMap[bet.id] || isCreator) { | ||||
|       pushGroup() | ||||
|       // Create a single item for this
 | ||||
|       items.push(toActivityItem(bet)) | ||||
|       items.push(toActivityItem(bet, true)) | ||||
|     } else { | ||||
|       if ( | ||||
|         group.length > 0 && | ||||
|  | @ -801,7 +810,7 @@ export function ContractFeed(props: { | |||
| 
 | ||||
|   const allItems: ActivityItem[] = [ | ||||
|     { type: 'start', id: '0' }, | ||||
|     ...groupBets(bets, comments, groupWindow, user?.id), | ||||
|     ...groupBets(bets, comments, groupWindow, contract, user?.id), | ||||
|   ] | ||||
|   if (contract.closeTime && contract.closeTime <= Date.now()) { | ||||
|     allItems.push({ type: 'close', id: `${contract.closeTime}` }) | ||||
|  | @ -851,7 +860,7 @@ export function ContractActivityFeed(props: { | |||
| 
 | ||||
|   const allItems: ActivityItem[] = [ | ||||
|     { type: 'start', id: '0' }, | ||||
|     ...groupBets(bets, comments, DAY_IN_MS, user?.id), | ||||
|     ...groupBets(bets, comments, DAY_IN_MS, contract, user?.id), | ||||
|   ] | ||||
|   if (contract.closeTime && contract.closeTime <= Date.now()) { | ||||
|     allItems.push({ type: 'close', id: `${contract.closeTime}` }) | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ import { useState, useEffect } from 'react' | |||
| import { IS_PRIVATE_MANIFOLD } from '../../common/envs/constants' | ||||
| 
 | ||||
| type PropzProps = { | ||||
|   // Params from the router query
 | ||||
|   params: any | ||||
| } | ||||
| 
 | ||||
|  | @ -12,9 +13,7 @@ type PropzProps = { | |||
| // TODO: Could cache the result using stale-while-revalidate: https://swr.vercel.app/
 | ||||
| export function usePropz( | ||||
|   initialProps: Object, | ||||
|   getStaticPropz: (props: PropzProps) => Promise<any>, | ||||
|   // Dynamic routes will need the query params from the router
 | ||||
|   needParams?: boolean | ||||
|   getStaticPropz: (props: PropzProps) => Promise<any> | ||||
| ) { | ||||
|   // If props were successfully server-side generated, just use those
 | ||||
|   if (!_.isEmpty(initialProps)) { | ||||
|  | @ -27,10 +26,9 @@ export function usePropz( | |||
| 
 | ||||
|   const [propz, setPropz] = useState<any>(undefined) | ||||
|   useEffect(() => { | ||||
|     if (needParams && _.isEmpty(params)) { | ||||
|       return | ||||
|     if (router.isReady) { | ||||
|       getStaticPropz({ params }).then((result) => setPropz(result.props)) | ||||
|     } | ||||
|     getStaticPropz({ params }).then((result) => setPropz(result.props)) | ||||
|   }, [params]) | ||||
|   return propz | ||||
| } | ||||
|  |  | |||
|  | @ -79,7 +79,7 @@ export default function ContractPage(props: { | |||
|   slug: string | ||||
|   folds: Fold[] | ||||
| }) { | ||||
|   props = usePropz(props, getStaticPropz, true) ?? { | ||||
|   props = usePropz(props, getStaticPropz) ?? { | ||||
|     contract: null, | ||||
|     username: '', | ||||
|     comments: [], | ||||
|  |  | |||
|  | @ -118,7 +118,7 @@ export default function FoldPage(props: { | |||
|   creatorScores: { [userId: string]: number } | ||||
|   topCreators: User[] | ||||
| }) { | ||||
|   props = usePropz(props, getStaticPropz, true) ?? { | ||||
|   props = usePropz(props, getStaticPropz) ?? { | ||||
|     fold: null, | ||||
|     curator: null, | ||||
|     contracts: [], | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user