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

Poc/freeze perf #134

Merged
merged 3 commits into from
Nov 8, 2024
Merged
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
Expand Up @@ -43,7 +43,7 @@ index 7558eb3..b7bb75e 100644
}) : STATE_TRANSITIONING_OR_BELOW_TOP;
}
+
+ const isHomeScreenAndNotOnTop = (route.name === 'BottomTabNavigator' || route.name === 'Workspace_Initial' || route.name === 'Home' || route.name === 'Search_Bottom_Tab' || route.name === 'Settings_Root') && isScreenActive !== STATE_ON_TOP;
+ const isHomeScreenAndNotOnTop = (route.name === 'BottomTabNavigator' || route.name === 'Workspace_Initial' || route.name === 'Home' || route.name === 'Search_Bottom_Tab' || route.name === 'Settings_Root' || route.name === 'ReportsSplitNavigator' || route.name === 'Search_Central_Pane') && isScreenActive !== STATE_ON_TOP;
+
const {
headerShown = true,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type {NavigationState} from '@react-navigation/native';
import {isFullScreenName} from '@libs/NavigationUtils';

function getIsScreenBlurred(state: NavigationState, currentRouteKey: string) {
const lastFullScreenRoute = state.routes.findLast((route) => isFullScreenName(route.name));
return lastFullScreenRoute?.key !== currentRouteKey;
}

export default getIsScreenBlurred;
28 changes: 28 additions & 0 deletions src/libs/Navigation/AppNavigator/FreezeWrapper/index.native.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {useNavigation, useRoute} from '@react-navigation/native';
import React, {useEffect, useState} from 'react';
import {Freeze} from 'react-freeze';
import type ChildrenProps from '@src/types/utils/ChildrenProps';
import getIsScreenBlurred from './getIsScreenBlurred';

type FreezeWrapperProps = ChildrenProps & {
/** Prop to disable freeze */
keepVisible?: boolean;
};

function FreezeWrapper({keepVisible = false, children}: FreezeWrapperProps) {
const navigation = useNavigation();
const currentRoute = useRoute();

const [isScreenBlurred, setIsScreenBlurred] = useState(false);

useEffect(() => {
const unsubscribe = navigation.addListener('state', (e) => setIsScreenBlurred(getIsScreenBlurred(e.data.state, currentRoute.key)));
return () => unsubscribe();
}, [currentRoute.key, navigation]);

return <Freeze freeze={isScreenBlurred && !keepVisible}>{children}</Freeze>;
}

FreezeWrapper.displayName = 'FreezeWrapper';

export default FreezeWrapper;
35 changes: 35 additions & 0 deletions src/libs/Navigation/AppNavigator/FreezeWrapper/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {useNavigation, useRoute} from '@react-navigation/native';
import React, {useEffect, useLayoutEffect, useState} from 'react';
import {Freeze} from 'react-freeze';
import type ChildrenProps from '@src/types/utils/ChildrenProps';
import getIsScreenBlurred from './getIsScreenBlurred';

type FreezeWrapperProps = ChildrenProps & {
/** Prop to disable freeze */
keepVisible?: boolean;
};

function FreezeWrapper({keepVisible = false, children}: FreezeWrapperProps) {
const navigation = useNavigation();
const currentRoute = useRoute();

const [isScreenBlurred, setIsScreenBlurred] = useState(false);
const [freezed, setFreezed] = useState(false);

useEffect(() => {
const unsubscribe = navigation.addListener('state', (e) => setIsScreenBlurred(getIsScreenBlurred(e.data.state, currentRoute.key)));
return () => unsubscribe();
}, [currentRoute.key, navigation]);

// Decouple the Suspense render task so it won't be interrupted by React's concurrent mode
// and stuck in an infinite loop
useLayoutEffect(() => {
setFreezed(isScreenBlurred && !keepVisible);
}, [isScreenBlurred, keepVisible]);

return <Freeze freeze={freezed}>{children}</Freeze>;
}

FreezeWrapper.displayName = 'FreezeWrapper';

export default FreezeWrapper;
Original file line number Diff line number Diff line change
@@ -1,42 +1,19 @@
import {useNavigationState, useRoute} from '@react-navigation/native';
import React, {useRef} from 'react';
import {Freeze} from 'react-freeze';
import FocusTrapForScreens from '@components/FocusTrap/FocusTrapForScreen';
import useActiveWorkspace from '@hooks/useActiveWorkspace';
import usePermissions from '@hooks/usePermissions';
import memoize from '@libs/memoize';
import createSplitStackNavigator from '@libs/Navigation/AppNavigator/createSplitStackNavigator';
import FreezeWrapper from '@libs/Navigation/AppNavigator/FreezeWrapper';
import getCurrentUrl from '@libs/Navigation/currentUrl';
import shouldOpenOnAdminRoom from '@libs/Navigation/shouldOpenOnAdminRoom';
import type {ReportsSplitNavigatorParamList} from '@libs/Navigation/types';
import {isFullScreenName} from '@libs/NavigationUtils';
import * as ReportUtils from '@libs/ReportUtils';
import CONST from '@src/CONST';
import SCREENS from '@src/SCREENS';
import type ReactComponentModule from '@src/types/utils/ReactComponentModule';

function FrozenScreen<TProps extends React.JSX.IntrinsicAttributes>(WrappedComponent: React.ComponentType<TProps>, freeze: boolean) {
return (props: TProps) => (
<Freeze freeze={freeze}>
<WrappedComponent
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
/>
</Freeze>
);
}

function freezeScreenWithLazyLoading(lazyComponent: () => React.ComponentType, freeze: boolean) {
return memoize(
() => {
const Component = lazyComponent();
return FrozenScreen(Component, freeze);
},
{monitoringName: 'freezeScreenWithLazyLoading'},
);
}

const loadReportScreen = () => require<ReactComponentModule>('../../../../pages/home/ReportScreen').default;
const loadSidebarScreen = () => require<ReactComponentModule>('@pages/home/sidebar/SidebarScreen').default;

const Stack = createSplitStackNavigator<ReportsSplitNavigatorParamList>();

Expand All @@ -47,8 +24,8 @@ function ReportsSplitNavigator() {
let initialReportID: string | undefined;
const isInitialRender = useRef(true);

const currentRoute = useRoute();

// TODO: Figure out if compiler affects this code.
// eslint-disable-next-line react-compiler/react-compiler
if (isInitialRender.current) {
const currentURL = getCurrentUrl();
if (currentURL) {
Expand All @@ -60,26 +37,20 @@ function ReportsSplitNavigator() {
initialReportID = initialReport?.reportID ?? '';
}

// eslint-disable-next-line react-compiler/react-compiler
isInitialRender.current = false;
}

const shouldFreeze = useNavigationState((state) => {
const lastFullScreenRoute = state.routes.findLast((route) => isFullScreenName(route.name));
return lastFullScreenRoute?.key !== currentRoute.key;
});

const getSidebarScreen = freezeScreenWithLazyLoading(() => require<ReactComponentModule>('@pages/home/sidebar/SidebarScreen').default, shouldFreeze);

return (
<Freeze freeze={shouldFreeze}>
<FreezeWrapper>
<FocusTrapForScreens>
<Stack.Navigator
sidebarScreen={SCREENS.HOME}
defaultCentralScreen={SCREENS.REPORT}
>
<Stack.Screen
name={SCREENS.HOME}
getComponent={getSidebarScreen}
getComponent={loadSidebarScreen}
/>
<Stack.Screen
name={SCREENS.REPORT}
Expand All @@ -88,7 +59,7 @@ function ReportsSplitNavigator() {
/>
</Stack.Navigator>
</FocusTrapForScreens>
</Freeze>
</FreezeWrapper>
);
}

Expand Down
93 changes: 48 additions & 45 deletions src/pages/Search/SearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode';
import BottomTabBar from '@libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar';
import TopBar from '@libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar';
import FreezeWrapper from '@libs/Navigation/AppNavigator/FreezeWrapper';
import Navigation from '@libs/Navigation/Navigation';
import type {AuthScreensParamList} from '@libs/Navigation/types';
import * as SearchQueryUtils from '@libs/SearchQueryUtils';
Expand Down Expand Up @@ -58,54 +59,56 @@ function SearchPage({route}: SearchPageProps) {
}

return (
<FullPageNotFoundView
shouldForceFullScreen
shouldShow={!queryJSON}
onBackButtonPress={handleOnBackButtonPress}
shouldShowLink={false}
>
{!!queryJSON && (
<View style={styles.searchSplitContainer}>
<View style={styles.searchSidebar}>
{/* {!selectionMode?.isEnabled && queryJSON ? ( */}
{queryJSON ? (
<View>
<TopBar
activeWorkspaceID={policyID}
breadcrumbLabel={translate('common.search')}
shouldDisplaySearch={false}
<FreezeWrapper>
<FullPageNotFoundView
shouldForceFullScreen
shouldShow={!queryJSON}
onBackButtonPress={handleOnBackButtonPress}
shouldShowLink={false}
>
{!!queryJSON && (
<View style={styles.searchSplitContainer}>
<View style={styles.searchSidebar}>
{/* {!selectionMode?.isEnabled && queryJSON ? ( */}
{queryJSON ? (
<View>
<TopBar
activeWorkspaceID={policyID}
breadcrumbLabel={translate('common.search')}
shouldDisplaySearch={false}
/>
<SearchTypeMenu
queryJSON={queryJSON}
searchName={searchName}
/>
</View>
) : (
<HeaderWithBackButton
title={translate('common.selectMultiple')}
onBackButtonPress={() => {
clearSelectedTransactions();
turnOffMobileSelectionMode();
}}
/>
<SearchTypeMenu
queryJSON={queryJSON}
searchName={searchName}
/>
</View>
) : (
<HeaderWithBackButton
title={translate('common.selectMultiple')}
onBackButtonPress={() => {
clearSelectedTransactions();
turnOffMobileSelectionMode();
}}
)}
<BottomTabBar selectedTab={SCREENS.SEARCH.CENTRAL_PANE} />
</View>
<ScreenWrapper
testID={Search.displayName}
shouldShowOfflineIndicatorInWideScreen
offlineIndicatorStyle={styles.mtAuto}
>
<SearchPageHeader
queryJSON={queryJSON}
hash={queryJSON.hash}
/>
)}
<BottomTabBar selectedTab={SCREENS.SEARCH.CENTRAL_PANE} />
<SearchStatusBar queryJSON={queryJSON} />
<Search queryJSON={queryJSON} />
</ScreenWrapper>
</View>
<ScreenWrapper
testID={Search.displayName}
shouldShowOfflineIndicatorInWideScreen
offlineIndicatorStyle={styles.mtAuto}
>
<SearchPageHeader
queryJSON={queryJSON}
hash={queryJSON.hash}
/>
<SearchStatusBar queryJSON={queryJSON} />
<Search queryJSON={queryJSON} />
</ScreenWrapper>
</View>
)}
</FullPageNotFoundView>
)}
</FullPageNotFoundView>
</FreezeWrapper>
);
}

Expand Down
Loading