Skip to content

Commit

Permalink
Merge pull request #66 from software-mansion-labs/wave9/create-onboar…
Browse files Browse the repository at this point in the history
…ding-navigator

[Wave9] Create onboarding navigator
  • Loading branch information
MaciejSWM authored Feb 23, 2024
2 parents 48de123 + 9cf6a20 commit 48b37cf
Show file tree
Hide file tree
Showing 18 changed files with 322 additions and 18 deletions.
1 change: 1 addition & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,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 @@ -513,6 +513,8 @@ const ROUTES = {
getRoute: (contentType: string, backTo?: string) => getUrlWithBackToParam(`referral/${contentType}`, backTo),
},
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 @@ -118,6 +118,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 @@ -231,6 +234,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
18 changes: 16 additions & 2 deletions src/components/HeaderWithBackButton/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useMemo } from 'react';
import React, {useMemo} from 'react';
import {Keyboard, StyleSheet, View} from 'react-native';
import AvatarWithDisplayName from '@components/AvatarWithDisplayName';
import Header from '@components/Header';
Expand Down Expand Up @@ -99,7 +99,21 @@ function HeaderWithBackButton({
textStyles={titleColor ? [StyleUtils.getTextColorStyle(titleColor)] : []}
/>
);
}, [StyleUtils, policy, progressBarPercentage, report, shouldEnableDetailPageNavigation, shouldShowAvatarWithDisplay, stepCounter, styles.progressBar, styles.progressBarWrapper, subtitle, title, titleColor, translate]);
}, [
StyleUtils,
policy,
progressBarPercentage,
report,
shouldEnableDetailPageNavigation,
shouldShowAvatarWithDisplay,
stepCounter,
styles.progressBar,
styles.progressBarWrapper,
subtitle,
title,
titleColor,
translate,
]);

return (
<View
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 {
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,7 +1,8 @@
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';
import useOnboardingLayout from '@hooks/useOnboardingLayout';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
Expand Down Expand Up @@ -40,6 +41,7 @@ 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);

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 useOnboardingLayout from '@hooks/useOnboardingLayout';
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 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>
</NoDropZone>
);
}

OnboardingModalNavigator.displayName = 'OnboardingModalNavigator';

export default OnboardingModalNavigator;
18 changes: 15 additions & 3 deletions src/libs/Navigation/AppNavigator/Navigators/Overlay/index.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,23 @@ 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
41 changes: 34 additions & 7 deletions src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import type {StackCardInterpolationProps, StackNavigationOptions} from '@react-navigation/stack';
import type {ViewStyle} from 'react-native';
import type {ThemeStyles} from '@styles/index';
import type {StyleUtilsType} from '@styles/utils';
import variables from '@styles/variables';
import CONFIG from '@src/CONFIG';
import createModalCardStyleInterpolator from './createModalCardStyleInterpolator';
import getRightModalNavigatorOptions from './getRightModalNavigatorOptions';

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

type ScreenOptions = {
rightModalNavigator: StackNavigationOptions;
onboardingModalNavigator: GetOnboardingModalNavigatorOptions;
leftModalNavigator: StackNavigationOptions;
homeScreen: StackNavigationOptions;
fullScreen: StackNavigationOptions;
centralPaneNavigator: StackNavigationOptions;
bottomTab: StackNavigationOptions;
};

const commonScreenOptions: StackNavigationOptions = {
headerShown: false,
Expand All @@ -27,11 +38,27 @@ const getRootNavigatorScreenOptions: GetRootNavigatorScreenOptions = (isSmallScr
rightModalNavigator: {
...commonScreenOptions,
...getRightModalNavigatorOptions(isSmallScreenWidth),
cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, false, props),
cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, false, false, props),
},
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: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, false, props, SLIDE_LEFT_OUTPUT_RANGE_MULTIPLIER),
cardStyleInterpolator: (props: StackCardInterpolationProps) => 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 @@ -50,7 +77,7 @@ const getRootNavigatorScreenOptions: GetRootNavigatorScreenOptions = (isSmallScr
title: CONFIG.SITE_TITLE,
...commonScreenOptions,
// Note: The card* properties won't be applied on mobile platforms, as they use the native defaults.
cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, false, props),
cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, false, false, props),
cardStyle: {
...StyleUtils.getNavigationModalCardStyle(),
width: isSmallScreenWidth ? '100%' : variables.sideBarWidth,
Expand All @@ -64,7 +91,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 @@ -77,7 +104,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),
// temporary solution - better to hide a keyboard than see keyboard flickering
// see https://github.com/software-mansion/react-native-screens/issues/2021 for more details
keyboardHandlingEnabled: true,
Expand All @@ -89,7 +116,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
12 changes: 12 additions & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,18 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
},
},
},
[NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR]: {
screens: {
[SCREENS.ONBOARDING.PERSONAL_DETAILS]: {
path: ROUTES.ONBOARDING_PERSONAL_DETAILS,
exact: true,
},
[SCREENS.ONBOARDING.PURPOSE]: {
path: ROUTES.ONBOARDING_PURPOSE,
exact: true,
},
},
},
[NAVIGATORS.RIGHT_MODAL_NAVIGATOR]: {
screens: {
[SCREENS.RIGHT_MODAL.SETTINGS]: {
Expand Down
Loading

0 comments on commit 48b37cf

Please sign in to comment.