Skip to content

Commit c62ab78

Browse files
authored
Merge pull request #52305 from nyomanjyotisa/issue-52086
Create SMS delivery failure sign in flow in homepage
2 parents f222bfe + fca78b0 commit c62ab78

File tree

5 files changed

+118
-39
lines changed

5 files changed

+118
-39
lines changed

src/languages/en.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1824,6 +1824,9 @@ const translations = {
18241824
onceTheAbove: 'Once the above steps are completed, please reach out to ',
18251825
toUnblock: ' to unblock your login.',
18261826
},
1827+
smsDeliveryFailurePage: {
1828+
smsDeliveryFailureMessage: ({login}: OurEmailProviderParams) => `We've been unable to deliver SMS messages to ${login}, so we've suspended it for 24 hours.`,
1829+
},
18271830
welcomeSignUpForm: {
18281831
join: 'Join',
18291832
},

src/languages/es.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1829,6 +1829,9 @@ const translations = {
18291829
onceTheAbove: 'Una vez completados los pasos anteriores, ponte en contacto con ',
18301830
toUnblock: ' para desbloquear el inicio de sesión.',
18311831
},
1832+
smsDeliveryFailurePage: {
1833+
smsDeliveryFailureMessage: ({login}: OurEmailProviderParams) => `No hemos podido entregar mensajes SMS a ${login}, por lo que lo hemos suspendido durante 24 horas.`,
1834+
},
18321835
welcomeSignUpForm: {
18331836
join: 'Unirse',
18341837
},
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import {Str} from 'expensify-common';
2+
import React, {useEffect, useMemo} from 'react';
3+
import {Keyboard, View} from 'react-native';
4+
import {useOnyx} from 'react-native-onyx';
5+
import Button from '@components/Button';
6+
import Text from '@components/Text';
7+
import useKeyboardState from '@hooks/useKeyboardState';
8+
import useLocalize from '@hooks/useLocalize';
9+
import useThemeStyles from '@hooks/useThemeStyles';
10+
import * as Session from '@userActions/Session';
11+
import ONYXKEYS from '@src/ONYXKEYS';
12+
import ChangeExpensifyLoginLink from './ChangeExpensifyLoginLink';
13+
import Terms from './Terms';
14+
15+
function SMSDeliveryFailurePage() {
16+
const styles = useThemeStyles();
17+
const {isKeyboardShown} = useKeyboardState();
18+
const {translate} = useLocalize();
19+
const [credentials] = useOnyx(ONYXKEYS.CREDENTIALS);
20+
const [account] = useOnyx(ONYXKEYS.ACCOUNT);
21+
22+
const login = useMemo(() => {
23+
if (!credentials?.login) {
24+
return '';
25+
}
26+
return Str.isSMSLogin(credentials.login) ? Str.removeSMSDomain(credentials.login) : credentials.login;
27+
}, [credentials?.login]);
28+
29+
const SMSDeliveryFailureMessage = account?.SMSDeliveryFailureStatus?.message;
30+
31+
useEffect(() => {
32+
if (!isKeyboardShown) {
33+
return;
34+
}
35+
Keyboard.dismiss();
36+
}, [isKeyboardShown]);
37+
38+
return (
39+
<>
40+
<View style={[styles.mv3, styles.flexRow]}>
41+
<View style={[styles.flex1]}>
42+
<Text>
43+
{translate('smsDeliveryFailurePage.smsDeliveryFailureMessage', {login})} {SMSDeliveryFailureMessage}
44+
</Text>
45+
</View>
46+
</View>
47+
<View style={[styles.mv4, styles.flexRow, styles.justifyContentBetween, styles.alignItemsEnd]}>
48+
<Button
49+
success
50+
medium
51+
text={translate('common.buttonConfirm')}
52+
onPress={() => Session.clearSignInData()}
53+
pressOnEnter
54+
/>
55+
</View>
56+
<View style={[styles.mt3, styles.mb2]}>
57+
<ChangeExpensifyLoginLink onPress={() => Session.clearSignInData()} />
58+
</View>
59+
<View style={[styles.mt4, styles.signInPageWelcomeTextContainer]}>
60+
<Terms />
61+
</View>
62+
</>
63+
);
64+
}
65+
66+
SMSDeliveryFailurePage.displayName = 'SMSDeliveryFailurePage';
67+
68+
export default SMSDeliveryFailurePage;

src/pages/signin/SignInPage.tsx

Lines changed: 32 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import {Str} from 'expensify-common';
22
import React, {forwardRef, useEffect, useImperativeHandle, useRef, useState} from 'react';
3-
import type {ForwardedRef, RefAttributes} from 'react';
4-
import {withOnyx} from 'react-native-onyx';
3+
import type {ForwardedRef} from 'react';
54
import type {OnyxEntry} from 'react-native-onyx';
5+
import {useOnyx} from 'react-native-onyx';
66
import ColorSchemeWrapper from '@components/ColorSchemeWrapper';
77
import CustomStatusBarAndBackground from '@components/CustomStatusBarAndBackground';
88
import ScreenWrapper from '@components/ScreenWrapper';
@@ -24,7 +24,7 @@ import * as Session from '@userActions/Session';
2424
import CONST from '@src/CONST';
2525
import ONYXKEYS from '@src/ONYXKEYS';
2626
import ROUTES from '@src/ROUTES';
27-
import type {Account, Credentials, Locale} from '@src/types/onyx';
27+
import type {Account, Credentials} from '@src/types/onyx';
2828
import {isEmptyObject} from '@src/types/utils/EmptyObject';
2929
import ChooseSSOOrMagicCode from './ChooseSSOOrMagicCode';
3030
import EmailDeliveryFailurePage from './EmailDeliveryFailurePage';
@@ -33,25 +33,12 @@ import type {InputHandle} from './LoginForm/types';
3333
import SignInPageLayout from './SignInPageLayout';
3434
import type {SignInPageLayoutRef} from './SignInPageLayout/types';
3535
import SignUpWelcomeForm from './SignUpWelcomeForm';
36+
import SMSDeliveryFailurePage from './SMSDeliveryFailurePage';
3637
import UnlinkLoginForm from './UnlinkLoginForm';
3738
import ValidateCodeForm from './ValidateCodeForm';
3839
import type {BaseValidateCodeFormRef} from './ValidateCodeForm/BaseValidateCodeForm';
3940

40-
type SignInPageInnerOnyxProps = {
41-
/** The details about the account that the user is signing in with */
42-
account: OnyxEntry<Account>;
43-
44-
/** The credentials of the person signing in */
45-
credentials: OnyxEntry<Credentials>;
46-
47-
/** Active Clients connected to ONYX Database */
48-
activeClients: OnyxEntry<string[]>;
49-
50-
/** The user's preferred locale */
51-
preferredLocale: OnyxEntry<Locale>;
52-
};
53-
54-
type SignInPageInnerProps = SignInPageInnerOnyxProps & {
41+
type SignInPageInnerProps = {
5542
shouldEnableMaxHeight?: boolean;
5643
};
5744

@@ -62,6 +49,7 @@ type SignInPageRef = {
6249
type RenderOption = {
6350
shouldShowLoginForm: boolean;
6451
shouldShowEmailDeliveryFailurePage: boolean;
52+
shouldShowSMSDeliveryFailurePage: boolean;
6553
shouldShowUnlinkLoginForm: boolean;
6654
shouldShowValidateCodeForm: boolean;
6755
shouldShowChooseSSOOrMagicCode: boolean;
@@ -90,6 +78,7 @@ type GetRenderOptionsParams = {
9078
* @param isUsingMagicCode
9179
* @param hasInitiatedSAMLLogin
9280
* @param hasEmailDeliveryFailure
81+
* @param hasSMSDeliveryFailure
9382
*/
9483
function getRenderOptions({
9584
hasLogin,
@@ -105,6 +94,7 @@ function getRenderOptions({
10594
const isSAMLEnabled = !!account?.isSAMLEnabled;
10695
const isSAMLRequired = !!account?.isSAMLRequired;
10796
const hasEmailDeliveryFailure = !!account?.hasEmailDeliveryFailure;
97+
const hasSMSDeliveryFailure = !!account?.SMSDeliveryFailureStatus?.hasSMSDeliveryFailure;
10898

10999
// True, if the user has SAML required, and we haven't yet initiated SAML for their account
110100
const shouldInitiateSAMLLogin = hasAccount && hasLogin && isSAMLRequired && !hasInitiatedSAMLLogin && !!account.isLoading;
@@ -121,13 +111,15 @@ function getRenderOptions({
121111
const shouldShouldSignUpWelcomeForm = !!credentials?.login && !account?.validated && !account?.accountExists && !account?.domainControlled;
122112
const shouldShowLoginForm = !shouldShowAnotherLoginPageOpenedMessage && !hasLogin && !hasValidateCode;
123113
const shouldShowEmailDeliveryFailurePage = hasLogin && hasEmailDeliveryFailure && !shouldShowChooseSSOOrMagicCode && !shouldInitiateSAMLLogin;
124-
const isUnvalidatedSecondaryLogin = hasLogin && !isPrimaryLogin && !account?.validated && !hasEmailDeliveryFailure;
114+
const shouldShowSMSDeliveryFailurePage = hasLogin && hasSMSDeliveryFailure && !shouldShowChooseSSOOrMagicCode && !shouldInitiateSAMLLogin;
115+
const isUnvalidatedSecondaryLogin = hasLogin && !isPrimaryLogin && !account?.validated && !hasEmailDeliveryFailure && !hasSMSDeliveryFailure;
125116
const shouldShowValidateCodeForm =
126117
!shouldShouldSignUpWelcomeForm &&
127118
hasAccount &&
128119
(hasLogin || hasValidateCode) &&
129120
!isUnvalidatedSecondaryLogin &&
130121
!hasEmailDeliveryFailure &&
122+
!hasSMSDeliveryFailure &&
131123
!shouldShowChooseSSOOrMagicCode &&
132124
!isSAMLRequired;
133125
const shouldShowWelcomeHeader = shouldShowLoginForm || shouldShowValidateCodeForm || shouldShowChooseSSOOrMagicCode || isUnvalidatedSecondaryLogin || shouldShouldSignUpWelcomeForm;
@@ -137,6 +129,7 @@ function getRenderOptions({
137129
return {
138130
shouldShowLoginForm,
139131
shouldShowEmailDeliveryFailurePage,
132+
shouldShowSMSDeliveryFailurePage,
140133
shouldShowUnlinkLoginForm: !shouldShouldSignUpWelcomeForm && isUnvalidatedSecondaryLogin,
141134
shouldShowValidateCodeForm,
142135
shouldShowChooseSSOOrMagicCode,
@@ -147,7 +140,7 @@ function getRenderOptions({
147140
};
148141
}
149142

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

153+
const [account] = useOnyx(ONYXKEYS.ACCOUNT);
154+
const [credentials] = useOnyx(ONYXKEYS.CREDENTIALS);
155+
/**
156+
This variable is only added to make sure the component is re-rendered
157+
whenever the activeClients change, so that we call the
158+
ActiveClientManager.isClientTheLeader function
159+
everytime the leader client changes.
160+
We use that function to prevent repeating code that checks which client is the leader.
161+
*/
162+
const [activeClients = []] = useOnyx(ONYXKEYS.ACTIVE_CLIENTS);
163+
const [preferredLocale] = useOnyx(ONYXKEYS.NVP_PREFERRED_LOCALE);
164+
160165
/** This state is needed to keep track of if user is using recovery code instead of 2fa code,
161166
* and we need it here since welcome text(`welcomeText`) also depends on it */
162167
const [isUsingRecoveryCode, setIsUsingRecoveryCode] = useState(false);
@@ -200,6 +205,7 @@ function SignInPage({credentials, account, activeClients = [], preferredLocale,
200205
const {
201206
shouldShowLoginForm,
202207
shouldShowEmailDeliveryFailurePage,
208+
shouldShowSMSDeliveryFailurePage,
203209
shouldShowUnlinkLoginForm,
204210
shouldShowValidateCodeForm,
205211
shouldShowChooseSSOOrMagicCode,
@@ -249,11 +255,11 @@ function SignInPage({credentials, account, activeClients = [], preferredLocale,
249255
? `${translate('welcomeText.welcome')} ${translate('welcomeText.welcomeEnterMagicCode', {login: userLoginToDisplay})}`
250256
: translate('welcomeText.welcomeEnterMagicCode', {login: userLoginToDisplay});
251257
}
252-
} else if (shouldShowUnlinkLoginForm || shouldShowEmailDeliveryFailurePage || shouldShowChooseSSOOrMagicCode) {
258+
} else if (shouldShowUnlinkLoginForm || shouldShowEmailDeliveryFailurePage || shouldShowChooseSSOOrMagicCode || shouldShowSMSDeliveryFailurePage) {
253259
welcomeHeader = shouldUseNarrowLayout ? headerText : translate('welcomeText.welcome');
254260

255261
// Don't show any welcome text if we're showing the user the email delivery failed view
256-
if (shouldShowEmailDeliveryFailurePage || shouldShowChooseSSOOrMagicCode) {
262+
if (shouldShowEmailDeliveryFailurePage || shouldShowChooseSSOOrMagicCode || shouldShowSMSDeliveryFailurePage) {
257263
welcomeText = '';
258264
}
259265
} else if (shouldShouldSignUpWelcomeForm) {
@@ -273,7 +279,8 @@ function SignInPage({credentials, account, activeClients = [], preferredLocale,
273279
const navigateBack = () => {
274280
if (
275281
shouldShouldSignUpWelcomeForm ||
276-
(!shouldShowAnotherLoginPageOpenedMessage && (shouldShowEmailDeliveryFailurePage || shouldShowUnlinkLoginForm || shouldShowChooseSSOOrMagicCode))
282+
(!shouldShowAnotherLoginPageOpenedMessage &&
283+
(shouldShowEmailDeliveryFailurePage || shouldShowUnlinkLoginForm || shouldShowChooseSSOOrMagicCode || shouldShowSMSDeliveryFailurePage))
277284
) {
278285
Session.clearSignInData();
279286
return;
@@ -331,6 +338,7 @@ function SignInPage({credentials, account, activeClients = [], preferredLocale,
331338
{shouldShowUnlinkLoginForm && <UnlinkLoginForm />}
332339
{shouldShowChooseSSOOrMagicCode && <ChooseSSOOrMagicCode setIsUsingMagicCode={setIsUsingMagicCode} />}
333340
{shouldShowEmailDeliveryFailurePage && <EmailDeliveryFailurePage />}
341+
{shouldShowSMSDeliveryFailurePage && <SMSDeliveryFailurePage />}
334342
</>
335343
)}
336344
</SignInPageLayout>
@@ -339,7 +347,6 @@ function SignInPage({credentials, account, activeClients = [], preferredLocale,
339347
}
340348

341349
type SignInPageProps = SignInPageInnerProps;
342-
type SignInPageOnyxProps = SignInPageInnerOnyxProps;
343350
const SignInPageWithRef = forwardRef(SignInPage);
344351

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

362369
SignInPageThemeWrapper.displayName = 'SignInPage';
363370

364-
export default withOnyx<SignInPageProps & RefAttributes<SignInPageRef>, SignInPageOnyxProps>({
365-
account: {key: ONYXKEYS.ACCOUNT},
366-
credentials: {key: ONYXKEYS.CREDENTIALS},
367-
/**
368-
This variable is only added to make sure the component is re-rendered
369-
whenever the activeClients change, so that we call the
370-
ActiveClientManager.isClientTheLeader function
371-
everytime the leader client changes.
372-
We use that function to prevent repeating code that checks which client is the leader.
373-
*/
374-
activeClients: {key: ONYXKEYS.ACTIVE_CLIENTS},
375-
preferredLocale: {
376-
key: ONYXKEYS.NVP_PREFERRED_LOCALE,
377-
},
378-
})(forwardRef(SignInPageThemeWrapper));
371+
export default forwardRef(SignInPageThemeWrapper);
379372

380373
export type {SignInPageRef};

src/types/onyx/Account.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ type DelegatedAccess = {
6060
errorFields?: DelegateErrors;
6161
};
6262

63+
/** Model of SMS delivery failure status */
64+
type SMSDeliveryFailureStatus = {
65+
/** Whether the account is having trouble receiving SMS */
66+
hasSMSDeliveryFailure: boolean;
67+
68+
/** The message associated with the SMS delivery failure */
69+
message: string;
70+
};
71+
6372
/** Model of user account */
6473
type Account = {
6574
/** Whether SAML is enabled for the current account */
@@ -145,6 +154,9 @@ type Account = {
145154

146155
/** The users you can access as delegate and the users who can access your account as a delegate */
147156
delegatedAccess?: DelegatedAccess;
157+
158+
/** Indicates SMS delivery failure status and associated information */
159+
SMSDeliveryFailureStatus?: SMSDeliveryFailureStatus;
148160
};
149161

150162
export default Account;

0 commit comments

Comments
 (0)