Merge branch 'main' into notifications

This commit is contained in:
Ian Philips 2022-05-31 09:24:37 -06:00 committed by GitHub
commit 63bdc2c2f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 83 additions and 55 deletions

View File

@ -116,7 +116,7 @@ export function getFreeAnswerAnte(
}
export function getNumericAnte(
creator: User,
anteBettorId: string,
contract: FullContract<DPM, Numeric>,
ante: number,
newBetId: string
@ -136,7 +136,7 @@ export function getNumericAnte(
const anteBet: NumericBet = {
id: newBetId,
userId: creator.id,
userId: anteBettorId,
contractId: contract.id,
amount: ante,
allBetAmounts,

View File

@ -131,9 +131,13 @@ export function getContractBetMetrics(contract: Contract, yourBets: Bet[]) {
let loan = 0
let saleValue = 0
let redeemed = 0
const totalShares: { [outcome: string]: number } = {}
for (const bet of yourBets) {
const { isSold, sale, amount, loanAmount, isRedemption } = bet
const { isSold, sale, amount, loanAmount, isRedemption, shares, outcome } =
bet
totalShares[outcome] = (totalShares[outcome] ?? 0) + shares
if (isSold) {
totalInvested += amount
} else if (sale) {
@ -165,6 +169,7 @@ export function getContractBetMetrics(contract: Contract, yourBets: Bet[]) {
netPayout,
profit,
profitPercent,
totalShares,
}
}
@ -175,6 +180,7 @@ export function getContractBetNullMetrics() {
netPayout: 0,
profit: 0,
profitPercent: 0,
totalShares: {} as { [outcome: string]: number },
}
}

View File

@ -170,7 +170,7 @@ export const createContract = newEndpoint(['POST'], async (req, [user, _]) => {
.doc()
const anteBet = getNumericAnte(
user,
providerId,
contract as FullContract<DPM, Numeric>,
ante,
anteBetDoc.id

View File

@ -125,13 +125,13 @@ export function BetsList(props: { user: User }) {
.filter((c) => {
if (filter === 'all') return true
const metrics = contractsMetrics[c.id]
const { totalShares } = contractsMetrics[c.id]
const hasSoldAll = Object.values(totalShares).every(
(shares) => shares === 0
)
// Filter for contracts you sold out of.
if (filter === 'sold') return metrics.payout === 0
// Filter for contracts where you currently have shares.
return metrics.payout > 0
if (filter === 'sold') return hasSoldAll
return !hasSoldAll
})
const [settled, unsettled] = partition(

View File

@ -28,6 +28,7 @@ import { AvatarDetails, MiscDetails } from './contract-details'
import { getExpectedValue, getValueFromBucket } from 'common/calculate-dpm'
import { QuickBet, ProbBar, getColor } from './quick-bet'
import { useContractWithPreload } from 'web/hooks/use-contract'
import { useUser } from 'web/hooks/use-user'
export function ContractCard(props: {
contract: Contract
@ -40,10 +41,16 @@ export function ContractCard(props: {
const { question, outcomeType } = contract
const { resolution } = contract
const marketClosed = (contract.closeTime || Infinity) < Date.now()
const user = useUser()
const marketClosed =
(contract.closeTime || Infinity) < Date.now() || !!resolution
const showQuickBet = !(
!user ||
marketClosed ||
(outcomeType === 'FREE_RESPONSE' && getTopAnswer(contract) === undefined)
(outcomeType === 'FREE_RESPONSE' && getTopAnswer(contract) === undefined) ||
outcomeType === 'NUMERIC'
)
return (
@ -96,7 +103,7 @@ export function ContractCard(props: {
/>
</Col>
{showQuickBet ? (
<QuickBet contract={contract} />
<QuickBet contract={contract} user={user} />
) : (
<Col className="m-auto pl-2">
{outcomeType === 'BINARY' && (

View File

@ -19,7 +19,7 @@ export function ContractsGrid(props: {
const isBottomVisible = useIsVisible(elem)
useEffect(() => {
if (isBottomVisible) {
if (isBottomVisible && hasMore) {
loadMore()
}
}, [isBottomVisible, hasMore, loadMore])

View File

@ -14,6 +14,7 @@ import {
NumericContract,
FreeResponseContract,
} from 'common/contract'
import { User } from 'common/user'
import {
formatLargeNumber,
formatMoney,
@ -21,7 +22,6 @@ import {
} from 'common/util/format'
import { useState } from 'react'
import toast from 'react-hot-toast'
import { useUser } from 'web/hooks/use-user'
import { useUserContractBets } from 'web/hooks/use-user-bets'
import { placeBet } from 'web/lib/firebase/api-call'
import { getBinaryProb, getBinaryProbPercent } from 'web/lib/firebase/contracts'
@ -33,11 +33,10 @@ import { useSaveShares } from '../use-save-shares'
const BET_SIZE = 10
export function QuickBet(props: { contract: Contract }) {
const { contract } = props
export function QuickBet(props: { contract: Contract; user: User }) {
const { contract, user } = props
const user = useUser()
const userBets = useUserContractBets(user?.id, contract.id)
const userBets = useUserContractBets(user.id, contract.id)
const topAnswer =
contract.outcomeType === 'FREE_RESPONSE'
? getTopAnswer(contract as FreeResponseContract)

View File

@ -479,9 +479,7 @@ export function getRecentContractActivityItems(
}
) {
const { contractPath } = options
bets = bets
.filter((bet) => !bet.isRedemption)
.sort((b1, b2) => b1.createdTime - b2.createdTime)
bets = bets.sort((b1, b2) => b1.createdTime - b2.createdTime)
comments = comments.sort((c1, c2) => c1.createdTime - c2.createdTime)
const questionItem: QuestionItem = {

View File

@ -39,7 +39,7 @@ export function ContractActivity(props: {
// eslint-disable-next-line react-hooks/rules-of-hooks
const updatedBets = mode === 'only-recent' ? undefined : useBets(contract.id)
const bets = updatedBets ?? props.bets
const bets = (updatedBets ?? props.bets).filter((bet) => !bet.isRedemption)
const items =
mode === 'only-recent'

View File

@ -16,6 +16,7 @@ import { Avatar } from '../avatar'
import clsx from 'clsx'
import { useRouter } from 'next/router'
import NotificationsIcon from 'web/components/notifications-icon'
import { useIsIframe } from 'web/hooks/use-is-iframe'
function getNavigation(username: string) {
return [
@ -47,6 +48,11 @@ export function BottomNavBar() {
const user = useUser()
const isIframe = useIsIframe()
if (isIframe) {
return null
}
const navigationOptions =
user === null
? signedOutNavigation

View File

@ -4,29 +4,6 @@ import { formatMoney } from 'common/util/format'
import { Avatar } from '../avatar'
import { IS_PRIVATE_MANIFOLD } from 'common/envs/constants'
export function getNavigationOptions(user?: User | null) {
if (IS_PRIVATE_MANIFOLD) {
return [{ name: 'Leaderboards', href: '/leaderboards' }]
}
if (!user) {
return [
{ name: 'Leaderboards', href: '/leaderboards' },
{ name: 'Discord', href: 'https://discord.gg/eHQBNBqXuh' },
{ name: 'Twitter', href: 'https://twitter.com/ManifoldMarkets' },
]
}
return [
{ name: 'Add funds', href: '/add-funds' },
{ name: 'Leaderboards', href: '/leaderboards' },
{ name: 'Discord', href: 'https://discord.gg/eHQBNBqXuh' },
{ name: 'Twitter', href: 'https://twitter.com/ManifoldMarkets' },
{ name: 'About', href: 'https://docs.manifold.markets' },
{ name: 'Sign out', href: '#', onClick: () => firebaseLogout() },
]
}
export function ProfileSummary(props: { user: User }) {
const { user } = props
return (

View File

@ -9,6 +9,7 @@ import {
PresentationChartLineIcon,
ChatAltIcon,
SparklesIcon,
NewspaperIcon,
} from '@heroicons/react/outline'
import clsx from 'clsx'
import { sortBy } from 'lodash'
@ -16,17 +17,18 @@ import Link from 'next/link'
import { useRouter } from 'next/router'
import { useFollowedFolds } from 'web/hooks/use-fold'
import { useUser } from 'web/hooks/use-user'
import { firebaseLogin, firebaseLogout } from 'web/lib/firebase/users'
import { firebaseLogin, firebaseLogout, User } from 'web/lib/firebase/users'
import { ManifoldLogo } from './manifold-logo'
import { MenuButton } from './menu'
import { getNavigationOptions, ProfileSummary } from './profile-menu'
import { ProfileSummary } from './profile-menu'
import {
getUtcFreeMarketResetTime,
useHasCreatedContractToday,
} from 'web/hooks/use-has-created-contract-today'
import { Row } from '../layout/row'
import { useEffect, useState } from 'react'
import NotificationsIcon from 'web/components/notifications-icon'
import React, { useEffect, useState } from 'react'
import { IS_PRIVATE_MANIFOLD } from 'common/envs/constants'
// Create an icon from the url of an image
function IconFromUrl(url: string): React.ComponentType<{ className?: string }> {
@ -53,6 +55,30 @@ function getNavigation(username: string) {
]
}
function getMoreNavigation(user?: User | null) {
if (IS_PRIVATE_MANIFOLD) {
return [{ name: 'Leaderboards', href: '/leaderboards' }]
}
if (!user) {
return [
{ name: 'Leaderboards', href: '/leaderboards' },
{ name: 'Discord', href: 'https://discord.gg/eHQBNBqXuh' },
{ name: 'Twitter', href: 'https://twitter.com/ManifoldMarkets' },
]
}
return [
{ name: 'Add funds', href: '/add-funds' },
{ name: 'Leaderboards', href: '/leaderboards' },
{ name: 'Blog', href: 'https://news.manifold.markets' },
{ name: 'Discord', href: 'https://discord.gg/eHQBNBqXuh' },
{ name: 'Twitter', href: 'https://twitter.com/ManifoldMarkets' },
{ name: 'About', href: 'https://docs.manifold.markets' },
{ name: 'Sign out', href: '#', onClick: () => firebaseLogout() },
]
}
const signedOutNavigation = [
{ name: 'Home', href: '/home', icon: HomeIcon },
{ name: 'Explore', href: '/markets', icon: SearchIcon },
@ -63,6 +89,7 @@ const signedOutNavigation = [
const signedOutMobileNavigation = [
{ name: 'Charity', href: '/charity', icon: HeartIcon },
{ name: 'Leaderboards', href: '/leaderboards', icon: CakeIcon },
{ name: 'Blog', href: 'https://news.manifold.markets', icon: NewspaperIcon },
{
name: 'Discord',
href: 'https://discord.gg/eHQBNBqXuh',
@ -199,7 +226,7 @@ export default function Sidebar(props: { className?: string }) {
))}
<MenuButton
menuItems={getNavigationOptions(user)}
menuItems={getMoreNavigation(user)}
buttonContent={<MoreButton />}
/>
</div>
@ -208,7 +235,7 @@ export default function Sidebar(props: { className?: string }) {
{user ? (
<Link href={'/create'} passHref>
<button className={clsx(gradient, buttonStyle)}>
Ask a question
Create a question
</button>
</Link>
) : (

View File

@ -4,10 +4,10 @@
"private": true,
"scripts": {
"dev": "concurrently -n NEXT,TS -c magenta,cyan \"next dev -p 3000\" \"yarn ts --watch\"",
"devdev": "NEXT_PUBLIC_FIREBASE_ENV=DEV concurrently -n NEXT,TS -c magenta,cyan \"FIREBASE_ENV=DEV next dev -p 3000\" \"FIREBASE_ENV=DEV yarn ts --watch\" # see https://github.com/vercel/next.js/discussions/33634",
"devdev": "cross-env NEXT_PUBLIC_FIREBASE_ENV=DEV concurrently -n NEXT,TS -c magenta,cyan \"cross-env FIREBASE_ENV=DEV next dev -p 3000\" \"cross-env FIREBASE_ENV=DEV yarn ts --watch\"",
"dev:dev": "yarn devdev",
"dev:the": "NEXT_PUBLIC_FIREBASE_ENV=THEOREMONE concurrently -n NEXT,TS -c magenta,cyan \"FIREBASE_ENV=THEOREMONE next dev -p 3000\" \"FIREBASE_ENV=THEOREMONE yarn ts --watch\"",
"dev:emulate": "NEXT_PUBLIC_FIREBASE_EMULATE=TRUE yarn devdev",
"dev:the": "cross-env NEXT_PUBLIC_FIREBASE_ENV=THEOREMONE concurrently -n NEXT,TS -c magenta,cyan \"cross-env FIREBASE_ENV=THEOREMONE next dev -p 3000\" \"cross-env FIREBASE_ENV=THEOREMONE yarn ts --watch\"",
"dev:emulate": "cross-env NEXT_PUBLIC_FIREBASE_EMULATE=TRUE yarn devdev",
"ts": "tsc --noEmit --incremental --preserveWatchOutput --pretty",
"build": "next build",
"start": "next start",
@ -48,6 +48,7 @@
"autoprefixer": "10.2.6",
"concurrently": "6.5.1",
"critters": "0.0.16",
"cross-env": "^7.0.3",
"eslint-config-next": "12.1.6",
"next-sitemap": "^2.5.14",
"postcss": "8.3.5",

View File

@ -1828,7 +1828,14 @@ critters@0.0.16:
postcss "^8.3.7"
pretty-bytes "^5.3.0"
cross-spawn@^7.0.2:
cross-env@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf"
integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==
dependencies:
cross-spawn "^7.0.1"
cross-spawn@^7.0.1, cross-spawn@^7.0.2:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==