Skip to content

Commit

Permalink
New user goals MVP (with A/B test)
Browse files Browse the repository at this point in the history
  • Loading branch information
jahooma committed Mar 20, 2024
1 parent 7b526b8 commit 1f80b32
Show file tree
Hide file tree
Showing 2 changed files with 210 additions and 22 deletions.
177 changes: 177 additions & 0 deletions web/components/home/new-user-goals.tsx
@@ -0,0 +1,177 @@
import { sumBy, uniqBy } from 'lodash'
import clsx from 'clsx'
import { GoGoal } from 'react-icons/go'
import Link from 'next/link'

import { useLeagueInfo } from 'web/hooks/use-leagues'
import { formatMoney } from 'common/util/format'
import { ProgressBar } from 'web/components/progress-bar'
import { useRemainingNewUserSignupBonuses } from 'web/hooks/use-request-new-user-signup-bonus'
import { useBets } from 'web/hooks/use-bets-supabase'
import {
DAYS_TO_USE_FREE_QUESTIONS,
User,
freeQuestionRemaining,
} from 'common/user'
import { Col } from '../layout/col'
import { Row } from '../layout/row'
import { useSubscription } from 'web/lib/supabase/realtime/use-subscription'
import { Bet } from 'common/bet'
import { ReactNode } from 'react'
import { InfoTooltip } from '../widgets/info-tooltip'
import { CreateQuestionButton } from '../buttons/create-question-button'
import { simpleFromNow } from 'web/lib/util/shortenedFromNow'
import { DAY_MS } from 'common/util/time'
import { Button } from '../buttons/button'

export const NewUserGoals = (props: { user: User }) => {
const { user } = props
const remainingViewBonuses = useRemainingNewUserSignupBonuses()

const bets = useBets({ userId: user.id, limit: 100, order: 'desc' })
const { rows } = useSubscription('contract_bets', {
k: 'user_id',
v: user.id,
})
const liveBets = rows?.map((r) => r.data as Bet)
const combinedBets = uniqBy(
[...(bets ?? []), ...(liveBets ?? [])],
(b) => b.id
)
const amountBet = sumBy(combinedBets, (b) => (b.amount > 0 ? b.amount : 0))

const leagueInfo = useLeagueInfo(user.id)
if (remainingViewBonuses === undefined) {
return null
}
if (remainingViewBonuses > 0) {
return (
<ProgressDisplay
className="w-full"
label="Goal 1"
value={10 - remainingViewBonuses}
goal={10}
description="questions visited"
/>
)
}

if (bets === undefined) {
return null
}

const goalAmountBet = 250
if (amountBet < goalAmountBet) {
return (
<ProgressDisplay
className="w-full"
label="Goal 2"
value={amountBet}
goal={goalAmountBet}
format={formatMoney}
description="bet"
/>
)
}

if (leagueInfo === undefined) {
return null
}

const manaEarned = leagueInfo?.mana_earned ?? 0
const manaEarnedGoal = 100

if (manaEarned < manaEarnedGoal) {
return (
<ProgressDisplay
className="w-full"
label="Goal 3"
value={manaEarned}
goal={manaEarnedGoal}
format={formatMoney}
description={
<>
mana earned{' '}
<InfoTooltip text="hi" tooltipParams={{ placement: 'bottom' }} />
</>
}
>
<Link href="/browse/for-you?s=newest" className="w-full">
<Button className="mt-2 w-full" size="xs">
Find profit in new markets
</Button>
</Link>
</ProgressDisplay>
)
}

const remaining = freeQuestionRemaining(
user?.freeQuestionsCreated,
user?.createdTime
)
const questionsCreated = user.freeQuestionsCreated ?? 0
const questionsCreatedGoal = 3
if (questionsCreated < questionsCreatedGoal) {
return (
<ProgressDisplay
className="w-full"
label="Goal 4"
value={questionsCreated}
goal={questionsCreatedGoal}
description={<>questions created</>}
>
<Row className="mt-2 gap-x-1">
🎉 You've got {remaining} free questions!
<span>
Expires in{' '}
{simpleFromNow(
user.createdTime + DAY_MS * DAYS_TO_USE_FREE_QUESTIONS
)}
</span>
</Row>
<CreateQuestionButton color="indigo" size="xs" />
</ProgressDisplay>
)
}
return null
}

const ProgressDisplay = (props: {
label: string
value: number
goal: number
description: ReactNode
format?: (value: number) => string
children?: ReactNode
className?: string
}) => {
const {
label,
value,
goal,
description,
format = (x: number) => x.toString(),
children,
className,
} = props
return (
<Col
className={clsx('gap-2 rounded-md bg-indigo-100 px-4 py-3', className)}
>
<Row className="items-center justify-between gap-4">
<div className="flex-shrink-0">
<span className="font-semibold">
{format(value)} of {format(goal)}
</span>{' '}
{description}
</div>
<Row className="items-center gap-2">
<GoGoal className="text-indigo-800" />
{label}
</Row>
</Row>
<ProgressBar className="mb-1" value={value / goal} max={1} />
{children}
</Col>
)
}
55 changes: 33 additions & 22 deletions web/pages/home/index.tsx
Expand Up @@ -42,6 +42,7 @@ import { usePersistentInMemoryState } from 'web/hooks/use-persistent-in-memory-s
import { User } from 'common/user'
import { YourTopicsSection } from 'web/components/topics/your-topics'
import { useABTest } from 'web/hooks/use-ab-test'
import { NewUserGoals } from 'web/components/home/new-user-goals'

export async function getStaticProps() {
try {
Expand Down Expand Up @@ -134,30 +135,40 @@ export function HomeContent(props: {
`tabs-home`
)

const hasAgedOutOfNewUserGoals =
(user?.createdTime ?? 0) + DAY_MS * DAYS_TO_USE_FREE_QUESTIONS < Date.now()
const newUserGoalsVariant = useABTest('new user goals', [
'enabled',
'disabled',
])
const newUserGoalsEnabled =
!hasAgedOutOfNewUserGoals && newUserGoalsVariant === 'enabled'

return (
<Col className="w-full max-w-[800px] items-center self-center pb-4 sm:px-2">
{user && freeQuestionsEnabled && remaining > 0 && (
<Col className="text-md mb-2 w-full items-stretch justify-stretch gap-2 self-center rounded-md bg-indigo-100 px-4 py-2 sm:flex-row sm:items-center">
<Row className="flex-1 flex-wrap gap-x-1">
<span>
🎉 You've got{' '}
<span className="font-semibold">{remaining} free questions</span>!
</span>
<span>
(Expires in{' '}
{simpleFromNow(
user.createdTime + DAY_MS * DAYS_TO_USE_FREE_QUESTIONS
)}
.)
</span>
</Row>
<CreateQuestionButton
className={'flex-1'}
color="indigo-outline"
size="xs"
/>
</Col>
)}
{user &&
!newUserGoalsEnabled &&
freeQuestionsEnabled &&
remaining > 0 && (
<Col className="text-md mb-2 w-full items-stretch justify-stretch gap-2 self-center rounded-md bg-indigo-100 px-4 py-2 sm:flex-row sm:items-center">
<Row className="flex-1 flex-wrap gap-x-1">
<span>🎉 You've got {remaining} free questions!</span>
<span>
Expires in{' '}
{simpleFromNow(
user.createdTime + DAY_MS * DAYS_TO_USE_FREE_QUESTIONS
)}
</span>
</Row>
<CreateQuestionButton
className={'flex-1'}
color="indigo-outline"
size="xs"
/>
</Col>
)}

{user && newUserGoalsEnabled && <NewUserGoals user={user} />}

<Row className="bg-canvas-50 sticky top-8 z-50 mb-2 w-full justify-between">
<ControlledTabs
Expand Down

0 comments on commit 1f80b32

Please sign in to comment.