diff --git a/src/languages/en.ts b/src/languages/en.ts index 7f15a7193fee..a10a7b4282ae 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -32,7 +32,6 @@ import type { LogSizeParams, ManagerApprovedAmountParams, ManagerApprovedParams, - NewFaceEnterMagicCodeParams, NoLongerHaveAccessParams, NotAllowedExtensionParams, NotYouParams, @@ -65,6 +64,7 @@ import type { SetTheRequestParams, SettledAfterAddedBankAccountParams, SettleExpensifyCardParams, + SignUpNewFaceCodeParams, SizeExceededParams, SplitAmountParams, StepCounterParams, @@ -430,11 +430,11 @@ export default { anotherLoginPageIsOpen: 'Another login page is open.', anotherLoginPageIsOpenExplanation: "You've opened the login page in a separate tab, please login from that specific tab.", welcome: 'Welcome!', + welcomeWithoutExclamation: 'Welcome', phrase2: "Money talks. And now that chat and payments are in one place, it's also easy.", phrase3: 'Your payments get to you as fast as you can get your point across.', enterPassword: 'Please enter your password', - newFaceEnterMagicCode: ({login}: NewFaceEnterMagicCodeParams) => - `It's always great to see a new face around here! Please enter the magic code sent to ${login}. It should arrive within a minute or two.`, + welcomeNewFace: ({login}: SignUpNewFaceCodeParams) => `${login}, it's always great to see a new face around here!`, welcomeEnterMagicCode: ({login}: WelcomeEnterMagicCodeParams) => `Please enter the magic code sent to ${login}. It should arrive within a minute or two.`, }, login: { @@ -1461,6 +1461,9 @@ export default { onceTheAbove: 'Once the above steps are completed, please reach out to ', toUnblock: ' to unblock your login.', }, + welcomeSignUpForm: { + join: 'Join', + }, detailsPage: { localTime: 'Local time', }, diff --git a/src/languages/es.ts b/src/languages/es.ts index c2e083bbc2c1..63f37e822603 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -32,7 +32,6 @@ import type { LogSizeParams, ManagerApprovedAmountParams, ManagerApprovedParams, - NewFaceEnterMagicCodeParams, NoLongerHaveAccessParams, NotAllowedExtensionParams, NotYouParams, @@ -65,6 +64,7 @@ import type { SetTheRequestParams, SettledAfterAddedBankAccountParams, SettleExpensifyCardParams, + SignUpNewFaceCodeParams, SizeExceededParams, SplitAmountParams, StepCounterParams, @@ -422,11 +422,11 @@ export default { anotherLoginPageIsOpen: 'Otra página de inicio de sesión está abierta.', anotherLoginPageIsOpenExplanation: 'Ha abierto la página de inicio de sesión en una pestaña separada, inicie sesión desde esa pestaña específica.', welcome: '¡Bienvenido!', + welcomeWithoutExclamation: 'Bienvenido', phrase2: 'El dinero habla. Y ahora que chat y pagos están en un mismo lugar, es también fácil.', phrase3: 'Tus pagos llegan tan rápido como tus mensajes.', enterPassword: 'Por favor, introduce tu contraseña', - newFaceEnterMagicCode: ({login}: NewFaceEnterMagicCodeParams) => - `¡Siempre es genial ver una cara nueva por aquí! Por favor ingresa el código mágico enviado a ${login}. Debería llegar en un par de minutos.`, + welcomeNewFace: ({login}: SignUpNewFaceCodeParams) => `${login}, siempre es genial ver una cara nueva por aquí!`, welcomeEnterMagicCode: ({login}: WelcomeEnterMagicCodeParams) => `Por favor, introduce el código mágico enviado a ${login}. Debería llegar en un par de minutos.`, }, login: { @@ -1463,6 +1463,9 @@ export default { onceTheAbove: 'Una vez completados los pasos anteriores, ponte en contacto con ', toUnblock: ' para desbloquear el inicio de sesión.', }, + welcomeSignUpForm: { + join: 'Unirse', + }, detailsPage: { localTime: 'Hora local', }, diff --git a/src/languages/types.ts b/src/languages/types.ts index e2e7e26e696b..5f0fc761e2de 100644 --- a/src/languages/types.ts +++ b/src/languages/types.ts @@ -18,7 +18,7 @@ type LoggedInAsParams = { email: string; }; -type NewFaceEnterMagicCodeParams = { +type SignUpNewFaceCodeParams = { login: string; }; @@ -329,7 +329,7 @@ export type { LoggedInAsParams, ManagerApprovedAmountParams, ManagerApprovedParams, - NewFaceEnterMagicCodeParams, + SignUpNewFaceCodeParams, NoLongerHaveAccessParams, NotAllowedExtensionParams, NotYouParams, diff --git a/src/libs/API/parameters/BeginSignInParams.ts b/src/libs/API/parameters/BeginSignInParams.ts index 2f85a3335c62..0dec2587ed14 100644 --- a/src/libs/API/parameters/BeginSignInParams.ts +++ b/src/libs/API/parameters/BeginSignInParams.ts @@ -1,5 +1,6 @@ type BeginSignInParams = { email: string; + useNewBeginSignIn: boolean; }; export default BeginSignInParams; diff --git a/src/libs/API/parameters/SignUpUserParams.ts b/src/libs/API/parameters/SignUpUserParams.ts new file mode 100644 index 000000000000..00080017d30f --- /dev/null +++ b/src/libs/API/parameters/SignUpUserParams.ts @@ -0,0 +1,9 @@ +import type {ValueOf} from 'type-fest'; +import type CONST from '@src/CONST'; + +type SignUpUserParams = { + email?: string; + preferredLocale: ValueOf | null; +}; + +export default SignUpUserParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index de8d6f55418b..6f642dab08e7 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -224,3 +224,4 @@ export type {default as SearchParams} from './Search'; export type {default as SendInvoiceParams} from './SendInvoiceParams'; export type {default as PayInvoiceParams} from './PayInvoiceParams'; export type {default as MarkAsCashParams} from './MarkAsCashParams'; +export type {default as SignUpUserParams} from './SignUpUserParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 3f1acaaed466..eca8692a1bfa 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -221,6 +221,7 @@ const WRITE_COMMANDS = { SEND_INVOICE: 'SendInvoice', PAY_INVOICE: 'PayInvoice', MARK_AS_CASH: 'MarkAsCash', + SIGN_UP_USER: 'SignUpUser', } as const; type WriteCommand = ValueOf; @@ -442,6 +443,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.SEND_INVOICE]: Parameters.SendInvoiceParams; [WRITE_COMMANDS.PAY_INVOICE]: Parameters.PayInvoiceParams; [WRITE_COMMANDS.MARK_AS_CASH]: Parameters.MarkAsCashParams; + [WRITE_COMMANDS.SIGN_UP_USER]: Parameters.SignUpUserParams; }; const READ_COMMANDS = { diff --git a/src/libs/LocalePhoneNumber.ts b/src/libs/LocalePhoneNumber.ts index 7249912e6f58..3e3e4c4e0def 100644 --- a/src/libs/LocalePhoneNumber.ts +++ b/src/libs/LocalePhoneNumber.ts @@ -19,6 +19,9 @@ function formatPhoneNumber(number: string): string { return ''; } + // eslint-disable-next-line no-param-reassign + number = number.replace(/ /g, '\u00A0'); + // do not parse the string, if it doesn't contain the SMS domain and it's not a phone number if (number.indexOf(CONST.SMS.DOMAIN) === -1 && !CONST.REGEX.DIGITS_AND_PLUS.test(number)) { return number; diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 303517558206..5d336a7bebb5 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -16,6 +16,7 @@ import type { RequestNewValidateCodeParams, RequestUnlinkValidationLinkParams, SignInUserWithLinkParams, + SignUpUserParams, UnlinkLoginParams, ValidateTwoFactorAuthParams, } from '@libs/API/parameters'; @@ -405,11 +406,52 @@ function signInAttemptState(): OnyxData { function beginSignIn(email: string) { const {optimisticData, successData, failureData} = signInAttemptState(); - const params: BeginSignInParams = {email}; + const params: BeginSignInParams = {email, useNewBeginSignIn: true}; API.read(READ_COMMANDS.BEGIN_SIGNIN, params, {optimisticData, successData, failureData}); } +/** + * Creates an account for the new user and signs them into the application with the newly created account. + * + */ +function signUpUser() { + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.ACCOUNT, + value: { + ...CONST.DEFAULT_ACCOUNT_DATA, + isLoading: true, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.ACCOUNT, + value: { + isLoading: false, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.ACCOUNT, + value: { + isLoading: false, + }, + }, + ]; + + const params: SignUpUserParams = {email: credentials.login, preferredLocale}; + + API.write(WRITE_COMMANDS.SIGN_UP_USER, params, {optimisticData, successData, failureData}); +} + /** * Given an idToken from Sign in with Apple, checks the API to see if an account * exists for that email address and signs the user in if so. @@ -1004,4 +1046,5 @@ export { signInWithSupportAuthToken, isSupportAuthToken, hasStashedSession, + signUpUser, }; diff --git a/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.tsx b/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.tsx index 28562c5e8d00..710bcd98c017 100644 --- a/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.tsx +++ b/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.tsx @@ -186,7 +186,7 @@ function ContactMethodDetailsPage({route}: ContactMethodDetailsPageProps) { } // Replacing spaces with "hard spaces" to prevent breaking the number - const formattedContactMethod = Str.isSMSLogin(contactMethod) ? formatPhoneNumber(contactMethod).replace(/ /g, '\u00A0') : contactMethod; + const formattedContactMethod = Str.isSMSLogin(contactMethod) ? formatPhoneNumber(contactMethod) : contactMethod; const hasMagicCodeBeenSent = !!loginData.validateCodeSent; const isFailedAddContactMethod = !!loginData.errorFields?.addedLogin; const isFailedRemovedContactMethod = !!loginData.errorFields?.deletedLogin; diff --git a/src/pages/signin/SignInPage.tsx b/src/pages/signin/SignInPage.tsx index 437ac8724c96..879f2c047052 100644 --- a/src/pages/signin/SignInPage.tsx +++ b/src/pages/signin/SignInPage.tsx @@ -31,6 +31,7 @@ import LoginForm from './LoginForm'; import type {InputHandle} from './LoginForm/types'; import SignInPageLayout from './SignInPageLayout'; import type {SignInPageLayoutRef} from './SignInPageLayout/types'; +import SignUpWelcomeForm from './SignUpWelcomeForm'; import UnlinkLoginForm from './UnlinkLoginForm'; import ValidateCodeForm from './ValidateCodeForm'; @@ -61,6 +62,7 @@ type RenderOption = { shouldInitiateSAMLLogin: boolean; shouldShowWelcomeHeader: boolean; shouldShowWelcomeText: boolean; + shouldShouldSignUpWelcomeForm: boolean; }; type GetRenderOptionsParams = { @@ -71,6 +73,7 @@ type GetRenderOptionsParams = { isUsingMagicCode: boolean; hasInitiatedSAMLLogin: boolean; shouldShowAnotherLoginPageOpenedMessage: boolean; + credentials: OnyxEntry; }; /** @@ -90,6 +93,7 @@ function getRenderOptions({ isUsingMagicCode, hasInitiatedSAMLLogin, shouldShowAnotherLoginPageOpenedMessage, + credentials, }: GetRenderOptionsParams): RenderOption { const hasAccount = !isEmptyObject(account); const isSAMLEnabled = !!account?.isSAMLEnabled; @@ -107,22 +111,33 @@ function getRenderOptions({ Session.clearSignInData(); } + // Show the Welcome form if a user is signing up for a new account in a domain that is not controlled + const shouldShouldSignUpWelcomeForm = Boolean(credentials?.login) && !account?.validated && !account?.accountExists && !account?.domainControlled; const shouldShowLoginForm = !shouldShowAnotherLoginPageOpenedMessage && !hasLogin && !hasValidateCode; const shouldShowEmailDeliveryFailurePage = hasLogin && hasEmailDeliveryFailure && !shouldShowChooseSSOOrMagicCode && !shouldInitiateSAMLLogin; const isUnvalidatedSecondaryLogin = hasLogin && !isPrimaryLogin && !account?.validated && !hasEmailDeliveryFailure; const shouldShowValidateCodeForm = - hasAccount && (hasLogin || hasValidateCode) && !isUnvalidatedSecondaryLogin && !hasEmailDeliveryFailure && !shouldShowChooseSSOOrMagicCode && !isSAMLRequired; - const shouldShowWelcomeHeader = shouldShowLoginForm || shouldShowValidateCodeForm || shouldShowChooseSSOOrMagicCode || isUnvalidatedSecondaryLogin; - const shouldShowWelcomeText = shouldShowLoginForm || shouldShowValidateCodeForm || shouldShowChooseSSOOrMagicCode || shouldShowAnotherLoginPageOpenedMessage; + !shouldShouldSignUpWelcomeForm && + hasAccount && + (hasLogin || hasValidateCode) && + !isUnvalidatedSecondaryLogin && + !hasEmailDeliveryFailure && + !shouldShowChooseSSOOrMagicCode && + !isSAMLRequired; + const shouldShowWelcomeHeader = shouldShowLoginForm || shouldShowValidateCodeForm || shouldShowChooseSSOOrMagicCode || isUnvalidatedSecondaryLogin || shouldShouldSignUpWelcomeForm; + const shouldShowWelcomeText = + shouldShowLoginForm || shouldShowValidateCodeForm || shouldShowChooseSSOOrMagicCode || shouldShowAnotherLoginPageOpenedMessage || shouldShouldSignUpWelcomeForm; + return { shouldShowLoginForm, shouldShowEmailDeliveryFailurePage, - shouldShowUnlinkLoginForm: isUnvalidatedSecondaryLogin, + shouldShowUnlinkLoginForm: !shouldShouldSignUpWelcomeForm && isUnvalidatedSecondaryLogin, shouldShowValidateCodeForm, shouldShowChooseSSOOrMagicCode, shouldInitiateSAMLLogin, shouldShowWelcomeHeader, shouldShowWelcomeText, + shouldShouldSignUpWelcomeForm, }; } @@ -181,6 +196,7 @@ function SignInPageInner({credentials, account, activeClients = [], preferredLoc shouldInitiateSAMLLogin, shouldShowWelcomeHeader, shouldShowWelcomeText, + shouldShouldSignUpWelcomeForm, } = getRenderOptions({ hasLogin: !!credentials?.login, hasValidateCode: !!credentials?.validateCode, @@ -189,6 +205,7 @@ function SignInPageInner({credentials, account, activeClients = [], preferredLoc isUsingMagicCode, hasInitiatedSAMLLogin, shouldShowAnotherLoginPageOpenedMessage, + credentials, }); if (shouldInitiateSAMLLogin) { @@ -200,6 +217,11 @@ function SignInPageInner({credentials, account, activeClients = [], preferredLoc let welcomeText = ''; const headerText = translate('login.hero.header'); + const userLogin = Str.removeSMSDomain(credentials?.login ?? ''); + + // replacing spaces with "hard spaces" to prevent breaking the number + const userLoginToDisplay = Str.isSMSLogin(userLogin) ? formatPhoneNumber(userLogin) : userLogin; + if (shouldShowAnotherLoginPageOpenedMessage) { welcomeHeader = translate('welcomeText.anotherLoginPageIsOpen'); welcomeText = translate('welcomeText.anotherLoginPageIsOpenExplanation'); @@ -212,21 +234,10 @@ function SignInPageInner({credentials, account, activeClients = [], preferredLoc welcomeHeader = shouldUseNarrowLayout ? '' : translate('welcomeText.welcome'); welcomeText = isUsingRecoveryCode ? translate('validateCodeForm.enterRecoveryCode') : translate('validateCodeForm.enterAuthenticatorCode'); } else { - const userLogin = Str.removeSMSDomain(credentials?.login ?? ''); - - // replacing spaces with "hard spaces" to prevent breaking the number - const userLoginToDisplay = Str.isSMSLogin(userLogin) ? formatPhoneNumber(userLogin).replace(/ /g, '\u00A0') : userLogin; - if (account?.validated) { - welcomeHeader = shouldUseNarrowLayout ? '' : translate('welcomeText.welcome'); - welcomeText = shouldUseNarrowLayout - ? `${translate('welcomeText.welcome')} ${translate('welcomeText.welcomeEnterMagicCode', {login: userLoginToDisplay})}` - : translate('welcomeText.welcomeEnterMagicCode', {login: userLoginToDisplay}); - } else { - welcomeHeader = shouldUseNarrowLayout ? '' : translate('welcomeText.welcome'); - welcomeText = shouldUseNarrowLayout - ? `${translate('welcomeText.welcome')} ${translate('welcomeText.newFaceEnterMagicCode', {login: userLoginToDisplay})}` - : translate('welcomeText.newFaceEnterMagicCode', {login: userLoginToDisplay}); - } + welcomeHeader = shouldUseNarrowLayout ? '' : translate('welcomeText.welcome'); + welcomeText = shouldUseNarrowLayout + ? `${translate('welcomeText.welcome')} ${translate('welcomeText.welcomeEnterMagicCode', {login: userLoginToDisplay})}` + : translate('welcomeText.welcomeEnterMagicCode', {login: userLoginToDisplay}); } } else if (shouldShowUnlinkLoginForm || shouldShowEmailDeliveryFailurePage || shouldShowChooseSSOOrMagicCode) { welcomeHeader = shouldUseNarrowLayout ? headerText : translate('welcomeText.welcome'); @@ -235,6 +246,11 @@ function SignInPageInner({credentials, account, activeClients = [], preferredLoc if (shouldShowEmailDeliveryFailurePage || shouldShowChooseSSOOrMagicCode) { welcomeText = ''; } + } else if (shouldShouldSignUpWelcomeForm) { + welcomeHeader = shouldUseNarrowLayout ? headerText : translate('welcomeText.welcome'); + welcomeText = shouldUseNarrowLayout + ? `${translate('welcomeText.welcomeWithoutExclamation')} ${translate('welcomeText.welcomeNewFace', {login: userLoginToDisplay})}` + : translate('welcomeText.welcomeNewFace', {login: userLoginToDisplay}); } else if (!shouldInitiateSAMLLogin && !hasInitiatedSAMLLogin) { Log.warn('SignInPage in unexpected state!'); } @@ -269,6 +285,7 @@ function SignInPageInner({credentials, account, activeClients = [], preferredLoc blurOnSubmit={account?.validated === false} scrollPageToTop={signInPageLayoutRef.current?.scrollPageToTop} /> + {shouldShouldSignUpWelcomeForm && } {shouldShowValidateCodeForm && ( ; +}; + +type SignUpWelcomeFormProps = SignUpWelcomeFormOnyxProps; + +function SignUpWelcomeForm({account}: SignUpWelcomeFormProps) { + const network = useNetwork(); + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + return ( + <> + +