Fix twitch onboarding (#910)
* don't show welcome dialog for twitch users * handle sign up race conditions with more hooks * content organization and copy tweaks * lint * fix import
This commit is contained in:
parent
8870f0d356
commit
54778ec1b1
|
@ -1,6 +1,9 @@
|
||||||
import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/solid'
|
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import { useState } from 'react'
|
import { useRouter } from 'next/router'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/solid'
|
||||||
|
|
||||||
|
import { User } from 'common/user'
|
||||||
import { useUser } from 'web/hooks/use-user'
|
import { useUser } from 'web/hooks/use-user'
|
||||||
import { updateUser } from 'web/lib/firebase/users'
|
import { updateUser } from 'web/lib/firebase/users'
|
||||||
import { Col } from '../layout/col'
|
import { Col } from '../layout/col'
|
||||||
|
@ -27,16 +30,12 @@ export default function Welcome() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setUserHasSeenWelcome() {
|
const setUserHasSeenWelcome = async () => {
|
||||||
if (user) {
|
if (user) await updateUser(user.id, { ['shouldShowWelcome']: false })
|
||||||
await updateUser(user.id, { ['shouldShowWelcome']: false })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const [groupSelectorOpen, setGroupSelectorOpen] = useState(false)
|
const [groupSelectorOpen, setGroupSelectorOpen] = useState(false)
|
||||||
|
|
||||||
if (!user || (!user.shouldShowWelcome && !groupSelectorOpen)) return <></>
|
|
||||||
|
|
||||||
const toggleOpen = (isOpen: boolean) => {
|
const toggleOpen = (isOpen: boolean) => {
|
||||||
setUserHasSeenWelcome()
|
setUserHasSeenWelcome()
|
||||||
setOpen(isOpen)
|
setOpen(isOpen)
|
||||||
|
@ -45,6 +44,12 @@ export default function Welcome() {
|
||||||
setGroupSelectorOpen(true)
|
setGroupSelectorOpen(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isTwitch = useIsTwitch(user)
|
||||||
|
|
||||||
|
if (isTwitch || !user || (!user.shouldShowWelcome && !groupSelectorOpen))
|
||||||
|
return <></>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<GroupSelectorDialog
|
<GroupSelectorDialog
|
||||||
|
@ -89,6 +94,21 @@ export default function Welcome() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const useIsTwitch = (user: User | null | undefined) => {
|
||||||
|
const router = useRouter()
|
||||||
|
const isTwitch = router.pathname === '/twitch'
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('twich?', isTwitch)
|
||||||
|
|
||||||
|
if (isTwitch && user?.shouldShowWelcome) {
|
||||||
|
updateUser(user.id, { ['shouldShowWelcome']: false })
|
||||||
|
}
|
||||||
|
}, [isTwitch, user])
|
||||||
|
|
||||||
|
return isTwitch
|
||||||
|
}
|
||||||
|
|
||||||
function PageIndicator(props: { page: number; totalpages: number }) {
|
function PageIndicator(props: { page: number; totalpages: number }) {
|
||||||
const { page, totalpages } = props
|
const { page, totalpages } = props
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { LinkIcon } from '@heroicons/react/solid'
|
import { LinkIcon } from '@heroicons/react/solid'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import { PrivateUser, User } from 'common/user'
|
import { PrivateUser, User } from 'common/user'
|
||||||
import Link from 'next/link'
|
import { MouseEventHandler, ReactNode, useEffect, useState } from 'react'
|
||||||
import { MouseEventHandler, ReactNode, useState } from 'react'
|
|
||||||
|
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { Button } from 'web/components/button'
|
import { Button } from 'web/components/button'
|
||||||
|
@ -17,11 +16,7 @@ import { Title } from 'web/components/title'
|
||||||
import { useSaveReferral } from 'web/hooks/use-save-referral'
|
import { useSaveReferral } from 'web/hooks/use-save-referral'
|
||||||
import { useTracking } from 'web/hooks/use-tracking'
|
import { useTracking } from 'web/hooks/use-tracking'
|
||||||
import { usePrivateUser, useUser } from 'web/hooks/use-user'
|
import { usePrivateUser, useUser } from 'web/hooks/use-user'
|
||||||
import {
|
import { firebaseLogin, updatePrivateUser } from 'web/lib/firebase/users'
|
||||||
firebaseLogin,
|
|
||||||
getUserAndPrivateUser,
|
|
||||||
updatePrivateUser,
|
|
||||||
} from 'web/lib/firebase/users'
|
|
||||||
import { track } from 'web/lib/service/analytics'
|
import { track } from 'web/lib/service/analytics'
|
||||||
import {
|
import {
|
||||||
getDockURLForUser,
|
getDockURLForUser,
|
||||||
|
@ -40,23 +35,32 @@ function ButtonGetStarted(props: {
|
||||||
const { user, privateUser, buttonClass, spinnerClass } = props
|
const { user, privateUser, buttonClass, spinnerClass } = props
|
||||||
|
|
||||||
const [isLoading, setLoading] = useState(false)
|
const [isLoading, setLoading] = useState(false)
|
||||||
|
|
||||||
const needsRelink =
|
const needsRelink =
|
||||||
privateUser?.twitchInfo?.twitchName &&
|
privateUser?.twitchInfo?.twitchName &&
|
||||||
privateUser?.twitchInfo?.needsRelinking
|
privateUser?.twitchInfo?.needsRelinking
|
||||||
|
|
||||||
|
const [waitingForUser, setWaitingForUser] = useState(false)
|
||||||
|
useEffect(() => {
|
||||||
|
if (waitingForUser && user && privateUser) {
|
||||||
|
setWaitingForUser(false)
|
||||||
|
|
||||||
|
if (privateUser.twitchInfo?.twitchName) return // If we've already linked Twitch, no need to do so again
|
||||||
|
|
||||||
|
setLoading(true)
|
||||||
|
|
||||||
|
linkTwitchAccountRedirect(user, privateUser).then(() => {
|
||||||
|
setLoading(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [user, privateUser, waitingForUser])
|
||||||
|
|
||||||
const callback =
|
const callback =
|
||||||
user && privateUser
|
user && privateUser
|
||||||
? () => linkTwitchAccountRedirect(user, privateUser)
|
? () => linkTwitchAccountRedirect(user, privateUser)
|
||||||
: async () => {
|
: async () => {
|
||||||
const result = await firebaseLogin()
|
await firebaseLogin()
|
||||||
|
setWaitingForUser(true)
|
||||||
const userId = result.user.uid
|
|
||||||
const { user, privateUser } = await getUserAndPrivateUser(userId)
|
|
||||||
if (!user || !privateUser) return
|
|
||||||
|
|
||||||
if (privateUser.twitchInfo?.twitchName) return // If we've already linked Twitch, no need to do so again
|
|
||||||
|
|
||||||
await linkTwitchAccountRedirect(user, privateUser)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStarted = async () => {
|
const getStarted = async () => {
|
||||||
|
@ -110,20 +114,9 @@ function TwitchPlaysManifoldMarkets(props: {
|
||||||
className={'!-my-0 md:block'}
|
className={'!-my-0 md:block'}
|
||||||
/>
|
/>
|
||||||
</Row>
|
</Row>
|
||||||
<Col className="gap-4">
|
<Col className="mb-4 gap-4">
|
||||||
<div>
|
Start betting on Twitch now by linking your account and typing commands
|
||||||
Similar to Twitch channel point predictions, Manifold Markets allows
|
in chat!
|
||||||
you to create and feature on stream any question you like with users
|
|
||||||
predicting to earn play money.
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
The key difference is that Manifold's questions function more like a
|
|
||||||
stock market and viewers can buy and sell shares over the course of
|
|
||||||
the event and not just at the start. The market will eventually
|
|
||||||
resolve to yes or no at which point the winning shareholders will
|
|
||||||
receive their profit.
|
|
||||||
</div>
|
|
||||||
Start playing now by logging in with Google and typing commands in chat!
|
|
||||||
{twitchUser && !twitchInfo.needsRelinking ? (
|
{twitchUser && !twitchInfo.needsRelinking ? (
|
||||||
<Button
|
<Button
|
||||||
size="xl"
|
size="xl"
|
||||||
|
@ -135,13 +128,25 @@ function TwitchPlaysManifoldMarkets(props: {
|
||||||
) : (
|
) : (
|
||||||
<ButtonGetStarted user={user} privateUser={privateUser} />
|
<ButtonGetStarted user={user} privateUser={privateUser} />
|
||||||
)}
|
)}
|
||||||
|
</Col>
|
||||||
|
<Col className="gap-4">
|
||||||
|
<Subtitle text="How it works" />
|
||||||
<div>
|
<div>
|
||||||
Instead of Twitch channel points we use our play money, Mana (M$). All
|
Similar to Twitch channel point predictions, Manifold Markets allows
|
||||||
viewers start with M$1000 and more can be earned for free and then{' '}
|
you to create a play-money betting market on any question you like and
|
||||||
<Link href="/charity">
|
feature it in your stream.
|
||||||
<a className="underline">donated to a charity</a>
|
</div>
|
||||||
</Link>{' '}
|
<div>
|
||||||
of their choice at no cost!
|
The key difference is that Manifold's questions function more like a
|
||||||
|
stock market and viewers can buy and sell shares over the course of
|
||||||
|
the event and not just at the start. The market will eventually
|
||||||
|
resolve to yes or no at which point the winning shareholders will
|
||||||
|
receive their profit.
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Instead of Twitch channel points we use our own play money, mana (M$).
|
||||||
|
All viewers start with M$1,000 and can earn more for free by betting
|
||||||
|
well.
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
</div>
|
</div>
|
||||||
|
@ -170,21 +175,26 @@ function TwitchChatCommands() {
|
||||||
<Title text="Twitch Chat Commands" className="md:block" />
|
<Title text="Twitch Chat Commands" className="md:block" />
|
||||||
<Col className="gap-4">
|
<Col className="gap-4">
|
||||||
<Subtitle text="For Chat" />
|
<Subtitle text="For Chat" />
|
||||||
<Command command="bet yes#" desc="Bets a # of Mana on yes." />
|
<Command
|
||||||
<Command command="bet no#" desc="Bets a # of Mana on no." />
|
command="bet yes #"
|
||||||
|
desc="Bets an amount of M$ on yes, for example !bet yes 20"
|
||||||
|
/>
|
||||||
|
<Command command="bet no #" desc="Bets an amount of M$ on no." />
|
||||||
<Command
|
<Command
|
||||||
command="sell"
|
command="sell"
|
||||||
desc="Sells all shares you own. Using this command causes you to
|
desc="Sells all shares you own. Using this command causes you to
|
||||||
cash out early before the market resolves. This could be profitable
|
cash out early before the market resolves. This could be profitable
|
||||||
(if the probability has moved towards the direction you bet) or cause
|
(if the probability has moved towards the direction you bet) or cause
|
||||||
a loss, although at least you keep some Mana. For maximum profit (but
|
a loss, although at least you keep some mana. For maximum profit (but
|
||||||
also risk) it is better to not sell and wait for a favourable
|
also risk) it is better to not sell and wait for a favourable
|
||||||
resolution."
|
resolution."
|
||||||
/>
|
/>
|
||||||
<Command command="balance" desc="Shows how much Mana you own." />
|
<Command command="balance" desc="Shows how much M$ you have." />
|
||||||
<Command command="allin yes" desc="Bets your entire balance on yes." />
|
<Command command="allin yes" desc="Bets your entire balance on yes." />
|
||||||
<Command command="allin no" desc="Bets your entire balance on no." />
|
<Command command="allin no" desc="Bets your entire balance on no." />
|
||||||
|
|
||||||
|
<div className="mb-4" />
|
||||||
|
|
||||||
<Subtitle text="For Mods/Streamer" />
|
<Subtitle text="For Mods/Streamer" />
|
||||||
<Command
|
<Command
|
||||||
command="create <question>"
|
command="create <question>"
|
||||||
|
@ -194,7 +204,7 @@ function TwitchChatCommands() {
|
||||||
<Command command="resolve no" desc="Resolves the market as 'No'." />
|
<Command command="resolve no" desc="Resolves the market as 'No'." />
|
||||||
<Command
|
<Command
|
||||||
command="resolve n/a"
|
command="resolve n/a"
|
||||||
desc="Resolves the market as 'N/A' and refunds everyone their Mana."
|
desc="Resolves the market as 'N/A' and refunds everyone their mana."
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user