Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/ROUTES.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,4 +183,5 @@ export default {
isSubReportPageRoute: Boolean(lodashGet(pathSegments, 2)),
};
},
SIGNINMODAL: 'signinmodal'
};
11 changes: 11 additions & 0 deletions src/libs/Navigation/AppNavigator/ModalStackNavigators.js
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,16 @@ const EditRequestStackNavigator = createModalStackNavigator([
},
]);

const SignInModalStackNavigator = createModalStackNavigator([
{
getComponent: () => {
const SignInModal = require('../../../pages/signin/SignInModal').default;
return SignInModal;
},
name: 'SignIn_Root',
}
]);

export {
MoneyRequestModalStackNavigator,
SplitDetailsModalStackNavigator,
Expand All @@ -755,4 +765,5 @@ export {
YearPickerStackNavigator,
FlagCommentStackNavigator,
EditRequestStackNavigator,
SignInModalStackNavigator,
};
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ function RigthModalNavigator() {
options={defaultModalScreenOptions}
component={ModalStackNavigators.EditRequestStackNavigator}
/>
<Stack.Screen
name="SignIn"
options={defaultModalScreenOptions}
component={ModalStackNavigators.SignInModalStackNavigator}
/>
</Stack.Navigator>
);
}
Expand Down
5 changes: 5 additions & 0 deletions src/libs/Navigation/linkingConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,11 @@ export default {
EditRequest_Root: ROUTES.EDIT_REQUEST,
},
},
SignIn: {
screens: {
SignIn_Root: ROUTES.SIGNINMODAL,
},
},
},
},
},
Expand Down
93 changes: 89 additions & 4 deletions src/libs/actions/Session/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,14 @@ function isAnonymousUser() {
}

function signOutAndRedirectToSignIn() {
hideContextMenu(false);
signOut();
redirectToSignIn();
Log.info('Redirecting to Sign In because signOut() was called');
if (isAnonymousUser()) {
hideContextMenu(false);
if (!isAnonymousUser()) {
signOut();
redirectToSignIn();
}
else {
Navigation.navigate(ROUTES.SIGNINMODAL);
Linking.getInitialURL().then((url) => {
const reportID = ReportUtils.getReportIDFromLink(url);
if (reportID) {
Expand Down Expand Up @@ -875,6 +878,87 @@ function validateTwoFactorAuth(twoFactorAuthCode) {
API.write('TwoFactorAuth_Validate', {twoFactorAuthCode}, {optimisticData, successData, failureData});
}

function signInAnonymousAccount(validateCode, preferredLocale = CONST.LOCALES.DEFAULT) {
const optimisticData = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.ACCOUNT,
value: {
...CONST.DEFAULT_ACCOUNT_DATA,
isLoading: true,
loadingForm: CONST.FORMS.VALIDATE_CODE_FORM,
},
},
{
// We need to clean the authToken so the app is refreshed
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.SESSION,
value: {
authToken: '',
},
},
];

const successData = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.ACCOUNT,
value: {
isLoading: false,
loadingForm: null,
},
},
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.CREDENTIALS,
value: {
validateCode,
},
},
{
// We need to manually set the authTokenType to NOT be anonymous
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.SESSION,
value: {
authTokenType: '',
},
}
];

const failureData = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.ACCOUNT,
value: {
isLoading: false,
loadingForm: null,
},
},
];

const params = {
email: credentials.login,
preferredLocale,
validateCode: validateCode || credentials.validateCode,
};

// TODO: Switch to ClaimAnonymousAccount command, when backend is ready
Device.getDeviceInfoWithID().then((deviceInfo) => {
API.write(
'SigninUser',
{
...params,
deviceInfo,
},
{
optimisticData,
successData,
failureData,
},
);
});
}

export {
beginSignIn,
checkIfActionIsAllowed,
Expand Down Expand Up @@ -902,4 +986,5 @@ export {
isAnonymousUser,
toggleTwoFactorAuth,
validateTwoFactorAuth,
signInAnonymousAccount,
};
1 change: 1 addition & 0 deletions src/libs/actions/SignInRedirect.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import NetworkConnection from '../NetworkConnection';
import HttpUtils from '../HttpUtils';
import navigationRef from '../Navigation/navigationRef';
import SCREENS from '../../SCREENS';
import ROUTES from '../../ROUTES';
import Navigation from '../Navigation/Navigation';
import * as ErrorUtils from '../ErrorUtils';

Expand Down
36 changes: 36 additions & 0 deletions src/pages/signin/SignInModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import SignInPage from './SignInPage';
import ScreenWrapper from '../../components/ScreenWrapper';
import HeaderWithBackButton from '../../components/HeaderWithBackButton';
import Navigation from '../../libs/Navigation/Navigation';

const propTypes = {
};

const defaultProps = {
};

function SignInModal(props) {
return (
<ScreenWrapper
includeSafeAreaPaddingBottom={false}
shouldEnableMaxHeight
onEntryTransitionEnd={() =>{}}
>
<HeaderWithBackButton
title=''//{props.translate('common.description')}
onBackButtonPress={() => Navigation.goBack()}
/>
<SignInPage
isInModal={true}
/>
</ScreenWrapper>

);
}

SignInModal.propTypes = propTypes;
SignInModal.defaultProps = defaultProps;
SignInModal.displayName = 'SignInModal';

export default (SignInModal);
30 changes: 17 additions & 13 deletions src/pages/signin/SignInPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ const propTypes = {
twoFactorAuthCode: PropTypes.string,
}),

isInModal: PropTypes.bool,

...withLocalizePropTypes,

...windowDimensionsPropTypes,
Expand All @@ -56,6 +58,7 @@ const defaultProps = {
account: {},
betas: [],
credentials: {},
isInModal: false,
};

class SignInPage extends Component {
Expand Down Expand Up @@ -110,41 +113,41 @@ class SignInPage extends Component {
(!this.props.account.validated || this.props.account.forgotPassword) &&
!showUnlinkLoginForm &&
!Permissions.canUsePasswordlessLogins(this.props.betas);

let welcomeHeader = '';
let welcomeText = '';
if (showValidateCodeForm) {
if (this.props.account.requiresTwoFactorAuth) {
// We will only know this after a user signs in successfully, without their 2FA code
welcomeHeader = this.props.isSmallScreenWidth ? '' : this.props.translate('welcomeText.welcomeBack');
welcomeHeader = this.props.isSmallScreenWidth || this.props.isInModal ? '' : this.props.translate('welcomeText.welcomeBack');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can make it a new variable with check for this.props.isSmallScreenWidth || this.props.isInModal and use it everywhere here.

welcomeText = this.props.translate('validateCodeForm.enterAuthenticatorCode');
} else {
const userLogin = Str.removeSMSDomain(lodashGet(this.props, 'credentials.login', ''));

// replacing spaces with "hard spaces" to prevent breaking the number
const userLoginToDisplay = Str.isSMSLogin(userLogin) ? this.props.formatPhoneNumber(userLogin).replace(/ /g, '\u00A0') : userLogin;
if (this.props.account.validated) {
welcomeHeader = this.props.isSmallScreenWidth ? '' : this.props.translate('welcomeText.welcomeBack');
welcomeText = this.props.isSmallScreenWidth
welcomeHeader = this.props.isSmallScreenWidth || this.props.isInModal ? '' : this.props.translate('welcomeText.welcomeBack');
welcomeText = this.props.isSmallScreenWidth || this.props.isInModal
? `${this.props.translate('welcomeText.welcomeBack')} ${this.props.translate('welcomeText.welcomeEnterMagicCode', {login: userLoginToDisplay})}`
: this.props.translate('welcomeText.welcomeEnterMagicCode', {login: userLoginToDisplay});
} else {
welcomeHeader = this.props.isSmallScreenWidth ? '' : this.props.translate('welcomeText.welcome');
welcomeText = this.props.isSmallScreenWidth
welcomeHeader = this.props.isSmallScreenWidth || this.props.isInModal ? '' : this.props.translate('welcomeText.welcome');
welcomeText = this.props.isSmallScreenWidth || this.props.isInModal
? `${this.props.translate('welcomeText.welcome')} ${this.props.translate('welcomeText.newFaceEnterMagicCode', {login: userLoginToDisplay})}`
: this.props.translate('welcomeText.newFaceEnterMagicCode', {login: userLoginToDisplay});
}
}
} else if (showPasswordForm) {
welcomeHeader = this.props.isSmallScreenWidth ? '' : this.props.translate('welcomeText.welcomeBack');
welcomeText = this.props.isSmallScreenWidth
welcomeHeader = this.props.isSmallScreenWidth || this.props.isInModal ? '' : this.props.translate('welcomeText.welcomeBack');
welcomeText = this.props.isSmallScreenWidth || this.props.isInModal
? `${this.props.translate('welcomeText.welcomeBack')} ${this.props.translate('welcomeText.enterPassword')}`
: this.props.translate('welcomeText.enterPassword');
} else if (showUnlinkLoginForm) {
welcomeHeader = this.props.isSmallScreenWidth ? this.props.translate('login.hero.header') : this.props.translate('welcomeText.welcomeBack');
welcomeHeader = this.props.isSmallScreenWidth || this.props.isInModal ? this.props.translate('login.hero.header') : this.props.translate('welcomeText.welcomeBack');
} else if (!showResendValidationForm) {
welcomeHeader = this.props.isSmallScreenWidth ? this.props.translate('login.hero.header') : this.props.translate('welcomeText.getStarted');
welcomeText = this.props.isSmallScreenWidth ? this.props.translate('welcomeText.getStarted') : '';
welcomeHeader = this.props.isSmallScreenWidth || this.props.isInModal ? this.props.translate('login.hero.header') : this.props.translate('welcomeText.getStarted');
welcomeText = this.props.isSmallScreenWidth || this.props.isInModal ? this.props.translate('welcomeText.getStarted') : '';
}

return (
Expand All @@ -154,16 +157,17 @@ class SignInPage extends Component {
<SignInPageLayout
welcomeHeader={welcomeHeader}
welcomeText={welcomeText}
shouldShowWelcomeHeader={showLoginForm || showPasswordForm || showValidateCodeForm || showUnlinkLoginForm || !this.props.isSmallScreenWidth}
shouldShowWelcomeHeader={showLoginForm || showPasswordForm || showValidateCodeForm || showUnlinkLoginForm || !this.props.isSmallScreenWidth || !this.props.isInModal}
shouldShowWelcomeText={showLoginForm || showPasswordForm || showValidateCodeForm}
isInModal={this.props.isInModal}
>
{/* LoginForm and PasswordForm must use the isVisible prop. This keeps them mounted, but visually hidden
so that password managers can access the values. Conditionally rendering these components will break this feature. */}
<LoginForm
isVisible={showLoginForm}
blurOnSubmit={this.props.account.validated === false}
/>
{showValidateCodeForm ? <ValidateCodeForm isVisible={showValidateCodeForm} /> : <PasswordForm isVisible={showPasswordForm} />}
{showValidateCodeForm ? <ValidateCodeForm isVisible={showValidateCodeForm} isInModal={this.props.isInModal}/> : <PasswordForm isVisible={showPasswordForm} />}
{showResendValidationForm && <ResendValidationForm />}
{showUnlinkLoginForm && <UnlinkLoginForm />}
</SignInPageLayout>
Expand Down
6 changes: 4 additions & 2 deletions src/pages/signin/SignInPageLayout/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ const propTypes = {
/** Whether to show welcome header on a particular page */
shouldShowWelcomeHeader: PropTypes.bool.isRequired,

isInModal: PropTypes.bool,

...windowDimensionsPropTypes,
};

Expand All @@ -45,7 +47,7 @@ function SignInPageLayout(props) {
// To scroll on both mobile and web, we need to set the container height manually
const containerHeight = props.windowHeight - props.insets.top - props.insets.bottom;

if (props.isSmallScreenWidth) {
if (props.isSmallScreenWidth || props.isInModal) {
containerStyles = [styles.flex1];
contentContainerStyles = [styles.flex1, styles.flexColumn];
}
Expand All @@ -61,7 +63,7 @@ function SignInPageLayout(props) {

return (
<View style={containerStyles}>
{!props.isSmallScreenWidth ? (
{!(props.isSmallScreenWidth || props.isInModal) ? (
<View style={contentContainerStyles}>
<SignInPageContent
welcomeHeader={props.welcomeHeader}
Expand Down
9 changes: 9 additions & 0 deletions src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ import usePrevious from '../../../hooks/usePrevious';
import * as StyleUtils from '../../../styles/StyleUtils';

const propTypes = {
/** Whether the user is anonymous. True when opening the Sign-In Page from the modal */
isInModal: PropTypes.bool,

/* Onyx Props */

/** The details about the account that the user is signing in with */
Expand Down Expand Up @@ -60,6 +63,7 @@ const propTypes = {
};

const defaultProps = {
isInModal: false,
account: {},
credentials: {},
preferredLocale: CONST.LOCALES.DEFAULT,
Expand Down Expand Up @@ -211,6 +215,11 @@ function BaseValidateCodeForm(props) {
}
setFormError({});

if (props.isInModal) {
Session.signInAnonymousAccount(validateCode, props.preferredLocale);
return;
}

const accountID = lodashGet(props.credentials, 'accountID');
if (accountID) {
Session.signInWithValidateCode(accountID, validateCode, props.preferredLocale, twoFactorAuthCode);
Expand Down
3 changes: 3 additions & 0 deletions src/pages/signin/ValidateCodeForm/index.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@ import BaseValidateCodeForm from './BaseValidateCodeForm';

const defaultProps = {
isVisible: false,
isInModal: false,
};

const propTypes = {
isVisible: PropTypes.bool,
isInModal: PropTypes.bool,
};
function ValidateCodeForm(props) {
return (
<BaseValidateCodeForm
isVisible={props.isVisible}
autoComplete="sms-otp"
isInModal={props.isInModal}
/>
);
}
Expand Down
3 changes: 3 additions & 0 deletions src/pages/signin/ValidateCodeForm/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@ import BaseValidateCodeForm from './BaseValidateCodeForm';

const defaultProps = {
isVisible: false,
isInModal: false,
};

const propTypes = {
isVisible: PropTypes.bool,
isInModal: PropTypes.bool,
};
function ValidateCodeForm(props) {
return (
<BaseValidateCodeForm
isVisible={props.isVisible}
autoComplete="one-time-code"
isInModal={props.isInModal}
/>
);
}
Expand Down