Change lodash stuff so that it can be tree-shaken out of build (#233)
* Set common package.json sideEffects: false * Configure SWC to modularize lodash imports * Import specific lodash functions instead of _ * Add an eslint rule to avoid full lodash import
This commit is contained in:
		
							parent
							
								
									0803a15902
								
							
						
					
					
						commit
						47f10301c8
					
				|  | @ -1,4 +1,5 @@ | |||
| module.exports = { | ||||
|   plugins: ['lodash'], | ||||
|   extends: ['eslint:recommended'], | ||||
|   env: { | ||||
|     browser: true, | ||||
|  | @ -14,5 +15,6 @@ module.exports = { | |||
|   rules: { | ||||
|     'no-unused-vars': 'off', | ||||
|     'no-constant-condition': ['error', { checkLoops: false }], | ||||
|     'lodash/import-scope': [2, 'member'], | ||||
|   }, | ||||
| } | ||||
|  |  | |||
|  | @ -1,3 +1,4 @@ | |||
| import { range } from 'lodash' | ||||
| import { Bet, NumericBet } from './bet' | ||||
| import { getDpmProbability, getValueFromBucket } from './calculate-dpm' | ||||
| import { | ||||
|  | @ -11,7 +12,6 @@ import { | |||
| import { User } from './user' | ||||
| import { LiquidityProvision } from './liquidity-provision' | ||||
| import { noFees } from './fees' | ||||
| import * as _ from 'lodash' | ||||
| 
 | ||||
| export const FIXED_ANTE = 100 | ||||
| 
 | ||||
|  | @ -127,11 +127,11 @@ export function getNumericAnte( | |||
|   const betShares = Math.sqrt(ante ** 2 / bucketCount) | ||||
| 
 | ||||
|   const allOutcomeShares = Object.fromEntries( | ||||
|     _.range(0, bucketCount).map((_, i) => [i, betShares]) | ||||
|     range(0, bucketCount).map((_, i) => [i, betShares]) | ||||
|   ) | ||||
| 
 | ||||
|   const allBetAmounts = Object.fromEntries( | ||||
|     _.range(0, bucketCount).map((_, i) => [i, betAnte]) | ||||
|     range(0, bucketCount).map((_, i) => [i, betAnte]) | ||||
|   ) | ||||
| 
 | ||||
|   const anteBet: NumericBet = { | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import * as _ from 'lodash' | ||||
| import { sum, groupBy, mapValues, sumBy } from 'lodash' | ||||
| 
 | ||||
| import { Binary, CPMM, FullContract } from './contract' | ||||
| import { CREATOR_FEE, Fees, LIQUIDITY_FEE, noFees, PLATFORM_FEE } from './fees' | ||||
|  | @ -278,16 +278,16 @@ export function getCpmmLiquidityPoolWeights( | |||
|     return liquidity | ||||
|   }) | ||||
| 
 | ||||
|   const shareSum = _.sum(liquidityShares) | ||||
|   const shareSum = sum(liquidityShares) | ||||
| 
 | ||||
|   const weights = liquidityShares.map((s, i) => ({ | ||||
|     weight: s / shareSum, | ||||
|     providerId: liquidities[i].userId, | ||||
|   })) | ||||
| 
 | ||||
|   const userWeights = _.groupBy(weights, (w) => w.providerId) | ||||
|   const totalUserWeights = _.mapValues(userWeights, (userWeight) => | ||||
|     _.sumBy(userWeight, (w) => w.weight) | ||||
|   const userWeights = groupBy(weights, (w) => w.providerId) | ||||
|   const totalUserWeights = mapValues(userWeights, (userWeight) => | ||||
|     sumBy(userWeight, (w) => w.weight) | ||||
|   ) | ||||
|   return totalUserWeights | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| import * as _ from 'lodash' | ||||
| 
 | ||||
| import { cloneDeep, range, sum, sumBy, sortBy, mapValues } from 'lodash' | ||||
| import { Bet, NumericBet } from './bet' | ||||
| import { | ||||
|   Binary, | ||||
|  | @ -24,7 +23,7 @@ export function getDpmOutcomeProbability( | |||
|   }, | ||||
|   outcome: string | ||||
| ) { | ||||
|   const squareSum = _.sumBy(Object.values(totalShares), (shares) => shares ** 2) | ||||
|   const squareSum = sumBy(Object.values(totalShares), (shares) => shares ** 2) | ||||
|   const shares = totalShares[outcome] ?? 0 | ||||
|   return shares ** 2 / squareSum | ||||
| } | ||||
|  | @ -32,8 +31,8 @@ export function getDpmOutcomeProbability( | |||
| export function getDpmOutcomeProbabilities(totalShares: { | ||||
|   [outcome: string]: number | ||||
| }) { | ||||
|   const squareSum = _.sumBy(Object.values(totalShares), (shares) => shares ** 2) | ||||
|   return _.mapValues(totalShares, (shares) => shares ** 2 / squareSum) | ||||
|   const squareSum = sumBy(Object.values(totalShares), (shares) => shares ** 2) | ||||
|   return mapValues(totalShares, (shares) => shares ** 2 / squareSum) | ||||
| } | ||||
| 
 | ||||
| export function getNumericBets( | ||||
|  | @ -44,20 +43,20 @@ export function getNumericBets( | |||
| ) { | ||||
|   const { bucketCount } = contract | ||||
|   const bucketNumber = parseInt(bucket) | ||||
|   const buckets = _.range(0, bucketCount) | ||||
|   const buckets = range(0, bucketCount) | ||||
| 
 | ||||
|   const mean = bucketNumber / bucketCount | ||||
| 
 | ||||
|   const allDensities = buckets.map((i) => | ||||
|     normpdf(i / bucketCount, mean, variance) | ||||
|   ) | ||||
|   const densitySum = _.sum(allDensities) | ||||
|   const densitySum = sum(allDensities) | ||||
| 
 | ||||
|   const rawBetAmounts = allDensities | ||||
|     .map((d) => (d / densitySum) * betAmount) | ||||
|     .map((x) => (x >= 1 / bucketCount ? x : 0)) | ||||
| 
 | ||||
|   const rawSum = _.sum(rawBetAmounts) | ||||
|   const rawSum = sum(rawBetAmounts) | ||||
|   const scaledBetAmounts = rawBetAmounts.map((x) => (x / rawSum) * betAmount) | ||||
| 
 | ||||
|   const bets = scaledBetAmounts | ||||
|  | @ -90,26 +89,24 @@ export const getValueFromBucket = ( | |||
| export const getExpectedValue = (contract: NumericContract) => { | ||||
|   const { bucketCount, min, max, totalShares } = contract | ||||
| 
 | ||||
|   const totalShareSum = _.sumBy( | ||||
|   const totalShareSum = sumBy( | ||||
|     Object.values(totalShares), | ||||
|     (shares) => shares ** 2 | ||||
|   ) | ||||
|   const probs = _.range(0, bucketCount).map( | ||||
|   const probs = range(0, bucketCount).map( | ||||
|     (i) => totalShares[i] ** 2 / totalShareSum | ||||
|   ) | ||||
| 
 | ||||
|   const values = _.range(0, bucketCount).map( | ||||
|   const values = range(0, bucketCount).map( | ||||
|     (i) => | ||||
|       // use mid point within bucket
 | ||||
|       0.5 * (min + (i / bucketCount) * (max - min)) + | ||||
|       0.5 * (min + ((i + 1) / bucketCount) * (max - min)) | ||||
|   ) | ||||
| 
 | ||||
|   const weightedValues = _.range(0, bucketCount).map( | ||||
|     (i) => probs[i] * values[i] | ||||
|   ) | ||||
|   const weightedValues = range(0, bucketCount).map((i) => probs[i] * values[i]) | ||||
| 
 | ||||
|   const expectation = _.sum(weightedValues) | ||||
|   const expectation = sum(weightedValues) | ||||
|   const rounded = Math.round(expectation * 1e2) / 1e2 | ||||
|   return rounded | ||||
| } | ||||
|  | @ -150,7 +147,7 @@ export function calculateDpmShares( | |||
|   bet: number, | ||||
|   betChoice: string | ||||
| ) { | ||||
|   const squareSum = _.sumBy(Object.values(totalShares), (shares) => shares ** 2) | ||||
|   const squareSum = sumBy(Object.values(totalShares), (shares) => shares ** 2) | ||||
|   const shares = totalShares[betChoice] ?? 0 | ||||
| 
 | ||||
|   const c = 2 * bet * Math.sqrt(squareSum) | ||||
|  | @ -166,9 +163,9 @@ export function calculateNumericDpmShares( | |||
| ) { | ||||
|   const shares: number[] = [] | ||||
| 
 | ||||
|   totalShares = _.cloneDeep(totalShares) | ||||
|   totalShares = cloneDeep(totalShares) | ||||
| 
 | ||||
|   const order = _.sortBy( | ||||
|   const order = sortBy( | ||||
|     bets.map(([, amount], i) => [amount, i]), | ||||
|     ([amount]) => amount | ||||
|   ).map(([, i]) => i) | ||||
|  | @ -190,11 +187,11 @@ export function calculateDpmRawShareValue( | |||
|   betChoice: string | ||||
| ) { | ||||
|   const currentValue = Math.sqrt( | ||||
|     _.sumBy(Object.values(totalShares), (shares) => shares ** 2) | ||||
|     sumBy(Object.values(totalShares), (shares) => shares ** 2) | ||||
|   ) | ||||
| 
 | ||||
|   const postSaleValue = Math.sqrt( | ||||
|     _.sumBy(Object.keys(totalShares), (outcome) => | ||||
|     sumBy(Object.keys(totalShares), (outcome) => | ||||
|       outcome === betChoice | ||||
|         ? Math.max(0, totalShares[outcome] - shares) ** 2 | ||||
|         : totalShares[outcome] ** 2 | ||||
|  | @ -214,12 +211,12 @@ export function calculateDpmMoneyRatio( | |||
| 
 | ||||
|   const p = getDpmOutcomeProbability(totalShares, outcome) | ||||
| 
 | ||||
|   const actual = _.sum(Object.values(pool)) - shareValue | ||||
|   const actual = sum(Object.values(pool)) - shareValue | ||||
| 
 | ||||
|   const betAmount = p * amount | ||||
| 
 | ||||
|   const expected = | ||||
|     _.sumBy( | ||||
|     sumBy( | ||||
|       Object.keys(totalBets), | ||||
|       (outcome) => | ||||
|         getDpmOutcomeProbability(totalShares, outcome) * | ||||
|  | @ -271,8 +268,8 @@ export function calculateDpmCancelPayout( | |||
|   bet: Bet | ||||
| ) { | ||||
|   const { totalBets, pool } = contract | ||||
|   const betTotal = _.sum(Object.values(totalBets)) | ||||
|   const poolTotal = _.sum(Object.values(pool)) | ||||
|   const betTotal = sum(Object.values(totalBets)) | ||||
|   const poolTotal = sum(Object.values(pool)) | ||||
| 
 | ||||
|   return (bet.amount / betTotal) * poolTotal | ||||
| } | ||||
|  | @ -295,7 +292,7 @@ export function calculateStandardDpmPayout( | |||
|   const { totalShares, phantomShares, pool } = contract | ||||
|   if (!totalShares[outcome]) return 0 | ||||
| 
 | ||||
|   const poolTotal = _.sum(Object.values(pool)) | ||||
|   const poolTotal = sum(Object.values(pool)) | ||||
| 
 | ||||
|   const total = | ||||
|     totalShares[outcome] - (phantomShares ? phantomShares[outcome] : 0) | ||||
|  | @ -356,19 +353,19 @@ function calculateMktDpmPayout( | |||
|   let probs: { [outcome: string]: number } | ||||
| 
 | ||||
|   if (resolutions) { | ||||
|     const probTotal = _.sum(Object.values(resolutions)) | ||||
|     probs = _.mapValues( | ||||
|     const probTotal = sum(Object.values(resolutions)) | ||||
|     probs = mapValues( | ||||
|       totalShares, | ||||
|       (_, outcome) => (resolutions[outcome] ?? 0) / probTotal | ||||
|     ) | ||||
|   } else { | ||||
|     const squareSum = _.sum( | ||||
|     const squareSum = sum( | ||||
|       Object.values(totalShares).map((shares) => shares ** 2) | ||||
|     ) | ||||
|     probs = _.mapValues(totalShares, (shares) => shares ** 2 / squareSum) | ||||
|     probs = mapValues(totalShares, (shares) => shares ** 2 / squareSum) | ||||
|   } | ||||
| 
 | ||||
|   const weightedShareTotal = _.sumBy(Object.keys(totalShares), (outcome) => { | ||||
|   const weightedShareTotal = sumBy(Object.keys(totalShares), (outcome) => { | ||||
|     return probs[outcome] * totalShares[outcome] | ||||
|   }) | ||||
| 
 | ||||
|  | @ -376,7 +373,7 @@ function calculateMktDpmPayout( | |||
| 
 | ||||
|   const poolFrac = | ||||
|     outcomeType === 'NUMERIC' | ||||
|       ? _.sumBy( | ||||
|       ? sumBy( | ||||
|           Object.keys((bet as NumericBet).allOutcomeShares ?? {}), | ||||
|           (outcome) => { | ||||
|             return ( | ||||
|  | @ -387,7 +384,7 @@ function calculateMktDpmPayout( | |||
|         ) | ||||
|       : (probs[outcome] * shares) / weightedShareTotal | ||||
| 
 | ||||
|   const totalPool = _.sum(Object.values(pool)) | ||||
|   const totalPool = sum(Object.values(pool)) | ||||
|   const winnings = poolFrac * totalPool | ||||
|   return deductDpmFees(amount, winnings) | ||||
| } | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import * as _ from 'lodash' | ||||
| import { maxBy } from 'lodash' | ||||
| import { Bet } from './bet' | ||||
| import { | ||||
|   calculateCpmmSale, | ||||
|  | @ -180,7 +180,7 @@ export function getContractBetNullMetrics() { | |||
| 
 | ||||
| export function getTopAnswer(contract: FreeResponseContract) { | ||||
|   const { answers } = contract | ||||
|   const top = _.maxBy( | ||||
|   const top = maxBy( | ||||
|     answers?.map((answer) => ({ | ||||
|       answer, | ||||
|       prob: getOutcomeProbability(contract, answer.id), | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| import * as _ from 'lodash' | ||||
| import { Answer } from './answer' | ||||
| import { Fees } from './fees' | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import * as _ from 'lodash' | ||||
| import { sumBy } from 'lodash' | ||||
| 
 | ||||
| import { Bet, MAX_LOAN_PER_CONTRACT, NumericBet } from './bet' | ||||
| import { | ||||
|  | @ -210,7 +210,7 @@ export const getNumericBetsInfo = ( | |||
| 
 | ||||
| export const getLoanAmount = (yourBets: Bet[], newBetAmount: number) => { | ||||
|   const openBets = yourBets.filter((bet) => !bet.isSold && !bet.sale) | ||||
|   const prevLoanAmount = _.sumBy(openBets, (bet) => bet.loanAmount ?? 0) | ||||
|   const prevLoanAmount = sumBy(openBets, (bet) => bet.loanAmount ?? 0) | ||||
|   const loanAmount = Math.min( | ||||
|     newBetAmount, | ||||
|     MAX_LOAN_PER_CONTRACT - prevLoanAmount | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| import * as _ from 'lodash' | ||||
| 
 | ||||
| import { range } from 'lodash' | ||||
| import { PHANTOM_ANTE } from './antes' | ||||
| import { | ||||
|   Binary, | ||||
|  | @ -131,7 +130,7 @@ const getNumericProps = ( | |||
|   min: number, | ||||
|   max: number | ||||
| ) => { | ||||
|   const buckets = _.range(0, bucketCount).map((i) => i.toString()) | ||||
|   const buckets = range(0, bucketCount).map((i) => i.toString()) | ||||
| 
 | ||||
|   const betAnte = ante / bucketCount | ||||
|   const pool = Object.fromEntries(buckets.map((answer) => [answer, betAnte])) | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ | |||
|   "version": "1.0.0", | ||||
|   "private": true, | ||||
|   "scripts": {}, | ||||
|   "sideEffects": false, | ||||
|   "dependencies": { | ||||
|     "lodash": "4.17.21" | ||||
|   }, | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import * as _ from 'lodash' | ||||
| import { sum, groupBy, sumBy, mapValues } from 'lodash' | ||||
| 
 | ||||
| import { Bet, NumericBet } from './bet' | ||||
| import { deductDpmFees, getDpmProbability } from './calculate-dpm' | ||||
|  | @ -17,10 +17,10 @@ export const getDpmCancelPayouts = ( | |||
|   bets: Bet[] | ||||
| ) => { | ||||
|   const { pool } = contract | ||||
|   const poolTotal = _.sum(Object.values(pool)) | ||||
|   const poolTotal = sum(Object.values(pool)) | ||||
|   console.log('resolved N/A, pool M$', poolTotal) | ||||
| 
 | ||||
|   const betSum = _.sumBy(bets, (b) => b.amount) | ||||
|   const betSum = sumBy(bets, (b) => b.amount) | ||||
| 
 | ||||
|   const payouts = bets.map((bet) => ({ | ||||
|     userId: bet.userId, | ||||
|  | @ -42,8 +42,8 @@ export const getDpmStandardPayouts = ( | |||
| ) => { | ||||
|   const winningBets = bets.filter((bet) => bet.outcome === outcome) | ||||
| 
 | ||||
|   const poolTotal = _.sum(Object.values(contract.pool)) | ||||
|   const totalShares = _.sumBy(winningBets, (b) => b.shares) | ||||
|   const poolTotal = sum(Object.values(contract.pool)) | ||||
|   const totalShares = sumBy(winningBets, (b) => b.shares) | ||||
| 
 | ||||
|   const payouts = winningBets.map(({ userId, amount, shares }) => { | ||||
|     const winnings = (shares / totalShares) * poolTotal | ||||
|  | @ -54,7 +54,7 @@ export const getDpmStandardPayouts = ( | |||
|     return { userId, profit, payout } | ||||
|   }) | ||||
| 
 | ||||
|   const profits = _.sumBy(payouts, (po) => Math.max(0, po.profit)) | ||||
|   const profits = sumBy(payouts, (po) => Math.max(0, po.profit)) | ||||
|   const creatorFee = DPM_CREATOR_FEE * profits | ||||
|   const platformFee = DPM_PLATFORM_FEE * profits | ||||
| 
 | ||||
|  | @ -93,10 +93,10 @@ export const getNumericDpmPayouts = ( | |||
|   contract: FullContract<DPM, any>, | ||||
|   bets: NumericBet[] | ||||
| ) => { | ||||
|   const totalShares = _.sumBy(bets, (bet) => bet.allOutcomeShares[outcome] ?? 0) | ||||
|   const totalShares = sumBy(bets, (bet) => bet.allOutcomeShares[outcome] ?? 0) | ||||
|   const winningBets = bets.filter((bet) => !!bet.allOutcomeShares[outcome]) | ||||
| 
 | ||||
|   const poolTotal = _.sum(Object.values(contract.pool)) | ||||
|   const poolTotal = sum(Object.values(contract.pool)) | ||||
| 
 | ||||
|   const payouts = winningBets.map( | ||||
|     ({ userId, allBetAmounts, allOutcomeShares }) => { | ||||
|  | @ -112,7 +112,7 @@ export const getNumericDpmPayouts = ( | |||
|     } | ||||
|   ) | ||||
| 
 | ||||
|   const profits = _.sumBy(payouts, (po) => Math.max(0, po.profit)) | ||||
|   const profits = sumBy(payouts, (po) => Math.max(0, po.profit)) | ||||
|   const creatorFee = DPM_CREATOR_FEE * profits | ||||
|   const platformFee = DPM_PLATFORM_FEE * profits | ||||
| 
 | ||||
|  | @ -156,7 +156,7 @@ export const getDpmMktPayouts = ( | |||
|       ? getDpmProbability(contract.totalShares) | ||||
|       : resolutionProbability | ||||
| 
 | ||||
|   const weightedShareTotal = _.sumBy(bets, (b) => | ||||
|   const weightedShareTotal = sumBy(bets, (b) => | ||||
|     b.outcome === 'YES' ? p * b.shares : (1 - p) * b.shares | ||||
|   ) | ||||
| 
 | ||||
|  | @ -170,7 +170,7 @@ export const getDpmMktPayouts = ( | |||
|     return { userId, profit, payout } | ||||
|   }) | ||||
| 
 | ||||
|   const profits = _.sumBy(payouts, (po) => Math.max(0, po.profit)) | ||||
|   const profits = sumBy(payouts, (po) => Math.max(0, po.profit)) | ||||
| 
 | ||||
|   const creatorFee = DPM_CREATOR_FEE * profits | ||||
|   const platformFee = DPM_PLATFORM_FEE * profits | ||||
|  | @ -210,15 +210,15 @@ export const getPayoutsMultiOutcome = ( | |||
|   contract: FullContract<DPM, Multi | FreeResponse>, | ||||
|   bets: Bet[] | ||||
| ) => { | ||||
|   const poolTotal = _.sum(Object.values(contract.pool)) | ||||
|   const poolTotal = sum(Object.values(contract.pool)) | ||||
|   const winningBets = bets.filter((bet) => resolutions[bet.outcome]) | ||||
| 
 | ||||
|   const betsByOutcome = _.groupBy(winningBets, (bet) => bet.outcome) | ||||
|   const sharesByOutcome = _.mapValues(betsByOutcome, (bets) => | ||||
|     _.sumBy(bets, (bet) => bet.shares) | ||||
|   const betsByOutcome = groupBy(winningBets, (bet) => bet.outcome) | ||||
|   const sharesByOutcome = mapValues(betsByOutcome, (bets) => | ||||
|     sumBy(bets, (bet) => bet.shares) | ||||
|   ) | ||||
| 
 | ||||
|   const probTotal = _.sum(Object.values(resolutions)) | ||||
|   const probTotal = sum(Object.values(resolutions)) | ||||
| 
 | ||||
|   const payouts = winningBets.map(({ userId, outcome, amount, shares }) => { | ||||
|     const prob = resolutions[outcome] / probTotal | ||||
|  | @ -229,7 +229,7 @@ export const getPayoutsMultiOutcome = ( | |||
|     return { userId, profit, payout } | ||||
|   }) | ||||
| 
 | ||||
|   const profits = _.sumBy(payouts, (po) => po.profit) | ||||
|   const profits = sumBy(payouts, (po) => po.profit) | ||||
| 
 | ||||
|   const creatorFee = DPM_CREATOR_FEE * profits | ||||
|   const platformFee = DPM_PLATFORM_FEE * profits | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import * as _ from 'lodash' | ||||
| import { sum } from 'lodash' | ||||
| 
 | ||||
| import { Bet } from './bet' | ||||
| import { getProbability } from './calculate' | ||||
|  | @ -50,7 +50,7 @@ export const getStandardFixedPayouts = ( | |||
|     'pool', | ||||
|     contract.pool[outcome], | ||||
|     'payouts', | ||||
|     _.sum(payouts), | ||||
|     sum(payouts), | ||||
|     'creator fee', | ||||
|     creatorPayout | ||||
|   ) | ||||
|  | @ -105,7 +105,7 @@ export const getMktFixedPayouts = ( | |||
|     'pool', | ||||
|     p * contract.pool.YES + (1 - p) * contract.pool.NO, | ||||
|     'payouts', | ||||
|     _.sum(payouts), | ||||
|     sum(payouts), | ||||
|     'creator fee', | ||||
|     creatorPayout | ||||
|   ) | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import * as _ from 'lodash' | ||||
| import { sumBy, groupBy, mapValues } from 'lodash' | ||||
| 
 | ||||
| import { Bet, NumericBet } from './bet' | ||||
| import { | ||||
|  | @ -32,16 +32,19 @@ export type Payout = { | |||
| 
 | ||||
| export const getLoanPayouts = (bets: Bet[]): Payout[] => { | ||||
|   const betsWithLoans = bets.filter((bet) => bet.loanAmount) | ||||
|   const betsByUser = _.groupBy(betsWithLoans, (bet) => bet.userId) | ||||
|   const loansByUser = _.mapValues(betsByUser, (bets) => | ||||
|     _.sumBy(bets, (bet) => -(bet.loanAmount ?? 0)) | ||||
|   const betsByUser = groupBy(betsWithLoans, (bet) => bet.userId) | ||||
|   const loansByUser = mapValues(betsByUser, (bets) => | ||||
|     sumBy(bets, (bet) => -(bet.loanAmount ?? 0)) | ||||
|   ) | ||||
|   return _.toPairs(loansByUser).map(([userId, payout]) => ({ userId, payout })) | ||||
|   return Object.entries(loansByUser).map(([userId, payout]) => ({ | ||||
|     userId, | ||||
|     payout, | ||||
|   })) | ||||
| } | ||||
| 
 | ||||
| export const groupPayoutsByUser = (payouts: Payout[]) => { | ||||
|   const groups = _.groupBy(payouts, (payout) => payout.userId) | ||||
|   return _.mapValues(groups, (group) => _.sumBy(group, (g) => g.payout)) | ||||
|   const groups = groupBy(payouts, (payout) => payout.userId) | ||||
|   return mapValues(groups, (group) => sumBy(group, (g) => g.payout)) | ||||
| } | ||||
| 
 | ||||
| export type PayoutInfo = { | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import * as _ from 'lodash' | ||||
| import { union, sum, sumBy, sortBy, groupBy, mapValues } from 'lodash' | ||||
| import { Bet } from './bet' | ||||
| import { Contract } from './contract' | ||||
| import { ClickEvent } from './tracking' | ||||
|  | @ -21,13 +21,13 @@ export const getRecommendedContracts = ( | |||
| 
 | ||||
|   const yourWordFrequency = contractsToWordFrequency(yourContracts) | ||||
|   const otherWordFrequency = contractsToWordFrequency(notYourContracts) | ||||
|   const words = _.union( | ||||
|   const words = union( | ||||
|     Object.keys(yourWordFrequency), | ||||
|     Object.keys(otherWordFrequency) | ||||
|   ) | ||||
| 
 | ||||
|   const yourWeightedFrequency = _.fromPairs( | ||||
|     _.map(words, (word) => { | ||||
|   const yourWeightedFrequency = Object.fromEntries( | ||||
|     words.map((word) => { | ||||
|       const [yourFreq, otherFreq] = [ | ||||
|         yourWordFrequency[word] ?? 0, | ||||
|         otherWordFrequency[word] ?? 0, | ||||
|  | @ -47,7 +47,7 @@ export const getRecommendedContracts = ( | |||
|   const scoredContracts = contracts.map((contract) => { | ||||
|     const wordFrequency = contractToWordFrequency(contract) | ||||
| 
 | ||||
|     const score = _.sumBy(Object.keys(wordFrequency), (word) => { | ||||
|     const score = sumBy(Object.keys(wordFrequency), (word) => { | ||||
|       const wordFreq = wordFrequency[word] ?? 0 | ||||
|       const weight = yourWeightedFrequency[word] ?? 0 | ||||
|       return wordFreq * weight | ||||
|  | @ -59,7 +59,7 @@ export const getRecommendedContracts = ( | |||
|     } | ||||
|   }) | ||||
| 
 | ||||
|   return _.sortBy(scoredContracts, (scored) => -scored.score).map( | ||||
|   return sortBy(scoredContracts, (scored) => -scored.score).map( | ||||
|     (scored) => scored.contract | ||||
|   ) | ||||
| } | ||||
|  | @ -87,8 +87,8 @@ const getWordsCount = (text: string) => { | |||
| } | ||||
| 
 | ||||
| const toFrequency = (counts: { [word: string]: number }) => { | ||||
|   const total = _.sum(Object.values(counts)) | ||||
|   return _.mapValues(counts, (count) => count / total) | ||||
|   const total = sum(Object.values(counts)) | ||||
|   return mapValues(counts, (count) => count / total) | ||||
| } | ||||
| 
 | ||||
| const contractToWordFrequency = (contract: Contract) => | ||||
|  | @ -108,8 +108,8 @@ export const getWordScores = ( | |||
|   clicks: ClickEvent[], | ||||
|   bets: Bet[] | ||||
| ) => { | ||||
|   const contractClicks = _.groupBy(clicks, (click) => click.contractId) | ||||
|   const contractBets = _.groupBy(bets, (bet) => bet.contractId) | ||||
|   const contractClicks = groupBy(clicks, (click) => click.contractId) | ||||
|   const contractBets = groupBy(bets, (bet) => bet.contractId) | ||||
| 
 | ||||
|   const yourContracts = contracts.filter( | ||||
|     (c) => | ||||
|  | @ -117,9 +117,7 @@ export const getWordScores = ( | |||
|   ) | ||||
|   const yourTfIdf = calculateContractTfIdf(yourContracts) | ||||
| 
 | ||||
|   const contractWordScores = _.mapValues( | ||||
|     yourTfIdf, | ||||
|     (wordsTfIdf, contractId) => { | ||||
|   const contractWordScores = mapValues(yourTfIdf, (wordsTfIdf, contractId) => { | ||||
|     const viewCount = contractViewCounts[contractId] ?? 0 | ||||
|     const clickCount = contractClicks[contractId]?.length ?? 0 | ||||
|     const betCount = contractBets[contractId]?.length ?? 0 | ||||
|  | @ -128,14 +126,13 @@ export const getWordScores = ( | |||
|       -1 * Math.log(viewCount + 1) + | ||||
|       10 * Math.log(betCount + clickCount / 4 + 1) | ||||
| 
 | ||||
|       return _.mapValues(wordsTfIdf, (tfIdf) => tfIdf * factor) | ||||
|     } | ||||
|   ) | ||||
|     return mapValues(wordsTfIdf, (tfIdf) => tfIdf * factor) | ||||
|   }) | ||||
| 
 | ||||
|   const wordScores = Object.values(contractWordScores).reduce(addObjects, {}) | ||||
|   const minScore = Math.min(...Object.values(wordScores)) | ||||
|   const maxScore = Math.max(...Object.values(wordScores)) | ||||
|   const normalizedWordScores = _.mapValues( | ||||
|   const normalizedWordScores = mapValues( | ||||
|     wordScores, | ||||
|     (score) => (score - minScore) / (maxScore - minScore) | ||||
|   ) | ||||
|  | @ -156,7 +153,7 @@ export function getContractScore( | |||
|   if (Object.keys(wordScores).length === 0) return 1 | ||||
| 
 | ||||
|   const wordFrequency = contractToWordFrequency(contract) | ||||
|   const score = _.sumBy(Object.keys(wordFrequency), (word) => { | ||||
|   const score = sumBy(Object.keys(wordFrequency), (word) => { | ||||
|     const wordFreq = wordFrequency[word] ?? 0 | ||||
|     const weight = wordScores[word] ?? 0 | ||||
|     return wordFreq * weight | ||||
|  | @ -178,11 +175,13 @@ function calculateContractTfIdf(contracts: Contract[]) { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   const wordIdf = _.mapValues(wordsCount, (count) => | ||||
|   const wordIdf = mapValues(wordsCount, (count) => | ||||
|     Math.log(contracts.length / count) | ||||
|   ) | ||||
|   const contractWordsTfIdf = _.map(contractFreq, (wordFreq) => | ||||
|     _.mapValues(wordFreq, (freq, word) => freq * wordIdf[word]) | ||||
|   const contractWordsTfIdf = contractFreq.map((wordFreq) => | ||||
|     mapValues(wordFreq, (freq, word) => freq * wordIdf[word]) | ||||
|   ) | ||||
|   return Object.fromEntries( | ||||
|     contracts.map((c, i) => [c.id, contractWordsTfIdf[i]]) | ||||
|   ) | ||||
|   return _.fromPairs(contracts.map((c, i) => [c.id, contractWordsTfIdf[i]])) | ||||
| } | ||||
|  |  | |||
|  | @ -1,13 +1,13 @@ | |||
| import * as _ from 'lodash' | ||||
| import { groupBy, sumBy, mapValues, partition } from 'lodash' | ||||
| 
 | ||||
| import { Bet } from './bet' | ||||
| import { Binary, Contract, FullContract } from './contract' | ||||
| import { getPayouts } from './payouts' | ||||
| 
 | ||||
| export function scoreCreators(contracts: Contract[], bets: Bet[][]) { | ||||
|   const creatorScore = _.mapValues( | ||||
|     _.groupBy(contracts, ({ creatorId }) => creatorId), | ||||
|     (contracts) => _.sumBy(contracts, ({ pool }) => pool.YES + pool.NO) | ||||
|   const creatorScore = mapValues( | ||||
|     groupBy(contracts, ({ creatorId }) => creatorId), | ||||
|     (contracts) => sumBy(contracts, ({ pool }) => pool.YES + pool.NO) | ||||
|   ) | ||||
| 
 | ||||
|   return creatorScore | ||||
|  | @ -30,7 +30,7 @@ export function scoreUsersByContract( | |||
| ) { | ||||
|   const { resolution, resolutionProbability } = contract | ||||
| 
 | ||||
|   const [closedBets, openBets] = _.partition( | ||||
|   const [closedBets, openBets] = partition( | ||||
|     bets, | ||||
|     (bet) => bet.isSold || bet.sale | ||||
|   ) | ||||
|  | @ -58,9 +58,9 @@ export function scoreUsersByContract( | |||
| 
 | ||||
|   const netPayouts = [...resolvePayouts, ...salePayouts, ...investments] | ||||
| 
 | ||||
|   const userScore = _.mapValues( | ||||
|     _.groupBy(netPayouts, (payout) => payout.userId), | ||||
|     (payouts) => _.sumBy(payouts, ({ payout }) => payout) | ||||
|   const userScore = mapValues( | ||||
|     groupBy(netPayouts, (payout) => payout.userId), | ||||
|     (payouts) => sumBy(payouts, ({ payout }) => payout) | ||||
|   ) | ||||
| 
 | ||||
|   return userScore | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import * as _ from 'lodash' | ||||
| import { union } from 'lodash' | ||||
| 
 | ||||
| export const removeUndefinedProps = <T>(obj: T): T => { | ||||
|   let newObj: any = {} | ||||
|  | @ -14,7 +14,7 @@ export const addObjects = <T extends { [key: string]: number }>( | |||
|   obj1: T, | ||||
|   obj2: T | ||||
| ) => { | ||||
|   const keys = _.union(Object.keys(obj1), Object.keys(obj2)) | ||||
|   const keys = union(Object.keys(obj1), Object.keys(obj2)) | ||||
|   const newObj = {} as any | ||||
| 
 | ||||
|   for (let key of keys) { | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| module.exports = { | ||||
|   plugins: ['lodash'], | ||||
|   extends: ['eslint:recommended'], | ||||
|   ignorePatterns: ['lib'], | ||||
|   env: { | ||||
|  | @ -17,5 +18,6 @@ module.exports = { | |||
|   rules: { | ||||
|     'no-unused-vars': 'off', | ||||
|     'no-constant-condition': ['error', { checkLoops: false }], | ||||
|     'lodash/import-scope': [2, 'member'], | ||||
|   }, | ||||
| } | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| import * as functions from 'firebase-functions' | ||||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| 
 | ||||
| import { getUser } from './utils' | ||||
| import { Contract } from '../../common/contract' | ||||
|  | @ -34,7 +33,7 @@ export const createFold = functions.runWith({ minInstances: 1 }).https.onCall( | |||
|       return { status: 'error', message: 'About must be a string' } | ||||
|     about = about.trim().slice(0, 140) | ||||
| 
 | ||||
|     if (!_.isArray(tags)) | ||||
|     if (!Array.isArray(tags)) | ||||
|       return { status: 'error', message: 'Tags must be an array of strings' } | ||||
| 
 | ||||
|     console.log( | ||||
|  |  | |||
|  | @ -1,5 +1,3 @@ | |||
| import * as _ from 'lodash' | ||||
| 
 | ||||
| import { DOMAIN, PROJECT_ID } from '../../common/envs/constants' | ||||
| import { Answer } from '../../common/answer' | ||||
| import { Bet } from '../../common/bet' | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| import * as functions from 'firebase-functions' | ||||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| 
 | ||||
| import { getContract } from './utils' | ||||
| import { Bet } from '../../common/bet' | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| import * as functions from 'firebase-functions' | ||||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| import { uniq } from 'lodash' | ||||
| 
 | ||||
| import { getContract, getUser, getValues } from './utils' | ||||
| import { Comment } from '../../common/comment' | ||||
|  | @ -60,7 +60,7 @@ export const onCreateComment = functions.firestore | |||
|       firestore.collection('contracts').doc(contractId).collection('comments') | ||||
|     ) | ||||
| 
 | ||||
|     const recipientUserIds = _.uniq([ | ||||
|     const recipientUserIds = uniq([ | ||||
|       contract.creatorId, | ||||
|       ...comments.map((comment) => comment.userId), | ||||
|     ]).filter((id) => id !== comment.userId) | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| import { partition, sumBy } from 'lodash' | ||||
| 
 | ||||
| import { Bet } from '../../common/bet' | ||||
| import { getProbability } from '../../common/calculate' | ||||
|  | @ -25,14 +25,14 @@ export const redeemShares = async (userId: string, contractId: string) => { | |||
|         .where('userId', '==', userId) | ||||
|     ) | ||||
|     const bets = betsSnap.docs.map((doc) => doc.data() as Bet) | ||||
|     const [yesBets, noBets] = _.partition(bets, (b) => b.outcome === 'YES') | ||||
|     const yesShares = _.sumBy(yesBets, (b) => b.shares) | ||||
|     const noShares = _.sumBy(noBets, (b) => b.shares) | ||||
|     const [yesBets, noBets] = partition(bets, (b) => b.outcome === 'YES') | ||||
|     const yesShares = sumBy(yesBets, (b) => b.shares) | ||||
|     const noShares = sumBy(noBets, (b) => b.shares) | ||||
| 
 | ||||
|     const amount = Math.min(yesShares, noShares) | ||||
|     if (amount <= 0) return | ||||
| 
 | ||||
|     const prevLoanAmount = _.sumBy(bets, (bet) => bet.loanAmount ?? 0) | ||||
|     const prevLoanAmount = sumBy(bets, (bet) => bet.loanAmount ?? 0) | ||||
|     const loanPaid = Math.min(prevLoanAmount, amount) | ||||
|     const netAmount = amount - loanPaid | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| import * as functions from 'firebase-functions' | ||||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| import { difference, uniq, mapValues, groupBy, sumBy } from 'lodash' | ||||
| 
 | ||||
| import { Contract } from '../../common/contract' | ||||
| import { User } from '../../common/user' | ||||
|  | @ -187,13 +187,13 @@ const sendResolutionEmails = async ( | |||
|   resolutionProbability?: number, | ||||
|   resolutions?: { [outcome: string]: number } | ||||
| ) => { | ||||
|   const nonWinners = _.difference( | ||||
|     _.uniq(openBets.map(({ userId }) => userId)), | ||||
|   const nonWinners = difference( | ||||
|     uniq(openBets.map(({ userId }) => userId)), | ||||
|     Object.keys(userPayouts) | ||||
|   ) | ||||
|   const investedByUser = _.mapValues( | ||||
|     _.groupBy(openBets, (bet) => bet.userId), | ||||
|     (bets) => _.sumBy(bets, (bet) => bet.amount) | ||||
|   const investedByUser = mapValues( | ||||
|     groupBy(openBets, (bet) => bet.userId), | ||||
|     (bets) => sumBy(bets, (bet) => bet.amount) | ||||
|   ) | ||||
|   const emailPayouts = [ | ||||
|     ...Object.entries(userPayouts), | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| 
 | ||||
| import { initAdmin } from './script-init' | ||||
| initAdmin() | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| 
 | ||||
| import { initAdmin } from './script-init' | ||||
| initAdmin() | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| import { sortBy } from 'lodash' | ||||
| 
 | ||||
| import { initAdmin } from './script-init' | ||||
| initAdmin() | ||||
|  | @ -20,7 +20,7 @@ async function migrateContract( | |||
|     .get() | ||||
|     .then((snap) => snap.docs.map((bet) => bet.data() as Bet)) | ||||
| 
 | ||||
|   const lastBet = _.sortBy(bets, (bet) => -bet.createdTime)[0] | ||||
|   const lastBet = sortBy(bets, (bet) => -bet.createdTime)[0] | ||||
|   if (lastBet) { | ||||
|     const probAfter = getDpmProbability(contract.totalShares) | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| 
 | ||||
| import { initAdmin } from './script-init' | ||||
| initAdmin() | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| import * as fs from 'fs' | ||||
| 
 | ||||
| import { initAdmin } from './script-init' | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| import { uniq } from 'lodash' | ||||
| 
 | ||||
| import { initAdmin } from './script-init' | ||||
| initAdmin() | ||||
|  | @ -19,7 +19,7 @@ async function lowercaseFoldTags() { | |||
|     const foldRef = firestore.doc(`folds/${fold.id}`) | ||||
| 
 | ||||
|     const { tags } = fold | ||||
|     const lowercaseTags = _.uniq(tags.map((tag) => tag.toLowerCase())) | ||||
|     const lowercaseTags = uniq(tags.map((tag) => tag.toLowerCase())) | ||||
| 
 | ||||
|     console.log('Adding lowercase tags', fold.slug, lowercaseTags) | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| 
 | ||||
| import { initAdmin } from './script-init' | ||||
| initAdmin() | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| import { sumBy } from 'lodash' | ||||
| 
 | ||||
| import { initAdmin } from './script-init' | ||||
| initAdmin() | ||||
|  | @ -25,8 +25,8 @@ async function migrateContract(contractRef: DocRef, contract: Contract) { | |||
|     .then((snap) => snap.docs.map((bet) => bet.data() as Bet)) | ||||
| 
 | ||||
|   const totalShares = { | ||||
|     YES: _.sumBy(bets, (bet) => (bet.outcome === 'YES' ? bet.shares : 0)), | ||||
|     NO: _.sumBy(bets, (bet) => (bet.outcome === 'NO' ? bet.shares : 0)), | ||||
|     YES: sumBy(bets, (bet) => (bet.outcome === 'YES' ? bet.shares : 0)), | ||||
|     NO: sumBy(bets, (bet) => (bet.outcome === 'NO' ? bet.shares : 0)), | ||||
|   } | ||||
| 
 | ||||
|   await contractRef.update({ totalShares }) | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| import { sortBy } from 'lodash' | ||||
| 
 | ||||
| import { initAdmin } from './script-init' | ||||
| initAdmin() | ||||
|  | @ -48,7 +48,7 @@ async function recalculateContract(contractRef: DocRef, isCommit = false) { | |||
| 
 | ||||
|     const betsRef = contractRef.collection('bets') | ||||
|     const betDocs = await transaction.get(betsRef) | ||||
|     const bets = _.sortBy( | ||||
|     const bets = sortBy( | ||||
|       betDocs.docs.map((d) => d.data() as Bet), | ||||
|       (b) => b.createdTime | ||||
|     ) | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| import { sortBy, sumBy } from 'lodash' | ||||
| 
 | ||||
| import { initAdmin } from './script-init' | ||||
| initAdmin() | ||||
|  | @ -35,7 +35,7 @@ async function recalculateContract( | |||
|     const contract = contractDoc.data() as FullContract<DPM, Binary> | ||||
| 
 | ||||
|     const betDocs = await transaction.get(contractRef.collection('bets')) | ||||
|     const bets = _.sortBy( | ||||
|     const bets = sortBy( | ||||
|       betDocs.docs.map((d) => d.data() as Bet), | ||||
|       (b) => b.createdTime | ||||
|     ) | ||||
|  | @ -43,8 +43,8 @@ async function recalculateContract( | |||
|     const phantomAnte = startPool.YES + startPool.NO | ||||
| 
 | ||||
|     const leftovers = | ||||
|       _.sumBy(bets, (b) => b.amount) - | ||||
|       _.sumBy(bets, (b) => { | ||||
|       sumBy(bets, (b) => b.amount) - | ||||
|       sumBy(bets, (b) => { | ||||
|         if (!b.sale) return b.amount | ||||
|         const soldBet = bets.find((bet) => bet.id === b.sale?.betId) | ||||
|         return soldBet?.amount || 0 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| import { flatten, groupBy, sumBy, mapValues } from 'lodash' | ||||
| 
 | ||||
| import { initAdmin } from './script-init' | ||||
| initAdmin() | ||||
|  | @ -35,12 +35,12 @@ async function checkIfPayOutAgain(contractRef: DocRef, contract: Contract) { | |||
|     ) | ||||
| 
 | ||||
|     const loanPayouts = getLoanPayouts(openBets) | ||||
|     const groups = _.groupBy( | ||||
|     const groups = groupBy( | ||||
|       [...payouts, ...loanPayouts], | ||||
|       (payout) => payout.userId | ||||
|     ) | ||||
|     const userPayouts = _.mapValues(groups, (group) => | ||||
|       _.sumBy(group, (g) => g.payout) | ||||
|     const userPayouts = mapValues(groups, (group) => | ||||
|       sumBy(group, (g) => g.payout) | ||||
|     ) | ||||
| 
 | ||||
|     const entries = Object.entries(userPayouts) | ||||
|  | @ -93,7 +93,7 @@ async function payOutContractAgain() { | |||
|     ) | ||||
|   ) | ||||
| 
 | ||||
|   const flattened = _.flatten(toPayOutAgain.map((d) => d.toBePaidOut)) | ||||
|   const flattened = flatten(toPayOutAgain.map((d) => d.toBePaidOut)) | ||||
| 
 | ||||
|   for (const [userId, payout] of flattened) { | ||||
|     console.log('Paying out', userId, payout) | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| import { sumBy } from 'lodash' | ||||
| 
 | ||||
| import { initAdmin } from './script-init' | ||||
| initAdmin() | ||||
|  | @ -20,13 +20,13 @@ async function recalculateContract(contractRef: DocRef, contract: Contract) { | |||
|   const openBets = bets.filter((b) => !b.isSold && !b.sale) | ||||
| 
 | ||||
|   const totalShares = { | ||||
|     YES: _.sumBy(openBets, (bet) => (bet.outcome === 'YES' ? bet.shares : 0)), | ||||
|     NO: _.sumBy(openBets, (bet) => (bet.outcome === 'NO' ? bet.shares : 0)), | ||||
|     YES: sumBy(openBets, (bet) => (bet.outcome === 'YES' ? bet.shares : 0)), | ||||
|     NO: sumBy(openBets, (bet) => (bet.outcome === 'NO' ? bet.shares : 0)), | ||||
|   } | ||||
| 
 | ||||
|   const totalBets = { | ||||
|     YES: _.sumBy(openBets, (bet) => (bet.outcome === 'YES' ? bet.amount : 0)), | ||||
|     NO: _.sumBy(openBets, (bet) => (bet.outcome === 'NO' ? bet.amount : 0)), | ||||
|     YES: sumBy(openBets, (bet) => (bet.outcome === 'YES' ? bet.amount : 0)), | ||||
|     NO: sumBy(openBets, (bet) => (bet.outcome === 'NO' ? bet.amount : 0)), | ||||
|   } | ||||
| 
 | ||||
|   await contractRef.update({ totalShares, totalBets }) | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| 
 | ||||
| import { initAdmin } from './script-init' | ||||
| initAdmin() | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| 
 | ||||
| import { initAdmin } from './script-init' | ||||
| initAdmin() | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| import { uniq } from 'lodash' | ||||
| 
 | ||||
| import { initAdmin } from './script-init' | ||||
| initAdmin() | ||||
|  | @ -19,7 +19,7 @@ async function updateContractTags() { | |||
|   for (const contract of contracts) { | ||||
|     const contractRef = firestore.doc(`contracts/${contract.id}`) | ||||
| 
 | ||||
|     const tags = _.uniq([ | ||||
|     const tags = uniq([ | ||||
|       ...parseTags(contract.question + contract.description), | ||||
|       ...(contract.tags ?? []), | ||||
|     ]) | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| 
 | ||||
| import { initAdmin } from './script-init' | ||||
| initAdmin() | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| 
 | ||||
| import { initAdmin } from './script-init' | ||||
| initAdmin() | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import * as _ from 'lodash' | ||||
| import { partition, sumBy } from 'lodash' | ||||
| import * as admin from 'firebase-admin' | ||||
| import * as functions from 'firebase-functions' | ||||
| 
 | ||||
|  | @ -51,15 +51,15 @@ export const sellShares = functions.runWith({ minInstances: 1 }).https.onCall( | |||
|         contractDoc.collection('bets').where('userId', '==', userId) | ||||
|       ) | ||||
| 
 | ||||
|       const prevLoanAmount = _.sumBy(userBets, (bet) => bet.loanAmount ?? 0) | ||||
|       const prevLoanAmount = sumBy(userBets, (bet) => bet.loanAmount ?? 0) | ||||
| 
 | ||||
|       const [yesBets, noBets] = _.partition( | ||||
|       const [yesBets, noBets] = partition( | ||||
|         userBets ?? [], | ||||
|         (bet) => bet.outcome === 'YES' | ||||
|       ) | ||||
|       const [yesShares, noShares] = [ | ||||
|         _.sumBy(yesBets, (bet) => bet.shares), | ||||
|         _.sumBy(noBets, (bet) => bet.shares), | ||||
|         sumBy(yesBets, (bet) => bet.shares), | ||||
|         sumBy(noBets, (bet) => bet.shares), | ||||
|       ] | ||||
| 
 | ||||
|       const maxShares = outcome === 'YES' ? yesShares : noShares | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| import * as functions from 'firebase-functions' | ||||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| import { getUser } from './utils' | ||||
| import { PrivateUser } from '../../common/user' | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| import * as functions from 'firebase-functions' | ||||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| import { sumBy } from 'lodash' | ||||
| 
 | ||||
| import { getValues } from './utils' | ||||
| import { Contract } from '../../common/contract' | ||||
|  | @ -39,5 +39,5 @@ const computeVolumeFrom = async (contract: Contract, timeAgoMs: number) => { | |||
|       .where('createdTime', '>', Date.now() - timeAgoMs) | ||||
|   ) | ||||
| 
 | ||||
|   return _.sumBy(bets, (bet) => (bet.isRedemption ? 0 : Math.abs(bet.amount))) | ||||
|   return sumBy(bets, (bet) => (bet.isRedemption ? 0 : Math.abs(bet.amount))) | ||||
| } | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| import * as _ from 'lodash' | ||||
| import * as functions from 'firebase-functions' | ||||
| import * as admin from 'firebase-admin' | ||||
| import { shuffle, sortBy } from 'lodash' | ||||
| 
 | ||||
| import { getValue, getValues } from './utils' | ||||
| import { Contract } from '../../common/contract' | ||||
|  | @ -30,7 +30,7 @@ const BATCH_SIZE = 30 | |||
| const MAX_BATCHES = 50 | ||||
| 
 | ||||
| const getUserBatches = async () => { | ||||
|   const users = _.shuffle(await getValues<User>(firestore.collection('users'))) | ||||
|   const users = shuffle(await getValues<User>(firestore.collection('users'))) | ||||
|   let userBatches: User[][] = [] | ||||
|   for (let i = 0; i < users.length; i += BATCH_SIZE) { | ||||
|     userBatches.push(users.slice(i, i + BATCH_SIZE)) | ||||
|  | @ -128,7 +128,7 @@ export const computeFeed = async (user: User, contracts: Contract[]) => { | |||
|     return [contract, score] as [Contract, number] | ||||
|   }) | ||||
| 
 | ||||
|   const sortedContracts = _.sortBy( | ||||
|   const sortedContracts = sortBy( | ||||
|     scoredContracts, | ||||
|     ([_, score]) => score | ||||
|   ).reverse() | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| import * as functions from 'firebase-functions' | ||||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| 
 | ||||
| import { getValue, getValues } from './utils' | ||||
| import { Contract } from '../../common/contract' | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| import * as functions from 'firebase-functions' | ||||
| import * as admin from 'firebase-admin' | ||||
| import * as _ from 'lodash' | ||||
| import { sum, sumBy } from 'lodash' | ||||
| 
 | ||||
| import { getValues } from './utils' | ||||
| import { Contract } from '../../common/contract' | ||||
|  | @ -19,7 +19,7 @@ export const updateUserMetrics = functions.pubsub | |||
|       getValues<Contract>(firestore.collection('contracts')), | ||||
|     ]) | ||||
| 
 | ||||
|     const contractsDict = _.fromPairs( | ||||
|     const contractsDict = Object.fromEntries( | ||||
|       contracts.map((contract) => [contract.id, contract]) | ||||
|     ) | ||||
| 
 | ||||
|  | @ -43,12 +43,12 @@ export const updateUserMetrics = functions.pubsub | |||
| 
 | ||||
| const computeInvestmentValue = async ( | ||||
|   user: User, | ||||
|   contractsDict: _.Dictionary<Contract> | ||||
|   contractsDict: { [k: string]: Contract } | ||||
| ) => { | ||||
|   const query = firestore.collectionGroup('bets').where('userId', '==', user.id) | ||||
|   const bets = await getValues<Bet>(query) | ||||
| 
 | ||||
|   return _.sumBy(bets, (bet) => { | ||||
|   return sumBy(bets, (bet) => { | ||||
|     const contract = contractsDict[bet.contractId] | ||||
|     if (!contract || contract.isResolved) return 0 | ||||
|     if (bet.sale || bet.isSold) return 0 | ||||
|  | @ -60,20 +60,20 @@ const computeInvestmentValue = async ( | |||
| 
 | ||||
| const computeTotalPool = async ( | ||||
|   user: User, | ||||
|   contractsDict: _.Dictionary<Contract> | ||||
|   contractsDict: { [k: string]: Contract } | ||||
| ) => { | ||||
|   const creatorContracts = Object.values(contractsDict).filter( | ||||
|     (contract) => contract.creatorId === user.id | ||||
|   ) | ||||
|   const pools = creatorContracts.map((contract) => | ||||
|     _.sum(Object.values(contract.pool)) | ||||
|     sum(Object.values(contract.pool)) | ||||
|   ) | ||||
|   return _.sum(pools) | ||||
|   return sum(pools) | ||||
| } | ||||
| 
 | ||||
| const computeVolume = async (contract: Contract) => { | ||||
|   const bets = await getValues<Bet>( | ||||
|     firestore.collection(`contracts/${contract.id}/bets`) | ||||
|   ) | ||||
|   return _.sumBy(bets, (bet) => Math.abs(bet.amount)) | ||||
|   return sumBy(bets, (bet) => Math.abs(bet.amount)) | ||||
| } | ||||
|  |  | |||
|  | @ -9,10 +9,11 @@ | |||
|   "scripts": {}, | ||||
|   "dependencies": {}, | ||||
|   "devDependencies": { | ||||
|     "typescript": "4.6.4", | ||||
|     "@typescript-eslint/eslint-plugin": "5.25.0", | ||||
|     "@typescript-eslint/parser": "5.25.0", | ||||
|     "eslint": "8.15.0", | ||||
|     "prettier": "2.5.0" | ||||
|     "eslint-plugin-lodash": "^7.4.0", | ||||
|     "prettier": "2.5.0", | ||||
|     "typescript": "4.6.4" | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -1,9 +1,10 @@ | |||
| module.exports = { | ||||
|   parser: '@typescript-eslint/parser', | ||||
|   plugins: ['lodash'], | ||||
|   extends: ['plugin:react-hooks/recommended', 'plugin:@next/next/recommended'], | ||||
|   rules: { | ||||
|     // Add or disable rules here.
 | ||||
|     '@next/next/no-img-element': 'off', | ||||
|     '@next/next/no-typos': 'off', | ||||
|     'lodash/import-scope': [2, 'member'], | ||||
|   }, | ||||
| } | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| import { Point, ResponsiveLine } from '@nivo/line' | ||||
| import dayjs from 'dayjs' | ||||
| import _ from 'lodash' | ||||
| import { zip } from 'lodash' | ||||
| import { useWindowSize } from 'web/hooks/use-window-size' | ||||
| import { Col } from '../layout/col' | ||||
| 
 | ||||
|  | @ -16,7 +16,7 @@ export function DailyCountChart(props: { | |||
|     dayjs(startDate).add(i, 'day').toDate() | ||||
|   ) | ||||
| 
 | ||||
|   const points = _.zip(dates, dailyCounts).map(([date, betCount]) => ({ | ||||
|   const points = zip(dates, dailyCounts).map(([date, betCount]) => ({ | ||||
|     x: date, | ||||
|     y: betCount, | ||||
|   })) | ||||
|  | @ -68,7 +68,7 @@ export function DailyPercentChart(props: { | |||
|     dayjs(startDate).add(i, 'day').toDate() | ||||
|   ) | ||||
| 
 | ||||
|   const points = _.zip(dates, dailyPercent).map(([date, betCount]) => ({ | ||||
|   const points = zip(dates, dailyPercent).map(([date, betCount]) => ({ | ||||
|     x: date, | ||||
|     y: betCount, | ||||
|   })) | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import clsx from 'clsx' | ||||
| import _ from 'lodash' | ||||
| import { sum, mapValues } from 'lodash' | ||||
| import { useState } from 'react' | ||||
| 
 | ||||
| import { DPM, FreeResponse, FullContract } from 'common/contract' | ||||
|  | @ -30,8 +30,8 @@ export function AnswerResolvePanel(props: { | |||
| 
 | ||||
|     setIsSubmitting(true) | ||||
| 
 | ||||
|     const totalProb = _.sum(Object.values(chosenAnswers)) | ||||
|     const normalizedProbs = _.mapValues( | ||||
|     const totalProb = sum(Object.values(chosenAnswers)) | ||||
|     const normalizedProbs = mapValues( | ||||
|       chosenAnswers, | ||||
|       (prob) => (100 * prob) / totalProb | ||||
|     ) | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import { DatumValue } from '@nivo/core' | ||||
| import { ResponsiveLine } from '@nivo/line' | ||||
| import dayjs from 'dayjs' | ||||
| import _ from 'lodash' | ||||
| import { groupBy, sortBy, sumBy } from 'lodash' | ||||
| import { memo } from 'react' | ||||
| 
 | ||||
| import { Bet } from 'common/bet' | ||||
|  | @ -48,7 +48,7 @@ export const AnswersGraph = memo(function AnswersGraph(props: { | |||
|         // to the right.
 | ||||
|         latestTime.add(1, 'month').valueOf() | ||||
| 
 | ||||
|   const times = _.sortBy([ | ||||
|   const times = sortBy([ | ||||
|     createdTime, | ||||
|     ...bets.map((bet) => bet.createdTime), | ||||
|     endTime, | ||||
|  | @ -167,7 +167,7 @@ const computeProbsByOutcome = ( | |||
| ) => { | ||||
|   const { totalBets } = contract | ||||
| 
 | ||||
|   const betsByOutcome = _.groupBy(bets, (bet) => bet.outcome) | ||||
|   const betsByOutcome = groupBy(bets, (bet) => bet.outcome) | ||||
|   const outcomes = Object.keys(betsByOutcome).filter((outcome) => { | ||||
|     const maxProb = Math.max( | ||||
|       ...betsByOutcome[outcome].map((bet) => bet.probAfter) | ||||
|  | @ -175,15 +175,15 @@ const computeProbsByOutcome = ( | |||
|     return outcome !== '0' && maxProb > 0.02 && totalBets[outcome] > 0.000000001 | ||||
|   }) | ||||
| 
 | ||||
|   const trackedOutcomes = _.sortBy( | ||||
|   const trackedOutcomes = sortBy( | ||||
|     outcomes, | ||||
|     (outcome) => -1 * getOutcomeProbability(contract, outcome) | ||||
|   ).slice(0, NUM_LINES) | ||||
| 
 | ||||
|   const probsByOutcome = _.fromPairs( | ||||
|   const probsByOutcome = Object.fromEntries( | ||||
|     trackedOutcomes.map((outcome) => [outcome, [] as number[]]) | ||||
|   ) | ||||
|   const sharesByOutcome = _.fromPairs( | ||||
|   const sharesByOutcome = Object.fromEntries( | ||||
|     Object.keys(betsByOutcome).map((outcome) => [outcome, 0]) | ||||
|   ) | ||||
| 
 | ||||
|  | @ -191,7 +191,7 @@ const computeProbsByOutcome = ( | |||
|     const { outcome, shares } = bet | ||||
|     sharesByOutcome[outcome] += shares | ||||
| 
 | ||||
|     const sharesSquared = _.sumBy( | ||||
|     const sharesSquared = sumBy( | ||||
|       Object.values(sharesByOutcome).map((shares) => shares ** 2) | ||||
|     ) | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import _ from 'lodash' | ||||
| import React, { useLayoutEffect, useState } from 'react' | ||||
| import { sortBy, partition, sum, uniq } from 'lodash' | ||||
| import { useLayoutEffect, useState } from 'react' | ||||
| 
 | ||||
| import { DPM, FreeResponse, FullContract } from 'common/contract' | ||||
| import { Col } from '../layout/col' | ||||
|  | @ -32,7 +32,7 @@ export function AnswersPanel(props: { | |||
|   const { creatorId, resolution, resolutions, totalBets } = contract | ||||
| 
 | ||||
|   const answers = useAnswers(contract.id) ?? contract.answers | ||||
|   const [winningAnswers, losingAnswers] = _.partition( | ||||
|   const [winningAnswers, losingAnswers] = partition( | ||||
|     answers.filter( | ||||
|       (answer) => answer.id !== '0' && totalBets[answer.id] > 0.000000001 | ||||
|     ), | ||||
|  | @ -40,10 +40,10 @@ export function AnswersPanel(props: { | |||
|       answer.id === resolution || (resolutions && resolutions[answer.id]) | ||||
|   ) | ||||
|   const sortedAnswers = [ | ||||
|     ..._.sortBy(winningAnswers, (answer) => | ||||
|     ...sortBy(winningAnswers, (answer) => | ||||
|       resolutions ? -1 * resolutions[answer.id] : 0 | ||||
|     ), | ||||
|     ..._.sortBy( | ||||
|     ...sortBy( | ||||
|       resolution ? [] : losingAnswers, | ||||
|       (answer) => -1 * getDpmOutcomeProbability(contract.totalShares, answer.id) | ||||
|     ), | ||||
|  | @ -58,7 +58,7 @@ export function AnswersPanel(props: { | |||
|     [answerId: string]: number | ||||
|   }>({}) | ||||
| 
 | ||||
|   const chosenTotal = _.sum(Object.values(chosenAnswers)) | ||||
|   const chosenTotal = sum(Object.values(chosenAnswers)) | ||||
| 
 | ||||
|   const answerItems = getAnswerItems( | ||||
|     contract, | ||||
|  | @ -158,10 +158,10 @@ function getAnswerItems( | |||
|   answers: Answer[], | ||||
|   user: User | undefined | null | ||||
| ) { | ||||
|   let outcomes = _.uniq( | ||||
|     answers.map((answer) => answer.number.toString()) | ||||
|   ).filter((outcome) => getOutcomeProbability(contract, outcome) > 0.0001) | ||||
|   outcomes = _.sortBy(outcomes, (outcome) => | ||||
|   let outcomes = uniq(answers.map((answer) => answer.number.toString())).filter( | ||||
|     (outcome) => getOutcomeProbability(contract, outcome) > 0.0001 | ||||
|   ) | ||||
|   outcomes = sortBy(outcomes, (outcome) => | ||||
|     getOutcomeProbability(contract, outcome) | ||||
|   ).reverse() | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| import clsx from 'clsx' | ||||
| import _ from 'lodash' | ||||
| import React, { useEffect, useState } from 'react' | ||||
| import { partition, sumBy } from 'lodash' | ||||
| 
 | ||||
| import { useUser } from 'web/hooks/use-user' | ||||
| import { Binary, CPMM, DPM, FullContract } from 'common/contract' | ||||
|  | @ -441,13 +441,13 @@ export function SellPanel(props: { | |||
|   const resultProb = getCpmmProbability(newPool, contract.p) | ||||
| 
 | ||||
|   const openUserBets = userBets.filter((bet) => !bet.isSold && !bet.sale) | ||||
|   const [yesBets, noBets] = _.partition( | ||||
|   const [yesBets, noBets] = partition( | ||||
|     openUserBets, | ||||
|     (bet) => bet.outcome === 'YES' | ||||
|   ) | ||||
|   const [yesShares, noShares] = [ | ||||
|     _.sumBy(yesBets, (bet) => bet.shares), | ||||
|     _.sumBy(noBets, (bet) => bet.shares), | ||||
|     sumBy(yesBets, (bet) => bet.shares), | ||||
|     sumBy(noBets, (bet) => bet.shares), | ||||
|   ] | ||||
| 
 | ||||
|   const sellOutcome = yesShares ? 'YES' : noShares ? 'NO' : undefined | ||||
|  |  | |||
|  | @ -1,5 +1,13 @@ | |||
| import Link from 'next/link' | ||||
| import _ from 'lodash' | ||||
| import { | ||||
|   uniq, | ||||
|   groupBy, | ||||
|   mapValues, | ||||
|   sortBy, | ||||
|   partition, | ||||
|   sumBy, | ||||
|   throttle, | ||||
| } from 'lodash' | ||||
| import dayjs from 'dayjs' | ||||
| import { useEffect, useState } from 'react' | ||||
| import clsx from 'clsx' | ||||
|  | @ -54,7 +62,7 @@ export function BetsList(props: { user: User }) { | |||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (bets) { | ||||
|       const contractIds = _.uniq(bets.map((bet) => bet.contractId)) | ||||
|       const contractIds = uniq(bets.map((bet) => bet.contractId)) | ||||
| 
 | ||||
|       let disposed = false | ||||
|       Promise.all(contractIds.map((id) => getContractFromId(id))).then( | ||||
|  | @ -84,10 +92,10 @@ export function BetsList(props: { user: User }) { | |||
|   if (bets.length === 0) return <NoBets /> | ||||
|   // Decending creation time.
 | ||||
|   bets.sort((bet1, bet2) => bet2.createdTime - bet1.createdTime) | ||||
|   const contractBets = _.groupBy(bets, 'contractId') | ||||
|   const contractsById = _.fromPairs(contracts.map((c) => [c.id, c])) | ||||
|   const contractBets = groupBy(bets, 'contractId') | ||||
|   const contractsById = Object.fromEntries(contracts.map((c) => [c.id, c])) | ||||
| 
 | ||||
|   const contractsMetrics = _.mapValues(contractBets, (bets, contractId) => { | ||||
|   const contractsMetrics = mapValues(contractBets, (bets, contractId) => { | ||||
|     const contract = contractsById[contractId] | ||||
|     if (!contract) return getContractBetNullMetrics() | ||||
|     return getContractBetMetrics(contract, bets) | ||||
|  | @ -110,7 +118,7 @@ export function BetsList(props: { user: User }) { | |||
|       (filter === 'open' ? -1 : 1) * | ||||
|       (c.resolutionTime ?? c.closeTime ?? Infinity), | ||||
|   } | ||||
|   const displayedContracts = _.sortBy(contracts, SORTS[sort]) | ||||
|   const displayedContracts = sortBy(contracts, SORTS[sort]) | ||||
|     .reverse() | ||||
|     .filter(FILTERS[filter]) | ||||
|     .filter((c) => { | ||||
|  | @ -121,20 +129,20 @@ export function BetsList(props: { user: User }) { | |||
|       return metrics.payout > 0 | ||||
|     }) | ||||
| 
 | ||||
|   const [settled, unsettled] = _.partition( | ||||
|   const [settled, unsettled] = partition( | ||||
|     contracts, | ||||
|     (c) => c.isResolved || contractsMetrics[c.id].invested === 0 | ||||
|   ) | ||||
| 
 | ||||
|   const currentInvested = _.sumBy( | ||||
|   const currentInvested = sumBy( | ||||
|     unsettled, | ||||
|     (c) => contractsMetrics[c.id].invested | ||||
|   ) | ||||
|   const currentBetsValue = _.sumBy( | ||||
|   const currentBetsValue = sumBy( | ||||
|     unsettled, | ||||
|     (c) => contractsMetrics[c.id].payout | ||||
|   ) | ||||
|   const currentNetInvestment = _.sumBy( | ||||
|   const currentNetInvestment = sumBy( | ||||
|     unsettled, | ||||
|     (c) => contractsMetrics[c.id].netPayout | ||||
|   ) | ||||
|  | @ -340,10 +348,10 @@ export function MyBetsSummary(props: { | |||
|   const excludeSalesAndAntes = bets.filter( | ||||
|     (b) => !b.isAnte && !b.isSold && !b.sale | ||||
|   ) | ||||
|   const yesWinnings = _.sumBy(excludeSalesAndAntes, (bet) => | ||||
|   const yesWinnings = sumBy(excludeSalesAndAntes, (bet) => | ||||
|     calculatePayout(contract, bet, 'YES') | ||||
|   ) | ||||
|   const noWinnings = _.sumBy(excludeSalesAndAntes, (bet) => | ||||
|   const noWinnings = sumBy(excludeSalesAndAntes, (bet) => | ||||
|     calculatePayout(contract, bet, 'NO') | ||||
|   ) | ||||
|   const { invested, profitPercent, payout, profit } = getContractBetMetrics( | ||||
|  | @ -421,21 +429,19 @@ export function ContractBetsTable(props: { | |||
| 
 | ||||
|   const bets = props.bets.filter((b) => !b.isAnte) | ||||
| 
 | ||||
|   const [sales, buys] = _.partition(bets, (bet) => bet.sale) | ||||
|   const [sales, buys] = partition(bets, (bet) => bet.sale) | ||||
| 
 | ||||
|   const salesDict = _.fromPairs( | ||||
|   const salesDict = Object.fromEntries( | ||||
|     sales.map((sale) => [sale.sale?.betId ?? '', sale]) | ||||
|   ) | ||||
| 
 | ||||
|   const [redemptions, normalBets] = _.partition( | ||||
|   const [redemptions, normalBets] = partition( | ||||
|     contract.mechanism === 'cpmm-1' ? bets : buys, | ||||
|     (b) => b.isRedemption | ||||
|   ) | ||||
|   const amountRedeemed = Math.floor( | ||||
|     -0.5 * _.sumBy(redemptions, (b) => b.shares) | ||||
|   ) | ||||
|   const amountRedeemed = Math.floor(-0.5 * sumBy(redemptions, (b) => b.shares)) | ||||
| 
 | ||||
|   const amountLoaned = _.sumBy( | ||||
|   const amountLoaned = sumBy( | ||||
|     bets.filter((bet) => !bet.isSold && !bet.sale), | ||||
|     (bet) => bet.loanAmount ?? 0 | ||||
|   ) | ||||
|  | @ -570,10 +576,7 @@ function BetRow(props: { bet: Bet; contract: Contract; saleBet?: Bet }) { | |||
|   ) | ||||
| } | ||||
| 
 | ||||
| const warmUpSellBet = _.throttle( | ||||
|   () => sellBet({}).catch(() => {}), | ||||
|   5000 /* ms */ | ||||
| ) | ||||
| const warmUpSellBet = throttle(() => sellBet({}).catch(() => {}), 5000 /* ms */) | ||||
| 
 | ||||
| function SellButton(props: { contract: Contract; bet: Bet }) { | ||||
|   useEffect(() => { | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| import _ from 'lodash' | ||||
| import { useState } from 'react' | ||||
| 
 | ||||
| import { NumericContract } from 'common/contract' | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import { StarIcon } from '@heroicons/react/solid' | ||||
| import _ from 'lodash' | ||||
| import { sumBy } from 'lodash' | ||||
| import Link from 'next/link' | ||||
| import Image from 'next/image' | ||||
| import { Charity } from 'common/charity' | ||||
|  | @ -11,7 +11,7 @@ export function CharityCard(props: { charity: Charity }) { | |||
|   const { name, slug, photo, preview, id, tags } = props.charity | ||||
| 
 | ||||
|   const txns = useCharityTxns(id) | ||||
|   const raised = _.sumBy(txns, (txn) => txn.amount) | ||||
|   const raised = sumBy(txns, (txn) => txn.amount) | ||||
| 
 | ||||
|   return ( | ||||
|     <Link href={`/charity/${slug}`} passHref> | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import { DotsHorizontalIcon } from '@heroicons/react/outline' | ||||
| import clsx from 'clsx' | ||||
| import dayjs from 'dayjs' | ||||
| import _ from 'lodash' | ||||
| import { uniqBy, sum } from 'lodash' | ||||
| import { useState } from 'react' | ||||
| import { Bet } from 'common/bet' | ||||
| 
 | ||||
|  | @ -26,7 +26,7 @@ export function ContractInfoDialog(props: { contract: Contract; bets: Bet[] }) { | |||
|   const formatTime = (dt: number) => dayjs(dt).format('MMM DD, YYYY hh:mm a z') | ||||
| 
 | ||||
|   const { createdTime, closeTime, resolutionTime } = contract | ||||
|   const tradersCount = _.uniqBy(bets, 'userId').length | ||||
|   const tradersCount = uniqBy(bets, 'userId').length | ||||
| 
 | ||||
|   return ( | ||||
|     <> | ||||
|  | @ -108,7 +108,7 @@ export function ContractInfoDialog(props: { contract: Contract; bets: Bet[] }) { | |||
|               {contract.mechanism === 'dpm-2' && ( | ||||
|                 <tr> | ||||
|                   <td>Pool</td> | ||||
|                   <td>{formatMoney(_.sum(Object.values(contract.pool)))}</td> | ||||
|                   <td>{formatMoney(sum(Object.values(contract.pool)))}</td> | ||||
|                 </tr> | ||||
|               )} | ||||
|             </tbody> | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| import { DatumValue } from '@nivo/core' | ||||
| import { ResponsiveLine } from '@nivo/line' | ||||
| import { NUMERIC_GRAPH_COLOR } from 'common/numeric-constants' | ||||
| import _ from 'lodash' | ||||
| import { memo } from 'react' | ||||
| import { range } from 'lodash' | ||||
| import { getDpmOutcomeProbabilities } from '../../../common/calculate-dpm' | ||||
| import { NumericContract } from '../../../common/contract' | ||||
| import { useWindowSize } from '../../hooks/use-window-size' | ||||
|  | @ -16,10 +16,10 @@ export const NumericGraph = memo(function NumericGraph(props: { | |||
| 
 | ||||
|   const bucketProbs = getDpmOutcomeProbabilities(totalShares) | ||||
| 
 | ||||
|   const xs = _.range(bucketCount).map( | ||||
|   const xs = range(bucketCount).map( | ||||
|     (i) => min + ((max - min) * i) / bucketCount | ||||
|   ) | ||||
|   const probs = _.range(bucketCount).map((i) => bucketProbs[`${i}`] * 100) | ||||
|   const probs = range(bucketCount).map((i) => bucketProbs[`${i}`] * 100) | ||||
|   const points = probs.map((prob, i) => ({ x: xs[i], y: prob })) | ||||
|   const maxProb = Math.max(...probs) | ||||
|   const data = [{ id: 'Probability', data: points, color: NUMERIC_GRAPH_COLOR }] | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import _ from 'lodash' | ||||
| import { sample } from 'lodash' | ||||
| import { SparklesIcon, XIcon } from '@heroicons/react/solid' | ||||
| import { Avatar } from './avatar' | ||||
| import { useEffect, useRef, useState } from 'react' | ||||
|  | @ -86,9 +86,7 @@ export default function FeedCreate(props: { | |||
|   // Take care not to produce a different placeholder on the server and client
 | ||||
|   const [defaultPlaceholder, setDefaultPlaceholder] = useState('') | ||||
|   useEffect(() => { | ||||
|     setDefaultPlaceholder( | ||||
|       `e.g. ${_.sample(ENV_CONFIG.newQuestionPlaceholders)}` | ||||
|     ) | ||||
|     setDefaultPlaceholder(`e.g. ${sample(ENV_CONFIG.newQuestionPlaceholders)}`) | ||||
|   }, []) | ||||
| 
 | ||||
|   const placeholder = props.placeholder ?? defaultPlaceholder | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import _ from 'lodash' | ||||
| import { last, findLastIndex, uniq, sortBy } from 'lodash' | ||||
| 
 | ||||
| import { Answer } from 'common/answer' | ||||
| import { Bet } from 'common/bet' | ||||
|  | @ -200,17 +200,17 @@ function getAnswerGroups( | |||
| ) { | ||||
|   const { sortByProb, abbreviated, reversed } = options | ||||
| 
 | ||||
|   let outcomes = _.uniq(bets.map((bet) => bet.outcome)).filter( | ||||
|   let outcomes = uniq(bets.map((bet) => bet.outcome)).filter( | ||||
|     (outcome) => getOutcomeProbability(contract, outcome) > 0.0001 | ||||
|   ) | ||||
|   if (abbreviated) { | ||||
|     const lastComment = _.last(comments) | ||||
|     const lastComment = last(comments) | ||||
|     const lastCommentOutcome = bets.find( | ||||
|       (bet) => bet.id === lastComment?.betId | ||||
|     )?.outcome | ||||
|     const lastBetOutcome = _.last(bets)?.outcome | ||||
|     const lastBetOutcome = last(bets)?.outcome | ||||
|     if (lastCommentOutcome && lastBetOutcome) { | ||||
|       outcomes = _.uniq([ | ||||
|       outcomes = uniq([ | ||||
|         ...outcomes.filter( | ||||
|           (outcome) => | ||||
|             outcome !== lastCommentOutcome && outcome !== lastBetOutcome | ||||
|  | @ -222,13 +222,13 @@ function getAnswerGroups( | |||
|     outcomes = outcomes.slice(-2) | ||||
|   } | ||||
|   if (sortByProb) { | ||||
|     outcomes = _.sortBy(outcomes, (outcome) => | ||||
|     outcomes = sortBy(outcomes, (outcome) => | ||||
|       getOutcomeProbability(contract, outcome) | ||||
|     ) | ||||
|   } else { | ||||
|     // Sort by recent bet.
 | ||||
|     outcomes = _.sortBy(outcomes, (outcome) => | ||||
|       _.findLastIndex(bets, (bet) => bet.outcome === outcome) | ||||
|     outcomes = sortBy(outcomes, (outcome) => | ||||
|       findLastIndex(bets, (bet) => bet.outcome === outcome) | ||||
|     ) | ||||
|   } | ||||
| 
 | ||||
|  | @ -274,10 +274,10 @@ function getAnswerAndCommentInputGroups( | |||
|   comments: Comment[], | ||||
|   user: User | undefined | null | ||||
| ) { | ||||
|   let outcomes = _.uniq(bets.map((bet) => bet.outcome)).filter( | ||||
|   let outcomes = uniq(bets.map((bet) => bet.outcome)).filter( | ||||
|     (outcome) => getOutcomeProbability(contract, outcome) > 0.0001 | ||||
|   ) | ||||
|   outcomes = _.sortBy(outcomes, (outcome) => | ||||
|   outcomes = sortBy(outcomes, (outcome) => | ||||
|     getOutcomeProbability(contract, outcome) | ||||
|   ) | ||||
|   const betsByCurrentUser = bets.filter((bet) => bet.userId === user?.id) | ||||
|  | @ -343,7 +343,7 @@ function groupBetsAndComments( | |||
| 
 | ||||
|   // iterate through the bets and comment activity items and add them to the items in order of comment creation time:
 | ||||
|   const unorderedBetsAndComments = [...commentsWithoutBets, ...groupedBets] | ||||
|   let sortedBetsAndComments = _.sortBy(unorderedBetsAndComments, (item) => { | ||||
|   let sortedBetsAndComments = sortBy(unorderedBetsAndComments, (item) => { | ||||
|     if (item.type === 'comment') { | ||||
|       return item.comment.createdTime | ||||
|     } else if (item.type === 'bet') { | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ import { formatMoney } from 'common/util/format' | |||
| import { OutcomeLabel } from 'web/components/outcome-label' | ||||
| import { RelativeTimestamp } from 'web/components/relative-timestamp' | ||||
| import React, { Fragment } from 'react' | ||||
| import * as _ from 'lodash' | ||||
| import { uniqBy, partition, sumBy, groupBy } from 'lodash' | ||||
| import { JoinSpans } from 'web/components/join-spans' | ||||
| 
 | ||||
| export function FeedBet(props: { | ||||
|  | @ -104,11 +104,11 @@ function BetGroupSpan(props: { | |||
| }) { | ||||
|   const { contract, bets, outcome } = props | ||||
| 
 | ||||
|   const numberTraders = _.uniqBy(bets, (b) => b.userId).length | ||||
|   const numberTraders = uniqBy(bets, (b) => b.userId).length | ||||
| 
 | ||||
|   const [buys, sells] = _.partition(bets, (bet) => bet.amount >= 0) | ||||
|   const buyTotal = _.sumBy(buys, (b) => b.amount) | ||||
|   const sellTotal = _.sumBy(sells, (b) => -b.amount) | ||||
|   const [buys, sells] = partition(bets, (bet) => bet.amount >= 0) | ||||
|   const buyTotal = sumBy(buys, (b) => b.amount) | ||||
|   const sellTotal = sumBy(sells, (b) => -b.amount) | ||||
| 
 | ||||
|   return ( | ||||
|     <span> | ||||
|  | @ -139,7 +139,7 @@ export function FeedBetGroup(props: { | |||
| }) { | ||||
|   const { contract, bets, hideOutcome } = props | ||||
| 
 | ||||
|   const betGroups = _.groupBy(bets, (bet) => bet.outcome) | ||||
|   const betGroups = groupBy(bets, (bet) => bet.outcome) | ||||
|   const outcomes = Object.keys(betGroups) | ||||
| 
 | ||||
|   // Use the time of the last bet for the entire group
 | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ import { Comment } from 'common/comment' | |||
| import { User } from 'common/user' | ||||
| import { Contract } from 'common/contract' | ||||
| import React, { useEffect, useState } from 'react' | ||||
| import { minBy, maxBy, groupBy, partition, sumBy } from 'lodash' | ||||
| import { useUser } from 'web/hooks/use-user' | ||||
| import { formatMoney } from 'common/util/format' | ||||
| import { useRouter } from 'next/router' | ||||
|  | @ -16,7 +17,6 @@ import { contractPath } from 'web/lib/firebase/contracts' | |||
| import { firebaseLogin } from 'web/lib/firebase/users' | ||||
| import { createComment, MAX_COMMENT_LENGTH } from 'web/lib/firebase/comments' | ||||
| import Textarea from 'react-expanding-textarea' | ||||
| import * as _ from 'lodash' | ||||
| import { Linkify } from 'web/components/linkify' | ||||
| import { SiteLink } from 'web/components/site-link' | ||||
| import { BetStatusText } from 'web/components/feed/feed-bets' | ||||
|  | @ -36,7 +36,7 @@ export function FeedCommentThread(props: { | |||
|     props | ||||
|   const [showReply, setShowReply] = useState(false) | ||||
|   const [replyToUsername, setReplyToUsername] = useState('') | ||||
|   const betsByUserId = _.groupBy(bets, (bet) => bet.userId) | ||||
|   const betsByUserId = groupBy(bets, (bet) => bet.userId) | ||||
|   const user = useUser() | ||||
|   const commentsList = comments.filter( | ||||
|     (comment) => | ||||
|  | @ -67,7 +67,7 @@ export function FeedCommentThread(props: { | |||
|             onReplyClick={scrollAndOpenReplyInput} | ||||
|             probAtCreatedTime={ | ||||
|               contract.outcomeType === 'BINARY' | ||||
|                 ? _.minBy(bets, (bet) => { | ||||
|                 ? minBy(bets, (bet) => { | ||||
|                     return bet.createdTime < comment.createdTime | ||||
|                       ? comment.createdTime - bet.createdTime | ||||
|                       : comment.createdTime | ||||
|  | @ -494,8 +494,7 @@ function getBettorsLargestPositionBeforeTime( | |||
|       } | ||||
|     } | ||||
|     const majorityAnswer = | ||||
|       _.maxBy(Object.keys(answerCounts), (outcome) => answerCounts[outcome]) ?? | ||||
|       '' | ||||
|       maxBy(Object.keys(answerCounts), (outcome) => answerCounts[outcome]) ?? '' | ||||
|     return { | ||||
|       userPosition: answerCounts[majorityAnswer] || 0, | ||||
|       outcome: majorityAnswer, | ||||
|  | @ -505,12 +504,12 @@ function getBettorsLargestPositionBeforeTime( | |||
|     return emptyReturn | ||||
|   } | ||||
| 
 | ||||
|   const [yesBets, noBets] = _.partition( | ||||
|   const [yesBets, noBets] = partition( | ||||
|     previousBets ?? [], | ||||
|     (bet) => bet.outcome === 'YES' | ||||
|   ) | ||||
|   yesShares = _.sumBy(yesBets, (bet) => bet.shares) | ||||
|   noShares = _.sumBy(noBets, (bet) => bet.shares) | ||||
|   yesShares = sumBy(yesBets, (bet) => bet.shares) | ||||
|   noShares = sumBy(noBets, (bet) => bet.shares) | ||||
|   yesFloorShares = Math.floor(yesShares) | ||||
|   noFloorShares = Math.floor(noShares) | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| // From https://tailwindui.com/components/application-ui/lists/feeds
 | ||||
| import React, { useState } from 'react' | ||||
| import * as _ from 'lodash' | ||||
| import { | ||||
|   BanIcon, | ||||
|   CheckIcon, | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import _ from 'lodash' | ||||
| import { groupBy, mapValues, maxBy, sortBy } from 'lodash' | ||||
| import { Contract } from 'web/lib/firebase/contracts' | ||||
| import { Comment } from 'web/lib/firebase/comments' | ||||
| import { Bet } from 'common/bet' | ||||
|  | @ -44,10 +44,10 @@ export function findActiveContracts( | |||
|   } | ||||
| 
 | ||||
|   // Add contracts by last bet time.
 | ||||
|   const contractBets = _.groupBy(recentBets, (bet) => bet.contractId) | ||||
|   const contractMostRecentBet = _.mapValues( | ||||
|   const contractBets = groupBy(recentBets, (bet) => bet.contractId) | ||||
|   const contractMostRecentBet = mapValues( | ||||
|     contractBets, | ||||
|     (bets) => _.maxBy(bets, (bet) => bet.createdTime) as Bet | ||||
|     (bets) => maxBy(bets, (bet) => bet.createdTime) as Bet | ||||
|   ) | ||||
|   for (const bet of Object.values(contractMostRecentBet)) { | ||||
|     const contract = contractsById.get(bet.contractId) | ||||
|  | @ -60,21 +60,21 @@ export function findActiveContracts( | |||
|       !contract.isResolved && | ||||
|       (contract.closeTime ?? Infinity) > Date.now() | ||||
|   ) | ||||
|   activeContracts = _.sortBy( | ||||
|   activeContracts = sortBy( | ||||
|     activeContracts, | ||||
|     (c) => -(idToActivityTime.get(c.id) ?? 0) | ||||
|   ) | ||||
| 
 | ||||
|   const contractComments = _.groupBy( | ||||
|   const contractComments = groupBy( | ||||
|     recentComments, | ||||
|     (comment) => comment.contractId | ||||
|   ) | ||||
|   const contractMostRecentComment = _.mapValues( | ||||
|   const contractMostRecentComment = mapValues( | ||||
|     contractComments, | ||||
|     (comments) => _.maxBy(comments, (c) => c.createdTime) as Comment | ||||
|     (comments) => maxBy(comments, (c) => c.createdTime) as Comment | ||||
|   ) | ||||
| 
 | ||||
|   const prioritizedContracts = _.sortBy(activeContracts, (c) => { | ||||
|   const prioritizedContracts = sortBy(activeContracts, (c) => { | ||||
|     const seenTime = seenContracts[c.id] | ||||
|     if (!seenTime) { | ||||
|       return 0 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import { useState } from 'react' | ||||
| import _ from 'lodash' | ||||
| import { isEqual } from 'lodash' | ||||
| import clsx from 'clsx' | ||||
| import { PencilIcon } from '@heroicons/react/outline' | ||||
| 
 | ||||
|  | @ -29,7 +29,7 @@ export function EditFoldButton(props: { fold: Fold; className?: string }) { | |||
| 
 | ||||
|   const saveDisabled = | ||||
|     name === fold.name && | ||||
|     _.isEqual(tags, fold.tags) && | ||||
|     isEqual(tags, fold.tags) && | ||||
|     about === (fold.about ?? '') | ||||
| 
 | ||||
|   const onSubmit = async () => { | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ import { | |||
|   SparklesIcon, | ||||
| } from '@heroicons/react/outline' | ||||
| import clsx from 'clsx' | ||||
| import _ from 'lodash' | ||||
| import { sortBy } from 'lodash' | ||||
| import Link from 'next/link' | ||||
| import { useRouter } from 'next/router' | ||||
| import { useFollowedFolds } from 'web/hooks/use-fold' | ||||
|  | @ -124,7 +124,7 @@ export default function Sidebar(props: { className?: string }) { | |||
| 
 | ||||
|   const user = useUser() | ||||
|   let folds = useFollowedFolds(user) || [] | ||||
|   folds = _.sortBy(folds, 'followCount').reverse() | ||||
|   folds = sortBy(folds, 'followCount').reverse() | ||||
|   const deservesDailyFreeMarket = !useHasCreatedContractToday(user) | ||||
| 
 | ||||
|   const navigationOptions = | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| import clsx from 'clsx' | ||||
| import _ from 'lodash' | ||||
| 
 | ||||
| import { Col } from './layout/col' | ||||
| import { Spacer } from './layout/spacer' | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import { Binary, CPMM, DPM, FullContract } from 'common/contract' | ||||
| import { Bet } from 'common/bet' | ||||
| import { useEffect, useState } from 'react' | ||||
| import _ from 'lodash' | ||||
| import { partition, sumBy } from 'lodash' | ||||
| 
 | ||||
| export const useSaveShares = ( | ||||
|   contract: FullContract<CPMM | DPM, Binary>, | ||||
|  | @ -17,13 +17,13 @@ export const useSaveShares = ( | |||
|     | undefined | ||||
|   >() | ||||
| 
 | ||||
|   const [yesBets, noBets] = _.partition( | ||||
|   const [yesBets, noBets] = partition( | ||||
|     userBets ?? [], | ||||
|     (bet) => bet.outcome === 'YES' | ||||
|   ) | ||||
|   const [yesShares, noShares] = [ | ||||
|     _.sumBy(yesBets, (bet) => bet.shares), | ||||
|     _.sumBy(noBets, (bet) => bet.shares), | ||||
|     sumBy(yesBets, (bet) => bet.shares), | ||||
|     sumBy(noBets, (bet) => bet.shares), | ||||
|   ] | ||||
| 
 | ||||
|   const yesFloorShares = Math.round(yesShares) === 0 ? 0 : Math.floor(yesShares) | ||||
|  |  | |||
|  | @ -19,10 +19,10 @@ import { Comment, getUsersComments } from 'web/lib/firebase/comments' | |||
| import { Contract } from 'common/contract' | ||||
| import { getContractFromId, listContracts } from 'web/lib/firebase/contracts' | ||||
| import { LoadingIndicator } from './loading-indicator' | ||||
| import _ from 'lodash' | ||||
| import { BetsList } from './bets-list' | ||||
| import { Bet } from 'common/bet' | ||||
| import { getUserBets } from 'web/lib/firebase/bets' | ||||
| import { uniq } from 'lodash' | ||||
| 
 | ||||
| export function UserLink(props: { | ||||
|   name: string | ||||
|  | @ -70,7 +70,7 @@ export function UserPage(props: { | |||
|   }, [user]) | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     const uniqueContractIds = _.uniq( | ||||
|     const uniqueContractIds = uniq( | ||||
|       usersComments.map((comment) => comment.contractId) | ||||
|     ) | ||||
|     Promise.all( | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| import _, { Dictionary } from 'lodash' | ||||
| import { useState, useEffect } from 'react' | ||||
| import type { feed } from 'common/feed' | ||||
| import { useTimeSinceFirstRender } from './use-time-since-first-render' | ||||
|  | @ -15,7 +14,7 @@ export const useAlgoFeed = ( | |||
|   category: string | ||||
| ) => { | ||||
|   const [allFeed, setAllFeed] = useState<feed>() | ||||
|   const [categoryFeeds, setCategoryFeeds] = useState<Dictionary<feed>>() | ||||
|   const [categoryFeeds, setCategoryFeeds] = useState<{ [x: string]: feed }>() | ||||
| 
 | ||||
|   const getTime = useTimeSinceFirstRender() | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import _ from 'lodash' | ||||
| import { isEqual } from 'lodash' | ||||
| import { useEffect, useRef, useState } from 'react' | ||||
| import { | ||||
|   Contract, | ||||
|  | @ -80,14 +80,14 @@ export const useUpdatedContracts = (contracts: Contract[] | undefined) => { | |||
|   useEffect(() => { | ||||
|     if (contracts === undefined) return | ||||
| 
 | ||||
|     contractDict.current = _.fromPairs(contracts.map((c) => [c.id, c])) | ||||
|     contractDict.current = Object.fromEntries(contracts.map((c) => [c.id, c])) | ||||
| 
 | ||||
|     const disposes = contracts.map((contract) => { | ||||
|       const { id } = contract | ||||
| 
 | ||||
|       return listenForContract(id, (contract) => { | ||||
|         const curr = contractDict.current[id] | ||||
|         if (!_.isEqual(curr, contract)) { | ||||
|         if (!isEqual(curr, contract)) { | ||||
|           contractDict.current[id] = contract as Contract | ||||
|           triggerUpdate((n) => n + 1) | ||||
|         } | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import _ from 'lodash' | ||||
| import { isEqual, sortBy } from 'lodash' | ||||
| import { useEffect, useState } from 'react' | ||||
| import { Fold } from 'common/fold' | ||||
| import { User } from 'common/user' | ||||
|  | @ -95,9 +95,9 @@ export const useFollowedFolds = (user: User | null | undefined) => { | |||
|         setFollowedFolds(JSON.parse(followedFoldJson)) | ||||
|         // Exit early if ids and followedFoldIds have all the same elements.
 | ||||
|         if ( | ||||
|           _.isEqual( | ||||
|             _.sortBy(ids), | ||||
|             _.sortBy(JSON.parse(followedFoldJson).map((f: Fold) => f.id)) | ||||
|           isEqual( | ||||
|             sortBy(ids), | ||||
|             sortBy(JSON.parse(followedFoldJson).map((f: Fold) => f.id)) | ||||
|           ) | ||||
|         ) { | ||||
|           return | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import _ from 'lodash' | ||||
| import { debounce } from 'lodash' | ||||
| import { RefObject, useMemo, useLayoutEffect, useRef, useState } from 'react' | ||||
| 
 | ||||
| type elem_size = | ||||
|  | @ -21,7 +21,7 @@ export function useListenElemSize<T extends HTMLElement>( | |||
|     } | ||||
| 
 | ||||
|     return debounceMs | ||||
|       ? _.debounce(updateSize, debounceMs, { leading: false, trailing: true }) | ||||
|       ? debounce(updateSize, debounceMs, { leading: false, trailing: true }) | ||||
|       : updateSize | ||||
|   }, [callback, elemRef, debounceMs]) | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import _ from 'lodash' | ||||
| import { isEmpty } from 'lodash' | ||||
| import { useRouter } from 'next/router' | ||||
| import { useState, useEffect } from 'react' | ||||
| import { IS_PRIVATE_MANIFOLD } from 'common/envs/constants' | ||||
|  | @ -16,7 +16,7 @@ export function usePropz( | |||
|   getStaticPropz: (props: PropzProps) => Promise<any> | ||||
| ) { | ||||
|   // If props were successfully server-side generated, just use those
 | ||||
|   if (!_.isEmpty(initialProps)) { | ||||
|   if (!isEmpty(initialProps)) { | ||||
|     return initialProps | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import _ from 'lodash' | ||||
| import { mapValues } from 'lodash' | ||||
| import { useEffect, useState } from 'react' | ||||
| import { Contract } from 'common/contract' | ||||
| import { trackView } from 'web/lib/firebase/tracking' | ||||
|  | @ -38,7 +38,7 @@ export const useSaveSeenContract = ( | |||
| const key = 'feed-seen-contracts' | ||||
| 
 | ||||
| const getSeenContracts = () => { | ||||
|   return _.mapValues( | ||||
|   return mapValues( | ||||
|     JSON.parse(localStorage.getItem(key) ?? '{}'), | ||||
|     (time) => +time | ||||
|   ) | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import _ from 'lodash' | ||||
| import { defaults, debounce } from 'lodash' | ||||
| import { useRouter } from 'next/router' | ||||
| import { useEffect, useMemo, useState } from 'react' | ||||
| import { useSearchBox } from 'react-instantsearch-hooks-web' | ||||
|  | @ -18,7 +18,7 @@ export function useInitialQueryAndSort(options?: { | |||
|   defaultSort: Sort | ||||
|   shouldLoadFromStorage?: boolean | ||||
| }) { | ||||
|   const { defaultSort, shouldLoadFromStorage } = _.defaults(options, { | ||||
|   const { defaultSort, shouldLoadFromStorage } = defaults(options, { | ||||
|     defaultSort: '24-hour-vol', | ||||
|     shouldLoadFromStorage: true, | ||||
|   }) | ||||
|  | @ -79,7 +79,7 @@ export function useUpdateQueryAndSort(props: { | |||
|   // Debounce router query update.
 | ||||
|   const pushQuery = useMemo( | ||||
|     () => | ||||
|       _.debounce((query: string | undefined) => { | ||||
|       debounce((query: string | undefined) => { | ||||
|         if (query) { | ||||
|           router.query.q = query | ||||
|         } else { | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import _ from 'lodash' | ||||
| import { isEqual } from 'lodash' | ||||
| import { useMemo, useRef, useState } from 'react' | ||||
| 
 | ||||
| export const useStateCheckEquality = <T>(initialState: T) => { | ||||
|  | @ -10,7 +10,7 @@ export const useStateCheckEquality = <T>(initialState: T) => { | |||
|   const checkSetState = useMemo( | ||||
|     () => (newState: T) => { | ||||
|       const state = stateRef.current | ||||
|       if (!_.isEqual(state, newState)) { | ||||
|       if (!isEqual(state, newState)) { | ||||
|         setState(newState) | ||||
|       } | ||||
|     }, | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import _ from 'lodash' | ||||
| import { uniq } from 'lodash' | ||||
| import { useEffect, useState } from 'react' | ||||
| import { | ||||
|   Bet, | ||||
|  | @ -51,7 +51,7 @@ export const useUserBetContracts = ( | |||
|       return listenForUserBets( | ||||
|         userId, | ||||
|         (bets) => { | ||||
|           const contractIds = _.uniq(bets.map((bet) => bet.contractId)) | ||||
|           const contractIds = uniq(bets.map((bet) => bet.contractId)) | ||||
|           setContractIds(contractIds) | ||||
|           localStorage.setItem(key, JSON.stringify(contractIds)) | ||||
|         }, | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| import _ from 'lodash' | ||||
| import { useEffect, useState } from 'react' | ||||
| import { PrivateUser } from 'common/user' | ||||
| import { | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ import { | |||
|   where, | ||||
|   orderBy, | ||||
| } from 'firebase/firestore' | ||||
| import _ from 'lodash' | ||||
| import { range } from 'lodash' | ||||
| 
 | ||||
| import { db } from './init' | ||||
| import { Bet } from 'common/bet' | ||||
|  | @ -138,7 +138,7 @@ export async function getDailyBets(startTime: number, numberOfDays: number) { | |||
|   const query = getBetsQuery(startTime, startTime + DAY_IN_MS * numberOfDays) | ||||
|   const bets = await getValues<Bet>(query) | ||||
| 
 | ||||
|   const betsByDay = _.range(0, numberOfDays).map(() => [] as Bet[]) | ||||
|   const betsByDay = range(0, numberOfDays).map(() => [] as Bet[]) | ||||
|   for (const bet of bets) { | ||||
|     const dayIndex = Math.floor((bet.createdTime - startTime) / DAY_IN_MS) | ||||
|     betsByDay[dayIndex].push(bet) | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ import { | |||
|   setDoc, | ||||
|   where, | ||||
| } from 'firebase/firestore' | ||||
| import _ from 'lodash' | ||||
| import { range } from 'lodash' | ||||
| 
 | ||||
| import { getValues, listenForValues } from './utils' | ||||
| import { db } from './init' | ||||
|  | @ -117,7 +117,7 @@ export async function getDailyComments( | |||
|   ) | ||||
|   const comments = await getValues<Comment>(query) | ||||
| 
 | ||||
|   const commentsByDay = _.range(0, numberOfDays).map(() => [] as Comment[]) | ||||
|   const commentsByDay = range(0, numberOfDays).map(() => [] as Comment[]) | ||||
|   for (const comment of comments) { | ||||
|     const dayIndex = Math.floor((comment.createdTime - startTime) / DAY_IN_MS) | ||||
|     commentsByDay[dayIndex].push(comment) | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ import { | |||
|   updateDoc, | ||||
|   limit, | ||||
| } from 'firebase/firestore' | ||||
| import _ from 'lodash' | ||||
| import { range, sortBy } from 'lodash' | ||||
| 
 | ||||
| import { app } from './init' | ||||
| import { getValues, listenForValue, listenForValues } from './utils' | ||||
|  | @ -225,7 +225,7 @@ export function listenForHotContracts( | |||
|   setHotContracts: (contracts: Contract[]) => void | ||||
| ) { | ||||
|   return listenForValues<Contract>(hotContractsQuery, (contracts) => { | ||||
|     const hotContracts = _.sortBy( | ||||
|     const hotContracts = sortBy( | ||||
|       chooseRandomSubset(contracts, 4), | ||||
|       (contract) => contract.volume24Hours | ||||
|     ) | ||||
|  | @ -235,7 +235,7 @@ export function listenForHotContracts( | |||
| 
 | ||||
| export async function getHotContracts() { | ||||
|   const contracts = await getValues<Contract>(hotContractsQuery) | ||||
|   return _.sortBy( | ||||
|   return sortBy( | ||||
|     chooseRandomSubset(contracts, 10), | ||||
|     (contract) => -1 * contract.volume24Hours | ||||
|   ) | ||||
|  | @ -245,7 +245,7 @@ export async function getContractsBySlugs(slugs: string[]) { | |||
|   const q = query(contractCollection, where('slug', 'in', slugs)) | ||||
|   const snapshot = await getDocs(q) | ||||
|   const contracts = snapshot.docs.map((doc) => doc.data() as Contract) | ||||
|   return _.sortBy(contracts, (contract) => -1 * contract.volume24Hours) | ||||
|   return sortBy(contracts, (contract) => -1 * contract.volume24Hours) | ||||
| } | ||||
| 
 | ||||
| const topWeeklyQuery = query( | ||||
|  | @ -269,7 +269,7 @@ const closingSoonQuery = query( | |||
| 
 | ||||
| export async function getClosingSoonContracts() { | ||||
|   const contracts = await getValues<Contract>(closingSoonQuery) | ||||
|   return _.sortBy( | ||||
|   return sortBy( | ||||
|     chooseRandomSubset(contracts, 2), | ||||
|     (contract) => contract.closeTime | ||||
|   ) | ||||
|  | @ -295,7 +295,7 @@ export async function getDailyContracts( | |||
|   ) | ||||
|   const contracts = await getValues<Contract>(query) | ||||
| 
 | ||||
|   const contractsByDay = _.range(0, numberOfDays).map(() => [] as Contract[]) | ||||
|   const contractsByDay = range(0, numberOfDays).map(() => [] as Contract[]) | ||||
|   for (const contract of contracts) { | ||||
|     const dayIndex = Math.floor((contract.createdTime - startTime) / DAY_IN_MS) | ||||
|     contractsByDay[dayIndex].push(contract) | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ import { | |||
|   updateDoc, | ||||
|   where, | ||||
| } from 'firebase/firestore' | ||||
| import _ from 'lodash' | ||||
| import { sortBy } from 'lodash' | ||||
| import { Fold } from 'common/fold' | ||||
| import { Contract, contractCollection } from './contracts' | ||||
| import { db } from './init' | ||||
|  | @ -171,7 +171,7 @@ export async function getFoldsByTags(tags: string[]) { | |||
|     ) | ||||
|   ) | ||||
| 
 | ||||
|   return _.sortBy(folds, (fold) => -1 * fold.followCount) | ||||
|   return sortBy(folds, (fold) => -1 * fold.followCount) | ||||
| } | ||||
| 
 | ||||
| export function listenForFoldsWithTags( | ||||
|  | @ -187,7 +187,7 @@ export function listenForFoldsWithTags( | |||
|   ) | ||||
| 
 | ||||
|   return listenForValues<Fold>(q, (folds) => { | ||||
|     const sorted = _.sortBy(folds, (fold) => -1 * fold.followCount) | ||||
|     const sorted = sortBy(folds, (fold) => -1 * fold.followCount) | ||||
|     setFolds(sorted) | ||||
|   }) | ||||
| } | ||||
|  |  | |||
|  | @ -18,7 +18,7 @@ import { | |||
|   GoogleAuthProvider, | ||||
|   signInWithPopup, | ||||
| } from 'firebase/auth' | ||||
| import _ from 'lodash' | ||||
| import { range, throttle, zip } from 'lodash' | ||||
| 
 | ||||
| import { app } from './init' | ||||
| import { PrivateUser, User } from 'common/user' | ||||
|  | @ -83,7 +83,7 @@ const CACHED_USER_KEY = 'CACHED_USER_KEY' | |||
| // used to avoid weird race condition
 | ||||
| let createUserPromise: Promise<User | null> | undefined = undefined | ||||
| 
 | ||||
| const warmUpCreateUser = _.throttle(createUser, 5000 /* ms */) | ||||
| const warmUpCreateUser = throttle(createUser, 5000 /* ms */) | ||||
| 
 | ||||
| export function listenForLogin(onUser: (user: User | null) => void) { | ||||
|   const cachedUser = localStorage.getItem(CACHED_USER_KEY) | ||||
|  | @ -210,7 +210,7 @@ export async function getDailyNewUsers( | |||
|   const query = getUsersQuery(startTime, startTime + DAY_MS * numberOfDays) | ||||
|   const users = await getValues<User>(query) | ||||
| 
 | ||||
|   const usersByDay = _.range(0, numberOfDays).map(() => [] as User[]) | ||||
|   const usersByDay = range(0, numberOfDays).map(() => [] as User[]) | ||||
|   for (const user of users) { | ||||
|     const dayIndex = Math.floor((user.createdTime - startTime) / DAY_MS) | ||||
|     usersByDay[dayIndex].push(user) | ||||
|  | @ -235,5 +235,5 @@ export async function getCategoryFeeds(userId: string) { | |||
|     ) | ||||
|   ) | ||||
|   const feeds = feedData.map((data) => data?.feed ?? []) | ||||
|   return _.fromPairs(_.zip(CATEGORY_LIST, feeds) as [string, feed][]) | ||||
|   return Object.fromEntries(zip(CATEGORY_LIST, feeds) as [string, feed][]) | ||||
| } | ||||
|  |  | |||
|  | @ -6,6 +6,11 @@ module.exports = { | |||
|   experimental: { | ||||
|     externalDir: true, | ||||
|     optimizeCss: true, | ||||
|     modularizeImports: { | ||||
|       lodash: { | ||||
|         transform: 'lodash/{{member}}', | ||||
|       }, | ||||
|     }, | ||||
|   }, | ||||
|   images: { | ||||
|     domains: ['lh3.googleusercontent.com', 'i.imgur.com'], | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| import React, { useEffect, useMemo, useState } from 'react' | ||||
| import { ArrowLeftIcon } from '@heroicons/react/outline' | ||||
| import _ from 'lodash' | ||||
| import { keyBy, sortBy, groupBy, sumBy, mapValues } from 'lodash' | ||||
| 
 | ||||
| import { useContractWithPreload } from 'web/hooks/use-contract' | ||||
| import { ContractOverview } from 'web/components/contract/contract-overview' | ||||
|  | @ -250,13 +250,13 @@ function ContractLeaderboard(props: { contract: Contract; bets: Bet[] }) { | |||
| 
 | ||||
|   const { userProfits, top5Ids } = useMemo(() => { | ||||
|     // Create a map of userIds to total profits (including sales)
 | ||||
|     const betsByUser = _.groupBy(bets, 'userId') | ||||
|     const userProfits = _.mapValues(betsByUser, (bets) => | ||||
|       _.sumBy(bets, (bet) => resolvedPayout(contract, bet) - bet.amount) | ||||
|     const betsByUser = groupBy(bets, 'userId') | ||||
|     const userProfits = mapValues(betsByUser, (bets) => | ||||
|       sumBy(bets, (bet) => resolvedPayout(contract, bet) - bet.amount) | ||||
|     ) | ||||
|     // Find the 5 users with the most profits
 | ||||
|     const top5Ids = _.entries(userProfits) | ||||
|       .sort(([i1, p1], [i2, p2]) => p2 - p1) | ||||
|     const top5Ids = Object.entries(userProfits) | ||||
|       .sort(([_i1, p1], [_i2, p2]) => p2 - p1) | ||||
|       .filter(([, p]) => p > 0) | ||||
|       .slice(0, 5) | ||||
|       .map(([id]) => id) | ||||
|  | @ -267,7 +267,7 @@ function ContractLeaderboard(props: { contract: Contract; bets: Bet[] }) { | |||
|     console.log('foo') | ||||
|     if (top5Ids.length > 0) { | ||||
|       listUsers(top5Ids).then((users) => { | ||||
|         const sortedUsers = _.sortBy(users, (user) => -userProfits[user.id]) | ||||
|         const sortedUsers = sortBy(users, (user) => -userProfits[user.id]) | ||||
|         setUsers(sortedUsers) | ||||
|       }) | ||||
|     } | ||||
|  | @ -294,8 +294,8 @@ function ContractTopTrades(props: { | |||
|   comments: Comment[] | ||||
| }) { | ||||
|   const { contract, bets, comments } = props | ||||
|   const commentsById = _.keyBy(comments, 'id') | ||||
|   const betsById = _.keyBy(bets, 'id') | ||||
|   const commentsById = keyBy(comments, 'id') | ||||
|   const betsById = keyBy(bets, 'id') | ||||
| 
 | ||||
|   // If 'id2' is the sale of 'id1', both are logged with (id2 - id1) of profit
 | ||||
|   // Otherwise, we record the profit at resolution time
 | ||||
|  | @ -312,11 +312,11 @@ function ContractTopTrades(props: { | |||
|   } | ||||
| 
 | ||||
|   // Now find the betId with the highest profit
 | ||||
|   const topBetId = _.sortBy(bets, (b) => -profitById[b.id])[0]?.id | ||||
|   const topBetId = sortBy(bets, (b) => -profitById[b.id])[0]?.id | ||||
|   const topBettor = useUserById(betsById[topBetId]?.userId) | ||||
| 
 | ||||
|   // And also the commentId of the comment with the highest profit
 | ||||
|   const topCommentId = _.sortBy( | ||||
|   const topCommentId = sortBy( | ||||
|     comments, | ||||
|     (c) => c.betId && -profitById[c.betId] | ||||
|   )[0]?.id | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ import dayjs from 'dayjs' | |||
| import { usePrivateUsers, useUsers } from 'web/hooks/use-users' | ||||
| import Custom404 from './404' | ||||
| import { useContracts } from 'web/hooks/use-contracts' | ||||
| import _ from 'lodash' | ||||
| import { mapKeys } from 'lodash' | ||||
| import { useAdmin } from 'web/hooks/use-admin' | ||||
| import { contractPath } from 'web/lib/firebase/contracts' | ||||
| 
 | ||||
|  | @ -23,7 +23,7 @@ function UsersTable() { | |||
|   let privateUsers = usePrivateUsers() | ||||
| 
 | ||||
|   // Map private users by user id
 | ||||
|   const privateUsersById = _.mapKeys(privateUsers, 'id') | ||||
|   const privateUsersById = mapKeys(privateUsers, 'id') | ||||
|   console.log('private users by id', privateUsersById) | ||||
| 
 | ||||
|   // For each user, set their email from the PrivateUser
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import dayjs from 'dayjs' | ||||
| import _ from 'lodash' | ||||
| import { zip, uniq, sumBy } from 'lodash' | ||||
| import { IS_PRIVATE_MANIFOLD } from 'common/envs/constants' | ||||
| import { | ||||
|   DailyCountChart, | ||||
|  | @ -40,12 +40,12 @@ export async function getStaticPropz() { | |||
|   ) | ||||
|   const dailyCommentCounts = dailyComments.map((comments) => comments.length) | ||||
| 
 | ||||
|   const dailyUserIds = _.zip(dailyContracts, dailyBets, dailyComments).map( | ||||
|   const dailyUserIds = zip(dailyContracts, dailyBets, dailyComments).map( | ||||
|     ([contracts, bets, comments]) => { | ||||
|       const creatorIds = (contracts ?? []).map((c) => c.creatorId) | ||||
|       const betUserIds = (bets ?? []).map((bet) => bet.userId) | ||||
|       const commentUserIds = (comments ?? []).map((comment) => comment.userId) | ||||
|       return _.uniq([...creatorIds, ...betUserIds, ...commentUserIds]) | ||||
|       return uniq([...creatorIds, ...betUserIds, ...commentUserIds]) | ||||
|     } | ||||
|   ) | ||||
| 
 | ||||
|  | @ -87,7 +87,7 @@ export async function getStaticPropz() { | |||
|     for (let j = lastWeek.start; j <= lastWeek.end; j++) { | ||||
|       dailyUserIds[j].forEach((userId) => activeLastWeek.add(userId)) | ||||
|     } | ||||
|     const retainedCount = _.sumBy(Array.from(activeTwoWeeksAgo), (userId) => | ||||
|     const retainedCount = sumBy(Array.from(activeTwoWeeksAgo), (userId) => | ||||
|       activeLastWeek.has(userId) ? 1 : 0 | ||||
|     ) | ||||
|     const retainedFrac = retainedCount / activeTwoWeeksAgo.size | ||||
|  | @ -112,7 +112,7 @@ export async function getStaticPropz() { | |||
|     for (let j = lastMonth.start; j <= lastMonth.end; j++) { | ||||
|       dailyUserIds[j].forEach((userId) => activeLastMonth.add(userId)) | ||||
|     } | ||||
|     const retainedCount = _.sumBy(Array.from(activeTwoMonthsAgo), (userId) => | ||||
|     const retainedCount = sumBy(Array.from(activeTwoMonthsAgo), (userId) => | ||||
|       activeLastMonth.has(userId) ? 1 : 0 | ||||
|     ) | ||||
|     const retainedFrac = retainedCount / activeTwoMonthsAgo.size | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import _ from 'lodash' | ||||
| import { sortBy, sumBy, uniqBy } from 'lodash' | ||||
| import clsx from 'clsx' | ||||
| import { useEffect, useRef, useState } from 'react' | ||||
| import { Col } from 'web/components/layout/col' | ||||
|  | @ -41,13 +41,13 @@ function CharityPage(props: { charity: Charity }) { | |||
|   const user = useUser() | ||||
| 
 | ||||
|   const txns = useCharityTxns(charity.id) | ||||
|   const newToOld = _.sortBy(txns, (txn) => -txn.createdTime) | ||||
|   const totalRaised = _.sumBy(txns, (txn) => txn.amount) | ||||
|   const fromYou = _.sumBy( | ||||
|   const newToOld = sortBy(txns, (txn) => -txn.createdTime) | ||||
|   const totalRaised = sumBy(txns, (txn) => txn.amount) | ||||
|   const fromYou = sumBy( | ||||
|     txns.filter((txn) => txn.fromId === user?.id), | ||||
|     (txn) => txn.amount | ||||
|   ) | ||||
|   const numSupporters = _.uniqBy(txns, (txn) => txn.fromId).length | ||||
|   const numSupporters = uniqBy(txns, (txn) => txn.fromId).length | ||||
| 
 | ||||
|   const { width, height } = useWindowSize() | ||||
|   const [showConfetti, setShowConfetti] = useState(false) | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import _ from 'lodash' | ||||
| import { mapValues, groupBy, sumBy, sum, sortBy, debounce } from 'lodash' | ||||
| import { useState, useMemo } from 'react' | ||||
| import { charities, Charity as CharityType } from 'common/charity' | ||||
| import { CharityCard } from 'web/components/charity/charity-card' | ||||
|  | @ -12,11 +12,11 @@ import { formatMoney } from 'common/util/format' | |||
| 
 | ||||
| export async function getStaticProps() { | ||||
|   const txns = await getAllCharityTxns() | ||||
|   const totals = _.mapValues(_.groupBy(txns, 'toId'), (txns) => | ||||
|     _.sumBy(txns, (txn) => txn.amount) | ||||
|   const totals = mapValues(groupBy(txns, 'toId'), (txns) => | ||||
|     sumBy(txns, (txn) => txn.amount) | ||||
|   ) | ||||
|   const totalRaised = _.sum(Object.values(totals)) | ||||
|   const sortedCharities = _.sortBy(charities, [ | ||||
|   const totalRaised = sum(Object.values(totals)) | ||||
|   const sortedCharities = sortBy(charities, [ | ||||
|     (charity) => (charity.tags?.includes('Featured') ? 0 : 1), | ||||
|     (charity) => -totals[charity.id], | ||||
|   ]) | ||||
|  | @ -37,7 +37,7 @@ export default function Charity(props: { | |||
|   const { totalRaised, charities } = props | ||||
| 
 | ||||
|   const [query, setQuery] = useState('') | ||||
|   const debouncedQuery = _.debounce(setQuery, 50) | ||||
|   const debouncedQuery = debounce(setQuery, 50) | ||||
| 
 | ||||
|   const filterCharities = useMemo( | ||||
|     () => | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import _ from 'lodash' | ||||
| import { flatten, take, partition, sortBy } from 'lodash' | ||||
| 
 | ||||
| import { Fold } from 'common/fold' | ||||
| import { Comment } from 'common/comment' | ||||
|  | @ -47,8 +47,8 @@ export async function getStaticPropz(props: { params: { slugs: string[] } }) { | |||
|     contracts.map((contract) => listAllBets(contract.id)) | ||||
|   ) | ||||
| 
 | ||||
|   let activeContracts = findActiveContracts(contracts, [], _.flatten(bets), {}) | ||||
|   const [resolved, unresolved] = _.partition( | ||||
|   let activeContracts = findActiveContracts(contracts, [], flatten(bets), {}) | ||||
|   const [resolved, unresolved] = partition( | ||||
|     activeContracts, | ||||
|     ({ isResolved }) => isResolved | ||||
|   ) | ||||
|  | @ -80,8 +80,8 @@ export async function getStaticPropz(props: { params: { slugs: string[] } }) { | |||
| } | ||||
| 
 | ||||
| async function toTopUsers(userScores: { [userId: string]: number }) { | ||||
|   const topUserPairs = _.take( | ||||
|     _.sortBy(Object.entries(userScores), ([_, score]) => -1 * score), | ||||
|   const topUserPairs = take( | ||||
|     sortBy(Object.entries(userScores), ([_, score]) => -1 * score), | ||||
|     10 | ||||
|   ).filter(([_, score]) => score >= 0.5) | ||||
| 
 | ||||
|  | @ -134,7 +134,7 @@ export default function FoldPage(props: { | |||
|   const isCurator = user && fold && user.id === fold.curatorId | ||||
| 
 | ||||
|   const taggedContracts = useTaggedContracts(fold?.tags) ?? props.contracts | ||||
|   const contractsMap = _.fromPairs( | ||||
|   const contractsMap = Object.fromEntries( | ||||
|     taggedContracts.map((contract) => [contract.id, contract]) | ||||
|   ) | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import _ from 'lodash' | ||||
| import { sortBy, debounce } from 'lodash' | ||||
| import Link from 'next/link' | ||||
| import { useEffect, useState } from 'react' | ||||
| import { Fold } from 'common/fold' | ||||
|  | @ -21,7 +21,7 @@ export async function getStaticProps() { | |||
|   const curators = await Promise.all( | ||||
|     folds.map((fold) => getUser(fold.curatorId)) | ||||
|   ) | ||||
|   const curatorsDict = _.fromPairs( | ||||
|   const curatorsDict = Object.fromEntries( | ||||
|     curators.map((curator) => [curator.id, curator]) | ||||
|   ) | ||||
| 
 | ||||
|  | @ -37,7 +37,7 @@ export async function getStaticProps() { | |||
| 
 | ||||
| export default function Folds(props: { | ||||
|   folds: Fold[] | ||||
|   curatorsDict: _.Dictionary<User> | ||||
|   curatorsDict: { [k: string]: User } | ||||
| }) { | ||||
|   const [curatorsDict, setCuratorsDict] = useState(props.curatorsDict) | ||||
| 
 | ||||
|  | @ -51,7 +51,7 @@ export default function Folds(props: { | |||
|     if (newFolds.length > 0) { | ||||
|       Promise.all(newFolds.map(({ curatorId }) => getUser(curatorId))).then( | ||||
|         (newUsers) => { | ||||
|           const newUsersDict = _.fromPairs( | ||||
|           const newUsersDict = Object.fromEntries( | ||||
|             newUsers.map((user) => [user.id, user]) | ||||
|           ) | ||||
|           setCuratorsDict({ ...curatorsDict, ...newUsersDict }) | ||||
|  | @ -68,7 +68,7 @@ export default function Folds(props: { | |||
|   } | ||||
| 
 | ||||
|   // List followed folds first, then folds with the highest follower count
 | ||||
|   const matches = _.sortBy(folds, [ | ||||
|   const matches = sortBy(folds, [ | ||||
|     (fold) => !followedFoldIds.includes(fold.id), | ||||
|     (fold) => -1 * fold.followCount, | ||||
|   ]).filter( | ||||
|  | @ -79,7 +79,7 @@ export default function Folds(props: { | |||
|       check(f.lowercaseTags.map((tag) => `#${tag}`).join(' ')) | ||||
|   ) | ||||
|   // Not strictly necessary, but makes the "hold delete" experience less laggy
 | ||||
|   const debouncedQuery = _.debounce(setQuery, 50) | ||||
|   const debouncedQuery = debounce(setQuery, 50) | ||||
| 
 | ||||
|   return ( | ||||
|     <Page> | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import _ from 'lodash' | ||||
| import { sortBy } from 'lodash' | ||||
| import { GetServerSideProps } from 'next' | ||||
| import { getServerSideSitemap, ISitemapField } from 'next-sitemap' | ||||
| 
 | ||||
|  | @ -10,7 +10,7 @@ export const getServerSideProps: GetServerSideProps = async (ctx) => { | |||
|   const response = await fetch(`https://${DOMAIN}/api/v0/markets`) | ||||
| 
 | ||||
|   const liteMarkets = (await response.json()) as LiteMarket[] | ||||
|   const sortedMarkets = _.sortBy(liteMarkets, (m) => -m.volume24Hours) | ||||
|   const sortedMarkets = sortBy(liteMarkets, (m) => -m.volume24Hours) | ||||
| 
 | ||||
|   const fields = sortedMarkets.map((market) => ({ | ||||
|     // See https://www.sitemaps.org/protocol.html
 | ||||
|  |  | |||
|  | @ -2394,6 +2394,13 @@ eslint-plugin-jsx-a11y@^6.5.1: | |||
|     language-tags "^1.0.5" | ||||
|     minimatch "^3.0.4" | ||||
| 
 | ||||
| eslint-plugin-lodash@^7.4.0: | ||||
|   version "7.4.0" | ||||
|   resolved "https://registry.yarnpkg.com/eslint-plugin-lodash/-/eslint-plugin-lodash-7.4.0.tgz#14a761547f126c92ff56789662a20a44f8bb6290" | ||||
|   integrity sha512-Tl83UwVXqe1OVeBRKUeWcfg6/pCW1GTRObbdnbEJgYwjxp5Q92MEWQaH9+dmzbRt6kvYU1Mp893E79nJiCSM8A== | ||||
|   dependencies: | ||||
|     lodash "^4.17.21" | ||||
| 
 | ||||
| eslint-plugin-react-hooks@^4.5.0: | ||||
|   version "4.5.0" | ||||
|   resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.5.0.tgz#5f762dfedf8b2cf431c689f533c9d3fa5dcf25ad" | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user