diff --git a/src/actions/actionWelcomeSMSAfterPhoneSignUp.ts b/src/actions/actionWelcomeSMSAfterPhoneSignUp.ts new file mode 100644 index 000000000..c6e5c81fc --- /dev/null +++ b/src/actions/actionWelcomeSMSAfterPhoneSignUp.ts @@ -0,0 +1,32 @@ +'use server' +import 'server-only' + +import { appRouterGetAuthUser } from '@/utils/server/authentication/appRouterGetAuthUser' +import { prismaClient } from '@/utils/server/prismaClient' +import { throwIfRateLimited } from '@/utils/server/ratelimit/throwIfRateLimited' +import { optInUser } from '@/utils/server/sms/actions' +import { withServerActionMiddleware } from '@/utils/server/withServerActionMiddleware' + +export const actionWelcomeSMSAfterPhoneSignUp = withServerActionMiddleware( + 'actionWelcomeSMSAfterPhoneSignUp', + _actionWelcomeSMSAfterPhoneSignUp, +) + +async function _actionWelcomeSMSAfterPhoneSignUp() { + const authUser = await appRouterGetAuthUser() + if (!authUser) { + throw new Error('Unauthenticated') + } + + await throwIfRateLimited({ context: 'authenticated' }) + + const user = await prismaClient.user.findFirstOrThrow({ + where: { + id: authUser.userId, + }, + }) + + if (user.hasOptedInToSms && user.phoneNumber) { + await optInUser(user.phoneNumber, user) + } +} diff --git a/src/app/[locale]/topLevelClientLogic.tsx b/src/app/[locale]/topLevelClientLogic.tsx index 1fb7abdd8..5eaa13df2 100644 --- a/src/app/[locale]/topLevelClientLogic.tsx +++ b/src/app/[locale]/topLevelClientLogic.tsx @@ -14,7 +14,9 @@ import { walletConnect, } from '@thirdweb-dev/react' import { usePathname, useSearchParams } from 'next/navigation' +import { AuthOption } from 'node_modules/@thirdweb-dev/react/dist/declarations/src/wallet/wallets/embeddedWallet/types' +import { useExperimentName } from '@/components/ui/experimentsTesting' import { useThirdwebAuthUser } from '@/hooks/useAuthUser' import { useDetectWipedDatabaseAndLogOutUser } from '@/hooks/useDetectWipedDatabaseAndLogOutUser' import { LocaleContext } from '@/hooks/useLocale' @@ -88,13 +90,19 @@ export function TopLevelClientLogic({ children: React.ReactNode locale: SupportedLocale }) { + const currentExperiment = useExperimentName({ + experimentName: 'gh03_ThirdwebSignUpPhoneNumberExperiment', + }) + + const walletAuthOptions: AuthOption[] = currentExperiment === 'variant' ? ['google', 'phone', 'email'] : ['google', 'email'] + const supportedWallets: WalletConfig[] = [ metamaskWallet(), coinbaseWallet({ recommended: true }), walletConnect(), embeddedWallet({ auth: { - options: ['google', 'phone', 'email'], + options: walletAuthOptions, }, }), ] diff --git a/src/clientModels/clientUser/sensitiveDataClientUser.ts b/src/clientModels/clientUser/sensitiveDataClientUser.ts index 3090f10f2..3ed914545 100644 --- a/src/clientModels/clientUser/sensitiveDataClientUser.ts +++ b/src/clientModels/clientUser/sensitiveDataClientUser.ts @@ -25,6 +25,7 @@ export type SensitiveDataClientUser = ClientModel< | 'hasOptedInToMembership' | 'hasOptedInToSms' | 'hasRepliedToOptInSms' + | 'smsStatus' | 'referralId' > & { hasEmbeddedWallet: boolean @@ -61,6 +62,7 @@ export const getSensitiveDataClientUser = ( hasRepliedToOptInSms, referralId, address, + smsStatus, } = record const userLocationDetails = address ? { @@ -89,6 +91,7 @@ export const getSensitiveDataClientUser = ( hasOptedInToSms, hasRepliedToOptInSms, userLocationDetails, + smsStatus, }) } diff --git a/src/components/app/authentication/loginDialogWrapper.tsx b/src/components/app/authentication/loginDialogWrapper.tsx index 8c4517d56..8437ef526 100644 --- a/src/components/app/authentication/loginDialogWrapper.tsx +++ b/src/components/app/authentication/loginDialogWrapper.tsx @@ -1,12 +1,14 @@ 'use client' import React from 'react' +import { SMSStatus } from '@prisma/client' import * as Sentry from '@sentry/nextjs' import { useENS } from '@thirdweb-dev/react' import dynamic from 'next/dynamic' import { useRouter } from 'next/navigation' import useSWR, { Arguments, useSWRConfig } from 'swr' +import { actionWelcomeSMSAfterPhoneSignUp } from '@/actions/actionWelcomeSMSAfterPhoneSignUp' import { ClientUnidentifiedUser } from '@/clientModels/clientUser/clientUser' import { ANALYTICS_NAME_LOGIN, @@ -20,6 +22,7 @@ import { useApiResponseForUserFullProfileInfo } from '@/hooks/useApiResponseForU import { useDialog } from '@/hooks/useDialog' import { useSections } from '@/hooks/useSections' import { useSession } from '@/hooks/useSession' +import { optInUser } from '@/utils/server/sms/actions' import { fetchReq } from '@/utils/shared/fetchReq' import { apiUrls } from '@/utils/shared/urls' import { appendENSHookDataToUser } from '@/utils/web/appendENSHookDataToUser' @@ -188,6 +191,10 @@ export function UnauthenticatedSection({ return } + if (user.phoneNumber && user.hasOptedInToSms && user.smsStatus === SMSStatus.NOT_OPTED_IN) { + await actionWelcomeSMSAfterPhoneSignUp() + } + const { wasRecentlyUpdated } = user.primaryUserCryptoAddress if (wasRecentlyUpdated) { goToSection(LoginSections.FINISH_PROFILE) diff --git a/src/components/app/authentication/thirdwebLoginContent.tsx b/src/components/app/authentication/thirdwebLoginContent.tsx index e0cb41b17..f6bfb06f5 100644 --- a/src/components/app/authentication/thirdwebLoginContent.tsx +++ b/src/components/app/authentication/thirdwebLoginContent.tsx @@ -93,7 +93,7 @@ export function ThirdwebLoginContent({ ) : ( - + By signing up with my phone number, you consent to receive recurring texts from Stand with Crypto. You can reply STOP to stop receiving texts. Message and data rates may apply. You understand that Stand With Crypto and its vendors may collect and use your Personal diff --git a/src/components/ui/experimentsTesting/index.ts b/src/components/ui/experimentsTesting/index.ts index 6aed1367d..4ccb9919c 100644 --- a/src/components/ui/experimentsTesting/index.ts +++ b/src/components/ui/experimentsTesting/index.ts @@ -6,7 +6,7 @@ import { IExperimentContext, } from '@/utils/shared/experiments' -function useExperimentName({ experimentName }: { experimentName: K }) { +export function useExperimentName({ experimentName }: { experimentName: K }) { const localUser = useLocalUser() const experimentVariants = getExperimentVariants(experimentName) diff --git a/src/globals.css b/src/globals.css index a0072db9d..ba7df6eba 100644 --- a/src/globals.css +++ b/src/globals.css @@ -122,6 +122,8 @@ /** Hides Thirdweb's phone sign in country select (locks to US only) **/ select[id='countries'] { - display: none !important; - user-select: none; + opacity: 0; + width: 10px; + padding-right: 0; + pointer-events: none; } diff --git a/src/utils/server/thirdweb/onLogin.ts b/src/utils/server/thirdweb/onLogin.ts index 664aa1f69..13f6fe1f5 100644 --- a/src/utils/server/thirdweb/onLogin.ts +++ b/src/utils/server/thirdweb/onLogin.ts @@ -385,7 +385,7 @@ async function queryMatchingUsers({ phoneNumber: embeddedWalletUserDetails.phone, hasOptedInToSms: true, hasRepliedToOptInSms: true, - smsStatus: SMSStatus.OPTED_IN_HAS_REPLIED, + smsStatus: SMSStatus.NOT_OPTED_IN, primaryUserEmailAddressId: { not: null, }, @@ -494,7 +494,7 @@ async function maybeUpsertPhoneNumber({ phoneNumber: embeddedWalletUserDetails.phone, hasOptedInToSms: true, hasRepliedToOptInSms: true, - smsStatus: SMSStatus.OPTED_IN_HAS_REPLIED, + smsStatus: SMSStatus.NOT_OPTED_IN, }, }) } diff --git a/src/utils/shared/experiments.ts b/src/utils/shared/experiments.ts index daf917d86..a1d6353d4 100644 --- a/src/utils/shared/experiments.ts +++ b/src/utils/shared/experiments.ts @@ -48,6 +48,13 @@ export const EXPERIMENTS_CONFIG = { { name: 'optionalFieldsVariant' as const, percentage: 0.5 }, ], }, + gh03_ThirdwebSignUpPhoneNumberExperiment: { + analyticsPropertyName: 'Thirdweb Sign Up Phone Number Experiment', + variants: [ + { name: 'control' as const, percentage: 0.5 }, + { name: 'variant' as const, percentage: 0.5 }, + ], + }, } satisfies Record> export type Experiments = keyof typeof EXPERIMENTS_CONFIG type _ExperimentVariantsConfig =