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:
Marshall Polaris 2022-05-22 01:36:05 -07:00 committed by GitHub
parent 0803a15902
commit 47f10301c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
93 changed files with 378 additions and 382 deletions

View File

@ -1,4 +1,5 @@
module.exports = { module.exports = {
plugins: ['lodash'],
extends: ['eslint:recommended'], extends: ['eslint:recommended'],
env: { env: {
browser: true, browser: true,
@ -14,5 +15,6 @@ module.exports = {
rules: { rules: {
'no-unused-vars': 'off', 'no-unused-vars': 'off',
'no-constant-condition': ['error', { checkLoops: false }], 'no-constant-condition': ['error', { checkLoops: false }],
'lodash/import-scope': [2, 'member'],
}, },
} }

View File

@ -1,3 +1,4 @@
import { range } from 'lodash'
import { Bet, NumericBet } from './bet' import { Bet, NumericBet } from './bet'
import { getDpmProbability, getValueFromBucket } from './calculate-dpm' import { getDpmProbability, getValueFromBucket } from './calculate-dpm'
import { import {
@ -11,7 +12,6 @@ import {
import { User } from './user' import { User } from './user'
import { LiquidityProvision } from './liquidity-provision' import { LiquidityProvision } from './liquidity-provision'
import { noFees } from './fees' import { noFees } from './fees'
import * as _ from 'lodash'
export const FIXED_ANTE = 100 export const FIXED_ANTE = 100
@ -127,11 +127,11 @@ export function getNumericAnte(
const betShares = Math.sqrt(ante ** 2 / bucketCount) const betShares = Math.sqrt(ante ** 2 / bucketCount)
const allOutcomeShares = Object.fromEntries( const allOutcomeShares = Object.fromEntries(
_.range(0, bucketCount).map((_, i) => [i, betShares]) range(0, bucketCount).map((_, i) => [i, betShares])
) )
const allBetAmounts = Object.fromEntries( const allBetAmounts = Object.fromEntries(
_.range(0, bucketCount).map((_, i) => [i, betAnte]) range(0, bucketCount).map((_, i) => [i, betAnte])
) )
const anteBet: NumericBet = { const anteBet: NumericBet = {

View File

@ -1,4 +1,4 @@
import * as _ from 'lodash' import { sum, groupBy, mapValues, sumBy } from 'lodash'
import { Binary, CPMM, FullContract } from './contract' import { Binary, CPMM, FullContract } from './contract'
import { CREATOR_FEE, Fees, LIQUIDITY_FEE, noFees, PLATFORM_FEE } from './fees' import { CREATOR_FEE, Fees, LIQUIDITY_FEE, noFees, PLATFORM_FEE } from './fees'
@ -278,16 +278,16 @@ export function getCpmmLiquidityPoolWeights(
return liquidity return liquidity
}) })
const shareSum = _.sum(liquidityShares) const shareSum = sum(liquidityShares)
const weights = liquidityShares.map((s, i) => ({ const weights = liquidityShares.map((s, i) => ({
weight: s / shareSum, weight: s / shareSum,
providerId: liquidities[i].userId, providerId: liquidities[i].userId,
})) }))
const userWeights = _.groupBy(weights, (w) => w.providerId) const userWeights = groupBy(weights, (w) => w.providerId)
const totalUserWeights = _.mapValues(userWeights, (userWeight) => const totalUserWeights = mapValues(userWeights, (userWeight) =>
_.sumBy(userWeight, (w) => w.weight) sumBy(userWeight, (w) => w.weight)
) )
return totalUserWeights return totalUserWeights
} }

View File

@ -1,5 +1,4 @@
import * as _ from 'lodash' import { cloneDeep, range, sum, sumBy, sortBy, mapValues } from 'lodash'
import { Bet, NumericBet } from './bet' import { Bet, NumericBet } from './bet'
import { import {
Binary, Binary,
@ -24,7 +23,7 @@ export function getDpmOutcomeProbability(
}, },
outcome: string 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 const shares = totalShares[outcome] ?? 0
return shares ** 2 / squareSum return shares ** 2 / squareSum
} }
@ -32,8 +31,8 @@ export function getDpmOutcomeProbability(
export function getDpmOutcomeProbabilities(totalShares: { export function getDpmOutcomeProbabilities(totalShares: {
[outcome: string]: number [outcome: string]: number
}) { }) {
const squareSum = _.sumBy(Object.values(totalShares), (shares) => shares ** 2) const squareSum = sumBy(Object.values(totalShares), (shares) => shares ** 2)
return _.mapValues(totalShares, (shares) => shares ** 2 / squareSum) return mapValues(totalShares, (shares) => shares ** 2 / squareSum)
} }
export function getNumericBets( export function getNumericBets(
@ -44,20 +43,20 @@ export function getNumericBets(
) { ) {
const { bucketCount } = contract const { bucketCount } = contract
const bucketNumber = parseInt(bucket) const bucketNumber = parseInt(bucket)
const buckets = _.range(0, bucketCount) const buckets = range(0, bucketCount)
const mean = bucketNumber / bucketCount const mean = bucketNumber / bucketCount
const allDensities = buckets.map((i) => const allDensities = buckets.map((i) =>
normpdf(i / bucketCount, mean, variance) normpdf(i / bucketCount, mean, variance)
) )
const densitySum = _.sum(allDensities) const densitySum = sum(allDensities)
const rawBetAmounts = allDensities const rawBetAmounts = allDensities
.map((d) => (d / densitySum) * betAmount) .map((d) => (d / densitySum) * betAmount)
.map((x) => (x >= 1 / bucketCount ? x : 0)) .map((x) => (x >= 1 / bucketCount ? x : 0))
const rawSum = _.sum(rawBetAmounts) const rawSum = sum(rawBetAmounts)
const scaledBetAmounts = rawBetAmounts.map((x) => (x / rawSum) * betAmount) const scaledBetAmounts = rawBetAmounts.map((x) => (x / rawSum) * betAmount)
const bets = scaledBetAmounts const bets = scaledBetAmounts
@ -90,26 +89,24 @@ export const getValueFromBucket = (
export const getExpectedValue = (contract: NumericContract) => { export const getExpectedValue = (contract: NumericContract) => {
const { bucketCount, min, max, totalShares } = contract const { bucketCount, min, max, totalShares } = contract
const totalShareSum = _.sumBy( const totalShareSum = sumBy(
Object.values(totalShares), Object.values(totalShares),
(shares) => shares ** 2 (shares) => shares ** 2
) )
const probs = _.range(0, bucketCount).map( const probs = range(0, bucketCount).map(
(i) => totalShares[i] ** 2 / totalShareSum (i) => totalShares[i] ** 2 / totalShareSum
) )
const values = _.range(0, bucketCount).map( const values = range(0, bucketCount).map(
(i) => (i) =>
// use mid point within bucket // use mid point within bucket
0.5 * (min + (i / bucketCount) * (max - min)) + 0.5 * (min + (i / bucketCount) * (max - min)) +
0.5 * (min + ((i + 1) / bucketCount) * (max - min)) 0.5 * (min + ((i + 1) / bucketCount) * (max - min))
) )
const weightedValues = _.range(0, bucketCount).map( const weightedValues = range(0, bucketCount).map((i) => probs[i] * values[i])
(i) => probs[i] * values[i]
)
const expectation = _.sum(weightedValues) const expectation = sum(weightedValues)
const rounded = Math.round(expectation * 1e2) / 1e2 const rounded = Math.round(expectation * 1e2) / 1e2
return rounded return rounded
} }
@ -150,7 +147,7 @@ export function calculateDpmShares(
bet: number, bet: number,
betChoice: string 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 shares = totalShares[betChoice] ?? 0
const c = 2 * bet * Math.sqrt(squareSum) const c = 2 * bet * Math.sqrt(squareSum)
@ -166,9 +163,9 @@ export function calculateNumericDpmShares(
) { ) {
const shares: number[] = [] const shares: number[] = []
totalShares = _.cloneDeep(totalShares) totalShares = cloneDeep(totalShares)
const order = _.sortBy( const order = sortBy(
bets.map(([, amount], i) => [amount, i]), bets.map(([, amount], i) => [amount, i]),
([amount]) => amount ([amount]) => amount
).map(([, i]) => i) ).map(([, i]) => i)
@ -190,11 +187,11 @@ export function calculateDpmRawShareValue(
betChoice: string betChoice: string
) { ) {
const currentValue = Math.sqrt( const currentValue = Math.sqrt(
_.sumBy(Object.values(totalShares), (shares) => shares ** 2) sumBy(Object.values(totalShares), (shares) => shares ** 2)
) )
const postSaleValue = Math.sqrt( const postSaleValue = Math.sqrt(
_.sumBy(Object.keys(totalShares), (outcome) => sumBy(Object.keys(totalShares), (outcome) =>
outcome === betChoice outcome === betChoice
? Math.max(0, totalShares[outcome] - shares) ** 2 ? Math.max(0, totalShares[outcome] - shares) ** 2
: totalShares[outcome] ** 2 : totalShares[outcome] ** 2
@ -214,12 +211,12 @@ export function calculateDpmMoneyRatio(
const p = getDpmOutcomeProbability(totalShares, outcome) const p = getDpmOutcomeProbability(totalShares, outcome)
const actual = _.sum(Object.values(pool)) - shareValue const actual = sum(Object.values(pool)) - shareValue
const betAmount = p * amount const betAmount = p * amount
const expected = const expected =
_.sumBy( sumBy(
Object.keys(totalBets), Object.keys(totalBets),
(outcome) => (outcome) =>
getDpmOutcomeProbability(totalShares, outcome) * getDpmOutcomeProbability(totalShares, outcome) *
@ -271,8 +268,8 @@ export function calculateDpmCancelPayout(
bet: Bet bet: Bet
) { ) {
const { totalBets, pool } = contract const { totalBets, pool } = contract
const betTotal = _.sum(Object.values(totalBets)) const betTotal = sum(Object.values(totalBets))
const poolTotal = _.sum(Object.values(pool)) const poolTotal = sum(Object.values(pool))
return (bet.amount / betTotal) * poolTotal return (bet.amount / betTotal) * poolTotal
} }
@ -295,7 +292,7 @@ export function calculateStandardDpmPayout(
const { totalShares, phantomShares, pool } = contract const { totalShares, phantomShares, pool } = contract
if (!totalShares[outcome]) return 0 if (!totalShares[outcome]) return 0
const poolTotal = _.sum(Object.values(pool)) const poolTotal = sum(Object.values(pool))
const total = const total =
totalShares[outcome] - (phantomShares ? phantomShares[outcome] : 0) totalShares[outcome] - (phantomShares ? phantomShares[outcome] : 0)
@ -356,19 +353,19 @@ function calculateMktDpmPayout(
let probs: { [outcome: string]: number } let probs: { [outcome: string]: number }
if (resolutions) { if (resolutions) {
const probTotal = _.sum(Object.values(resolutions)) const probTotal = sum(Object.values(resolutions))
probs = _.mapValues( probs = mapValues(
totalShares, totalShares,
(_, outcome) => (resolutions[outcome] ?? 0) / probTotal (_, outcome) => (resolutions[outcome] ?? 0) / probTotal
) )
} else { } else {
const squareSum = _.sum( const squareSum = sum(
Object.values(totalShares).map((shares) => shares ** 2) 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] return probs[outcome] * totalShares[outcome]
}) })
@ -376,7 +373,7 @@ function calculateMktDpmPayout(
const poolFrac = const poolFrac =
outcomeType === 'NUMERIC' outcomeType === 'NUMERIC'
? _.sumBy( ? sumBy(
Object.keys((bet as NumericBet).allOutcomeShares ?? {}), Object.keys((bet as NumericBet).allOutcomeShares ?? {}),
(outcome) => { (outcome) => {
return ( return (
@ -387,7 +384,7 @@ function calculateMktDpmPayout(
) )
: (probs[outcome] * shares) / weightedShareTotal : (probs[outcome] * shares) / weightedShareTotal
const totalPool = _.sum(Object.values(pool)) const totalPool = sum(Object.values(pool))
const winnings = poolFrac * totalPool const winnings = poolFrac * totalPool
return deductDpmFees(amount, winnings) return deductDpmFees(amount, winnings)
} }

View File

@ -1,4 +1,4 @@
import * as _ from 'lodash' import { maxBy } from 'lodash'
import { Bet } from './bet' import { Bet } from './bet'
import { import {
calculateCpmmSale, calculateCpmmSale,
@ -180,7 +180,7 @@ export function getContractBetNullMetrics() {
export function getTopAnswer(contract: FreeResponseContract) { export function getTopAnswer(contract: FreeResponseContract) {
const { answers } = contract const { answers } = contract
const top = _.maxBy( const top = maxBy(
answers?.map((answer) => ({ answers?.map((answer) => ({
answer, answer,
prob: getOutcomeProbability(contract, answer.id), prob: getOutcomeProbability(contract, answer.id),

View File

@ -1,4 +1,3 @@
import * as _ from 'lodash'
import { Answer } from './answer' import { Answer } from './answer'
import { Fees } from './fees' import { Fees } from './fees'

View File

@ -1,4 +1,4 @@
import * as _ from 'lodash' import { sumBy } from 'lodash'
import { Bet, MAX_LOAN_PER_CONTRACT, NumericBet } from './bet' import { Bet, MAX_LOAN_PER_CONTRACT, NumericBet } from './bet'
import { import {
@ -210,7 +210,7 @@ export const getNumericBetsInfo = (
export const getLoanAmount = (yourBets: Bet[], newBetAmount: number) => { export const getLoanAmount = (yourBets: Bet[], newBetAmount: number) => {
const openBets = yourBets.filter((bet) => !bet.isSold && !bet.sale) 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( const loanAmount = Math.min(
newBetAmount, newBetAmount,
MAX_LOAN_PER_CONTRACT - prevLoanAmount MAX_LOAN_PER_CONTRACT - prevLoanAmount

View File

@ -1,5 +1,4 @@
import * as _ from 'lodash' import { range } from 'lodash'
import { PHANTOM_ANTE } from './antes' import { PHANTOM_ANTE } from './antes'
import { import {
Binary, Binary,
@ -131,7 +130,7 @@ const getNumericProps = (
min: number, min: number,
max: 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 betAnte = ante / bucketCount
const pool = Object.fromEntries(buckets.map((answer) => [answer, betAnte])) const pool = Object.fromEntries(buckets.map((answer) => [answer, betAnte]))

View File

@ -3,6 +3,7 @@
"version": "1.0.0", "version": "1.0.0",
"private": true, "private": true,
"scripts": {}, "scripts": {},
"sideEffects": false,
"dependencies": { "dependencies": {
"lodash": "4.17.21" "lodash": "4.17.21"
}, },

View File

@ -1,4 +1,4 @@
import * as _ from 'lodash' import { sum, groupBy, sumBy, mapValues } from 'lodash'
import { Bet, NumericBet } from './bet' import { Bet, NumericBet } from './bet'
import { deductDpmFees, getDpmProbability } from './calculate-dpm' import { deductDpmFees, getDpmProbability } from './calculate-dpm'
@ -17,10 +17,10 @@ export const getDpmCancelPayouts = (
bets: Bet[] bets: Bet[]
) => { ) => {
const { pool } = contract const { pool } = contract
const poolTotal = _.sum(Object.values(pool)) const poolTotal = sum(Object.values(pool))
console.log('resolved N/A, pool M$', poolTotal) 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) => ({ const payouts = bets.map((bet) => ({
userId: bet.userId, userId: bet.userId,
@ -42,8 +42,8 @@ export const getDpmStandardPayouts = (
) => { ) => {
const winningBets = bets.filter((bet) => bet.outcome === outcome) const winningBets = bets.filter((bet) => bet.outcome === outcome)
const poolTotal = _.sum(Object.values(contract.pool)) const poolTotal = sum(Object.values(contract.pool))
const totalShares = _.sumBy(winningBets, (b) => b.shares) const totalShares = sumBy(winningBets, (b) => b.shares)
const payouts = winningBets.map(({ userId, amount, shares }) => { const payouts = winningBets.map(({ userId, amount, shares }) => {
const winnings = (shares / totalShares) * poolTotal const winnings = (shares / totalShares) * poolTotal
@ -54,7 +54,7 @@ export const getDpmStandardPayouts = (
return { userId, profit, payout } 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 creatorFee = DPM_CREATOR_FEE * profits
const platformFee = DPM_PLATFORM_FEE * profits const platformFee = DPM_PLATFORM_FEE * profits
@ -93,10 +93,10 @@ export const getNumericDpmPayouts = (
contract: FullContract<DPM, any>, contract: FullContract<DPM, any>,
bets: NumericBet[] 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 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( const payouts = winningBets.map(
({ userId, allBetAmounts, allOutcomeShares }) => { ({ 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 creatorFee = DPM_CREATOR_FEE * profits
const platformFee = DPM_PLATFORM_FEE * profits const platformFee = DPM_PLATFORM_FEE * profits
@ -156,7 +156,7 @@ export const getDpmMktPayouts = (
? getDpmProbability(contract.totalShares) ? getDpmProbability(contract.totalShares)
: resolutionProbability : resolutionProbability
const weightedShareTotal = _.sumBy(bets, (b) => const weightedShareTotal = sumBy(bets, (b) =>
b.outcome === 'YES' ? p * b.shares : (1 - p) * b.shares b.outcome === 'YES' ? p * b.shares : (1 - p) * b.shares
) )
@ -170,7 +170,7 @@ export const getDpmMktPayouts = (
return { userId, profit, payout } 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 creatorFee = DPM_CREATOR_FEE * profits
const platformFee = DPM_PLATFORM_FEE * profits const platformFee = DPM_PLATFORM_FEE * profits
@ -210,15 +210,15 @@ export const getPayoutsMultiOutcome = (
contract: FullContract<DPM, Multi | FreeResponse>, contract: FullContract<DPM, Multi | FreeResponse>,
bets: Bet[] 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 winningBets = bets.filter((bet) => resolutions[bet.outcome])
const betsByOutcome = _.groupBy(winningBets, (bet) => bet.outcome) const betsByOutcome = groupBy(winningBets, (bet) => bet.outcome)
const sharesByOutcome = _.mapValues(betsByOutcome, (bets) => const sharesByOutcome = mapValues(betsByOutcome, (bets) =>
_.sumBy(bets, (bet) => bet.shares) 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 payouts = winningBets.map(({ userId, outcome, amount, shares }) => {
const prob = resolutions[outcome] / probTotal const prob = resolutions[outcome] / probTotal
@ -229,7 +229,7 @@ export const getPayoutsMultiOutcome = (
return { userId, profit, payout } 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 creatorFee = DPM_CREATOR_FEE * profits
const platformFee = DPM_PLATFORM_FEE * profits const platformFee = DPM_PLATFORM_FEE * profits

View File

@ -1,4 +1,4 @@
import * as _ from 'lodash' import { sum } from 'lodash'
import { Bet } from './bet' import { Bet } from './bet'
import { getProbability } from './calculate' import { getProbability } from './calculate'
@ -50,7 +50,7 @@ export const getStandardFixedPayouts = (
'pool', 'pool',
contract.pool[outcome], contract.pool[outcome],
'payouts', 'payouts',
_.sum(payouts), sum(payouts),
'creator fee', 'creator fee',
creatorPayout creatorPayout
) )
@ -105,7 +105,7 @@ export const getMktFixedPayouts = (
'pool', 'pool',
p * contract.pool.YES + (1 - p) * contract.pool.NO, p * contract.pool.YES + (1 - p) * contract.pool.NO,
'payouts', 'payouts',
_.sum(payouts), sum(payouts),
'creator fee', 'creator fee',
creatorPayout creatorPayout
) )

View File

@ -1,4 +1,4 @@
import * as _ from 'lodash' import { sumBy, groupBy, mapValues } from 'lodash'
import { Bet, NumericBet } from './bet' import { Bet, NumericBet } from './bet'
import { import {
@ -32,16 +32,19 @@ export type Payout = {
export const getLoanPayouts = (bets: Bet[]): Payout[] => { export const getLoanPayouts = (bets: Bet[]): Payout[] => {
const betsWithLoans = bets.filter((bet) => bet.loanAmount) const betsWithLoans = bets.filter((bet) => bet.loanAmount)
const betsByUser = _.groupBy(betsWithLoans, (bet) => bet.userId) const betsByUser = groupBy(betsWithLoans, (bet) => bet.userId)
const loansByUser = _.mapValues(betsByUser, (bets) => const loansByUser = mapValues(betsByUser, (bets) =>
_.sumBy(bets, (bet) => -(bet.loanAmount ?? 0)) 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[]) => { export const groupPayoutsByUser = (payouts: Payout[]) => {
const groups = _.groupBy(payouts, (payout) => payout.userId) const groups = groupBy(payouts, (payout) => payout.userId)
return _.mapValues(groups, (group) => _.sumBy(group, (g) => g.payout)) return mapValues(groups, (group) => sumBy(group, (g) => g.payout))
} }
export type PayoutInfo = { export type PayoutInfo = {

View File

@ -1,4 +1,4 @@
import * as _ from 'lodash' import { union, sum, sumBy, sortBy, groupBy, mapValues } from 'lodash'
import { Bet } from './bet' import { Bet } from './bet'
import { Contract } from './contract' import { Contract } from './contract'
import { ClickEvent } from './tracking' import { ClickEvent } from './tracking'
@ -21,13 +21,13 @@ export const getRecommendedContracts = (
const yourWordFrequency = contractsToWordFrequency(yourContracts) const yourWordFrequency = contractsToWordFrequency(yourContracts)
const otherWordFrequency = contractsToWordFrequency(notYourContracts) const otherWordFrequency = contractsToWordFrequency(notYourContracts)
const words = _.union( const words = union(
Object.keys(yourWordFrequency), Object.keys(yourWordFrequency),
Object.keys(otherWordFrequency) Object.keys(otherWordFrequency)
) )
const yourWeightedFrequency = _.fromPairs( const yourWeightedFrequency = Object.fromEntries(
_.map(words, (word) => { words.map((word) => {
const [yourFreq, otherFreq] = [ const [yourFreq, otherFreq] = [
yourWordFrequency[word] ?? 0, yourWordFrequency[word] ?? 0,
otherWordFrequency[word] ?? 0, otherWordFrequency[word] ?? 0,
@ -47,7 +47,7 @@ export const getRecommendedContracts = (
const scoredContracts = contracts.map((contract) => { const scoredContracts = contracts.map((contract) => {
const wordFrequency = contractToWordFrequency(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 wordFreq = wordFrequency[word] ?? 0
const weight = yourWeightedFrequency[word] ?? 0 const weight = yourWeightedFrequency[word] ?? 0
return wordFreq * weight 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 (scored) => scored.contract
) )
} }
@ -87,8 +87,8 @@ const getWordsCount = (text: string) => {
} }
const toFrequency = (counts: { [word: string]: number }) => { const toFrequency = (counts: { [word: string]: number }) => {
const total = _.sum(Object.values(counts)) const total = sum(Object.values(counts))
return _.mapValues(counts, (count) => count / total) return mapValues(counts, (count) => count / total)
} }
const contractToWordFrequency = (contract: Contract) => const contractToWordFrequency = (contract: Contract) =>
@ -108,8 +108,8 @@ export const getWordScores = (
clicks: ClickEvent[], clicks: ClickEvent[],
bets: Bet[] bets: Bet[]
) => { ) => {
const contractClicks = _.groupBy(clicks, (click) => click.contractId) const contractClicks = groupBy(clicks, (click) => click.contractId)
const contractBets = _.groupBy(bets, (bet) => bet.contractId) const contractBets = groupBy(bets, (bet) => bet.contractId)
const yourContracts = contracts.filter( const yourContracts = contracts.filter(
(c) => (c) =>
@ -117,9 +117,7 @@ export const getWordScores = (
) )
const yourTfIdf = calculateContractTfIdf(yourContracts) const yourTfIdf = calculateContractTfIdf(yourContracts)
const contractWordScores = _.mapValues( const contractWordScores = mapValues(yourTfIdf, (wordsTfIdf, contractId) => {
yourTfIdf,
(wordsTfIdf, contractId) => {
const viewCount = contractViewCounts[contractId] ?? 0 const viewCount = contractViewCounts[contractId] ?? 0
const clickCount = contractClicks[contractId]?.length ?? 0 const clickCount = contractClicks[contractId]?.length ?? 0
const betCount = contractBets[contractId]?.length ?? 0 const betCount = contractBets[contractId]?.length ?? 0
@ -128,14 +126,13 @@ export const getWordScores = (
-1 * Math.log(viewCount + 1) + -1 * Math.log(viewCount + 1) +
10 * Math.log(betCount + clickCount / 4 + 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 wordScores = Object.values(contractWordScores).reduce(addObjects, {})
const minScore = Math.min(...Object.values(wordScores)) const minScore = Math.min(...Object.values(wordScores))
const maxScore = Math.max(...Object.values(wordScores)) const maxScore = Math.max(...Object.values(wordScores))
const normalizedWordScores = _.mapValues( const normalizedWordScores = mapValues(
wordScores, wordScores,
(score) => (score - minScore) / (maxScore - minScore) (score) => (score - minScore) / (maxScore - minScore)
) )
@ -156,7 +153,7 @@ export function getContractScore(
if (Object.keys(wordScores).length === 0) return 1 if (Object.keys(wordScores).length === 0) return 1
const wordFrequency = contractToWordFrequency(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 wordFreq = wordFrequency[word] ?? 0
const weight = wordScores[word] ?? 0 const weight = wordScores[word] ?? 0
return wordFreq * weight 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) Math.log(contracts.length / count)
) )
const contractWordsTfIdf = _.map(contractFreq, (wordFreq) => const contractWordsTfIdf = contractFreq.map((wordFreq) =>
_.mapValues(wordFreq, (freq, word) => freq * wordIdf[word]) 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]]))
} }

View File

@ -1,13 +1,13 @@
import * as _ from 'lodash' import { groupBy, sumBy, mapValues, partition } from 'lodash'
import { Bet } from './bet' import { Bet } from './bet'
import { Binary, Contract, FullContract } from './contract' import { Binary, Contract, FullContract } from './contract'
import { getPayouts } from './payouts' import { getPayouts } from './payouts'
export function scoreCreators(contracts: Contract[], bets: Bet[][]) { export function scoreCreators(contracts: Contract[], bets: Bet[][]) {
const creatorScore = _.mapValues( const creatorScore = mapValues(
_.groupBy(contracts, ({ creatorId }) => creatorId), groupBy(contracts, ({ creatorId }) => creatorId),
(contracts) => _.sumBy(contracts, ({ pool }) => pool.YES + pool.NO) (contracts) => sumBy(contracts, ({ pool }) => pool.YES + pool.NO)
) )
return creatorScore return creatorScore
@ -30,7 +30,7 @@ export function scoreUsersByContract(
) { ) {
const { resolution, resolutionProbability } = contract const { resolution, resolutionProbability } = contract
const [closedBets, openBets] = _.partition( const [closedBets, openBets] = partition(
bets, bets,
(bet) => bet.isSold || bet.sale (bet) => bet.isSold || bet.sale
) )
@ -58,9 +58,9 @@ export function scoreUsersByContract(
const netPayouts = [...resolvePayouts, ...salePayouts, ...investments] const netPayouts = [...resolvePayouts, ...salePayouts, ...investments]
const userScore = _.mapValues( const userScore = mapValues(
_.groupBy(netPayouts, (payout) => payout.userId), groupBy(netPayouts, (payout) => payout.userId),
(payouts) => _.sumBy(payouts, ({ payout }) => payout) (payouts) => sumBy(payouts, ({ payout }) => payout)
) )
return userScore return userScore

View File

@ -1,4 +1,4 @@
import * as _ from 'lodash' import { union } from 'lodash'
export const removeUndefinedProps = <T>(obj: T): T => { export const removeUndefinedProps = <T>(obj: T): T => {
let newObj: any = {} let newObj: any = {}
@ -14,7 +14,7 @@ export const addObjects = <T extends { [key: string]: number }>(
obj1: T, obj1: T,
obj2: 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 const newObj = {} as any
for (let key of keys) { for (let key of keys) {

View File

@ -1,4 +1,5 @@
module.exports = { module.exports = {
plugins: ['lodash'],
extends: ['eslint:recommended'], extends: ['eslint:recommended'],
ignorePatterns: ['lib'], ignorePatterns: ['lib'],
env: { env: {
@ -17,5 +18,6 @@ module.exports = {
rules: { rules: {
'no-unused-vars': 'off', 'no-unused-vars': 'off',
'no-constant-condition': ['error', { checkLoops: false }], 'no-constant-condition': ['error', { checkLoops: false }],
'lodash/import-scope': [2, 'member'],
}, },
} }

View File

@ -1,6 +1,5 @@
import * as functions from 'firebase-functions' import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { getUser } from './utils' import { getUser } from './utils'
import { Contract } from '../../common/contract' 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' } return { status: 'error', message: 'About must be a string' }
about = about.trim().slice(0, 140) about = about.trim().slice(0, 140)
if (!_.isArray(tags)) if (!Array.isArray(tags))
return { status: 'error', message: 'Tags must be an array of strings' } return { status: 'error', message: 'Tags must be an array of strings' }
console.log( console.log(

View File

@ -1,5 +1,3 @@
import * as _ from 'lodash'
import { DOMAIN, PROJECT_ID } from '../../common/envs/constants' import { DOMAIN, PROJECT_ID } from '../../common/envs/constants'
import { Answer } from '../../common/answer' import { Answer } from '../../common/answer'
import { Bet } from '../../common/bet' import { Bet } from '../../common/bet'

View File

@ -1,6 +1,5 @@
import * as functions from 'firebase-functions' import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { getContract } from './utils' import { getContract } from './utils'
import { Bet } from '../../common/bet' import { Bet } from '../../common/bet'

View File

@ -1,6 +1,6 @@
import * as functions from 'firebase-functions' import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash' import { uniq } from 'lodash'
import { getContract, getUser, getValues } from './utils' import { getContract, getUser, getValues } from './utils'
import { Comment } from '../../common/comment' import { Comment } from '../../common/comment'
@ -60,7 +60,7 @@ export const onCreateComment = functions.firestore
firestore.collection('contracts').doc(contractId).collection('comments') firestore.collection('contracts').doc(contractId).collection('comments')
) )
const recipientUserIds = _.uniq([ const recipientUserIds = uniq([
contract.creatorId, contract.creatorId,
...comments.map((comment) => comment.userId), ...comments.map((comment) => comment.userId),
]).filter((id) => id !== comment.userId) ]).filter((id) => id !== comment.userId)

View File

@ -1,5 +1,5 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash' import { partition, sumBy } from 'lodash'
import { Bet } from '../../common/bet' import { Bet } from '../../common/bet'
import { getProbability } from '../../common/calculate' import { getProbability } from '../../common/calculate'
@ -25,14 +25,14 @@ export const redeemShares = async (userId: string, contractId: string) => {
.where('userId', '==', userId) .where('userId', '==', userId)
) )
const bets = betsSnap.docs.map((doc) => doc.data() as Bet) const bets = betsSnap.docs.map((doc) => doc.data() as Bet)
const [yesBets, noBets] = _.partition(bets, (b) => b.outcome === 'YES') const [yesBets, noBets] = partition(bets, (b) => b.outcome === 'YES')
const yesShares = _.sumBy(yesBets, (b) => b.shares) const yesShares = sumBy(yesBets, (b) => b.shares)
const noShares = _.sumBy(noBets, (b) => b.shares) const noShares = sumBy(noBets, (b) => b.shares)
const amount = Math.min(yesShares, noShares) const amount = Math.min(yesShares, noShares)
if (amount <= 0) return 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 loanPaid = Math.min(prevLoanAmount, amount)
const netAmount = amount - loanPaid const netAmount = amount - loanPaid

View File

@ -1,6 +1,6 @@
import * as functions from 'firebase-functions' import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash' import { difference, uniq, mapValues, groupBy, sumBy } from 'lodash'
import { Contract } from '../../common/contract' import { Contract } from '../../common/contract'
import { User } from '../../common/user' import { User } from '../../common/user'
@ -187,13 +187,13 @@ const sendResolutionEmails = async (
resolutionProbability?: number, resolutionProbability?: number,
resolutions?: { [outcome: string]: number } resolutions?: { [outcome: string]: number }
) => { ) => {
const nonWinners = _.difference( const nonWinners = difference(
_.uniq(openBets.map(({ userId }) => userId)), uniq(openBets.map(({ userId }) => userId)),
Object.keys(userPayouts) Object.keys(userPayouts)
) )
const investedByUser = _.mapValues( const investedByUser = mapValues(
_.groupBy(openBets, (bet) => bet.userId), groupBy(openBets, (bet) => bet.userId),
(bets) => _.sumBy(bets, (bet) => bet.amount) (bets) => sumBy(bets, (bet) => bet.amount)
) )
const emailPayouts = [ const emailPayouts = [
...Object.entries(userPayouts), ...Object.entries(userPayouts),

View File

@ -1,5 +1,4 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { initAdmin } from './script-init' import { initAdmin } from './script-init'
initAdmin() initAdmin()

View File

@ -1,5 +1,4 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { initAdmin } from './script-init' import { initAdmin } from './script-init'
initAdmin() initAdmin()

View File

@ -1,5 +1,5 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash' import { sortBy } from 'lodash'
import { initAdmin } from './script-init' import { initAdmin } from './script-init'
initAdmin() initAdmin()
@ -20,7 +20,7 @@ async function migrateContract(
.get() .get()
.then((snap) => snap.docs.map((bet) => bet.data() as Bet)) .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) { if (lastBet) {
const probAfter = getDpmProbability(contract.totalShares) const probAfter = getDpmProbability(contract.totalShares)

View File

@ -1,5 +1,4 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { initAdmin } from './script-init' import { initAdmin } from './script-init'
initAdmin() initAdmin()

View File

@ -1,5 +1,4 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import * as fs from 'fs' import * as fs from 'fs'
import { initAdmin } from './script-init' import { initAdmin } from './script-init'

View File

@ -1,5 +1,5 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash' import { uniq } from 'lodash'
import { initAdmin } from './script-init' import { initAdmin } from './script-init'
initAdmin() initAdmin()
@ -19,7 +19,7 @@ async function lowercaseFoldTags() {
const foldRef = firestore.doc(`folds/${fold.id}`) const foldRef = firestore.doc(`folds/${fold.id}`)
const { tags } = fold 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) console.log('Adding lowercase tags', fold.slug, lowercaseTags)

View File

@ -1,5 +1,4 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { initAdmin } from './script-init' import { initAdmin } from './script-init'
initAdmin() initAdmin()

View File

@ -1,5 +1,5 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash' import { sumBy } from 'lodash'
import { initAdmin } from './script-init' import { initAdmin } from './script-init'
initAdmin() initAdmin()
@ -25,8 +25,8 @@ async function migrateContract(contractRef: DocRef, contract: Contract) {
.then((snap) => snap.docs.map((bet) => bet.data() as Bet)) .then((snap) => snap.docs.map((bet) => bet.data() as Bet))
const totalShares = { const totalShares = {
YES: _.sumBy(bets, (bet) => (bet.outcome === 'YES' ? bet.shares : 0)), YES: sumBy(bets, (bet) => (bet.outcome === 'YES' ? bet.shares : 0)),
NO: _.sumBy(bets, (bet) => (bet.outcome === 'NO' ? bet.shares : 0)), NO: sumBy(bets, (bet) => (bet.outcome === 'NO' ? bet.shares : 0)),
} }
await contractRef.update({ totalShares }) await contractRef.update({ totalShares })

View File

@ -1,5 +1,5 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash' import { sortBy } from 'lodash'
import { initAdmin } from './script-init' import { initAdmin } from './script-init'
initAdmin() initAdmin()
@ -48,7 +48,7 @@ async function recalculateContract(contractRef: DocRef, isCommit = false) {
const betsRef = contractRef.collection('bets') const betsRef = contractRef.collection('bets')
const betDocs = await transaction.get(betsRef) const betDocs = await transaction.get(betsRef)
const bets = _.sortBy( const bets = sortBy(
betDocs.docs.map((d) => d.data() as Bet), betDocs.docs.map((d) => d.data() as Bet),
(b) => b.createdTime (b) => b.createdTime
) )

View File

@ -1,5 +1,5 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash' import { sortBy, sumBy } from 'lodash'
import { initAdmin } from './script-init' import { initAdmin } from './script-init'
initAdmin() initAdmin()
@ -35,7 +35,7 @@ async function recalculateContract(
const contract = contractDoc.data() as FullContract<DPM, Binary> const contract = contractDoc.data() as FullContract<DPM, Binary>
const betDocs = await transaction.get(contractRef.collection('bets')) const betDocs = await transaction.get(contractRef.collection('bets'))
const bets = _.sortBy( const bets = sortBy(
betDocs.docs.map((d) => d.data() as Bet), betDocs.docs.map((d) => d.data() as Bet),
(b) => b.createdTime (b) => b.createdTime
) )
@ -43,8 +43,8 @@ async function recalculateContract(
const phantomAnte = startPool.YES + startPool.NO const phantomAnte = startPool.YES + startPool.NO
const leftovers = const leftovers =
_.sumBy(bets, (b) => b.amount) - sumBy(bets, (b) => b.amount) -
_.sumBy(bets, (b) => { sumBy(bets, (b) => {
if (!b.sale) return b.amount if (!b.sale) return b.amount
const soldBet = bets.find((bet) => bet.id === b.sale?.betId) const soldBet = bets.find((bet) => bet.id === b.sale?.betId)
return soldBet?.amount || 0 return soldBet?.amount || 0

View File

@ -1,5 +1,5 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash' import { flatten, groupBy, sumBy, mapValues } from 'lodash'
import { initAdmin } from './script-init' import { initAdmin } from './script-init'
initAdmin() initAdmin()
@ -35,12 +35,12 @@ async function checkIfPayOutAgain(contractRef: DocRef, contract: Contract) {
) )
const loanPayouts = getLoanPayouts(openBets) const loanPayouts = getLoanPayouts(openBets)
const groups = _.groupBy( const groups = groupBy(
[...payouts, ...loanPayouts], [...payouts, ...loanPayouts],
(payout) => payout.userId (payout) => payout.userId
) )
const userPayouts = _.mapValues(groups, (group) => const userPayouts = mapValues(groups, (group) =>
_.sumBy(group, (g) => g.payout) sumBy(group, (g) => g.payout)
) )
const entries = Object.entries(userPayouts) 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) { for (const [userId, payout] of flattened) {
console.log('Paying out', userId, payout) console.log('Paying out', userId, payout)

View File

@ -1,5 +1,5 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash' import { sumBy } from 'lodash'
import { initAdmin } from './script-init' import { initAdmin } from './script-init'
initAdmin() initAdmin()
@ -20,13 +20,13 @@ async function recalculateContract(contractRef: DocRef, contract: Contract) {
const openBets = bets.filter((b) => !b.isSold && !b.sale) const openBets = bets.filter((b) => !b.isSold && !b.sale)
const totalShares = { const totalShares = {
YES: _.sumBy(openBets, (bet) => (bet.outcome === 'YES' ? bet.shares : 0)), YES: sumBy(openBets, (bet) => (bet.outcome === 'YES' ? bet.shares : 0)),
NO: _.sumBy(openBets, (bet) => (bet.outcome === 'NO' ? bet.shares : 0)), NO: sumBy(openBets, (bet) => (bet.outcome === 'NO' ? bet.shares : 0)),
} }
const totalBets = { const totalBets = {
YES: _.sumBy(openBets, (bet) => (bet.outcome === 'YES' ? bet.amount : 0)), YES: sumBy(openBets, (bet) => (bet.outcome === 'YES' ? bet.amount : 0)),
NO: _.sumBy(openBets, (bet) => (bet.outcome === 'NO' ? bet.amount : 0)), NO: sumBy(openBets, (bet) => (bet.outcome === 'NO' ? bet.amount : 0)),
} }
await contractRef.update({ totalShares, totalBets }) await contractRef.update({ totalShares, totalBets })

View File

@ -1,5 +1,4 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { initAdmin } from './script-init' import { initAdmin } from './script-init'
initAdmin() initAdmin()

View File

@ -1,5 +1,4 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { initAdmin } from './script-init' import { initAdmin } from './script-init'
initAdmin() initAdmin()

View File

@ -1,5 +1,5 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash' import { uniq } from 'lodash'
import { initAdmin } from './script-init' import { initAdmin } from './script-init'
initAdmin() initAdmin()
@ -19,7 +19,7 @@ async function updateContractTags() {
for (const contract of contracts) { for (const contract of contracts) {
const contractRef = firestore.doc(`contracts/${contract.id}`) const contractRef = firestore.doc(`contracts/${contract.id}`)
const tags = _.uniq([ const tags = uniq([
...parseTags(contract.question + contract.description), ...parseTags(contract.question + contract.description),
...(contract.tags ?? []), ...(contract.tags ?? []),
]) ])

View File

@ -1,5 +1,4 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { initAdmin } from './script-init' import { initAdmin } from './script-init'
initAdmin() initAdmin()

View File

@ -1,5 +1,4 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { initAdmin } from './script-init' import { initAdmin } from './script-init'
initAdmin() initAdmin()

View File

@ -1,4 +1,4 @@
import * as _ from 'lodash' import { partition, sumBy } from 'lodash'
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as functions from 'firebase-functions' 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) 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 ?? [], userBets ?? [],
(bet) => bet.outcome === 'YES' (bet) => bet.outcome === 'YES'
) )
const [yesShares, noShares] = [ const [yesShares, noShares] = [
_.sumBy(yesBets, (bet) => bet.shares), sumBy(yesBets, (bet) => bet.shares),
_.sumBy(noBets, (bet) => bet.shares), sumBy(noBets, (bet) => bet.shares),
] ]
const maxShares = outcome === 'YES' ? yesShares : noShares const maxShares = outcome === 'YES' ? yesShares : noShares

View File

@ -1,6 +1,5 @@
import * as functions from 'firebase-functions' import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { getUser } from './utils' import { getUser } from './utils'
import { PrivateUser } from '../../common/user' import { PrivateUser } from '../../common/user'

View File

@ -1,6 +1,6 @@
import * as functions from 'firebase-functions' import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash' import { sumBy } from 'lodash'
import { getValues } from './utils' import { getValues } from './utils'
import { Contract } from '../../common/contract' import { Contract } from '../../common/contract'
@ -39,5 +39,5 @@ const computeVolumeFrom = async (contract: Contract, timeAgoMs: number) => {
.where('createdTime', '>', Date.now() - timeAgoMs) .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)))
} }

View File

@ -1,6 +1,6 @@
import * as _ from 'lodash'
import * as functions from 'firebase-functions' import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import { shuffle, sortBy } from 'lodash'
import { getValue, getValues } from './utils' import { getValue, getValues } from './utils'
import { Contract } from '../../common/contract' import { Contract } from '../../common/contract'
@ -30,7 +30,7 @@ const BATCH_SIZE = 30
const MAX_BATCHES = 50 const MAX_BATCHES = 50
const getUserBatches = async () => { const getUserBatches = async () => {
const users = _.shuffle(await getValues<User>(firestore.collection('users'))) const users = shuffle(await getValues<User>(firestore.collection('users')))
let userBatches: User[][] = [] let userBatches: User[][] = []
for (let i = 0; i < users.length; i += BATCH_SIZE) { for (let i = 0; i < users.length; i += BATCH_SIZE) {
userBatches.push(users.slice(i, 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] return [contract, score] as [Contract, number]
}) })
const sortedContracts = _.sortBy( const sortedContracts = sortBy(
scoredContracts, scoredContracts,
([_, score]) => score ([_, score]) => score
).reverse() ).reverse()

View File

@ -1,6 +1,5 @@
import * as functions from 'firebase-functions' import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { getValue, getValues } from './utils' import { getValue, getValues } from './utils'
import { Contract } from '../../common/contract' import { Contract } from '../../common/contract'

View File

@ -1,6 +1,6 @@
import * as functions from 'firebase-functions' import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash' import { sum, sumBy } from 'lodash'
import { getValues } from './utils' import { getValues } from './utils'
import { Contract } from '../../common/contract' import { Contract } from '../../common/contract'
@ -19,7 +19,7 @@ export const updateUserMetrics = functions.pubsub
getValues<Contract>(firestore.collection('contracts')), getValues<Contract>(firestore.collection('contracts')),
]) ])
const contractsDict = _.fromPairs( const contractsDict = Object.fromEntries(
contracts.map((contract) => [contract.id, contract]) contracts.map((contract) => [contract.id, contract])
) )
@ -43,12 +43,12 @@ export const updateUserMetrics = functions.pubsub
const computeInvestmentValue = async ( const computeInvestmentValue = async (
user: User, user: User,
contractsDict: _.Dictionary<Contract> contractsDict: { [k: string]: Contract }
) => { ) => {
const query = firestore.collectionGroup('bets').where('userId', '==', user.id) const query = firestore.collectionGroup('bets').where('userId', '==', user.id)
const bets = await getValues<Bet>(query) const bets = await getValues<Bet>(query)
return _.sumBy(bets, (bet) => { return sumBy(bets, (bet) => {
const contract = contractsDict[bet.contractId] const contract = contractsDict[bet.contractId]
if (!contract || contract.isResolved) return 0 if (!contract || contract.isResolved) return 0
if (bet.sale || bet.isSold) return 0 if (bet.sale || bet.isSold) return 0
@ -60,20 +60,20 @@ const computeInvestmentValue = async (
const computeTotalPool = async ( const computeTotalPool = async (
user: User, user: User,
contractsDict: _.Dictionary<Contract> contractsDict: { [k: string]: Contract }
) => { ) => {
const creatorContracts = Object.values(contractsDict).filter( const creatorContracts = Object.values(contractsDict).filter(
(contract) => contract.creatorId === user.id (contract) => contract.creatorId === user.id
) )
const pools = creatorContracts.map((contract) => 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 computeVolume = async (contract: Contract) => {
const bets = await getValues<Bet>( const bets = await getValues<Bet>(
firestore.collection(`contracts/${contract.id}/bets`) firestore.collection(`contracts/${contract.id}/bets`)
) )
return _.sumBy(bets, (bet) => Math.abs(bet.amount)) return sumBy(bets, (bet) => Math.abs(bet.amount))
} }

View File

@ -9,10 +9,11 @@
"scripts": {}, "scripts": {},
"dependencies": {}, "dependencies": {},
"devDependencies": { "devDependencies": {
"typescript": "4.6.4",
"@typescript-eslint/eslint-plugin": "5.25.0", "@typescript-eslint/eslint-plugin": "5.25.0",
"@typescript-eslint/parser": "5.25.0", "@typescript-eslint/parser": "5.25.0",
"eslint": "8.15.0", "eslint": "8.15.0",
"prettier": "2.5.0" "eslint-plugin-lodash": "^7.4.0",
"prettier": "2.5.0",
"typescript": "4.6.4"
} }
} }

View File

@ -1,9 +1,10 @@
module.exports = { module.exports = {
parser: '@typescript-eslint/parser', parser: '@typescript-eslint/parser',
plugins: ['lodash'],
extends: ['plugin:react-hooks/recommended', 'plugin:@next/next/recommended'], extends: ['plugin:react-hooks/recommended', 'plugin:@next/next/recommended'],
rules: { rules: {
// Add or disable rules here.
'@next/next/no-img-element': 'off', '@next/next/no-img-element': 'off',
'@next/next/no-typos': 'off', '@next/next/no-typos': 'off',
'lodash/import-scope': [2, 'member'],
}, },
} }

View File

@ -1,6 +1,6 @@
import { Point, ResponsiveLine } from '@nivo/line' import { Point, ResponsiveLine } from '@nivo/line'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import _ from 'lodash' import { zip } from 'lodash'
import { useWindowSize } from 'web/hooks/use-window-size' import { useWindowSize } from 'web/hooks/use-window-size'
import { Col } from '../layout/col' import { Col } from '../layout/col'
@ -16,7 +16,7 @@ export function DailyCountChart(props: {
dayjs(startDate).add(i, 'day').toDate() dayjs(startDate).add(i, 'day').toDate()
) )
const points = _.zip(dates, dailyCounts).map(([date, betCount]) => ({ const points = zip(dates, dailyCounts).map(([date, betCount]) => ({
x: date, x: date,
y: betCount, y: betCount,
})) }))
@ -68,7 +68,7 @@ export function DailyPercentChart(props: {
dayjs(startDate).add(i, 'day').toDate() dayjs(startDate).add(i, 'day').toDate()
) )
const points = _.zip(dates, dailyPercent).map(([date, betCount]) => ({ const points = zip(dates, dailyPercent).map(([date, betCount]) => ({
x: date, x: date,
y: betCount, y: betCount,
})) }))

View File

@ -1,5 +1,5 @@
import clsx from 'clsx' import clsx from 'clsx'
import _ from 'lodash' import { sum, mapValues } from 'lodash'
import { useState } from 'react' import { useState } from 'react'
import { DPM, FreeResponse, FullContract } from 'common/contract' import { DPM, FreeResponse, FullContract } from 'common/contract'
@ -30,8 +30,8 @@ export function AnswerResolvePanel(props: {
setIsSubmitting(true) setIsSubmitting(true)
const totalProb = _.sum(Object.values(chosenAnswers)) const totalProb = sum(Object.values(chosenAnswers))
const normalizedProbs = _.mapValues( const normalizedProbs = mapValues(
chosenAnswers, chosenAnswers,
(prob) => (100 * prob) / totalProb (prob) => (100 * prob) / totalProb
) )

View File

@ -1,7 +1,7 @@
import { DatumValue } from '@nivo/core' import { DatumValue } from '@nivo/core'
import { ResponsiveLine } from '@nivo/line' import { ResponsiveLine } from '@nivo/line'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import _ from 'lodash' import { groupBy, sortBy, sumBy } from 'lodash'
import { memo } from 'react' import { memo } from 'react'
import { Bet } from 'common/bet' import { Bet } from 'common/bet'
@ -48,7 +48,7 @@ export const AnswersGraph = memo(function AnswersGraph(props: {
// to the right. // to the right.
latestTime.add(1, 'month').valueOf() latestTime.add(1, 'month').valueOf()
const times = _.sortBy([ const times = sortBy([
createdTime, createdTime,
...bets.map((bet) => bet.createdTime), ...bets.map((bet) => bet.createdTime),
endTime, endTime,
@ -167,7 +167,7 @@ const computeProbsByOutcome = (
) => { ) => {
const { totalBets } = contract 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 outcomes = Object.keys(betsByOutcome).filter((outcome) => {
const maxProb = Math.max( const maxProb = Math.max(
...betsByOutcome[outcome].map((bet) => bet.probAfter) ...betsByOutcome[outcome].map((bet) => bet.probAfter)
@ -175,15 +175,15 @@ const computeProbsByOutcome = (
return outcome !== '0' && maxProb > 0.02 && totalBets[outcome] > 0.000000001 return outcome !== '0' && maxProb > 0.02 && totalBets[outcome] > 0.000000001
}) })
const trackedOutcomes = _.sortBy( const trackedOutcomes = sortBy(
outcomes, outcomes,
(outcome) => -1 * getOutcomeProbability(contract, outcome) (outcome) => -1 * getOutcomeProbability(contract, outcome)
).slice(0, NUM_LINES) ).slice(0, NUM_LINES)
const probsByOutcome = _.fromPairs( const probsByOutcome = Object.fromEntries(
trackedOutcomes.map((outcome) => [outcome, [] as number[]]) trackedOutcomes.map((outcome) => [outcome, [] as number[]])
) )
const sharesByOutcome = _.fromPairs( const sharesByOutcome = Object.fromEntries(
Object.keys(betsByOutcome).map((outcome) => [outcome, 0]) Object.keys(betsByOutcome).map((outcome) => [outcome, 0])
) )
@ -191,7 +191,7 @@ const computeProbsByOutcome = (
const { outcome, shares } = bet const { outcome, shares } = bet
sharesByOutcome[outcome] += shares sharesByOutcome[outcome] += shares
const sharesSquared = _.sumBy( const sharesSquared = sumBy(
Object.values(sharesByOutcome).map((shares) => shares ** 2) Object.values(sharesByOutcome).map((shares) => shares ** 2)
) )

View File

@ -1,5 +1,5 @@
import _ from 'lodash' import { sortBy, partition, sum, uniq } from 'lodash'
import React, { useLayoutEffect, useState } from 'react' import { useLayoutEffect, useState } from 'react'
import { DPM, FreeResponse, FullContract } from 'common/contract' import { DPM, FreeResponse, FullContract } from 'common/contract'
import { Col } from '../layout/col' import { Col } from '../layout/col'
@ -32,7 +32,7 @@ export function AnswersPanel(props: {
const { creatorId, resolution, resolutions, totalBets } = contract const { creatorId, resolution, resolutions, totalBets } = contract
const answers = useAnswers(contract.id) ?? contract.answers const answers = useAnswers(contract.id) ?? contract.answers
const [winningAnswers, losingAnswers] = _.partition( const [winningAnswers, losingAnswers] = partition(
answers.filter( answers.filter(
(answer) => answer.id !== '0' && totalBets[answer.id] > 0.000000001 (answer) => answer.id !== '0' && totalBets[answer.id] > 0.000000001
), ),
@ -40,10 +40,10 @@ export function AnswersPanel(props: {
answer.id === resolution || (resolutions && resolutions[answer.id]) answer.id === resolution || (resolutions && resolutions[answer.id])
) )
const sortedAnswers = [ const sortedAnswers = [
..._.sortBy(winningAnswers, (answer) => ...sortBy(winningAnswers, (answer) =>
resolutions ? -1 * resolutions[answer.id] : 0 resolutions ? -1 * resolutions[answer.id] : 0
), ),
..._.sortBy( ...sortBy(
resolution ? [] : losingAnswers, resolution ? [] : losingAnswers,
(answer) => -1 * getDpmOutcomeProbability(contract.totalShares, answer.id) (answer) => -1 * getDpmOutcomeProbability(contract.totalShares, answer.id)
), ),
@ -58,7 +58,7 @@ export function AnswersPanel(props: {
[answerId: string]: number [answerId: string]: number
}>({}) }>({})
const chosenTotal = _.sum(Object.values(chosenAnswers)) const chosenTotal = sum(Object.values(chosenAnswers))
const answerItems = getAnswerItems( const answerItems = getAnswerItems(
contract, contract,
@ -158,10 +158,10 @@ function getAnswerItems(
answers: Answer[], answers: Answer[],
user: User | undefined | null user: User | undefined | null
) { ) {
let outcomes = _.uniq( let outcomes = uniq(answers.map((answer) => answer.number.toString())).filter(
answers.map((answer) => answer.number.toString()) (outcome) => getOutcomeProbability(contract, outcome) > 0.0001
).filter((outcome) => getOutcomeProbability(contract, outcome) > 0.0001) )
outcomes = _.sortBy(outcomes, (outcome) => outcomes = sortBy(outcomes, (outcome) =>
getOutcomeProbability(contract, outcome) getOutcomeProbability(contract, outcome)
).reverse() ).reverse()

View File

@ -1,6 +1,6 @@
import clsx from 'clsx' import clsx from 'clsx'
import _ from 'lodash'
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { partition, sumBy } from 'lodash'
import { useUser } from 'web/hooks/use-user' import { useUser } from 'web/hooks/use-user'
import { Binary, CPMM, DPM, FullContract } from 'common/contract' import { Binary, CPMM, DPM, FullContract } from 'common/contract'
@ -441,13 +441,13 @@ export function SellPanel(props: {
const resultProb = getCpmmProbability(newPool, contract.p) const resultProb = getCpmmProbability(newPool, contract.p)
const openUserBets = userBets.filter((bet) => !bet.isSold && !bet.sale) const openUserBets = userBets.filter((bet) => !bet.isSold && !bet.sale)
const [yesBets, noBets] = _.partition( const [yesBets, noBets] = partition(
openUserBets, openUserBets,
(bet) => bet.outcome === 'YES' (bet) => bet.outcome === 'YES'
) )
const [yesShares, noShares] = [ const [yesShares, noShares] = [
_.sumBy(yesBets, (bet) => bet.shares), sumBy(yesBets, (bet) => bet.shares),
_.sumBy(noBets, (bet) => bet.shares), sumBy(noBets, (bet) => bet.shares),
] ]
const sellOutcome = yesShares ? 'YES' : noShares ? 'NO' : undefined const sellOutcome = yesShares ? 'YES' : noShares ? 'NO' : undefined

View File

@ -1,5 +1,13 @@
import Link from 'next/link' import Link from 'next/link'
import _ from 'lodash' import {
uniq,
groupBy,
mapValues,
sortBy,
partition,
sumBy,
throttle,
} from 'lodash'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import clsx from 'clsx' import clsx from 'clsx'
@ -54,7 +62,7 @@ export function BetsList(props: { user: User }) {
useEffect(() => { useEffect(() => {
if (bets) { if (bets) {
const contractIds = _.uniq(bets.map((bet) => bet.contractId)) const contractIds = uniq(bets.map((bet) => bet.contractId))
let disposed = false let disposed = false
Promise.all(contractIds.map((id) => getContractFromId(id))).then( Promise.all(contractIds.map((id) => getContractFromId(id))).then(
@ -84,10 +92,10 @@ export function BetsList(props: { user: User }) {
if (bets.length === 0) return <NoBets /> if (bets.length === 0) return <NoBets />
// Decending creation time. // Decending creation time.
bets.sort((bet1, bet2) => bet2.createdTime - bet1.createdTime) bets.sort((bet1, bet2) => bet2.createdTime - bet1.createdTime)
const contractBets = _.groupBy(bets, 'contractId') const contractBets = groupBy(bets, 'contractId')
const contractsById = _.fromPairs(contracts.map((c) => [c.id, c])) 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] const contract = contractsById[contractId]
if (!contract) return getContractBetNullMetrics() if (!contract) return getContractBetNullMetrics()
return getContractBetMetrics(contract, bets) return getContractBetMetrics(contract, bets)
@ -110,7 +118,7 @@ export function BetsList(props: { user: User }) {
(filter === 'open' ? -1 : 1) * (filter === 'open' ? -1 : 1) *
(c.resolutionTime ?? c.closeTime ?? Infinity), (c.resolutionTime ?? c.closeTime ?? Infinity),
} }
const displayedContracts = _.sortBy(contracts, SORTS[sort]) const displayedContracts = sortBy(contracts, SORTS[sort])
.reverse() .reverse()
.filter(FILTERS[filter]) .filter(FILTERS[filter])
.filter((c) => { .filter((c) => {
@ -121,20 +129,20 @@ export function BetsList(props: { user: User }) {
return metrics.payout > 0 return metrics.payout > 0
}) })
const [settled, unsettled] = _.partition( const [settled, unsettled] = partition(
contracts, contracts,
(c) => c.isResolved || contractsMetrics[c.id].invested === 0 (c) => c.isResolved || contractsMetrics[c.id].invested === 0
) )
const currentInvested = _.sumBy( const currentInvested = sumBy(
unsettled, unsettled,
(c) => contractsMetrics[c.id].invested (c) => contractsMetrics[c.id].invested
) )
const currentBetsValue = _.sumBy( const currentBetsValue = sumBy(
unsettled, unsettled,
(c) => contractsMetrics[c.id].payout (c) => contractsMetrics[c.id].payout
) )
const currentNetInvestment = _.sumBy( const currentNetInvestment = sumBy(
unsettled, unsettled,
(c) => contractsMetrics[c.id].netPayout (c) => contractsMetrics[c.id].netPayout
) )
@ -340,10 +348,10 @@ export function MyBetsSummary(props: {
const excludeSalesAndAntes = bets.filter( const excludeSalesAndAntes = bets.filter(
(b) => !b.isAnte && !b.isSold && !b.sale (b) => !b.isAnte && !b.isSold && !b.sale
) )
const yesWinnings = _.sumBy(excludeSalesAndAntes, (bet) => const yesWinnings = sumBy(excludeSalesAndAntes, (bet) =>
calculatePayout(contract, bet, 'YES') calculatePayout(contract, bet, 'YES')
) )
const noWinnings = _.sumBy(excludeSalesAndAntes, (bet) => const noWinnings = sumBy(excludeSalesAndAntes, (bet) =>
calculatePayout(contract, bet, 'NO') calculatePayout(contract, bet, 'NO')
) )
const { invested, profitPercent, payout, profit } = getContractBetMetrics( const { invested, profitPercent, payout, profit } = getContractBetMetrics(
@ -421,21 +429,19 @@ export function ContractBetsTable(props: {
const bets = props.bets.filter((b) => !b.isAnte) 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]) sales.map((sale) => [sale.sale?.betId ?? '', sale])
) )
const [redemptions, normalBets] = _.partition( const [redemptions, normalBets] = partition(
contract.mechanism === 'cpmm-1' ? bets : buys, contract.mechanism === 'cpmm-1' ? bets : buys,
(b) => b.isRedemption (b) => b.isRedemption
) )
const amountRedeemed = Math.floor( const amountRedeemed = Math.floor(-0.5 * sumBy(redemptions, (b) => b.shares))
-0.5 * _.sumBy(redemptions, (b) => b.shares)
)
const amountLoaned = _.sumBy( const amountLoaned = sumBy(
bets.filter((bet) => !bet.isSold && !bet.sale), bets.filter((bet) => !bet.isSold && !bet.sale),
(bet) => bet.loanAmount ?? 0 (bet) => bet.loanAmount ?? 0
) )
@ -570,10 +576,7 @@ function BetRow(props: { bet: Bet; contract: Contract; saleBet?: Bet }) {
) )
} }
const warmUpSellBet = _.throttle( const warmUpSellBet = throttle(() => sellBet({}).catch(() => {}), 5000 /* ms */)
() => sellBet({}).catch(() => {}),
5000 /* ms */
)
function SellButton(props: { contract: Contract; bet: Bet }) { function SellButton(props: { contract: Contract; bet: Bet }) {
useEffect(() => { useEffect(() => {

View File

@ -1,4 +1,3 @@
import _ from 'lodash'
import { useState } from 'react' import { useState } from 'react'
import { NumericContract } from 'common/contract' import { NumericContract } from 'common/contract'

View File

@ -1,5 +1,5 @@
import { StarIcon } from '@heroicons/react/solid' import { StarIcon } from '@heroicons/react/solid'
import _ from 'lodash' import { sumBy } from 'lodash'
import Link from 'next/link' import Link from 'next/link'
import Image from 'next/image' import Image from 'next/image'
import { Charity } from 'common/charity' 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 { name, slug, photo, preview, id, tags } = props.charity
const txns = useCharityTxns(id) const txns = useCharityTxns(id)
const raised = _.sumBy(txns, (txn) => txn.amount) const raised = sumBy(txns, (txn) => txn.amount)
return ( return (
<Link href={`/charity/${slug}`} passHref> <Link href={`/charity/${slug}`} passHref>

View File

@ -1,7 +1,7 @@
import { DotsHorizontalIcon } from '@heroicons/react/outline' import { DotsHorizontalIcon } from '@heroicons/react/outline'
import clsx from 'clsx' import clsx from 'clsx'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import _ from 'lodash' import { uniqBy, sum } from 'lodash'
import { useState } from 'react' import { useState } from 'react'
import { Bet } from 'common/bet' 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 formatTime = (dt: number) => dayjs(dt).format('MMM DD, YYYY hh:mm a z')
const { createdTime, closeTime, resolutionTime } = contract const { createdTime, closeTime, resolutionTime } = contract
const tradersCount = _.uniqBy(bets, 'userId').length const tradersCount = uniqBy(bets, 'userId').length
return ( return (
<> <>
@ -108,7 +108,7 @@ export function ContractInfoDialog(props: { contract: Contract; bets: Bet[] }) {
{contract.mechanism === 'dpm-2' && ( {contract.mechanism === 'dpm-2' && (
<tr> <tr>
<td>Pool</td> <td>Pool</td>
<td>{formatMoney(_.sum(Object.values(contract.pool)))}</td> <td>{formatMoney(sum(Object.values(contract.pool)))}</td>
</tr> </tr>
)} )}
</tbody> </tbody>

View File

@ -1,8 +1,8 @@
import { DatumValue } from '@nivo/core' import { DatumValue } from '@nivo/core'
import { ResponsiveLine } from '@nivo/line' import { ResponsiveLine } from '@nivo/line'
import { NUMERIC_GRAPH_COLOR } from 'common/numeric-constants' import { NUMERIC_GRAPH_COLOR } from 'common/numeric-constants'
import _ from 'lodash'
import { memo } from 'react' import { memo } from 'react'
import { range } from 'lodash'
import { getDpmOutcomeProbabilities } from '../../../common/calculate-dpm' import { getDpmOutcomeProbabilities } from '../../../common/calculate-dpm'
import { NumericContract } from '../../../common/contract' import { NumericContract } from '../../../common/contract'
import { useWindowSize } from '../../hooks/use-window-size' import { useWindowSize } from '../../hooks/use-window-size'
@ -16,10 +16,10 @@ export const NumericGraph = memo(function NumericGraph(props: {
const bucketProbs = getDpmOutcomeProbabilities(totalShares) const bucketProbs = getDpmOutcomeProbabilities(totalShares)
const xs = _.range(bucketCount).map( const xs = range(bucketCount).map(
(i) => min + ((max - min) * i) / bucketCount (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 points = probs.map((prob, i) => ({ x: xs[i], y: prob }))
const maxProb = Math.max(...probs) const maxProb = Math.max(...probs)
const data = [{ id: 'Probability', data: points, color: NUMERIC_GRAPH_COLOR }] const data = [{ id: 'Probability', data: points, color: NUMERIC_GRAPH_COLOR }]

View File

@ -1,4 +1,4 @@
import _ from 'lodash' import { sample } from 'lodash'
import { SparklesIcon, XIcon } from '@heroicons/react/solid' import { SparklesIcon, XIcon } from '@heroicons/react/solid'
import { Avatar } from './avatar' import { Avatar } from './avatar'
import { useEffect, useRef, useState } from 'react' 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 // Take care not to produce a different placeholder on the server and client
const [defaultPlaceholder, setDefaultPlaceholder] = useState('') const [defaultPlaceholder, setDefaultPlaceholder] = useState('')
useEffect(() => { useEffect(() => {
setDefaultPlaceholder( setDefaultPlaceholder(`e.g. ${sample(ENV_CONFIG.newQuestionPlaceholders)}`)
`e.g. ${_.sample(ENV_CONFIG.newQuestionPlaceholders)}`
)
}, []) }, [])
const placeholder = props.placeholder ?? defaultPlaceholder const placeholder = props.placeholder ?? defaultPlaceholder

View File

@ -1,4 +1,4 @@
import _ from 'lodash' import { last, findLastIndex, uniq, sortBy } from 'lodash'
import { Answer } from 'common/answer' import { Answer } from 'common/answer'
import { Bet } from 'common/bet' import { Bet } from 'common/bet'
@ -200,17 +200,17 @@ function getAnswerGroups(
) { ) {
const { sortByProb, abbreviated, reversed } = options 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 (outcome) => getOutcomeProbability(contract, outcome) > 0.0001
) )
if (abbreviated) { if (abbreviated) {
const lastComment = _.last(comments) const lastComment = last(comments)
const lastCommentOutcome = bets.find( const lastCommentOutcome = bets.find(
(bet) => bet.id === lastComment?.betId (bet) => bet.id === lastComment?.betId
)?.outcome )?.outcome
const lastBetOutcome = _.last(bets)?.outcome const lastBetOutcome = last(bets)?.outcome
if (lastCommentOutcome && lastBetOutcome) { if (lastCommentOutcome && lastBetOutcome) {
outcomes = _.uniq([ outcomes = uniq([
...outcomes.filter( ...outcomes.filter(
(outcome) => (outcome) =>
outcome !== lastCommentOutcome && outcome !== lastBetOutcome outcome !== lastCommentOutcome && outcome !== lastBetOutcome
@ -222,13 +222,13 @@ function getAnswerGroups(
outcomes = outcomes.slice(-2) outcomes = outcomes.slice(-2)
} }
if (sortByProb) { if (sortByProb) {
outcomes = _.sortBy(outcomes, (outcome) => outcomes = sortBy(outcomes, (outcome) =>
getOutcomeProbability(contract, outcome) getOutcomeProbability(contract, outcome)
) )
} else { } else {
// Sort by recent bet. // Sort by recent bet.
outcomes = _.sortBy(outcomes, (outcome) => outcomes = sortBy(outcomes, (outcome) =>
_.findLastIndex(bets, (bet) => bet.outcome === outcome) findLastIndex(bets, (bet) => bet.outcome === outcome)
) )
} }
@ -274,10 +274,10 @@ function getAnswerAndCommentInputGroups(
comments: Comment[], comments: Comment[],
user: User | undefined | null 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 (outcome) => getOutcomeProbability(contract, outcome) > 0.0001
) )
outcomes = _.sortBy(outcomes, (outcome) => outcomes = sortBy(outcomes, (outcome) =>
getOutcomeProbability(contract, outcome) getOutcomeProbability(contract, outcome)
) )
const betsByCurrentUser = bets.filter((bet) => bet.userId === user?.id) 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: // iterate through the bets and comment activity items and add them to the items in order of comment creation time:
const unorderedBetsAndComments = [...commentsWithoutBets, ...groupedBets] const unorderedBetsAndComments = [...commentsWithoutBets, ...groupedBets]
let sortedBetsAndComments = _.sortBy(unorderedBetsAndComments, (item) => { let sortedBetsAndComments = sortBy(unorderedBetsAndComments, (item) => {
if (item.type === 'comment') { if (item.type === 'comment') {
return item.comment.createdTime return item.comment.createdTime
} else if (item.type === 'bet') { } else if (item.type === 'bet') {

View File

@ -10,7 +10,7 @@ import { formatMoney } from 'common/util/format'
import { OutcomeLabel } from 'web/components/outcome-label' import { OutcomeLabel } from 'web/components/outcome-label'
import { RelativeTimestamp } from 'web/components/relative-timestamp' import { RelativeTimestamp } from 'web/components/relative-timestamp'
import React, { Fragment } from 'react' import React, { Fragment } from 'react'
import * as _ from 'lodash' import { uniqBy, partition, sumBy, groupBy } from 'lodash'
import { JoinSpans } from 'web/components/join-spans' import { JoinSpans } from 'web/components/join-spans'
export function FeedBet(props: { export function FeedBet(props: {
@ -104,11 +104,11 @@ function BetGroupSpan(props: {
}) { }) {
const { contract, bets, outcome } = 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 [buys, sells] = partition(bets, (bet) => bet.amount >= 0)
const buyTotal = _.sumBy(buys, (b) => b.amount) const buyTotal = sumBy(buys, (b) => b.amount)
const sellTotal = _.sumBy(sells, (b) => -b.amount) const sellTotal = sumBy(sells, (b) => -b.amount)
return ( return (
<span> <span>
@ -139,7 +139,7 @@ export function FeedBetGroup(props: {
}) { }) {
const { contract, bets, hideOutcome } = 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) const outcomes = Object.keys(betGroups)
// Use the time of the last bet for the entire group // Use the time of the last bet for the entire group

View File

@ -3,6 +3,7 @@ import { Comment } from 'common/comment'
import { User } from 'common/user' import { User } from 'common/user'
import { Contract } from 'common/contract' import { Contract } from 'common/contract'
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { minBy, maxBy, groupBy, partition, sumBy } from 'lodash'
import { useUser } from 'web/hooks/use-user' import { useUser } from 'web/hooks/use-user'
import { formatMoney } from 'common/util/format' import { formatMoney } from 'common/util/format'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
@ -16,7 +17,6 @@ import { contractPath } from 'web/lib/firebase/contracts'
import { firebaseLogin } from 'web/lib/firebase/users' import { firebaseLogin } from 'web/lib/firebase/users'
import { createComment, MAX_COMMENT_LENGTH } from 'web/lib/firebase/comments' import { createComment, MAX_COMMENT_LENGTH } from 'web/lib/firebase/comments'
import Textarea from 'react-expanding-textarea' import Textarea from 'react-expanding-textarea'
import * as _ from 'lodash'
import { Linkify } from 'web/components/linkify' import { Linkify } from 'web/components/linkify'
import { SiteLink } from 'web/components/site-link' import { SiteLink } from 'web/components/site-link'
import { BetStatusText } from 'web/components/feed/feed-bets' import { BetStatusText } from 'web/components/feed/feed-bets'
@ -36,7 +36,7 @@ export function FeedCommentThread(props: {
props props
const [showReply, setShowReply] = useState(false) const [showReply, setShowReply] = useState(false)
const [replyToUsername, setReplyToUsername] = useState('') const [replyToUsername, setReplyToUsername] = useState('')
const betsByUserId = _.groupBy(bets, (bet) => bet.userId) const betsByUserId = groupBy(bets, (bet) => bet.userId)
const user = useUser() const user = useUser()
const commentsList = comments.filter( const commentsList = comments.filter(
(comment) => (comment) =>
@ -67,7 +67,7 @@ export function FeedCommentThread(props: {
onReplyClick={scrollAndOpenReplyInput} onReplyClick={scrollAndOpenReplyInput}
probAtCreatedTime={ probAtCreatedTime={
contract.outcomeType === 'BINARY' contract.outcomeType === 'BINARY'
? _.minBy(bets, (bet) => { ? minBy(bets, (bet) => {
return bet.createdTime < comment.createdTime return bet.createdTime < comment.createdTime
? comment.createdTime - bet.createdTime ? comment.createdTime - bet.createdTime
: comment.createdTime : comment.createdTime
@ -494,8 +494,7 @@ function getBettorsLargestPositionBeforeTime(
} }
} }
const majorityAnswer = const majorityAnswer =
_.maxBy(Object.keys(answerCounts), (outcome) => answerCounts[outcome]) ?? maxBy(Object.keys(answerCounts), (outcome) => answerCounts[outcome]) ?? ''
''
return { return {
userPosition: answerCounts[majorityAnswer] || 0, userPosition: answerCounts[majorityAnswer] || 0,
outcome: majorityAnswer, outcome: majorityAnswer,
@ -505,12 +504,12 @@ function getBettorsLargestPositionBeforeTime(
return emptyReturn return emptyReturn
} }
const [yesBets, noBets] = _.partition( const [yesBets, noBets] = partition(
previousBets ?? [], previousBets ?? [],
(bet) => bet.outcome === 'YES' (bet) => bet.outcome === 'YES'
) )
yesShares = _.sumBy(yesBets, (bet) => bet.shares) yesShares = sumBy(yesBets, (bet) => bet.shares)
noShares = _.sumBy(noBets, (bet) => bet.shares) noShares = sumBy(noBets, (bet) => bet.shares)
yesFloorShares = Math.floor(yesShares) yesFloorShares = Math.floor(yesShares)
noFloorShares = Math.floor(noShares) noFloorShares = Math.floor(noShares)

View File

@ -1,6 +1,5 @@
// From https://tailwindui.com/components/application-ui/lists/feeds // From https://tailwindui.com/components/application-ui/lists/feeds
import React, { useState } from 'react' import React, { useState } from 'react'
import * as _ from 'lodash'
import { import {
BanIcon, BanIcon,
CheckIcon, CheckIcon,

View File

@ -1,4 +1,4 @@
import _ from 'lodash' import { groupBy, mapValues, maxBy, sortBy } from 'lodash'
import { Contract } from 'web/lib/firebase/contracts' import { Contract } from 'web/lib/firebase/contracts'
import { Comment } from 'web/lib/firebase/comments' import { Comment } from 'web/lib/firebase/comments'
import { Bet } from 'common/bet' import { Bet } from 'common/bet'
@ -44,10 +44,10 @@ export function findActiveContracts(
} }
// Add contracts by last bet time. // Add contracts by last bet time.
const contractBets = _.groupBy(recentBets, (bet) => bet.contractId) const contractBets = groupBy(recentBets, (bet) => bet.contractId)
const contractMostRecentBet = _.mapValues( const contractMostRecentBet = mapValues(
contractBets, contractBets,
(bets) => _.maxBy(bets, (bet) => bet.createdTime) as Bet (bets) => maxBy(bets, (bet) => bet.createdTime) as Bet
) )
for (const bet of Object.values(contractMostRecentBet)) { for (const bet of Object.values(contractMostRecentBet)) {
const contract = contractsById.get(bet.contractId) const contract = contractsById.get(bet.contractId)
@ -60,21 +60,21 @@ export function findActiveContracts(
!contract.isResolved && !contract.isResolved &&
(contract.closeTime ?? Infinity) > Date.now() (contract.closeTime ?? Infinity) > Date.now()
) )
activeContracts = _.sortBy( activeContracts = sortBy(
activeContracts, activeContracts,
(c) => -(idToActivityTime.get(c.id) ?? 0) (c) => -(idToActivityTime.get(c.id) ?? 0)
) )
const contractComments = _.groupBy( const contractComments = groupBy(
recentComments, recentComments,
(comment) => comment.contractId (comment) => comment.contractId
) )
const contractMostRecentComment = _.mapValues( const contractMostRecentComment = mapValues(
contractComments, 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] const seenTime = seenContracts[c.id]
if (!seenTime) { if (!seenTime) {
return 0 return 0

View File

@ -1,5 +1,5 @@
import { useState } from 'react' import { useState } from 'react'
import _ from 'lodash' import { isEqual } from 'lodash'
import clsx from 'clsx' import clsx from 'clsx'
import { PencilIcon } from '@heroicons/react/outline' import { PencilIcon } from '@heroicons/react/outline'
@ -29,7 +29,7 @@ export function EditFoldButton(props: { fold: Fold; className?: string }) {
const saveDisabled = const saveDisabled =
name === fold.name && name === fold.name &&
_.isEqual(tags, fold.tags) && isEqual(tags, fold.tags) &&
about === (fold.about ?? '') about === (fold.about ?? '')
const onSubmit = async () => { const onSubmit = async () => {

View File

@ -11,7 +11,7 @@ import {
SparklesIcon, SparklesIcon,
} from '@heroicons/react/outline' } from '@heroicons/react/outline'
import clsx from 'clsx' import clsx from 'clsx'
import _ from 'lodash' import { sortBy } from 'lodash'
import Link from 'next/link' import Link from 'next/link'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { useFollowedFolds } from 'web/hooks/use-fold' import { useFollowedFolds } from 'web/hooks/use-fold'
@ -124,7 +124,7 @@ export default function Sidebar(props: { className?: string }) {
const user = useUser() const user = useUser()
let folds = useFollowedFolds(user) || [] let folds = useFollowedFolds(user) || []
folds = _.sortBy(folds, 'followCount').reverse() folds = sortBy(folds, 'followCount').reverse()
const deservesDailyFreeMarket = !useHasCreatedContractToday(user) const deservesDailyFreeMarket = !useHasCreatedContractToday(user)
const navigationOptions = const navigationOptions =

View File

@ -1,5 +1,4 @@
import clsx from 'clsx' import clsx from 'clsx'
import _ from 'lodash'
import { Col } from './layout/col' import { Col } from './layout/col'
import { Spacer } from './layout/spacer' import { Spacer } from './layout/spacer'

View File

@ -1,7 +1,7 @@
import { Binary, CPMM, DPM, FullContract } from 'common/contract' import { Binary, CPMM, DPM, FullContract } from 'common/contract'
import { Bet } from 'common/bet' import { Bet } from 'common/bet'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import _ from 'lodash' import { partition, sumBy } from 'lodash'
export const useSaveShares = ( export const useSaveShares = (
contract: FullContract<CPMM | DPM, Binary>, contract: FullContract<CPMM | DPM, Binary>,
@ -17,13 +17,13 @@ export const useSaveShares = (
| undefined | undefined
>() >()
const [yesBets, noBets] = _.partition( const [yesBets, noBets] = partition(
userBets ?? [], userBets ?? [],
(bet) => bet.outcome === 'YES' (bet) => bet.outcome === 'YES'
) )
const [yesShares, noShares] = [ const [yesShares, noShares] = [
_.sumBy(yesBets, (bet) => bet.shares), sumBy(yesBets, (bet) => bet.shares),
_.sumBy(noBets, (bet) => bet.shares), sumBy(noBets, (bet) => bet.shares),
] ]
const yesFloorShares = Math.round(yesShares) === 0 ? 0 : Math.floor(yesShares) const yesFloorShares = Math.round(yesShares) === 0 ? 0 : Math.floor(yesShares)

View File

@ -19,10 +19,10 @@ import { Comment, getUsersComments } from 'web/lib/firebase/comments'
import { Contract } from 'common/contract' import { Contract } from 'common/contract'
import { getContractFromId, listContracts } from 'web/lib/firebase/contracts' import { getContractFromId, listContracts } from 'web/lib/firebase/contracts'
import { LoadingIndicator } from './loading-indicator' import { LoadingIndicator } from './loading-indicator'
import _ from 'lodash'
import { BetsList } from './bets-list' import { BetsList } from './bets-list'
import { Bet } from 'common/bet' import { Bet } from 'common/bet'
import { getUserBets } from 'web/lib/firebase/bets' import { getUserBets } from 'web/lib/firebase/bets'
import { uniq } from 'lodash'
export function UserLink(props: { export function UserLink(props: {
name: string name: string
@ -70,7 +70,7 @@ export function UserPage(props: {
}, [user]) }, [user])
useEffect(() => { useEffect(() => {
const uniqueContractIds = _.uniq( const uniqueContractIds = uniq(
usersComments.map((comment) => comment.contractId) usersComments.map((comment) => comment.contractId)
) )
Promise.all( Promise.all(

View File

@ -1,4 +1,3 @@
import _, { Dictionary } from 'lodash'
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import type { feed } from 'common/feed' import type { feed } from 'common/feed'
import { useTimeSinceFirstRender } from './use-time-since-first-render' import { useTimeSinceFirstRender } from './use-time-since-first-render'
@ -15,7 +14,7 @@ export const useAlgoFeed = (
category: string category: string
) => { ) => {
const [allFeed, setAllFeed] = useState<feed>() const [allFeed, setAllFeed] = useState<feed>()
const [categoryFeeds, setCategoryFeeds] = useState<Dictionary<feed>>() const [categoryFeeds, setCategoryFeeds] = useState<{ [x: string]: feed }>()
const getTime = useTimeSinceFirstRender() const getTime = useTimeSinceFirstRender()

View File

@ -1,4 +1,4 @@
import _ from 'lodash' import { isEqual } from 'lodash'
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { import {
Contract, Contract,
@ -80,14 +80,14 @@ export const useUpdatedContracts = (contracts: Contract[] | undefined) => {
useEffect(() => { useEffect(() => {
if (contracts === undefined) return 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 disposes = contracts.map((contract) => {
const { id } = contract const { id } = contract
return listenForContract(id, (contract) => { return listenForContract(id, (contract) => {
const curr = contractDict.current[id] const curr = contractDict.current[id]
if (!_.isEqual(curr, contract)) { if (!isEqual(curr, contract)) {
contractDict.current[id] = contract as Contract contractDict.current[id] = contract as Contract
triggerUpdate((n) => n + 1) triggerUpdate((n) => n + 1)
} }

View File

@ -1,4 +1,4 @@
import _ from 'lodash' import { isEqual, sortBy } from 'lodash'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { Fold } from 'common/fold' import { Fold } from 'common/fold'
import { User } from 'common/user' import { User } from 'common/user'
@ -95,9 +95,9 @@ export const useFollowedFolds = (user: User | null | undefined) => {
setFollowedFolds(JSON.parse(followedFoldJson)) setFollowedFolds(JSON.parse(followedFoldJson))
// Exit early if ids and followedFoldIds have all the same elements. // Exit early if ids and followedFoldIds have all the same elements.
if ( if (
_.isEqual( isEqual(
_.sortBy(ids), sortBy(ids),
_.sortBy(JSON.parse(followedFoldJson).map((f: Fold) => f.id)) sortBy(JSON.parse(followedFoldJson).map((f: Fold) => f.id))
) )
) { ) {
return return

View File

@ -1,4 +1,4 @@
import _ from 'lodash' import { debounce } from 'lodash'
import { RefObject, useMemo, useLayoutEffect, useRef, useState } from 'react' import { RefObject, useMemo, useLayoutEffect, useRef, useState } from 'react'
type elem_size = type elem_size =
@ -21,7 +21,7 @@ export function useListenElemSize<T extends HTMLElement>(
} }
return debounceMs return debounceMs
? _.debounce(updateSize, debounceMs, { leading: false, trailing: true }) ? debounce(updateSize, debounceMs, { leading: false, trailing: true })
: updateSize : updateSize
}, [callback, elemRef, debounceMs]) }, [callback, elemRef, debounceMs])

View File

@ -1,4 +1,4 @@
import _ from 'lodash' import { isEmpty } from 'lodash'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { IS_PRIVATE_MANIFOLD } from 'common/envs/constants' import { IS_PRIVATE_MANIFOLD } from 'common/envs/constants'
@ -16,7 +16,7 @@ export function usePropz(
getStaticPropz: (props: PropzProps) => Promise<any> getStaticPropz: (props: PropzProps) => Promise<any>
) { ) {
// If props were successfully server-side generated, just use those // If props were successfully server-side generated, just use those
if (!_.isEmpty(initialProps)) { if (!isEmpty(initialProps)) {
return initialProps return initialProps
} }

View File

@ -1,4 +1,4 @@
import _ from 'lodash' import { mapValues } from 'lodash'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { Contract } from 'common/contract' import { Contract } from 'common/contract'
import { trackView } from 'web/lib/firebase/tracking' import { trackView } from 'web/lib/firebase/tracking'
@ -38,7 +38,7 @@ export const useSaveSeenContract = (
const key = 'feed-seen-contracts' const key = 'feed-seen-contracts'
const getSeenContracts = () => { const getSeenContracts = () => {
return _.mapValues( return mapValues(
JSON.parse(localStorage.getItem(key) ?? '{}'), JSON.parse(localStorage.getItem(key) ?? '{}'),
(time) => +time (time) => +time
) )

View File

@ -1,4 +1,4 @@
import _ from 'lodash' import { defaults, debounce } from 'lodash'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { useEffect, useMemo, useState } from 'react' import { useEffect, useMemo, useState } from 'react'
import { useSearchBox } from 'react-instantsearch-hooks-web' import { useSearchBox } from 'react-instantsearch-hooks-web'
@ -18,7 +18,7 @@ export function useInitialQueryAndSort(options?: {
defaultSort: Sort defaultSort: Sort
shouldLoadFromStorage?: boolean shouldLoadFromStorage?: boolean
}) { }) {
const { defaultSort, shouldLoadFromStorage } = _.defaults(options, { const { defaultSort, shouldLoadFromStorage } = defaults(options, {
defaultSort: '24-hour-vol', defaultSort: '24-hour-vol',
shouldLoadFromStorage: true, shouldLoadFromStorage: true,
}) })
@ -79,7 +79,7 @@ export function useUpdateQueryAndSort(props: {
// Debounce router query update. // Debounce router query update.
const pushQuery = useMemo( const pushQuery = useMemo(
() => () =>
_.debounce((query: string | undefined) => { debounce((query: string | undefined) => {
if (query) { if (query) {
router.query.q = query router.query.q = query
} else { } else {

View File

@ -1,4 +1,4 @@
import _ from 'lodash' import { isEqual } from 'lodash'
import { useMemo, useRef, useState } from 'react' import { useMemo, useRef, useState } from 'react'
export const useStateCheckEquality = <T>(initialState: T) => { export const useStateCheckEquality = <T>(initialState: T) => {
@ -10,7 +10,7 @@ export const useStateCheckEquality = <T>(initialState: T) => {
const checkSetState = useMemo( const checkSetState = useMemo(
() => (newState: T) => { () => (newState: T) => {
const state = stateRef.current const state = stateRef.current
if (!_.isEqual(state, newState)) { if (!isEqual(state, newState)) {
setState(newState) setState(newState)
} }
}, },

View File

@ -1,4 +1,4 @@
import _ from 'lodash' import { uniq } from 'lodash'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { import {
Bet, Bet,
@ -51,7 +51,7 @@ export const useUserBetContracts = (
return listenForUserBets( return listenForUserBets(
userId, userId,
(bets) => { (bets) => {
const contractIds = _.uniq(bets.map((bet) => bet.contractId)) const contractIds = uniq(bets.map((bet) => bet.contractId))
setContractIds(contractIds) setContractIds(contractIds)
localStorage.setItem(key, JSON.stringify(contractIds)) localStorage.setItem(key, JSON.stringify(contractIds))
}, },

View File

@ -1,4 +1,3 @@
import _ from 'lodash'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { PrivateUser } from 'common/user' import { PrivateUser } from 'common/user'
import { import {

View File

@ -5,7 +5,7 @@ import {
where, where,
orderBy, orderBy,
} from 'firebase/firestore' } from 'firebase/firestore'
import _ from 'lodash' import { range } from 'lodash'
import { db } from './init' import { db } from './init'
import { Bet } from 'common/bet' 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 query = getBetsQuery(startTime, startTime + DAY_IN_MS * numberOfDays)
const bets = await getValues<Bet>(query) 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) { for (const bet of bets) {
const dayIndex = Math.floor((bet.createdTime - startTime) / DAY_IN_MS) const dayIndex = Math.floor((bet.createdTime - startTime) / DAY_IN_MS)
betsByDay[dayIndex].push(bet) betsByDay[dayIndex].push(bet)

View File

@ -7,7 +7,7 @@ import {
setDoc, setDoc,
where, where,
} from 'firebase/firestore' } from 'firebase/firestore'
import _ from 'lodash' import { range } from 'lodash'
import { getValues, listenForValues } from './utils' import { getValues, listenForValues } from './utils'
import { db } from './init' import { db } from './init'
@ -117,7 +117,7 @@ export async function getDailyComments(
) )
const comments = await getValues<Comment>(query) 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) { for (const comment of comments) {
const dayIndex = Math.floor((comment.createdTime - startTime) / DAY_IN_MS) const dayIndex = Math.floor((comment.createdTime - startTime) / DAY_IN_MS)
commentsByDay[dayIndex].push(comment) commentsByDay[dayIndex].push(comment)

View File

@ -13,7 +13,7 @@ import {
updateDoc, updateDoc,
limit, limit,
} from 'firebase/firestore' } from 'firebase/firestore'
import _ from 'lodash' import { range, sortBy } from 'lodash'
import { app } from './init' import { app } from './init'
import { getValues, listenForValue, listenForValues } from './utils' import { getValues, listenForValue, listenForValues } from './utils'
@ -225,7 +225,7 @@ export function listenForHotContracts(
setHotContracts: (contracts: Contract[]) => void setHotContracts: (contracts: Contract[]) => void
) { ) {
return listenForValues<Contract>(hotContractsQuery, (contracts) => { return listenForValues<Contract>(hotContractsQuery, (contracts) => {
const hotContracts = _.sortBy( const hotContracts = sortBy(
chooseRandomSubset(contracts, 4), chooseRandomSubset(contracts, 4),
(contract) => contract.volume24Hours (contract) => contract.volume24Hours
) )
@ -235,7 +235,7 @@ export function listenForHotContracts(
export async function getHotContracts() { export async function getHotContracts() {
const contracts = await getValues<Contract>(hotContractsQuery) const contracts = await getValues<Contract>(hotContractsQuery)
return _.sortBy( return sortBy(
chooseRandomSubset(contracts, 10), chooseRandomSubset(contracts, 10),
(contract) => -1 * contract.volume24Hours (contract) => -1 * contract.volume24Hours
) )
@ -245,7 +245,7 @@ export async function getContractsBySlugs(slugs: string[]) {
const q = query(contractCollection, where('slug', 'in', slugs)) const q = query(contractCollection, where('slug', 'in', slugs))
const snapshot = await getDocs(q) const snapshot = await getDocs(q)
const contracts = snapshot.docs.map((doc) => doc.data() as Contract) 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( const topWeeklyQuery = query(
@ -269,7 +269,7 @@ const closingSoonQuery = query(
export async function getClosingSoonContracts() { export async function getClosingSoonContracts() {
const contracts = await getValues<Contract>(closingSoonQuery) const contracts = await getValues<Contract>(closingSoonQuery)
return _.sortBy( return sortBy(
chooseRandomSubset(contracts, 2), chooseRandomSubset(contracts, 2),
(contract) => contract.closeTime (contract) => contract.closeTime
) )
@ -295,7 +295,7 @@ export async function getDailyContracts(
) )
const contracts = await getValues<Contract>(query) 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) { for (const contract of contracts) {
const dayIndex = Math.floor((contract.createdTime - startTime) / DAY_IN_MS) const dayIndex = Math.floor((contract.createdTime - startTime) / DAY_IN_MS)
contractsByDay[dayIndex].push(contract) contractsByDay[dayIndex].push(contract)

View File

@ -10,7 +10,7 @@ import {
updateDoc, updateDoc,
where, where,
} from 'firebase/firestore' } from 'firebase/firestore'
import _ from 'lodash' import { sortBy } from 'lodash'
import { Fold } from 'common/fold' import { Fold } from 'common/fold'
import { Contract, contractCollection } from './contracts' import { Contract, contractCollection } from './contracts'
import { db } from './init' 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( export function listenForFoldsWithTags(
@ -187,7 +187,7 @@ export function listenForFoldsWithTags(
) )
return listenForValues<Fold>(q, (folds) => { return listenForValues<Fold>(q, (folds) => {
const sorted = _.sortBy(folds, (fold) => -1 * fold.followCount) const sorted = sortBy(folds, (fold) => -1 * fold.followCount)
setFolds(sorted) setFolds(sorted)
}) })
} }

View File

@ -18,7 +18,7 @@ import {
GoogleAuthProvider, GoogleAuthProvider,
signInWithPopup, signInWithPopup,
} from 'firebase/auth' } from 'firebase/auth'
import _ from 'lodash' import { range, throttle, zip } from 'lodash'
import { app } from './init' import { app } from './init'
import { PrivateUser, User } from 'common/user' import { PrivateUser, User } from 'common/user'
@ -83,7 +83,7 @@ const CACHED_USER_KEY = 'CACHED_USER_KEY'
// used to avoid weird race condition // used to avoid weird race condition
let createUserPromise: Promise<User | null> | undefined = undefined 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) { export function listenForLogin(onUser: (user: User | null) => void) {
const cachedUser = localStorage.getItem(CACHED_USER_KEY) const cachedUser = localStorage.getItem(CACHED_USER_KEY)
@ -210,7 +210,7 @@ export async function getDailyNewUsers(
const query = getUsersQuery(startTime, startTime + DAY_MS * numberOfDays) const query = getUsersQuery(startTime, startTime + DAY_MS * numberOfDays)
const users = await getValues<User>(query) 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) { for (const user of users) {
const dayIndex = Math.floor((user.createdTime - startTime) / DAY_MS) const dayIndex = Math.floor((user.createdTime - startTime) / DAY_MS)
usersByDay[dayIndex].push(user) usersByDay[dayIndex].push(user)
@ -235,5 +235,5 @@ export async function getCategoryFeeds(userId: string) {
) )
) )
const feeds = feedData.map((data) => data?.feed ?? []) 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][])
} }

View File

@ -6,6 +6,11 @@ module.exports = {
experimental: { experimental: {
externalDir: true, externalDir: true,
optimizeCss: true, optimizeCss: true,
modularizeImports: {
lodash: {
transform: 'lodash/{{member}}',
},
},
}, },
images: { images: {
domains: ['lh3.googleusercontent.com', 'i.imgur.com'], domains: ['lh3.googleusercontent.com', 'i.imgur.com'],

View File

@ -1,6 +1,6 @@
import React, { useEffect, useMemo, useState } from 'react' import React, { useEffect, useMemo, useState } from 'react'
import { ArrowLeftIcon } from '@heroicons/react/outline' 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 { useContractWithPreload } from 'web/hooks/use-contract'
import { ContractOverview } from 'web/components/contract/contract-overview' import { ContractOverview } from 'web/components/contract/contract-overview'
@ -250,13 +250,13 @@ function ContractLeaderboard(props: { contract: Contract; bets: Bet[] }) {
const { userProfits, top5Ids } = useMemo(() => { const { userProfits, top5Ids } = useMemo(() => {
// Create a map of userIds to total profits (including sales) // Create a map of userIds to total profits (including sales)
const betsByUser = _.groupBy(bets, 'userId') const betsByUser = groupBy(bets, 'userId')
const userProfits = _.mapValues(betsByUser, (bets) => const userProfits = mapValues(betsByUser, (bets) =>
_.sumBy(bets, (bet) => resolvedPayout(contract, bet) - bet.amount) sumBy(bets, (bet) => resolvedPayout(contract, bet) - bet.amount)
) )
// Find the 5 users with the most profits // Find the 5 users with the most profits
const top5Ids = _.entries(userProfits) const top5Ids = Object.entries(userProfits)
.sort(([i1, p1], [i2, p2]) => p2 - p1) .sort(([_i1, p1], [_i2, p2]) => p2 - p1)
.filter(([, p]) => p > 0) .filter(([, p]) => p > 0)
.slice(0, 5) .slice(0, 5)
.map(([id]) => id) .map(([id]) => id)
@ -267,7 +267,7 @@ function ContractLeaderboard(props: { contract: Contract; bets: Bet[] }) {
console.log('foo') console.log('foo')
if (top5Ids.length > 0) { if (top5Ids.length > 0) {
listUsers(top5Ids).then((users) => { listUsers(top5Ids).then((users) => {
const sortedUsers = _.sortBy(users, (user) => -userProfits[user.id]) const sortedUsers = sortBy(users, (user) => -userProfits[user.id])
setUsers(sortedUsers) setUsers(sortedUsers)
}) })
} }
@ -294,8 +294,8 @@ function ContractTopTrades(props: {
comments: Comment[] comments: Comment[]
}) { }) {
const { contract, bets, comments } = props const { contract, bets, comments } = props
const commentsById = _.keyBy(comments, 'id') const commentsById = keyBy(comments, 'id')
const betsById = _.keyBy(bets, 'id') const betsById = keyBy(bets, 'id')
// If 'id2' is the sale of 'id1', both are logged with (id2 - id1) of profit // If 'id2' is the sale of 'id1', both are logged with (id2 - id1) of profit
// Otherwise, we record the profit at resolution time // Otherwise, we record the profit at resolution time
@ -312,11 +312,11 @@ function ContractTopTrades(props: {
} }
// Now find the betId with the highest profit // 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) const topBettor = useUserById(betsById[topBetId]?.userId)
// And also the commentId of the comment with the highest profit // And also the commentId of the comment with the highest profit
const topCommentId = _.sortBy( const topCommentId = sortBy(
comments, comments,
(c) => c.betId && -profitById[c.betId] (c) => c.betId && -profitById[c.betId]
)[0]?.id )[0]?.id

View File

@ -6,7 +6,7 @@ import dayjs from 'dayjs'
import { usePrivateUsers, useUsers } from 'web/hooks/use-users' import { usePrivateUsers, useUsers } from 'web/hooks/use-users'
import Custom404 from './404' import Custom404 from './404'
import { useContracts } from 'web/hooks/use-contracts' import { useContracts } from 'web/hooks/use-contracts'
import _ from 'lodash' import { mapKeys } from 'lodash'
import { useAdmin } from 'web/hooks/use-admin' import { useAdmin } from 'web/hooks/use-admin'
import { contractPath } from 'web/lib/firebase/contracts' import { contractPath } from 'web/lib/firebase/contracts'
@ -23,7 +23,7 @@ function UsersTable() {
let privateUsers = usePrivateUsers() let privateUsers = usePrivateUsers()
// Map private users by user id // Map private users by user id
const privateUsersById = _.mapKeys(privateUsers, 'id') const privateUsersById = mapKeys(privateUsers, 'id')
console.log('private users by id', privateUsersById) console.log('private users by id', privateUsersById)
// For each user, set their email from the PrivateUser // For each user, set their email from the PrivateUser

View File

@ -1,5 +1,5 @@
import dayjs from 'dayjs' import dayjs from 'dayjs'
import _ from 'lodash' import { zip, uniq, sumBy } from 'lodash'
import { IS_PRIVATE_MANIFOLD } from 'common/envs/constants' import { IS_PRIVATE_MANIFOLD } from 'common/envs/constants'
import { import {
DailyCountChart, DailyCountChart,
@ -40,12 +40,12 @@ export async function getStaticPropz() {
) )
const dailyCommentCounts = dailyComments.map((comments) => comments.length) const dailyCommentCounts = dailyComments.map((comments) => comments.length)
const dailyUserIds = _.zip(dailyContracts, dailyBets, dailyComments).map( const dailyUserIds = zip(dailyContracts, dailyBets, dailyComments).map(
([contracts, bets, comments]) => { ([contracts, bets, comments]) => {
const creatorIds = (contracts ?? []).map((c) => c.creatorId) const creatorIds = (contracts ?? []).map((c) => c.creatorId)
const betUserIds = (bets ?? []).map((bet) => bet.userId) const betUserIds = (bets ?? []).map((bet) => bet.userId)
const commentUserIds = (comments ?? []).map((comment) => comment.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++) { for (let j = lastWeek.start; j <= lastWeek.end; j++) {
dailyUserIds[j].forEach((userId) => activeLastWeek.add(userId)) 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 activeLastWeek.has(userId) ? 1 : 0
) )
const retainedFrac = retainedCount / activeTwoWeeksAgo.size const retainedFrac = retainedCount / activeTwoWeeksAgo.size
@ -112,7 +112,7 @@ export async function getStaticPropz() {
for (let j = lastMonth.start; j <= lastMonth.end; j++) { for (let j = lastMonth.start; j <= lastMonth.end; j++) {
dailyUserIds[j].forEach((userId) => activeLastMonth.add(userId)) 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 activeLastMonth.has(userId) ? 1 : 0
) )
const retainedFrac = retainedCount / activeTwoMonthsAgo.size const retainedFrac = retainedCount / activeTwoMonthsAgo.size

View File

@ -1,4 +1,4 @@
import _ from 'lodash' import { sortBy, sumBy, uniqBy } from 'lodash'
import clsx from 'clsx' import clsx from 'clsx'
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { Col } from 'web/components/layout/col' import { Col } from 'web/components/layout/col'
@ -41,13 +41,13 @@ function CharityPage(props: { charity: Charity }) {
const user = useUser() const user = useUser()
const txns = useCharityTxns(charity.id) const txns = useCharityTxns(charity.id)
const newToOld = _.sortBy(txns, (txn) => -txn.createdTime) const newToOld = sortBy(txns, (txn) => -txn.createdTime)
const totalRaised = _.sumBy(txns, (txn) => txn.amount) const totalRaised = sumBy(txns, (txn) => txn.amount)
const fromYou = _.sumBy( const fromYou = sumBy(
txns.filter((txn) => txn.fromId === user?.id), txns.filter((txn) => txn.fromId === user?.id),
(txn) => txn.amount (txn) => txn.amount
) )
const numSupporters = _.uniqBy(txns, (txn) => txn.fromId).length const numSupporters = uniqBy(txns, (txn) => txn.fromId).length
const { width, height } = useWindowSize() const { width, height } = useWindowSize()
const [showConfetti, setShowConfetti] = useState(false) const [showConfetti, setShowConfetti] = useState(false)

View File

@ -1,4 +1,4 @@
import _ from 'lodash' import { mapValues, groupBy, sumBy, sum, sortBy, debounce } from 'lodash'
import { useState, useMemo } from 'react' import { useState, useMemo } from 'react'
import { charities, Charity as CharityType } from 'common/charity' import { charities, Charity as CharityType } from 'common/charity'
import { CharityCard } from 'web/components/charity/charity-card' import { CharityCard } from 'web/components/charity/charity-card'
@ -12,11 +12,11 @@ import { formatMoney } from 'common/util/format'
export async function getStaticProps() { export async function getStaticProps() {
const txns = await getAllCharityTxns() const txns = await getAllCharityTxns()
const totals = _.mapValues(_.groupBy(txns, 'toId'), (txns) => const totals = mapValues(groupBy(txns, 'toId'), (txns) =>
_.sumBy(txns, (txn) => txn.amount) sumBy(txns, (txn) => txn.amount)
) )
const totalRaised = _.sum(Object.values(totals)) const totalRaised = sum(Object.values(totals))
const sortedCharities = _.sortBy(charities, [ const sortedCharities = sortBy(charities, [
(charity) => (charity.tags?.includes('Featured') ? 0 : 1), (charity) => (charity.tags?.includes('Featured') ? 0 : 1),
(charity) => -totals[charity.id], (charity) => -totals[charity.id],
]) ])
@ -37,7 +37,7 @@ export default function Charity(props: {
const { totalRaised, charities } = props const { totalRaised, charities } = props
const [query, setQuery] = useState('') const [query, setQuery] = useState('')
const debouncedQuery = _.debounce(setQuery, 50) const debouncedQuery = debounce(setQuery, 50)
const filterCharities = useMemo( const filterCharities = useMemo(
() => () =>

View File

@ -1,4 +1,4 @@
import _ from 'lodash' import { flatten, take, partition, sortBy } from 'lodash'
import { Fold } from 'common/fold' import { Fold } from 'common/fold'
import { Comment } from 'common/comment' import { Comment } from 'common/comment'
@ -47,8 +47,8 @@ export async function getStaticPropz(props: { params: { slugs: string[] } }) {
contracts.map((contract) => listAllBets(contract.id)) contracts.map((contract) => listAllBets(contract.id))
) )
let activeContracts = findActiveContracts(contracts, [], _.flatten(bets), {}) let activeContracts = findActiveContracts(contracts, [], flatten(bets), {})
const [resolved, unresolved] = _.partition( const [resolved, unresolved] = partition(
activeContracts, activeContracts,
({ isResolved }) => isResolved ({ isResolved }) => isResolved
) )
@ -80,8 +80,8 @@ export async function getStaticPropz(props: { params: { slugs: string[] } }) {
} }
async function toTopUsers(userScores: { [userId: string]: number }) { async function toTopUsers(userScores: { [userId: string]: number }) {
const topUserPairs = _.take( const topUserPairs = take(
_.sortBy(Object.entries(userScores), ([_, score]) => -1 * score), sortBy(Object.entries(userScores), ([_, score]) => -1 * score),
10 10
).filter(([_, score]) => score >= 0.5) ).filter(([_, score]) => score >= 0.5)
@ -134,7 +134,7 @@ export default function FoldPage(props: {
const isCurator = user && fold && user.id === fold.curatorId const isCurator = user && fold && user.id === fold.curatorId
const taggedContracts = useTaggedContracts(fold?.tags) ?? props.contracts const taggedContracts = useTaggedContracts(fold?.tags) ?? props.contracts
const contractsMap = _.fromPairs( const contractsMap = Object.fromEntries(
taggedContracts.map((contract) => [contract.id, contract]) taggedContracts.map((contract) => [contract.id, contract])
) )

View File

@ -1,4 +1,4 @@
import _ from 'lodash' import { sortBy, debounce } from 'lodash'
import Link from 'next/link' import Link from 'next/link'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { Fold } from 'common/fold' import { Fold } from 'common/fold'
@ -21,7 +21,7 @@ export async function getStaticProps() {
const curators = await Promise.all( const curators = await Promise.all(
folds.map((fold) => getUser(fold.curatorId)) folds.map((fold) => getUser(fold.curatorId))
) )
const curatorsDict = _.fromPairs( const curatorsDict = Object.fromEntries(
curators.map((curator) => [curator.id, curator]) curators.map((curator) => [curator.id, curator])
) )
@ -37,7 +37,7 @@ export async function getStaticProps() {
export default function Folds(props: { export default function Folds(props: {
folds: Fold[] folds: Fold[]
curatorsDict: _.Dictionary<User> curatorsDict: { [k: string]: User }
}) { }) {
const [curatorsDict, setCuratorsDict] = useState(props.curatorsDict) const [curatorsDict, setCuratorsDict] = useState(props.curatorsDict)
@ -51,7 +51,7 @@ export default function Folds(props: {
if (newFolds.length > 0) { if (newFolds.length > 0) {
Promise.all(newFolds.map(({ curatorId }) => getUser(curatorId))).then( Promise.all(newFolds.map(({ curatorId }) => getUser(curatorId))).then(
(newUsers) => { (newUsers) => {
const newUsersDict = _.fromPairs( const newUsersDict = Object.fromEntries(
newUsers.map((user) => [user.id, user]) newUsers.map((user) => [user.id, user])
) )
setCuratorsDict({ ...curatorsDict, ...newUsersDict }) setCuratorsDict({ ...curatorsDict, ...newUsersDict })
@ -68,7 +68,7 @@ export default function Folds(props: {
} }
// List followed folds first, then folds with the highest follower count // 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) => !followedFoldIds.includes(fold.id),
(fold) => -1 * fold.followCount, (fold) => -1 * fold.followCount,
]).filter( ]).filter(
@ -79,7 +79,7 @@ export default function Folds(props: {
check(f.lowercaseTags.map((tag) => `#${tag}`).join(' ')) check(f.lowercaseTags.map((tag) => `#${tag}`).join(' '))
) )
// Not strictly necessary, but makes the "hold delete" experience less laggy // Not strictly necessary, but makes the "hold delete" experience less laggy
const debouncedQuery = _.debounce(setQuery, 50) const debouncedQuery = debounce(setQuery, 50)
return ( return (
<Page> <Page>

View File

@ -1,4 +1,4 @@
import _ from 'lodash' import { sortBy } from 'lodash'
import { GetServerSideProps } from 'next' import { GetServerSideProps } from 'next'
import { getServerSideSitemap, ISitemapField } from 'next-sitemap' 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 response = await fetch(`https://${DOMAIN}/api/v0/markets`)
const liteMarkets = (await response.json()) as LiteMarket[] 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) => ({ const fields = sortedMarkets.map((market) => ({
// See https://www.sitemaps.org/protocol.html // See https://www.sitemaps.org/protocol.html

View File

@ -2394,6 +2394,13 @@ eslint-plugin-jsx-a11y@^6.5.1:
language-tags "^1.0.5" language-tags "^1.0.5"
minimatch "^3.0.4" 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: eslint-plugin-react-hooks@^4.5.0:
version "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" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.5.0.tgz#5f762dfedf8b2cf431c689f533c9d3fa5dcf25ad"