Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
formatBalance,
formatEthereumAddress,
useAccount,
useProviderType,
useChainId,
useMaskBoxConstants,
EthereumTokenType,
Expand Down Expand Up @@ -115,7 +114,6 @@ export function DrawDialog(props: DrawDialogProps) {
const providerDescriptor = useProviderDescriptor()
const account = useAccount()
const chainId = useChainId()
const providerType = useProviderType()

const onCount = useCallback(
(step: number) => {
Expand Down Expand Up @@ -256,7 +254,7 @@ export function DrawDialog(props: DrawDialogProps) {
</Typography>
<Box className={classes.content}>
<GasSettingBar
gasLimit={openBoxTransactionGasLimit}
gasLimit={openBoxTransactionGasLimit || 0}
onChange={setOpenBoxTransactionOverrides}
/>
</Box>
Expand Down
35 changes: 5 additions & 30 deletions packages/mask/src/plugins/MaskBox/helpers/formatCountdown.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,7 @@
const msPerSecond = 1000
const msPerMinute = msPerSecond * 60
const msPerHour = msPerMinute * 60
const msPerDay = msPerHour * 24
import formatDuration from 'date-fns/formatDuration'
import intervalToDuration from 'date-fns/intervalToDuration'

const units = [
['day', 'days'],
['hour', 'hours'],
['minute', 'minutes'],
['second', 'seconds'],
]

export function formatCountdown(countdown: number) {
if (countdown <= 0) return ''

const amounts: number[] = []

;[msPerDay, msPerHour, msPerMinute, msPerSecond].reduce((accumulator, x) => {
amounts.push(Math.floor(accumulator / x))
return accumulator % x
}, countdown)

return amounts
.map((x, i) => {
if (x <= 0) return ''
if (x === 1) return `${x} ${units[i][0]}`
return `${x} ${units[i][1]}`
})
.filter(Boolean)
.join(' ')
.trim()
export function formatCountdown(from: number, to: number) {
Comment thread
septs marked this conversation as resolved.
const duration = intervalToDuration({ start: from, end: to })
return formatDuration(duration)
}
40 changes: 26 additions & 14 deletions packages/mask/src/plugins/MaskBox/hooks/useContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,13 @@ import { useMaskBoxPurchasedTokens } from './useMaskBoxPurchasedTokens'
import { formatCountdown } from '../helpers/formatCountdown'
import { useOpenBoxTransaction } from './useOpenBoxTransaction'
import { useMaskBoxMetadata } from './useMaskBoxMetadata'
import { useHeartBit } from './useHeartBit'
import { useHeartBeat } from './useHeartBeat'
import { useIsWhitelisted } from './useIsWhitelisted'
import { isGreaterThanOrEqualTo, isLessThanOrEqualTo, isZero, multipliedBy } from '@masknet/web3-shared-base'

function useContext(initialState?: { boxId: string }) {
const heartBit = useHeartBit()
const now = new Date()
const heartBeat = useHeartBeat()
const account = useAccount()
const chainId = useChainId()
const { NATIVE_TOKEN_ADDRESS } = useTokenConstants(ChainId.Mainnet)
Expand Down Expand Up @@ -92,8 +93,8 @@ function useContext(initialState?: { boxId: string }) {
const totalComputed = total && remaining && remaining > total ? remaining : total
const sold = Math.max(0, totalComputed - remaining)
const personalRemaining = Math.max(0, personalLimit - purchasedTokens.length)
const startAt = Number.parseInt(maskBoxCreationSuccessEvent?.returnValues.start_time ?? '0', 10)
const endAt = Number.parseInt(maskBoxCreationSuccessEvent?.returnValues.end_time ?? '0', 10)
const startAt = Number.parseInt(maskBoxCreationSuccessEvent?.returnValues.start_time || '0', 10)
const endAt = Number.parseInt(maskBoxCreationSuccessEvent?.returnValues.end_time || '0', 10)
const info: BoxInfo = {
boxId,
creator: maskBoxInfo.creator,
Expand All @@ -105,6 +106,7 @@ function useContext(initialState?: { boxId: string }) {
availableAmount: Math.min(personalRemaining, remaining),
startAt: startAt === 0 ? subDays(new Date(), 1) : fromUnixTime(startAt),
endAt: endAt === 0 ? addDays(new Date(), 1) : fromUnixTime(endAt),
started: maskBoxStatus.started,
total: totalComputed,
sold,
canceled: maskBoxStatus.canceled,
Expand Down Expand Up @@ -135,17 +137,18 @@ function useContext(initialState?: { boxId: string }) {

const boxState = useMemo(() => {
if (errorMaskBoxInfo || errorMaskBoxStatus || errorBoxInfo) return BoxState.ERROR
if (loadingMaskBoxInfo || loadingMaskBoxStatus || loadingBoxInfo) return BoxState.UNKNOWN
if (loadingMaskBoxInfo || loadingMaskBoxStatus || loadingBoxInfo) {
if (!maskBoxInfo && !boxInfo) return BoxState.UNKNOWN

@UncleBill UncleBill Dec 28, 2021

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we already know something, don't lie.

Actually, to avoid ui flickering when call retry callback.

}
if (maskBoxInfo && !boxInfo) return BoxState.UNKNOWN
if (!maskBoxInfo || !maskBoxStatus || !boxInfo) return BoxState.NOT_FOUND
if (maskBoxStatus.canceled) return BoxState.CANCELED
const now = new Date()
if (isGreaterThanOrEqualTo(boxInfo.tokenIdsPurchased.length, boxInfo.personalLimit)) return BoxState.DRAWED_OUT
if (isLessThanOrEqualTo(boxInfo.remaining, 0)) return BoxState.SOLD_OUT
if (boxInfo.startAt > now) return BoxState.NOT_READY
if (boxInfo.startAt > now || !boxInfo.started) return BoxState.NOT_READY
if (boxInfo.endAt < now || maskBoxStatus?.expired) return BoxState.EXPIRED
return BoxState.READY
}, [boxInfo, loadingBoxInfo, errorBoxInfo, maskBoxInfo, loadingMaskBoxInfo, errorMaskBoxInfo, heartBit])
}, [boxInfo, loadingBoxInfo, errorBoxInfo, maskBoxInfo, loadingMaskBoxInfo, errorMaskBoxInfo, heartBeat])

const isWhitelisted = useIsWhitelisted(boxInfo?.qualificationAddress, account)
const isQualifiedByContract =
Expand All @@ -170,10 +173,10 @@ function useContext(initialState?: { boxId: string }) {
case BoxState.EXPIRED:
return 'Ended'
case BoxState.NOT_READY:
const now = Date.now()
const nowAt = now.getTime()
const startAt = boxInfo?.startAt.getTime() ?? 0
if (startAt <= now) return 'Loading...'
const countdown = formatCountdown(startAt - now)
if (startAt <= nowAt) return 'Syncing status...'
const countdown = formatCountdown(startAt, nowAt)
return countdown ? `Start sale in ${countdown}` : 'Loading...'
case BoxState.SOLD_OUT:
return 'Sold Out'
Expand All @@ -186,7 +189,16 @@ function useContext(initialState?: { boxId: string }) {
default:
unreachable(boxState)
}
}, [boxState, heartBit])
}, [boxState, boxInfo?.startAt, heartBeat])

useEffect(() => {
if (!boxInfo || boxInfo.started) return

if (boxInfo.startAt < now) {
retryMaskBoxStatus()
}
}, [boxInfo, heartBeat])

//#endregion

//#region the box metadata
Expand Down Expand Up @@ -253,8 +265,8 @@ function useContext(initialState?: { boxId: string }) {
const canPurchase = !isBalanceInsufficient && isQualified && !!boxInfo?.personalRemaining
const allowToPurchase = boxState === BoxState.READY
const isAllowanceEnough = isNativeToken ? true : costAmount.lte(erc20Allowance ?? '0')
const { value: openBoxTransactionGasLimit = 0 } = useAsyncRetry(async () => {
if (!openBoxTransaction || !canPurchase || !allowToPurchase || !isAllowanceEnough) return 0
const { value: openBoxTransactionGasLimit } = useAsyncRetry(async () => {
if (!openBoxTransaction || !canPurchase || !allowToPurchase || !isAllowanceEnough) return
Comment on lines +268 to +269

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unset, let next step make the decision.

const estimatedGas = await openBoxTransaction.method.estimateGas(omit(openBoxTransaction.config, 'gas'))
return new BigNumber(estimatedGas).toNumber()
}, [openBoxTransaction, canPurchase, allowToPurchase, isAllowanceEnough])
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useState } from 'react'
import { useTimeoutFn } from 'react-use'

export function useHeartBit(delay = 1000) {
export function useHeartBeat(delay = 1000) {
const [bit, setBit] = useState(0)
const [, , reset] = useTimeoutFn(() => {
setBit((x) => (x + 1) % Number.MAX_SAFE_INTEGER)
Expand Down
1 change: 1 addition & 0 deletions packages/mask/src/plugins/MaskBox/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export interface BoxInfo {
sold: number
startAt: Date
endAt: Date
started: boolean
tokenIds: string[]
tokenIdsPurchased: string[]
tokenAddress: string
Expand Down
9 changes: 7 additions & 2 deletions packages/web3-shared/evm/hooks/useTransactionCallback.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useCallback } from 'react'
import { omit } from 'lodash-unified'
import type { PayableTransactionObject, PayableTx } from '@masknet/web3-contracts/types/types'
import { useTransactionState } from './useTransactionState'
import { TransactionStateType, TransactionEventType } from '../types'
Expand All @@ -21,9 +22,13 @@ export function useTransactionCallback<T extends unknown>(
setState({
type: TransactionStateType.WAIT_FOR_CONFIRMING,
})
const gasExpectedConfig = { ...config }
Comment thread
guanbinrui marked this conversation as resolved.

try {
await method.estimateGas(config)
const estimatedGas = await method.estimateGas(omit(config, 'gas'))
if (!gasExpectedConfig.gas && estimatedGas) {
gasExpectedConfig.gas = estimatedGas
}
} catch (error) {
try {
await method.call(config)
Expand All @@ -38,7 +43,7 @@ export function useTransactionCallback<T extends unknown>(

return new Promise<void>(async (resolve, reject) => {
method
.send(config)
.send(gasExpectedConfig)
.once(TransactionEventType.TRANSACTION_HASH, (hash) => {
if (type !== TransactionStateType.HASH) return
setState({
Expand Down