Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Wave9] Create onboarding navigator #66

Merged
merged 13 commits into from
Feb 23, 2024
1 change: 1 addition & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,7 @@ const CONST = {
BOTTOM_DOCKED: 'bottom_docked',
POPOVER: 'popover',
RIGHT_DOCKED: 'right_docked',
ONBOARDING: 'onboarding',
},
ANCHOR_ORIGIN_VERTICAL: {
TOP: 'top',
Expand Down
1 change: 1 addition & 0 deletions src/NAVIGATORS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ export default {
BOTTOM_TAB_NAVIGATOR: 'BottomTabNavigator',
LEFT_MODAL_NAVIGATOR: 'LeftModalNavigator',
RIGHT_MODAL_NAVIGATOR: 'RightModalNavigator',
ONBOARDING_MODAL_NAVIGATOR: 'OnboardingModalNavigator',
FULL_SCREEN_NAVIGATOR: 'FullScreenNavigator',
} as const;
2 changes: 2 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,8 @@ const ROUTES = {
getRoute: (contentType: string) => `referral/${contentType}` as const,
},
PROCESS_MONEY_REQUEST_HOLD: 'hold-request-educational',
ONBOARDING_PERSONAL_DETAILS: 'onboarding/personal-details',
ONBOARDING_PURPOSE: 'onboarding/purpose',
} as const;

/**
Expand Down
7 changes: 7 additions & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ const SCREENS = {
REFERRAL: 'Referral',
PROCESS_MONEY_REQUEST_HOLD: 'ProcessMoneyRequestHold',
},
ONBOARDING_MODAL: {
ONBOARDING: 'Onboarding',
},
SIGN_IN_WITH_APPLE_DESKTOP: 'AppleSignInDesktop',
SIGN_IN_WITH_GOOGLE_DESKTOP: 'GoogleSignInDesktop',
DESKTOP_SIGN_IN_REDIRECT: 'DesktopSignInRedirect',
Expand Down Expand Up @@ -228,6 +231,10 @@ const SCREENS = {
EDIT_CURRENCY: 'SplitDetails_Edit_Currency',
},

ONBOARDING: {
PERSONAL_DETAILS: 'Onboarding_Personal_Details',
PURPOSE: 'Onboarding_Purpose',
},
ONBOARD_ENGAGEMENT: {
ROOT: 'Onboard_Engagement_Root',
MANAGE_TEAMS_EXPENSES: 'Manage_Teams_Expenses',
Expand Down
8 changes: 6 additions & 2 deletions src/components/Modal/BaseModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {View} from 'react-native';
import ReactNativeModal from 'react-native-modal';
import ColorSchemeWrapper from '@components/ColorSchemeWrapper';
import useKeyboardState from '@hooks/useKeyboardState';
import useOnboardingLayout from '@hooks/useOnboardingLayout';
import usePrevious from '@hooks/usePrevious';
import useSafeAreaInsets from '@hooks/useSafeAreaInsets';
import useStyleUtils from '@hooks/useStyleUtils';
Expand All @@ -22,6 +23,7 @@ function BaseModal(
isVisible,
onClose,
shouldSetModalVisibility = true,
shouldForceHideBackdrop = false,
onModalHide = () => {},
type,
popoverAnchorPosition = {},
Expand All @@ -48,6 +50,7 @@ function BaseModal(
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const {windowWidth, windowHeight, isSmallScreenWidth} = useWindowDimensions();
const {shouldUseNarrowLayout} = useOnboardingLayout();
const keyboardStateContextValue = useKeyboardState();

const safeAreaInsets = useSafeAreaInsets();
Expand Down Expand Up @@ -147,8 +150,9 @@ function BaseModal(
popoverAnchorPosition,
innerContainerStyle,
outerStyle,
shouldUseNarrowLayout,
),
[StyleUtils, type, windowWidth, windowHeight, isSmallScreenWidth, popoverAnchorPosition, innerContainerStyle, outerStyle],
[StyleUtils, type, windowWidth, windowHeight, isSmallScreenWidth, popoverAnchorPosition, innerContainerStyle, outerStyle, shouldUseNarrowLayout],
);

const {
Expand Down Expand Up @@ -191,7 +195,7 @@ function BaseModal(
swipeDirection={swipeDirection}
isVisible={isVisible}
backdropColor={theme.overlay}
backdropOpacity={!shouldUseCustomBackdrop && hideBackdrop ? 0 : variables.overlayOpacity}
backdropOpacity={(!shouldUseCustomBackdrop && hideBackdrop) || shouldForceHideBackdrop ? 0 : variables.overlayOpacity}
backdropTransitionOutTiming={0}
hasBackdrop={fullscreen}
coverScreen={fullscreen}
Expand Down
3 changes: 3 additions & 0 deletions src/components/Modal/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ type BaseModalProps = Partial<ModalProps> & {
/** Should we announce the Modal visibility changes? */
shouldSetModalVisibility?: boolean;

/** Should we hide backdrop no matter what value is set in modal styles */
shouldForceHideBackdrop?: boolean;

/** Callback method fired when the user requests to close the modal */
onClose: () => void;

Expand Down
16 changes: 16 additions & 0 deletions src/hooks/useOnboardingLayout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// eslint-disable-next-line no-restricted-imports
import {useWindowDimensions} from 'react-native';
import variables from '@styles/variables';

type OnboardingLayout = {
shouldUseNarrowLayout: boolean;
};

/**
* Onboarding layout for medium screen width is narrowed similarly as on web/desktop.
*/
export default function useOnboardingLayout(): OnboardingLayout {
mateuuszzzzz marked this conversation as resolved.
Show resolved Hide resolved
const {width: windowWidth} = useWindowDimensions();

return {shouldUseNarrowLayout: windowWidth > variables.mobileResponsiveWidthBreakpoint};
}
11 changes: 10 additions & 1 deletion src/libs/Navigation/AppNavigator/AuthScreens.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {memo, useEffect, useRef} from 'react';
import React, {memo, useEffect, useMemo, useRef} from 'react';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import Onyx, {withOnyx} from 'react-native-onyx';
Expand Down Expand Up @@ -33,13 +33,15 @@ import ROUTES from '@src/ROUTES';
import SCREENS from '@src/SCREENS';
import type * as OnyxTypes from '@src/types/onyx';
import type {SelectedTimezone, Timezone} from '@src/types/onyx/PersonalDetails';
import useOnboardingLayout from '@hooks/useOnboardingLayout';
import createCustomStackNavigator from './createCustomStackNavigator';
import defaultScreenOptions from './defaultScreenOptions';
import getRootNavigatorScreenOptions from './getRootNavigatorScreenOptions';
import BottomTabNavigator from './Navigators/BottomTabNavigator';
import CentralPaneNavigator from './Navigators/CentralPaneNavigator';
import FullScreenNavigator from './Navigators/FullScreenNavigator';
import LeftModalNavigator from './Navigators/LeftModalNavigator';
import OnboardingModalNavigator from './Navigators/OnboardingModalNavigator';
import RightModalNavigator from './Navigators/RightModalNavigator';

type AuthScreensProps = {
Expand Down Expand Up @@ -153,7 +155,9 @@ function AuthScreens({session, lastOpenedPublicRoomID, isUsingMemoryOnlyKeys = f
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const {isSmallScreenWidth} = useWindowDimensions();
const {shouldUseNarrowLayout} = useOnboardingLayout();
const screenOptions = getRootNavigatorScreenOptions(isSmallScreenWidth, styles, StyleUtils);
const onboardingScreenOptions = useMemo(() => screenOptions.onboardingModalNavigator(shouldUseNarrowLayout), [screenOptions, shouldUseNarrowLayout]);
const isInitialRender = useRef(true);
mateuuszzzzz marked this conversation as resolved.
Show resolved Hide resolved

if (isInitialRender.current) {
Expand Down Expand Up @@ -264,6 +268,11 @@ function AuthScreens({session, lastOpenedPublicRoomID, isUsingMemoryOnlyKeys = f
return (
<View style={styles.rootNavigatorContainerStyles(isSmallScreenWidth)}>
<RootStack.Navigator isSmallScreenWidth={isSmallScreenWidth}>
<RootStack.Screen
name={NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR}
options={onboardingScreenOptions}
component={OnboardingModalNavigator}
/>
<RootStack.Screen
name={NAVIGATORS.BOTTOM_TAB_NAVIGATOR}
options={screenOptions.bottomTab}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {createStackNavigator} from '@react-navigation/stack';
import React, {useMemo} from 'react';
import {View} from 'react-native';
import NoDropZone from '@components/DragAndDrop/NoDropZone';
import useThemeStyles from '@hooks/useThemeStyles';
import OnboardingModalNavigatorScreenOptions from '@libs/Navigation/AppNavigator/OnboardingModalNavigatorScreenOptions';
import type {OnboardingModalNavigatorParamList} from '@libs/Navigation/types';
import OnboardingPersonalDetails from '@pages/OnboardingPersonalDetails';
import OnboardingPurpose from '@pages/OnboardingPurpose';
import SCREENS from '@src/SCREENS';
import useOnboardingLayout from '@hooks/useOnboardingLayout';
import Overlay from './Overlay';

const Stack = createStackNavigator<OnboardingModalNavigatorParamList>();

function OnboardingModalNavigator() {
const styles = useThemeStyles();
const screenOptions = useMemo(() => OnboardingModalNavigatorScreenOptions(styles) ,[styles]);
const {shouldUseNarrowLayout} = useOnboardingLayout();

return (
<NoDropZone>
<Overlay onPress={() => {}}/>
<View style={styles.onboardingNavigatorOuterView}>
<View style={styles.OnboardingNavigatorInnerView(shouldUseNarrowLayout)}>
<Stack.Navigator screenOptions={screenOptions}>
<Stack.Screen
name={SCREENS.ONBOARDING.PERSONAL_DETAILS}
component={OnboardingPersonalDetails}
/>
<Stack.Screen
name={SCREENS.ONBOARDING.PURPOSE}
component={OnboardingPurpose}
/>
</Stack.Navigator>
</View>
</View>
mateuuszzzzz marked this conversation as resolved.
Show resolved Hide resolved
</NoDropZone>
);
}

OnboardingModalNavigator.displayName = 'OnboardingModalNavigator';

export default OnboardingModalNavigator;
18 changes: 15 additions & 3 deletions src/libs/Navigation/AppNavigator/Navigators/Overlay.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import {useCardAnimation} from '@react-navigation/stack';
import React from 'react';
import React, {useMemo} from 'react';
import {Animated, View} from 'react-native';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
import useLocalize from '@hooks/useLocalize';
import useOnboardingLayout from '@hooks/useOnboardingLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import getOperatingSystem from '@libs/getOperatingSystem';
import CONST from '@src/CONST';

type OverlayProps = {
/* Callback to close the modal */
onPress: () => void;
onPress?: () => void;

/* Returns whether a modal is displayed on the left side of the screen. By default, the modal is displayed on the right */
isModalOnTheLeft?: boolean;
Expand All @@ -18,9 +20,19 @@ function Overlay({onPress, isModalOnTheLeft = false}: OverlayProps) {
const styles = useThemeStyles();
const {current} = useCardAnimation();
const {translate} = useLocalize();
const {shouldUseNarrowLayout} = useOnboardingLayout();

// non-native styling uses fixed positioning not supported on native platforms
const shouldUseNativeStyles = useMemo(() => {
const os = getOperatingSystem();
if ((os === CONST.OS.ANDROID || os === CONST.OS.IOS || os === CONST.OS.NATIVE) && shouldUseNarrowLayout) {
return true;
}
return false;
}, [shouldUseNarrowLayout]);

return (
<Animated.View style={styles.overlayStyles(current, isModalOnTheLeft)}>
<Animated.View style={shouldUseNativeStyles ? styles.nativeOverlayStyles(current) : styles.overlayStyles(current, isModalOnTheLeft)}>
<View style={[styles.flex1, styles.flexColumn]}>
{/* In the latest Electron version buttons can't be both clickable and draggable.
That's why we added this workaround. Because of two Pressable components on the desktop app
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type {StackNavigationOptions} from '@react-navigation/stack';
import {CardStyleInterpolators} from '@react-navigation/stack';
import type { ThemeStyles } from '@styles/index';

/**
* Modal stack navigator screen options generator function
* @returns The screen options object
*/
const OnboardingModalNavigatorScreenOptions = (themeStyles: ThemeStyles): StackNavigationOptions => ({
headerShown: false,
animationEnabled: true,
gestureDirection: 'horizontal',
cardStyle: themeStyles.navigationOnboardingScreenCardStyle,
cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS,
presentation: 'transparentModal',
});

export default OnboardingModalNavigatorScreenOptions;
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,24 @@ import variables from '@styles/variables';
type ModalCardStyleInterpolator = (
isSmallScreenWidth: boolean,
isFullScreenModal: boolean,
shouldUseNarrowLayout: boolean,
stackCardInterpolationProps: StackCardInterpolationProps,
outputRangeMultiplier?: number,
) => StackCardInterpolatedStyle;
type CreateModalCardStyleInterpolator = (StyleUtils: StyleUtilsType) => ModalCardStyleInterpolator;

const createModalCardStyleInterpolator: CreateModalCardStyleInterpolator =
(StyleUtils) =>
(isSmallScreenWidth, isFullScreenModal, {current: {progress}, inverted, layouts: {screen}}, outputRangeMultiplier = 1) => {
(isSmallScreenWidth, isFullScreenModal, shouldUseNarrowLayout, {current: {progress}, inverted, layouts: {screen}}, outputRangeMultiplier = 1) => {

if(shouldUseNarrowLayout) {
return {
cardStyle: {
opacity: progress,
},
};
}

const translateX = Animated.multiply(
progress.interpolate({
inputRange: [0, 1],
Expand Down
43 changes: 35 additions & 8 deletions src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,20 @@ import type {ThemeStyles} from '@styles/index';
import type {StyleUtilsType} from '@styles/utils';
import variables from '@styles/variables';
import CONFIG from '@src/CONFIG';
import type { ViewStyle } from 'react-native';
import createModalCardStyleInterpolator from './createModalCardStyleInterpolator';

type ScreenOptions = Record<string, StackNavigationOptions>;
type GetOnboardingModalNavigatorOptions = (shouldUseNarrowLayout: boolean) => StackNavigationOptions;

type ScreenOptions = {
mateuuszzzzz marked this conversation as resolved.
Show resolved Hide resolved
rightModalNavigator: StackNavigationOptions,
onboardingModalNavigator: GetOnboardingModalNavigatorOptions,
leftModalNavigator: StackNavigationOptions,
homeScreen: StackNavigationOptions,
fullScreen: StackNavigationOptions,
centralPaneNavigator: StackNavigationOptions,
bottomTab: StackNavigationOptions,
}

const commonScreenOptions: StackNavigationOptions = {
headerShown: false,
Expand All @@ -20,12 +31,12 @@ const SLIDE_LEFT_OUTPUT_RANGE_MULTIPLIER = -1;
type GetRootNavigatorScreenOptions = (isSmallScreenWidth: boolean, styles: ThemeStyles, StyleUtils: StyleUtilsType) => ScreenOptions;

const getRootNavigatorScreenOptions: GetRootNavigatorScreenOptions = (isSmallScreenWidth, themeStyles, StyleUtils) => {
const modalCardStyleInterpolator = createModalCardStyleInterpolator(StyleUtils);
const modalCardStyleInterpolator = createModalCardStyleInterpolator(StyleUtils);

return {
rightModalNavigator: {
...commonScreenOptions,
cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, false, props),
cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, false, false, props),
presentation: 'transparentModal',

// We want pop in RHP since there are some flows that would work weird otherwise
Expand All @@ -39,9 +50,25 @@ const getRootNavigatorScreenOptions: GetRootNavigatorScreenOptions = (isSmallScr
right: 0,
},
},
onboardingModalNavigator: (shouldUseNarrowLayout: boolean) => ({
cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, false, shouldUseNarrowLayout, props),
headerShown: false,
animationEnabled: true,
cardOverlayEnabled: false,
presentation: 'transparentModal',
cardStyle: {
...StyleUtils.getNavigationModalCardStyle(),
backgroundColor: 'transparent',
width: '100%',
top: 0,
left: 0,
// We need to guarantee that it covers BottomTabBar on web, but fixed position is not supported in react native.
position: 'fixed' as ViewStyle['position'],
},
}),
leftModalNavigator: {
...commonScreenOptions,
cardStyleInterpolator: (props) => modalCardStyleInterpolator(isSmallScreenWidth, false, props, SLIDE_LEFT_OUTPUT_RANGE_MULTIPLIER),
cardStyleInterpolator: (props) => modalCardStyleInterpolator(isSmallScreenWidth, false, false, props, SLIDE_LEFT_OUTPUT_RANGE_MULTIPLIER),
presentation: 'transparentModal',

// We want pop in LHP since there are some flows that would work weird otherwise
Expand All @@ -59,7 +86,7 @@ const getRootNavigatorScreenOptions: GetRootNavigatorScreenOptions = (isSmallScr
homeScreen: {
title: CONFIG.SITE_TITLE,
...commonScreenOptions,
cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, false, props),
cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, false, false, props),

cardStyle: {
...StyleUtils.getNavigationModalCardStyle(),
Expand All @@ -73,7 +100,7 @@ const getRootNavigatorScreenOptions: GetRootNavigatorScreenOptions = (isSmallScr

fullScreen: {
...commonScreenOptions,
cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, true, props),
cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, true, false, props),
cardStyle: {
...StyleUtils.getNavigationModalCardStyle(),

Expand All @@ -86,7 +113,7 @@ const getRootNavigatorScreenOptions: GetRootNavigatorScreenOptions = (isSmallScr
title: CONFIG.SITE_TITLE,
...commonScreenOptions,
animationEnabled: isSmallScreenWidth,
cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, true, props),
cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, true, false, props),

cardStyle: {
...StyleUtils.getNavigationModalCardStyle(),
Expand All @@ -96,7 +123,7 @@ const getRootNavigatorScreenOptions: GetRootNavigatorScreenOptions = (isSmallScr

bottomTab: {
...commonScreenOptions,
cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, false, props),
cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, false, false, props),

cardStyle: {
...StyleUtils.getNavigationModalCardStyle(),
Expand Down
Loading
Loading