Add countdown timer for daily free market (#276)
* Add countdown timer for daily free market * Reset example numbers * Remove daily * Free market reset => 4pm UTC
This commit is contained in:
parent
355b2261a7
commit
d0347ff5c2
|
@ -87,13 +87,17 @@ export const createContract = newEndpoint(['POST'], async (req, _res) => {
|
||||||
)
|
)
|
||||||
throw new APIError(400, 'Invalid initial probability')
|
throw new APIError(400, 'Invalid initial probability')
|
||||||
|
|
||||||
// uses utc time on server:
|
// Uses utc time on server:
|
||||||
const today = new Date().setHours(0, 0, 0, 0)
|
const yesterday = new Date()
|
||||||
|
yesterday.setUTCDate(yesterday.getUTCDate() - 1)
|
||||||
|
const freeMarketResetTime = yesterday.setUTCHours(16, 0, 0, 0)
|
||||||
|
|
||||||
const userContractsCreatedTodaySnapshot = await firestore
|
const userContractsCreatedTodaySnapshot = await firestore
|
||||||
.collection(`contracts`)
|
.collection(`contracts`)
|
||||||
.where('creatorId', '==', creator.id)
|
.where('creatorId', '==', creator.id)
|
||||||
.where('createdTime', '>=', today)
|
.where('createdTime', '>=', freeMarketResetTime)
|
||||||
.get()
|
.get()
|
||||||
|
console.log('free market reset time: ', freeMarketResetTime)
|
||||||
const isFree = userContractsCreatedTodaySnapshot.size === 0
|
const isFree = userContractsCreatedTodaySnapshot.size === 0
|
||||||
|
|
||||||
const ante = FIXED_ANTE
|
const ante = FIXED_ANTE
|
||||||
|
|
|
@ -20,8 +20,12 @@ import { firebaseLogin, firebaseLogout } from 'web/lib/firebase/users'
|
||||||
import { ManifoldLogo } from './manifold-logo'
|
import { ManifoldLogo } from './manifold-logo'
|
||||||
import { MenuButton } from './menu'
|
import { MenuButton } from './menu'
|
||||||
import { getNavigationOptions, ProfileSummary } from './profile-menu'
|
import { getNavigationOptions, ProfileSummary } from './profile-menu'
|
||||||
import { useHasCreatedContractToday } from 'web/hooks/use-has-created-contract-today'
|
import {
|
||||||
|
getUtcFreeMarketResetTimeToday,
|
||||||
|
useHasCreatedContractToday,
|
||||||
|
} from 'web/hooks/use-has-created-contract-today'
|
||||||
import { Row } from '../layout/row'
|
import { Row } from '../layout/row'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
// Create an icon from the url of an image
|
// Create an icon from the url of an image
|
||||||
function IconFromUrl(url: string): React.ComponentType<{ className?: string }> {
|
function IconFromUrl(url: string): React.ComponentType<{ className?: string }> {
|
||||||
|
@ -121,12 +125,30 @@ export default function Sidebar(props: { className?: string }) {
|
||||||
const { className } = props
|
const { className } = props
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const currentPage = router.pathname
|
const currentPage = router.pathname
|
||||||
|
const [countdown, setCountdown] = useState('...')
|
||||||
|
useEffect(() => {
|
||||||
|
const utcMidnightToLocalDate = new Date(getUtcFreeMarketResetTimeToday())
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
const timeUntil = utcMidnightToLocalDate.getTime() - new Date().getTime()
|
||||||
|
const hoursUntil = 24 + timeUntil / 1000 / 60 / 60
|
||||||
|
const minutesUntil = Math.floor((hoursUntil * 60) % 60)
|
||||||
|
const secondsUntil = Math.floor((hoursUntil * 60 * 60) % 60)
|
||||||
|
const hoursUntilFloor = Math.floor(hoursUntil)
|
||||||
|
const timeString =
|
||||||
|
minutesUntil < 1
|
||||||
|
? `${secondsUntil}s`
|
||||||
|
: hoursUntilFloor < 1
|
||||||
|
? `${minutesUntil}m`
|
||||||
|
: `${hoursUntilFloor}h`
|
||||||
|
setCountdown(timeString)
|
||||||
|
}, 1000)
|
||||||
|
return () => clearInterval(interval)
|
||||||
|
}, [])
|
||||||
|
|
||||||
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 mustWaitForFreeMarketStatus = useHasCreatedContractToday(user)
|
||||||
|
|
||||||
const navigationOptions =
|
const navigationOptions =
|
||||||
user === null
|
user === null
|
||||||
? signedOutNavigation
|
? signedOutNavigation
|
||||||
|
@ -186,13 +208,25 @@ export default function Sidebar(props: { className?: string }) {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{user && deservesDailyFreeMarket && (
|
{user &&
|
||||||
|
mustWaitForFreeMarketStatus != 'loading' &&
|
||||||
|
mustWaitForFreeMarketStatus ? (
|
||||||
<Row className="mt-2 justify-center">
|
<Row className="mt-2 justify-center">
|
||||||
<Row className="gap-1 text-sm text-indigo-400">
|
<Row className="gap-1 text-sm text-gray-400">
|
||||||
Daily free market
|
Next free market in {countdown}
|
||||||
<SparklesIcon className="mt-0.5 h-4 w-4" aria-hidden="true" />
|
|
||||||
</Row>
|
</Row>
|
||||||
</Row>
|
</Row>
|
||||||
|
) : (
|
||||||
|
user &&
|
||||||
|
mustWaitForFreeMarketStatus != 'loading' &&
|
||||||
|
!mustWaitForFreeMarketStatus && (
|
||||||
|
<Row className="mt-2 justify-center">
|
||||||
|
<Row className="gap-1 text-sm text-indigo-400">
|
||||||
|
Daily free market
|
||||||
|
<SparklesIcon className="mt-0.5 h-4 w-4" aria-hidden="true" />
|
||||||
|
</Row>
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
</nav>
|
</nav>
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,16 +4,22 @@ import { User } from 'common/user'
|
||||||
|
|
||||||
let sessionCreatedContractToday = true
|
let sessionCreatedContractToday = true
|
||||||
|
|
||||||
|
export function getUtcFreeMarketResetTimeToday() {
|
||||||
|
// Uses utc time like the server.
|
||||||
|
const utcFreeMarketResetTime = new Date()
|
||||||
|
utcFreeMarketResetTime.setUTCDate(utcFreeMarketResetTime.getUTCDate() - 1)
|
||||||
|
const utcFreeMarketMS = utcFreeMarketResetTime.setUTCHours(16, 0, 0, 0)
|
||||||
|
return utcFreeMarketMS
|
||||||
|
}
|
||||||
|
|
||||||
export const useHasCreatedContractToday = (user: User | null | undefined) => {
|
export const useHasCreatedContractToday = (user: User | null | undefined) => {
|
||||||
const [hasCreatedContractToday, setHasCreatedContractToday] = useState(
|
const [hasCreatedContractToday, setHasCreatedContractToday] = useState<
|
||||||
sessionCreatedContractToday
|
boolean | 'loading'
|
||||||
)
|
>('loading')
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Uses utc time like the server.
|
setHasCreatedContractToday('loading')
|
||||||
const utcTimeString = new Date().toISOString()
|
const todayAtMidnight = getUtcFreeMarketResetTimeToday()
|
||||||
const todayAtMidnight = new Date(utcTimeString).setUTCHours(0, 0, 0, 0)
|
|
||||||
|
|
||||||
async function listUserContractsForToday() {
|
async function listUserContractsForToday() {
|
||||||
if (!user) return
|
if (!user) return
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ export function NewContract(props: { question: string; tag?: string }) {
|
||||||
|
|
||||||
const [ante, setAnte] = useState(FIXED_ANTE)
|
const [ante, setAnte] = useState(FIXED_ANTE)
|
||||||
|
|
||||||
const deservesDailyFreeMarket = !useHasCreatedContractToday(creator)
|
const mustWaitForDailyFreeMarketStatus = useHasCreatedContractToday(creator)
|
||||||
|
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
// if (ante === null && creator) {
|
// if (ante === null && creator) {
|
||||||
|
@ -107,7 +107,9 @@ export function NewContract(props: { question: string; tag?: string }) {
|
||||||
ante !== undefined &&
|
ante !== undefined &&
|
||||||
ante !== null &&
|
ante !== null &&
|
||||||
ante >= MINIMUM_ANTE &&
|
ante >= MINIMUM_ANTE &&
|
||||||
(ante <= balance || deservesDailyFreeMarket) &&
|
(ante <= balance ||
|
||||||
|
(mustWaitForDailyFreeMarketStatus != 'loading' &&
|
||||||
|
!mustWaitForDailyFreeMarketStatus)) &&
|
||||||
// closeTime must be in the future
|
// closeTime must be in the future
|
||||||
closeTime &&
|
closeTime &&
|
||||||
closeTime > Date.now() &&
|
closeTime > Date.now() &&
|
||||||
|
@ -368,13 +370,15 @@ export function NewContract(props: { question: string; tag?: string }) {
|
||||||
<div className="form-control mb-1 items-start">
|
<div className="form-control mb-1 items-start">
|
||||||
<label className="label mb-1 gap-2">
|
<label className="label mb-1 gap-2">
|
||||||
<span>Cost</span>
|
<span>Cost</span>
|
||||||
{!deservesDailyFreeMarket && (
|
{mustWaitForDailyFreeMarketStatus != 'loading' &&
|
||||||
<InfoTooltip
|
mustWaitForDailyFreeMarketStatus && (
|
||||||
text={`Cost to create your market. This amount is used to subsidize trading.`}
|
<InfoTooltip
|
||||||
/>
|
text={`Cost to create your market. This amount is used to subsidize trading.`}
|
||||||
)}
|
/>
|
||||||
|
)}
|
||||||
</label>
|
</label>
|
||||||
{deservesDailyFreeMarket ? (
|
{mustWaitForDailyFreeMarketStatus != 'loading' &&
|
||||||
|
!mustWaitForDailyFreeMarketStatus ? (
|
||||||
<div className="label-text text-primary pl-1">
|
<div className="label-text text-primary pl-1">
|
||||||
<span className={'label-text text-neutral line-through '}>
|
<span className={'label-text text-neutral line-through '}>
|
||||||
{formatMoney(ante)}
|
{formatMoney(ante)}
|
||||||
|
@ -382,21 +386,25 @@ export function NewContract(props: { question: string; tag?: string }) {
|
||||||
FREE
|
FREE
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="label-text text-neutral pl-1">
|
mustWaitForDailyFreeMarketStatus != 'loading' && (
|
||||||
{formatMoney(ante)}
|
<div className="label-text text-neutral pl-1">
|
||||||
</div>
|
{formatMoney(ante)}
|
||||||
)}
|
</div>
|
||||||
{!deservesDailyFreeMarket && ante > balance && (
|
)
|
||||||
<div className="mb-2 mt-2 mr-auto self-center whitespace-nowrap text-xs font-medium tracking-wide">
|
|
||||||
<span className="mr-2 text-red-500">Insufficient balance</span>
|
|
||||||
<button
|
|
||||||
className="btn btn-xs btn-primary"
|
|
||||||
onClick={() => (window.location.href = '/add-funds')}
|
|
||||||
>
|
|
||||||
Add funds
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
{mustWaitForDailyFreeMarketStatus != 'loading' &&
|
||||||
|
mustWaitForDailyFreeMarketStatus &&
|
||||||
|
ante > balance && (
|
||||||
|
<div className="mb-2 mt-2 mr-auto self-center whitespace-nowrap text-xs font-medium tracking-wide">
|
||||||
|
<span className="mr-2 text-red-500">Insufficient balance</span>
|
||||||
|
<button
|
||||||
|
className="btn btn-xs btn-primary"
|
||||||
|
onClick={() => (window.location.href = '/add-funds')}
|
||||||
|
>
|
||||||
|
Add funds
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* <BuyAmountInput
|
{/* <BuyAmountInput
|
||||||
amount={ante ?? undefined}
|
amount={ante ?? undefined}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user