Skip to content

Commit

Permalink
Merge pull request Expensify#52305 from nyomanjyotisa/issue-52086
Browse files Browse the repository at this point in the history
Create SMS delivery failure sign in flow in homepage
  • Loading branch information
carlosmiceli authored Nov 27, 2024
2 parents f222bfe + fca78b0 commit c62ab78
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 39 deletions.
3 changes: 3 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1824,6 +1824,9 @@ const translations = {
onceTheAbove: 'Once the above steps are completed, please reach out to ',
toUnblock: ' to unblock your login.',
},
smsDeliveryFailurePage: {
smsDeliveryFailureMessage: ({login}: OurEmailProviderParams) => `We've been unable to deliver SMS messages to ${login}, so we've suspended it for 24 hours.`,
},
welcomeSignUpForm: {
join: 'Join',
},
Expand Down
3 changes: 3 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1829,6 +1829,9 @@ const translations = {
onceTheAbove: 'Una vez completados los pasos anteriores, ponte en contacto con ',
toUnblock: ' para desbloquear el inicio de sesión.',
},
smsDeliveryFailurePage: {
smsDeliveryFailureMessage: ({login}: OurEmailProviderParams) => `No hemos podido entregar mensajes SMS a ${login}, por lo que lo hemos suspendido durante 24 horas.`,
},
welcomeSignUpForm: {
join: 'Unirse',
},
Expand Down
68 changes: 68 additions & 0 deletions src/pages/signin/SMSDeliveryFailurePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import {Str} from 'expensify-common';
import React, {useEffect, useMemo} from 'react';
import {Keyboard, View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import Button from '@components/Button';
import Text from '@components/Text';
import useKeyboardState from '@hooks/useKeyboardState';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as Session from '@userActions/Session';
import ONYXKEYS from '@src/ONYXKEYS';
import ChangeExpensifyLoginLink from './ChangeExpensifyLoginLink';
import Terms from './Terms';

function SMSDeliveryFailurePage() {
const styles = useThemeStyles();
const {isKeyboardShown} = useKeyboardState();
const {translate} = useLocalize();
const [credentials] = useOnyx(ONYXKEYS.CREDENTIALS);
const [account] = useOnyx(ONYXKEYS.ACCOUNT);

const login = useMemo(() => {
if (!credentials?.login) {
return '';
}
return Str.isSMSLogin(credentials.login) ? Str.removeSMSDomain(credentials.login) : credentials.login;
}, [credentials?.login]);

const SMSDeliveryFailureMessage = account?.SMSDeliveryFailureStatus?.message;

useEffect(() => {
if (!isKeyboardShown) {
return;
}
Keyboard.dismiss();
}, [isKeyboardShown]);

return (
<>
<View style={[styles.mv3, styles.flexRow]}>
<View style={[styles.flex1]}>
<Text>
{translate('smsDeliveryFailurePage.smsDeliveryFailureMessage', {login})} {SMSDeliveryFailureMessage}
</Text>
</View>
</View>
<View style={[styles.mv4, styles.flexRow, styles.justifyContentBetween, styles.alignItemsEnd]}>
<Button
success
medium
text={translate('common.buttonConfirm')}
onPress={() => Session.clearSignInData()}
pressOnEnter
/>
</View>
<View style={[styles.mt3, styles.mb2]}>
<ChangeExpensifyLoginLink onPress={() => Session.clearSignInData()} />
</View>
<View style={[styles.mt4, styles.signInPageWelcomeTextContainer]}>
<Terms />
</View>
</>
);
}

SMSDeliveryFailurePage.displayName = 'SMSDeliveryFailurePage';

export default SMSDeliveryFailurePage;
71 changes: 32 additions & 39 deletions src/pages/signin/SignInPage.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {Str} from 'expensify-common';
import React, {forwardRef, useEffect, useImperativeHandle, useRef, useState} from 'react';
import type {ForwardedRef, RefAttributes} from 'react';
import {withOnyx} from 'react-native-onyx';
import type {ForwardedRef} from 'react';
import type {OnyxEntry} from 'react-native-onyx';
import {useOnyx} from 'react-native-onyx';
import ColorSchemeWrapper from '@components/ColorSchemeWrapper';
import CustomStatusBarAndBackground from '@components/CustomStatusBarAndBackground';
import ScreenWrapper from '@components/ScreenWrapper';
Expand All @@ -24,7 +24,7 @@ import * as Session from '@userActions/Session';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {Account, Credentials, Locale} from '@src/types/onyx';
import type {Account, Credentials} from '@src/types/onyx';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import ChooseSSOOrMagicCode from './ChooseSSOOrMagicCode';
import EmailDeliveryFailurePage from './EmailDeliveryFailurePage';
Expand All @@ -33,25 +33,12 @@ import type {InputHandle} from './LoginForm/types';
import SignInPageLayout from './SignInPageLayout';
import type {SignInPageLayoutRef} from './SignInPageLayout/types';
import SignUpWelcomeForm from './SignUpWelcomeForm';
import SMSDeliveryFailurePage from './SMSDeliveryFailurePage';
import UnlinkLoginForm from './UnlinkLoginForm';
import ValidateCodeForm from './ValidateCodeForm';
import type {BaseValidateCodeFormRef} from './ValidateCodeForm/BaseValidateCodeForm';

type SignInPageInnerOnyxProps = {
/** The details about the account that the user is signing in with */
account: OnyxEntry<Account>;

/** The credentials of the person signing in */
credentials: OnyxEntry<Credentials>;

/** Active Clients connected to ONYX Database */
activeClients: OnyxEntry<string[]>;

/** The user's preferred locale */
preferredLocale: OnyxEntry<Locale>;
};

type SignInPageInnerProps = SignInPageInnerOnyxProps & {
type SignInPageInnerProps = {
shouldEnableMaxHeight?: boolean;
};

Expand All @@ -62,6 +49,7 @@ type SignInPageRef = {
type RenderOption = {
shouldShowLoginForm: boolean;
shouldShowEmailDeliveryFailurePage: boolean;
shouldShowSMSDeliveryFailurePage: boolean;
shouldShowUnlinkLoginForm: boolean;
shouldShowValidateCodeForm: boolean;
shouldShowChooseSSOOrMagicCode: boolean;
Expand Down Expand Up @@ -90,6 +78,7 @@ type GetRenderOptionsParams = {
* @param isUsingMagicCode
* @param hasInitiatedSAMLLogin
* @param hasEmailDeliveryFailure
* @param hasSMSDeliveryFailure
*/
function getRenderOptions({
hasLogin,
Expand All @@ -105,6 +94,7 @@ function getRenderOptions({
const isSAMLEnabled = !!account?.isSAMLEnabled;
const isSAMLRequired = !!account?.isSAMLRequired;
const hasEmailDeliveryFailure = !!account?.hasEmailDeliveryFailure;
const hasSMSDeliveryFailure = !!account?.SMSDeliveryFailureStatus?.hasSMSDeliveryFailure;

// True, if the user has SAML required, and we haven't yet initiated SAML for their account
const shouldInitiateSAMLLogin = hasAccount && hasLogin && isSAMLRequired && !hasInitiatedSAMLLogin && !!account.isLoading;
Expand All @@ -121,13 +111,15 @@ function getRenderOptions({
const shouldShouldSignUpWelcomeForm = !!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 shouldShowSMSDeliveryFailurePage = hasLogin && hasSMSDeliveryFailure && !shouldShowChooseSSOOrMagicCode && !shouldInitiateSAMLLogin;
const isUnvalidatedSecondaryLogin = hasLogin && !isPrimaryLogin && !account?.validated && !hasEmailDeliveryFailure && !hasSMSDeliveryFailure;
const shouldShowValidateCodeForm =
!shouldShouldSignUpWelcomeForm &&
hasAccount &&
(hasLogin || hasValidateCode) &&
!isUnvalidatedSecondaryLogin &&
!hasEmailDeliveryFailure &&
!hasSMSDeliveryFailure &&
!shouldShowChooseSSOOrMagicCode &&
!isSAMLRequired;
const shouldShowWelcomeHeader = shouldShowLoginForm || shouldShowValidateCodeForm || shouldShowChooseSSOOrMagicCode || isUnvalidatedSecondaryLogin || shouldShouldSignUpWelcomeForm;
Expand All @@ -137,6 +129,7 @@ function getRenderOptions({
return {
shouldShowLoginForm,
shouldShowEmailDeliveryFailurePage,
shouldShowSMSDeliveryFailurePage,
shouldShowUnlinkLoginForm: !shouldShouldSignUpWelcomeForm && isUnvalidatedSecondaryLogin,
shouldShowValidateCodeForm,
shouldShowChooseSSOOrMagicCode,
Expand All @@ -147,7 +140,7 @@ function getRenderOptions({
};
}

function SignInPage({credentials, account, activeClients = [], preferredLocale, shouldEnableMaxHeight = true}: SignInPageInnerProps, ref: ForwardedRef<SignInPageRef>) {
function SignInPage({shouldEnableMaxHeight = true}: SignInPageInnerProps, ref: ForwardedRef<SignInPageRef>) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const {translate, formatPhoneNumber} = useLocalize();
Expand All @@ -157,6 +150,18 @@ function SignInPage({credentials, account, activeClients = [], preferredLocale,
const loginFormRef = useRef<InputHandle>(null);
const validateCodeFormRef = useRef<BaseValidateCodeFormRef>(null);

const [account] = useOnyx(ONYXKEYS.ACCOUNT);
const [credentials] = useOnyx(ONYXKEYS.CREDENTIALS);
/**
This variable is only added to make sure the component is re-rendered
whenever the activeClients change, so that we call the
ActiveClientManager.isClientTheLeader function
everytime the leader client changes.
We use that function to prevent repeating code that checks which client is the leader.
*/
const [activeClients = []] = useOnyx(ONYXKEYS.ACTIVE_CLIENTS);
const [preferredLocale] = useOnyx(ONYXKEYS.NVP_PREFERRED_LOCALE);

/** This state is needed to keep track of if user is using recovery code instead of 2fa code,
* and we need it here since welcome text(`welcomeText`) also depends on it */
const [isUsingRecoveryCode, setIsUsingRecoveryCode] = useState(false);
Expand Down Expand Up @@ -200,6 +205,7 @@ function SignInPage({credentials, account, activeClients = [], preferredLocale,
const {
shouldShowLoginForm,
shouldShowEmailDeliveryFailurePage,
shouldShowSMSDeliveryFailurePage,
shouldShowUnlinkLoginForm,
shouldShowValidateCodeForm,
shouldShowChooseSSOOrMagicCode,
Expand Down Expand Up @@ -249,11 +255,11 @@ function SignInPage({credentials, account, activeClients = [], preferredLocale,
? `${translate('welcomeText.welcome')} ${translate('welcomeText.welcomeEnterMagicCode', {login: userLoginToDisplay})}`
: translate('welcomeText.welcomeEnterMagicCode', {login: userLoginToDisplay});
}
} else if (shouldShowUnlinkLoginForm || shouldShowEmailDeliveryFailurePage || shouldShowChooseSSOOrMagicCode) {
} else if (shouldShowUnlinkLoginForm || shouldShowEmailDeliveryFailurePage || shouldShowChooseSSOOrMagicCode || shouldShowSMSDeliveryFailurePage) {
welcomeHeader = shouldUseNarrowLayout ? headerText : translate('welcomeText.welcome');

// Don't show any welcome text if we're showing the user the email delivery failed view
if (shouldShowEmailDeliveryFailurePage || shouldShowChooseSSOOrMagicCode) {
if (shouldShowEmailDeliveryFailurePage || shouldShowChooseSSOOrMagicCode || shouldShowSMSDeliveryFailurePage) {
welcomeText = '';
}
} else if (shouldShouldSignUpWelcomeForm) {
Expand All @@ -273,7 +279,8 @@ function SignInPage({credentials, account, activeClients = [], preferredLocale,
const navigateBack = () => {
if (
shouldShouldSignUpWelcomeForm ||
(!shouldShowAnotherLoginPageOpenedMessage && (shouldShowEmailDeliveryFailurePage || shouldShowUnlinkLoginForm || shouldShowChooseSSOOrMagicCode))
(!shouldShowAnotherLoginPageOpenedMessage &&
(shouldShowEmailDeliveryFailurePage || shouldShowUnlinkLoginForm || shouldShowChooseSSOOrMagicCode || shouldShowSMSDeliveryFailurePage))
) {
Session.clearSignInData();
return;
Expand Down Expand Up @@ -331,6 +338,7 @@ function SignInPage({credentials, account, activeClients = [], preferredLocale,
{shouldShowUnlinkLoginForm && <UnlinkLoginForm />}
{shouldShowChooseSSOOrMagicCode && <ChooseSSOOrMagicCode setIsUsingMagicCode={setIsUsingMagicCode} />}
{shouldShowEmailDeliveryFailurePage && <EmailDeliveryFailurePage />}
{shouldShowSMSDeliveryFailurePage && <SMSDeliveryFailurePage />}
</>
)}
</SignInPageLayout>
Expand All @@ -339,7 +347,6 @@ function SignInPage({credentials, account, activeClients = [], preferredLocale,
}

type SignInPageProps = SignInPageInnerProps;
type SignInPageOnyxProps = SignInPageInnerOnyxProps;
const SignInPageWithRef = forwardRef(SignInPage);

function SignInPageThemeWrapper(props: SignInPageProps, ref: ForwardedRef<SignInPageRef>) {
Expand All @@ -361,20 +368,6 @@ function SignInPageThemeWrapper(props: SignInPageProps, ref: ForwardedRef<SignIn

SignInPageThemeWrapper.displayName = 'SignInPage';

export default withOnyx<SignInPageProps & RefAttributes<SignInPageRef>, SignInPageOnyxProps>({
account: {key: ONYXKEYS.ACCOUNT},
credentials: {key: ONYXKEYS.CREDENTIALS},
/**
This variable is only added to make sure the component is re-rendered
whenever the activeClients change, so that we call the
ActiveClientManager.isClientTheLeader function
everytime the leader client changes.
We use that function to prevent repeating code that checks which client is the leader.
*/
activeClients: {key: ONYXKEYS.ACTIVE_CLIENTS},
preferredLocale: {
key: ONYXKEYS.NVP_PREFERRED_LOCALE,
},
})(forwardRef(SignInPageThemeWrapper));
export default forwardRef(SignInPageThemeWrapper);

export type {SignInPageRef};
12 changes: 12 additions & 0 deletions src/types/onyx/Account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ type DelegatedAccess = {
errorFields?: DelegateErrors;
};

/** Model of SMS delivery failure status */
type SMSDeliveryFailureStatus = {
/** Whether the account is having trouble receiving SMS */
hasSMSDeliveryFailure: boolean;

/** The message associated with the SMS delivery failure */
message: string;
};

/** Model of user account */
type Account = {
/** Whether SAML is enabled for the current account */
Expand Down Expand Up @@ -145,6 +154,9 @@ type Account = {

/** The users you can access as delegate and the users who can access your account as a delegate */
delegatedAccess?: DelegatedAccess;

/** Indicates SMS delivery failure status and associated information */
SMSDeliveryFailureStatus?: SMSDeliveryFailureStatus;
};

export default Account;
Expand Down

0 comments on commit c62ab78

Please sign in to comment.