From 0354131250232a1bcffa04acf90cdf6d0b3f890e Mon Sep 17 00:00:00 2001 From: miljakljajic Date: Fri, 29 Sep 2023 16:08:36 +0800 Subject: [PATCH 001/244] Update Uber.md Updating the Uber integration page --- .../integrations/travel-integrations/Uber.md | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/docs/articles/expensify-classic/integrations/travel-integrations/Uber.md b/docs/articles/expensify-classic/integrations/travel-integrations/Uber.md index 3ee1c8656b4b..b88cbc0aca39 100644 --- a/docs/articles/expensify-classic/integrations/travel-integrations/Uber.md +++ b/docs/articles/expensify-classic/integrations/travel-integrations/Uber.md @@ -1,5 +1,26 @@ --- -title: Coming Soon -description: Coming Soon +Uber integration +Connecting your Uber account to Expensify --- -## Resource Coming Soon! +## Overview + +Link Expensify directly to your Uber account so your Uber for Business receipts populate automatically in Expensify. + +# How to connect Uber to Expensify + +You can do this right in the Uber app: + +1. Head to Account > Business hub > Get started +2. Tap Create an individual account > Get started +3. Enter your business email and tap Next +4. Select the payment card you'd like to use for your business profile +5. Choose how frequently you’d like to receive travel summaries +6. Select Expensify as your expense provider +Expensify and Uber are now connected! + +Now, every time you use Uber for Business – be it for rides or meals – the receipt will be imported and scanned into Expensify automatically. + +![Alt text for accessibility] +(https://help.expensify.com/assets/images/Uber1.png){:width="100%"} +![Alt text for accessibility] +(https://help.expensify.com/assets/images/Uber2.png){:width="100%"} From ebaea0f0a39ff5750d1ef077748e49013462fd30 Mon Sep 17 00:00:00 2001 From: miljakljajic Date: Tue, 3 Oct 2023 10:59:43 +0200 Subject: [PATCH 002/244] Update docs/articles/expensify-classic/integrations/travel-integrations/Uber.md Co-authored-by: Rushat Gabhane --- .../expensify-classic/integrations/travel-integrations/Uber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/articles/expensify-classic/integrations/travel-integrations/Uber.md b/docs/articles/expensify-classic/integrations/travel-integrations/Uber.md index b88cbc0aca39..52b46a631367 100644 --- a/docs/articles/expensify-classic/integrations/travel-integrations/Uber.md +++ b/docs/articles/expensify-classic/integrations/travel-integrations/Uber.md @@ -1,5 +1,5 @@ --- -Uber integration +title: Uber integration Connecting your Uber account to Expensify --- ## Overview From b88fc1fe768e3aea1aac6d31d8f919b3d417be81 Mon Sep 17 00:00:00 2001 From: miljakljajic Date: Tue, 3 Oct 2023 10:59:50 +0200 Subject: [PATCH 003/244] Update docs/articles/expensify-classic/integrations/travel-integrations/Uber.md Co-authored-by: Rushat Gabhane --- .../expensify-classic/integrations/travel-integrations/Uber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/articles/expensify-classic/integrations/travel-integrations/Uber.md b/docs/articles/expensify-classic/integrations/travel-integrations/Uber.md index 52b46a631367..c4f2ed78f2cd 100644 --- a/docs/articles/expensify-classic/integrations/travel-integrations/Uber.md +++ b/docs/articles/expensify-classic/integrations/travel-integrations/Uber.md @@ -1,6 +1,6 @@ --- title: Uber integration -Connecting your Uber account to Expensify +description: Connecting your Uber account to Expensify --- ## Overview From 5bbde269e9a4d8a16c67558bc5dcde8cea007f05 Mon Sep 17 00:00:00 2001 From: miljakljajic Date: Wed, 4 Oct 2023 10:11:44 +0200 Subject: [PATCH 004/244] Update Uber.md Made the suggested updates to the accessibility description --- .../integrations/travel-integrations/Uber.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/articles/expensify-classic/integrations/travel-integrations/Uber.md b/docs/articles/expensify-classic/integrations/travel-integrations/Uber.md index c4f2ed78f2cd..73ad2c050cd4 100644 --- a/docs/articles/expensify-classic/integrations/travel-integrations/Uber.md +++ b/docs/articles/expensify-classic/integrations/travel-integrations/Uber.md @@ -20,7 +20,7 @@ Expensify and Uber are now connected! Now, every time you use Uber for Business – be it for rides or meals – the receipt will be imported and scanned into Expensify automatically. -![Alt text for accessibility] +![Uber integration set up steps: Connecting your account] (https://help.expensify.com/assets/images/Uber1.png){:width="100%"} -![Alt text for accessibility] +![Uber integration set up steps: Selecting Expensify] (https://help.expensify.com/assets/images/Uber2.png){:width="100%"} From 9117c61bae77f3cd18e3d7668bbd4e800bc60b42 Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Tue, 10 Oct 2023 09:22:16 +0200 Subject: [PATCH 005/244] [TS migration] Migrate 'AppNavigator' lib --- src/ONYXKEYS.ts | 1 + src/SCREENS.ts | 148 ++++++++++++ .../{AuthScreens.js => AuthScreens.tsx} | 123 +++++----- .../AppNavigator/ModalStackNavigators.js | 226 ------------------ .../AppNavigator/ModalStackNavigators.tsx | 226 ++++++++++++++++++ ...eNavigator.js => CentralPaneNavigator.tsx} | 5 +- .../Navigators/{Overlay.js => Overlay.tsx} | 25 +- ...alNavigator.js => RightModalNavigator.tsx} | 18 +- .../{PublicScreens.js => PublicScreens.tsx} | 11 +- ...HPScreenOptions.js => RHPScreenOptions.ts} | 4 +- ...eenIDSetter.js => ReportScreenIDSetter.ts} | 88 +++---- .../AppNavigator/ReportScreenWrapper.js | 43 ---- .../AppNavigator/ReportScreenWrapper.tsx | 25 ++ .../{CustomRouter.js => CustomRouter.ts} | 27 ++- .../createCustomStackNavigator/index.js | 60 ----- .../createCustomStackNavigator/index.tsx | 46 ++++ ...reenOptions.js => defaultScreenOptions.ts} | 4 +- ...ns.js => getRootNavigatorScreenOptions.ts} | 22 +- .../AppNavigator/{index.js => index.tsx} | 8 +- ...lator.js => modalCardStyleInterpolator.ts} | 3 +- src/libs/Navigation/AppNavigator/types.ts | 87 +++++++ src/libs/Navigation/Navigation.js | 2 +- src/libs/compose.ts | 27 +-- src/styles/cardStyles/types.ts | 2 +- src/styles/getModalStyles.ts | 1 + src/types/onyx/Report.ts | 3 + 26 files changed, 714 insertions(+), 521 deletions(-) rename src/libs/Navigation/AppNavigator/{AuthScreens.js => AuthScreens.tsx} (82%) delete mode 100644 src/libs/Navigation/AppNavigator/ModalStackNavigators.js create mode 100644 src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx rename src/libs/Navigation/AppNavigator/Navigators/{CentralPaneNavigator.js => CentralPaneNavigator.tsx} (89%) rename src/libs/Navigation/AppNavigator/Navigators/{Overlay.js => Overlay.tsx} (67%) rename src/libs/Navigation/AppNavigator/Navigators/{RightModalNavigator.js => RightModalNavigator.tsx} (88%) rename src/libs/Navigation/AppNavigator/{PublicScreens.js => PublicScreens.tsx} (84%) rename src/libs/Navigation/AppNavigator/{RHPScreenOptions.js => RHPScreenOptions.ts} (72%) rename src/libs/Navigation/AppNavigator/{ReportScreenIDSetter.js => ReportScreenIDSetter.ts} (54%) delete mode 100644 src/libs/Navigation/AppNavigator/ReportScreenWrapper.js create mode 100644 src/libs/Navigation/AppNavigator/ReportScreenWrapper.tsx rename src/libs/Navigation/AppNavigator/createCustomStackNavigator/{CustomRouter.js => CustomRouter.ts} (60%) delete mode 100644 src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js create mode 100644 src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx rename src/libs/Navigation/AppNavigator/{defaultScreenOptions.js => defaultScreenOptions.ts} (68%) rename src/libs/Navigation/AppNavigator/{getRootNavigatorScreenOptions.js => getRootNavigatorScreenOptions.ts} (71%) rename src/libs/Navigation/AppNavigator/{index.js => index.tsx} (73%) rename src/libs/Navigation/AppNavigator/{modalCardStyleInterpolator.js => modalCardStyleInterpolator.ts} (70%) create mode 100644 src/libs/Navigation/AppNavigator/types.ts diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 0a17d3a1d2f7..6685dd29a9c7 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -353,6 +353,7 @@ type OnyxValues = { [ONYXKEYS.IS_LOADING_REPORT_DATA]: boolean; [ONYXKEYS.IS_SHORTCUTS_MODAL_OPEN]: boolean; [ONYXKEYS.IS_TEST_TOOLS_MODAL_OPEN]: boolean; + [ONYXKEYS.IS_LOADING_APP]: boolean; [ONYXKEYS.WALLET_TRANSFER]: OnyxTypes.WalletTransfer; [ONYXKEYS.LAST_ACCESSED_WORKSPACE_POLICY_ID]: string; [ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT]: boolean; diff --git a/src/SCREENS.ts b/src/SCREENS.ts index eb125a43c239..1320d2541d93 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -10,6 +10,7 @@ export default { NOT_FOUND: 'not-found', TRANSITION_BETWEEN_APPS: 'TransitionBetweenApps', VALIDATE_LOGIN: 'ValidateLogin', + UNLINK_LOGIN: 'UnlinkLogin', CONCIERGE: 'Concierge', SETTINGS: { ROOT: 'Settings_Root', @@ -17,11 +18,158 @@ export default { WORKSPACES: 'Settings_Workspaces', SECURITY: 'Settings_Security', STATUS: 'Settings_Status', + PROFILE: 'Settings_Profile', + PRONOUNS: 'Settings_Pronouns', + DISPLAY_NAME: 'Settings_Display_Name', + TIMEZONE: 'Settings_Timezone', + TIMEZONE_SELECT: 'Settings_Timezone_Select', + PERSONAL_DETAILS_INITIAL: 'Settings_PersonalDetails_Initial', + PERSONAL_DETAILS_LEGAL_NAME: 'Settings_PersonalDetails_LegalName', + PERSONAL_DETAILS_DATE_OF_BIRTH: 'Settings_PersonalDetails_DateOfBirth', + PERSONAL_DETAILS_ADDRESS: 'Settings_PersonalDetails_Address', + PERSONAL_DETAILS_ADDRESS_COUNTRY: 'Settings_PersonalDetails_Address_Country', + CONTACT_METHODS: 'Settings_ContactMethods', + CONTACT_METHOD_DETAILS: 'Settings_ContactMethodDetails', + NEW_CONTACT_METHOD: 'Settings_NewContactMethod', + SHARE_CODE: 'Settings_Share_Code', + ABOUT: 'Settings_About', + APP_DOWNLOAD_LINKS: 'Settings_App_Download_Links', + LOUNGE_ACCESS: 'Settings_Lounge_Access', + WALLET: 'Settings_Wallet', + WALLET_DOMAIN_CARDS: 'Settings_Wallet_DomainCards', + WALLET_TRANSFER_BALANCE: 'Settings_Wallet_Transfer_Balance', + WALLET_CHOOSE_TRANSFER_ACCOUNT: 'Settings_Wallet_Choose_Transfer_Account', + WALLET_ENABLE_PAYMENTS: 'Settings_Wallet_EnablePayments', + ADD_DEBIT_CARD: 'Settings_Add_Debit_Card', + ADD_BANK_ACCOUNT: 'Settings_Add_Bank_Account', + PREFERENCES_PRIORITY_MODE: 'Settings_Preferences_PriorityMode', + PREFERENCES_LANGUAGE: 'Settings_Preferences_Language', + CLOSE: 'Settings_Close', + STATUS_SET: 'Settings_Status_Set', + TWO_FACTOR_AUTH: 'Settings_TwoFactorAuth', }, SAVE_THE_WORLD: { ROOT: 'SaveTheWorld_Root', }, + RIGHT_MODAL: { + SETTINGS: 'Settings', + NEW_CHAT: 'NewChat', + SEARCH: 'Search', + DETAILS: 'Details', + PROFILE: 'Profile', + REPORT_DETAILS: 'Report_Details', + REPORT_SETTINGS: 'Report_Settings', + REPORT_WELCOME_MESSAGE: 'Report_WelcomeMessage', + PARTICIPANTS: 'Participants', + MONEY_REQUEST: 'MoneyRequest', + NEW_TASK: 'NewTask', + TEACHERS_UNITE: 'TeachersUnite', + TASK_DETAILS: 'Task_Details', + ENABLE_PAYMENTS: 'EnablePayments', + SPLIT_DETAILS: 'SplitDetails', + ADD_PERSONAL_BANK_ACCOUNT: 'AddPersonalBankAccount', + WALLET_STATEMENT: 'Wallet_Statement', + FLAG_COMMENT: 'Flag_Comment', + EDIT_REQUEST: 'EditRequest', + SIGN_IN: 'SignIn', + PRIVATE_NOTES: 'Private_Notes', + }, SIGN_IN_WITH_APPLE_DESKTOP: 'AppleSignInDesktop', SIGN_IN_WITH_GOOGLE_DESKTOP: 'GoogleSignInDesktop', DESKTOP_SIGN_IN_REDIRECT: 'DesktopSignInRedirect', + + MONEY_REQUEST: { + ROOT: 'Money_Request', + AMOUNT: 'Money_Request_Amount', + PARTICIPANTS: 'Money_Request_Participants', + CONFIRMATION: 'Money_Request_Confirmation', + CURRENCY: 'Money_Request_Currency', + DATE: 'Money_Request_Date', + DESCRIPTION: 'Money_Request_Description', + CATEGORY: 'Money_Request_Category', + TAG: 'Money_Request_Tag', + MERCHANT: 'Money_Request_Merchant', + WAYPOINT: 'Money_Request_Waypoint', + EDIT_WAYPOINT: 'Money_Request_Edit_Waypoint', + DISTANCE: 'Money_Request_Distance', + RECEIPT: 'Money_Request_Receipt', + }, + + IOU_SEND: { + ADD_BANK_ACCOUNT: 'IOU_Send_Add_Bank_Account', + ADD_DEBIT_CARD: 'IOU_Send_Add_Debit_Card', + ENABLE_PAYMENTS: 'IOU_Send_Enable_Payments', + }, + + REPORT_SETTINGS: { + ROOT: 'Report_Settings_Root', + ROOM_NAME: 'Report_Settings_Room_Name', + NOTIFICATION_PREFERENCES: 'Report_Settings_Notification_Preferences', + WRITE_CAPABILITY: 'Report_Settings_Write_Capability', + }, + + NEW_TASK: { + ROOT: 'NewTask_Root', + TASK_ASSIGNEE_SELECTOR: 'NewTask_TaskAssigneeSelector', + TASK_SHARE_DESTINATION_SELECTOR: 'NewTask_TaskShareDestinationSelector', + DETAILS: 'NewTask_Details', + TITLE: 'NewTask_Title', + DESCRIPTION: 'NewTask_Description', + }, + + TASK: { + TITLE: 'Task_Title', + DESCRIPTION: 'Task_Description', + ASSIGNEE: 'Task_Assignee', + }, + + PRIVATE_NOTES: { + VIEW: 'PrivateNotes_View', + LIST: 'PrivateNotes_List', + EDIT: 'PrivateNotes_Edit', + }, + + REPORT_DETAILS: { + ROOT: 'Report_Details_Root', + SHARE_CODE: 'Report_Details_Share_Code', + }, + + WORKSPACE: { + INITIAL: 'Workspace_Initial', + SETTINGS: 'Workspace_Settings', + CARD: 'Workspace_Card', + REIMBURSE: 'Workspace_Reimburse', + RATE_AND_UNIT: 'Workspace_RateAndUnit', + BILLS: 'Workspace_Bills', + INVOICES: 'Workspace_Invoices', + TRAVEL: 'Workspace_Travel', + MEMBERS: 'Workspace_Members', + INVITE: 'Workspace_Invite', + INVITE_MESSAGE: 'Workspace_Invite_Message', + }, + + EDIT_REQUEST: { + ROOT: 'EditRequest_Root', + CURRENCY: 'EditRequest_Currency', + }, + + I_KNOW_A_TEACHER: 'I_Know_A_Teacher', + INTRO_SCHOOL_PRINCIPAL: 'Intro_School_Principal', + I_AM_A_TEACHER: 'I_Am_A_Teacher', + + ENABLE_PAYMENTS_ROOT: 'EnablePayments_Root', + ADD_PERSONAL_BANK_ACCOUNT_ROOT: 'AddPersonalBankAccount_Root', + REIMBURSEMENT_ACCOUNT_ROOT: 'Reimbursement_Account_Root', + WALLET_STATEMENT_ROOT: 'WalletStatement_Root', + SIGN_IN_ROOT: 'SignIn_Root', + SPLIT_DETAILS_ROOT: 'SplitDetails_Root', + DETAILS_ROOT: 'Details_Root', + PROFILE_ROOT: 'Profile_Root', + REPORT_WELCOME_MESSAGE_ROOT: 'Report_WelcomeMessage_Root', + REPORT_PARTICIPANTS_ROOT: 'ReportParticipants_Root', + SEARCH_ROOT: 'Search_Root', + NEW_CHAT_ROOT: 'NewChat_Root', + FLAG_COMMENT_ROOT: 'FlagComment_Root', + REIMBURSEMENT_ACCOUNT: 'ReimbursementAccount', + GET_ASSISTANCE: 'GetAssistance', } as const; diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.tsx similarity index 82% rename from src/libs/Navigation/AppNavigator/AuthScreens.js rename to src/libs/Navigation/AppNavigator/AuthScreens.tsx index 428550a43aa8..5e8f471d4a79 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -1,10 +1,7 @@ import React from 'react'; -import Onyx, {withOnyx} from 'react-native-onyx'; -import PropTypes from 'prop-types'; -import _ from 'underscore'; -import lodashGet from 'lodash/get'; +import Onyx, {OnyxEntry, withOnyx} from 'react-native-onyx'; import {View} from 'react-native'; -import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions'; +import withWindowDimensions from '../../../components/withWindowDimensions'; import CONST from '../../../CONST'; import compose from '../../compose'; import * as PersonalDetails from '../../actions/PersonalDetails'; @@ -35,16 +32,43 @@ import * as SessionUtils from '../../SessionUtils'; import NotFoundPage from '../../../pages/ErrorPage/NotFoundPage'; import getRootNavigatorScreenOptions from './getRootNavigatorScreenOptions'; import DemoSetupPage from '../../../pages/DemoSetupPage'; +import * as OnyxTypes from '../../../types/onyx'; +import type {WindowDimensions} from '../../../styles/getModalStyles'; +import type {AuthScreensStackParamList} from './types'; -let timezone; -let currentAccountID; -let isLoadingApp; +type AuthScreensOnyxProps = { + /** Session of currently logged in user */ + session: OnyxEntry; + + /** The report ID of the last opened public room as anonymous user */ + lastOpenedPublicRoomID: OnyxEntry; + + /** Opt-in experimental mode that prevents certain Onyx keys from persisting to disk */ + isUsingMemoryOnlyKeys: OnyxEntry; + + /** The last Onyx update ID was applied to the client */ + lastUpdateIDAppliedToClient: OnyxEntry; +}; + +type AuthScreensProps = WindowDimensions & AuthScreensOnyxProps; + +type Timezone = { + automatic?: boolean; + selected?: string; +}; + +type UnsubscribeChatShortcut = () => void; +type UnsubscribeSearchShortcut = () => void; + +let timezone: Timezone | null; +let currentAccountID: number; +let isLoadingApp: boolean; Onyx.connect({ key: ONYXKEYS.SESSION, callback: (val) => { // When signed out, val hasn't accountID - if (!_.has(val, 'accountID')) { + if (!val?.accountID) { timezone = null; return; } @@ -52,7 +76,7 @@ Onyx.connect({ currentAccountID = val.accountID; if (Navigation.isActiveRoute(ROUTES.SIGN_IN_MODAL)) { // This means sign in in RHP was successful, so we can dismiss the modal and subscribe to user events - Navigation.dismissModal(); + Navigation.dismissModal(undefined); User.subscribeToUserEvents(); } }, @@ -65,12 +89,12 @@ Onyx.connect({ return; } - timezone = lodashGet(val, [currentAccountID, 'timezone'], {}); + timezone = val?.[currentAccountID]?.timezone ?? {}; const currentTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; // If the current timezone is different than the user's timezone, and their timezone is set to automatic // then update their timezone. - if (_.isObject(timezone) && timezone.automatic && timezone.selected !== currentTimezone) { + if (timezone !== null && timezone.automatic && timezone.selected !== currentTimezone) { timezone.selected = currentTimezone; PersonalDetails.updateAutomaticTimezone({ automatic: true, @@ -83,11 +107,11 @@ Onyx.connect({ Onyx.connect({ key: ONYXKEYS.IS_LOADING_APP, callback: (val) => { - isLoadingApp = val; + isLoadingApp = !!val; }, }); -const RootStack = createCustomStackNavigator(); +const RootStack = createCustomStackNavigator(); // We want to delay the re-rendering for components(e.g. ReportActionCompose) // that depends on modal visibility until Modal is completely closed and its focused @@ -102,35 +126,14 @@ const modalScreenListeners = { }, }; -const propTypes = { - /** Session of currently logged in user */ - session: PropTypes.shape({ - email: PropTypes.string.isRequired, - }), - - /** The report ID of the last opened public room as anonymous user */ - lastOpenedPublicRoomID: PropTypes.string, +class AuthScreens extends React.Component { + unsubscribeSearchShortcut?: UnsubscribeSearchShortcut; - /** Opt-in experimental mode that prevents certain Onyx keys from persisting to disk */ - isUsingMemoryOnlyKeys: PropTypes.bool, + unsubscribeChatShortcut?: UnsubscribeChatShortcut; - /** The last Onyx update ID was applied to the client */ - lastUpdateIDAppliedToClient: PropTypes.number, - - ...windowDimensionsPropTypes, -}; + interval?: number; -const defaultProps = { - isUsingMemoryOnlyKeys: false, - session: { - email: null, - }, - lastOpenedPublicRoomID: null, - lastUpdateIDAppliedToClient: null, -}; - -class AuthScreens extends React.Component { - constructor(props) { + constructor(props: AuthScreensProps) { super(props); Timing.start(CONST.TIMING.HOMEPAGE_INITIAL_RENDER); @@ -142,7 +145,7 @@ class AuthScreens extends React.Component { if (isLoadingApp) { App.openApp(); } else { - App.reconnectApp(this.props.lastUpdateIDAppliedToClient); + App.reconnectApp(this.props.lastUpdateIDAppliedToClient ?? 0); } }); PusherConnectionManager.init(); @@ -159,14 +162,16 @@ class AuthScreens extends React.Component { // Note: If a Guide has enabled the memory only key mode then we do want to run OpenApp as their app will not be rehydrated with // the correct state on refresh. They are explicitly opting out of storing data they would need (i.e. reports_) to take advantage of // the optimizations performed during ReconnectApp. - const shouldGetAllData = this.props.isUsingMemoryOnlyKeys || SessionUtils.didUserLogInDuringSession(); + const shouldGetAllData = !!this.props.isUsingMemoryOnlyKeys || SessionUtils.didUserLogInDuringSession(); if (shouldGetAllData) { App.openApp(); } else { - App.reconnectApp(this.props.lastUpdateIDAppliedToClient); + App.reconnectApp(this.props.lastUpdateIDAppliedToClient ?? 0); } - App.setUpPoliciesAndNavigate(this.props.session, !this.props.isSmallScreenWidth); + if (this.props.session) { + App.setUpPoliciesAndNavigate(this.props.session, !this.props.isSmallScreenWidth); + } App.redirectThirdPartyDesktopSignIn(); if (this.props.lastOpenedPublicRoomID) { @@ -193,9 +198,9 @@ class AuthScreens extends React.Component { }); }, searchShortcutConfig.descriptionKey, - searchShortcutConfig.modifiers, + [...searchShortcutConfig.modifiers], true, - ); + ) as UnsubscribeSearchShortcut; this.unsubscribeChatShortcut = KeyboardShortcut.subscribe( chatShortcutConfig.shortcutKey, () => { @@ -207,12 +212,12 @@ class AuthScreens extends React.Component { }); }, chatShortcutConfig.descriptionKey, - chatShortcutConfig.modifiers, + [...chatShortcutConfig.modifiers], true, - ); + ) as UnsubscribeChatShortcut; } - shouldComponentUpdate(nextProps) { + shouldComponentUpdate(nextProps: AuthScreensProps) { return nextProps.windowHeight !== this.props.windowHeight || nextProps.isSmallScreenWidth !== this.props.isSmallScreenWidth; } @@ -225,7 +230,7 @@ class AuthScreens extends React.Component { } Session.cleanupSession(); clearInterval(this.interval); - this.interval = null; + this.interval = undefined; } render() { @@ -235,18 +240,16 @@ class AuthScreens extends React.Component { { - const SidebarScreen = require('../../../pages/home/sidebar/SidebarScreen').default; + const SidebarScreen = require('../../../pages/home/sidebar/SidebarScreen').default as React.ComponentType; return SidebarScreen; }} /> @@ -263,7 +266,7 @@ class AuthScreens extends React.Component { title: 'New Expensify', }} getComponent={() => { - const ValidateLoginPage = require('../../../pages/ValidateLoginPage').default; + const ValidateLoginPage = require('../../../pages/ValidateLoginPage').default as React.ComponentType; return ValidateLoginPage; }} /> @@ -271,7 +274,7 @@ class AuthScreens extends React.Component { name={SCREENS.TRANSITION_BETWEEN_APPS} options={defaultScreenOptions} getComponent={() => { - const LogOutPreviousUserPage = require('../../../pages/LogOutPreviousUserPage').default; + const LogOutPreviousUserPage = require('../../../pages/LogOutPreviousUserPage').default as React.ComponentType; return LogOutPreviousUserPage; }} /> @@ -279,7 +282,7 @@ class AuthScreens extends React.Component { name={SCREENS.CONCIERGE} options={defaultScreenOptions} getComponent={() => { - const ConciergePage = require('../../../pages/ConciergePage').default; + const ConciergePage = require('../../../pages/ConciergePage').default as React.ComponentType; return ConciergePage; }} /> @@ -300,7 +303,7 @@ class AuthScreens extends React.Component { presentation: 'transparentModal', }} getComponent={() => { - const ReportAttachments = require('../../../pages/home/report/ReportAttachments').default; + const ReportAttachments = require('../../../pages/home/report/ReportAttachments').default as React.ComponentType; return ReportAttachments; }} listeners={modalScreenListeners} @@ -327,11 +330,9 @@ class AuthScreens extends React.Component { } } -AuthScreens.propTypes = propTypes; -AuthScreens.defaultProps = defaultProps; export default compose( withWindowDimensions, - withOnyx({ + withOnyx({ session: { key: ONYXKEYS.SESSION, }, diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js deleted file mode 100644 index 6636702592c0..000000000000 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ /dev/null @@ -1,226 +0,0 @@ -import _ from 'underscore'; -import React from 'react'; -import {createStackNavigator, CardStyleInterpolators} from '@react-navigation/stack'; -import styles from '../../../styles/styles'; -import SCREENS from '../../../SCREENS'; - -const defaultSubRouteOptions = { - cardStyle: styles.navigationScreenCardStyle, - headerShown: false, - cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS, -}; - -/** - * Create a modal stack navigator with an array of sub-screens. - * - * @param {Object} screens key/value pairs where the key is the name of the screen and the value is a functon that returns the lazy-loaded component - * @returns {Function} - */ -function createModalStackNavigator(screens) { - const ModalStackNavigator = createStackNavigator(); - return () => ( - - {_.map(screens, (getComponent, name) => ( - - ))} - - ); -} - -const MoneyRequestModalStackNavigator = createModalStackNavigator({ - Money_Request: () => require('../../../pages/iou/MoneyRequestSelectorPage').default, - Money_Request_Amount: () => require('../../../pages/iou/steps/NewRequestAmountPage').default, - Money_Request_Participants: () => require('../../../pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage').default, - Money_Request_Confirmation: () => require('../../../pages/iou/steps/MoneyRequestConfirmPage').default, - Money_Request_Currency: () => require('../../../pages/iou/IOUCurrencySelection').default, - Money_Request_Date: () => require('../../../pages/iou/MoneyRequestDatePage').default, - Money_Request_Description: () => require('../../../pages/iou/MoneyRequestDescriptionPage').default, - Money_Request_Category: () => require('../../../pages/iou/MoneyRequestCategoryPage').default, - Money_Request_Tag: () => require('../../../pages/iou/MoneyRequestTagPage').default, - Money_Request_Merchant: () => require('../../../pages/iou/MoneyRequestMerchantPage').default, - IOU_Send_Add_Bank_Account: () => require('../../../pages/AddPersonalBankAccountPage').default, - IOU_Send_Add_Debit_Card: () => require('../../../pages/settings/Wallet/AddDebitCardPage').default, - IOU_Send_Enable_Payments: () => require('../../../pages/EnablePayments/EnablePaymentsPage').default, - Money_Request_Waypoint: () => require('../../../pages/iou/NewDistanceRequestWaypointEditorPage').default, - Money_Request_Edit_Waypoint: () => require('../../../pages/iou/MoneyRequestEditWaypointPage').default, - Money_Request_Distance: () => require('../../../pages/iou/NewDistanceRequestPage').default, - Money_Request_Receipt: () => require('../../../pages/EditRequestReceiptPage').default, -}); - -const SplitDetailsModalStackNavigator = createModalStackNavigator({ - SplitDetails_Root: () => require('../../../pages/iou/SplitBillDetailsPage').default, -}); - -const DetailsModalStackNavigator = createModalStackNavigator({ - Details_Root: () => require('../../../pages/DetailsPage').default, -}); - -const ProfileModalStackNavigator = createModalStackNavigator({ - Profile_Root: () => require('../../../pages/ProfilePage').default, -}); - -const ReportDetailsModalStackNavigator = createModalStackNavigator({ - Report_Details_Root: () => require('../../../pages/ReportDetailsPage').default, - Report_Details_Share_Code: () => require('../../../pages/home/report/ReportDetailsShareCodePage').default, -}); - -const ReportSettingsModalStackNavigator = createModalStackNavigator({ - Report_Settings_Root: () => require('../../../pages/settings/Report/ReportSettingsPage').default, - Report_Settings_Room_Name: () => require('../../../pages/settings/Report/RoomNamePage').default, - Report_Settings_Notification_Preferences: () => require('../../../pages/settings/Report/NotificationPreferencePage').default, - Report_Settings_Write_Capability: () => require('../../../pages/settings/Report/WriteCapabilityPage').default, -}); - -const TaskModalStackNavigator = createModalStackNavigator({ - Task_Title: () => require('../../../pages/tasks/TaskTitlePage').default, - Task_Description: () => require('../../../pages/tasks/TaskDescriptionPage').default, - Task_Assignee: () => require('../../../pages/tasks/TaskAssigneeSelectorModal').default, -}); - -const ReportWelcomeMessageModalStackNavigator = createModalStackNavigator({ - Report_WelcomeMessage_Root: () => require('../../../pages/ReportWelcomeMessagePage').default, -}); - -const ReportParticipantsModalStackNavigator = createModalStackNavigator({ - ReportParticipants_Root: () => require('../../../pages/ReportParticipantsPage').default, -}); - -const SearchModalStackNavigator = createModalStackNavigator({ - Search_Root: () => require('../../../pages/SearchPage').default, -}); - -const NewChatModalStackNavigator = createModalStackNavigator({ - NewChat_Root: () => require('../../../pages/NewChatSelectorPage').default, -}); - -const NewTaskModalStackNavigator = createModalStackNavigator({ - NewTask_Root: () => require('../../../pages/tasks/NewTaskPage').default, - NewTask_TaskAssigneeSelector: () => require('../../../pages/tasks/TaskAssigneeSelectorModal').default, - NewTask_TaskShareDestinationSelector: () => require('../../../pages/tasks/TaskShareDestinationSelectorModal').default, - NewTask_Details: () => require('../../../pages/tasks/NewTaskDetailsPage').default, - NewTask_Title: () => require('../../../pages/tasks/NewTaskTitlePage').default, - NewTask_Description: () => require('../../../pages/tasks/NewTaskDescriptionPage').default, -}); - -const NewTeachersUniteNavigator = createModalStackNavigator({ - [SCREENS.SAVE_THE_WORLD.ROOT]: () => require('../../../pages/TeachersUnite/SaveTheWorldPage').default, - I_Know_A_Teacher: () => require('../../../pages/TeachersUnite/KnowATeacherPage').default, - Intro_School_Principal: () => require('../../../pages/TeachersUnite/ImTeacherPage').default, - I_Am_A_Teacher: () => require('../../../pages/TeachersUnite/ImTeacherPage').default, -}); - -const SettingsModalStackNavigator = createModalStackNavigator({ - [SCREENS.SETTINGS.ROOT]: () => require('../../../pages/settings/InitialSettingsPage').default, - Settings_Share_Code: () => require('../../../pages/ShareCodePage').default, - [SCREENS.SETTINGS.WORKSPACES]: () => require('../../../pages/workspace/WorkspacesListPage').default, - Settings_Profile: () => require('../../../pages/settings/Profile/ProfilePage').default, - Settings_Pronouns: () => require('../../../pages/settings/Profile/PronounsPage').default, - Settings_Display_Name: () => require('../../../pages/settings/Profile/DisplayNamePage').default, - Settings_Timezone: () => require('../../../pages/settings/Profile/TimezoneInitialPage').default, - Settings_Timezone_Select: () => require('../../../pages/settings/Profile/TimezoneSelectPage').default, - Settings_PersonalDetails_Initial: () => require('../../../pages/settings/Profile/PersonalDetails/PersonalDetailsInitialPage').default, - Settings_PersonalDetails_LegalName: () => require('../../../pages/settings/Profile/PersonalDetails/LegalNamePage').default, - Settings_PersonalDetails_DateOfBirth: () => require('../../../pages/settings/Profile/PersonalDetails/DateOfBirthPage').default, - Settings_PersonalDetails_Address: () => require('../../../pages/settings/Profile/PersonalDetails/AddressPage').default, - Settings_PersonalDetails_Address_Country: () => require('../../../pages/settings/Profile/PersonalDetails/CountrySelectionPage').default, - Settings_ContactMethods: () => require('../../../pages/settings/Profile/Contacts/ContactMethodsPage').default, - Settings_ContactMethodDetails: () => require('../../../pages/settings/Profile/Contacts/ContactMethodDetailsPage').default, - Settings_NewContactMethod: () => require('../../../pages/settings/Profile/Contacts/NewContactMethodPage').default, - [SCREENS.SETTINGS.PREFERENCES]: () => require('../../../pages/settings/Preferences/PreferencesPage').default, - Settings_Preferences_PriorityMode: () => require('../../../pages/settings/Preferences/PriorityModePage').default, - Settings_Preferences_Language: () => require('../../../pages/settings/Preferences/LanguagePage').default, - // Will be uncommented as part of https://github.com/Expensify/App/issues/21670 - // Settings_Preferences_Theme: () => require('../../../pages/settings/Preferences/ThemePage').default, - Settings_Close: () => require('../../../pages/settings/Security/CloseAccountPage').default, - [SCREENS.SETTINGS.SECURITY]: () => require('../../../pages/settings/Security/SecuritySettingsPage').default, - Settings_About: () => require('../../../pages/settings/AboutPage/AboutPage').default, - Settings_App_Download_Links: () => require('../../../pages/settings/AppDownloadLinks').default, - Settings_Lounge_Access: () => require('../../../pages/settings/Profile/LoungeAccessPage').default, - Settings_Wallet: () => require('../../../pages/settings/Wallet/WalletPage').default, - Settings_Wallet_DomainCards: () => require('../../../pages/settings/Wallet/ExpensifyCardPage').default, - Settings_Wallet_Transfer_Balance: () => require('../../../pages/settings/Wallet/TransferBalancePage').default, - Settings_Wallet_Choose_Transfer_Account: () => require('../../../pages/settings/Wallet/ChooseTransferAccountPage').default, - Settings_Wallet_EnablePayments: () => require('../../../pages/EnablePayments/EnablePaymentsPage').default, - Settings_Add_Debit_Card: () => require('../../../pages/settings/Wallet/AddDebitCardPage').default, - Settings_Add_Bank_Account: () => require('../../../pages/AddPersonalBankAccountPage').default, - [SCREENS.SETTINGS.STATUS]: () => require('../../../pages/settings/Profile/CustomStatus/StatusPage').default, - Settings_Status_Set: () => require('../../../pages/settings/Profile/CustomStatus/StatusSetPage').default, - Workspace_Initial: () => require('../../../pages/workspace/WorkspaceInitialPage').default, - Workspace_Settings: () => require('../../../pages/workspace/WorkspaceSettingsPage').default, - Workspace_Card: () => require('../../../pages/workspace/card/WorkspaceCardPage').default, - Workspace_Reimburse: () => require('../../../pages/workspace/reimburse/WorkspaceReimbursePage').default, - Workspace_RateAndUnit: () => require('../../../pages/workspace/reimburse/WorkspaceRateAndUnitPage').default, - Workspace_Bills: () => require('../../../pages/workspace/bills/WorkspaceBillsPage').default, - Workspace_Invoices: () => require('../../../pages/workspace/invoices/WorkspaceInvoicesPage').default, - Workspace_Travel: () => require('../../../pages/workspace/travel/WorkspaceTravelPage').default, - Workspace_Members: () => require('../../../pages/workspace/WorkspaceMembersPage').default, - Workspace_Invite: () => require('../../../pages/workspace/WorkspaceInvitePage').default, - Workspace_Invite_Message: () => require('../../../pages/workspace/WorkspaceInviteMessagePage').default, - ReimbursementAccount: () => require('../../../pages/ReimbursementAccount/ReimbursementAccountPage').default, - GetAssistance: () => require('../../../pages/GetAssistancePage').default, - Settings_TwoFactorAuth: () => require('../../../pages/settings/Security/TwoFactorAuth/TwoFactorAuthPage').default, -}); - -const EnablePaymentsStackNavigator = createModalStackNavigator({ - EnablePayments_Root: () => require('../../../pages/EnablePayments/EnablePaymentsPage').default, -}); - -const AddPersonalBankAccountModalStackNavigator = createModalStackNavigator({ - AddPersonalBankAccount_Root: () => require('../../../pages/AddPersonalBankAccountPage').default, -}); - -const ReimbursementAccountModalStackNavigator = createModalStackNavigator({ - ReimbursementAccount_Root: () => require('../../../pages/ReimbursementAccount/ReimbursementAccountPage').default, -}); - -const WalletStatementStackNavigator = createModalStackNavigator({ - WalletStatement_Root: () => require('../../../pages/wallet/WalletStatementPage').default, -}); - -const FlagCommentStackNavigator = createModalStackNavigator({ - FlagComment_Root: () => require('../../../pages/FlagCommentPage').default, -}); - -const EditRequestStackNavigator = createModalStackNavigator({ - EditRequest_Root: () => require('../../../pages/EditRequestPage').default, - EditRequest_Currency: () => require('../../../pages/iou/IOUCurrencySelection').default, -}); - -const PrivateNotesModalStackNavigator = createModalStackNavigator({ - PrivateNotes_View: () => require('../../../pages/PrivateNotes/PrivateNotesViewPage').default, - PrivateNotes_List: () => require('../../../pages/PrivateNotes/PrivateNotesListPage').default, - PrivateNotes_Edit: () => require('../../../pages/PrivateNotes/PrivateNotesEditPage').default, -}); - -const SignInModalStackNavigator = createModalStackNavigator({ - SignIn_Root: () => require('../../../pages/signin/SignInModal').default, -}); - -export { - MoneyRequestModalStackNavigator, - SplitDetailsModalStackNavigator, - DetailsModalStackNavigator, - ProfileModalStackNavigator, - ReportDetailsModalStackNavigator, - TaskModalStackNavigator, - ReportSettingsModalStackNavigator, - ReportWelcomeMessageModalStackNavigator, - ReportParticipantsModalStackNavigator, - SearchModalStackNavigator, - NewChatModalStackNavigator, - NewTaskModalStackNavigator, - SettingsModalStackNavigator, - EnablePaymentsStackNavigator, - AddPersonalBankAccountModalStackNavigator, - ReimbursementAccountModalStackNavigator, - WalletStatementStackNavigator, - FlagCommentStackNavigator, - EditRequestStackNavigator, - PrivateNotesModalStackNavigator, - NewTeachersUniteNavigator, - SignInModalStackNavigator, -}; diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx new file mode 100644 index 000000000000..9e8cf9680e73 --- /dev/null +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -0,0 +1,226 @@ +import React from 'react'; +import {createStackNavigator, CardStyleInterpolators} from '@react-navigation/stack'; +import styles from '../../../styles/styles'; +import SCREENS from '../../../SCREENS'; + +const defaultSubRouteOptions = { + cardStyle: styles.navigationScreenCardStyle, + headerShown: false, + cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS, +}; + +type Screens = Record React.ComponentType>; + +/** + * Create a modal stack navigator with an array of sub-screens. + * + * @param screens key/value pairs where the key is the name of the screen and the value is a functon that returns the lazy-loaded component + */ +function createModalStackNavigator(screens: Screens): () => React.JSX.Element { + const ModalStackNavigator = createStackNavigator(); + return () => ( + + {Object.keys(screens).map((name) => ( + + ))} + + ); +} + +const MoneyRequestModalStackNavigator = createModalStackNavigator({ + [SCREENS.MONEY_REQUEST.ROOT]: () => require('../../../pages/iou/MoneyRequestSelectorPage').default as React.ComponentType, + [SCREENS.MONEY_REQUEST.AMOUNT]: () => require('../../../pages/iou/steps/NewRequestAmountPage').default as React.ComponentType, + [SCREENS.MONEY_REQUEST.PARTICIPANTS]: () => require('../../../pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage').default as React.ComponentType, + [SCREENS.MONEY_REQUEST.CONFIRMATION]: () => require('../../../pages/iou/steps/MoneyRequestConfirmPage').default as React.ComponentType, + [SCREENS.MONEY_REQUEST.CURRENCY]: () => require('../../../pages/iou/IOUCurrencySelection').default as React.ComponentType, + [SCREENS.MONEY_REQUEST.DATE]: () => require('../../../pages/iou/MoneyRequestDatePage').default as React.ComponentType, + [SCREENS.MONEY_REQUEST.DESCRIPTION]: () => require('../../../pages/iou/MoneyRequestDescriptionPage').default as React.ComponentType, + [SCREENS.MONEY_REQUEST.CATEGORY]: () => require('../../../pages/iou/MoneyRequestCategoryPage').default as React.ComponentType, + [SCREENS.MONEY_REQUEST.TAG]: () => require('../../../pages/iou/MoneyRequestTagPage').default as React.ComponentType, + [SCREENS.MONEY_REQUEST.MERCHANT]: () => require('../../../pages/iou/MoneyRequestMerchantPage').default as React.ComponentType, + [SCREENS.IOU_SEND.ADD_BANK_ACCOUNT]: () => require('../../../pages/AddPersonalBankAccountPage').default as React.ComponentType, + [SCREENS.IOU_SEND.ADD_DEBIT_CARD]: () => require('../../../pages/settings/Wallet/AddDebitCardPage').default as React.ComponentType, + [SCREENS.IOU_SEND.ENABLE_PAYMENTS]: () => require('../../../pages/EnablePayments/EnablePaymentsPage').default as React.ComponentType, + [SCREENS.MONEY_REQUEST.WAYPOINT]: () => require('../../../pages/iou/NewDistanceRequestWaypointEditorPage').default as React.ComponentType, + [SCREENS.MONEY_REQUEST.EDIT_WAYPOINT]: () => require('../../../pages/iou/MoneyRequestEditWaypointPage').default as React.ComponentType, + [SCREENS.MONEY_REQUEST.DISTANCE]: () => require('../../../pages/iou/NewDistanceRequestPage').default as React.ComponentType, + [SCREENS.MONEY_REQUEST.RECEIPT]: () => require('../../../pages/EditRequestReceiptPage').default as React.ComponentType, +}); + +const SplitDetailsModalStackNavigator = createModalStackNavigator({ + [SCREENS.SPLIT_DETAILS_ROOT]: () => require('../../../pages/iou/SplitBillDetailsPage').default as React.ComponentType, +}); + +const DetailsModalStackNavigator = createModalStackNavigator({ + [SCREENS.DETAILS_ROOT]: () => require('../../../pages/DetailsPage').default as React.ComponentType, +}); + +const ProfileModalStackNavigator = createModalStackNavigator({ + [SCREENS.PROFILE_ROOT]: () => require('../../../pages/ProfilePage').default as React.ComponentType, +}); + +const ReportDetailsModalStackNavigator = createModalStackNavigator({ + [SCREENS.REPORT_DETAILS.ROOT]: () => require('../../../pages/ReportDetailsPage').default as React.ComponentType, + [SCREENS.REPORT_DETAILS.SHARE_CODE]: () => require('../../../pages/home/report/ReportDetailsShareCodePage').default as React.ComponentType, +}); + +const ReportSettingsModalStackNavigator = createModalStackNavigator({ + [SCREENS.REPORT_SETTINGS.ROOT]: () => require('../../../pages/settings/Report/ReportSettingsPage').default as React.ComponentType, + [SCREENS.REPORT_SETTINGS.ROOM_NAME]: () => require('../../../pages/settings/Report/RoomNamePage').default as React.ComponentType, + [SCREENS.REPORT_SETTINGS.NOTIFICATION_PREFERENCES]: () => require('../../../pages/settings/Report/NotificationPreferencePage').default as React.ComponentType, + [SCREENS.REPORT_SETTINGS.WRITE_CAPABILITY]: () => require('../../../pages/settings/Report/WriteCapabilityPage').default as React.ComponentType, +}); + +const TaskModalStackNavigator = createModalStackNavigator({ + [SCREENS.TASK.TITLE]: () => require('../../../pages/tasks/TaskTitlePage').default as React.ComponentType, + [SCREENS.TASK.DESCRIPTION]: () => require('../../../pages/tasks/TaskDescriptionPage').default as React.ComponentType, + [SCREENS.TASK.ASSIGNEE]: () => require('../../../pages/tasks/TaskAssigneeSelectorModal').default as React.ComponentType, +}); + +const ReportWelcomeMessageModalStackNavigator = createModalStackNavigator({ + [SCREENS.REPORT_WELCOME_MESSAGE_ROOT]: () => require('../../../pages/ReportWelcomeMessagePage').default as React.ComponentType, +}); + +const ReportParticipantsModalStackNavigator = createModalStackNavigator({ + [SCREENS.REPORT_PARTICIPANTS_ROOT]: () => require('../../../pages/ReportParticipantsPage').default as React.ComponentType, +}); + +const SearchModalStackNavigator = createModalStackNavigator({ + [SCREENS.SEARCH_ROOT]: () => require('../../../pages/SearchPage').default as React.ComponentType, +}); + +const NewChatModalStackNavigator = createModalStackNavigator({ + [SCREENS.NEW_CHAT_ROOT]: () => require('../../../pages/NewChatSelectorPage').default as React.ComponentType, +}); + +const NewTaskModalStackNavigator = createModalStackNavigator({ + [SCREENS.NEW_TASK.ROOT]: () => require('../../../pages/tasks/NewTaskPage').default as React.ComponentType, + [SCREENS.NEW_TASK.TASK_ASSIGNEE_SELECTOR]: () => require('../../../pages/tasks/TaskAssigneeSelectorModal').default as React.ComponentType, + [SCREENS.NEW_TASK.TASK_SHARE_DESTINATION_SELECTOR]: () => require('../../../pages/tasks/TaskShareDestinationSelectorModal').default as React.ComponentType, + [SCREENS.NEW_TASK.DETAILS]: () => require('../../../pages/tasks/NewTaskDetailsPage').default as React.ComponentType, + [SCREENS.NEW_TASK.TITLE]: () => require('../../../pages/tasks/NewTaskTitlePage').default as React.ComponentType, + [SCREENS.NEW_TASK.DESCRIPTION]: () => require('../../../pages/tasks/NewTaskDescriptionPage').default as React.ComponentType, +}); + +const NewTeachersUniteNavigator = createModalStackNavigator({ + [SCREENS.SAVE_THE_WORLD.ROOT]: () => require('../../../pages/TeachersUnite/SaveTheWorldPage').default as React.ComponentType, + [SCREENS.I_KNOW_A_TEACHER]: () => require('../../../pages/TeachersUnite/KnowATeacherPage').default as React.ComponentType, + [SCREENS.INTRO_SCHOOL_PRINCIPAL]: () => require('../../../pages/TeachersUnite/ImTeacherPage').default as React.ComponentType, + [SCREENS.I_AM_A_TEACHER]: () => require('../../../pages/TeachersUnite/ImTeacherPage').default as React.ComponentType, +}); + +const SettingsModalStackNavigator = createModalStackNavigator({ + [SCREENS.SETTINGS.ROOT]: () => require('../../../pages/settings/InitialSettingsPage').default as React.ComponentType, + [SCREENS.SETTINGS.SHARE_CODE]: () => require('../../../pages/ShareCodePage').default as React.ComponentType, + [SCREENS.SETTINGS.WORKSPACES]: () => require('../../../pages/workspace/WorkspacesListPage').default as React.ComponentType, + [SCREENS.SETTINGS.PROFILE]: () => require('../../../pages/settings/Profile/ProfilePage').default as React.ComponentType, + [SCREENS.SETTINGS.PRONOUNS]: () => require('../../../pages/settings/Profile/PronounsPage').default as React.ComponentType, + [SCREENS.SETTINGS.DISPLAY_NAME]: () => require('../../../pages/settings/Profile/DisplayNamePage').default as React.ComponentType, + [SCREENS.SETTINGS.TIMEZONE]: () => require('../../../pages/settings/Profile/TimezoneInitialPage').default as React.ComponentType, + [SCREENS.SETTINGS.TIMEZONE_SELECT]: () => require('../../../pages/settings/Profile/TimezoneSelectPage').default as React.ComponentType, + [SCREENS.SETTINGS.PERSONAL_DETAILS_INITIAL]: () => require('../../../pages/settings/Profile/PersonalDetails/PersonalDetailsInitialPage').default as React.ComponentType, + [SCREENS.SETTINGS.PERSONAL_DETAILS_LEGAL_NAME]: () => require('../../../pages/settings/Profile/PersonalDetails/LegalNamePage').default as React.ComponentType, + [SCREENS.SETTINGS.PERSONAL_DETAILS_DATE_OF_BIRTH]: () => require('../../../pages/settings/Profile/PersonalDetails/DateOfBirthPage').default as React.ComponentType, + [SCREENS.SETTINGS.PERSONAL_DETAILS_ADDRESS]: () => require('../../../pages/settings/Profile/PersonalDetails/AddressPage').default as React.ComponentType, + [SCREENS.SETTINGS.PERSONAL_DETAILS_ADDRESS_COUNTRY]: () => require('../../../pages/settings/Profile/PersonalDetails/CountrySelectionPage').default as React.ComponentType, + [SCREENS.SETTINGS.CONTACT_METHODS]: () => require('../../../pages/settings/Profile/Contacts/ContactMethodsPage').default as React.ComponentType, + [SCREENS.SETTINGS.CONTACT_METHOD_DETAILS]: () => require('../../../pages/settings/Profile/Contacts/ContactMethodDetailsPage').default as React.ComponentType, + [SCREENS.SETTINGS.NEW_CONTACT_METHOD]: () => require('../../../pages/settings/Profile/Contacts/NewContactMethodPage').default as React.ComponentType, + [SCREENS.SETTINGS.PREFERENCES]: () => require('../../../pages/settings/Preferences/PreferencesPage').default as React.ComponentType, + [SCREENS.SETTINGS.PREFERENCES_PRIORITY_MODE]: () => require('../../../pages/settings/Preferences/PriorityModePage').default as React.ComponentType, + [SCREENS.SETTINGS.PREFERENCES_LANGUAGE]: () => require('../../../pages/settings/Preferences/LanguagePage').default as React.ComponentType, + // Will be uncommented as part of https://github.com/Expensify/App/issues/21670 + // Settings_Preferences_Theme: () => require('../../../pages/settings/Preferences/ThemePage').default as React.ComponentType, + [SCREENS.SETTINGS.CLOSE]: () => require('../../../pages/settings/Security/CloseAccountPage').default as React.ComponentType, + [SCREENS.SETTINGS.SECURITY]: () => require('../../../pages/settings/Security/SecuritySettingsPage').default as React.ComponentType, + [SCREENS.SETTINGS.ABOUT]: () => require('../../../pages/settings/AboutPage/AboutPage').default as React.ComponentType, + [SCREENS.SETTINGS.APP_DOWNLOAD_LINKS]: () => require('../../../pages/settings/AppDownloadLinks').default as React.ComponentType, + [SCREENS.SETTINGS.LOUNGE_ACCESS]: () => require('../../../pages/settings/Profile/LoungeAccessPage').default as React.ComponentType, + [SCREENS.SETTINGS.WALLET]: () => require('../../../pages/settings/Wallet/WalletPage').default as React.ComponentType, + [SCREENS.SETTINGS.WALLET_DOMAIN_CARDS]: () => require('../../../pages/settings/Wallet/ExpensifyCardPage').default as React.ComponentType, + [SCREENS.SETTINGS.WALLET_TRANSFER_BALANCE]: () => require('../../../pages/settings/Wallet/TransferBalancePage').default as React.ComponentType, + [SCREENS.SETTINGS.WALLET_CHOOSE_TRANSFER_ACCOUNT]: () => require('../../../pages/settings/Wallet/ChooseTransferAccountPage').default as React.ComponentType, + [SCREENS.SETTINGS.WALLET_ENABLE_PAYMENTS]: () => require('../../../pages/EnablePayments/EnablePaymentsPage').default as React.ComponentType, + [SCREENS.SETTINGS.ADD_DEBIT_CARD]: () => require('../../../pages/settings/Wallet/AddDebitCardPage').default as React.ComponentType, + [SCREENS.SETTINGS.ADD_BANK_ACCOUNT]: () => require('../../../pages/AddPersonalBankAccountPage').default as React.ComponentType, + [SCREENS.SETTINGS.STATUS]: () => require('../../../pages/settings/Profile/CustomStatus/StatusPage').default as React.ComponentType, + [SCREENS.SETTINGS.STATUS_SET]: () => require('../../../pages/settings/Profile/CustomStatus/StatusSetPage').default as React.ComponentType, + [SCREENS.WORKSPACE.INITIAL]: () => require('../../../pages/workspace/WorkspaceInitialPage').default as React.ComponentType, + [SCREENS.WORKSPACE.SETTINGS]: () => require('../../../pages/workspace/WorkspaceSettingsPage').default as React.ComponentType, + [SCREENS.WORKSPACE.CARD]: () => require('../../../pages/workspace/card/WorkspaceCardPage').default as React.ComponentType, + [SCREENS.WORKSPACE.REIMBURSE]: () => require('../../../pages/workspace/reimburse/WorkspaceReimbursePage').default as React.ComponentType, + [SCREENS.WORKSPACE.RATE_AND_UNIT]: () => require('../../../pages/workspace/reimburse/WorkspaceRateAndUnitPage').default as React.ComponentType, + [SCREENS.WORKSPACE.BILLS]: () => require('../../../pages/workspace/bills/WorkspaceBillsPage').default as React.ComponentType, + [SCREENS.WORKSPACE.INVOICES]: () => require('../../../pages/workspace/invoices/WorkspaceInvoicesPage').default as React.ComponentType, + [SCREENS.WORKSPACE.TRAVEL]: () => require('../../../pages/workspace/travel/WorkspaceTravelPage').default as React.ComponentType, + [SCREENS.WORKSPACE.MEMBERS]: () => require('../../../pages/workspace/WorkspaceMembersPage').default as React.ComponentType, + [SCREENS.WORKSPACE.INVITE]: () => require('../../../pages/workspace/WorkspaceInvitePage').default as React.ComponentType, + [SCREENS.WORKSPACE.INVITE_MESSAGE]: () => require('../../../pages/workspace/WorkspaceInviteMessagePage').default as React.ComponentType, + [SCREENS.REIMBURSEMENT_ACCOUNT]: () => require('../../../pages/ReimbursementAccount/ReimbursementAccountPage').default as React.ComponentType, + [SCREENS.GET_ASSISTANCE]: () => require('../../../pages/GetAssistancePage').default as React.ComponentType, + [SCREENS.SETTINGS.TWO_FACTOR_AUTH]: () => require('../../../pages/settings/Security/TwoFactorAuth/TwoFactorAuthPage').default as React.ComponentType, +}); + +const EnablePaymentsStackNavigator = createModalStackNavigator({ + [SCREENS.ENABLE_PAYMENTS_ROOT]: () => require('../../../pages/EnablePayments/EnablePaymentsPage').default as React.ComponentType, +}); + +const AddPersonalBankAccountModalStackNavigator = createModalStackNavigator({ + [SCREENS.ADD_PERSONAL_BANK_ACCOUNT_ROOT]: () => require('../../../pages/AddPersonalBankAccountPage').default as React.ComponentType, +}); + +const ReimbursementAccountModalStackNavigator = createModalStackNavigator({ + [SCREENS.REIMBURSEMENT_ACCOUNT_ROOT]: () => require('../../../pages/ReimbursementAccount/ReimbursementAccountPage').default as React.ComponentType, +}); + +const WalletStatementStackNavigator = createModalStackNavigator({ + [SCREENS.WALLET_STATEMENT_ROOT]: () => require('../../../pages/wallet/WalletStatementPage').default as React.ComponentType, +}); + +const FlagCommentStackNavigator = createModalStackNavigator({ + [SCREENS.FLAG_COMMENT_ROOT]: () => require('../../../pages/FlagCommentPage').default as React.ComponentType, +}); + +const EditRequestStackNavigator = createModalStackNavigator({ + [SCREENS.EDIT_REQUEST.ROOT]: () => require('../../../pages/EditRequestPage').default as React.ComponentType, + [SCREENS.EDIT_REQUEST.CURRENCY]: () => require('../../../pages/iou/IOUCurrencySelection').default as React.ComponentType, +}); + +const PrivateNotesModalStackNavigator = createModalStackNavigator({ + [SCREENS.PRIVATE_NOTES.VIEW]: () => require('../../../pages/PrivateNotes/PrivateNotesViewPage').default as React.ComponentType, + [SCREENS.PRIVATE_NOTES.LIST]: () => require('../../../pages/PrivateNotes/PrivateNotesListPage').default as React.ComponentType, + [SCREENS.PRIVATE_NOTES.EDIT]: () => require('../../../pages/PrivateNotes/PrivateNotesEditPage').default as React.ComponentType, +}); + +const SignInModalStackNavigator = createModalStackNavigator({ + [SCREENS.SIGN_IN_ROOT]: () => require('../../../pages/signin/SignInModal').default as React.ComponentType, +}); + +export { + MoneyRequestModalStackNavigator, + SplitDetailsModalStackNavigator, + DetailsModalStackNavigator, + ProfileModalStackNavigator, + ReportDetailsModalStackNavigator, + TaskModalStackNavigator, + ReportSettingsModalStackNavigator, + ReportWelcomeMessageModalStackNavigator, + ReportParticipantsModalStackNavigator, + SearchModalStackNavigator, + NewChatModalStackNavigator, + NewTaskModalStackNavigator, + SettingsModalStackNavigator, + EnablePaymentsStackNavigator, + AddPersonalBankAccountModalStackNavigator, + ReimbursementAccountModalStackNavigator, + WalletStatementStackNavigator, + FlagCommentStackNavigator, + EditRequestStackNavigator, + PrivateNotesModalStackNavigator, + NewTeachersUniteNavigator, + SignInModalStackNavigator, +}; diff --git a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator.js b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator.tsx similarity index 89% rename from src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator.js rename to src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator.tsx index 64eadcbe06c3..49c880bb2252 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator.js +++ b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator.tsx @@ -5,8 +5,9 @@ import ReportScreenWrapper from '../ReportScreenWrapper'; import getCurrentUrl from '../../currentUrl'; import styles from '../../../../styles/styles'; import FreezeWrapper from '../../FreezeWrapper'; +import type {CentralPaneStackParamList} from '../types'; -const Stack = createStackNavigator(); +const Stack = createStackNavigator(); const url = getCurrentUrl(); const openOnAdminRoom = url ? new URL(url).searchParams.get('openOnAdminRoom') : undefined; @@ -18,7 +19,7 @@ function CentralPaneNavigator() { unknown; }; -function Overlay(props) { +function Overlay(props: OverlayProps) { const {current} = useCardAnimation(); - const {translate} = useLocalize(); + // TODO: remove type assertion when useLocalize is migrated + const {translate} = useLocalize() as unknown as {translate: (phrase: string) => string}; return ( - {/* In the latest Electron version buttons can't be both clickable and draggable. - That's why we added this workaround. Because of two Pressable components on the desktop app + {/* In the latest Electron version buttons can't be both clickable and draggable. + That's why we added this workaround. Because of two Pressable components on the desktop app we have 30px draggable ba at the top and the rest of the dimmed area is clickable. On other devices, everything behaves normally like one big pressable */} (); -const propTypes = { - ...withNavigationPropTypes, -}; - -function RightModalNavigator(props) { - const {isSmallScreenWidth} = useWindowDimensions(); +function RightModalNavigator(props: StackScreenProps) { + // TODO: remove type assertion when useWindowDimensions is migrated to TS + const {isSmallScreenWidth} = useWindowDimensions() as WindowDimensions; return ( @@ -118,7 +117,6 @@ function RightModalNavigator(props) { ); } -RightModalNavigator.propTypes = propTypes; RightModalNavigator.displayName = 'RightModalNavigator'; export default RightModalNavigator; diff --git a/src/libs/Navigation/AppNavigator/PublicScreens.js b/src/libs/Navigation/AppNavigator/PublicScreens.tsx similarity index 84% rename from src/libs/Navigation/AppNavigator/PublicScreens.js rename to src/libs/Navigation/AppNavigator/PublicScreens.tsx index 7a87530a2d9e..fa3c9dc8d806 100644 --- a/src/libs/Navigation/AppNavigator/PublicScreens.js +++ b/src/libs/Navigation/AppNavigator/PublicScreens.tsx @@ -8,8 +8,9 @@ import defaultScreenOptions from './defaultScreenOptions'; import UnlinkLoginPage from '../../../pages/UnlinkLoginPage'; import AppleSignInDesktopPage from '../../../pages/signin/AppleSignInDesktopPage'; import GoogleSignInDesktopPage from '../../../pages/signin/GoogleSignInDesktopPage'; +import {PublicScreensStackParamList} from './types'; -const RootStack = createStackNavigator(); +const RootStack = createStackNavigator(); function PublicScreens() { return ( @@ -25,22 +26,22 @@ function PublicScreens() { component={LogInWithShortLivedAuthTokenPage} /> diff --git a/src/libs/Navigation/AppNavigator/RHPScreenOptions.js b/src/libs/Navigation/AppNavigator/RHPScreenOptions.ts similarity index 72% rename from src/libs/Navigation/AppNavigator/RHPScreenOptions.js rename to src/libs/Navigation/AppNavigator/RHPScreenOptions.ts index d7448dcf2314..b21f29fc466e 100644 --- a/src/libs/Navigation/AppNavigator/RHPScreenOptions.js +++ b/src/libs/Navigation/AppNavigator/RHPScreenOptions.ts @@ -1,4 +1,4 @@ -import {CardStyleInterpolators} from '@react-navigation/stack'; +import {CardStyleInterpolators, StackNavigationOptions} from '@react-navigation/stack'; import styles from '../../../styles/styles'; const RHPScreenOptions = { @@ -7,6 +7,6 @@ const RHPScreenOptions = { gestureDirection: 'horizontal', cardStyle: styles.navigationScreenCardStyle, cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS, -}; +} as StackNavigationOptions; export default RHPScreenOptions; diff --git a/src/libs/Navigation/AppNavigator/ReportScreenIDSetter.js b/src/libs/Navigation/AppNavigator/ReportScreenIDSetter.ts similarity index 54% rename from src/libs/Navigation/AppNavigator/ReportScreenIDSetter.js rename to src/libs/Navigation/AppNavigator/ReportScreenIDSetter.ts index 24f855645870..de725a59f42c 100644 --- a/src/libs/Navigation/AppNavigator/ReportScreenIDSetter.js +++ b/src/libs/Navigation/AppNavigator/ReportScreenIDSetter.ts @@ -1,91 +1,66 @@ import {useEffect} from 'react'; -import PropTypes from 'prop-types'; -import lodashGet from 'lodash/get'; -import {withOnyx} from 'react-native-onyx'; +import {OnyxCollection, OnyxEntry, withOnyx} from 'react-native-onyx'; import ONYXKEYS from '../../../ONYXKEYS'; import * as ReportUtils from '../../ReportUtils'; -import reportPropTypes from '../../../pages/reportPropTypes'; -import {withNavigationPropTypes} from '../../../components/withNavigation'; import * as App from '../../actions/App'; import usePermissions from '../../../hooks/usePermissions'; import CONST from '../../../CONST'; import Navigation from '../Navigation'; +import {Policy, Report} from '../../../types/onyx'; +import {ReportScreenWrapperProps} from './types'; -const propTypes = { +type ReportScreenIDSetterComponentProps = { /** Available reports that would be displayed in this navigator */ - reports: PropTypes.objectOf(reportPropTypes), + reports: OnyxCollection; /** The policies which the user has access to */ - policies: PropTypes.objectOf( - PropTypes.shape({ - /** The policy name */ - name: PropTypes.string, + policies: OnyxCollection; - /** The type of the policy */ - type: PropTypes.string, - }), - ), - - isFirstTimeNewExpensifyUser: PropTypes.bool, - - /** Navigation route context info provided by react navigation */ - route: PropTypes.shape({ - /** Route specific parameters used on this screen */ - params: PropTypes.shape({ - /** If the admin room should be opened */ - openOnAdminRoom: PropTypes.bool, - - /** The ID of the report this screen should display */ - reportID: PropTypes.string, - }), - }).isRequired, - - ...withNavigationPropTypes, + isFirstTimeNewExpensifyUser: OnyxEntry; }; -const defaultProps = { - reports: {}, - policies: {}, - isFirstTimeNewExpensifyUser: false, -}; +type ReportScreenIDSetterProps = ReportScreenIDSetterComponentProps & ReportScreenWrapperProps; /** * Get the most recently accessed report for the user - * - * @param {Object} reports - * @param {Boolean} ignoreDefaultRooms - * @param {Object} policies - * @param {Boolean} isFirstTimeNewExpensifyUser - * @param {Boolean} openOnAdminRoom - * @returns {Number} */ -const getLastAccessedReportID = (reports, ignoreDefaultRooms, policies, isFirstTimeNewExpensifyUser, openOnAdminRoom) => { +const getLastAccessedReportID = ( + reports: OnyxCollection | Report[], + ignoreDefaultRooms: boolean, + policies: OnyxCollection, + isFirstTimeNewExpensifyUser: OnyxEntry, + openOnAdminRoom: boolean, +): number | string => { // If deeplink url is of an attachment, we should show the report that the attachment comes from. const currentRoute = Navigation.getActiveRoute(); const matches = CONST.REGEX.ATTACHMENT_ROUTE.exec(currentRoute); - const reportID = lodashGet(matches, 1, null); + const reportID = matches?.[1] ?? null; if (reportID) { return reportID; } - const lastReport = ReportUtils.findLastAccessedReport(reports, ignoreDefaultRooms, policies, isFirstTimeNewExpensifyUser, openOnAdminRoom); + // TODO: get rid of ignore when ReportUtils is migrated to TS + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore till ReportUtils file is migrated + const lastReport = ReportUtils.findLastAccessedReport(reports, ignoreDefaultRooms, policies, isFirstTimeNewExpensifyUser, openOnAdminRoom) as Report; - return lodashGet(lastReport, 'reportID'); + return lastReport?.reportID; }; // This wrapper is reponsible for opening the last accessed report if there is no reportID specified in the route params -function ReportScreenIDSetter({route, reports, policies, isFirstTimeNewExpensifyUser, navigation}) { - const {canUseDefaultRooms} = usePermissions(); +function ReportScreenIDSetter({route, reports, policies, isFirstTimeNewExpensifyUser, navigation}: ReportScreenIDSetterProps): null { + // TODO: remove type assertion when usePermissions is migrated + const {canUseDefaultRooms} = usePermissions() as {canUseDefaultRooms: boolean}; useEffect(() => { // Don't update if there is a reportID in the params already - if (lodashGet(route, 'params.reportID', null)) { + if (route?.params?.reportID ?? null) { App.confirmReadyToOpenApp(); return; } // If there is no reportID in route, try to find last accessed and use it for setParams - const reportID = getLastAccessedReportID(reports, !canUseDefaultRooms, policies, isFirstTimeNewExpensifyUser, lodashGet(route, 'params.openOnAdminRoom', false)); + const reportID = getLastAccessedReportID(reports, !canUseDefaultRooms, policies, isFirstTimeNewExpensifyUser, reports?.params?.openOnAdminRoom ?? false); // It's possible that reports aren't fully loaded yet // in that case the reportID is undefined @@ -101,21 +76,26 @@ function ReportScreenIDSetter({route, reports, policies, isFirstTimeNewExpensify return null; } -ReportScreenIDSetter.propTypes = propTypes; -ReportScreenIDSetter.defaultProps = defaultProps; ReportScreenIDSetter.displayName = 'ReportScreenIDSetter'; -export default withOnyx({ +export default withOnyx({ reports: { key: ONYXKEYS.COLLECTION.REPORT, + // TODO: I think we need to update onyx mapping types to include allowStaleData/initialValue keys + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore allowStaleData: true, }, policies: { key: ONYXKEYS.COLLECTION.POLICY, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore allowStaleData: true, }, isFirstTimeNewExpensifyUser: { key: ONYXKEYS.NVP_IS_FIRST_TIME_NEW_EXPENSIFY_USER, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore initialValue: false, }, })(ReportScreenIDSetter); diff --git a/src/libs/Navigation/AppNavigator/ReportScreenWrapper.js b/src/libs/Navigation/AppNavigator/ReportScreenWrapper.js deleted file mode 100644 index 767bd9793ac2..000000000000 --- a/src/libs/Navigation/AppNavigator/ReportScreenWrapper.js +++ /dev/null @@ -1,43 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import {withNavigationPropTypes} from '../../../components/withNavigation'; -import ReportScreen from '../../../pages/home/ReportScreen'; -import ReportScreenIDSetter from './ReportScreenIDSetter'; - -const propTypes = { - /** Navigation route context info provided by react navigation */ - route: PropTypes.shape({ - /** Route specific parameters used on this screen */ - params: PropTypes.shape({ - /** If the admin room should be opened */ - openOnAdminRoom: PropTypes.bool, - - /** The ID of the report this screen should display */ - reportID: PropTypes.string, - }), - }).isRequired, - - ...withNavigationPropTypes, -}; - -const defaultProps = {}; - -function ReportScreenWrapper(props) { - // The ReportScreen without the reportID set will display a skeleton - // until the reportID is loaded and set in the route param - return ( - <> - - - - ); -} - -ReportScreenWrapper.propTypes = propTypes; -ReportScreenWrapper.defaultProps = defaultProps; -ReportScreenWrapper.displayName = 'ReportScreenWrapper'; - -export default ReportScreenWrapper; diff --git a/src/libs/Navigation/AppNavigator/ReportScreenWrapper.tsx b/src/libs/Navigation/AppNavigator/ReportScreenWrapper.tsx new file mode 100644 index 000000000000..2c17bf4b2ed0 --- /dev/null +++ b/src/libs/Navigation/AppNavigator/ReportScreenWrapper.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import ReportScreen from '../../../pages/home/ReportScreen'; +import ReportScreenIDSetter from './ReportScreenIDSetter'; +import {ReportScreenWrapperProps} from './types'; + +function ReportScreenWrapper(props: ReportScreenWrapperProps) { + // The ReportScreen without the reportID set will display a skeleton + // until the reportID is loaded and set in the route param + return ( + <> + {/* TODO: Remove when ReportScreen is migrated */} + {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */} + {/* @ts-ignore */} + + + + ); +} + +ReportScreenWrapper.displayName = 'ReportScreenWrapper'; + +export default ReportScreenWrapper; diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.js b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts similarity index 60% rename from src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.js rename to src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts index a3d8398a22b0..845a37a95c20 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.js +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts @@ -1,31 +1,32 @@ -import _ from 'underscore'; -import {StackRouter} from '@react-navigation/native'; +import {StackRouter, RouterConfigOptions, ParamListBase, StackNavigationState, PartialState} from '@react-navigation/native'; import NAVIGATORS from '../../../../NAVIGATORS'; +import {ResponsiveStackNavigatorRouterOptions} from '../types'; -/** - * @param {Object} state - react-navigation state - * @returns {Boolean} - */ -const isAtLeastOneCentralPaneNavigatorInState = (state) => _.find(state.routes, (r) => r.name === NAVIGATORS.CENTRAL_PANE_NAVIGATOR); +type MutableState = { + index: number; + stale: true; +}; + +type State = Omit>, 'stale'> & MutableState; + +const isAtLeastOneCentralPaneNavigatorInState = (state: State): boolean => !!state.routes.find((r) => r.name === NAVIGATORS.CENTRAL_PANE_NAVIGATOR); /** * Adds report route without any specific reportID to the state. * The report screen will self set proper reportID param based on the helper function findLastAccessedReport (look at ReportScreenWrapper for more info) - * - * @param {Object} state - react-navigation state */ -const addCentralPaneNavigatorRoute = (state) => { +const addCentralPaneNavigatorRoute = (state: State) => { state.routes.splice(1, 0, {name: NAVIGATORS.CENTRAL_PANE_NAVIGATOR}); // eslint-disable-next-line no-param-reassign state.index = state.routes.length - 1; }; -function CustomRouter(options) { +function CustomRouter(options: ResponsiveStackNavigatorRouterOptions) { const stackRouter = StackRouter(options); return { ...stackRouter, - getRehydratedState(partialState, {routeNames, routeParamList}) { + getRehydratedState(partialState: State, {routeNames, routeParamList, routeGetIdList}: RouterConfigOptions) { // Make sure that there is at least one CentralPaneNavigator (ReportScreen by default) in the state if this is a wide layout if (!isAtLeastOneCentralPaneNavigatorInState(partialState) && !options.getIsSmallScreenWidth()) { // If we added a route we need to make sure that the state.stale is true to generate new key for this route @@ -33,7 +34,7 @@ function CustomRouter(options) { partialState.stale = true; addCentralPaneNavigatorRoute(partialState); } - const state = stackRouter.getRehydratedState(partialState, {routeNames, routeParamList}); + const state = stackRouter.getRehydratedState(partialState, {routeNames, routeParamList, routeGetIdList}); return state; }, }; diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js deleted file mode 100644 index 58be3d2af3da..000000000000 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js +++ /dev/null @@ -1,60 +0,0 @@ -import React, {useRef} from 'react'; -import PropTypes from 'prop-types'; -import {useNavigationBuilder, createNavigatorFactory} from '@react-navigation/native'; -import {StackView} from '@react-navigation/stack'; -import CustomRouter from './CustomRouter'; -import useWindowDimensions from '../../../../hooks/useWindowDimensions'; - -const propTypes = { - /* Determines if the navigator should render the StackView (narrow) or ThreePaneView (wide) */ - isSmallScreenWidth: PropTypes.bool.isRequired, - - /* Children for the useNavigationBuilder hook */ - children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]).isRequired, - - /* initialRouteName for this navigator */ - initialRouteName: PropTypes.oneOf([PropTypes.string, PropTypes.undefined]), - - /* Screen options defined for this navigator */ - // eslint-disable-next-line react/forbid-prop-types - screenOptions: PropTypes.object, -}; - -const defaultProps = { - initialRouteName: undefined, - screenOptions: undefined, -}; - -function ResponsiveStackNavigator(props) { - const {isSmallScreenWidth} = useWindowDimensions(); - - const isSmallScreenWidthRef = useRef(isSmallScreenWidth); - - isSmallScreenWidthRef.current = isSmallScreenWidth; - - const {navigation, state, descriptors, NavigationContent} = useNavigationBuilder(CustomRouter, { - children: props.children, - screenOptions: props.screenOptions, - initialRouteName: props.initialRouteName, - // Options for useNavigationBuilder won't update on prop change, so we need to pass a getter for the router to have the current state of isSmallScreenWidth. - getIsSmallScreenWidth: () => isSmallScreenWidthRef.current, - }); - - return ( - - - - ); -} - -ResponsiveStackNavigator.defaultProps = defaultProps; -ResponsiveStackNavigator.propTypes = propTypes; -ResponsiveStackNavigator.displayName = 'ResponsiveStackNavigator'; - -export default createNavigatorFactory(ResponsiveStackNavigator); diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx new file mode 100644 index 000000000000..a97bb3de9611 --- /dev/null +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx @@ -0,0 +1,46 @@ +import React, {useRef} from 'react'; +import {useNavigationBuilder, createNavigatorFactory, ParamListBase, StackActionHelpers, StackNavigationState} from '@react-navigation/native'; +import {StackNavigationEventMap, StackNavigationOptions, StackView} from '@react-navigation/stack'; +import CustomRouter from './CustomRouter'; +import useWindowDimensions from '../../../../hooks/useWindowDimensions'; +import {ResponsiveStackNavigatorProps, ResponsiveStackNavigatorRouterOptions} from '../types'; +import type {WindowDimensions} from '../../../../styles/getModalStyles'; + +function ResponsiveStackNavigator(props: ResponsiveStackNavigatorProps) { + // TODO: remove type assertion when useWindowDimensions is migrated to TS + const {isSmallScreenWidth} = useWindowDimensions() as WindowDimensions; + + const isSmallScreenWidthRef = useRef(isSmallScreenWidth); + + isSmallScreenWidthRef.current = isSmallScreenWidth; + + const {navigation, state, descriptors, NavigationContent} = useNavigationBuilder< + StackNavigationState, + ResponsiveStackNavigatorRouterOptions, + StackActionHelpers, + StackNavigationOptions, + StackNavigationEventMap + >(CustomRouter, { + children: props.children, + screenOptions: props.screenOptions, + initialRouteName: props.initialRouteName, + // Options for useNavigationBuilder won't update on prop change, so we need to pass a getter for the router to have the current state of isSmallScreenWidth. + getIsSmallScreenWidth: () => isSmallScreenWidthRef.current, + }); + + return ( + + + + ); +} + +ResponsiveStackNavigator.displayName = 'ResponsiveStackNavigator'; + +export default createNavigatorFactory, StackNavigationOptions, StackNavigationEventMap, typeof ResponsiveStackNavigator>(ResponsiveStackNavigator); diff --git a/src/libs/Navigation/AppNavigator/defaultScreenOptions.js b/src/libs/Navigation/AppNavigator/defaultScreenOptions.ts similarity index 68% rename from src/libs/Navigation/AppNavigator/defaultScreenOptions.js rename to src/libs/Navigation/AppNavigator/defaultScreenOptions.ts index 3ccffb5f09ab..745e66c52e94 100644 --- a/src/libs/Navigation/AppNavigator/defaultScreenOptions.js +++ b/src/libs/Navigation/AppNavigator/defaultScreenOptions.ts @@ -1,3 +1,5 @@ +import {StackNavigationOptions} from '@react-navigation/stack'; + const defaultScreenOptions = { cardStyle: { overflow: 'visible', @@ -5,6 +7,6 @@ const defaultScreenOptions = { }, headerShown: false, animationTypeForReplace: 'push', -}; +} as StackNavigationOptions; export default defaultScreenOptions; diff --git a/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.js b/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.ts similarity index 71% rename from src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.js rename to src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.ts index a7456fb071b4..48dac2d13051 100644 --- a/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.js +++ b/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.ts @@ -1,3 +1,4 @@ +import {StackCardInterpolationProps, StackNavigationOptions} from '@react-navigation/stack'; import modalCardStyleInterpolator from './modalCardStyleInterpolator'; import styles from '../../../styles/styles'; import variables from '../../../styles/variables'; @@ -10,12 +11,12 @@ const commonScreenOptions = { animationEnabled: true, cardOverlayEnabled: true, animationTypeForReplace: 'push', -}; +} as StackNavigationOptions; -export default (isSmallScreenWidth) => ({ +export default (isSmallScreenWidth: boolean) => ({ rightModalNavigator: { ...commonScreenOptions, - cardStyleInterpolator: (props) => modalCardStyleInterpolator(isSmallScreenWidth, false, props), + cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, false, props), presentation: 'transparentModal', // We want pop in RHP since there are some flows that would work weird otherwise @@ -28,12 +29,12 @@ export default (isSmallScreenWidth) => ({ // Excess space should be on the left so we need to position from right. right: 0, }, - }, + } as StackNavigationOptions, homeScreen: { title: CONFIG.SITE_TITLE, ...commonScreenOptions, - cardStyleInterpolator: (props) => modalCardStyleInterpolator(isSmallScreenWidth, false, props), + cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, false, props), cardStyle: { ...getNavigationModalCardStyle(), @@ -43,28 +44,27 @@ export default (isSmallScreenWidth) => ({ transform: [{translateX: isSmallScreenWidth ? 0 : -variables.sideBarWidth}], ...(isSmallScreenWidth ? {} : styles.borderRight), }, - }, - // eslint-disable-next-line rulesdir/no-negated-variables + } as StackNavigationOptions, fullScreen: { ...commonScreenOptions, - cardStyleInterpolator: (props) => modalCardStyleInterpolator(isSmallScreenWidth, true, props), + cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, true, props), cardStyle: { ...getNavigationModalCardStyle(), // This is necessary to cover whole screen. Including translated sidebar. marginLeft: isSmallScreenWidth ? 0 : -variables.sideBarWidth, }, - }, + } as StackNavigationOptions, centralPaneNavigator: { title: CONFIG.SITE_TITLE, ...commonScreenOptions, animationEnabled: isSmallScreenWidth, - cardStyleInterpolator: (props) => modalCardStyleInterpolator(isSmallScreenWidth, true, props), + cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, true, props), cardStyle: { ...getNavigationModalCardStyle(), paddingRight: isSmallScreenWidth ? 0 : variables.sideBarWidth, }, - }, + } as StackNavigationOptions, }); diff --git a/src/libs/Navigation/AppNavigator/index.js b/src/libs/Navigation/AppNavigator/index.tsx similarity index 73% rename from src/libs/Navigation/AppNavigator/index.js rename to src/libs/Navigation/AppNavigator/index.tsx index dee8027b2f30..d2cf587b8fa8 100644 --- a/src/libs/Navigation/AppNavigator/index.js +++ b/src/libs/Navigation/AppNavigator/index.tsx @@ -1,12 +1,11 @@ import React from 'react'; -import PropTypes from 'prop-types'; -const propTypes = { +type AppNavigatorProps = { /** If we have an authToken this is true */ - authenticated: PropTypes.bool.isRequired, + authenticated: boolean; }; -function AppNavigator(props) { +function AppNavigator(props: AppNavigatorProps) { if (props.authenticated) { const AuthScreens = require('./AuthScreens').default; @@ -17,6 +16,5 @@ function AppNavigator(props) { return ; } -AppNavigator.propTypes = propTypes; AppNavigator.displayName = 'AppNavigator'; export default AppNavigator; diff --git a/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.js b/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.ts similarity index 70% rename from src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.js rename to src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.ts index ec442efbba86..7dd26be93252 100644 --- a/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.js +++ b/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.ts @@ -1,8 +1,9 @@ import {Animated} from 'react-native'; +import type {StackCardInterpolatedStyle, StackCardInterpolationProps} from '@react-navigation/stack'; import variables from '../../../styles/variables'; import getCardStyles from '../../../styles/cardStyles'; -export default (isSmallScreenWidth, isFullScreenModal, {current: {progress}, inverted, layouts: {screen}}) => { +export default (isSmallScreenWidth: boolean, isFullScreenModal: boolean, {current: {progress}, inverted, layouts: {screen}}: StackCardInterpolationProps): StackCardInterpolatedStyle => { const translateX = Animated.multiply( progress.interpolate({ inputRange: [0, 1], diff --git a/src/libs/Navigation/AppNavigator/types.ts b/src/libs/Navigation/AppNavigator/types.ts new file mode 100644 index 000000000000..67286a6cbd85 --- /dev/null +++ b/src/libs/Navigation/AppNavigator/types.ts @@ -0,0 +1,87 @@ +import {StackScreenProps, StackNavigationOptions, StackNavigationEventMap} from '@react-navigation/stack'; +import {DefaultNavigatorOptions, ParamListBase, StackNavigationState, DefaultRouterOptions, NavigatorScreenParams} from '@react-navigation/native'; +import {ValueOf} from 'type-fest'; + +import SCREENS from '../../../SCREENS'; +import NAVIGATORS from '../../../NAVIGATORS'; +import CONST from '../../../CONST'; + +type AccountValidationParams = { + /** AccountID associated with the validation link */ + accountID: string; + + /** Validation code associated with the validation link */ + validateCode: string; +}; + +type CentralPaneStackParamList = { + [SCREENS.REPORT]: { + /** If the admin room should be opened */ + openOnAdminRoom?: boolean; + + /** The ID of the report this screen should display */ + reportID: string; + }; +}; +type ReportScreenWrapperProps = StackScreenProps; + +type PublicScreensStackParamList = { + [SCREENS.HOME]: undefined; + [SCREENS.TRANSITION_BETWEEN_APPS]: { + /** Short-lived authToken to sign in a user */ + shortLivedAuthToken: string; + + /** Short-lived authToken to sign in as a user, if they are coming from the old mobile app */ + shortLivedToken: string; + + /** The email of the transitioning user */ + email: string; + }; + [SCREENS.VALIDATE_LOGIN]: AccountValidationParams; + [SCREENS.UNLINK_LOGIN]: AccountValidationParams; + [SCREENS.SIGN_IN_WITH_APPLE_DESKTOP]: undefined; + [SCREENS.SIGN_IN_WITH_GOOGLE_DESKTOP]: undefined; +}; + +type AuthScreensStackParamList = { + [SCREENS.HOME]: undefined; + [NAVIGATORS.CENTRAL_PANE_NAVIGATOR]: NavigatorScreenParams; + [SCREENS.VALIDATE_LOGIN]: AccountValidationParams; + [SCREENS.TRANSITION_BETWEEN_APPS]: undefined; + [SCREENS.CONCIERGE]: undefined; + [CONST.DEMO_PAGES.SAASTR]: {name: string}; + [CONST.DEMO_PAGES.SBE]: {name: string}; + [SCREENS.REPORT_ATTACHMENTS]: { + /** The report ID which the attachment is associated with */ + reportID: string; + + /** The uri encoded source of the attachment */ + source: string; + }; + [SCREENS.NOT_FOUND]: undefined; + [NAVIGATORS.RIGHT_MODAL_NAVIGATOR]: NavigatorScreenParams; + [SCREENS.DESKTOP_SIGN_IN_REDIRECT]: undefined; +}; + +// TODO: describe all of the nested navigators +type RightModalNavigatorStackParamList = Record, undefined>; + +type ResponsiveStackNavigatorConfig = { + isSmallScreenWidth: boolean; +}; +type ResponsiveStackNavigatorRouterOptions = DefaultRouterOptions & { + getIsSmallScreenWidth: () => boolean; +}; +type ResponsiveStackNavigatorProps = DefaultNavigatorOptions, StackNavigationOptions, StackNavigationEventMap> & + ResponsiveStackNavigatorConfig; + +export type { + CentralPaneStackParamList, + ReportScreenWrapperProps, + PublicScreensStackParamList, + RightModalNavigatorStackParamList, + ResponsiveStackNavigatorRouterOptions, + ResponsiveStackNavigatorProps, + ResponsiveStackNavigatorConfig, + AuthScreensStackParamList, +}; diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js index de6162685079..5cbd8e9b0af6 100644 --- a/src/libs/Navigation/Navigation.js +++ b/src/libs/Navigation/Navigation.js @@ -77,7 +77,7 @@ const getActiveRouteIndex = function (route, index) { /** * Main navigation method for redirecting to a route. * @param {String} route - * @param {String} type - Type of action to perform. Currently UP is supported. + * @param {String} [type] - Type of action to perform. Currently UP is supported. */ function navigate(route = ROUTES.HOME, type) { if (!canNavigate('navigate', {route})) { diff --git a/src/libs/compose.ts b/src/libs/compose.ts index dadc586d0f0d..043e1e9f1bd5 100644 --- a/src/libs/compose.ts +++ b/src/libs/compose.ts @@ -17,41 +17,40 @@ * hoc3(config3), * )(Component) */ + +type Func = (...a: T) => R; + export default function compose(): (a: R) => R; export default function compose(f: F): F; /* two functions */ -export default function compose(f1: (...args: A) => R1, f2: (a: R1) => R2): (...args: A) => R2; +export default function compose(f1: (args: A) => R1, f2: Func): Func; /* three functions */ -export default function compose(f1: (...args: A) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3): (...args: A) => R3; +export default function compose(f1: (args: A) => R1, f2: (a: R1) => R2, f3: Func): Func; /* four functions */ -export default function compose(f1: (...args: A) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4): (...args: A) => R4; +export default function compose(f1: (args: A) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: Func): Func; /* five functions */ -export default function compose( - f1: (...args: A) => R1, - f2: (a: R1) => R2, - f3: (a: R2) => R3, - f4: (a: R3) => R4, - f5: (a: R4) => R5, -): (...args: A) => R5; +export default function compose(f1: (args: A) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: Func): Func; /* six functions */ -export default function compose( - f1: (...args: A) => R1, +export default function compose( + f1: (args: A) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, - f6: (a: R5) => R6, -): (...args: A) => R6; + f6: Func, +): Func; /* rest */ export default function compose(f1: (a: unknown) => R, ...funcs: Function[]): (...args: unknown[]) => R; +export default function compose(...funcs: Function[]): (...args: unknown[]) => R; + export default function compose(...funcs: Function[]): Function { if (funcs.length === 0) { // infer the argument type so it is usable in inference down the line diff --git a/src/styles/cardStyles/types.ts b/src/styles/cardStyles/types.ts index e1598b7696ff..ce3e377bdc8f 100644 --- a/src/styles/cardStyles/types.ts +++ b/src/styles/cardStyles/types.ts @@ -1,6 +1,6 @@ import {CSSProperties} from 'react'; import {ViewStyle} from 'react-native'; -type GetCardStyles = (screenWidth: number) => Partial>; +type GetCardStyles = (screenWidth: number) => Partial>; export default GetCardStyles; diff --git a/src/styles/getModalStyles.ts b/src/styles/getModalStyles.ts index 32e6cf6f98e7..6af1354a4112 100644 --- a/src/styles/getModalStyles.ts +++ b/src/styles/getModalStyles.ts @@ -300,3 +300,4 @@ export default function getModalStyles( shouldAddTopSafeAreaPadding, }; } +export type {WindowDimensions}; diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index e2d4da88a0fb..5d22eaedd788 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -57,6 +57,9 @@ type Report = { /** The report type */ type?: string; + /** If the admin room should be opened */ + openOnAdminRoom?: boolean; + parentReportID?: string; parentReportActionID?: string; isOptimisticReport?: boolean; From fa528388f44aefd2a2fc59648b106274b2c928a3 Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Tue, 10 Oct 2023 09:53:43 +0200 Subject: [PATCH 006/244] Fix ts error --- src/libs/NetworkConnection.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/NetworkConnection.ts b/src/libs/NetworkConnection.ts index 663a9c1b37d5..7c1bcaa0b339 100644 --- a/src/libs/NetworkConnection.ts +++ b/src/libs/NetworkConnection.ts @@ -13,7 +13,7 @@ let hasPendingNetworkCheck = false; // Holds all of the callbacks that need to be triggered when the network reconnects let callbackID = 0; -const reconnectionCallbacks: Record Promise> = {}; +const reconnectionCallbacks: Record void> = {}; /** * Loop over all reconnection callbacks and fire each one @@ -122,7 +122,7 @@ function listenForReconnect() { * Register callback to fire when we reconnect * @returns unsubscribe method */ -function onReconnect(callback: () => Promise): () => void { +function onReconnect(callback: () => void): () => void { const currentID = callbackID; callbackID++; reconnectionCallbacks[currentID] = callback; From 0a8b4ad6177ff5a2fe0f1b7cd16d176b35ec8fcf Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Thu, 12 Oct 2023 10:25:36 +0200 Subject: [PATCH 007/244] Minor code improvements --- src/libs/Navigation/AppNavigator/AuthScreens.tsx | 12 +++++------- .../Navigation/AppNavigator/Navigators/Overlay.tsx | 2 +- src/libs/Navigation/AppNavigator/RHPScreenOptions.ts | 4 ++-- .../Navigation/AppNavigator/ReportScreenIDSetter.ts | 2 +- .../Navigation/AppNavigator/defaultScreenOptions.ts | 4 ++-- .../AppNavigator/getRootNavigatorScreenOptions.ts | 4 ++-- src/libs/Navigation/Navigation.js | 2 +- 7 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 5e8f471d4a79..c6269163a7f8 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -35,6 +35,7 @@ import DemoSetupPage from '../../../pages/DemoSetupPage'; import * as OnyxTypes from '../../../types/onyx'; import type {WindowDimensions} from '../../../styles/getModalStyles'; import type {AuthScreensStackParamList} from './types'; +import type {Timezone} from '../../../types/onyx/PersonalDetails'; type AuthScreensOnyxProps = { /** Session of currently logged in user */ @@ -52,11 +53,6 @@ type AuthScreensOnyxProps = { type AuthScreensProps = WindowDimensions & AuthScreensOnyxProps; -type Timezone = { - automatic?: boolean; - selected?: string; -}; - type UnsubscribeChatShortcut = () => void; type UnsubscribeSearchShortcut = () => void; @@ -76,7 +72,7 @@ Onyx.connect({ currentAccountID = val.accountID; if (Navigation.isActiveRoute(ROUTES.SIGN_IN_MODAL)) { // This means sign in in RHP was successful, so we can dismiss the modal and subscribe to user events - Navigation.dismissModal(undefined); + Navigation.dismissModal(); User.subscribeToUserEvents(); } }, @@ -94,7 +90,7 @@ Onyx.connect({ // If the current timezone is different than the user's timezone, and their timezone is set to automatic // then update their timezone. - if (timezone !== null && timezone.automatic && timezone.selected !== currentTimezone) { + if (timezone?.automatic && timezone?.selected !== currentTimezone) { timezone.selected = currentTimezone; PersonalDetails.updateAutomaticTimezone({ automatic: true, @@ -200,6 +196,7 @@ class AuthScreens extends React.Component { searchShortcutConfig.descriptionKey, [...searchShortcutConfig.modifiers], true, + // TODO: remove type assertion when KeyboardShortcut is migrated ) as UnsubscribeSearchShortcut; this.unsubscribeChatShortcut = KeyboardShortcut.subscribe( chatShortcutConfig.shortcutKey, @@ -214,6 +211,7 @@ class AuthScreens extends React.Component { chatShortcutConfig.descriptionKey, [...chatShortcutConfig.modifiers], true, + // TODO: remove type assertion when KeyboardShortcut is migrated ) as UnsubscribeChatShortcut; } diff --git a/src/libs/Navigation/AppNavigator/Navigators/Overlay.tsx b/src/libs/Navigation/AppNavigator/Navigators/Overlay.tsx index 66888ac6201c..19b8a0b4453a 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/Overlay.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/Overlay.tsx @@ -9,7 +9,7 @@ import useLocalize from '../../../../hooks/useLocalize'; import CONST from '../../../../CONST'; type OverlayProps = { - onPress: () => unknown; + onPress: () => void; }; function Overlay(props: OverlayProps) { diff --git a/src/libs/Navigation/AppNavigator/RHPScreenOptions.ts b/src/libs/Navigation/AppNavigator/RHPScreenOptions.ts index b21f29fc466e..ae2244cb510b 100644 --- a/src/libs/Navigation/AppNavigator/RHPScreenOptions.ts +++ b/src/libs/Navigation/AppNavigator/RHPScreenOptions.ts @@ -1,12 +1,12 @@ import {CardStyleInterpolators, StackNavigationOptions} from '@react-navigation/stack'; import styles from '../../../styles/styles'; -const RHPScreenOptions = { +const RHPScreenOptions: StackNavigationOptions = { headerShown: false, animationEnabled: true, gestureDirection: 'horizontal', cardStyle: styles.navigationScreenCardStyle, cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS, -} as StackNavigationOptions; +}; export default RHPScreenOptions; diff --git a/src/libs/Navigation/AppNavigator/ReportScreenIDSetter.ts b/src/libs/Navigation/AppNavigator/ReportScreenIDSetter.ts index de725a59f42c..2a5f7e3aac31 100644 --- a/src/libs/Navigation/AppNavigator/ReportScreenIDSetter.ts +++ b/src/libs/Navigation/AppNavigator/ReportScreenIDSetter.ts @@ -25,7 +25,7 @@ type ReportScreenIDSetterProps = ReportScreenIDSetterComponentProps & ReportScre * Get the most recently accessed report for the user */ const getLastAccessedReportID = ( - reports: OnyxCollection | Report[], + reports: OnyxCollection, ignoreDefaultRooms: boolean, policies: OnyxCollection, isFirstTimeNewExpensifyUser: OnyxEntry, diff --git a/src/libs/Navigation/AppNavigator/defaultScreenOptions.ts b/src/libs/Navigation/AppNavigator/defaultScreenOptions.ts index 745e66c52e94..65a6bd052742 100644 --- a/src/libs/Navigation/AppNavigator/defaultScreenOptions.ts +++ b/src/libs/Navigation/AppNavigator/defaultScreenOptions.ts @@ -1,12 +1,12 @@ import {StackNavigationOptions} from '@react-navigation/stack'; -const defaultScreenOptions = { +const defaultScreenOptions: StackNavigationOptions = { cardStyle: { overflow: 'visible', flex: 1, }, headerShown: false, animationTypeForReplace: 'push', -} as StackNavigationOptions; +}; export default defaultScreenOptions; diff --git a/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.ts b/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.ts index 48dac2d13051..253006373a8e 100644 --- a/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.ts +++ b/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.ts @@ -5,13 +5,13 @@ import variables from '../../../styles/variables'; import getNavigationModalCardStyle from '../../../styles/getNavigationModalCardStyles'; import CONFIG from '../../../CONFIG'; -const commonScreenOptions = { +const commonScreenOptions: StackNavigationOptions = { headerShown: false, gestureDirection: 'horizontal', animationEnabled: true, cardOverlayEnabled: true, animationTypeForReplace: 'push', -} as StackNavigationOptions; +}; export default (isSmallScreenWidth: boolean) => ({ rightModalNavigator: { diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js index 09171503706c..f24270cfcd5c 100644 --- a/src/libs/Navigation/Navigation.js +++ b/src/libs/Navigation/Navigation.js @@ -159,7 +159,7 @@ function setParams(params, routeKey) { /** * Dismisses the last modal stack if there is any * - * @param {String | undefined} targetReportID - The reportID to navigate to after dismissing the modal + * @param {String | undefined} [targetReportID] - The reportID to navigate to after dismissing the modal */ function dismissModal(targetReportID) { if (!canNavigate('dismissModal')) { From b0c9b955e8df53f51281b405aa235fb804c2a92b Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Sun, 22 Oct 2023 00:40:27 +0500 Subject: [PATCH 008/244] fix: unread logic --- src/libs/ReportUtils.js | 10 ++++++++++ src/libs/UnreadIndicatorUpdater/index.js | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 75ee6257caab..7b55903bedf4 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -2992,6 +2992,15 @@ function buildTransactionThread(reportAction, moneyRequestReportID) { ); } +/** + * @param {Object} report + * @returns {Boolean} + */ +function isEmptyReport(report) { + const lastVisibleMessage = ReportActionsUtils.getLastVisibleMessage(report.reportID); + return !report.lastMessageText && !report.lastMessageTranslationKey && !lastVisibleMessage.lastMessageText && !lastVisibleMessage.lastMessageTranslationKey; +} + /** * @param {Object} report * @returns {Boolean} @@ -4000,6 +4009,7 @@ export { navigateToDetailsPage, generateReportID, hasReportNameError, + isEmptyReport, isUnread, isUnreadWithMention, buildOptimisticWorkspaceChats, diff --git a/src/libs/UnreadIndicatorUpdater/index.js b/src/libs/UnreadIndicatorUpdater/index.js index 09fa82612314..983fc57c511c 100644 --- a/src/libs/UnreadIndicatorUpdater/index.js +++ b/src/libs/UnreadIndicatorUpdater/index.js @@ -8,7 +8,7 @@ Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, waitForCollectionCallback: true, callback: (reportsFromOnyx) => { - const unreadReports = _.filter(reportsFromOnyx, ReportUtils.isUnread); + const unreadReports = _.filter(reportsFromOnyx, (report) => ReportUtils.unread(report) && ReportUtils.isEmptyReport(report)); updateUnread(_.size(unreadReports)); }, }); From 90c915b71daf148b34c3b8ea2282c7fbcff3de3c Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Sun, 22 Oct 2023 02:08:23 +0500 Subject: [PATCH 009/244] fix: empty condition --- src/libs/UnreadIndicatorUpdater/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/UnreadIndicatorUpdater/index.js b/src/libs/UnreadIndicatorUpdater/index.js index 983fc57c511c..be11b3cc3a62 100644 --- a/src/libs/UnreadIndicatorUpdater/index.js +++ b/src/libs/UnreadIndicatorUpdater/index.js @@ -8,7 +8,7 @@ Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, waitForCollectionCallback: true, callback: (reportsFromOnyx) => { - const unreadReports = _.filter(reportsFromOnyx, (report) => ReportUtils.unread(report) && ReportUtils.isEmptyReport(report)); + const unreadReports = _.filter(reportsFromOnyx, (report) => ReportUtils.isUnread(report) && !ReportUtils.isEmptyReport(report)); updateUnread(_.size(unreadReports)); }, }); From 15294bed03d9df1331630eaeb4cc02a6bbef2aaa Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Mon, 23 Oct 2023 01:47:50 +0500 Subject: [PATCH 010/244] feat: mimic lhn --- src/libs/UnreadIndicatorUpdater/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/UnreadIndicatorUpdater/index.js b/src/libs/UnreadIndicatorUpdater/index.js index be11b3cc3a62..a2c523d082f3 100644 --- a/src/libs/UnreadIndicatorUpdater/index.js +++ b/src/libs/UnreadIndicatorUpdater/index.js @@ -1,5 +1,6 @@ import _ from 'underscore'; import Onyx from 'react-native-onyx'; +import Navigation from '../Navigation/Navigation'; import ONYXKEYS from '../../ONYXKEYS'; import updateUnread from './updateUnread/index'; import * as ReportUtils from '../ReportUtils'; @@ -8,7 +9,7 @@ Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, waitForCollectionCallback: true, callback: (reportsFromOnyx) => { - const unreadReports = _.filter(reportsFromOnyx, (report) => ReportUtils.isUnread(report) && !ReportUtils.isEmptyReport(report)); + const unreadReports = _.filter(reportsFromOnyx, (report) => ReportUtils.isUnread(report) && ReportUtils.shouldReportBeInOptionList(report, Navigation.getTopmostReportId())); updateUnread(_.size(unreadReports)); }, }); From 7fba7598f009fd8cbae6961a04ccb822e75a118a Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Tue, 24 Oct 2023 19:26:28 +0500 Subject: [PATCH 011/244] fix: remove unused variable --- src/libs/ReportUtils.js | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 7b55903bedf4..93774afc0aef 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -2992,15 +2992,6 @@ function buildTransactionThread(reportAction, moneyRequestReportID) { ); } -/** - * @param {Object} report - * @returns {Boolean} - */ -function isEmptyReport(report) { - const lastVisibleMessage = ReportActionsUtils.getLastVisibleMessage(report.reportID); - return !report.lastMessageText && !report.lastMessageTranslationKey && !lastVisibleMessage.lastMessageText && !lastVisibleMessage.lastMessageTranslationKey; -} - /** * @param {Object} report * @returns {Boolean} @@ -3412,8 +3403,16 @@ function parseReportRouteParams(route) { } const pathSegments = parsingRoute.split('/'); + + const reportIDSegment = pathSegments[1]; + + // Check for "undefined" or any other unwanted string values + if (!reportIDSegment || reportIDSegment === 'undefined') { + return {reportID: '', isSubReportPageRoute: false}; + } + return { - reportID: pathSegments[1], + reportID: reportIDSegment, isSubReportPageRoute: pathSegments.length > 2, }; } @@ -3730,6 +3729,21 @@ function getPolicyExpenseChatReportIDByOwner(policyOwner) { return expenseChat.reportID; } +/** + * Check if the report can create the request with type is iouType + * @param {Object} report + * @param {Array} betas + * @param {String} iouType + * @returns {Boolean} + */ +function canCreateRequest(report, betas, iouType) { + const participantAccountIDs = lodashGet(report, 'participantAccountIDs', []); + if (shouldDisableWriteActions(report)) { + return false; + } + return getMoneyRequestOptions(report, participantAccountIDs, betas).includes(iouType); +} + /** * @param {String} policyID * @param {Array} accountIDs @@ -4009,7 +4023,6 @@ export { navigateToDetailsPage, generateReportID, hasReportNameError, - isEmptyReport, isUnread, isUnreadWithMention, buildOptimisticWorkspaceChats, @@ -4060,6 +4073,7 @@ export { getCommentLength, getParsedComment, getMoneyRequestOptions, + canCreateRequest, hasIOUWaitingOnCurrentUserBankAccount, canRequestMoney, getWhisperDisplayNames, From d65d65b71d6b14aa41f310c1fb75c41ab623d31f Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 24 Oct 2023 17:38:44 +0200 Subject: [PATCH 012/244] Rename ConfirmationPage --- src/components/{ConfirmationPage.js => ConfirmationPage.tsx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/components/{ConfirmationPage.js => ConfirmationPage.tsx} (100%) diff --git a/src/components/ConfirmationPage.js b/src/components/ConfirmationPage.tsx similarity index 100% rename from src/components/ConfirmationPage.js rename to src/components/ConfirmationPage.tsx From 1b1d2ccd942911798b15510a8ec1ca1a9444ed9d Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 24 Oct 2023 17:46:29 +0200 Subject: [PATCH 013/244] [TS migration] Migrate 'ConfirmationPage.js' component to TypeScript --- src/components/ConfirmationPage.tsx | 49 +++++++++++++---------------- src/components/FixedFooter.tsx | 2 +- 2 files changed, 23 insertions(+), 28 deletions(-) diff --git a/src/components/ConfirmationPage.tsx b/src/components/ConfirmationPage.tsx index 4549d6ca6072..1cb3a262094d 100644 --- a/src/components/ConfirmationPage.tsx +++ b/src/components/ConfirmationPage.tsx @@ -1,6 +1,6 @@ import React from 'react'; import {View} from 'react-native'; -import PropTypes from 'prop-types'; +import {AnimationObject} from 'lottie-react-native'; import Lottie from './Lottie'; import * as LottieAnimations from './LottieAnimations'; import Text from './Text'; @@ -8,57 +8,54 @@ import styles from '../styles/styles'; import Button from './Button'; import FixedFooter from './FixedFooter'; -const propTypes = { +type ConfirmationPageProps = { /** The asset to render */ - // eslint-disable-next-line react/forbid-prop-types - animation: PropTypes.object, + animation: string | AnimationObject | {uri: string}; /** Heading of the confirmation page */ - heading: PropTypes.string, + heading: string; /** Description of the confirmation page */ - description: PropTypes.string, + description: string; /** The text for the button label */ - buttonText: PropTypes.string, + buttonText?: string; /** A function that is called when the button is clicked on */ - onButtonPress: PropTypes.func, + onButtonPress?: () => void; /** Whether we should show a confirmation button */ - shouldShowButton: PropTypes.bool, + shouldShowButton?: boolean; }; -const defaultProps = { - animation: LottieAnimations.Fireworks, - heading: '', - description: '', - buttonText: '', - onButtonPress: () => {}, - shouldShowButton: false, -}; - -function ConfirmationPage(props) { +function ConfirmationPage({ + animation = LottieAnimations.Fireworks, + heading = '', + description = '', + buttonText = '', + onButtonPress = () => {}, + shouldShowButton = false, +}: ConfirmationPageProps) { return ( <> - {props.heading} - {props.description} + {heading} + {description} - {props.shouldShowButton && ( + {shouldShowButton && (