diff --git a/common/user.ts b/common/user.ts index 0dac5a19..78b76511 100644 --- a/common/user.ts +++ b/common/user.ts @@ -40,6 +40,7 @@ export type User = { referredByContractId?: string referredByGroupId?: string lastPingTime?: number + shouldShowWelcome?: boolean } export const STARTING_BALANCE = ENV_CONFIG.startingBalance ?? 1000 diff --git a/firestore.rules b/firestore.rules index 0f28ca80..05721dcf 100644 --- a/firestore.rules +++ b/firestore.rules @@ -22,7 +22,7 @@ service cloud.firestore { allow read; allow update: if resource.data.id == request.auth.uid && request.resource.data.diff(resource.data).affectedKeys() - .hasOnly(['bio', 'bannerUrl', 'website', 'twitterHandle', 'discordHandle', 'followedCategories', 'lastPingTime']); + .hasOnly(['bio', 'bannerUrl', 'website', 'twitterHandle', 'discordHandle', 'followedCategories', 'lastPingTime','shouldShowWelcome']); // User referral rules allow update: if resource.data.id == request.auth.uid && request.resource.data.diff(resource.data).affectedKeys() diff --git a/functions/src/create-user.ts b/functions/src/create-user.ts index ab7c8e9a..70e81055 100644 --- a/functions/src/create-user.ts +++ b/functions/src/create-user.ts @@ -77,6 +77,7 @@ export const createuser = newEndpoint(opts, async (req, auth) => { creatorVolumeCached: { daily: 0, weekly: 0, monthly: 0, allTime: 0 }, followerCountCached: 0, followedCategories: DEFAULT_CATEGORIES, + shouldShowWelcome: true, } await firestore.collection('users').doc(auth.uid).create(user) diff --git a/web/components/button.tsx b/web/components/button.tsx index 8877c023..7eeca3d2 100644 --- a/web/components/button.tsx +++ b/web/components/button.tsx @@ -39,8 +39,10 @@ export function Button(props: { color === 'yellow' && 'bg-yellow-400 text-white hover:bg-yellow-500', color === 'blue' && 'bg-blue-400 text-white hover:bg-blue-500', color === 'indigo' && 'bg-indigo-500 text-white hover:bg-indigo-600', - color === 'gray' && 'bg-gray-100 text-gray-600 hover:bg-gray-200', - color === 'gray-white' && 'bg-white text-gray-500 hover:bg-gray-200', + color === 'gray' && + 'bg-greyscale-1 text-greyscale-7 hover:bg-greyscale-2', + color === 'gray-white' && + 'text-greyscale-6 hover:bg-greyscale-2 bg-white', className )} disabled={disabled} diff --git a/web/components/buttons/pill-button.tsx b/web/components/buttons/pill-button.tsx index 5b4962b7..8e47c94e 100644 --- a/web/components/buttons/pill-button.tsx +++ b/web/components/buttons/pill-button.tsx @@ -15,8 +15,8 @@ export function PillButton(props: { className={clsx( 'cursor-pointer select-none whitespace-nowrap rounded-full', selected - ? ['text-white', color ?? 'bg-gray-700'] - : 'bg-gray-100 hover:bg-gray-200', + ? ['text-white', color ?? 'bg-greyscale-6'] + : 'bg-greyscale-2 hover:bg-greyscale-3', big ? 'px-8 py-2' : 'px-3 py-1.5 text-sm' )} onClick={onSelect} diff --git a/web/components/onboarding/welcome.tsx b/web/components/onboarding/welcome.tsx new file mode 100644 index 00000000..5a187a24 --- /dev/null +++ b/web/components/onboarding/welcome.tsx @@ -0,0 +1,173 @@ +import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/solid' +import clsx from 'clsx' +import { useState } from 'react' +import { useUser } from 'web/hooks/use-user' +import { updateUser } from 'web/lib/firebase/users' +import { Col } from '../layout/col' +import { Modal } from '../layout/modal' +import { Row } from '../layout/row' +import { Title } from '../title' + +export default function Welcome() { + const user = useUser() + const [open, setOpen] = useState(true) + const [page, setPage] = useState(0) + const TOTAL_PAGES = 4 + + function increasePage() { + if (page < TOTAL_PAGES - 1) { + setPage(page + 1) + } + } + + function decreasePage() { + if (page > 0) { + setPage(page - 1) + } + } + + async function setUserHasSeenWelcome() { + if (user) { + await updateUser(user.id, { ['shouldShowWelcome']: false }) + } + } + + if (!user || !user.shouldShowWelcome) { + return <> + } else + return ( + { + setUserHasSeenWelcome() + setOpen(newOpen) + }} + > + + {page === 0 && } + {page === 1 && } + {page === 2 && } + {page === 3 && } + + + + + + + { + setOpen(false) + setUserHasSeenWelcome() + }} + > + I got the gist, exit welcome + + + + + ) +} + +function PageIndicator(props: { page: number; totalpages: number }) { + const { page, totalpages } = props + return ( + + {[...Array(totalpages)].map((e, i) => ( +
+ ))} + + ) +} + +function Page0() { + return ( + <> + + + <p> + Manifold Markets is a place where anyone can ask a question about the + future. + </p> + <div className="mt-4">For example,</div> + <div className="mt-2 font-normal text-indigo-700"> + “Will Michelle Obama be the next president of the United States?” + </div> + </> + ) +} + +function Page1() { + return ( + <> + <p> + Your question becomes a prediction market that people can bet{' '} + <span className="font-normal text-indigo-700">mana (M$)</span> on. + </p> + <div className="mt-8 font-semibold">The core idea</div> + <div className="mt-2"> + If people have to put their mana where their mouth is, you’ll get a + pretty accurate answer! + </div> + <video loop autoPlay className="my-4 h-full w-full"> + <source src="/welcome/mana-example.mp4" type="video/mp4" /> + Your browser does not support video + </video> + </> + ) +} + +function Page2() { + return ( + <> + <p> + <span className="mt-4 font-normal text-indigo-700">Mana (M$)</span> is + the play money you bet with. You can also turn it into a real donation + to charity, at a 100:1 ratio. + </p> + <div className="mt-8 font-semibold">Example</div> + <p className="mt-2"> + When you donate <span className="font-semibold">M$1000</span> to + Givewell, Manifold sends them{' '} + <span className="font-semibold">$10 USD</span>. + </p> + <video loop autoPlay className="my-4 h-full w-full"> + <source src="/welcome/charity.mp4" type="video/mp4" /> + Your browser does not support video + </video> + </> + ) +} + +function Page3() { + return ( + <> + <img className="mx-auto object-contain" src="/welcome/treasure.png" /> + <Title className="mx-auto" text="Let's start predicting!" /> + <p className="mb-8"> + As a thank you for signing up, we’ve sent you{' '} + <span className="font-normal text-indigo-700">M$1000 Mana</span>{' '} + </p> + </> + ) +} diff --git a/web/components/title.tsx b/web/components/title.tsx index e58aee39..e0a0be61 100644 --- a/web/components/title.tsx +++ b/web/components/title.tsx @@ -5,7 +5,7 @@ export function Title(props: { text: string; className?: string }) { return ( <h1 className={clsx( - 'my-4 inline-block text-2xl text-indigo-700 sm:my-6 sm:text-3xl', + 'my-4 inline-block text-2xl font-normal text-indigo-700 sm:my-6 sm:text-3xl', className )} > diff --git a/web/pages/_app.tsx b/web/pages/_app.tsx index 52316eb0..14dd6cf0 100644 --- a/web/pages/_app.tsx +++ b/web/pages/_app.tsx @@ -6,6 +6,7 @@ import Script from 'next/script' import { usePreserveScroll } from 'web/hooks/use-preserve-scroll' import { QueryClient, QueryClientProvider } from 'react-query' import { AuthProvider } from 'web/components/auth-context' +import Welcome from 'web/components/onboarding/welcome' function firstLine(msg: string) { return msg.replace(/\r?\n.*/s, '') @@ -78,9 +79,9 @@ function MyApp({ Component, pageProps }: AppProps) { content="width=device-width, initial-scale=1, maximum-scale=1" /> </Head> - <AuthProvider> <QueryClientProvider client={queryClient}> + <Welcome {...pageProps} /> <Component {...pageProps} /> </QueryClientProvider> </AuthProvider> diff --git a/web/pages/group/[...slugs]/index.tsx b/web/pages/group/[...slugs]/index.tsx index dd712a36..5c52c7dc 100644 --- a/web/pages/group/[...slugs]/index.tsx +++ b/web/pages/group/[...slugs]/index.tsx @@ -242,6 +242,7 @@ export default function GroupPage(props: { href: groupPath(group.slug, 'about'), }, ] + const tabIndex = tabs.map((t) => t.title).indexOf(page ?? GROUP_CHAT_SLUG) return ( diff --git a/web/public/welcome/charity.mp4 b/web/public/welcome/charity.mp4 new file mode 100755 index 00000000..e9ba5a8a Binary files /dev/null and b/web/public/welcome/charity.mp4 differ diff --git a/web/public/welcome/mana-example.mp4 b/web/public/welcome/mana-example.mp4 new file mode 100755 index 00000000..bb28a4bd Binary files /dev/null and b/web/public/welcome/mana-example.mp4 differ diff --git a/web/public/welcome/manipurple.png b/web/public/welcome/manipurple.png new file mode 100644 index 00000000..97d361b9 Binary files /dev/null and b/web/public/welcome/manipurple.png differ diff --git a/web/public/welcome/treasure.png b/web/public/welcome/treasure.png new file mode 100644 index 00000000..9c590d63 Binary files /dev/null and b/web/public/welcome/treasure.png differ diff --git a/web/tailwind.config.js b/web/tailwind.config.js index 3457b7a6..5fbc6c15 100644 --- a/web/tailwind.config.js +++ b/web/tailwind.config.js @@ -18,6 +18,15 @@ module.exports = { backgroundImage: { 'world-trading': "url('/world-trading-background.webp')", }, + colors: { + 'greyscale-1': '#FBFBFF', + 'greyscale-2': '#E7E7F4', + 'greyscale-3': '#D8D8EB', + 'greyscale-4': '#B1B1C7', + 'greyscale-5': '#9191A7', + 'greyscale-6': '#66667C', + 'greyscale-7': '#111140', + }, typography: { quoteless: { css: {