From 57e63066ecf2cb1654002ffb909ca5f8ed9edfaa Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Mon, 4 Mar 2024 12:23:17 +0100 Subject: [PATCH 001/194] Add initial config for MyTripsPage --- src/ROUTES.ts | 2 ++ src/SCREENS.ts | 3 +++ .../BaseCentralPaneNavigator.tsx | 6 +++++- .../BottomTabBar.tsx | 9 +++++---- src/libs/Navigation/linkingConfig/config.ts | 2 ++ src/libs/Navigation/types.ts | 1 + src/pages/Travel/MyTripsPage.tsx | 20 +++++++++++++++++++ 7 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 src/pages/Travel/MyTripsPage.tsx diff --git a/src/ROUTES.ts b/src/ROUTES.ts index e9cdce4f6ed9..f7c455e923a6 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -553,6 +553,8 @@ const ROUTES = { getRoute: (contentType: string, backTo?: string) => getUrlWithBackToParam(`referral/${contentType}`, backTo), }, PROCESS_MONEY_REQUEST_HOLD: 'hold-request-educational', + + TRAVEL_MY_TRIPS: 'travel', } as const; /** diff --git a/src/SCREENS.ts b/src/SCREENS.ts index ff3dbfd7f901..234a24476d2a 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -22,6 +22,9 @@ const SCREENS = { VALIDATE_LOGIN: 'ValidateLogin', UNLINK_LOGIN: 'UnlinkLogin', SETTINGS_CENTRAL_PANE: 'SettingsCentralPane', + TRAVEL: { + MY_TRIPS: 'Travel_MyTrips', + }, SETTINGS: { ROOT: 'Settings_Root', SHARE_CODE: 'Settings_Share_Code', diff --git a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx index 1e5d3639a32f..ab20497a3c73 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx @@ -4,6 +4,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import ReportScreenWrapper from '@libs/Navigation/AppNavigator/ReportScreenWrapper'; import getCurrentUrl from '@libs/Navigation/currentUrl'; import type {CentralPaneNavigatorParamList} from '@navigation/types'; +import MyTripsPage from '@pages/Travel/MyTripsPage'; import SCREENS from '@src/SCREENS'; const Stack = createStackNavigator(); @@ -43,7 +44,10 @@ function BaseCentralPaneNavigator() { initialParams={{openOnAdminRoom: openOnAdminRoom === 'true' || undefined}} component={ReportScreenWrapper} /> - + {Object.entries(workspaceSettingsScreens).map(([screenName, componentGetter]) => ( - interceptAnonymousUser(() => - activeWorkspaceID ? Navigation.navigate(ROUTES.WORKSPACE_INITIAL.getRoute(activeWorkspaceID)) : Navigation.navigate(ROUTES.ALL_SETTINGS), - ) + onPress={ + () => Navigation.navigate(ROUTES.TRAVEL_MY_TRIPS) + // interceptAnonymousUser(() => + // activeWorkspaceID ? Navigation.navigate(ROUTES.WORKSPACE_INITIAL.getRoute(activeWorkspaceID)) : Navigation.navigate(ROUTES.ALL_SETTINGS), + // ) } role={CONST.ROLE.BUTTON} accessibilityLabel={translate('common.settings')} diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 276829e8c691..3515f17f3867 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -67,6 +67,8 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.CATEGORIES]: { path: ROUTES.WORKSPACE_CATEGORIES.route, }, + + [SCREENS.TRAVEL.MY_TRIPS]: ROUTES.TRAVEL_MY_TRIPS, }, }, [SCREENS.NOT_FOUND]: '*', diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index a1e558869ebe..03d6bbeb5cca 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -90,6 +90,7 @@ type CentralPaneNavigatorParamList = { [SCREENS.WORKSPACE.CATEGORIES]: { policyID: string; }; + [SCREENS.TRAVEL.MY_TRIPS]: undefined; }; type WorkspaceSwitcherNavigatorParamList = { diff --git a/src/pages/Travel/MyTripsPage.tsx b/src/pages/Travel/MyTripsPage.tsx new file mode 100644 index 000000000000..1983332c4e10 --- /dev/null +++ b/src/pages/Travel/MyTripsPage.tsx @@ -0,0 +1,20 @@ +import type {StackScreenProps} from '@react-navigation/stack'; +import React from 'react'; +import {View} from 'react-native'; +import Text from '@components/Text'; +import type {CentralPaneNavigatorParamList} from '@navigation/types'; +import type SCREENS from '@src/SCREENS'; + +type MyTripsPageProps = StackScreenProps; + +function MyTripsPage({route}: MyTripsPageProps) { + return ( + + MyTripsPage + + ); +} + +MyTripsPage.displayName = 'MyTripsPage'; + +export default MyTripsPage; From 3e95765db62e01513f27bfaf32161a4dd6598ec4 Mon Sep 17 00:00:00 2001 From: smelaa Date: Mon, 4 Mar 2024 15:50:52 +0100 Subject: [PATCH 002/194] ManageTrips init --- src/languages/en.ts | 10 +++++++ src/languages/es.ts | 10 +++++++ src/pages/Travel/ManageTrips.tsx | 47 ++++++++++++++++++++++++++++++++ src/pages/Travel/MyTripsPage.tsx | 27 ++++++++++++++---- 4 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 src/pages/Travel/ManageTrips.tsx diff --git a/src/languages/en.ts b/src/languages/en.ts index bd57843e9245..2b483de57c93 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1717,6 +1717,16 @@ export default { session: { offlineMessageRetry: "Looks like you're offline. Please check your connection and try again.", }, + travel: { + header: 'My trips', + title: 'Book or manage your trips', + subtitle: 'Use Expensify Travel to get the best travel offers and manage all your business expenses in a single place.', + bookOrManage: 'Book or manage', + features: { + saveMoney: 'Save money on your bookings', + alerts: 'Get real time alerts if your travel plans change', + }, + }, workspace: { common: { card: 'Cards', diff --git a/src/languages/es.ts b/src/languages/es.ts index 83ed2ca1c89c..d0ca18d0aead 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1741,6 +1741,16 @@ export default { session: { offlineMessageRetry: 'Parece que estás desconectado. Por favor, comprueba tu conexión e inténtalo de nuevo.', }, + travel: { + header: 'Mis viajes', + title: 'Reserva o gestiona tus viajes', + subtitle: 'Utiliza Expensify Travel para obtener las mejores ofertas de viaje y gestionar todos tus gastos de negocio en un solo lugar.', + bookOrManage: 'Reservar o gestionar', + features: { + saveMoney: 'Ahorra dinero en tus reservas', + alerts: 'Recibe alertas en tiempo real si cambian tus planes de viaje', + }, + }, workspace: { common: { card: 'Tarjetas', diff --git a/src/pages/Travel/ManageTrips.tsx b/src/pages/Travel/ManageTrips.tsx new file mode 100644 index 000000000000..3cd09c8e98d2 --- /dev/null +++ b/src/pages/Travel/ManageTrips.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import {ScrollView, View} from 'react-native'; +import type {FeatureListItem} from '@components/FeatureList'; +import FeatureList from '@components/FeatureList'; +import * as Illustrations from '@components/Icon/Illustrations'; +import LottieAnimations from '@components/LottieAnimations'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import useWindowDimensions from '@hooks/useWindowDimensions'; +import App from '@src/App'; + +const tripsFeatures: FeatureListItem[] = [ + { + icon: Illustrations.MoneyReceipts, + translationKey: 'travel.features.saveMoney', + }, + { + icon: Illustrations.CreditCardsNew, + translationKey: 'travel.features.alerts', + }, +]; + +function ManageTrips() { + const styles = useThemeStyles(); + const {isSmallScreenWidth} = useWindowDimensions(); + const {translate} = useLocalize(); + + return ( + + + console.log('pressed')} + illustration={LottieAnimations.SaveTheWorld} + /> + + + ); +} + +ManageTrips.displayName = 'ManageTrips'; + +export default ManageTrips; diff --git a/src/pages/Travel/MyTripsPage.tsx b/src/pages/Travel/MyTripsPage.tsx index 1983332c4e10..ae9e604cc177 100644 --- a/src/pages/Travel/MyTripsPage.tsx +++ b/src/pages/Travel/MyTripsPage.tsx @@ -1,17 +1,34 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React from 'react'; -import {View} from 'react-native'; -import Text from '@components/Text'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import * as Illustrations from '@components/Icon/Illustrations'; +import ScreenWrapper from '@components/ScreenWrapper'; +import useLocalize from '@hooks/useLocalize'; +import Navigation from '@libs/Navigation/Navigation'; import type {CentralPaneNavigatorParamList} from '@navigation/types'; import type SCREENS from '@src/SCREENS'; +import ManageTrips from './ManageTrips'; type MyTripsPageProps = StackScreenProps; function MyTripsPage({route}: MyTripsPageProps) { + const {translate} = useLocalize(); return ( - - MyTripsPage - + + Navigation.goBack()} + /> + + ); } From 011d2273613ae5e72108af56212456267e2fef3a Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Mon, 4 Mar 2024 15:55:15 +0100 Subject: [PATCH 003/194] Add TravelMenu page --- src/ROUTES.ts | 2 + src/SCREENS.ts | 1 + src/languages/en.ts | 3 + src/languages/es.ts | 3 + .../Navigators/BottomTabNavigator.tsx | 6 ++ .../BottomTabBar.tsx | 29 +++++-- .../TAB_TO_CENTRAL_PANE_MAPPING.ts | 1 + src/libs/Navigation/linkingConfig/config.ts | 1 + src/libs/Navigation/types.ts | 1 + src/pages/Travel/TravelMenu.tsx | 86 +++++++++++++++++++ 10 files changed, 127 insertions(+), 6 deletions(-) create mode 100644 src/pages/Travel/TravelMenu.tsx diff --git a/src/ROUTES.ts b/src/ROUTES.ts index f7c455e923a6..4496d102d037 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -555,6 +555,8 @@ const ROUTES = { PROCESS_MONEY_REQUEST_HOLD: 'hold-request-educational', TRAVEL_MY_TRIPS: 'travel', + + TRAVEL_HOME: 'travel-home', } as const; /** diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 234a24476d2a..b08f8efabf45 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -23,6 +23,7 @@ const SCREENS = { UNLINK_LOGIN: 'UnlinkLogin', SETTINGS_CENTRAL_PANE: 'SettingsCentralPane', TRAVEL: { + HOME: 'Travel_Home', MY_TRIPS: 'Travel_MyTrips', }, SETTINGS: { diff --git a/src/languages/en.ts b/src/languages/en.ts index bd57843e9245..1a7c08371f27 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2427,4 +2427,7 @@ export default { offline: "You appear to be offline. Unfortunately, Expensify Classic doesn't work offline, but New Expensify does. If you prefer to use Expensify Classic, try again when you have an internet connection.", }, + travel: { + myTrips: 'My trips', + }, } satisfies TranslationBase; diff --git a/src/languages/es.ts b/src/languages/es.ts index 83ed2ca1c89c..ff6cc2b03e9c 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2919,4 +2919,7 @@ export default { offline: 'Parece que estás desconectado. Desafortunadamente, Expensify Classic no funciona sin conexión, pero New Expensify sí. Si prefieres utilizar Expensify Classic, inténtalo de nuevo cuando tengas conexión a internet.', }, + travel: { + myTrips: 'Mis viajes', + }, } satisfies EnglishTranslation; diff --git a/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx index ce03a8d5bcba..9ce1b8425f51 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx @@ -11,6 +11,8 @@ import ActiveRouteContext from './ActiveRouteContext'; const loadWorkspaceInitialPage = () => require('../../../../pages/workspace/WorkspaceInitialPage').default as React.ComponentType; +const loadTravelMenuPage = () => require('../../../../pages/Travel/TravelMenu').default as React.ComponentType; + const Tab = createCustomBottomTabNavigator(); const screenOptions: StackNavigationOptions = { @@ -35,6 +37,10 @@ function BottomTabNavigator() { name={SCREENS.WORKSPACE.INITIAL} getComponent={loadWorkspaceInitialPage} /> + ); diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx index 39435fb6287d..6e2595c515eb 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx @@ -91,14 +91,30 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps - + + Navigation.navigate(ROUTES.TRAVEL_MY_TRIPS)} + role={CONST.ROLE.BUTTON} + accessibilityLabel={translate('workspace.common.travel')} + wrapperStyle={styles.flexGrow1} + style={styles.bottomTabBarItem} + > + + + + + Navigation.navigate(ROUTES.TRAVEL_MY_TRIPS) - // interceptAnonymousUser(() => - // activeWorkspaceID ? Navigation.navigate(ROUTES.WORKSPACE_INITIAL.getRoute(activeWorkspaceID)) : Navigation.navigate(ROUTES.ALL_SETTINGS), - // ) + onPress={() => + interceptAnonymousUser(() => + activeWorkspaceID ? Navigation.navigate(ROUTES.WORKSPACE_INITIAL.getRoute(activeWorkspaceID)) : Navigation.navigate(ROUTES.ALL_SETTINGS), + ) } role={CONST.ROLE.BUTTON} accessibilityLabel={translate('common.settings')} @@ -116,6 +132,7 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps + ); } diff --git a/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts b/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts index f4316009b70b..94945fc2aa54 100755 --- a/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts @@ -15,6 +15,7 @@ const TAB_TO_CENTRAL_PANE_MAPPING: Record = { SCREENS.WORKSPACE.MEMBERS, SCREENS.WORKSPACE.CATEGORIES, ], + [SCREENS.TRAVEL.HOME]: [SCREENS.TRAVEL.MY_TRIPS], }; const generateCentralPaneToTabMapping = (): Record => { diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 3515f17f3867..babe4862bfeb 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -34,6 +34,7 @@ const config: LinkingOptions['config'] = { path: ROUTES.WORKSPACE_INITIAL.route, exact: true, }, + [SCREENS.TRAVEL.HOME]: ROUTES.TRAVEL_HOME, }, }, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 03d6bbeb5cca..daaf867f16c2 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -514,6 +514,7 @@ type BottomTabNavigatorParamList = { [SCREENS.HOME]: undefined; [SCREENS.ALL_SETTINGS]: undefined; [SCREENS.WORKSPACE.INITIAL]: undefined; + [SCREENS.TRAVEL.HOME]: undefined; }; type PublicScreensParamList = { diff --git a/src/pages/Travel/TravelMenu.tsx b/src/pages/Travel/TravelMenu.tsx new file mode 100644 index 000000000000..19a5a61991b4 --- /dev/null +++ b/src/pages/Travel/TravelMenu.tsx @@ -0,0 +1,86 @@ +import type {StackScreenProps} from '@react-navigation/stack'; +import React, {useMemo} from 'react'; +import {ScrollView} from 'react-native'; +import Breadcrumbs from '@components/Breadcrumbs'; +import * as Expensicons from '@components/Icon/Expensicons'; +import MenuItemList from '@components/MenuItemList'; +import ScreenWrapper from '@components/ScreenWrapper'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import useWaitForNavigation from '@hooks/useWaitForNavigation'; +import useWindowDimensions from '@hooks/useWindowDimensions'; +import Navigation from '@libs/Navigation/Navigation'; +import type {BottomTabNavigatorParamList} from '@navigation/types'; +import CONST from '@src/CONST'; +import type {TranslationPaths} from '@src/languages/types'; +import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; + +type TravelMenuProps = StackScreenProps; + +function TravelMenu({route}: TravelMenuProps) { + const styles = useThemeStyles(); + const waitForNavigate = useWaitForNavigation(); + const {translate} = useLocalize(); + const {isSmallScreenWidth} = useWindowDimensions(); + + /** + * Retuns a list of menu items data for Travel menu + * @returns {Object} object with translationKey, style and items + */ + const menuItems = useMemo(() => { + const baseMenuItems = [ + { + translationKey: 'travel.myTrips', + icon: Expensicons.Luggage, + action: () => { + waitForNavigate(() => { + Navigation.navigate(ROUTES.TRAVEL_MY_TRIPS); + })(); + }, + focused: !isSmallScreenWidth, + }, + ]; + return baseMenuItems.map((item) => ({ + key: item.translationKey, + title: translate(item.translationKey as TranslationPaths), + icon: item.icon, + onPress: item.action, + wrapperStyle: styles.sectionMenuItem, + isPaneMenu: true, + focused: item.focused, + hoverAndPressStyle: styles.hoveredComponentBG, + })); + }, [isSmallScreenWidth, styles.hoveredComponentBG, styles.sectionMenuItem, translate, waitForNavigate]); + + return ( + + + + + + + ); +} + +TravelMenu.displayName = 'TravelMenu'; + +export default TravelMenu; From 0bf79160068973dad3dd71df6098e8eb364275da Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Tue, 5 Mar 2024 12:38:43 +0100 Subject: [PATCH 004/194] Add suitcase icon to Expensicons --- assets/images/suitcase.svg | 3 +++ src/components/Icon/Expensicons.ts | 2 ++ .../createCustomBottomTabNavigator/BottomTabBar.tsx | 2 +- src/pages/Travel/TravelMenu.tsx | 2 +- 4 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 assets/images/suitcase.svg diff --git a/assets/images/suitcase.svg b/assets/images/suitcase.svg new file mode 100644 index 000000000000..97036db6b5ac --- /dev/null +++ b/assets/images/suitcase.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts index 6c6c1b86eee1..4eaeff4b15ac 100644 --- a/src/components/Icon/Expensicons.ts +++ b/src/components/Icon/Expensicons.ts @@ -134,6 +134,7 @@ import Podcast from '@assets/images/social-podcast.svg'; import Twitter from '@assets/images/social-twitter.svg'; import Youtube from '@assets/images/social-youtube.svg'; import Stopwatch from '@assets/images/stopwatch.svg'; +import Suitcase from '@assets/images/suitcase.svg'; import Sync from '@assets/images/sync.svg'; import Task from '@assets/images/task.svg'; import ThreeDots from '@assets/images/three-dots.svg'; @@ -280,6 +281,7 @@ export { Send, Shield, Stopwatch, + Suitcase, Sync, Task, ThumbsUp, diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx index 6e2595c515eb..f3911f84ce05 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx @@ -101,7 +101,7 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps > { waitForNavigate(() => { Navigation.navigate(ROUTES.TRAVEL_MY_TRIPS); From d2cd60cd1921bb5851a6158499e5054d13583dee Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Tue, 5 Mar 2024 13:44:07 +0100 Subject: [PATCH 005/194] Add simple illustrations for MyTripsPage --- .../simple-illustration__alert.svg | 15 ++++++ .../simple-illustration__piggybank.svg | 50 +++++++++++++++++++ src/components/Icon/Illustrations.ts | 4 ++ src/pages/Travel/ManageTrips.tsx | 7 +-- src/pages/Travel/MyTripsPage.tsx | 7 ++- 5 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 assets/images/simple-illustrations/simple-illustration__alert.svg create mode 100644 assets/images/simple-illustrations/simple-illustration__piggybank.svg diff --git a/assets/images/simple-illustrations/simple-illustration__alert.svg b/assets/images/simple-illustrations/simple-illustration__alert.svg new file mode 100644 index 000000000000..55429cf39b8f --- /dev/null +++ b/assets/images/simple-illustrations/simple-illustration__alert.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/assets/images/simple-illustrations/simple-illustration__piggybank.svg b/assets/images/simple-illustrations/simple-illustration__piggybank.svg new file mode 100644 index 000000000000..c89eb3b342ef --- /dev/null +++ b/assets/images/simple-illustrations/simple-illustration__piggybank.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Icon/Illustrations.ts b/src/components/Icon/Illustrations.ts index f8c048ebc4c0..728400f95bb3 100644 --- a/src/components/Icon/Illustrations.ts +++ b/src/components/Icon/Illustrations.ts @@ -29,6 +29,7 @@ import TadaYellow from '@assets/images/product-illustrations/tada--yellow.svg'; import TeleScope from '@assets/images/product-illustrations/telescope.svg'; import ThreeLeggedLaptopWoman from '@assets/images/product-illustrations/three_legged_laptop_woman.svg'; import ToddBehindCloud from '@assets/images/product-illustrations/todd-behind-cloud.svg'; +import Alert from '@assets/images/simple-illustrations/simple-illustration__alert.svg'; import Approval from '@assets/images/simple-illustrations/simple-illustration__approval.svg'; import BankArrow from '@assets/images/simple-illustrations/simple-illustration__bank-arrow.svg'; import BigRocket from '@assets/images/simple-illustrations/simple-illustration__bigrocket.svg'; @@ -58,6 +59,7 @@ import MoneyIntoWallet from '@assets/images/simple-illustrations/simple-illustra import MoneyWings from '@assets/images/simple-illustrations/simple-illustration__moneywings.svg'; import OpenSafe from '@assets/images/simple-illustrations/simple-illustration__opensafe.svg'; import PalmTree from '@assets/images/simple-illustrations/simple-illustration__palmtree.svg'; +import PiggyBank from '@assets/images/simple-illustrations/simple-illustration__piggybank.svg'; import Profile from '@assets/images/simple-illustrations/simple-illustration__profile.svg'; import QRCode from '@assets/images/simple-illustrations/simple-illustration__qr-code.svg'; import ReceiptEnvelope from '@assets/images/simple-illustrations/simple-illustration__receipt-envelope.svg'; @@ -146,4 +148,6 @@ export { Workflows, ThreeLeggedLaptopWoman, House, + PiggyBank, + Alert, }; diff --git a/src/pages/Travel/ManageTrips.tsx b/src/pages/Travel/ManageTrips.tsx index 3cd09c8e98d2..fd608374b4f4 100644 --- a/src/pages/Travel/ManageTrips.tsx +++ b/src/pages/Travel/ManageTrips.tsx @@ -7,15 +7,15 @@ import LottieAnimations from '@components/LottieAnimations'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import App from '@src/App'; +import colors from '@styles/theme/colors'; const tripsFeatures: FeatureListItem[] = [ { - icon: Illustrations.MoneyReceipts, + icon: Illustrations.PiggyBank, translationKey: 'travel.features.saveMoney', }, { - icon: Illustrations.CreditCardsNew, + icon: Illustrations.Alert, translationKey: 'travel.features.alerts', }, ]; @@ -36,6 +36,7 @@ function ManageTrips() { ctaAccessibilityLabel={translate('travel.bookOrManage')} onCtaPress={() => console.log('pressed')} illustration={LottieAnimations.SaveTheWorld} + illustrationBackgroundColor={colors.blue600} /> diff --git a/src/pages/Travel/MyTripsPage.tsx b/src/pages/Travel/MyTripsPage.tsx index ae9e604cc177..a89a0d2a60da 100644 --- a/src/pages/Travel/MyTripsPage.tsx +++ b/src/pages/Travel/MyTripsPage.tsx @@ -4,6 +4,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Illustrations from '@components/Icon/Illustrations'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; +import useWindowDimensions from '@hooks/useWindowDimensions'; import Navigation from '@libs/Navigation/Navigation'; import type {CentralPaneNavigatorParamList} from '@navigation/types'; import type SCREENS from '@src/SCREENS'; @@ -13,6 +14,8 @@ type MyTripsPageProps = StackScreenProps Navigation.goBack()} /> From a467a7403fe662f23e9e31db16b6e50e88907a61 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Tue, 5 Mar 2024 14:53:47 +0100 Subject: [PATCH 006/194] Remove TravelMenuProps --- src/pages/Travel/TravelMenu.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/pages/Travel/TravelMenu.tsx b/src/pages/Travel/TravelMenu.tsx index 5c0830549f56..7edb2d37da24 100644 --- a/src/pages/Travel/TravelMenu.tsx +++ b/src/pages/Travel/TravelMenu.tsx @@ -1,4 +1,3 @@ -import type {StackScreenProps} from '@react-navigation/stack'; import React, {useMemo} from 'react'; import {ScrollView} from 'react-native'; import Breadcrumbs from '@components/Breadcrumbs'; @@ -10,15 +9,13 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWaitForNavigation from '@hooks/useWaitForNavigation'; import useWindowDimensions from '@hooks/useWindowDimensions'; import Navigation from '@libs/Navigation/Navigation'; -import type {BottomTabNavigatorParamList} from '@navigation/types'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ROUTES from '@src/ROUTES'; -import type SCREENS from '@src/SCREENS'; -type TravelMenuProps = StackScreenProps; +// type TravelMenuProps = StackScreenProps; -function TravelMenu({route}: TravelMenuProps) { +function TravelMenu() { const styles = useThemeStyles(); const waitForNavigate = useWaitForNavigation(); const {translate} = useLocalize(); From 4cb11200f50d5fc35664b6b3824d170f31cfa86e Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Tue, 5 Mar 2024 15:45:08 +0100 Subject: [PATCH 007/194] Add fixes to travel page --- src/languages/en.ts | 3 --- .../createCustomBottomTabNavigator/BottomTabBar.tsx | 2 +- src/pages/Travel/TravelMenu.tsx | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 34314e1e65d8..2b483de57c93 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2437,7 +2437,4 @@ export default { offline: "You appear to be offline. Unfortunately, Expensify Classic doesn't work offline, but New Expensify does. If you prefer to use Expensify Classic, try again when you have an internet connection.", }, - travel: { - myTrips: 'My trips', - }, } satisfies TranslationBase; diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx index f3911f84ce05..5eaf56eb7edd 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx @@ -93,7 +93,7 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps Navigation.navigate(ROUTES.TRAVEL_MY_TRIPS)} + onPress={() => Navigation.navigate(ROUTES.TRAVEL_HOME)} role={CONST.ROLE.BUTTON} accessibilityLabel={translate('workspace.common.travel')} wrapperStyle={styles.flexGrow1} diff --git a/src/pages/Travel/TravelMenu.tsx b/src/pages/Travel/TravelMenu.tsx index 7edb2d37da24..12fca5d0c587 100644 --- a/src/pages/Travel/TravelMenu.tsx +++ b/src/pages/Travel/TravelMenu.tsx @@ -28,7 +28,7 @@ function TravelMenu() { const menuItems = useMemo(() => { const baseMenuItems = [ { - translationKey: 'travel.myTrips', + translationKey: 'travel.header', icon: Expensicons.Suitcase, action: () => { waitForNavigate(() => { From ec06f1646d3de6329f2ff5fb43b0652c48a7d6f1 Mon Sep 17 00:00:00 2001 From: smelaa Date: Wed, 6 Mar 2024 10:32:34 +0100 Subject: [PATCH 008/194] Add main illustration --- assets/animations/Plane.lottie | Bin 0 -> 26027 bytes src/components/LottieAnimations/index.tsx | 5 +++++ src/pages/Travel/ManageTrips.tsx | 3 ++- src/styles/index.ts | 5 +++++ 4 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 assets/animations/Plane.lottie diff --git a/assets/animations/Plane.lottie b/assets/animations/Plane.lottie new file mode 100644 index 0000000000000000000000000000000000000000..77a7c0f5dfa8a734b8e0b6c7cb021088ecf3674c GIT binary patch literal 26027 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 literal 0 HcmV?d00001 diff --git a/src/components/LottieAnimations/index.tsx b/src/components/LottieAnimations/index.tsx index 18cb9188d60c..598819e19361 100644 --- a/src/components/LottieAnimations/index.tsx +++ b/src/components/LottieAnimations/index.tsx @@ -67,6 +67,11 @@ const DotLottieAnimations = { w: 200, h: 120, }, + Plane: { + file: require('@assets/animations/Plane.lottie'), + w: 180, + h: 200, + }, } satisfies Record; export default DotLottieAnimations; diff --git a/src/pages/Travel/ManageTrips.tsx b/src/pages/Travel/ManageTrips.tsx index fd608374b4f4..30c2f1151492 100644 --- a/src/pages/Travel/ManageTrips.tsx +++ b/src/pages/Travel/ManageTrips.tsx @@ -35,7 +35,8 @@ function ManageTrips() { ctaText={translate('travel.bookOrManage')} ctaAccessibilityLabel={translate('travel.bookOrManage')} onCtaPress={() => console.log('pressed')} - illustration={LottieAnimations.SaveTheWorld} + illustration={LottieAnimations.Plane} + illustrationStyle={styles.travelIllustrationStyle} illustrationBackgroundColor={colors.blue600} /> diff --git a/src/styles/index.ts b/src/styles/index.ts index 405a05cfce78..d16e769f95b8 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -1769,6 +1769,11 @@ const styles = (theme: ThemeColors) => marginBottom: -20, }, + travelIllustrationStyle: { + marginTop: 20, + marginBottom: -20, + }, + overlayStyles: (current: OverlayStylesParams, isModalOnTheLeft: boolean) => ({ ...positioning.pFixed, From bb77b15c4833a6185d0b0c279ef45402b4360f66 Mon Sep 17 00:00:00 2001 From: smelaa Date: Thu, 7 Mar 2024 14:04:36 +0100 Subject: [PATCH 009/194] Add beta visibility --- src/CONST.ts | 1 + src/libs/Permissions.ts | 5 +++++ src/pages/Travel/MyTripsPage.tsx | 21 +++++++++++++++++---- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 9ed2903941b6..fa33b2578a54 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -309,6 +309,7 @@ const CONST = { VIOLATIONS: 'violations', REPORT_FIELDS: 'reportFields', WORKFLOWS_DELAYED_SUBMISSION: 'workflowsDelayedSubmission', + SPOTNANA_TRAVEL: 'spotnanaTravel', }, BUTTON_STATES: { DEFAULT: 'default', diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index c9f386f5bd7a..b3d7c302234d 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -30,6 +30,10 @@ function canUseWorkflowsDelayedSubmission(betas: OnyxEntry): boolean { return !!betas?.includes(CONST.BETAS.WORKFLOWS_DELAYED_SUBMISSION) || canUseAllBetas(betas); } +function canSeeTravelPage(betas: OnyxEntry): boolean { + return !!betas?.includes(CONST.BETAS.SPOTNANA_TRAVEL) || canUseAllBetas(betas); +} + /** * Link previews are temporarily disabled. */ @@ -45,4 +49,5 @@ export default { canUseViolations, canUseReportFields, canUseWorkflowsDelayedSubmission, + canSeeTravelPage, }; diff --git a/src/pages/Travel/MyTripsPage.tsx b/src/pages/Travel/MyTripsPage.tsx index a89a0d2a60da..e82a73dd5247 100644 --- a/src/pages/Travel/MyTripsPage.tsx +++ b/src/pages/Travel/MyTripsPage.tsx @@ -1,20 +1,29 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React from 'react'; +import {withOnyx} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; +import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Illustrations from '@components/Icon/Illustrations'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useWindowDimensions from '@hooks/useWindowDimensions'; import Navigation from '@libs/Navigation/Navigation'; +import Permissions from '@libs/Permissions'; import type {CentralPaneNavigatorParamList} from '@navigation/types'; +import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; +import type {Beta} from '@src/types/onyx'; import ManageTrips from './ManageTrips'; -type MyTripsPageProps = StackScreenProps; +type MyTripsPageOnyxProps = {betas: OnyxEntry}; -function MyTripsPage({route}: MyTripsPageProps) { +type MyTripsPageProps = StackScreenProps & MyTripsPageOnyxProps; + +function MyTripsPage({betas}: MyTripsPageProps) { const {translate} = useLocalize(); const {isSmallScreenWidth} = useWindowDimensions(); + const canSeeTravelPage = Permissions.canSeeTravelPage(betas); return ( Navigation.goBack()} /> - + {canSeeTravelPage ? : } ); } MyTripsPage.displayName = 'MyTripsPage'; -export default MyTripsPage; +export default withOnyx({ + betas: { + key: ONYXKEYS.BETAS, + }, +})(MyTripsPage); From ff03721e9dfba525fa5f4329a97579372cac3d10 Mon Sep 17 00:00:00 2001 From: smelaa Date: Thu, 7 Mar 2024 14:10:54 +0100 Subject: [PATCH 010/194] Wrap ManageTrips in FullPageNotFoundView --- src/pages/Travel/MyTripsPage.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/Travel/MyTripsPage.tsx b/src/pages/Travel/MyTripsPage.tsx index e82a73dd5247..ddef2f303318 100644 --- a/src/pages/Travel/MyTripsPage.tsx +++ b/src/pages/Travel/MyTripsPage.tsx @@ -39,7 +39,9 @@ function MyTripsPage({betas}: MyTripsPageProps) { shouldShowBackButton={isSmallScreenWidth} onBackButtonPress={() => Navigation.goBack()} /> - {canSeeTravelPage ? : } + + + ); } From 7c30e70bc77b637bfc3a59fba430ebc839875aee Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Fri, 8 Mar 2024 13:03:36 +0100 Subject: [PATCH 011/194] Refactor MyTripsPage --- src/ROUTES.ts | 2 - src/SCREENS.ts | 2 +- src/languages/en.ts | 1 + src/languages/es.ts | 4 +- .../AppNavigator/ModalStackNavigators.tsx | 6 ++ .../Navigators/BottomTabNavigator.tsx | 6 -- .../BaseCentralPaneNavigator.tsx | 5 -- .../Navigators/RightModalNavigator.tsx | 4 + .../BottomTabBar.tsx | 24 +----- .../TAB_TO_CENTRAL_PANE_MAPPING.ts | 1 - src/libs/Navigation/linkingConfig/config.ts | 9 +- src/libs/Navigation/types.ts | 8 +- src/libs/Permissions.ts | 4 +- src/pages/Travel/ManageTrips.tsx | 2 +- src/pages/Travel/MyTripsPage.tsx | 42 +++------- src/pages/Travel/TravelMenu.tsx | 83 ------------------- .../FloatingActionButtonAndPopover.js | 12 +++ 17 files changed, 56 insertions(+), 159 deletions(-) delete mode 100644 src/pages/Travel/TravelMenu.tsx diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 4496d102d037..f7c455e923a6 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -555,8 +555,6 @@ const ROUTES = { PROCESS_MONEY_REQUEST_HOLD: 'hold-request-educational', TRAVEL_MY_TRIPS: 'travel', - - TRAVEL_HOME: 'travel-home', } as const; /** diff --git a/src/SCREENS.ts b/src/SCREENS.ts index b08f8efabf45..b30ee4d45ec3 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -23,7 +23,6 @@ const SCREENS = { UNLINK_LOGIN: 'UnlinkLogin', SETTINGS_CENTRAL_PANE: 'SettingsCentralPane', TRAVEL: { - HOME: 'Travel_Home', MY_TRIPS: 'Travel_MyTrips', }, SETTINGS: { @@ -127,6 +126,7 @@ const SCREENS = { ROOM_INVITE: 'RoomInvite', REFERRAL: 'Referral', PROCESS_MONEY_REQUEST_HOLD: 'ProcessMoneyRequestHold', + TRAVEL: 'Travel', }, SIGN_IN_WITH_APPLE_DESKTOP: 'AppleSignInDesktop', SIGN_IN_WITH_GOOGLE_DESKTOP: 'GoogleSignInDesktop', diff --git a/src/languages/en.ts b/src/languages/en.ts index 2b483de57c93..65a3be6a2cfb 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1726,6 +1726,7 @@ export default { saveMoney: 'Save money on your bookings', alerts: 'Get real time alerts if your travel plans change', }, + bookTravel: 'Book Travel', }, workspace: { common: { diff --git a/src/languages/es.ts b/src/languages/es.ts index ca6cc81e8705..35707cf7424e 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1750,6 +1750,7 @@ export default { saveMoney: 'Ahorra dinero en tus reservas', alerts: 'Recibe alertas en tiempo real si cambian tus planes de viaje', }, + bookTravel: 'Reservar viajes', }, workspace: { common: { @@ -2929,7 +2930,4 @@ export default { offline: 'Parece que estás desconectado. Desafortunadamente, Expensify Classic no funciona sin conexión, pero New Expensify sí. Si prefieres utilizar Expensify Classic, inténtalo de nuevo cuando tengas conexión a internet.', }, - travel: { - myTrips: 'Mis viajes', - }, } satisfies EnglishTranslation; diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index 545641957c9a..ed3e34e76c1c 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -29,6 +29,7 @@ import type { SplitDetailsNavigatorParamList, TaskDetailsNavigatorParamList, TeachersUniteNavigatorParamList, + TravelNavigatorParamList, WalletStatementNavigatorParamList, WorkspaceSwitcherNavigatorParamList, } from '@navigation/types'; @@ -108,6 +109,10 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator require('../../../pages/EditRequestReceiptPage').default as React.ComponentType, }); +const TravelModalStackNavigator = createModalStackNavigator({ + [SCREENS.TRAVEL.MY_TRIPS]: () => require('../../../pages/Travel/MyTripsPage').default as React.ComponentType, +}); + const SplitDetailsModalStackNavigator = createModalStackNavigator({ [SCREENS.SPLIT_DETAILS.ROOT]: () => require('../../../pages/iou/SplitBillDetailsPage').default as React.ComponentType, [SCREENS.SPLIT_DETAILS.EDIT_REQUEST]: () => require('../../../pages/EditSplitBillPage').default as React.ComponentType, @@ -336,4 +341,5 @@ export { TaskModalStackNavigator, WalletStatementStackNavigator, ProcessMoneyRequestHoldStackNavigator, + TravelModalStackNavigator, }; diff --git a/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx index 9ce1b8425f51..ce03a8d5bcba 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx @@ -11,8 +11,6 @@ import ActiveRouteContext from './ActiveRouteContext'; const loadWorkspaceInitialPage = () => require('../../../../pages/workspace/WorkspaceInitialPage').default as React.ComponentType; -const loadTravelMenuPage = () => require('../../../../pages/Travel/TravelMenu').default as React.ComponentType; - const Tab = createCustomBottomTabNavigator(); const screenOptions: StackNavigationOptions = { @@ -37,10 +35,6 @@ function BottomTabNavigator() { name={SCREENS.WORKSPACE.INITIAL} getComponent={loadWorkspaceInitialPage} /> - ); diff --git a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx index ab20497a3c73..5f3d522f6b41 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx @@ -4,7 +4,6 @@ import useThemeStyles from '@hooks/useThemeStyles'; import ReportScreenWrapper from '@libs/Navigation/AppNavigator/ReportScreenWrapper'; import getCurrentUrl from '@libs/Navigation/currentUrl'; import type {CentralPaneNavigatorParamList} from '@navigation/types'; -import MyTripsPage from '@pages/Travel/MyTripsPage'; import SCREENS from '@src/SCREENS'; const Stack = createStackNavigator(); @@ -44,10 +43,6 @@ function BaseCentralPaneNavigator() { initialParams={{openOnAdminRoom: openOnAdminRoom === 'true' || undefined}} component={ReportScreenWrapper} /> - {Object.entries(workspaceSettingsScreens).map(([screenName, componentGetter]) => ( + diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx index 5eaf56eb7edd..58d9efb43df5 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx @@ -11,6 +11,7 @@ import useActiveWorkspace from '@hooks/useActiveWorkspace'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import * as Session from '@libs/actions/Session'; import interceptAnonymousUser from '@libs/interceptAnonymousUser'; import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute'; import Navigation from '@libs/Navigation/Navigation'; @@ -47,7 +48,8 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps // When we are redirected to the Settings tab from the OldDot, we don't want to call the Welcome.show() method. // To prevent this, the value of the bottomTabRoute?.name is checked here bottomTabRoute?.name === SCREENS.WORKSPACE.INITIAL || - (currentRoute && currentRoute.name !== NAVIGATORS.BOTTOM_TAB_NAVIGATOR && currentRoute.name !== NAVIGATORS.CENTRAL_PANE_NAVIGATOR) + Boolean(currentRoute && currentRoute.name !== NAVIGATORS.BOTTOM_TAB_NAVIGATOR && currentRoute.name !== NAVIGATORS.CENTRAL_PANE_NAVIGATOR) || + Session.isAnonymousUser() ) { return; } @@ -91,24 +93,7 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps - - Navigation.navigate(ROUTES.TRAVEL_HOME)} - role={CONST.ROLE.BUTTON} - accessibilityLabel={translate('workspace.common.travel')} - wrapperStyle={styles.flexGrow1} - style={styles.bottomTabBarItem} - > - - - - - + @@ -132,7 +117,6 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps - ); } diff --git a/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts b/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts index 94945fc2aa54..f4316009b70b 100755 --- a/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts @@ -15,7 +15,6 @@ const TAB_TO_CENTRAL_PANE_MAPPING: Record = { SCREENS.WORKSPACE.MEMBERS, SCREENS.WORKSPACE.CATEGORIES, ], - [SCREENS.TRAVEL.HOME]: [SCREENS.TRAVEL.MY_TRIPS], }; const generateCentralPaneToTabMapping = (): Record => { diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index babe4862bfeb..c7a259513885 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -34,14 +34,12 @@ const config: LinkingOptions['config'] = { path: ROUTES.WORKSPACE_INITIAL.route, exact: true, }, - [SCREENS.TRAVEL.HOME]: ROUTES.TRAVEL_HOME, }, }, [NAVIGATORS.CENTRAL_PANE_NAVIGATOR]: { screens: { [SCREENS.REPORT]: ROUTES.REPORT_WITH_ID.route, - [SCREENS.SETTINGS.WORKSPACES]: ROUTES.SETTINGS_WORKSPACES, [SCREENS.WORKSPACE.PROFILE]: ROUTES.WORKSPACE_PROFILE.route, [SCREENS.WORKSPACE.CARD]: { @@ -68,8 +66,6 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.CATEGORIES]: { path: ROUTES.WORKSPACE_CATEGORIES.route, }, - - [SCREENS.TRAVEL.MY_TRIPS]: ROUTES.TRAVEL_MY_TRIPS, }, }, [SCREENS.NOT_FOUND]: '*', @@ -529,6 +525,11 @@ const config: LinkingOptions['config'] = { [SCREENS.PROCESS_MONEY_REQUEST_HOLD_ROOT]: ROUTES.PROCESS_MONEY_REQUEST_HOLD, }, }, + [SCREENS.RIGHT_MODAL.TRAVEL]: { + screens: { + [SCREENS.TRAVEL.MY_TRIPS]: ROUTES.TRAVEL_MY_TRIPS, + }, + }, }, }, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index daaf867f16c2..8b89ee7b9ea2 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -90,7 +90,6 @@ type CentralPaneNavigatorParamList = { [SCREENS.WORKSPACE.CATEGORIES]: { policyID: string; }; - [SCREENS.TRAVEL.MY_TRIPS]: undefined; }; type WorkspaceSwitcherNavigatorParamList = { @@ -495,6 +494,11 @@ type RightModalNavigatorParamList = { [SCREENS.RIGHT_MODAL.PROCESS_MONEY_REQUEST_HOLD]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.REFERRAL]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.PRIVATE_NOTES]: NavigatorScreenParams; + [SCREENS.RIGHT_MODAL.TRAVEL]: NavigatorScreenParams; +}; + +type TravelNavigatorParamList = { + [SCREENS.TRAVEL.MY_TRIPS]: undefined; }; type SettingsCentralPaneNavigatorParamList = { @@ -514,7 +518,6 @@ type BottomTabNavigatorParamList = { [SCREENS.HOME]: undefined; [SCREENS.ALL_SETTINGS]: undefined; [SCREENS.WORKSPACE.INITIAL]: undefined; - [SCREENS.TRAVEL.HOME]: undefined; }; type PublicScreensParamList = { @@ -635,4 +638,5 @@ export type { WorkspaceSwitcherNavigatorParamList, OnboardEngagementNavigatorParamList, SwitchPolicyIDParams, + TravelNavigatorParamList, }; diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index b3d7c302234d..beafd92024bd 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -30,7 +30,7 @@ function canUseWorkflowsDelayedSubmission(betas: OnyxEntry): boolean { return !!betas?.includes(CONST.BETAS.WORKFLOWS_DELAYED_SUBMISSION) || canUseAllBetas(betas); } -function canSeeTravelPage(betas: OnyxEntry): boolean { +function canUseSpotnanaTravel(betas: OnyxEntry): boolean { return !!betas?.includes(CONST.BETAS.SPOTNANA_TRAVEL) || canUseAllBetas(betas); } @@ -49,5 +49,5 @@ export default { canUseViolations, canUseReportFields, canUseWorkflowsDelayedSubmission, - canSeeTravelPage, + canUseSpotnanaTravel, }; diff --git a/src/pages/Travel/ManageTrips.tsx b/src/pages/Travel/ManageTrips.tsx index 30c2f1151492..817fb430c054 100644 --- a/src/pages/Travel/ManageTrips.tsx +++ b/src/pages/Travel/ManageTrips.tsx @@ -34,7 +34,7 @@ function ManageTrips() { subtitle={translate('travel.subtitle')} ctaText={translate('travel.bookOrManage')} ctaAccessibilityLabel={translate('travel.bookOrManage')} - onCtaPress={() => console.log('pressed')} + onCtaPress={() => {}} illustration={LottieAnimations.Plane} illustrationStyle={styles.travelIllustrationStyle} illustrationBackgroundColor={colors.blue600} diff --git a/src/pages/Travel/MyTripsPage.tsx b/src/pages/Travel/MyTripsPage.tsx index ddef2f303318..003775d1a10c 100644 --- a/src/pages/Travel/MyTripsPage.tsx +++ b/src/pages/Travel/MyTripsPage.tsx @@ -1,29 +1,15 @@ -import type {StackScreenProps} from '@react-navigation/stack'; import React from 'react'; -import {withOnyx} from 'react-native-onyx'; -import type {OnyxEntry} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Illustrations from '@components/Icon/Illustrations'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; -import useWindowDimensions from '@hooks/useWindowDimensions'; -import Navigation from '@libs/Navigation/Navigation'; -import Permissions from '@libs/Permissions'; -import type {CentralPaneNavigatorParamList} from '@navigation/types'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type SCREENS from '@src/SCREENS'; -import type {Beta} from '@src/types/onyx'; +import usePermissions from '@hooks/usePermissions'; import ManageTrips from './ManageTrips'; -type MyTripsPageOnyxProps = {betas: OnyxEntry}; - -type MyTripsPageProps = StackScreenProps & MyTripsPageOnyxProps; - -function MyTripsPage({betas}: MyTripsPageProps) { +function MyTripsPage() { const {translate} = useLocalize(); - const {isSmallScreenWidth} = useWindowDimensions(); - const canSeeTravelPage = Permissions.canSeeTravelPage(betas); + const {canUseSpotnanaTravel} = usePermissions(); return ( - Navigation.goBack()} - /> - + + @@ -48,8 +36,4 @@ function MyTripsPage({betas}: MyTripsPageProps) { MyTripsPage.displayName = 'MyTripsPage'; -export default withOnyx({ - betas: { - key: ONYXKEYS.BETAS, - }, -})(MyTripsPage); +export default MyTripsPage; diff --git a/src/pages/Travel/TravelMenu.tsx b/src/pages/Travel/TravelMenu.tsx deleted file mode 100644 index 12fca5d0c587..000000000000 --- a/src/pages/Travel/TravelMenu.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import React, {useMemo} from 'react'; -import {ScrollView} from 'react-native'; -import Breadcrumbs from '@components/Breadcrumbs'; -import * as Expensicons from '@components/Icon/Expensicons'; -import MenuItemList from '@components/MenuItemList'; -import ScreenWrapper from '@components/ScreenWrapper'; -import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; -import useWaitForNavigation from '@hooks/useWaitForNavigation'; -import useWindowDimensions from '@hooks/useWindowDimensions'; -import Navigation from '@libs/Navigation/Navigation'; -import CONST from '@src/CONST'; -import type {TranslationPaths} from '@src/languages/types'; -import ROUTES from '@src/ROUTES'; - -// type TravelMenuProps = StackScreenProps; - -function TravelMenu() { - const styles = useThemeStyles(); - const waitForNavigate = useWaitForNavigation(); - const {translate} = useLocalize(); - const {isSmallScreenWidth} = useWindowDimensions(); - - /** - * Retuns a list of menu items data for Travel menu - * @returns {Object} object with translationKey, style and items - */ - const menuItems = useMemo(() => { - const baseMenuItems = [ - { - translationKey: 'travel.header', - icon: Expensicons.Suitcase, - action: () => { - waitForNavigate(() => { - Navigation.navigate(ROUTES.TRAVEL_MY_TRIPS); - })(); - }, - focused: !isSmallScreenWidth, - }, - ]; - return baseMenuItems.map((item) => ({ - key: item.translationKey, - title: translate(item.translationKey as TranslationPaths), - icon: item.icon, - onPress: item.action, - wrapperStyle: styles.sectionMenuItem, - isPaneMenu: true, - focused: item.focused, - hoverAndPressStyle: styles.hoveredComponentBG, - })); - }, [isSmallScreenWidth, styles.hoveredComponentBG, styles.sectionMenuItem, translate, waitForNavigate]); - - return ( - - - - - - - ); -} - -TravelMenu.displayName = 'TravelMenu'; - -export default TravelMenu; diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js index 573cbe370aa7..090e8384c615 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js @@ -9,6 +9,7 @@ import withNavigation from '@components/withNavigation'; import withNavigationFocus from '@components/withNavigationFocus'; import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; import useLocalize from '@hooks/useLocalize'; +import usePermissions from '@hooks/usePermissions'; import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; import compose from '@libs/compose'; @@ -78,6 +79,8 @@ function FloatingActionButtonAndPopover(props) { const prevIsFocused = usePrevious(props.isFocused); + const {canUseSpotnanaTravel} = usePermissions(); + /** * Check if LHN status changed from active to inactive. * Used to close already opened FAB menu when open any other pages (i.e. Press Command + K on web). @@ -191,6 +194,15 @@ function FloatingActionButtonAndPopover(props) { text: translate('sidebarScreen.saveTheWorld'), onSelected: () => interceptAnonymousUser(() => Navigation.navigate(ROUTES.TEACHERS_UNITE)), }, + ...(canUseSpotnanaTravel + ? [ + { + icon: Expensicons.Suitcase, + text: translate('travel.bookTravel'), + onSelected: () => interceptAnonymousUser(() => Navigation.navigate(ROUTES.TRAVEL_MY_TRIPS)), + }, + ] + : []), ...(!props.isLoading && !Policy.hasActiveFreePolicy(props.allPolicies) ? [ { From afd404c89b69ff0d5b509f1f7b10a54defe7b5a3 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Thu, 14 Mar 2024 17:45:12 +0100 Subject: [PATCH 012/194] add base terms and conditions modal --- src/languages/en.ts | 11 ++++ src/pages/Travel/TravelTerms.tsx | 100 +++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 src/pages/Travel/TravelTerms.tsx diff --git a/src/languages/en.ts b/src/languages/en.ts index 2b483de57c93..5104feaa7b4e 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1726,6 +1726,17 @@ export default { saveMoney: 'Save money on your bookings', alerts: 'Get real time alerts if your travel plans change', }, + termsAndConditions: { + header: 'Before we continue...', + title: 'Please read the Terms & Conditions for travel', + subtitle: 'To enable travel on your workspace you must agree to our ', + termsconditions: 'terms & conditions', + helpDocIntro: 'Check out this ', + helpDocOutro: 'for more information or reach out to Concierge or your Account Manager.', + helpDoc: 'Help Doc', + agree: 'I agree to the travel ', + error: 'You must accept the Terms & Conditions for travel to continue' + } }, workspace: { common: { diff --git a/src/pages/Travel/TravelTerms.tsx b/src/pages/Travel/TravelTerms.tsx new file mode 100644 index 000000000000..ffd18031ee65 --- /dev/null +++ b/src/pages/Travel/TravelTerms.tsx @@ -0,0 +1,100 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import CheckboxWithLabel from "@components/CheckboxWithLabel"; +import HeaderWithBackButton from "@components/HeaderWithBackButton"; +import ScreenWrapper from "@components/ScreenWrapper"; +import Text from "@components/Text"; +import TextLink from "@components/TextLink"; +import useLocalize from "@hooks/useLocalize"; +import useThemeStyles from "@hooks/useThemeStyles"; +import Navigation from "@libs/Navigation/Navigation"; +import { ScrollView } from "react-native-gesture-handler"; +import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton'; +import { View } from 'react-native'; +import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; + +function TravelTerms() { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + const [hasAcceptedTravelTerms, setHasAcceptedTravelTerms] = useState(false); + const [error, setError] = useState(false); + + const errorMessage = error ? 'travel.termsAndConditions.error' : ''; + + const toggleTravelTerms = () => { + setHasAcceptedTravelTerms(!hasAcceptedTravelTerms); + }; + + useEffect(() => { + if(!hasAcceptedTravelTerms) { + return; + } + + setError(false); + }, [hasAcceptedTravelTerms]); + + const AgreeToTheLabel = useCallback(() => ( + + {`${translate('travel.termsAndConditions.agree')}`} + {`${translate('travel.termsAndConditions.termsconditions')}`} + + ), [translate]); + + // Add beta support for FullPageNotFound that is universal across travel pages + return ( + + + Navigation.goBack()} + /> + + + {`${translate('travel.termsAndConditions.title')}`} + + {`${translate('travel.termsAndConditions.subtitle')}`} + {`${translate('travel.termsAndConditions.termsconditions')}.`} + + + {`${translate('travel.termsAndConditions.helpDocIntro')}`} + {`${translate('travel.termsAndConditions.helpDoc')} `} + {`${translate('travel.termsAndConditions.helpDocOutro')}`} + + + + + + { + if(!hasAcceptedTravelTerms) { + setError(true); + return; + } + + // API call for AcceptSpontanaTerms when backend gets implemented + setError(false); + Navigation.goBack(); + }} + message={errorMessage} + isAlertVisible={error || Boolean(errorMessage)} + containerStyles={[styles.mh0, styles.mv4]} + /> + + + + ) +} + +TravelTerms.displayName = 'TravelMenu'; + +export default TravelTerms; \ No newline at end of file From f74e2d27a62e367e1552ff43657e5aed3a212703 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Fri, 29 Mar 2024 16:42:55 +0100 Subject: [PATCH 013/194] Add es translations for TravelTerms page --- src/languages/en.ts | 5 +++-- src/languages/es.ts | 14 +++++++++++++- src/pages/Travel/TravelTerms.tsx | 4 ++-- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 32d9239104c7..fe425cf78889 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1732,12 +1732,13 @@ export default { title: 'Please read the Terms & Conditions for travel', subtitle: 'To enable travel on your workspace you must agree to our ', termsconditions: 'terms & conditions', + travelTermsAndConditions: 'terms & conditions', helpDocIntro: 'Check out this ', helpDocOutro: 'for more information or reach out to Concierge or your Account Manager.', helpDoc: 'Help Doc', agree: 'I agree to the travel ', - error: 'You must accept the Terms & Conditions for travel to continue' - } + error: 'You must accept the Terms & Conditions for travel to continue', + }, }, workspace: { common: { diff --git a/src/languages/es.ts b/src/languages/es.ts index 35707cf7424e..79f230088d08 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1746,11 +1746,23 @@ export default { title: 'Reserva o gestiona tus viajes', subtitle: 'Utiliza Expensify Travel para obtener las mejores ofertas de viaje y gestionar todos tus gastos de negocio en un solo lugar.', bookOrManage: 'Reservar o gestionar', + bookTravel: 'Reservar viajes', features: { saveMoney: 'Ahorra dinero en tus reservas', alerts: 'Recibe alertas en tiempo real si cambian tus planes de viaje', }, - bookTravel: 'Reservar viajes', + termsAndConditions: { + header: 'Antes de continuar...', + title: 'Por favor lea los Términos y condiciones para viajar', + subtitle: 'Para permitir viajar en su espacio de trabajo debe aceptar nuestros ', + termsconditions: 'términos y condiciones', + travelTermsAndConditions: 'términos y condiciones de viaje', + helpDocIntro: 'Consulte este ', + helpDocOutro: 'para obtener más información o comuníquese con el conserje o su administrador de cuentas.', + helpDoc: 'documento de ayuda', + agree: 'Acepto los ', + error: 'Debes aceptar los Términos y condiciones para que el viaje continúe', + }, }, workspace: { common: { diff --git a/src/pages/Travel/TravelTerms.tsx b/src/pages/Travel/TravelTerms.tsx index ffd18031ee65..dfcf8a722888 100644 --- a/src/pages/Travel/TravelTerms.tsx +++ b/src/pages/Travel/TravelTerms.tsx @@ -35,7 +35,7 @@ function TravelTerms() { const AgreeToTheLabel = useCallback(() => ( {`${translate('travel.termsAndConditions.agree')}`} - {`${translate('travel.termsAndConditions.termsconditions')}`} + {`${translate('travel.termsAndConditions.travelTermsAndConditions')}`} ), [translate]); @@ -65,7 +65,7 @@ function TravelTerms() { From 255255bdcf12d6ae5116f33ede11cdec33119abd Mon Sep 17 00:00:00 2001 From: NikkiWines Date: Tue, 2 Apr 2024 19:49:54 -0700 Subject: [PATCH 014/194] fix reversed actions logic --- src/pages/home/report/ReportActionsView.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index 99fa266aa0de..bced0fdbea22 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -360,13 +360,13 @@ function ReportActionsView({ } if (!isEmptyObject(transactionThreadReport)) { - // Get newer actions based on the newest reportAction for the current report + // Get older actions based on the oldest reportAction for the current report const oldestActionCurrentReport = reportActionIDMap.findLast((item) => item.reportID === reportID); - Report.getNewerActions(oldestActionCurrentReport?.reportID ?? '0', oldestActionCurrentReport?.reportActionID ?? '0'); + Report.getOlderActions(oldestActionCurrentReport?.reportID ?? '0', oldestActionCurrentReport?.reportActionID ?? '0'); - // Get newer actions based on the newest reportAction for the transaction thread report + // Get older actions based on the oldest reportAction for the transaction thread report const oldestActionTransactionThreadReport = reportActionIDMap.findLast((item) => item.reportID === transactionThreadReport.reportID); - Report.getNewerActions(oldestActionTransactionThreadReport?.reportID ?? '0', oldestActionTransactionThreadReport?.reportActionID ?? '0'); + Report.getOlderActions(oldestActionTransactionThreadReport?.reportID ?? '0', oldestActionTransactionThreadReport?.reportActionID ?? '0'); } else { // Retrieve the next REPORT.ACTIONS.LIMIT sized page of comments Report.getOlderActions(reportID, oldestReportAction.reportActionID); From f9d5eaafd9e2063dee81d0b4e1696bd6260c021c Mon Sep 17 00:00:00 2001 From: NikkiWines Date: Thu, 4 Apr 2024 16:38:19 -0700 Subject: [PATCH 015/194] don't display report as one-transaction report if it has previously deleted IOU requests --- src/libs/ReportActionsUtils.ts | 3 ++- src/types/onyx/OriginalMessage.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index bf1a9f994c37..434cabbf2bb6 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -230,7 +230,8 @@ function getOneTransactionThreadReportID(reportActions: OnyxEntry action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU && (iouRequestTypes.includes(action.originalMessage.type) ?? []) && action.childReportID && - action.originalMessage.IOUTransactionID, + // Include deleted IOU reportActions because they might have associated comments that we'd want to display + (action.originalMessage.deleted || action.originalMessage.IOUTransactionID), ); // If we don't have any IOU request actions, or we have more than one IOU request actions, this isn't a oneTransaction report diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index 2e24fe00539a..764a490cc917 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -66,6 +66,7 @@ type IOUMessage = { type: ValueOf; cancellationReason?: string; paymentType?: PaymentMethodType; + deleted?: string; /** Only exists when we are sending money */ IOUDetails?: IOUDetails; }; From a0a3ff047054fb3b79f56369ee5cca6c78da28b6 Mon Sep 17 00:00:00 2001 From: NikkiWines Date: Thu, 4 Apr 2024 16:49:44 -0700 Subject: [PATCH 016/194] only include deleted IOUs if they have visible child actions we need to display --- src/libs/ReportActionsUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 434cabbf2bb6..e6c63df9d9a9 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -230,8 +230,8 @@ function getOneTransactionThreadReportID(reportActions: OnyxEntry action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU && (iouRequestTypes.includes(action.originalMessage.type) ?? []) && action.childReportID && - // Include deleted IOU reportActions because they might have associated comments that we'd want to display - (action.originalMessage.deleted || action.originalMessage.IOUTransactionID), + // Include deleted IOU reportActions if they have childAactions because we want to display those comments + ((action.originalMessage.deleted && action.childVisibleActionCount) || action.originalMessage.IOUTransactionID), ); // If we don't have any IOU request actions, or we have more than one IOU request actions, this isn't a oneTransaction report From 2e030d9709cf90a886f386c63324b92108e62ea3 Mon Sep 17 00:00:00 2001 From: NikkiWines Date: Fri, 5 Apr 2024 11:30:30 -0700 Subject: [PATCH 017/194] move combined reportAction logic into ReportActionUtils --- src/libs/ReportActionsUtils.ts | 22 +++++++++++++++++++++ src/pages/home/report/ReportActionsView.tsx | 14 +------------ 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index e6c63df9d9a9..881aabdd7493 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -231,6 +231,7 @@ function getOneTransactionThreadReportID(reportActions: OnyxEntry (iouRequestTypes.includes(action.originalMessage.type) ?? []) && action.childReportID && // Include deleted IOU reportActions if they have childAactions because we want to display those comments + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing ((action.originalMessage.deleted && action.childVisibleActionCount) || action.originalMessage.IOUTransactionID), ); @@ -279,6 +280,26 @@ function getSortedReportActions(reportActions: ReportAction[] | null, shouldSort return sortedActions; } +/** + * Returns a combined list of report actions for a report and associated transaction thread report + */ +function getCombinedReportActions(reportActions: ReportAction[], transactionThreadReportActions: ReportAction[]): ReportAction[] { + if (isEmptyObject(transactionThreadReportActions)) { + return reportActions; + } + + // Filter out the created action from the transaction thread report actions, since we already have the parent report's created action in `reportActions` + const filteredTransactionThreadReportActions = transactionThreadReportActions?.filter((action) => action.actionName !== CONST.REPORT.ACTIONS.TYPE.CREATED); + + // Filter out request and send money request actions because we don't want to show any preview actions for one transaction reports + const filteredReportActions = [...reportActions, ...filteredTransactionThreadReportActions].filter((action) => { + const actionType = (action as OriginalMessageIOU).originalMessage?.type ?? ''; + return actionType !== CONST.IOU.REPORT_ACTION_TYPE.CREATE && !isSentMoneyReportAction(action); + }); + + return getSortedReportActions(filteredReportActions, true); +} + /** * Returns the largest gapless range of reportActions including a the provided reportActionID, where a "gap" is defined as a reportAction's `previousReportActionID` not matching the previous reportAction in the sortedReportActions array. * See unit tests for example of inputs and expected outputs. @@ -1077,6 +1098,7 @@ export { isApprovedOrSubmittedReportAction, getReportPreviewAction, getSortedReportActions, + getCombinedReportActions, getSortedReportActionsForDisplay, isConsecutiveActionMadeByPreviousActor, isCreatedAction, diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index 43081cf72a33..7c2f15705788 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -138,19 +138,7 @@ function ReportActionsView({ // Get a sorted array of reportActions for both the current report and the transaction thread report associated with this report (if there is one) // so that we display transaction-level and report-level report actions in order in the one-transaction view const combinedReportActions = useMemo(() => { - if (isEmptyObject(transactionThreadReportActions)) { - return allReportActions; - } - - // Filter out the created action from the transaction thread report actions, since we already have the parent report's created action in `reportActions` - const filteredTransactionThreadReportActions = transactionThreadReportActions?.filter((action) => action.actionName !== CONST.REPORT.ACTIONS.TYPE.CREATED); - - // Filter out request and send money request actions because we don't want to show any preview actions for one transaction reports - const filteredReportActions = [...allReportActions, ...filteredTransactionThreadReportActions].filter((action) => { - const actionType = (action as OnyxTypes.OriginalMessageIOU).originalMessage?.type ?? ''; - return actionType !== CONST.IOU.REPORT_ACTION_TYPE.CREATE && !ReportActionsUtils.isSentMoneyReportAction(action); - }); - return ReportActionsUtils.getSortedReportActions(filteredReportActions, true); + return ReportActionsUtils.getCombinedReportActions(allReportActions, transactionThreadReportActions); }, [allReportActions, transactionThreadReportActions]); const indexOfLinkedAction = useMemo(() => { From 1ca6f0d3f1136abaa5439dd3419acb067d55ccf2 Mon Sep 17 00:00:00 2001 From: NikkiWines Date: Fri, 5 Apr 2024 11:30:46 -0700 Subject: [PATCH 018/194] ensure lastMessageText is correct for oneTransaction reports --- src/libs/OptionsListUtils.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index bd8b799bdc52..63a41e785cff 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -560,7 +560,13 @@ function getAlternateText( * Get the last message text from the report directly or from other sources for special cases. */ function getLastMessageTextForReport(report: OnyxEntry, lastActorDetails: Partial | null, policy?: OnyxEntry): string { - const lastReportAction = allSortedReportActions[report?.reportID ?? '']?.find((reportAction) => ReportActionUtils.shouldReportActionBeVisibleAsLastAction(reportAction)) ?? null; + let reportActions = allSortedReportActions[report?.reportID ?? '']; + const transactionThreadReportID = ReportActionUtils.getOneTransactionThreadReportID(allReportActions[report?.reportID ?? '']); + if (transactionThreadReportID) { + reportActions = ReportActionUtils.getCombinedReportActions(reportActions, allSortedReportActions[transactionThreadReportID]); + } + + const lastReportAction = reportActions?.find((reportAction) => ReportActionUtils.shouldReportActionBeVisibleAsLastAction(reportAction)) ?? null; // some types of actions are filtered out for lastReportAction, in some cases we need to check the actual last action const lastOriginalReportAction = lastReportActions[report?.reportID ?? ''] ?? null; let lastMessageTextFromReport = ''; From 7936ff1685130c28cb1ab33fece14710ae55fabe Mon Sep 17 00:00:00 2001 From: NikkiWines Date: Fri, 5 Apr 2024 14:42:05 -0700 Subject: [PATCH 019/194] clarifying comment --- src/libs/ReportActionsUtils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 881aabdd7493..78ec1600be12 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -281,7 +281,8 @@ function getSortedReportActions(reportActions: ReportAction[] | null, shouldSort } /** - * Returns a combined list of report actions for a report and associated transaction thread report + * Returns a sorted and filtered list of report actions from both the parent report and the child + * transaction thread report in order to display details from both report s in the one-transaction report view. */ function getCombinedReportActions(reportActions: ReportAction[], transactionThreadReportActions: ReportAction[]): ReportAction[] { if (isEmptyObject(transactionThreadReportActions)) { From 50a275cbd65575cd303f0b2c538e8e7ebf5a5833 Mon Sep 17 00:00:00 2001 From: NikkiWines Date: Fri, 5 Apr 2024 16:18:20 -0700 Subject: [PATCH 020/194] minor style change and also use isMessageDeleted instead of originalMessage.deleted --- src/libs/ReportActionsUtils.ts | 74 ++++++++++++--------- src/pages/home/report/ReportActionsView.tsx | 8 +-- 2 files changed, 45 insertions(+), 37 deletions(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 78ec1600be12..0f6ca4168332 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -213,37 +213,6 @@ function isTransactionThread(parentReportAction: OnyxEntry | Empty ); } -/** - * Returns the reportID for the transaction thread associated with a report by iterating over the reportActions and identifying the IOU report actions with a childReportID. Returns a reportID if there is exactly one transaction thread for the report, and null otherwise. - */ -function getOneTransactionThreadReportID(reportActions: OnyxEntry | ReportAction[]): string | null { - const reportActionsArray = Object.values(reportActions ?? {}); - - if (!reportActionsArray.length) { - return null; - } - - // Get all IOU report actions for the report. - const iouRequestTypes: Array> = [CONST.IOU.REPORT_ACTION_TYPE.CREATE, CONST.IOU.REPORT_ACTION_TYPE.SPLIT, CONST.IOU.REPORT_ACTION_TYPE.PAY]; - const iouRequestActions = reportActionsArray.filter( - (action) => - action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU && - (iouRequestTypes.includes(action.originalMessage.type) ?? []) && - action.childReportID && - // Include deleted IOU reportActions if they have childAactions because we want to display those comments - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - ((action.originalMessage.deleted && action.childVisibleActionCount) || action.originalMessage.IOUTransactionID), - ); - - // If we don't have any IOU request actions, or we have more than one IOU request actions, this isn't a oneTransaction report - if (!iouRequestActions.length || iouRequestActions.length > 1) { - return null; - } - - // Ensure we have a childReportID associated with the IOU report action - return iouRequestActions[0].childReportID ?? null; -} - /** * Sort an array of reportActions by their created timestamp first, and reportActionID second * This gives us a stable order even in the case of multiple reportActions created on the same millisecond @@ -281,8 +250,8 @@ function getSortedReportActions(reportActions: ReportAction[] | null, shouldSort } /** - * Returns a sorted and filtered list of report actions from both the parent report and the child - * transaction thread report in order to display details from both report s in the one-transaction report view. + * Returns a sorted and filtered list of report actions from a report and it's associated child + * transaction thread report in order to correctly display reportActions from both reports in the one-transaction report view. */ function getCombinedReportActions(reportActions: ReportAction[], transactionThreadReportActions: ReportAction[]): ReportAction[] { if (isEmptyObject(transactionThreadReportActions)) { @@ -803,6 +772,45 @@ function isTaskAction(reportAction: OnyxEntry): boolean { ); } +/** + * Gets the reportID for the transaction thread associated with a report by iterating over the reportActions and identifying the IOU report actions. + * Returns a reportID if there is exactly one transaction thread for the report, and null otherwise. + */ +function getOneTransactionThreadReportID(reportActions: OnyxEntry | ReportAction[]): string | null { + const reportActionsArray = Object.values(reportActions ?? {}); + + if (!reportActionsArray.length) { + return null; + } + + // Get all IOU report actions for the report. + const iouRequestTypes: Array> = [CONST.IOU.REPORT_ACTION_TYPE.CREATE, CONST.IOU.REPORT_ACTION_TYPE.SPLIT, CONST.IOU.REPORT_ACTION_TYPE.PAY]; + const iouRequestActions = reportActionsArray.filter( + (action) => + action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU && + (iouRequestTypes.includes(action.originalMessage.type) ?? []) && + action.childReportID && + // Include deleted IOU reportActions if they have visibile childActions (like comments) because we'll want to display + // those reports using the standard report view + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + ((isMessageDeleted(action) && action.childVisibleActionCount) || action.originalMessage.IOUTransactionID), + ); + + // If we don't have any IOU request actions, or we have more than one IOU request actions, this isn't a oneTransaction report + if (!iouRequestActions.length || iouRequestActions.length > 1) { + return null; + } + + // If there's only IOU request action associated with the report but it's been deleted, then we don't consider this a oneTransaction report + // and want to display it using the standard view + if (((iouRequestActions[0] as OriginalMessageIOU).originalMessage?.deleted ?? '') !== '') { + return null; + } + + // Ensure we have a childReportID associated with the IOU report action + return iouRequestActions[0].childReportID ?? null; +} + /** * When we delete certain reports, we want to check whether there are any visible actions left to display. * If there are no visible actions left (including system messages), we can hide the report from view entirely diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index 7c2f15705788..88650995d8bd 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -137,10 +137,10 @@ function ReportActionsView({ // Get a sorted array of reportActions for both the current report and the transaction thread report associated with this report (if there is one) // so that we display transaction-level and report-level report actions in order in the one-transaction view - const combinedReportActions = useMemo(() => { - return ReportActionsUtils.getCombinedReportActions(allReportActions, transactionThreadReportActions); - }, [allReportActions, transactionThreadReportActions]); - + const combinedReportActions = useMemo( + () => ReportActionsUtils.getCombinedReportActions(allReportActions, transactionThreadReportActions), + [allReportActions, transactionThreadReportActions], + ); const indexOfLinkedAction = useMemo(() => { if (!reportActionID) { return -1; From c261f00eb7075eefdcaf0f304c87348e9687b444 Mon Sep 17 00:00:00 2001 From: smelaa Date: Tue, 9 Apr 2024 17:16:00 +0200 Subject: [PATCH 021/194] Fixes after merging with main --- .../Navigation/AppNavigator/ModalStackNavigators/index.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index aab9ad12baf1..71fc44fa992b 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -28,6 +28,7 @@ import type { SplitDetailsNavigatorParamList, TaskDetailsNavigatorParamList, TeachersUniteNavigatorParamList, + TravelNavigatorParamList, WalletStatementNavigatorParamList, WorkspaceSwitcherNavigatorParamList, } from '@navigation/types'; @@ -97,6 +98,10 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator require('../../../../pages/settings/Profile/PersonalDetails/StateSelectionPage').default as React.ComponentType, }); +const TravelModalStackNavigator = createModalStackNavigator({ + [SCREENS.TRAVEL.MY_TRIPS]: () => require('../../../../pages/Travel/MyTripsPage').default as React.ComponentType, +}); + const SplitDetailsModalStackNavigator = createModalStackNavigator({ [SCREENS.SPLIT_DETAILS.ROOT]: () => require('../../../../pages/iou/SplitBillDetailsPage').default as React.ComponentType, [SCREENS.SPLIT_DETAILS.EDIT_REQUEST]: () => require('../../../../pages/EditSplitBillPage').default as React.ComponentType, @@ -342,5 +347,6 @@ export { TaskModalStackNavigator, WalletStatementStackNavigator, ProcessMoneyRequestHoldStackNavigator, + TravelModalStackNavigator, WorkspaceSettingsModalStackNavigator, }; From 5ef17d93ae4cf7a3536a73245f1ea20177d9c318 Mon Sep 17 00:00:00 2001 From: smelaa Date: Tue, 9 Apr 2024 17:55:28 +0200 Subject: [PATCH 022/194] Fixes after matching with main --- .../Navigation/AppNavigator/ModalStackNavigators/index.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index aab9ad12baf1..4f5598007d64 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -28,6 +28,7 @@ import type { SplitDetailsNavigatorParamList, TaskDetailsNavigatorParamList, TeachersUniteNavigatorParamList, + TravelNavigatorParamList, WalletStatementNavigatorParamList, WorkspaceSwitcherNavigatorParamList, } from '@navigation/types'; @@ -97,6 +98,10 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator require('../../../../pages/settings/Profile/PersonalDetails/StateSelectionPage').default as React.ComponentType, }); +const TravelModalStackNavigator = createModalStackNavigator({ + [SCREENS.TRAVEL.MY_TRIPS]: () => require('../../../../pages/Travel/MyTripsPage').default as React.ComponentType, +}); + const SplitDetailsModalStackNavigator = createModalStackNavigator({ [SCREENS.SPLIT_DETAILS.ROOT]: () => require('../../../../pages/iou/SplitBillDetailsPage').default as React.ComponentType, [SCREENS.SPLIT_DETAILS.EDIT_REQUEST]: () => require('../../../../pages/EditSplitBillPage').default as React.ComponentType, @@ -327,6 +332,7 @@ export { PrivateNotesModalStackNavigator, ProfileModalStackNavigator, ReferralModalStackNavigator, + TravelModalStackNavigator, WorkspaceSwitcherModalStackNavigator, ReimbursementAccountModalStackNavigator, ReportDetailsModalStackNavigator, From fa4fe368d2ea4e88614ee37c3502f83aed64158d Mon Sep 17 00:00:00 2001 From: smelaa Date: Tue, 9 Apr 2024 18:21:21 +0200 Subject: [PATCH 023/194] Fixes after merging with travel/travel-page --- src/components/Icon/Illustrations.ts | 4 ++-- src/languages/en.ts | 2 +- src/languages/es.ts | 2 +- .../Navigation/AppNavigator/ModalStackNavigators/index.tsx | 1 - 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/Icon/Illustrations.ts b/src/components/Icon/Illustrations.ts index f12cde1083d3..44b4d08134e8 100644 --- a/src/components/Icon/Illustrations.ts +++ b/src/components/Icon/Illustrations.ts @@ -156,8 +156,6 @@ export { QRCode, ReceiptEnvelope, Approval, - PiggyBank, - Pencil, WalletAlt, Workflows, ThreeLeggedLaptopWoman, @@ -169,9 +167,11 @@ export { CompanyCard, ReceiptUpload, SplitBill, + PiggyBank, Accounting, Car, Coins, + Pencil, Tag, CarIce, }; diff --git a/src/languages/en.ts b/src/languages/en.ts index 51c4617e1c2a..c30b3a7f92b1 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1809,11 +1809,11 @@ export default { title: 'Book or manage your trips', subtitle: 'Use Expensify Travel to get the best travel offers and manage all your business expenses in a single place.', bookOrManage: 'Book or manage', - bookTravel: 'Book Travel', features: { saveMoney: 'Save money on your bookings', alerts: 'Get real time alerts if your travel plans change', }, + bookTravel: 'Book Travel', termsAndConditions: { header: 'Before we continue...', title: 'Please read the Terms & Conditions for travel', diff --git a/src/languages/es.ts b/src/languages/es.ts index c2b4334351d8..6980fad0fa10 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1836,11 +1836,11 @@ export default { title: 'Reserva o gestiona tus viajes', subtitle: 'Utiliza Expensify Travel para obtener las mejores ofertas de viaje y gestionar todos tus gastos de negocio en un solo lugar.', bookOrManage: 'Reservar o gestionar', - bookTravel: 'Reservar viajes', features: { saveMoney: 'Ahorra dinero en tus reservas', alerts: 'Recibe alertas en tiempo real si cambian tus planes de viaje', }, + bookTravel: 'Reservar viajes', termsAndConditions: { header: 'Antes de continuar...', title: 'Por favor lea los Términos y condiciones para viajar', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 6a7bc8e870b7..4f5598007d64 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -348,6 +348,5 @@ export { TaskModalStackNavigator, WalletStatementStackNavigator, ProcessMoneyRequestHoldStackNavigator, - TravelModalStackNavigator, WorkspaceSettingsModalStackNavigator, }; From 5972893e5533b72eadeaaf869430728dcf1f2bb8 Mon Sep 17 00:00:00 2001 From: maddylewis <38016013+maddylewis@users.noreply.github.com> Date: Tue, 9 Apr 2024 13:54:04 -0400 Subject: [PATCH 024/194] Create Remove_Members.md https://github.com/Expensify/Expensify/issues/381301#issuecomment-2025642424 --- .../workspaces/Remove_Members.md | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/articles/expensify-classic/workspaces/Remove_Members.md diff --git a/docs/articles/expensify-classic/workspaces/Remove_Members.md b/docs/articles/expensify-classic/workspaces/Remove_Members.md new file mode 100644 index 000000000000..a79d75edc77d --- /dev/null +++ b/docs/articles/expensify-classic/workspaces/Remove_Members.md @@ -0,0 +1,33 @@ +--- +title: Remove a Workspace Member +description: How to remove a member from a Workspace in Expensify +--- + +Removing a member from a workspace prevents them from submitting reports to or accessing the workspace. Please note that it does not delete their account or deactivate their Expensify Card. + +## Remove a Workspace Member +Important: Make sure the employee has submitted all reports, and the reports have been approved and reimbursed, and are in the final approval state. +1. Go to Settings > Workspaces > Group > [Workspace Name] > Members > Workspace Members +2. Select the member you'd like to remove and click the **Remove** button at the top of the Members table +3. If this member was an approver, update the approval workflow so that reports are no longer routed to them + +![image of members table in a workspace]({{site.url}}/assets/images/ExpensifyHelp_RemovingMembers.png){:width="100%"} + +{% include faq-begin.md %} + +## Will reports from this member on this workspace still be available? +Yes, as long as the reports have been submitted. You can navigate to the Reports page and enter the member's email in the search field to find them. However, Draft reports will be removed from the workspace, so these will no longer be visible to the Workspace Admin. + +## Can members still access their reports on a workspace after they have been removed? +Yes. Any report that has been approved will now show the workspace as “(not shared)” in their account. If it is a Draft Report they will still be able to edit it and add it to a new workspace. If the report is Approved or Reimbursed they will not be able to edit it further. + +## Who can remove members from a workspace? +Only Workspace Admins. It is not possible for a member to add or remove themselves from a workspace. It is not possible for a Domain Admin who is not also a Workspace Admin to remove a member from a workspace. + +## How do I remove a member from a workspace if I am seeing an error message? +If a member is a **preferred exporter, billing owner, report approver** or has **processing reports**, to remove them from the workspace you will first need to: + +* **Preferred Exporter**: Go to Settings > Workspaces > Group > [Workspace Name] > Connections > Configure and select a different Workspace Admin in the dropdown for **Preferred Exporter**. +* **Billing Owner**: Take over billing on the Settings > Workspaces > Group > [Workspace Name] > Overview page. +* **Processing reports**: Approve or reject the member’s reports on your Reports page. +* **Approval Workflow**: Remove them as a workflow approver on your Settings > Workspaces > Group > [Workspace Name] > Members > Approval Mode > page by changing the "**Submit reports to**" field. From abbe116aa3e57dc383e5136a9809a9b841e4a1ee Mon Sep 17 00:00:00 2001 From: smelaa Date: Wed, 10 Apr 2024 10:22:56 +0200 Subject: [PATCH 025/194] Text changes --- src/languages/en.ts | 7 +++---- src/languages/es.ts | 5 ++--- src/pages/Travel/ManageTrips.tsx | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 1bf50c9adae8..ba3cf3213026 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1806,14 +1806,13 @@ export default { }, travel: { header: 'My trips', - title: 'Book or manage your trips', + title: 'Book and manage your trips', subtitle: 'Use Expensify Travel to get the best travel offers and manage all your business expenses in a single place.', - bookOrManage: 'Book or manage', features: { saveMoney: 'Save money on your bookings', - alerts: 'Get real time alerts if your travel plans change', + alerts: 'Get realtime updates and alerts', }, - bookTravel: 'Book Travel', + bookTravel: 'Book travel', }, workspace: { common: { diff --git a/src/languages/es.ts b/src/languages/es.ts index ba7eb05e1823..6b3d9d1e7eec 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1833,12 +1833,11 @@ export default { }, travel: { header: 'Mis viajes', - title: 'Reserva o gestiona tus viajes', + title: 'Reserva y gestiona tus viajes', subtitle: 'Utiliza Expensify Travel para obtener las mejores ofertas de viaje y gestionar todos tus gastos de negocio en un solo lugar.', - bookOrManage: 'Reservar o gestionar', features: { saveMoney: 'Ahorra dinero en tus reservas', - alerts: 'Recibe alertas en tiempo real si cambian tus planes de viaje', + alerts: 'Obtén actualizaciones y alertas en tiempo real', }, bookTravel: 'Reservar viajes', }, diff --git a/src/pages/Travel/ManageTrips.tsx b/src/pages/Travel/ManageTrips.tsx index 817fb430c054..e09e475118d0 100644 --- a/src/pages/Travel/ManageTrips.tsx +++ b/src/pages/Travel/ManageTrips.tsx @@ -32,8 +32,8 @@ function ManageTrips() { menuItems={tripsFeatures} title={translate('travel.title')} subtitle={translate('travel.subtitle')} - ctaText={translate('travel.bookOrManage')} - ctaAccessibilityLabel={translate('travel.bookOrManage')} + ctaText={translate('travel.bookTravel')} + ctaAccessibilityLabel={translate('travel.bookTravel')} onCtaPress={() => {}} illustration={LottieAnimations.Plane} illustrationStyle={styles.travelIllustrationStyle} From 3bfea6950158bca8adca5ae22ecb30e15f058052 Mon Sep 17 00:00:00 2001 From: smelaa Date: Wed, 10 Apr 2024 10:27:15 +0200 Subject: [PATCH 026/194] Fix a typo --- src/languages/en.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index d122f558e8d7..b705441e2d60 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1810,7 +1810,7 @@ export default { subtitle: 'Use Expensify Travel to get the best travel offers and manage all your business expenses in a single place.', features: { saveMoney: 'Save money on your bookings', - alerts: 'Get realtime updates and alerts', + alerts: 'Get real time updates and alerts', }, bookTravel: 'Book travel', termsAndConditions: { From 48976d7127c241319af855dffdf93c2223926593 Mon Sep 17 00:00:00 2001 From: NikkiWines Date: Thu, 11 Apr 2024 15:01:04 -0700 Subject: [PATCH 027/194] update stray function to pass correct params --- src/libs/OptionsListUtils.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 24b5ff311e4b..71fa4d0920d5 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -565,8 +565,9 @@ function getAlternateText( * Get the last message text from the report directly or from other sources for special cases. */ function getLastMessageTextForReport(report: OnyxEntry, lastActorDetails: Partial | null, policy?: OnyxEntry): string { - let reportActions = allSortedReportActions[report?.reportID ?? '']; - const transactionThreadReportID = ReportActionUtils.getOneTransactionThreadReportID(allReportActions[report?.reportID ?? '']); + let reportID = report?.reportID ?? ''; + let reportActions = allSortedReportActions[reportID]; + const transactionThreadReportID = ReportActionUtils.getOneTransactionThreadReportID(reportID, allReportActions[reportID]); if (transactionThreadReportID) { reportActions = ReportActionUtils.getCombinedReportActions(reportActions, allSortedReportActions[transactionThreadReportID]); } From 6423f2b808a7bc9bb4b1528169f858e95dc3890d Mon Sep 17 00:00:00 2001 From: NikkiWines Date: Thu, 11 Apr 2024 15:25:53 -0700 Subject: [PATCH 028/194] use const --- src/libs/OptionsListUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 71fa4d0920d5..ca0214eaf50b 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -565,7 +565,7 @@ function getAlternateText( * Get the last message text from the report directly or from other sources for special cases. */ function getLastMessageTextForReport(report: OnyxEntry, lastActorDetails: Partial | null, policy?: OnyxEntry): string { - let reportID = report?.reportID ?? ''; + const reportID = report?.reportID ?? ''; let reportActions = allSortedReportActions[reportID]; const transactionThreadReportID = ReportActionUtils.getOneTransactionThreadReportID(reportID, allReportActions[reportID]); if (transactionThreadReportID) { From 16c7b658c9c42d75dcd531b4b44ec8f4a1b062c3 Mon Sep 17 00:00:00 2001 From: gijoe0295 Date: Tue, 16 Apr 2024 03:58:31 +0700 Subject: [PATCH 029/194] feature: silently update on desktop --- desktop/ELECTRON_EVENTS.ts | 1 + desktop/contextBridge.ts | 1 + desktop/main.ts | 21 ++++++++++++++----- .../AppUpdate/updateApp/index.desktop.ts | 5 ++--- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/desktop/ELECTRON_EVENTS.ts b/desktop/ELECTRON_EVENTS.ts index de0bd655e12c..607ad7b21580 100644 --- a/desktop/ELECTRON_EVENTS.ts +++ b/desktop/ELECTRON_EVENTS.ts @@ -9,6 +9,7 @@ const ELECTRON_EVENTS = { KEYBOARD_SHORTCUTS_PAGE: 'keyboard-shortcuts-page', START_UPDATE: 'start-update', UPDATE_DOWNLOADED: 'update-downloaded', + SILENT_UPDATE: 'silent-update', } as const; export default ELECTRON_EVENTS; diff --git a/desktop/contextBridge.ts b/desktop/contextBridge.ts index 689c69de0cc8..487e528a7485 100644 --- a/desktop/contextBridge.ts +++ b/desktop/contextBridge.ts @@ -16,6 +16,7 @@ const WHITELIST_CHANNELS_RENDERER_TO_MAIN = [ ELECTRON_EVENTS.REQUEST_VISIBILITY, ELECTRON_EVENTS.START_UPDATE, ELECTRON_EVENTS.LOCALE_UPDATED, + ELECTRON_EVENTS.SILENT_UPDATE, ] as const; const WHITELIST_CHANNELS_MAIN_TO_RENDERER = [ELECTRON_EVENTS.KEYBOARD_SHORTCUTS_PAGE, ELECTRON_EVENTS.UPDATE_DOWNLOADED, ELECTRON_EVENTS.FOCUS, ELECTRON_EVENTS.BLUR] as const; diff --git a/desktop/main.ts b/desktop/main.ts index 6e14d661b345..640b6a417a73 100644 --- a/desktop/main.ts +++ b/desktop/main.ts @@ -111,6 +111,7 @@ process.argv.forEach((arg) => { // happens correctly. let hasUpdate = false; let downloadedVersion: string; +let isSilentUpdate = false; // Note that we have to subscribe to this separately and cannot use Localize.translateLocal, // because the only way code can be shared between the main and renderer processes at runtime is via the context bridge @@ -128,11 +129,12 @@ const quitAndInstallWithUpdate = () => { }; /** Menu Item callback to triggers an update check */ -const manuallyCheckForUpdates = (menuItem: MenuItem, browserWindow?: BrowserWindow) => { +const manuallyCheckForUpdates = (menuItem?: MenuItem, browserWindow?: BrowserWindow) => { // Disable item until the check (and download) is complete - // eslint: menu item flags like enabled or visible can be dynamically toggled by mutating the object - // eslint-disable-next-line no-param-reassign - menuItem.enabled = false; + if (menuItem) { + // eslint-disable-next-line no-param-reassign -- menu item flags like enabled or visible can be dynamically toggled by mutating the object + menuItem.enabled = false; + } autoUpdater .checkForUpdates() @@ -172,6 +174,9 @@ const manuallyCheckForUpdates = (menuItem: MenuItem, browserWindow?: BrowserWind return downloadPromise; }) .finally(() => { + if (!menuItem) { + return; + } // eslint-disable-next-line no-param-reassign menuItem.enabled = true; }); @@ -201,7 +206,7 @@ const electronUpdater = (browserWindow: BrowserWindow): PlatformSpecificUpdater if (checkForUpdatesMenuItem) { checkForUpdatesMenuItem.visible = false; } - if (browserWindow.isVisible()) { + if (browserWindow.isVisible() && !isSilentUpdate) { browserWindow.webContents.send(ELECTRON_EVENTS.UPDATE_DOWNLOADED, info.version); } else { quitAndInstallWithUpdate(); @@ -604,6 +609,12 @@ const mainWindow = (): Promise => { } }); + // Automatically check for and install the latest version in the background + ipcMain.on(ELECTRON_EVENTS.SILENT_UPDATE, () => { + isSilentUpdate = true; + manuallyCheckForUpdates(); + }); + return browserWindow; }) diff --git a/src/libs/actions/AppUpdate/updateApp/index.desktop.ts b/src/libs/actions/AppUpdate/updateApp/index.desktop.ts index fb3a7d649baa..5c1ecbe05742 100644 --- a/src/libs/actions/AppUpdate/updateApp/index.desktop.ts +++ b/src/libs/actions/AppUpdate/updateApp/index.desktop.ts @@ -1,6 +1,5 @@ -import {Linking} from 'react-native'; -import CONST from '@src/CONST'; +import ELECTRON_EVENTS from '@desktop/ELECTRON_EVENTS'; export default function updateApp() { - Linking.openURL(CONST.APP_DOWNLOAD_LINKS.DESKTOP); + window.electron.send(ELECTRON_EVENTS.SILENT_UPDATE); } From 316552a18579fbd5706b8306a1db30c986545eae Mon Sep 17 00:00:00 2001 From: gijoe0295 Date: Wed, 17 Apr 2024 08:03:05 +0700 Subject: [PATCH 030/194] temporary change version to test --- desktop/main.ts | 2 +- package-lock.json | 4 ++-- package.json | 2 +- src/languages/en.ts | 2 +- src/languages/es.ts | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/desktop/main.ts b/desktop/main.ts index 640b6a417a73..662278d540fa 100644 --- a/desktop/main.ts +++ b/desktop/main.ts @@ -150,7 +150,7 @@ const manuallyCheckForUpdates = (menuItem?: MenuItem, browserWindow?: BrowserWin dialog.showMessageBox(browserWindow, { type: 'info', message: Localize.translate(preferredLocale, 'checkForUpdatesModal.available.title'), - detail: Localize.translate(preferredLocale, 'checkForUpdatesModal.available.message'), + detail: Localize.translate(preferredLocale, 'checkForUpdatesModal.available.message', {isSilentUpdate}), buttons: [Localize.translate(preferredLocale, 'checkForUpdatesModal.available.soundsGood')], }); } else if (result && 'error' in result && result.error) { diff --git a/package-lock.json b/package-lock.json index ae50c5d543b2..32adc60abe02 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.62-10", + "version": "1.4.61-8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.62-10", + "version": "1.4.61-8", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 9a7d7ba0a333..0efbab6c503e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.62-10", + "version": "1.4.57-5", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", diff --git a/src/languages/en.ts b/src/languages/en.ts index 9451407c822f..4f226ae96b59 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2456,7 +2456,7 @@ export default { checkForUpdatesModal: { available: { title: 'Update Available', - message: "The new version will be available shortly. We'll notify you when we're ready to update.", + message: ({isSilentUpdate}: {isSilentUpdate: boolean}) => `The new version will be available shortly.${isSilentUpdate ? " We'll notify you when we're ready to update." : ''}`, soundsGood: 'Sounds good', }, notAvailable: { diff --git a/src/languages/es.ts b/src/languages/es.ts index a56c8ac2739d..2ebfa6085286 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2488,7 +2488,7 @@ export default { checkForUpdatesModal: { available: { title: 'Actualización disponible', - message: 'La nueva versión estará disponible dentro de poco. Te notificaremos cuando esté lista.', + message: ({isSilentUpdate}: {isSilentUpdate: boolean}) => `La nueva versión estará disponible dentro de poco.${isSilentUpdate ? ' Te notificaremos cuando esté lista.' : ''}`, soundsGood: 'Suena bien', }, notAvailable: { From d0a32a63ed370f618f98645c7f2dee857c6e31ff Mon Sep 17 00:00:00 2001 From: smelaa Date: Thu, 18 Apr 2024 14:02:30 +0200 Subject: [PATCH 031/194] Address design feedback --- assets/animations/Plane.lottie | Bin 26027 -> 26709 bytes src/components/FeatureList.tsx | 12 +++++++++++- src/languages/en.ts | 2 +- src/languages/es.ts | 2 +- src/libs/Permissions.ts | 1 + src/pages/Travel/ManageTrips.tsx | 2 ++ src/pages/Travel/MyTripsPage.tsx | 1 - src/styles/index.ts | 4 ++-- 8 files changed, 18 insertions(+), 6 deletions(-) diff --git a/assets/animations/Plane.lottie b/assets/animations/Plane.lottie index 77a7c0f5dfa8a734b8e0b6c7cb021088ecf3674c..5244cb7bea10be474ab631acd5ce3b5e51d8626e 100644 GIT binary patch delta 26583 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 I|5AMZ2lYh2_W%F@ delta 25912 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 Ii~o)O2ZA&hy#N3J diff --git a/src/components/FeatureList.tsx b/src/components/FeatureList.tsx index 5f713d5f3aef..a9e928b28084 100644 --- a/src/components/FeatureList.tsx +++ b/src/components/FeatureList.tsx @@ -1,6 +1,6 @@ import React from 'react'; import {View} from 'react-native'; -import type {StyleProp, ViewStyle} from 'react-native'; +import type {StyleProp, TextStyle, ViewStyle} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import variables from '@styles/variables'; @@ -43,6 +43,12 @@ type FeatureListProps = { /** The background color to apply in the upper half of the screen. */ illustrationBackgroundColor?: string; + + /** The style passed to the list container */ + containerStyles?: StyleProp; + + /** The style used for the title */ + titleStyles?: StyleProp; }; function FeatureList({ @@ -55,6 +61,8 @@ function FeatureList({ illustration, illustrationStyle, illustrationBackgroundColor, + containerStyles, + titleStyles, }: FeatureListProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -68,6 +76,8 @@ function FeatureList({ illustration={illustration} illustrationBackgroundColor={illustrationBackgroundColor} illustrationStyle={illustrationStyle} + containerStyles={containerStyles} + titleStyles={titleStyles} > diff --git a/src/languages/en.ts b/src/languages/en.ts index c7be8408ec9a..30bb2d49c3eb 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1801,7 +1801,7 @@ export default { offlineMessageRetry: "Looks like you're offline. Please check your connection and try again.", }, travel: { - header: 'My trips', + header: 'Book travel', title: 'Book and manage your trips', subtitle: 'Use Expensify Travel to get the best travel offers and manage all your business expenses in a single place.', features: { diff --git a/src/languages/es.ts b/src/languages/es.ts index 95a79a840247..12da744dc102 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1825,7 +1825,7 @@ export default { offlineMessageRetry: 'Parece que estás desconectado. Por favor, comprueba tu conexión e inténtalo de nuevo.', }, travel: { - header: 'Mis viajes', + header: 'Reservar viajes', title: 'Reserva y gestiona tus viajes', subtitle: 'Utiliza Expensify Travel para obtener las mejores ofertas de viaje y gestionar todos tus gastos de negocio en un solo lugar.', features: { diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index 3473a181a4f8..f51158f2bef6 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -37,6 +37,7 @@ function canUseWorkflowsDelayedSubmission(betas: OnyxEntry): boolean { } function canUseSpotnanaTravel(betas: OnyxEntry): boolean { + return true; return !!betas?.includes(CONST.BETAS.SPOTNANA_TRAVEL) || canUseAllBetas(betas); } diff --git a/src/pages/Travel/ManageTrips.tsx b/src/pages/Travel/ManageTrips.tsx index e09e475118d0..af38e2e09905 100644 --- a/src/pages/Travel/ManageTrips.tsx +++ b/src/pages/Travel/ManageTrips.tsx @@ -38,6 +38,8 @@ function ManageTrips() { illustration={LottieAnimations.Plane} illustrationStyle={styles.travelIllustrationStyle} illustrationBackgroundColor={colors.blue600} + containerStyles={styles.p5} + titleStyles={styles.textHeadlineH1} /> diff --git a/src/pages/Travel/MyTripsPage.tsx b/src/pages/Travel/MyTripsPage.tsx index 003775d1a10c..6ab85c7b7207 100644 --- a/src/pages/Travel/MyTripsPage.tsx +++ b/src/pages/Travel/MyTripsPage.tsx @@ -24,7 +24,6 @@ function MyTripsPage() { shouldShow={!canUseSpotnanaTravel} > diff --git a/src/styles/index.ts b/src/styles/index.ts index ee5338f394ac..6d9deb8dbcc8 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -1818,8 +1818,8 @@ const styles = (theme: ThemeColors) => }, travelIllustrationStyle: { - marginTop: 20, - marginBottom: -20, + marginTop: 16, + marginBottom: -16, }, overlayStyles: (current: OverlayStylesParams, isModalOnTheLeft: boolean) => From 24835cc067eacbc53b6220376f93cb182f809c3b Mon Sep 17 00:00:00 2001 From: smelaa Date: Thu, 18 Apr 2024 14:07:22 +0200 Subject: [PATCH 032/194] Fix spotnana travel beta --- src/libs/Permissions.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index f51158f2bef6..3473a181a4f8 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -37,7 +37,6 @@ function canUseWorkflowsDelayedSubmission(betas: OnyxEntry): boolean { } function canUseSpotnanaTravel(betas: OnyxEntry): boolean { - return true; return !!betas?.includes(CONST.BETAS.SPOTNANA_TRAVEL) || canUseAllBetas(betas); } From 09329008ae638604df026ae44ab5d2261ba53ca6 Mon Sep 17 00:00:00 2001 From: smelaa Date: Thu, 18 Apr 2024 15:03:52 +0200 Subject: [PATCH 033/194] Change card content padding --- src/components/FeatureList.tsx | 10 +++++----- src/components/Section/index.tsx | 6 +++++- src/pages/Travel/ManageTrips.tsx | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/components/FeatureList.tsx b/src/components/FeatureList.tsx index a9e928b28084..85f421731cfa 100644 --- a/src/components/FeatureList.tsx +++ b/src/components/FeatureList.tsx @@ -44,11 +44,11 @@ type FeatureListProps = { /** The background color to apply in the upper half of the screen. */ illustrationBackgroundColor?: string; - /** The style passed to the list container */ - containerStyles?: StyleProp; - /** The style used for the title */ titleStyles?: StyleProp; + + /** Padding for content on large screens */ + contentPaddingOnLargeScreens?: {padding: number}; }; function FeatureList({ @@ -61,8 +61,8 @@ function FeatureList({ illustration, illustrationStyle, illustrationBackgroundColor, - containerStyles, titleStyles, + contentPaddingOnLargeScreens, }: FeatureListProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -76,8 +76,8 @@ function FeatureList({ illustration={illustration} illustrationBackgroundColor={illustrationBackgroundColor} illustrationStyle={illustrationStyle} - containerStyles={containerStyles} titleStyles={titleStyles} + contentPaddingOnLargeScreens={contentPaddingOnLargeScreens} > diff --git a/src/components/Section/index.tsx b/src/components/Section/index.tsx index 848761c9e982..9ac4ebd58181 100644 --- a/src/components/Section/index.tsx +++ b/src/components/Section/index.tsx @@ -68,6 +68,9 @@ type SectionProps = ChildrenProps & { /** Styles to apply to illustration component */ illustrationStyle?: StyleProp; + /** Padding for content on large screens*/ + contentPaddingOnLargeScreens?: {padding: number}; + /** Overlay content to display on top of animation */ overlayContent?: () => ReactNode; @@ -92,6 +95,7 @@ function Section({ illustration, illustrationBackgroundColor, illustrationStyle, + contentPaddingOnLargeScreens, overlayContent, renderSubtitle, }: SectionProps) { @@ -124,7 +128,7 @@ function Section({ {overlayContent?.()} )} - + {cardLayout === CARD_LAYOUT.ICON_ON_LEFT && ( From f9149cc43093bd37b9086d0a85bd07092109f42c Mon Sep 17 00:00:00 2001 From: NikkiWines Date: Mon, 22 Apr 2024 16:37:29 +0200 Subject: [PATCH 034/194] fix typing --- src/pages/home/report/ReportActionsView.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index fbb71f62147c..1701f427f564 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -143,7 +143,10 @@ function ReportActionsView({ ); const parentReportActionForTransactionThread = useMemo( - () => (isEmptyObject(transactionThreadReportActions) ? null : allReportActions.find((action) => action.reportActionID === transactionThreadReport?.parentReportActionID)), + () => + isEmptyObject(transactionThreadReportActions) + ? null + : (allReportActions.find((action) => action.reportActionID === transactionThreadReport?.parentReportActionID) as OnyxEntry), [allReportActions, transactionThreadReportActions, transactionThreadReport?.parentReportActionID], ); From 55a56b4db3fef75e09c127bbf46bca649d04e0b8 Mon Sep 17 00:00:00 2001 From: Joe Ph <153004152+gijoe0295@users.noreply.github.com> Date: Mon, 22 Apr 2024 22:34:10 +0700 Subject: [PATCH 035/194] Fix typo Co-authored-by: Alex Beaman --- desktop/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/main.ts b/desktop/main.ts index 662278d540fa..0b5c59a8a78d 100644 --- a/desktop/main.ts +++ b/desktop/main.ts @@ -128,7 +128,7 @@ const quitAndInstallWithUpdate = () => { autoUpdater.quitAndInstall(); }; -/** Menu Item callback to triggers an update check */ +/** Menu Item callback to trigger an update check */ const manuallyCheckForUpdates = (menuItem?: MenuItem, browserWindow?: BrowserWindow) => { // Disable item until the check (and download) is complete if (menuItem) { From 6d319508fdf1cb39eafbb6c993ca0208d25b5dbc Mon Sep 17 00:00:00 2001 From: NikkiWines Date: Mon, 22 Apr 2024 23:56:16 +0200 Subject: [PATCH 036/194] lint --- src/libs/ReportActionsUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index f2228bc3018e..11e26652e253 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -831,6 +831,7 @@ function getOneTransactionThreadReportID(reportID: string, reportActions: OnyxEn // - they have an assocaited IOU transaction ID or // - they have visibile childActions (like comments) that we'd want to display // - the action is pending deletion and the user is offline + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing (Boolean(action.originalMessage.IOUTransactionID) || (isMessageDeleted(action) && action.childVisibleActionCount) || (action.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && (isOffline ?? isNetworkOffline))), From b1e1957d776ea96e7ea983c636883961cad3c8de Mon Sep 17 00:00:00 2001 From: NikkiWines Date: Tue, 23 Apr 2024 00:29:12 +0200 Subject: [PATCH 037/194] lint 2 --- src/libs/ReportActionsUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 11e26652e253..210965156155 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -831,8 +831,8 @@ function getOneTransactionThreadReportID(reportID: string, reportActions: OnyxEn // - they have an assocaited IOU transaction ID or // - they have visibile childActions (like comments) that we'd want to display // - the action is pending deletion and the user is offline - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing (Boolean(action.originalMessage.IOUTransactionID) || + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing (isMessageDeleted(action) && action.childVisibleActionCount) || (action.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && (isOffline ?? isNetworkOffline))), ); From 8a27a7d5a142064d404c00a55e19f7d91ae87d4e Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Tue, 23 Apr 2024 17:12:25 +0530 Subject: [PATCH 038/194] Save logs data in downloads and show meaningful path --- .../BaseClientSideLoggingToolMenu.tsx | 6 +++-- .../index.android.tsx | 1 + .../ClientSideLoggingToolMenu/index.ios.tsx | 1 + ...x.native.tsx => BaseProfilingToolMenu.tsx} | 26 ++++++++++++------- .../ProfilingToolMenu/index.android.tsx | 16 ++++++++++++ .../ProfilingToolMenu/index.ios.tsx | 16 ++++++++++++ 6 files changed, 54 insertions(+), 12 deletions(-) rename src/components/ProfilingToolMenu/{index.native.tsx => BaseProfilingToolMenu.tsx} (87%) create mode 100644 src/components/ProfilingToolMenu/index.android.tsx create mode 100644 src/components/ProfilingToolMenu/index.ios.tsx diff --git a/src/components/ClientSideLoggingToolMenu/BaseClientSideLoggingToolMenu.tsx b/src/components/ClientSideLoggingToolMenu/BaseClientSideLoggingToolMenu.tsx index fcad770908a6..e69da3ba1200 100644 --- a/src/components/ClientSideLoggingToolMenu/BaseClientSideLoggingToolMenu.tsx +++ b/src/components/ClientSideLoggingToolMenu/BaseClientSideLoggingToolMenu.tsx @@ -30,9 +30,11 @@ type BaseClientSideLoggingToolProps = { onDisableLogging: (logs: Log[]) => void; /** Action to run when enabling logging */ onEnableLogging?: () => void; + /** Path used to display location of saved file */ + displayPath: string; } & BaseClientSideLoggingToolMenuOnyxProps; -function BaseClientSideLoggingToolMenu({shouldStoreLogs, capturedLogs, file, onShareLogs, onDisableLogging, onEnableLogging}: BaseClientSideLoggingToolProps) { +function BaseClientSideLoggingToolMenu({shouldStoreLogs, capturedLogs, file, onShareLogs, onDisableLogging, onEnableLogging, displayPath}: BaseClientSideLoggingToolProps) { const {translate} = useLocalize(); const onToggle = () => { @@ -70,7 +72,7 @@ function BaseClientSideLoggingToolMenu({shouldStoreLogs, capturedLogs, file, onS {!!file && ( <> - {`path: ${file.path}`} + {`path: ${displayPath}/${file.newFileName}`}