Skip to content

Commit

Permalink
[PAY-2009] Challenge cooldown UI (#6363)
Browse files Browse the repository at this point in the history
  • Loading branch information
dharit-tan authored Oct 18, 2023
1 parent 5debe03 commit 8cc5a64
Show file tree
Hide file tree
Showing 15 changed files with 190 additions and 223 deletions.
2 changes: 2 additions & 0 deletions packages/common/src/hooks/purchaseContent/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ export const AMOUNT_PRESET = 'amountPreset'
// Pay between $1 and $100 extra
export const minimumPayExtraAmountCents = 100
export const maximumPayExtraAmountCents = 10000

export const COOLDOWN_DAYS = 7
1 change: 1 addition & 0 deletions packages/common/src/hooks/purchaseContent/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './usePurchaseContentFormConfiguration'
export * from './useChallengeCooldownSchedule'
export * from './usePayExtraPresets'
export * from './utils'
export * from './types'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import dayjs, { Dayjs } from 'dayjs'
import { useSelector } from 'react-redux'

import { ChallengeRewardID } from 'models/AudioRewards'
import {
UndisbursedUserChallenge,
audioRewardsPageSelectors
} from 'store/pages'

import { COOLDOWN_DAYS } from './constants'

const { getUndisbursedUserChallenges } = audioRewardsPageSelectors

const messages = {
laterToday: 'Later Today',
readyToClaim: 'Ready to Claim!',
tomorrow: 'Tomorrow'
}

/**
* Returns a list of challenges in cooldown period (not claimable yet), and
* the total currently claimable amount (for challenges past the cooldown period).
*/
export const useChallengeCooldownSchedule = (
challengeId?: ChallengeRewardID
) => {
const challenges = useSelector(getUndisbursedUserChallenges)
.filter((c) => c.challenge_id === challengeId)
.map((c) => ({ ...c, createdAtDate: dayjs.utc(c.created_at) }))
// Clamp "now" to beginning of day to avoid duplicate dates in cooldown items
const now = dayjs.utc().startOf('day')
// Only challenges past the cooldown period are claimable
const claimableAmount = challenges
.filter((c) => now.diff(c.createdAtDate, 'day') >= COOLDOWN_DAYS)
.reduce((acc, curr) => acc + curr.amount, 0)
// Challenges are already ordered by completed_blocknumber ascending.
const cooldownChallenges = challenges.filter(
(c) => now.diff(c.createdAtDate, 'day') < COOLDOWN_DAYS
)
return { claimableAmount, cooldownChallenges }
}

const getAudioMatchingCooldownLabel = (now: Dayjs, created_at: Dayjs) => {
const diff = now.diff(created_at, 'day')
if (diff === COOLDOWN_DAYS - 1) {
return messages.laterToday
} else if (diff === COOLDOWN_DAYS - 2) {
return messages.tomorrow
}
return created_at.add(COOLDOWN_DAYS, 'day').local().format('ddd M/D')
}

const formatAudioMatchingChallengeCooldownSchedule = (
challenges: UndisbursedUserChallenge[]
) => {
const now = dayjs.utc().startOf('day')
const cooldownChallenges = new Array(7)
challenges.forEach((c) => {
const diff = now.diff(c.created_at, 'day')
cooldownChallenges[diff] = {
...cooldownChallenges[diff],
id: c.specifier,
label: getAudioMatchingCooldownLabel(now, dayjs.utc(c.created_at)),
value: (cooldownChallenges[diff]?.value ?? 0) + c.amount
}
})
return cooldownChallenges
}

const getAudioMatchingChallengeCooldownSummary = (claimableAmount: number) => ({
id: messages.readyToClaim,
label: messages.readyToClaim,
value: claimableAmount
})

/**
* Custom hook using values specific to $AUDIO matching challenges.
*/
export const useAudioMatchingChallengeCooldownSchedule = (
challengeId?: ChallengeRewardID
) => {
const { cooldownChallenges, claimableAmount } =
useChallengeCooldownSchedule(challengeId)
return {
claimableAmount,
cooldownChallenges:
formatAudioMatchingChallengeCooldownSchedule(cooldownChallenges),
cooldownChallengesSummary:
claimableAmount > 0
? getAudioMatchingChallengeCooldownSummary(claimableAmount)
: undefined
}
}
5 changes: 4 additions & 1 deletion packages/common/src/models/AudioRewards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,10 @@ export type UserChallengeState =
| 'completed'
| 'disbursed'

export type SpecifierWithAmount = { specifier: string; amount: number }
export type SpecifierWithAmount = {
specifier: string
amount: number
}

/**
* A User Challenge that has been updated by the client to optimistically include any updates
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ type UndisbursedUserChallengesResponse = [
completed_blocknumber: number
handle: string
wallet: string
created_at: string
}
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
getUserChallengesOverrides
} from 'store/pages/audio-rewards/selectors'
import { UndisbursedUserChallenge } from 'store/pages/audio-rewards/types'
import { isCooldownChallengeClaimable } from 'utils/challenges'
import { removeNullable } from 'utils/typeUtils'

import {
Expand Down Expand Up @@ -109,7 +110,11 @@ const toOptimisticChallenge = (
? state === 'completed'
? totalAmount
: 0
: undisbursed.reduce<number>((acc, val) => acc + val.amount, 0)
: undisbursed.reduce<number>(
(acc, val) =>
isCooldownChallengeClaimable(val) ? acc + val.amount : acc + 0,
0
)

const undisbursedSpecifiers = undisbursed.reduce(
(acc, c) => [...acc, { specifier: c.specifier, amount: c.amount }],
Expand Down
1 change: 1 addition & 0 deletions packages/common/src/store/pages/audio-rewards/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export type UndisbursedUserChallenge = Pick<
completed_blocknumber: number
handle: string
wallet: string
created_at: string
}

export enum HCaptchaStatus {
Expand Down
8 changes: 8 additions & 0 deletions packages/common/src/utils/challenges.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import dayjs from 'dayjs'

import { UndisbursedUserChallenge } from 'store/pages'

import {
ChallengeRewardID,
UserChallenge,
Expand Down Expand Up @@ -240,3 +244,7 @@ export const isAudioMatchingChallenge = (
challenge === ChallengeName.AudioMatchingBuy
)
}

export const isCooldownChallengeClaimable = (
challenge: UndisbursedUserChallenge
) => dayjs.utc().diff(dayjs.utc(challenge.created_at), 'day') >= 7

This file was deleted.

Loading

0 comments on commit 8cc5a64

Please sign in to comment.