diff --git a/common/user.ts b/common/user.ts index 48a3d59c..34dce8c9 100644 --- a/common/user.ts +++ b/common/user.ts @@ -62,6 +62,10 @@ export type PrivateUser = { initialIpAddress?: string apiKey?: string notificationPreferences?: notification_subscribe_types + twitchInfo?: { + twitchName: string + controlToken: string + } } export type notification_subscribe_types = 'all' | 'less' | 'none' diff --git a/firestore.rules b/firestore.rules index 4cd718d3..aa0dbb53 100644 --- a/firestore.rules +++ b/firestore.rules @@ -70,7 +70,7 @@ service cloud.firestore { allow read: if userId == request.auth.uid || isAdmin(); allow update: if (userId == request.auth.uid || isAdmin()) && request.resource.data.diff(resource.data).affectedKeys() - .hasOnly(['apiKey', 'unsubscribedFromResolutionEmails', 'unsubscribedFromCommentEmails', 'unsubscribedFromAnswerEmails', 'notificationPreferences', 'unsubscribedFromWeeklyTrendingEmails' ]); + .hasOnly(['apiKey', 'twitchInfo', 'unsubscribedFromResolutionEmails', 'unsubscribedFromCommentEmails', 'unsubscribedFromAnswerEmails', 'notificationPreferences', 'unsubscribedFromWeeklyTrendingEmails' ]); } match /private-users/{userId}/views/{viewId} { diff --git a/web/components/auth-context.tsx b/web/components/auth-context.tsx index 5b7c9c2b..6957d062 100644 --- a/web/components/auth-context.tsx +++ b/web/components/auth-context.tsx @@ -13,7 +13,6 @@ import { createUser } from 'web/lib/firebase/api' import { randomString } from 'common/util/random' import { identifyUser, setUserProperty } from 'web/lib/service/analytics' import { useStateCheckEquality } from 'web/hooks/use-state-check-equality' -import { handleRedirectAfterSignup } from 'web/hooks/use-redirect-after-signup' // Either we haven't looked up the logged in user yet (undefined), or we know // the user is not logged in (null), or we know the user is logged in. @@ -64,13 +63,11 @@ export function AuthProvider(props: { // Note: Cap on localStorage size is ~5mb localStorage.setItem(CACHED_USER_KEY, JSON.stringify(current)) setCachedReferralInfoForUser(current.user) - handleRedirectAfterSignup(current.user) } else { // User logged out; reset to null deleteTokenCookies() setAuthUser(null) localStorage.removeItem(CACHED_USER_KEY) - handleRedirectAfterSignup(null) } }) }, [setAuthUser]) diff --git a/web/lib/twitch/link-twitch-account.ts b/web/lib/twitch/link-twitch-account.ts index ba3d1e4a..d604c9b1 100644 --- a/web/lib/twitch/link-twitch-account.ts +++ b/web/lib/twitch/link-twitch-account.ts @@ -1,3 +1,7 @@ +import { User, PrivateUser } from 'common/lib/user' +import { generateNewApiKey } from '../api/api-key' +import { updatePrivateUser } from '../firebase/users' + const TWITCH_BOT_PUBLIC_URL = 'https://king-prawn-app-5btyw.ondigitalocean.app' export async function initLinkTwitchAccount( @@ -23,3 +27,21 @@ export async function initLinkTwitchAccount( ) return [responseData.twitchAuthURL, responseFetch.then((r) => r.json())] } + +export async function linkTwitchAccount(user: User, privateUser: PrivateUser) { + const apiKey = privateUser.apiKey ?? (await generateNewApiKey(user.id)) + if (!apiKey) throw new Error("Couldn't retrieve or create Manifold api key") + + const [twitchAuthURL, linkSuccessPromise] = await initLinkTwitchAccount( + user.id, + apiKey + ) + + console.log('opening twitch link', twitchAuthURL) + window.open(twitchAuthURL) + + const twitchInfo = await linkSuccessPromise + await updatePrivateUser(user.id, { twitchInfo }) + + console.log(`Successfully linked Twitch account '${twitchInfo.twitchName}'`) +} diff --git a/web/pages/twitch.tsx b/web/pages/twitch.tsx index a8fdb291..440c0615 100644 --- a/web/pages/twitch.tsx +++ b/web/pages/twitch.tsx @@ -1,21 +1,53 @@ +import { useState } from 'react' + import { Page } from 'web/components/page' import { Col } from 'web/components/layout/col' import { ManifoldLogo } from 'web/components/nav/manifold-logo' import { useSaveReferral } from 'web/hooks/use-save-referral' import { SEO } from 'web/components/SEO' import { Spacer } from 'web/components/layout/spacer' -import { firebaseLogin } from 'web/lib/firebase/users' -import { withTracking } from 'web/lib/service/analytics' +import { firebaseLogin, getUserAndPrivateUser } from 'web/lib/firebase/users' +import { track } from 'web/lib/service/analytics' import { Row } from 'web/components/layout/row' import { Button } from 'web/components/button' import { useTracking } from 'web/hooks/use-tracking' -import { useRedirectAfterSignup } from 'web/hooks/use-redirect-after-signup' +import { linkTwitchAccount } from 'web/lib/twitch/link-twitch-account' +import { usePrivateUser, useUser } from 'web/hooks/use-user' +import { LoadingIndicator } from 'web/components/loading-indicator' export default function TwitchLandingPage() { - useRedirectAfterSignup('twitch') useSaveReferral() useTracking('view twitch landing page') + const user = useUser() + const privateUser = usePrivateUser() + const twitchUser = privateUser?.twitchInfo?.twitchName + + const callback = + user && privateUser + ? () => linkTwitchAccount(user, privateUser) + : async () => { + const result = await firebaseLogin() + + const userId = result.user.uid + const { user, privateUser } = await getUserAndPrivateUser(userId) + if (!user || !privateUser) return + + await linkTwitchAccount(user, privateUser) + } + + const [isLoading, setLoading] = useState(false) + + const getStarted = async () => { + setLoading(true) + + const promise = callback() + track('twitch page button click') + await promise + + setLoading(false) + } + return ( + - + + {twitchUser ? ( +
+
+
+ Twitch account linked +
+
+ {twitchUser} +
+
+
+ ) : isLoading ? ( + + ) : ( + + )}