diff --git a/web/components/info-box.tsx b/web/components/info-box.tsx new file mode 100644 index 00000000..34f65089 --- /dev/null +++ b/web/components/info-box.tsx @@ -0,0 +1,30 @@ +import clsx from 'clsx' +import { InformationCircleIcon } from '@heroicons/react/solid' + +import { Linkify } from './linkify' + +export function InfoBox(props: { + title: string + text: string + className?: string +}) { + const { title, text, className } = props + return ( +
+
+
+
+
+

{title}

+
+ +
+
+
+
+ ) +} diff --git a/web/components/nav/sidebar.tsx b/web/components/nav/sidebar.tsx index 9486a97b..b7117a20 100644 --- a/web/components/nav/sidebar.tsx +++ b/web/components/nav/sidebar.tsx @@ -63,6 +63,7 @@ function getMoreNavigation(user?: User | null) { return [ { name: 'Leaderboards', href: '/leaderboards' }, + { name: 'Referrals', href: '/referrals' }, { name: 'Charity', href: '/charity' }, { name: 'Send M$', href: '/links' }, { name: 'Discord', href: 'https://discord.gg/eHQBNBqXuh' }, @@ -114,6 +115,7 @@ function getMoreMobileNav() { ...(IS_PRIVATE_MANIFOLD ? [] : [ + { name: 'Referrals', href: '/referrals' }, { name: 'Charity', href: '/charity' }, { name: 'Send M$', href: '/links' }, { name: 'Discord', href: 'https://discord.gg/eHQBNBqXuh' }, diff --git a/web/hooks/use-save-referral.ts b/web/hooks/use-save-referral.ts new file mode 100644 index 00000000..788268b0 --- /dev/null +++ b/web/hooks/use-save-referral.ts @@ -0,0 +1,27 @@ +import { useRouter } from 'next/router' +import { useEffect } from 'react' + +import { User, writeReferralInfo } from 'web/lib/firebase/users' + +export const useSaveReferral = ( + user?: User | null, + options?: { + defaultReferrer?: string + contractId?: string + groupId?: string + } +) => { + const router = useRouter() + + useEffect(() => { + const { referrer } = router.query as { + referrer?: string + } + + const actualReferrer = referrer || options?.defaultReferrer + + if (!user && router.isReady && actualReferrer) { + writeReferralInfo(actualReferrer, options?.contractId, options?.groupId) + } + }, [user, router, options]) +} diff --git a/web/pages/[username]/[contractSlug].tsx b/web/pages/[username]/[contractSlug].tsx index 17453770..11d9af9c 100644 --- a/web/pages/[username]/[contractSlug].tsx +++ b/web/pages/[username]/[contractSlug].tsx @@ -10,7 +10,7 @@ import { useUser } from 'web/hooks/use-user' import { ResolutionPanel } from 'web/components/resolution-panel' import { Title } from 'web/components/title' import { Spacer } from 'web/components/layout/spacer' -import { listUsers, User, writeReferralInfo } from 'web/lib/firebase/users' +import { listUsers, User } from 'web/lib/firebase/users' import { Contract, getContractFromSlug, @@ -43,9 +43,9 @@ import { CPMMBinaryContract } from 'common/contract' import { AlertBox } from 'web/components/alert-box' import { useTracking } from 'web/hooks/use-tracking' import { CommentTipMap, useTipTxns } from 'web/hooks/use-tip-txns' -import { useRouter } from 'next/router' import { useLiquidity } from 'web/hooks/use-liquidity' import { richTextToString } from 'common/util/parse' +import { useSaveReferral } from 'web/hooks/use-save-referral' export const getStaticProps = fromPropz(getStaticPropz) export async function getStaticPropz(props: { @@ -157,15 +157,10 @@ export function ContractPageContent( const ogCardProps = getOpenGraphProps(contract) - const router = useRouter() - - useEffect(() => { - const { referrer } = router.query as { - referrer?: string - } - if (!user && router.isReady) - writeReferralInfo(contract.creatorUsername, contract.id, referrer) - }, [user, contract, router]) + useSaveReferral(user, { + defaultReferrer: contract.creatorUsername, + contractId: contract.id, + }) const rightSidebar = hasSidePanel ? ( diff --git a/web/pages/group/[...slugs]/index.tsx b/web/pages/group/[...slugs]/index.tsx index 0d38580c..90f39e83 100644 --- a/web/pages/group/[...slugs]/index.tsx +++ b/web/pages/group/[...slugs]/index.tsx @@ -14,12 +14,7 @@ import { } from 'web/lib/firebase/groups' import { Row } from 'web/components/layout/row' import { UserLink } from 'web/components/user-page' -import { - firebaseLogin, - getUser, - User, - writeReferralInfo, -} from 'web/lib/firebase/users' +import { firebaseLogin, getUser, User } from 'web/lib/firebase/users' import { Col } from 'web/components/layout/col' import { useUser } from 'web/hooks/use-user' import { listMembers, useGroup, useMembers } from 'web/hooks/use-group' @@ -34,7 +29,7 @@ import { Linkify } from 'web/components/linkify' import { fromPropz, usePropz } from 'web/hooks/use-propz' import { Tabs } from 'web/components/layout/tabs' import { CreateQuestionButton } from 'web/components/create-question-button' -import React, { useEffect, useState } from 'react' +import React, { useState } from 'react' import { GroupChat } from 'web/components/groups/group-chat' import { LoadingIndicator } from 'web/components/loading-indicator' import { Modal } from 'web/components/layout/modal' @@ -53,6 +48,7 @@ import { searchInAny } from 'common/util/parse' import { useWindowSize } from 'web/hooks/use-window-size' import { CopyLinkButton } from 'web/components/copy-link-button' import { ENV_CONFIG } from 'common/envs/constants' +import { useSaveReferral } from 'web/hooks/use-save-referral' export const getStaticProps = fromPropz(getStaticPropz) export async function getStaticPropz(props: { params: { slugs: string[] } }) { @@ -155,13 +151,11 @@ export default function GroupPage(props: { const messages = useCommentsOnGroup(group?.id) const user = useUser() - useEffect(() => { - const { referrer } = router.query as { - referrer?: string - } - if (!user && router.isReady) - writeReferralInfo(creator.username, undefined, referrer, group?.id) - }, [user, creator, group, router]) + + useSaveReferral(user, { + defaultReferrer: creator.username, + groupId: group?.id, + }) const { width } = useWindowSize() const chatDisabled = !group || group.chatDisabled diff --git a/web/pages/home.tsx b/web/pages/home.tsx index 53bb6ec9..61003895 100644 --- a/web/pages/home.tsx +++ b/web/pages/home.tsx @@ -12,6 +12,7 @@ import { getContractFromSlug } from 'web/lib/firebase/contracts' import { useTracking } from 'web/hooks/use-tracking' import { track } from 'web/lib/service/analytics' import { redirectIfLoggedOut } from 'web/lib/firebase/server-auth' +import { useSaveReferral } from 'web/hooks/use-save-referral' export const getServerSideProps = redirectIfLoggedOut('/') @@ -21,6 +22,8 @@ const Home = () => { const router = useRouter() useTracking('view home') + useSaveReferral() + return ( <> diff --git a/web/pages/index.tsx b/web/pages/index.tsx index d9ff7f51..fd5cf382 100644 --- a/web/pages/index.tsx +++ b/web/pages/index.tsx @@ -7,6 +7,7 @@ import { LandingPagePanel } from 'web/components/landing-page-panel' import { Col } from 'web/components/layout/col' import { ManifoldLogo } from 'web/components/nav/manifold-logo' import { redirectIfLoggedIn } from 'web/lib/firebase/server-auth' +import { useSaveReferral } from 'web/hooks/use-save-referral' export const getServerSideProps = redirectIfLoggedIn('/home', async (_) => { // These hardcoded markets will be shown in the frontpage for signed-out users: @@ -32,6 +33,9 @@ export default function Home(props: { hotContracts: Contract[] }) { // on this page and they log in -- in the future we will make some cleaner way const user = useUser() const router = useRouter() + + useSaveReferral() + useEffect(() => { if (user != null) { router.replace('/home') diff --git a/web/pages/referrals.tsx b/web/pages/referrals.tsx new file mode 100644 index 00000000..c879afaa --- /dev/null +++ b/web/pages/referrals.tsx @@ -0,0 +1,57 @@ +import { Col } from 'web/components/layout/col' +import { SEO } from 'web/components/SEO' +import { Title } from 'web/components/title' +import { useUser } from 'web/hooks/use-user' +import { Page } from 'web/components/page' +import { useTracking } from 'web/hooks/use-tracking' +import { redirectIfLoggedOut } from 'web/lib/firebase/server-auth' +import { REFERRAL_AMOUNT } from 'common/user' +import { CopyLinkButton } from 'web/components/copy-link-button' +import { ENV_CONFIG } from 'common/envs/constants' +import { InfoBox } from 'web/components/info-box' + +export const getServerSideProps = redirectIfLoggedOut('/') + +export default function ReferralsPage() { + const user = useUser() + + useTracking('view referrals') + + const url = `https://${ENV_CONFIG.domain}?referrer=${user?.username}` + + return ( + + + + + + + <img + className="mb-6 block -scale-x-100 self-center" + src="/logo-flapping-with-money.gif" + width={200} + height={200} + /> + + <div className={'mb-4'}> + Invite new users to Manifold and get M${REFERRAL_AMOUNT} if they + sign up! + </div> + + <CopyLinkButton + url={url} + tracking="copy referral link" + buttonClassName="btn-md rounded-l-none" + toastClassName={'-left-28 mt-1'} + /> + + <InfoBox + title="FYI" + className="mt-4 max-w-md" + text="You can also earn the referral bonus from sharing the link to any market or group you've created!" + /> + </Col> + </Col> + </Page> + ) +} diff --git a/web/public/logo-flapping-with-money.gif b/web/public/logo-flapping-with-money.gif new file mode 100644 index 00000000..0ef936a4 Binary files /dev/null and b/web/public/logo-flapping-with-money.gif differ