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

Initial implementation of the right pane #56490

Draft
wants to merge 2 commits into
base: main
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
7 changes: 7 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,9 @@ const ONYXKEYS = {
/** Information about travel provisioning process */
TRAVEL_PROVISIONING: 'travelProvisioning',

/** Stores the information about the state of side panel */
NVP_SIDE_PANEL: 'nvp_sidePanelExpanded',

/** Collection Keys */
COLLECTION: {
DOWNLOAD: 'download_',
Expand Down Expand Up @@ -1074,6 +1077,10 @@ type OnyxValuesMapping = {
[ONYXKEYS.CORPAY_ONBOARDING_FIELDS]: OnyxTypes.CorpayOnboardingFields;
[ONYXKEYS.LAST_FULL_RECONNECT_TIME]: string;
[ONYXKEYS.TRAVEL_PROVISIONING]: OnyxTypes.TravelProvisioning;
[ONYXKEYS.NVP_SIDE_PANEL]: {
open: boolean;
openMobile: boolean;
};
};
type OnyxValues = OnyxValuesMapping & OnyxCollectionValuesMapping & OnyxFormValuesMapping & OnyxFormDraftValuesMapping;

Expand Down
69 changes: 51 additions & 18 deletions src/components/Search/SearchRouter/SearchButton.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import React, {useRef} from 'react';
import type {StyleProp, View, ViewStyle} from 'react-native';
import Onyx, {useOnyx} from 'react-native-onyx';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import {PressableWithoutFeedback} from '@components/Pressable';
import Tooltip from '@components/Tooltip';
import useLocalize from '@hooks/useLocalize';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import Performance from '@libs/Performance';
import {callFunctionIfActionIsAllowed} from '@userActions/Session';
import Timing from '@userActions/Timing';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import {useSearchRouterContext} from './SearchRouterContext';

type SearchButtonProps = {
Expand All @@ -25,31 +28,61 @@ function SearchButton({style}: SearchButtonProps) {
const pressableRef = useRef<View>(null);

return (
<Tooltip text={translate('common.search')}>
<PressableWithoutFeedback
ref={pressableRef}
nativeID="searchButton"
accessibilityLabel={translate('common.search')}
style={[styles.flexRow, styles.touchableButtonImage, style]}
// eslint-disable-next-line react-compiler/react-compiler
onPress={callFunctionIfActionIsAllowed(() => {
pressableRef?.current?.blur();
<>
<HelpButton />
<Tooltip text={translate('common.search')}>
<PressableWithoutFeedback
ref={pressableRef}
nativeID="searchButton"
accessibilityLabel={translate('common.search')}
style={[styles.flexRow, styles.touchableButtonImage, style]}
// eslint-disable-next-line react-compiler/react-compiler
onPress={callFunctionIfActionIsAllowed(() => {
pressableRef?.current?.blur();

Timing.start(CONST.TIMING.OPEN_SEARCH);
Performance.markStart(CONST.TIMING.OPEN_SEARCH);

openSearchRouter();
})}
>
<Icon
src={Expensicons.MagnifyingGlass}
fill={theme.icon}
/>
</PressableWithoutFeedback>
</Tooltip>
</>
);
}

SearchButton.displayName = 'SearchButton';

export default SearchButton;

Timing.start(CONST.TIMING.OPEN_SEARCH);
Performance.markStart(CONST.TIMING.OPEN_SEARCH);
function HelpButton({style}: SearchButtonProps) {
const styles = useThemeStyles();
const theme = useTheme();
const {translate} = useLocalize();
const [sidePanel] = useOnyx(ONYXKEYS.NVP_SIDE_PANEL);
const {isExtraLargeScreenWidth} = useResponsiveLayout();

openSearchRouter();
})}
return (
<Tooltip text={translate('initialSettingsPage.help')}>
<PressableWithoutFeedback
nativeID="helpButton"
accessibilityLabel={translate('initialSettingsPage.help')}
style={[styles.flexRow, styles.touchableButtonImage, style]}
onPress={() => {
// eslint-disable-next-line rulesdir/prefer-actions-set-data
Onyx.merge(ONYXKEYS.NVP_SIDE_PANEL, isExtraLargeScreenWidth ? {open: !sidePanel?.open} : {open: !sidePanel?.openMobile, openMobile: !sidePanel?.openMobile});
}}
>
<Icon
src={Expensicons.MagnifyingGlass}
src={Expensicons.QuestionMark}
fill={theme.icon}
/>
</PressableWithoutFeedback>
</Tooltip>
);
}

SearchButton.displayName = 'SearchButton';

export default SearchButton;
2 changes: 2 additions & 0 deletions src/hooks/useResponsiveLayout/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export default function useResponsiveLayout(): ResponsiveLayoutResult {
const isMediumScreenWidth = windowWidth > variables.mobileResponsiveWidthBreakpoint && windowWidth <= variables.tabletResponsiveWidthBreakpoint;
const onboardingIsMediumOrLargerScreenWidth = windowWidth > variables.mobileResponsiveWidthBreakpoint;
const isLargeScreenWidth = windowWidth > variables.tabletResponsiveWidthBreakpoint;
const isExtraLargeScreenWidth = windowWidth > variables.sidePanelResponsiveWidthBreakpoint;
const isExtraSmallScreenWidth = windowWidth <= variables.extraSmallMobileResponsiveWidthBreakpoint;

const lowerScreenDimmension = Math.min(windowWidth, windowHeight);
Expand Down Expand Up @@ -76,6 +77,7 @@ export default function useResponsiveLayout(): ResponsiveLayoutResult {
isMediumScreenWidth,
onboardingIsMediumOrLargerScreenWidth,
isLargeScreenWidth,
isExtraLargeScreenWidth,
isSmallScreen,
};
}
1 change: 1 addition & 0 deletions src/hooks/useResponsiveLayout/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ type ResponsiveLayoutResult = {
isExtraSmallScreenHeight: boolean;
isMediumScreenWidth: boolean;
isLargeScreenWidth: boolean;
isExtraLargeScreenWidth: boolean;
isExtraSmallScreenWidth: boolean;
isSmallScreen: boolean;
onboardingIsMediumOrLargerScreenWidth: boolean;
Expand Down
116 changes: 112 additions & 4 deletions src/libs/Navigation/AppNavigator/AuthScreens.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import {findFocusedRoute, useNavigation} from '@react-navigation/native';
import React, {memo, useEffect, useMemo, useRef, useState} from 'react';
import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {NativeModules, View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import Onyx, {withOnyx} from 'react-native-onyx';
import Onyx, {useOnyx, withOnyx} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import ActiveGuidesEventListener from '@components/ActiveGuidesEventListener';
import ComposeProviders from '@components/ComposeProviders';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import OptionsListContextProvider from '@components/OptionListContextProvider';
import {PressableWithFeedback, PressableWithoutFeedback} from '@components/Pressable';
import {SearchContextProvider} from '@components/Search/SearchContext';
import {useSearchRouterContext} from '@components/Search/SearchRouter/SearchRouterContext';
import SearchRouterModal from '@components/Search/SearchRouter/SearchRouterModal';
import TestToolsModal from '@components/TestToolsModal';
import useActiveWorkspace from '@hooks/useActiveWorkspace';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useLocalize from '@hooks/useLocalize';
import useOnboardingFlowRouter from '@hooks/useOnboardingFlow';
import usePermissions from '@hooks/usePermissions';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
Expand Down Expand Up @@ -42,6 +45,7 @@
import ConnectionCompletePage from '@pages/ConnectionCompletePage';
import NotFoundPage from '@pages/ErrorPage/NotFoundPage';
import DesktopSignInRedirectPage from '@pages/signin/DesktopSignInRedirectPage';
import variables from '@styles/variables';
import * as App from '@userActions/App';
import * as Download from '@userActions/Download';
import * as Modal from '@userActions/Modal';
Expand Down Expand Up @@ -243,12 +247,13 @@
function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDAppliedToClient}: AuthScreensProps) {
const theme = useTheme();
const styles = useThemeStyles();
const {shouldUseNarrowLayout} = useResponsiveLayout();
const {shouldUseNarrowLayout, isExtraLargeScreenWidth} = useResponsiveLayout();
const rootNavigatorOptions = useRootNavigatorOptions();
const {canUseDefaultRooms} = usePermissions();
const {activeWorkspaceID} = useActiveWorkspace();
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
const {toggleSearch} = useSearchRouterContext();
const [sidePanel] = useOnyx(ONYXKEYS.NVP_SIDE_PANEL);

const modal = useRef<OnyxTypes.Modal>({});
const {isOnboardingCompleted} = useOnboardingFlowRouter();
Expand Down Expand Up @@ -463,9 +468,11 @@
},
};

const shouldShowHelpPanel = isExtraLargeScreenWidth && !!sidePanel?.open;

return (
<ComposeProviders components={[OptionsListContextProvider, SearchContextProvider]}>
<View style={styles.rootNavigatorContainerStyles(shouldUseNarrowLayout)}>
<View style={styles.rootNavigatorContainerStyles(shouldUseNarrowLayout, shouldShowHelpPanel)}>
<RootStack.Navigator screenOptions={rootNavigatorOptions.centralPaneNavigator}>
<RootStack.Screen
name={NAVIGATORS.BOTTOM_TAB_NAVIGATOR}
Expand Down Expand Up @@ -646,6 +653,7 @@
<SearchRouterModal />
</View>
<ActiveGuidesEventListener />
<HelpPanel />
</ComposeProviders>
);
}
Expand All @@ -669,3 +677,103 @@
key: ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT,
},
})(AuthScreensMemoized);

function HelpPanel() {
const theme = useTheme();
const [sidePanel] = useOnyx(ONYXKEYS.NVP_SIDE_PANEL);
const styles = useThemeStyles();
const {isExtraLargeScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout();
const {translate} = useLocalize();

const resetTriggered = useRef(false);

const onClose = useCallback(
(updateNarrow = false) => {
// eslint-disable-next-line rulesdir/prefer-actions-set-data
Onyx.merge(ONYXKEYS.NVP_SIDE_PANEL, isExtraLargeScreenWidth && !updateNarrow ? {open: false} : {openMobile: false});
},
[isExtraLargeScreenWidth],
);

useEffect(() => {
if (!isExtraLargeScreenWidth && !resetTriggered.current) {
onClose(true);
resetTriggered.current = true;
}

if (isExtraLargeScreenWidth) {
resetTriggered.current = false;
}
}, [isExtraLargeScreenWidth, onClose]);

if (!isExtraLargeScreenWidth && !sidePanel?.openMobile) {
return null;
}

if (isExtraLargeScreenWidth && !sidePanel?.open) {
return null;
}

return (
<>
{!isExtraLargeScreenWidth && (
<View
style={{
position: 'fixed',
// We need to stretch the overlay to cover the sidebar and the translate animation distance.
left: 0,
top: 0,
bottom: 0,
right: 0,
backgroundColor: theme.overlay,
opacity: 0.72,
}}
>
<PressableWithoutFeedback
style={[styles.draggableTopBar, styles.boxShadowNone]}
onPress={onClose}

Check failure on line 734 in src/libs/Navigation/AppNavigator/AuthScreens.tsx

View workflow job for this annotation

GitHub Actions / typecheck

Type '(updateNarrow?: boolean) => void' is not assignable to type '(((event: GestureResponderEvent) => void) & ((event?: GestureResponderEvent | KeyboardEvent | undefined) => void | Promise<...>)) | (((event: GestureResponderEvent) => void) & ((event?: GestureResponderEvent | ... 1 more ... | undefined) => void | Promise<...>)) | undefined'.
accessibilityLabel={translate('common.close')}
role={CONST.ROLE.BUTTON}
id={CONST.OVERLAY.TOP_BUTTON_NATIVE_ID}
tabIndex={-1}
/>
<PressableWithoutFeedback
style={[styles.flex1, styles.boxShadowNone]}
onPress={onClose}

Check failure on line 742 in src/libs/Navigation/AppNavigator/AuthScreens.tsx

View workflow job for this annotation

GitHub Actions / typecheck

Type '(updateNarrow?: boolean) => void' is not assignable to type '(((event: GestureResponderEvent) => void) & ((event?: GestureResponderEvent | KeyboardEvent | undefined) => void | Promise<...>)) | (((event: GestureResponderEvent) => void) & ((event?: GestureResponderEvent | ... 1 more ... | undefined) => void | Promise<...>)) | undefined'.
accessibilityLabel={translate('common.close')}
role={CONST.ROLE.BUTTON}
noDragArea
id={CONST.OVERLAY.BOTTOM_BUTTON_NATIVE_ID}
tabIndex={-1}
/>
</View>
)}
<View
style={[
{
width: shouldUseNarrowLayout ? '100%' : variables.sideBarWidth,
height: '100%',
backgroundColor: theme.modalBackground,
right: 0,
position: 'fixed',
},
isExtraLargeScreenWidth && {borderLeftWidth: 1, borderLeftColor: theme.border},
]}
>
<PressableWithFeedback
onPress={() => onClose(false)}
role={CONST.ROLE.BUTTON}
accessibilityLabel={translate('common.close')}
>
<HeaderWithBackButton
title="Help"
onBackButtonPress={() => onClose(false)}
onCloseButtonPress={() => onClose(false)}
shouldShowBackButton={!isExtraLargeScreenWidth}
shouldShowCloseButton={isExtraLargeScreenWidth}
/>
</PressableWithFeedback>
</View>
</>
);
}
3 changes: 2 additions & 1 deletion src/styles/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2869,7 +2869,8 @@ const styles = (theme: ThemeColors) =>
borderRadius: 88,
},

rootNavigatorContainerStyles: (isSmallScreenWidth: boolean) => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1} satisfies ViewStyle),
rootNavigatorContainerStyles: (isSmallScreenWidth: boolean, shouldShowHelpPanel: boolean) =>
({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, marginRight: shouldShowHelpPanel ? variables.sideBarWidth : 0, flex: 1} satisfies ViewStyle),
RHPNavigatorContainerNavigatorContainerStyles: (isSmallScreenWidth: boolean) => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1} satisfies ViewStyle),

avatarInnerTextChat: {
Expand Down
1 change: 1 addition & 0 deletions src/styles/variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export default {
extraSmallMobileResponsiveHeightBreakpoint: 667,
mobileResponsiveWidthBreakpoint: 800,
tabletResponsiveWidthBreakpoint: 1024,
sidePanelResponsiveWidthBreakpoint: 1175,
iosSafeAreaInsetsPercentage: 0.7,
androidSafeAreaInsetsPercentage: 1,
sideBarWidth: 375,
Expand Down
Loading