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

@swm/global nav menu v1 #28277

Merged
merged 32 commits into from
Oct 7, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
aacb4a6
feat: first look at adding global nav menu
WoLewicki Sep 11, 2023
fe7851f
feat: remove unused onLayout and move globalNavigation higher
WoLewicki Sep 12, 2023
5bc0dec
feat: prepare app for different menus
WoLewicki Sep 12, 2023
b1af947
add first global navigation option
adamgrzybowski Sep 26, 2023
2344abc
style changes
adamgrzybowski Sep 26, 2023
a966c89
style changes v2
adamgrzybowski Sep 28, 2023
76ef562
Merge branch 'main' into @swm/global-nav-menu-v1
adamgrzybowski Sep 28, 2023
f8784a0
fix initial global navigation option on small screen
adamgrzybowski Sep 29, 2023
3c0295e
adjustments
adamgrzybowski Sep 29, 2023
c83bad0
change OD to OLDDOT
adamgrzybowski Sep 29, 2023
1c8c9db
set includePaddingTop to false
adamgrzybowski Oct 2, 2023
5f10a5b
Merge branch 'main' into @swm/global-nav-menu-v1
adamgrzybowski Oct 2, 2023
96197e3
Update src/ROUTES.ts
adamgrzybowski Oct 2, 2023
a2c65db
Update src/pages/home/sidebar/GlobalNavigation/GlobalNavigationMenuIt…
adamgrzybowski Oct 2, 2023
ce4f907
adjustments v2
adamgrzybowski Oct 2, 2023
d962df5
Merge branch 'main' into @swm/global-nav-menu-v1
adamgrzybowski Oct 3, 2023
6001c23
Merge branch 'main' into @swm/global-nav-menu-v1
adamgrzybowski Oct 3, 2023
0f29a39
style adjustments
adamgrzybowski Oct 3, 2023
95f47b3
Update src/SCREENS.ts
adamgrzybowski Oct 4, 2023
dfd2e82
Update src/libs/Navigation/getTopMostCentralPaneRouteName.js
adamgrzybowski Oct 4, 2023
50117d7
adjustments v3
adamgrzybowski Oct 4, 2023
36e6c61
adjustments v4
adamgrzybowski Oct 4, 2023
6a361b2
Merge branch 'main' into @swm/global-nav-menu-v1
adamgrzybowski Oct 4, 2023
c35523c
remove nested ScreenWrapper
adamgrzybowski Oct 4, 2023
a5ae067
Update src/components/LHNOptionsList/OptionRowLHN.js
adamgrzybowski Oct 4, 2023
1f6da9f
Revert "Update src/components/LHNOptionsList/OptionRowLHN.js"
adamgrzybowski Oct 4, 2023
ca99028
fix styles for OptionRowLHN
adamgrzybowski Oct 4, 2023
7250a2f
fix GLOBAL_NAVIGATION_MAPPING
adamgrzybowski Oct 4, 2023
8bb5406
adjustments v5
adamgrzybowski Oct 5, 2023
4c056fa
fix color for focused item in global navigation
adamgrzybowski Oct 5, 2023
a6ad209
fix top elements aligment
adamgrzybowski Oct 5, 2023
9e87953
change ref for GlobalNavigationMenuItem
adamgrzybowski Oct 5, 2023
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
2 changes: 2 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {ReportAttachmentsProvider} from './pages/home/report/ReportAttachmentsCo
import * as Session from './libs/actions/Session';
import useDefaultDragAndDrop from './hooks/useDefaultDragAndDrop';
import OnyxUpdateManager from './libs/actions/OnyxUpdateManager';
import {SidebarNavigationContextProvider} from './pages/home/sidebar/SidebarNavigationContext';

// For easier debugging and development, when we are in web we expose Onyx to the window, so you can more easily set data into Onyx
if (window && Environment.isDevelopment()) {
Expand Down Expand Up @@ -64,6 +65,7 @@ function App() {
EnvironmentProvider,
ThemeProvider,
ThemeStylesProvider,
SidebarNavigationContextProvider,
]}
>
<CustomStatusBar />
Expand Down
10 changes: 10 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2664,19 +2664,29 @@ const CONST = {
DEFAULT_COORDINATE: [-122.4021, 37.7911],
STYLE_URL: 'mapbox://styles/expensify/cllcoiqds00cs01r80kp34tmq',
},

ONYX_UPDATE_TYPES: {
HTTPS: 'https',
PUSHER: 'pusher',
},

EVENTS: {
SCROLLING: 'scrolling',
},

HORIZONTAL_SPACER: {
DEFAULT_BORDER_BOTTOM_WIDTH: 1,
DEFAULT_MARGIN_VERTICAL: 8,
HIDDEN_MARGIN_VERTICAL: 0,
HIDDEN_BORDER_BOTTOM_WIDTH: 0,
},

GLOBAL_NAVIGATION_OPTION: {
HOME: 'home',
CHATS: 'chats',
SPEND: 'spend',
WORKSPACES: 'workspaces',
},
} as const;

export default CONST;
9 changes: 9 additions & 0 deletions src/GLOBAL_NAVIGATION_MAPPING.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import CONST from './CONST';
import SCREENS from './SCREENS';

export default {
[CONST.GLOBAL_NAVIGATION_OPTION.HOME]: [SCREENS.HOME_OD],
[CONST.GLOBAL_NAVIGATION_OPTION.CHATS]: [SCREENS.REPORT],
[CONST.GLOBAL_NAVIGATION_OPTION.SPEND]: [SCREENS.EXPENSES_OD, SCREENS.REPORTS_OD, SCREENS.INSIGHTS_OD],
[CONST.GLOBAL_NAVIGATION_OPTION.WORKSPACES]: [SCREENS.INDIVIDUALS_OD, SCREENS.GROUPS_OD, SCREENS.CARDS_AND_DOMAINS_OD],
} as const;
13 changes: 13 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,4 +318,17 @@ export default {
// These are some on-off routes that will be removed once they're no longer needed (see GH issues for details)
SAASTR: 'saastr',
SBE: 'sbe',

// Iframe screens from olddot
HOME_OD: 'home',

// Spend tab
EXPENSES_OD: 'expenses',
REPORTS_OD: 'reports',
INSIGHTS_OD: 'insights',

// Workspaces tab
INDIVIDUALS_OD: 'individuals',
GROUPS_OD: 'groups',
CARDS_AND_DOMAINS_OD: 'cards-and-domains',
} as const;
13 changes: 13 additions & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,17 @@ export default {
SIGN_IN_WITH_APPLE_DESKTOP: 'AppleSignInDesktop',
SIGN_IN_WITH_GOOGLE_DESKTOP: 'GoogleSignInDesktop',
DESKTOP_SIGN_IN_REDIRECT: 'DesktopSignInRedirect',

// Iframe screens from olddot
HOME_OD: 'Home_OD',

// Spend tab
EXPENSES_OD: 'Expenses_OD',
REPORTS_OD: 'Reports_OD',
INSIGHTS_OD: 'INSIGHTS_OD',

// Workspaces tab
INDIVIDUALS_OD: 'Individuals_OD',
GROUPS_OD: 'Groups_OD',
CARDS_AND_DOMAINS_OD: 'CardsAndDomains_OD',
} as const;
3 changes: 3 additions & 0 deletions src/components/FloatingActionButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import themeColors from '../styles/themes/default';
import Tooltip from './Tooltip';
import withLocalize, {withLocalizePropTypes} from './withLocalize';
import PressableWithFeedback from './Pressable/PressableWithFeedback';
import variables from '../styles/variables';

const AnimatedIcon = Animated.createAnimatedComponent(Icon);
AnimatedIcon.displayName = 'AnimatedIcon';
Expand Down Expand Up @@ -100,6 +101,8 @@ class FloatingActionButton extends PureComponent {
style={[styles.floatingActionButton, StyleUtils.getAnimatedFABStyle(rotate, backgroundColor)]}
>
<AnimatedIcon
width={variables.iconSizeSmall}
height={variables.iconSizeSmall}
src={Expensicons.Plus}
fill={fill}
/>
Expand Down
5 changes: 5 additions & 0 deletions src/libs/Navigation/Navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import linkingConfig from './linkingConfig';
import navigationRef from './navigationRef';
import NAVIGATORS from '../../NAVIGATORS';
import originalGetTopmostReportId from './getTopmostReportId';
import originalGetTopMostCentralPaneRouteName from './getTopMostCentralPaneRouteName';
import getStateFromPath from './getStateFromPath';
import SCREENS from '../../SCREENS';
import CONST from '../../CONST';
Expand Down Expand Up @@ -46,6 +47,9 @@ function canNavigate(methodName, params = {}) {
// Re-exporting the getTopmostReportId here to fill in default value for state. The getTopmostReportId isn't defined in this file to avoid cyclic dependencies.
const getTopmostReportId = (state = navigationRef.getState()) => originalGetTopmostReportId(state);

// Re-exporting the getTopMostCentralPaneRouteName here to fill in default value for state. The getTopMostCentralPaneRouteName isn't defined in this file to avoid cyclic dependencies.
const getTopMostCentralPaneRouteName = (state = navigationRef.getState()) => originalGetTopMostCentralPaneRouteName(state);
adamgrzybowski marked this conversation as resolved.
Show resolved Hide resolved

/**
* Method for finding on which index in stack we are.
* @param {Object} route
Expand Down Expand Up @@ -268,6 +272,7 @@ export default {
setIsNavigationReady,
getTopmostReportId,
getRouteNameFromStateEvent,
getTopMostCentralPaneRouteName,
};

export {navigationRef};
7 changes: 6 additions & 1 deletion src/libs/Navigation/NavigationRoot.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useRef, useEffect} from 'react';
import React, {useRef, useEffect, useContext} from 'react';
import PropTypes from 'prop-types';
import {NavigationContainer, DefaultTheme, getPathFromState} from '@react-navigation/native';
import {useFlipper} from '@react-navigation/devtools';
Expand All @@ -11,6 +11,7 @@ import Log from '../Log';
import StatusBar from '../StatusBar';
import useCurrentReportID from '../../hooks/useCurrentReportID';
import useWindowDimensions from '../../hooks/useWindowDimensions';
import {SidebarNavigationContext} from '../../pages/home/sidebar/SidebarNavigationContext';

// https://reactnavigation.org/docs/themes
const navigationTheme = {
Expand Down Expand Up @@ -53,6 +54,7 @@ function parseAndLogRoute(state) {
function NavigationRoot(props) {
useFlipper(navigationRef);
const firstRenderRef = useRef(true);
const globalNavigation = useContext(SidebarNavigationContext);

const {updateCurrentReportID} = useCurrentReportID();
const {isSmallScreenWidth} = useWindowDimensions();
Expand Down Expand Up @@ -128,6 +130,9 @@ function NavigationRoot(props) {
}, 0);
parseAndLogRoute(state);
animateStatusBarBackgroundColor();

// Update the global navigation to show the correct selected menu items.
globalNavigation.updateFromNavigationState(state);
};

return (
Expand Down
32 changes: 32 additions & 0 deletions src/libs/Navigation/getTopMostCentralPaneRouteName.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import lodashFindLast from 'lodash/findLast';

/**
* Find the name of top most central pane route.
hayata-suenaga marked this conversation as resolved.
Show resolved Hide resolved
*
* @param {Object} state - The react-navigation state
* @returns {String | undefined} - It's possible that there is no central pane in the state.
*/
function getTopMostCentralPaneRouteName(state) {
if (!state) {
return;
}
const topmostCentralPane = lodashFindLast(state.routes, (route) => route.name === 'CentralPaneNavigator');

if (!topmostCentralPane) {
return;
adamgrzybowski marked this conversation as resolved.
Show resolved Hide resolved
}

if (topmostCentralPane.state && topmostCentralPane.state.routes) {
// State may don't have index in some cases. But in this case there will be only one route in state.
hayata-suenaga marked this conversation as resolved.
Show resolved Hide resolved
return topmostCentralPane.state.routes[topmostCentralPane.state.index || 0].name;
}

if (topmostCentralPane.params) {
// State may don't have inner state in some cases (e.g generating actions from path). But in this case there will be params available.
adamgrzybowski marked this conversation as resolved.
Show resolved Hide resolved
return topmostCentralPane.screen;
adamgrzybowski marked this conversation as resolved.
Show resolved Hide resolved
}

return undefined;
}

export default getTopMostCentralPaneRouteName;
7 changes: 6 additions & 1 deletion src/libs/Navigation/linkTo.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import linkingConfig from './linkingConfig';
import getTopmostReportId from './getTopmostReportId';
import getStateFromPath from './getStateFromPath';
import CONST from '../../CONST';
import getTopMostCentralPaneRouteName from './getTopMostCentralPaneRouteName';

/**
* Motivation for this function is described in NAVIGATION.md
Expand Down Expand Up @@ -66,7 +67,11 @@ export default function linkTo(navigation, path, type) {
action.type = CONST.NAVIGATION.ACTION_TYPE.REPLACE;

// If this action is navigating to the report screen and the top most navigator is different from the one we want to navigate - PUSH the new screen to the top of the stack
adamgrzybowski marked this conversation as resolved.
Show resolved Hide resolved
} else if (action.payload.name === NAVIGATORS.CENTRAL_PANE_NAVIGATOR && getTopmostReportId(root.getState()) !== getTopmostReportId(state)) {
} else if (
action.payload.name === NAVIGATORS.CENTRAL_PANE_NAVIGATOR &&
(getTopmostReportId(root.getState()) !== getTopmostReportId(state) || getTopMostCentralPaneRouteName(root.getState()) !== getTopMostCentralPaneRouteName(state))
hayata-suenaga marked this conversation as resolved.
Show resolved Hide resolved
// getTopmostReportId(root.getState()) !== getTopmostReportId(state)
adamgrzybowski marked this conversation as resolved.
Show resolved Hide resolved
) {
action.type = CONST.NAVIGATION.ACTION_TYPE.PUSH;

// If the type is UP, we deeplinked into one of the RHP flows and we want to replace the current screen with the previous one in the flow
Expand Down
53 changes: 53 additions & 0 deletions src/pages/home/sidebar/GlobalNavigation/GlobalNavigation.js
adamgrzybowski marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React, {useMemo} from 'react';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import PressableAvatarWithIndicator from '../PressableAvatarWithIndicator';
import styles from '../../../../styles/styles';
import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize';
import * as Expensicons from '../../../../components/Icon/Expensicons';
import GlobalNavigationMenuItemList from './GlobalNavigationMenuItemList';
import CONST from '../../../../CONST';
import Navigation from '../../../../libs/Navigation/Navigation';
import ROUTES from '../../../../ROUTES';

const propTypes = {
isCreateMenuOpen: PropTypes.bool,
...withLocalizePropTypes,
};

const defaultProps = {
isCreateMenuOpen: false,
};

function GlobalNavigation({isCreateMenuOpen, children}) {
const items = useMemo(
() => [
{
icon: Expensicons.ChatBubble,
text: 'Chats',
adamgrzybowski marked this conversation as resolved.
Show resolved Hide resolved
value: CONST.GLOBAL_NAVIGATION_OPTION.CHATS,
onSelected: () => {
Navigation.navigate(ROUTES.REPORT);
},
},
],
[],
);

return (
<View style={[styles.ph5, styles.pv3, styles.alignItemsCenter, styles.h100, styles.globalNavigation]}>
<PressableAvatarWithIndicator isCreateMenuOpen={isCreateMenuOpen} />
<GlobalNavigationMenuItemList
menuItems={items}
style={styles.mt4}
/>
{children}
</View>
);
}

GlobalNavigation.propTypes = propTypes;
GlobalNavigation.defaultProps = defaultProps;
GlobalNavigation.displayName = 'GlobalNavigation';

export default withLocalize(GlobalNavigation);
adamgrzybowski marked this conversation as resolved.
Show resolved Hide resolved
104 changes: 104 additions & 0 deletions src/pages/home/sidebar/GlobalNavigation/GlobalNavigationMenuItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React from 'react';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import _ from 'underscore';
import Text from '../../../../components/Text';
import styles from '../../../../styles/styles';
import * as StyleUtils from '../../../../styles/StyleUtils';
import Icon from '../../../../components/Icon';
import CONST from '../../../../CONST';
import PressableWithSecondaryInteraction from '../../../../components/PressableWithSecondaryInteraction';
import Hoverable from '../../../../components/Hoverable';
import variables from '../../../../styles/variables';
import stylePropTypes from '../../../../styles/stylePropTypes';
import defaultTheme from '../../../../styles/themes/default';
import fontWeightBold from '../../../../styles/fontWeight/bold';

const propTypes = {
/** Function to fire when component is pressed */
onPress: PropTypes.func,

/** Icon to display */
icon: PropTypes.elementType,

/** Icon Width */
iconWidth: PropTypes.number,

/** Icon Height */
iconHeight: PropTypes.number,

/** Text to display for the item */
title: PropTypes.string,

/** Any additional styles to pass to the icon container. */
iconStyles: stylePropTypes,

/** The fill color to pass into the icon. */
iconFill: PropTypes.string,

/** Whether item is focused or active */
focused: PropTypes.bool,
adamgrzybowski marked this conversation as resolved.
Show resolved Hide resolved

/** Prop to represent the size of the avatar images to be shown */
avatarSize: PropTypes.oneOf(_.values(CONST.AVATAR_SIZE)),
};

const defaultProps = {
icon: undefined,
iconWidth: undefined,
iconHeight: undefined,
iconStyles: [],
iconFill: undefined,
focused: false,
onPress: () => {},
title: '',
avatarSize: CONST.AVATAR_SIZE.DEFAULT,
};
adamgrzybowski marked this conversation as resolved.
Show resolved Hide resolved

const GlobalNavigationMenuItem = React.forwardRef((props, ref) => (
adamgrzybowski marked this conversation as resolved.
Show resolved Hide resolved
<Hoverable>
{(isHovered) => (
<PressableWithSecondaryInteraction
adamgrzybowski marked this conversation as resolved.
Show resolved Hide resolved
onPress={props.focused ? () => {} : props.onPress}
style={styles.globalNavigationItemContainer}
ref={ref}
accessibilityRole={CONST.ACCESSIBILITY_ROLE.MENUITEM}
accessibilityLabel={props.title ? props.title.toString() : ''}
>
{({pressed}) => (
adamgrzybowski marked this conversation as resolved.
Show resolved Hide resolved
<View style={[styles.alignItemsCenter, styles.flexRow, styles.flex1]}>
<View style={[styles.globalNavigationSelectionIndicator, {backgroundColor: props.focused ? defaultTheme.iconMenu : defaultTheme.transparent}]} />
<View style={[styles.flexColumn, styles.flex1, styles.alignItemsCenter, styles.mr1]}>
<View style={[styles.popoverMenuIcon, ...props.iconStyles, StyleUtils.getAvatarWidthStyle(props.avatarSize)]}>
<Icon
hovered={isHovered}
pressed={pressed}
src={props.icon}
width={props.iconWidth}
height={props.iconHeight}
fill={props.focused ? defaultTheme.iconMenu : props.iconFill}
/>
</View>
<View style={[styles.mt1, styles.alignItemsCenter]}>
<Text
style={[
StyleUtils.getFontSizeStyle(variables.fontSizeExtraSmall),
props.focused ? {color: defaultTheme.textLight, fontWeight: fontWeightBold} : {color: props.iconFill},
]}
>
{props.title}
</Text>
</View>
</View>
</View>
)}
</PressableWithSecondaryInteraction>
)}
</Hoverable>
));

GlobalNavigationMenuItem.propTypes = propTypes;
GlobalNavigationMenuItem.defaultProps = defaultProps;
GlobalNavigationMenuItem.displayName = 'GlobalNavigationMenuItem';

export default GlobalNavigationMenuItem;
Loading
Loading