diff --git a/assets/images/new-expensify.svg b/assets/images/new-expensify.svg index 38276ecd9385..dc7aec87c6fd 100644 --- a/assets/images/new-expensify.svg +++ b/assets/images/new-expensify.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts index 4d4d187b5510..164f0b6d2454 100644 --- a/src/components/Icon/Expensicons.ts +++ b/src/components/Icon/Expensicons.ts @@ -52,6 +52,7 @@ import EReceiptIcon from '@assets/images/eReceiptIcon.svg'; import Exclamation from '@assets/images/exclamation.svg'; import Exit from '@assets/images/exit.svg'; import Expand from '@assets/images/expand.svg'; +import ExpensifyAppIcon from '@assets/images/expensify-app-icon.svg'; import ExpensifyFooterLogoVertical from '@assets/images/expensify-footer-logo-vertical.svg'; import ExpensifyFooterLogo from '@assets/images/expensify-footer-logo.svg'; import ExpensifyWordmark from '@assets/images/expensify-wordmark.svg'; @@ -185,6 +186,7 @@ export { EmptyStateAttachReceipt, Exclamation, Exit, + ExpensifyAppIcon, ExpensifyCard, ExpensifyWordmark, ExpensifyFooterLogo, diff --git a/src/components/Icon/index.tsx b/src/components/Icon/index.tsx index 5f82421c0e8e..0ba7d444324b 100644 --- a/src/components/Icon/index.tsx +++ b/src/components/Icon/index.tsx @@ -100,4 +100,5 @@ function Icon({ Icon.displayName = 'Icon'; +export type {IconProps}; export default Icon; diff --git a/src/components/IllustratedHeaderPageLayout.js b/src/components/IllustratedHeaderPageLayout.js index dff3c52f4315..89e21c92c31e 100644 --- a/src/components/IllustratedHeaderPageLayout.js +++ b/src/components/IllustratedHeaderPageLayout.js @@ -1,8 +1,8 @@ import PropTypes from 'prop-types'; import React from 'react'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import useTheme from '../hooks/useTheme'; -import useThemeStyles from '../hooks/useThemeStyles'; import HeaderPageLayout from './HeaderPageLayout'; import headerWithBackButtonPropTypes from './HeaderWithBackButton/headerWithBackButtonPropTypes'; import Lottie from './Lottie'; diff --git a/src/components/MenuItem.js b/src/components/MenuItem.js index b1f6b7f7319a..4c47f60a894d 100644 --- a/src/components/MenuItem.js +++ b/src/components/MenuItem.js @@ -84,6 +84,7 @@ const defaultProps = { shouldShowRightComponent: false, titleWithTooltips: [], shouldCheckActionAllowedOnPress: true, + isPaneMenu: false, }; const MenuItem = React.forwardRef((props, ref) => { @@ -234,6 +235,7 @@ const MenuItem = React.forwardRef((props, ref) => { StyleUtils.getIconFillColor( getButtonState(props.focused || isHovered, pressed, props.success, props.disabled, props.interactive), true, + props.isPaneMenu, ) } /> @@ -266,7 +268,11 @@ const MenuItem = React.forwardRef((props, ref) => { height={props.iconHeight} fill={ props.secondaryIconFill || - StyleUtils.getIconFillColor(getButtonState(props.focused || isHovered, pressed, props.success, props.disabled, props.interactive), true) + StyleUtils.getIconFillColor( + getButtonState(props.focused || isHovered, pressed, props.success, props.disabled, props.interactive), + true, + props.isPaneMenu, + ) } /> diff --git a/src/components/SubscriptAvatar.tsx b/src/components/SubscriptAvatar.tsx index 9a0aa934ff1b..05c0aa6138e6 100644 --- a/src/components/SubscriptAvatar.tsx +++ b/src/components/SubscriptAvatar.tsx @@ -8,8 +8,20 @@ import type {AvatarSource} from '@libs/UserUtils'; import CONST from '@src/CONST'; import {AvatarType} from '@src/types/onyx/OnyxCommon'; import Avatar from './Avatar'; +import Icon, {IconProps} from './Icon'; import UserDetailsTooltip from './UserDetailsTooltip'; +type SubIcon = { + /** Avatar source to display */ + source: IconProps['src']; + + /** Width of the icon */ + width?: number; + + /** Height of the icon */ + height?: number; +}; + type SubAvatar = { /** Avatar source to display */ source?: AvatarSource; @@ -31,15 +43,18 @@ type SubscriptAvatarProps = { /** Avatar URL or icon */ mainAvatar?: SubAvatar; - /** Subscript avatar URL or icon */ - secondaryAvatar?: SubAvatar; - /** Set the size of avatars */ size?: ValueOf; /** Background color used for subscript avatar border */ backgroundColor?: string; + /** Subscript avatar URL or icon */ + secondaryAvatar?: SubAvatar; + + /** Subscript icon type */ + subscriptIcon?: SubIcon; + /** Removes margin from around the avatar, used for the chat view */ noMargin?: boolean; @@ -47,7 +62,7 @@ type SubscriptAvatarProps = { showTooltip?: boolean; }; -function SubscriptAvatar({mainAvatar = {}, secondaryAvatar = {}, size = CONST.AVATAR_SIZE.DEFAULT, backgroundColor, noMargin = false, showTooltip = true}: SubscriptAvatarProps) { +function SubscriptAvatar({mainAvatar = {}, secondaryAvatar, subscriptIcon, size = CONST.AVATAR_SIZE.DEFAULT, backgroundColor, noMargin = false, showTooltip = true}: SubscriptAvatarProps) { const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -73,31 +88,58 @@ function SubscriptAvatar({mainAvatar = {}, secondaryAvatar = {}, size = CONST.AV /> - + {secondaryAvatar && ( + + + + + + )} + {subscriptIcon && ( - - + )} ); } diff --git a/src/components/WorkspaceSwitcherButton.tsx b/src/components/WorkspaceSwitcherButton.tsx new file mode 100644 index 000000000000..30a10cc96da0 --- /dev/null +++ b/src/components/WorkspaceSwitcherButton.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import useLocalize from '@hooks/useLocalize'; +import CONST from '@src/CONST'; +import * as Expensicons from './Icon/Expensicons'; +import {PressableWithFeedback} from './Pressable'; +import SubscriptAvatar from './SubscriptAvatar'; + +function WorkspaceSwitcherButton() { + const {translate} = useLocalize(); + + return ( + {}} + > + + + ); +} + +WorkspaceSwitcherButton.displayName = 'WorkspaceSwitcherButton'; + +export default WorkspaceSwitcherButton; diff --git a/src/components/menuItemPropTypes.js b/src/components/menuItemPropTypes.js index 4d2de3275e23..576fff6d78da 100644 --- a/src/components/menuItemPropTypes.js +++ b/src/components/menuItemPropTypes.js @@ -162,6 +162,9 @@ const propTypes = { /** Should check anonymous user in onPress function */ shouldCheckActionAllowedOnPress: PropTypes.bool, + + /** Is this menu item in the settings pane */ + isPaneMenu: PropTypes.bool, }; export default propTypes; diff --git a/src/languages/en.ts b/src/languages/en.ts index 563f111a9772..dfe9e02ab03a 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1996,7 +1996,6 @@ export default { transactionDate: 'Transaction date', }, breadcrumbs: { - // TODO-IDEAL: Verify translations in Spanish chats: 'Chats', }, referralProgram: { diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar.js b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar.js new file mode 100644 index 000000000000..fc92d8514b7c --- /dev/null +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar.js @@ -0,0 +1,61 @@ +import PropTypes from 'prop-types'; +import React, {useCallback} from 'react'; +import {View} from 'react-native'; +import * as Expensicons from '@components/Icon/Expensicons'; +import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; +import Search from '@components/Search'; +import SubscriptAvatar from '@components/SubscriptAvatar'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Navigation from '@libs/Navigation/Navigation'; +import SignInOrAvatarWithOptionalStatus from '@pages/home/sidebar/SignInOrAvatarWithOptionalStatus'; +import * as Session from '@userActions/Session'; +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; + +// TODO-IDEAL: isCreateMenuOpen wasn't used before +function TopBar({isCreateMenuOpen = false}) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + const showSearchPage = useCallback(() => { + if (isCreateMenuOpen) { + // Prevent opening Search page when click Search icon quickly after clicking FAB icon + return; + } + + Navigation.navigate(ROUTES.SEARCH); + }, [isCreateMenuOpen]); + + return ( + + + + + + + + ); +} + +TopBar.displayName = 'TopBar'; +TopBar.propTypes = { + isCreateMenuOpen: PropTypes.bool, +}; +TopBar.defaultProps = { + isCreateMenuOpen: false, +}; + +export default TopBar; diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/index.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/index.tsx index 24be4ce5a174..e6a34a428527 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/index.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/index.tsx @@ -15,6 +15,7 @@ import {View} from 'react-native'; import {NavigationStateRoute} from '@libs/Navigation/types'; import SCREENS from '@src/SCREENS'; import BottomTabBar from './BottomTabBar'; +import TopBar from './TopBar'; type CustomNavigatorProps = DefaultNavigatorOptions, StackNavigationOptions, StackNavigationEventMap> & { initialRouteName: string; @@ -67,6 +68,7 @@ function CustomBottomTabNavigator({initialRouteName, children, screenOptions, .. return ( + { - if (isCreateMenuOpen) { - // Prevent opening Search page when click Search icon quickly after clicking FAB icon - return; - } - - Navigation.navigate(ROUTES.SEARCH); - }, [isCreateMenuOpen]); - /** * Show Report page with selected report id * @@ -152,33 +132,6 @@ function SidebarLinks({onLinkClick, insets, optionListItems, isLoading, priority return ( - -
- } - role={CONST.ROLE.PRESENTATION} - shouldShowEnvironmentBadge - /> - - - - - - - { Link.openExternalLink(CONST.EXPENSIFY_INBOX_URL); }, - shouldShowRightIcon: true, - iconRight: Expensicons.NewWindow, link: CONST.EXPENSIFY_INBOX_URL, }, { @@ -210,8 +208,6 @@ function InitialSettingsPage(props) { action: () => { Link.openExternalLink(CONST.NEWHELP_URL); }, - shouldShowRightIcon: true, - iconRight: Expensicons.NewWindow, link: CONST.NEWHELP_URL, }, { @@ -273,6 +269,7 @@ function InitialSettingsPage(props) { !_.isEmpty(item.link) ? (e) => ReportActionContextMenu.showContextMenu(CONTEXT_MENU_TYPES.LINK, e, item.link, popoverAnchor.current) : undefined } focused={activeRoute && activeRoute.startsWith(item.routeName, 1)} + isPaneMenu /> ); })} @@ -355,14 +352,12 @@ function InitialSettingsPage(props) { ); - const navigateBackTo = lodashGet(props.route, 'params.backTo', ROUTES.HOME); - return ( Navigation.navigate(navigateBackTo)} + onBackButtonPress={() => Navigation.closeFullScreen()} backgroundColor={theme.PAGE_THEMES[SCREENS.SETTINGS.ROOT].backgroundColor} childrenContainerStyles={[styles.m0, styles.p0]} > diff --git a/src/stories/SubscriptAvatar.stories.js b/src/stories/SubscriptAvatar.stories.js index 9ca055aaa5d8..f2cf1c216052 100644 --- a/src/stories/SubscriptAvatar.stories.js +++ b/src/stories/SubscriptAvatar.stories.js @@ -1,6 +1,6 @@ import React from 'react'; import * as defaultAvatars from '@components/Icon/DefaultAvatars'; -import * as defaultWorkspaceAvatars from '@components/Icon/WorkspaceDefaultAvatars'; +import * as Expensicons from '@components/Icon/Expensicons'; import SubscriptAvatar from '@components/SubscriptAvatar'; import CONST from '@src/CONST'; @@ -14,7 +14,6 @@ export default { component: SubscriptAvatar, args: { mainAvatar: {source: defaultAvatars.Avatar5, name: '', type: CONST.ICON_TYPE_AVATAR}, - secondaryAvatar: {source: defaultWorkspaceAvatars.WorkspaceE, name: '', type: CONST.ICON_TYPE_WORKSPACE}, size: CONST.AVATAR_SIZE.DEFAULT, }, argTypes: { @@ -39,4 +38,15 @@ AvatarURLStory.args = { secondaryAvatar: {source: defaultAvatars.Avatar3, name: '', type: CONST.ICON_TYPE_AVATAR}, }; -export {Default, AvatarURLStory}; +const SubscriptIcon = Template.bind({}); +SubscriptIcon.args = { + subscriptIcon: {source: Expensicons.DownArrow, width: 8, height: 8}, +}; + +const WorkspaceSubscriptIcon = Template.bind({}); +WorkspaceSubscriptIcon.args = { + mainAvatar: {source: defaultAvatars.Avatar1, name: '', type: CONST.ICON_TYPE_WORKSPACE}, + subscriptIcon: {source: Expensicons.DownArrow, width: 8, height: 8}, +}; + +export {Default, AvatarURLStory, SubscriptIcon, WorkspaceSubscriptIcon}; diff --git a/src/styles/index.ts b/src/styles/index.ts index 713e32eeab45..5c7743ecb01b 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -2149,6 +2149,15 @@ const styles = (theme: ThemeColors) => marginRight: variables.avatarChatSpacing - 4, }, + subscriptIcon: { + position: 'absolute', + bottom: -4, + right: -4, + width: 20, + height: 20, + backgroundColor: theme.buttonDefaultBG, + }, + borderTop: { borderTopWidth: variables.borderTopWidth, borderColor: theme.border, diff --git a/src/styles/theme/themes/dark.ts b/src/styles/theme/themes/dark.ts index 315593a3a8b2..7a94988eb019 100644 --- a/src/styles/theme/themes/dark.ts +++ b/src/styles/theme/themes/dark.ts @@ -12,7 +12,7 @@ const darkTheme = { borderLighter: colors.productDark400, borderFocus: colors.green400, icon: colors.productDark700, - iconMenu: colors.productDark700, + iconMenu: colors.green400, iconHovered: colors.productDark900, iconMenuHovered: colors.green400, iconSuccessFill: colors.green400, @@ -45,7 +45,7 @@ const darkTheme = { hoverComponentBG: colors.productDark300, activeComponentBG: colors.productDark400, signInSidebar: colors.green800, - sidebar: colors.productDark200, + sidebar: colors.productDark100, sidebarHover: colors.productDark300, heading: colors.productDark900, textLight: colors.productDark900, diff --git a/src/styles/theme/themes/light.ts b/src/styles/theme/themes/light.ts index c957bccf6fbe..228917d73d26 100644 --- a/src/styles/theme/themes/light.ts +++ b/src/styles/theme/themes/light.ts @@ -12,7 +12,7 @@ const lightTheme = { borderLighter: colors.productLight400, borderFocus: colors.green400, icon: colors.productLight700, - iconMenu: colors.productLight700, + iconMenu: colors.green400, iconHovered: colors.productLight900, iconMenuHovered: colors.green400, iconSuccessFill: colors.green400, @@ -45,7 +45,7 @@ const lightTheme = { hoverComponentBG: colors.productLight300, activeComponentBG: colors.productLight400, signInSidebar: colors.green800, - sidebar: colors.productLight200, + sidebar: colors.productLight100, sidebarHover: colors.productLight300, heading: colors.productLight900, textLight: colors.white, diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index c4130d9b2475..ab2f42065c72 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -1301,12 +1301,12 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ * @param buttonState - One of {'default', 'hovered', 'pressed'} * @param isMenuIcon - whether this icon is apart of a list */ - getIconFillColor: (buttonState: ButtonStateName = CONST.BUTTON_STATES.DEFAULT, isMenuIcon = false): string => { + getIconFillColor: (buttonState: ButtonStateName = CONST.BUTTON_STATES.DEFAULT, isMenuIcon = false, isPane = false): string => { switch (buttonState) { case CONST.BUTTON_STATES.ACTIVE: case CONST.BUTTON_STATES.PRESSED: - if (isMenuIcon) { - return theme.iconMenuHovered; + if (isPane) { + return theme.iconMenu; } return theme.iconHovered; case CONST.BUTTON_STATES.COMPLETE: @@ -1314,7 +1314,7 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ case CONST.BUTTON_STATES.DEFAULT: case CONST.BUTTON_STATES.DISABLED: default: - if (isMenuIcon) { + if (isMenuIcon && !isPane) { return theme.iconMenu; } return theme.icon; diff --git a/src/styles/utils/spacing.ts b/src/styles/utils/spacing.ts index b2597fc64603..8551bee420ba 100644 --- a/src/styles/utils/spacing.ts +++ b/src/styles/utils/spacing.ts @@ -553,6 +553,10 @@ export default { gap: 20, }, + gap6: { + gap: 24, + }, + gap7: { gap: 28, },