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

[TESTING] PlatformStackNavigation #23

Draft
wants to merge 1 commit into
base: perf/native-stack-v2
Choose a base branch
from
Draft
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
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type {RouteProp} from '@react-navigation/native';
import {useRoute} from '@react-navigation/native';
import lodashSortBy from 'lodash/sortBy';
import truncate from 'lodash/truncate';
Expand Down Expand Up @@ -29,6 +28,7 @@ import * as CurrencyUtils from '@libs/CurrencyUtils';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import * as IOUUtils from '@libs/IOUUtils';
import Navigation from '@libs/Navigation/Navigation';
import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types';
import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/types';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as ReceiptUtils from '@libs/ReceiptUtils';
Expand Down Expand Up @@ -70,7 +70,7 @@ function MoneyRequestPreviewContent({
const StyleUtils = useStyleUtils();
const {translate} = useLocalize();
const {windowWidth} = useWindowDimensions();
const route = useRoute<RouteProp<TransactionDuplicateNavigatorParamList, typeof SCREENS.TRANSACTION_DUPLICATE.REVIEW>>();
const route = useRoute<PlatformStackRouteProp<TransactionDuplicateNavigatorParamList, typeof SCREENS.TRANSACTION_DUPLICATE.REVIEW>>();
const {shouldUseNarrowLayout} = useResponsiveLayout();
const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST);
const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID || '-1'}`);
Expand Down
6 changes: 3 additions & 3 deletions src/components/ScreenWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {useIsFocused, useNavigation} from '@react-navigation/native';
import type {StackNavigationProp} from '@react-navigation/stack';
import type {ForwardedRef, ReactNode} from 'react';
import React, {createContext, forwardRef, useEffect, useMemo, useRef, useState} from 'react';
import type {StyleProp, ViewStyle} from 'react-native';
Expand All @@ -15,6 +14,7 @@ import useTackInputFocus from '@hooks/useTackInputFocus';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as Browser from '@libs/Browser';
import type {PlatformStackNavigationProp} from '@libs/Navigation/PlatformStackNavigation/types';
import type {AuthScreensParamList, RootStackParamList} from '@libs/Navigation/types';
import toggleTestToolsModal from '@userActions/TestTool';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -96,7 +96,7 @@ type ScreenWrapperProps = {
*
* This is required because transitionEnd event doesn't trigger in the testing environment.
*/
navigation?: StackNavigationProp<RootStackParamList> | StackNavigationProp<AuthScreensParamList>;
navigation?: PlatformStackNavigationProp<RootStackParamList> | PlatformStackNavigationProp<AuthScreensParamList>;

/** Whether to show offline indicator on wide screens */
shouldShowOfflineIndicatorInWideScreen?: boolean;
Expand Down Expand Up @@ -141,7 +141,7 @@ function ScreenWrapper(
* so in other places where ScreenWrapper is used, we need to
* fallback to useNavigation.
*/
const navigationFallback = useNavigation<StackNavigationProp<RootStackParamList>>();
const navigationFallback = useNavigation<PlatformStackNavigationProp<RootStackParamList>>();
const navigation = navigationProp ?? navigationFallback;
const isFocused = useIsFocused();
const {windowHeight} = useWindowDimensions(shouldUseCachedViewportHeight);
Expand Down
9 changes: 5 additions & 4 deletions src/components/ScrollOffsetContextProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type {ParamListBase, RouteProp} from '@react-navigation/native';
import type {ParamListBase} from '@react-navigation/native';
import React, {createContext, useCallback, useEffect, useMemo, useRef} from 'react';
import {withOnyx} from 'react-native-onyx';
import usePrevious from '@hooks/usePrevious';
import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types';
import type {NavigationPartialRoute, State} from '@libs/Navigation/types';
import NAVIGATORS from '@src/NAVIGATORS';
import ONYXKEYS from '@src/ONYXKEYS';
Expand All @@ -10,10 +11,10 @@ import type {PriorityMode} from '@src/types/onyx';

type ScrollOffsetContextValue = {
/** Save scroll offset of flashlist on given screen */
saveScrollOffset: (route: RouteProp<ParamListBase>, scrollOffset: number) => void;
saveScrollOffset: (route: PlatformStackRouteProp<ParamListBase>, scrollOffset: number) => void;

/** Get scroll offset value for given screen */
getScrollOffset: (route: RouteProp<ParamListBase>) => number | undefined;
getScrollOffset: (route: PlatformStackRouteProp<ParamListBase>) => number | undefined;

/** Clean scroll offsets of screen that aren't anymore in the state */
cleanStaleScrollOffsets: (state: State) => void;
Expand All @@ -38,7 +39,7 @@ const defaultValue: ScrollOffsetContextValue = {
const ScrollOffsetContext = createContext<ScrollOffsetContextValue>(defaultValue);

/** This function is prepared to work with HOME screens. May need modification if we want to handle other types of screens. */
function getKey(route: RouteProp<ParamListBase> | NavigationPartialRoute): string {
function getKey(route: PlatformStackRouteProp<ParamListBase> | NavigationPartialRoute): string {
if (route.params && 'policyID' in route.params && typeof route.params.policyID === 'string') {
return `${route.name}-${route.params.policyID}`;
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/Search/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {useNavigation} from '@react-navigation/native';
import type {StackNavigationProp} from '@react-navigation/stack';
import React, {useCallback, useEffect, useRef, useState} from 'react';
import {View} from 'react-native';
import type {NativeScrollEvent, NativeSyntheticEvent, StyleProp, ViewStyle} from 'react-native';
Expand All @@ -20,6 +19,7 @@ import * as SearchActions from '@libs/actions/Search';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import Log from '@libs/Log';
import memoize from '@libs/memoize';
import type {PlatformStackNavigationProp} from '@libs/Navigation/PlatformStackNavigation/types';
import * as ReportUtils from '@libs/ReportUtils';
import * as SearchUtils from '@libs/SearchUtils';
import Navigation from '@navigation/Navigation';
Expand Down Expand Up @@ -81,7 +81,7 @@ function Search({queryJSON, onSearchListScroll, contentContainerStyle}: SearchPr
const {shouldUseNarrowLayout} = useResponsiveLayout();
const styles = useThemeStyles();
const {isSmallScreenWidth, isLargeScreenWidth} = useResponsiveLayout();
const navigation = useNavigation<StackNavigationProp<AuthScreensParamList>>();
const navigation = useNavigation<PlatformStackNavigationProp<AuthScreensParamList>>();
const lastSearchResultsRef = useRef<OnyxEntry<SearchResults>>();
const {setCurrentSearchHash, setSelectedTransactions, selectedTransactions, clearSelectedTransactions, setShouldShowStatusBarLoading} = useSearchContext();
const {selectionMode} = useMobileSelectionMode();
Expand Down
4 changes: 2 additions & 2 deletions src/components/withNavigationTransitionEnd.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import {useNavigation} from '@react-navigation/native';
import type {StackNavigationProp} from '@react-navigation/stack';
import type {ComponentType, ForwardedRef, RefAttributes} from 'react';
import React, {useEffect, useState} from 'react';
import getComponentDisplayName from '@libs/getComponentDisplayName';
import type {PlatformStackNavigationProp} from '@libs/Navigation/PlatformStackNavigation/types';
import type {RootStackParamList} from '@libs/Navigation/types';

type WithNavigationTransitionEndProps = {didScreenTransitionEnd: boolean};

export default function <TProps, TRef>(WrappedComponent: ComponentType<TProps & RefAttributes<TRef>>): React.ComponentType<TProps & RefAttributes<TRef>> {
function WithNavigationTransitionEnd(props: TProps, ref: ForwardedRef<TRef>) {
const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false);
const navigation = useNavigation<StackNavigationProp<RootStackParamList>>();
const navigation = useNavigation<PlatformStackNavigationProp<RootStackParamList>>();

useEffect(() => {
const unsubscribeTransitionEnd = navigation.addListener('transitionEnd', () => {
Expand Down
12 changes: 5 additions & 7 deletions src/libs/Navigation/AppNavigator/AuthScreens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject';
import type ReactComponentModule from '@src/types/utils/ReactComponentModule';
import beforeRemoveReportOpenedFromSearchRHP from './beforeRemoveReportOpenedFromSearchRHP';
import CENTRAL_PANE_SCREENS from './CENTRAL_PANE_SCREENS';
import createCustomStackNavigator from './createCustomStackNavigator';
import createResponsiveStackNavigator from './createResponsiveStackNavigator';
import defaultScreenOptions from './defaultScreenOptions';
import getRootNavigatorScreenOptions from './getRootNavigatorScreenOptions';
import BottomTabNavigator from './Navigators/BottomTabNavigator';
Expand Down Expand Up @@ -196,7 +196,7 @@ function handleNetworkReconnect() {
}
}

const RootStack = createCustomStackNavigator<AuthScreensParamList>();
const RootStack = createResponsiveStackNavigator<AuthScreensParamList>();
// We want to delay the re-rendering for components(e.g. ReportActionCompose)
// that depends on modal visibility until Modal is completely closed and its focused
// When modal screen is focused, update modal visibility in Onyx
Expand Down Expand Up @@ -227,7 +227,7 @@ const modalScreenListenersWithCancelSearch = {
function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDAppliedToClient}: AuthScreensProps) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const {shouldUseNarrowLayout, onboardingIsMediumOrLargerScreenWidth, isSmallScreenWidth} = useResponsiveLayout();
const {shouldUseNarrowLayout, onboardingIsMediumOrLargerScreenWidth} = useResponsiveLayout();
const screenOptions = getRootNavigatorScreenOptions(shouldUseNarrowLayout, styles, StyleUtils);
const {canUseDefaultRooms} = usePermissions();
const {activeWorkspaceID} = useActiveWorkspace();
Expand Down Expand Up @@ -410,10 +410,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
return (
<ComposeProviders components={[OptionsListContextProvider, SearchContextProvider]}>
<View style={styles.rootNavigatorContainerStyles(shouldUseNarrowLayout)}>
<RootStack.Navigator
screenOptions={screenOptions.centralPaneNavigator}
isSmallScreenWidth={isSmallScreenWidth}
>
<RootStack.Navigator screenOptions={screenOptions.centralPaneNavigator}>
<RootStack.Screen
name={NAVIGATORS.BOTTOM_TAB_NAVIGATOR}
options={screenOptions.bottomTab}
Expand Down Expand Up @@ -462,6 +459,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
options={{
headerShown: false,
presentation: 'transparentModal',
animation: 'none',
}}
getComponent={loadProfileAvatar}
listeners={modalScreenListeners}
Expand Down
29 changes: 21 additions & 8 deletions src/libs/Navigation/AppNavigator/ModalNavigatorScreenOptions.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
import type {StackNavigationOptions} from '@react-navigation/stack';
import {CardStyleInterpolators} from '@react-navigation/stack';
import type {GestureDirection} from '@react-navigation/stack/lib/typescript/src/types';
import type {PlatformStackNavigationOptions} from '@libs/Navigation/PlatformStackNavigation/types';
import type {ThemeStyles} from '@styles/index';

/**
* Modal stack navigator screen options generator function
* @param themeStyles - The styles object
* @returns The screen options object
*/
const ModalNavigatorScreenOptions = (themeStyles: ThemeStyles, gestureDirection: GestureDirection = 'horizontal'): StackNavigationOptions => ({
headerShown: false,
animationEnabled: true,
gestureDirection,
cardStyle: themeStyles.navigationScreenCardStyle,
cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS,
});
const ModalNavigatorScreenOptions = (themeStyles: ThemeStyles, gestureDirection: GestureDirection = 'horizontal'): PlatformStackNavigationOptions => {
let universalGestureDirection: PlatformStackNavigationOptions['gestureDirection'] | undefined;
let webGestureDirection: GestureDirection | undefined;
if (gestureDirection === 'horizontal' || gestureDirection === 'vertical') {
universalGestureDirection = gestureDirection;
} else {
webGestureDirection = gestureDirection;
}

return {
headerShown: false,
animation: 'slide_from_right',
gestureDirection: universalGestureDirection,
web: {
cardStyle: themeStyles.navigationScreenCardStyle,
cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS,
gestureDirection: webGestureDirection,
},
};
};

export default ModalNavigatorScreenOptions;
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type {ParamListBase} from '@react-navigation/routers';
import type {StackNavigationOptions} from '@react-navigation/stack';
import {createStackNavigator} from '@react-navigation/stack';
import React from 'react';
import createPlatformStackNavigator from '@libs/Navigation/PlatformStackNavigation/createPlatformStackNavigator';
import type {
AddPersonalBankAccountNavigatorParamList,
DebugParamList,
Expand Down Expand Up @@ -33,10 +32,10 @@ import type {
TravelNavigatorParamList,
WalletStatementNavigatorParamList,
} from '@navigation/types';
import type {ThemeStyles} from '@styles/index';
import type {Screen} from '@src/SCREENS';
import SCREENS from '@src/SCREENS';
import type ReactComponentModule from '@src/types/utils/ReactComponentModule';
import type {GetModalStackScreenOptions} from './useModalScreenOptions';
import useModalScreenOptions from './useModalScreenOptions';

type Screens = Partial<Record<Screen, () => React.ComponentType>>;
Expand All @@ -47,8 +46,8 @@ type Screens = Partial<Record<Screen, () => React.ComponentType>>;
* @param screens key/value pairs where the key is the name of the screen and the value is a functon that returns the lazy-loaded component
* @param getScreenOptions optional function that returns the screen options, override the default options
*/
function createModalStackNavigator<TStackParams extends ParamListBase>(screens: Screens, getScreenOptions?: (styles: ThemeStyles) => StackNavigationOptions): React.ComponentType {
const ModalStackNavigator = createStackNavigator<TStackParams>();
function createModalStackNavigator<ParamList extends ParamListBase>(screens: Screens, getScreenOptions?: GetModalStackScreenOptions): React.ComponentType {
const ModalStackNavigator = createPlatformStackNavigator<ParamList>();

function ModalStack() {
const screenOptions = useModalScreenOptions(getScreenOptions);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import type {StackCardInterpolationProps, StackNavigationOptions} from '@react-navigation/stack';
import type {StackCardInterpolationProps} from '@react-navigation/stack';
import {CardStyleInterpolators} from '@react-navigation/stack';
import {useMemo} from 'react';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
import {isSafari} from '@libs/Browser';
import hideKeyboardOnSwipe from '@libs/Navigation/AppNavigator/hideKeyboardOnSwipe';
import type {PlatformStackNavigationOptions} from '@libs/Navigation/PlatformStackNavigation/types';
import createModalCardStyleInterpolator from '@navigation/AppNavigator/createModalCardStyleInterpolator';
import type {ThemeStyles} from '@src/styles';

function useModalScreenOptions(getScreenOptions?: (styles: ThemeStyles) => StackNavigationOptions) {
type GetModalStackScreenOptions = (styles: ThemeStyles) => PlatformStackNavigationOptions;

function useModalScreenOptions(getScreenOptions?: GetModalStackScreenOptions) {
const styles = useThemeStyles();
const styleUtils = useStyleUtils();
const {shouldUseNarrowLayout} = useResponsiveLayout();
Expand All @@ -21,15 +25,19 @@ function useModalScreenOptions(getScreenOptions?: (styles: ThemeStyles) => Stack
}

const defaultSubRouteOptions = useMemo(
(): StackNavigationOptions => ({
cardStyle: styles.navigationScreenCardStyle,
(): PlatformStackNavigationOptions => ({
...hideKeyboardOnSwipe,
headerShown: false,
cardStyleInterpolator,
web: {
cardStyle: styles.navigationScreenCardStyle,
cardStyleInterpolator,
},
}),
[styles, cardStyleInterpolator],
[cardStyleInterpolator, styles.navigationScreenCardStyle],
);

return getScreenOptions?.(styles) ?? defaultSubRouteOptions;
}

export default useModalScreenOptions;
export type {GetModalStackScreenOptions};
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {useNavigationState} from '@react-navigation/native';
import type {StackNavigationOptions} from '@react-navigation/stack';
import React from 'react';
import createCustomBottomTabNavigator from '@libs/Navigation/AppNavigator/createCustomBottomTabNavigator';
import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute';
import type {PlatformStackNavigationOptions} from '@libs/Navigation/PlatformStackNavigation/types';
import type {BottomTabNavigatorParamList, CentralPaneName, NavigationPartialRoute, RootStackParamList} from '@libs/Navigation/types';
import SidebarScreen from '@pages/home/sidebar/SidebarScreen';
import SearchPageBottomTab from '@pages/Search/SearchPageBottomTab';
Expand All @@ -13,9 +13,8 @@ import ActiveCentralPaneRouteContext from './ActiveCentralPaneRouteContext';
const loadInitialSettingsPage = () => require<ReactComponentModule>('../../../../pages/settings/InitialSettingsPage').default;
const Tab = createCustomBottomTabNavigator<BottomTabNavigatorParamList>();

const screenOptions: StackNavigationOptions = {
const screenOptions: PlatformStackNavigationOptions = {
headerShown: false,
animationEnabled: false,
};

function BottomTabNavigator() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import {createStackNavigator} from '@react-navigation/stack';
import React from 'react';
import {View} from 'react-native';
import NoDropZone from '@components/DragAndDrop/NoDropZone';
import ExplanationModal from '@components/ExplanationModal';
import createPlatformStackNavigator from '@libs/Navigation/PlatformStackNavigation/createPlatformStackNavigator';
import type {ExplanationModalNavigatorParamList} from '@libs/Navigation/types';
import SCREENS from '@src/SCREENS';

const Stack = createStackNavigator<ExplanationModalNavigatorParamList>();
const Stack = createPlatformStackNavigator<ExplanationModalNavigatorParamList>();

function ExplanationModalNavigator() {
return (
<NoDropZone>
<View>
<Stack.Navigator screenOptions={{headerShown: false, animationEnabled: true}}>
<Stack.Navigator screenOptions={{headerShown: false, animation: 'slide_from_right'}}>
<Stack.Screen
name={SCREENS.EXPLANATION_MODAL.ROOT}
component={ExplanationModal}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import {createStackNavigator} from '@react-navigation/stack';
import React from 'react';
import {View} from 'react-native';
import NoDropZone from '@components/DragAndDrop/NoDropZone';
import createPlatformStackNavigator from '@libs/Navigation/PlatformStackNavigation/createPlatformStackNavigator';
import type {FeatureTrainingNavigatorParamList} from '@libs/Navigation/types';
import TrackTrainingPage from '@pages/TrackTrainingPage';
import SCREENS from '@src/SCREENS';

const Stack = createStackNavigator<FeatureTrainingNavigatorParamList>();
const Stack = createPlatformStackNavigator<FeatureTrainingNavigatorParamList>();

function FeatureTrainingModalNavigator() {
return (
<NoDropZone>
<View>
<Stack.Navigator screenOptions={{headerShown: false, animationEnabled: true}}>
<Stack.Navigator screenOptions={{headerShown: false, animation: 'slide_from_right'}}>
<Stack.Screen
name={SCREENS.FEATURE_TRAINING_ROOT}
component={TrackTrainingPage}
Expand Down
Loading
Loading