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
15 changes: 7 additions & 8 deletions src/pages/signin/SignInModal.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,22 @@
import React, {useEffect, useMemo, useRef} from 'react';
import React, {useEffect, useRef} from 'react';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import {useSession} from '@components/OnyxListItemProvider';
import ScreenWrapper from '@components/ScreenWrapper';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import {openApp} from '@libs/actions/App';
import {isMobileSafari} from '@libs/Browser';
import Navigation from '@libs/Navigation/Navigation';
import {waitForIdle} from '@libs/Network/SequentialQueue';
import CONST from '@src/CONST';
import SCREENS from '@src/SCREENS';
import SignInPageWrapped, {SignInPage} from './SignInPage';
import SignInPage from './SignInPage';
import type {SignInPageRef} from './SignInPage';

function SignInModal() {
const theme = useTheme();
const StyleUtils = useStyleUtils();
const signinPageRef = useRef<SignInPageRef | null>(null);
const session = useSession();
// Use of SignInPageWrapped (with shouldEnableMaxHeight prop in SignInPageWrapper) is a workaround for Safari not supporting interactive-widget=resizes-content.
// This allows better scrolling experience after keyboard shows for modals with input, that are larger than remaining screen height.
// More info https://github.com/Expensify/App/pull/62799#issuecomment-2943136220.
const SignInPageBase = useMemo(() => (isMobileSafari() ? SignInPageWrapped : SignInPage), []);

useEffect(() => {
const isAnonymousUser = session?.authTokenType === CONST.AUTH_TOKEN_TYPES.ANONYMOUS;
Expand All @@ -44,6 +39,7 @@ function SignInModal() {
<ScreenWrapper
style={[StyleUtils.getBackgroundColorStyle(theme.PAGE_THEMES[SCREENS.RIGHT_MODAL.SIGN_IN].backgroundColor)]}
includeSafeAreaPaddingBottom={false}
shouldEnableMaxHeight
shouldShowOfflineIndicator={false}
testID={SignInModal.displayName}
>
Expand All @@ -56,7 +52,10 @@ function SignInModal() {
signinPageRef.current?.navigateBack();
}}
/>
<SignInPageBase ref={signinPageRef} />
<SignInPage
shouldEnableMaxHeight={false}
ref={signinPageRef}
/>
</ScreenWrapper>
);
}
Expand Down
127 changes: 61 additions & 66 deletions src/pages/signin/SignInPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Str} from 'expensify-common';
import type {Ref} from 'react';
import React, {useEffect, useImperativeHandle, useRef, useState} from 'react';
import type {ForwardedRef} from 'react';
import type {OnyxEntry} from 'react-native-onyx';
import ColorSchemeWrapper from '@components/ColorSchemeWrapper';
import CustomStatusBarAndBackground from '@components/CustomStatusBarAndBackground';
Expand Down Expand Up @@ -39,8 +39,9 @@ import UnlinkLoginForm from './UnlinkLoginForm';
import ValidateCodeForm from './ValidateCodeForm';
import type {BaseValidateCodeFormRef} from './ValidateCodeForm/BaseValidateCodeForm';

type SignInPageProps = {
ref?: Ref<SignInPageRef>;
type SignInPageInnerProps = {
shouldEnableMaxHeight?: boolean;
ref?: ForwardedRef<SignInPageRef>;
};

type SignInPageRef = {
Expand Down Expand Up @@ -143,9 +144,12 @@ function getRenderOptions({
};
}

function SignInPage({ref}: SignInPageProps) {
function SignInPage({shouldEnableMaxHeight = true, ref}: SignInPageInnerProps) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const {translate, formatPhoneNumber} = useLocalize();
const {shouldUseNarrowLayout} = useResponsiveLayout();
const {shouldUseNarrowLayout, isInNarrowPaneModal} = useResponsiveLayout();
const safeAreaInsets = useSafeAreaInsets();
const signInPageLayoutRef = useRef<SignInPageLayoutRef>(null);
const loginFormRef = useRef<InputHandle>(null);
const validateCodeFormRef = useRef<BaseValidateCodeFormRef>(null);
Expand Down Expand Up @@ -294,87 +298,78 @@ function SignInPage({ref}: SignInPageProps) {
}));
useHandleBackButton(navigateBack);

return (
<ColorSchemeWrapper>
<CustomStatusBarAndBackground isNested />
<LoginProvider>
<SignInPageLayout
welcomeHeader={welcomeHeader}
welcomeText={welcomeText}
shouldShowWelcomeHeader={shouldShowWelcomeHeader || !shouldUseNarrowLayout}
shouldShowWelcomeText={shouldShowWelcomeText}
ref={signInPageLayoutRef}
navigateFocus={navigateFocus}
>
{/* LoginForm must use the isVisible prop. This keeps it mounted, but visually hidden
so that password managers can access the values. Conditionally rendering this component will break this feature. */}
<LoginForm
ref={loginFormRef}
isVisible={shouldShowLoginForm}
blurOnSubmit={isAccountValidated === false}
// eslint-disable-next-line react-compiler/react-compiler
scrollPageToTop={signInPageLayoutRef.current?.scrollPageToTop}
/>
{shouldShouldSignUpWelcomeForm && <SignUpWelcomeForm />}
{shouldShowValidateCodeForm && (
<ValidateCodeForm
isVisible={!shouldShowAnotherLoginPageOpenedMessage}
isUsingRecoveryCode={isUsingRecoveryCode}
setIsUsingRecoveryCode={setIsUsingRecoveryCode}
ref={validateCodeFormRef}
/>
)}
{!shouldShowAnotherLoginPageOpenedMessage && (
<>
{shouldShowUnlinkLoginForm && <UnlinkLoginForm />}
{shouldShowChooseSSOOrMagicCode && <ChooseSSOOrMagicCode setIsUsingMagicCode={setIsUsingMagicCode} />}
{shouldShowEmailDeliveryFailurePage && <EmailDeliveryFailurePage />}
{shouldShowSMSDeliveryFailurePage && <SMSDeliveryFailurePage />}
</>
)}
</SignInPageLayout>
</LoginProvider>
</ColorSchemeWrapper>
);
}

function SignInPageWrapper({ref}: SignInPageProps) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const safeAreaInsets = useSafeAreaInsets();
const {isInNarrowPaneModal} = useResponsiveLayout();

return (
// Bottom SafeAreaView is removed so that login screen svg displays correctly on mobile.
// The SVG should flow under the Home Indicator on iOS.
<ScreenWrapper
shouldShowOfflineIndicator={false}
shouldEnableMaxHeight
shouldEnableMaxHeight={shouldEnableMaxHeight}
style={[styles.signInPage, StyleUtils.getPlatformSafeAreaPadding({...safeAreaInsets, bottom: 0, top: isInNarrowPaneModal ? 0 : safeAreaInsets.top}, 1)]}
testID={SignInPageWrapper.displayName}
>
<SignInPage ref={ref} />
<SignInPageLayout
welcomeHeader={welcomeHeader}
welcomeText={welcomeText}
shouldShowWelcomeHeader={shouldShowWelcomeHeader || !shouldUseNarrowLayout}
shouldShowWelcomeText={shouldShowWelcomeText}
ref={signInPageLayoutRef}
navigateFocus={navigateFocus}
>
{/* LoginForm must use the isVisible prop. This keeps it mounted, but visually hidden
so that password managers can access the values. Conditionally rendering this component will break this feature. */}
<LoginForm
ref={loginFormRef}
isVisible={shouldShowLoginForm}
blurOnSubmit={isAccountValidated === false}
// eslint-disable-next-line react-compiler/react-compiler
scrollPageToTop={signInPageLayoutRef.current?.scrollPageToTop}
/>
{shouldShouldSignUpWelcomeForm && <SignUpWelcomeForm />}
{shouldShowValidateCodeForm && (
<ValidateCodeForm
isVisible={!shouldShowAnotherLoginPageOpenedMessage}
isUsingRecoveryCode={isUsingRecoveryCode}
setIsUsingRecoveryCode={setIsUsingRecoveryCode}
ref={validateCodeFormRef}
/>
)}
{!shouldShowAnotherLoginPageOpenedMessage && (
<>
{shouldShowUnlinkLoginForm && <UnlinkLoginForm />}
{shouldShowChooseSSOOrMagicCode && <ChooseSSOOrMagicCode setIsUsingMagicCode={setIsUsingMagicCode} />}
{shouldShowEmailDeliveryFailurePage && <EmailDeliveryFailurePage />}
{shouldShowSMSDeliveryFailurePage && <SMSDeliveryFailurePage />}
</>
)}
</SignInPageLayout>
</ScreenWrapper>
);
}

SignInPageWrapper.displayName = 'SignInPageWrapper';
type SignInPageProps = SignInPageInnerProps;
const SignInPageWithRef = SignInPage;

// WithTheme is a HOC that provides theme-related contexts (e.g. to the SignInPageWrapper component since these contexts are required for variable declarations).
function WithTheme(Component: React.ComponentType<SignInPageProps>) {
return ({ref}: SignInPageProps) => (
function SignInPageWrapper({ref, ...props}: SignInPageProps) {
return (
<ThemeProvider theme={CONST.THEME.DARK}>
<ThemeStylesProvider>
<Component ref={ref} />
<ColorSchemeWrapper>
<CustomStatusBarAndBackground isNested />
<LoginProvider>
<SignInPageWithRef
ref={ref}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
/>
</LoginProvider>
</ColorSchemeWrapper>
</ThemeStylesProvider>
</ThemeProvider>
);
}

const SignInPageThemed = WithTheme(SignInPage);

export {SignInPageThemed as SignInPage};
SignInPageWrapper.displayName = 'SignInPage';

export default WithTheme(SignInPageWrapper);
export default SignInPageWrapper;

export type {SignInPageRef};
Loading