From e8f948f68656e68853e5522a8b4df732e05e6e85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Thu, 19 Oct 2023 14:24:41 +0200 Subject: [PATCH 001/402] install native-stack --- package-lock.json | 45 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 46 insertions(+) diff --git a/package-lock.json b/package-lock.json index 95c25f602e30..75d3e9fe0ed0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,6 +37,7 @@ "@react-native-picker/picker": "^2.4.3", "@react-navigation/material-top-tabs": "^6.6.3", "@react-navigation/native": "6.1.6", + "@react-navigation/native-stack": "^6.9.14", "@react-navigation/stack": "6.3.16", "@react-ng/bounds-observer": "^0.2.1", "@rnmapbox/maps": "^10.0.11", @@ -8961,6 +8962,33 @@ "react-native": "*" } }, + "node_modules/@react-navigation/native-stack": { + "version": "6.9.14", + "resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-6.9.14.tgz", + "integrity": "sha512-7RiZkvMFN6f0kmANc63B/0m9ttQ2JpDIPWQwPU93FP698s19KTOyu7uxgl7Oi3bvsqHFO5JfiR7B+4h8lh9dxw==", + "dependencies": { + "@react-navigation/elements": "^1.3.19", + "warn-once": "^0.1.0" + }, + "peerDependencies": { + "@react-navigation/native": "^6.0.0", + "react": "*", + "react-native": "*", + "react-native-safe-area-context": ">= 3.0.0", + "react-native-screens": ">= 3.0.0" + } + }, + "node_modules/@react-navigation/native-stack/node_modules/@react-navigation/elements": { + "version": "1.3.19", + "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.19.tgz", + "integrity": "sha512-7hLvSYKPuDS070pza5gd43WDX7QgfuEmuTWNbCJhKdWlLudYmq3qzxGCBwCfO2dEI6+p8tla5wruaWiGKAbTYw==", + "peerDependencies": { + "@react-navigation/native": "^6.0.0", + "react": "*", + "react-native": "*", + "react-native-safe-area-context": ">= 3.0.0" + } + }, "node_modules/@react-navigation/routers": { "version": "6.1.8", "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-6.1.8.tgz", @@ -59320,6 +59348,23 @@ "nanoid": "^3.1.23" } }, + "@react-navigation/native-stack": { + "version": "6.9.14", + "resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-6.9.14.tgz", + "integrity": "sha512-7RiZkvMFN6f0kmANc63B/0m9ttQ2JpDIPWQwPU93FP698s19KTOyu7uxgl7Oi3bvsqHFO5JfiR7B+4h8lh9dxw==", + "requires": { + "@react-navigation/elements": "^1.3.19", + "warn-once": "^0.1.0" + }, + "dependencies": { + "@react-navigation/elements": { + "version": "1.3.19", + "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.19.tgz", + "integrity": "sha512-7hLvSYKPuDS070pza5gd43WDX7QgfuEmuTWNbCJhKdWlLudYmq3qzxGCBwCfO2dEI6+p8tla5wruaWiGKAbTYw==", + "requires": {} + } + } + }, "@react-navigation/routers": { "version": "6.1.8", "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-6.1.8.tgz", diff --git a/package.json b/package.json index 1db859827c41..665951328bd1 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "@react-native-picker/picker": "^2.4.3", "@react-navigation/material-top-tabs": "^6.6.3", "@react-navigation/native": "6.1.6", + "@react-navigation/native-stack": "^6.9.14", "@react-navigation/stack": "6.3.16", "@react-ng/bounds-observer": "^0.2.1", "@rnmapbox/maps": "^10.0.11", From b180872b639ceb3e4aa399645c9c85ec87b0b0eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Thu, 19 Oct 2023 15:14:37 +0200 Subject: [PATCH 002/402] create PlatformStackNavigator with different impls for web & native --- src/libs/Navigation/AppNavigator/ModalStackNavigators.js | 7 +++++-- .../AppNavigator/Navigators/CentralPaneNavigator.js | 4 ++-- src/libs/Navigation/AppNavigator/Navigators/Overlay.js | 1 + .../AppNavigator/Navigators/RightModalNavigator.js | 4 ++-- src/libs/Navigation/AppNavigator/PublicScreens.js | 4 ++-- .../AppNavigator/createCustomStackNavigator/index.js | 4 ++-- src/libs/Navigation/PlatformStackNavigator/index.native.ts | 7 +++++++ src/libs/Navigation/PlatformStackNavigator/index.ts | 7 +++++++ 8 files changed, 28 insertions(+), 10 deletions(-) create mode 100644 src/libs/Navigation/PlatformStackNavigator/index.native.ts create mode 100644 src/libs/Navigation/PlatformStackNavigator/index.ts diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index cfc8f815e4fe..2e5950e1b094 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -1,9 +1,12 @@ import _ from 'underscore'; import React from 'react'; -import {createStackNavigator, CardStyleInterpolators} from '@react-navigation/stack'; +import {CardStyleInterpolators} from '@react-navigation/stack'; +import * as PlatformStackNavigator from '../PlatformStackNavigator'; + import styles from '../../../styles/styles'; import SCREENS from '../../../SCREENS'; +// TODO: migrate options const defaultSubRouteOptions = { cardStyle: styles.navigationScreenCardStyle, headerShown: false, @@ -17,7 +20,7 @@ const defaultSubRouteOptions = { * @returns {Function} */ function createModalStackNavigator(screens) { - const ModalStackNavigator = createStackNavigator(); + const ModalStackNavigator = PlatformStackNavigator.createPlatformStackNavigator(); return () => ( {_.map(screens, (getComponent, name) => ( diff --git a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator.js b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator.js index 64eadcbe06c3..b5fa76a48b90 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator.js +++ b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator.js @@ -1,12 +1,12 @@ import React from 'react'; -import {createStackNavigator} from '@react-navigation/stack'; +import * as PlatformStackNavigator from '../../PlatformStackNavigator'; import SCREENS from '../../../../SCREENS'; import ReportScreenWrapper from '../ReportScreenWrapper'; import getCurrentUrl from '../../currentUrl'; import styles from '../../../../styles/styles'; import FreezeWrapper from '../../FreezeWrapper'; -const Stack = createStackNavigator(); +const Stack = PlatformStackNavigator.createPlatformStackNavigator(); const url = getCurrentUrl(); const openOnAdminRoom = url ? new URL(url).searchParams.get('openOnAdminRoom') : undefined; diff --git a/src/libs/Navigation/AppNavigator/Navigators/Overlay.js b/src/libs/Navigation/AppNavigator/Navigators/Overlay.js index 1b2faff8c5e3..ed3c27aed673 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/Overlay.js +++ b/src/libs/Navigation/AppNavigator/Navigators/Overlay.js @@ -1,5 +1,6 @@ import React from 'react'; import {Animated, View} from 'react-native'; +// TODO: migrate import {useCardAnimation} from '@react-navigation/stack'; import PropTypes from 'prop-types'; diff --git a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js index 5f24ec159828..822a1c6ead55 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js +++ b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js @@ -1,6 +1,6 @@ import React from 'react'; import {View} from 'react-native'; -import {createStackNavigator} from '@react-navigation/stack'; +import * as PlatformStackNavigator from '../../PlatformStackNavigator'; import * as ModalStackNavigators from '../ModalStackNavigators'; import RHPScreenOptions from '../RHPScreenOptions'; @@ -10,7 +10,7 @@ import styles from '../../../../styles/styles'; import Overlay from './Overlay'; import NoDropZone from '../../../../components/DragAndDrop/NoDropZone'; -const Stack = createStackNavigator(); +const Stack = PlatformStackNavigator.createPlatformStackNavigator(); const propTypes = { ...withNavigationPropTypes, diff --git a/src/libs/Navigation/AppNavigator/PublicScreens.js b/src/libs/Navigation/AppNavigator/PublicScreens.js index 7b0afb787278..6a2ad51cab6b 100644 --- a/src/libs/Navigation/AppNavigator/PublicScreens.js +++ b/src/libs/Navigation/AppNavigator/PublicScreens.js @@ -1,5 +1,5 @@ import React from 'react'; -import {createStackNavigator} from '@react-navigation/stack'; +import * as PlatformStackNavigator from '../PlatformStackNavigator'; import SignInPage from '../../../pages/signin/SignInPage'; import ValidateLoginPage from '../../../pages/ValidateLoginPage'; import LogInWithShortLivedAuthTokenPage from '../../../pages/LogInWithShortLivedAuthTokenPage'; @@ -10,7 +10,7 @@ import AppleSignInDesktopPage from '../../../pages/signin/AppleSignInDesktopPage import GoogleSignInDesktopPage from '../../../pages/signin/GoogleSignInDesktopPage'; import SAMLSignInPage from '../../../pages/signin/SAMLSignInPage'; -const RootStack = createStackNavigator(); +const RootStack = PlatformStackNavigator.createPlatformStackNavigator(); function PublicScreens() { return ( diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js index 58be3d2af3da..5b9679426a4f 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js @@ -1,7 +1,7 @@ import React, {useRef} from 'react'; import PropTypes from 'prop-types'; import {useNavigationBuilder, createNavigatorFactory} from '@react-navigation/native'; -import {StackView} from '@react-navigation/stack'; +import * as PlatformStackNavigator from '../../PlatformStackNavigator'; import CustomRouter from './CustomRouter'; import useWindowDimensions from '../../../../hooks/useWindowDimensions'; @@ -42,7 +42,7 @@ function ResponsiveStackNavigator(props) { return ( - Date: Thu, 19 Oct 2023 15:22:14 +0200 Subject: [PATCH 003/402] create separate functions --- src/libs/Navigation/AppNavigator/ModalStackNavigators.js | 4 ++-- .../AppNavigator/Navigators/CentralPaneNavigator.js | 4 ++-- .../AppNavigator/Navigators/RightModalNavigator.js | 4 ++-- src/libs/Navigation/AppNavigator/PublicScreens.js | 4 ++-- .../AppNavigator/createCustomStackNavigator/index.js | 4 ++-- .../Navigation/PlatformStackNavigation/StackView.native.ts | 3 +++ src/libs/Navigation/PlatformStackNavigation/StackView.ts | 3 +++ .../createPlatformStackNavigator.native.ts | 7 +++++++ .../createPlatformStackNavigator.ts | 7 +++++++ src/libs/Navigation/PlatformStackNavigator/index.native.ts | 7 ------- src/libs/Navigation/PlatformStackNavigator/index.ts | 7 ------- 11 files changed, 30 insertions(+), 24 deletions(-) create mode 100644 src/libs/Navigation/PlatformStackNavigation/StackView.native.ts create mode 100644 src/libs/Navigation/PlatformStackNavigation/StackView.ts create mode 100644 src/libs/Navigation/PlatformStackNavigation/createPlatformStackNavigator.native.ts create mode 100644 src/libs/Navigation/PlatformStackNavigation/createPlatformStackNavigator.ts delete mode 100644 src/libs/Navigation/PlatformStackNavigator/index.native.ts delete mode 100644 src/libs/Navigation/PlatformStackNavigator/index.ts diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index 2e5950e1b094..985aacc9da96 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -1,7 +1,7 @@ import _ from 'underscore'; import React from 'react'; import {CardStyleInterpolators} from '@react-navigation/stack'; -import * as PlatformStackNavigator from '../PlatformStackNavigator'; +import createPlatformStackNavigator from '../PlatformStackNavigation/createPlatformStackNavigator'; import styles from '../../../styles/styles'; import SCREENS from '../../../SCREENS'; @@ -20,7 +20,7 @@ const defaultSubRouteOptions = { * @returns {Function} */ function createModalStackNavigator(screens) { - const ModalStackNavigator = PlatformStackNavigator.createPlatformStackNavigator(); + const ModalStackNavigator = createPlatformStackNavigator(); return () => ( {_.map(screens, (getComponent, name) => ( diff --git a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator.js b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator.js index b5fa76a48b90..754a25694785 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator.js +++ b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator.js @@ -1,12 +1,12 @@ import React from 'react'; -import * as PlatformStackNavigator from '../../PlatformStackNavigator'; +import createPlatformStackNavigator from '../../PlatformStackNavigation/createPlatformStackNavigator'; import SCREENS from '../../../../SCREENS'; import ReportScreenWrapper from '../ReportScreenWrapper'; import getCurrentUrl from '../../currentUrl'; import styles from '../../../../styles/styles'; import FreezeWrapper from '../../FreezeWrapper'; -const Stack = PlatformStackNavigator.createPlatformStackNavigator(); +const Stack = createPlatformStackNavigator(); const url = getCurrentUrl(); const openOnAdminRoom = url ? new URL(url).searchParams.get('openOnAdminRoom') : undefined; diff --git a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js index 822a1c6ead55..2086490c597f 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js +++ b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js @@ -1,6 +1,6 @@ import React from 'react'; import {View} from 'react-native'; -import * as PlatformStackNavigator from '../../PlatformStackNavigator'; +import createPlatformStackNavigator from '../../PlatformStackNavigation/createPlatformStackNavigator'; import * as ModalStackNavigators from '../ModalStackNavigators'; import RHPScreenOptions from '../RHPScreenOptions'; @@ -10,7 +10,7 @@ import styles from '../../../../styles/styles'; import Overlay from './Overlay'; import NoDropZone from '../../../../components/DragAndDrop/NoDropZone'; -const Stack = PlatformStackNavigator.createPlatformStackNavigator(); +const Stack = createPlatformStackNavigator(); const propTypes = { ...withNavigationPropTypes, diff --git a/src/libs/Navigation/AppNavigator/PublicScreens.js b/src/libs/Navigation/AppNavigator/PublicScreens.js index 6a2ad51cab6b..720784f97ec1 100644 --- a/src/libs/Navigation/AppNavigator/PublicScreens.js +++ b/src/libs/Navigation/AppNavigator/PublicScreens.js @@ -1,5 +1,5 @@ import React from 'react'; -import * as PlatformStackNavigator from '../PlatformStackNavigator'; +import createPlatformStackNavigator from '../PlatformStackNavigation/createPlatformStackNavigator'; import SignInPage from '../../../pages/signin/SignInPage'; import ValidateLoginPage from '../../../pages/ValidateLoginPage'; import LogInWithShortLivedAuthTokenPage from '../../../pages/LogInWithShortLivedAuthTokenPage'; @@ -10,7 +10,7 @@ import AppleSignInDesktopPage from '../../../pages/signin/AppleSignInDesktopPage import GoogleSignInDesktopPage from '../../../pages/signin/GoogleSignInDesktopPage'; import SAMLSignInPage from '../../../pages/signin/SAMLSignInPage'; -const RootStack = PlatformStackNavigator.createPlatformStackNavigator(); +const RootStack = createPlatformStackNavigator(); function PublicScreens() { return ( diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js index 5b9679426a4f..60894018ffd3 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js @@ -1,7 +1,7 @@ import React, {useRef} from 'react'; import PropTypes from 'prop-types'; import {useNavigationBuilder, createNavigatorFactory} from '@react-navigation/native'; -import * as PlatformStackNavigator from '../../PlatformStackNavigator'; +import StackView from '../../PlatformStackNavigation/StackView'; import CustomRouter from './CustomRouter'; import useWindowDimensions from '../../../../hooks/useWindowDimensions'; @@ -42,7 +42,7 @@ function ResponsiveStackNavigator(props) { return ( - Date: Thu, 19 Oct 2023 17:06:52 +0200 Subject: [PATCH 004/402] fix right modal options --- .../defaultScreenOptions/index.native.ts | 10 +++++++++ .../index.ts} | 0 .../index.native.ts | 7 +++++++ .../getRightModalNavigatorOptions/index.ts | 21 +++++++++++++++++++ .../getRootNavigatorScreenOptions.js | 15 ++----------- .../getNavigationModalCardStyles/types.ts | 4 +--- 6 files changed, 41 insertions(+), 16 deletions(-) create mode 100644 src/libs/Navigation/AppNavigator/defaultScreenOptions/index.native.ts rename src/libs/Navigation/AppNavigator/{defaultScreenOptions.js => defaultScreenOptions/index.ts} (100%) create mode 100644 src/libs/Navigation/AppNavigator/getRightModalNavigatorOptions/index.native.ts create mode 100644 src/libs/Navigation/AppNavigator/getRightModalNavigatorOptions/index.ts diff --git a/src/libs/Navigation/AppNavigator/defaultScreenOptions/index.native.ts b/src/libs/Navigation/AppNavigator/defaultScreenOptions/index.native.ts new file mode 100644 index 000000000000..12ba580c41d8 --- /dev/null +++ b/src/libs/Navigation/AppNavigator/defaultScreenOptions/index.native.ts @@ -0,0 +1,10 @@ +const defaultScreenOptions = { + contentStyle: { + overflow: 'visible', + flex: 1, + }, + headerShown: false, + animationTypeForReplace: 'push', +}; + +export default defaultScreenOptions; diff --git a/src/libs/Navigation/AppNavigator/defaultScreenOptions.js b/src/libs/Navigation/AppNavigator/defaultScreenOptions/index.ts similarity index 100% rename from src/libs/Navigation/AppNavigator/defaultScreenOptions.js rename to src/libs/Navigation/AppNavigator/defaultScreenOptions/index.ts diff --git a/src/libs/Navigation/AppNavigator/getRightModalNavigatorOptions/index.native.ts b/src/libs/Navigation/AppNavigator/getRightModalNavigatorOptions/index.native.ts new file mode 100644 index 000000000000..35d4605a2e5c --- /dev/null +++ b/src/libs/Navigation/AppNavigator/getRightModalNavigatorOptions/index.native.ts @@ -0,0 +1,7 @@ +import {NativeStackNavigationOptions} from '@react-navigation/native-stack'; + +const rightModalNavigatorOptions = (): NativeStackNavigationOptions => ({ + presentation: 'card', +}); + +export default rightModalNavigatorOptions; diff --git a/src/libs/Navigation/AppNavigator/getRightModalNavigatorOptions/index.ts b/src/libs/Navigation/AppNavigator/getRightModalNavigatorOptions/index.ts new file mode 100644 index 000000000000..ccab8ecd9fa5 --- /dev/null +++ b/src/libs/Navigation/AppNavigator/getRightModalNavigatorOptions/index.ts @@ -0,0 +1,21 @@ +import {StackNavigationOptions} from '@react-navigation/stack'; +import getNavigationModalCardStyle from '../../../../styles/getNavigationModalCardStyles'; +import modalCardStyleInterpolator from '../modalCardStyleInterpolator'; + +const rightModalNavigatorOptions = (isSmallScreenWidth: boolean): StackNavigationOptions => ({ + cardStyleInterpolator: (props) => modalCardStyleInterpolator(isSmallScreenWidth, false, props), + presentation: 'transparentModal', + + // We want pop in RHP since there are some flows that would work weird otherwise + animationTypeForReplace: 'pop', + cardStyle: { + ...getNavigationModalCardStyle(), + + // This is necessary to cover translated sidebar with overlay. + width: isSmallScreenWidth ? '100%' : '200%', + // Excess space should be on the left so we need to position from right. + right: 0, + }, +}); + +export default rightModalNavigatorOptions; diff --git a/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.js b/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.js index a7456fb071b4..ef879e269242 100644 --- a/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.js +++ b/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.js @@ -3,6 +3,7 @@ import styles from '../../../styles/styles'; import variables from '../../../styles/variables'; import getNavigationModalCardStyle from '../../../styles/getNavigationModalCardStyles'; import CONFIG from '../../../CONFIG'; +import getRightModalNavigatorOptions from './getRightModalNavigatorOptions'; const commonScreenOptions = { headerShown: false, @@ -15,19 +16,7 @@ const commonScreenOptions = { export default (isSmallScreenWidth) => ({ rightModalNavigator: { ...commonScreenOptions, - cardStyleInterpolator: (props) => modalCardStyleInterpolator(isSmallScreenWidth, false, props), - presentation: 'transparentModal', - - // We want pop in RHP since there are some flows that would work weird otherwise - animationTypeForReplace: 'pop', - cardStyle: { - ...getNavigationModalCardStyle(), - - // This is necessary to cover translated sidebar with overlay. - width: isSmallScreenWidth ? '100%' : '200%', - // Excess space should be on the left so we need to position from right. - right: 0, - }, + ...getRightModalNavigatorOptions(isSmallScreenWidth), }, homeScreen: { diff --git a/src/styles/getNavigationModalCardStyles/types.ts b/src/styles/getNavigationModalCardStyles/types.ts index 877981dd4dd2..e0dba07dc908 100644 --- a/src/styles/getNavigationModalCardStyles/types.ts +++ b/src/styles/getNavigationModalCardStyles/types.ts @@ -1,7 +1,5 @@ import {ViewStyle} from 'react-native'; -type GetNavigationModalCardStylesParams = {isSmallScreenWidth: number}; - -type GetNavigationModalCardStyles = (params: GetNavigationModalCardStylesParams) => ViewStyle; +type GetNavigationModalCardStyles = () => ViewStyle; export default GetNavigationModalCardStyles; From 27b2a131ccc8d7a9652adc127f67752f6911b447 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Thu, 19 Oct 2023 17:09:36 +0200 Subject: [PATCH 005/402] add note about card* property usage --- .../Navigation/AppNavigator/getRootNavigatorScreenOptions.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.js b/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.js index ef879e269242..578ed1881b97 100644 --- a/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.js +++ b/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.js @@ -22,8 +22,8 @@ export default (isSmallScreenWidth) => ({ homeScreen: { title: CONFIG.SITE_TITLE, ...commonScreenOptions, + // Note: The card* properties won't be applied on mobile platforms, as they use the native defaults. cardStyleInterpolator: (props) => modalCardStyleInterpolator(isSmallScreenWidth, false, props), - cardStyle: { ...getNavigationModalCardStyle(), width: isSmallScreenWidth ? '100%' : variables.sideBarWidth, @@ -36,6 +36,7 @@ export default (isSmallScreenWidth) => ({ // eslint-disable-next-line rulesdir/no-negated-variables fullScreen: { ...commonScreenOptions, + cardStyleInterpolator: (props) => modalCardStyleInterpolator(isSmallScreenWidth, true, props), cardStyle: { ...getNavigationModalCardStyle(), @@ -49,8 +50,8 @@ export default (isSmallScreenWidth) => ({ title: CONFIG.SITE_TITLE, ...commonScreenOptions, animationEnabled: isSmallScreenWidth, - cardStyleInterpolator: (props) => modalCardStyleInterpolator(isSmallScreenWidth, true, props), + cardStyleInterpolator: (props) => modalCardStyleInterpolator(isSmallScreenWidth, true, props), cardStyle: { ...getNavigationModalCardStyle(), paddingRight: isSmallScreenWidth ? 0 : variables.sideBarWidth, From 5e5deb49b113133448114eff68fb05c8547b822d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Thu, 19 Oct 2023 17:40:19 +0200 Subject: [PATCH 006/402] migrate defaultSubRouteOptions --- .../Navigation/AppNavigator/ModalStackNavigators.js | 11 +---------- .../modalStackNavigatorOptions/index.native.ts | 9 +++++++++ .../AppNavigator/modalStackNavigatorOptions/index.ts | 10 ++++++++++ 3 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 src/libs/Navigation/AppNavigator/modalStackNavigatorOptions/index.native.ts create mode 100644 src/libs/Navigation/AppNavigator/modalStackNavigatorOptions/index.ts diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index 985aacc9da96..7f5e8fbc3579 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -1,17 +1,8 @@ import _ from 'underscore'; import React from 'react'; -import {CardStyleInterpolators} from '@react-navigation/stack'; import createPlatformStackNavigator from '../PlatformStackNavigation/createPlatformStackNavigator'; - -import styles from '../../../styles/styles'; import SCREENS from '../../../SCREENS'; - -// TODO: migrate options -const defaultSubRouteOptions = { - cardStyle: styles.navigationScreenCardStyle, - headerShown: false, - cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS, -}; +import defaultSubRouteOptions from './modalStackNavigatorOptions'; /** * Create a modal stack navigator with an array of sub-screens. diff --git a/src/libs/Navigation/AppNavigator/modalStackNavigatorOptions/index.native.ts b/src/libs/Navigation/AppNavigator/modalStackNavigatorOptions/index.native.ts new file mode 100644 index 000000000000..c54b1d653aca --- /dev/null +++ b/src/libs/Navigation/AppNavigator/modalStackNavigatorOptions/index.native.ts @@ -0,0 +1,9 @@ +import {NativeStackNavigationOptions} from '@react-navigation/native-stack'; +import styles from '../../../../styles/styles'; + +const defaultSubRouteOptions: NativeStackNavigationOptions = { + contentStyle: styles.navigationScreenCardStyle, + headerShown: false, +}; + +export default defaultSubRouteOptions; diff --git a/src/libs/Navigation/AppNavigator/modalStackNavigatorOptions/index.ts b/src/libs/Navigation/AppNavigator/modalStackNavigatorOptions/index.ts new file mode 100644 index 000000000000..ea241e6f441b --- /dev/null +++ b/src/libs/Navigation/AppNavigator/modalStackNavigatorOptions/index.ts @@ -0,0 +1,10 @@ +import {CardStyleInterpolators, StackNavigationOptions} from '@react-navigation/stack'; +import styles from '../../../../styles/styles'; + +const defaultSubRouteOptions: StackNavigationOptions = { + cardStyle: styles.navigationScreenCardStyle, + headerShown: false, + cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS, +}; + +export default defaultSubRouteOptions; From 6e49ccf73e6aa9907a24debc676696bdb9c83c1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Fri, 20 Oct 2023 08:08:20 +0200 Subject: [PATCH 007/402] change default animation for android --- .../Navigation/AppNavigator/defaultScreenOptions/index.native.ts | 1 + .../AppNavigator/getRightModalNavigatorOptions/index.native.ts | 1 + .../Navigation/AppNavigator/getRootNavigatorScreenOptions.js | 1 + .../AppNavigator/modalStackNavigatorOptions/index.native.ts | 1 + 4 files changed, 4 insertions(+) diff --git a/src/libs/Navigation/AppNavigator/defaultScreenOptions/index.native.ts b/src/libs/Navigation/AppNavigator/defaultScreenOptions/index.native.ts index 12ba580c41d8..17100bc71bda 100644 --- a/src/libs/Navigation/AppNavigator/defaultScreenOptions/index.native.ts +++ b/src/libs/Navigation/AppNavigator/defaultScreenOptions/index.native.ts @@ -5,6 +5,7 @@ const defaultScreenOptions = { }, headerShown: false, animationTypeForReplace: 'push', + animation: 'slide_from_right', }; export default defaultScreenOptions; diff --git a/src/libs/Navigation/AppNavigator/getRightModalNavigatorOptions/index.native.ts b/src/libs/Navigation/AppNavigator/getRightModalNavigatorOptions/index.native.ts index 35d4605a2e5c..9c0d1fe3abff 100644 --- a/src/libs/Navigation/AppNavigator/getRightModalNavigatorOptions/index.native.ts +++ b/src/libs/Navigation/AppNavigator/getRightModalNavigatorOptions/index.native.ts @@ -2,6 +2,7 @@ import {NativeStackNavigationOptions} from '@react-navigation/native-stack'; const rightModalNavigatorOptions = (): NativeStackNavigationOptions => ({ presentation: 'card', + animation: 'slide_from_right', }); export default rightModalNavigatorOptions; diff --git a/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.js b/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.js index 578ed1881b97..9d485f239fe6 100644 --- a/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.js +++ b/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.js @@ -11,6 +11,7 @@ const commonScreenOptions = { animationEnabled: true, cardOverlayEnabled: true, animationTypeForReplace: 'push', + animation: 'slide_from_right', }; export default (isSmallScreenWidth) => ({ diff --git a/src/libs/Navigation/AppNavigator/modalStackNavigatorOptions/index.native.ts b/src/libs/Navigation/AppNavigator/modalStackNavigatorOptions/index.native.ts index c54b1d653aca..96ee4c4b26d2 100644 --- a/src/libs/Navigation/AppNavigator/modalStackNavigatorOptions/index.native.ts +++ b/src/libs/Navigation/AppNavigator/modalStackNavigatorOptions/index.native.ts @@ -4,6 +4,7 @@ import styles from '../../../../styles/styles'; const defaultSubRouteOptions: NativeStackNavigationOptions = { contentStyle: styles.navigationScreenCardStyle, headerShown: false, + animation: 'slide_from_right', }; export default defaultSubRouteOptions; From 08c6181183ac2f092e739985a75fed309d8b65c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Fri, 20 Oct 2023 08:59:45 +0200 Subject: [PATCH 008/402] migrate overlay --- .../Navigators/{Overlay.js => Overlay/index.js} | 9 ++++----- .../AppNavigator/Navigators/Overlay/index.native.js | 7 +++++++ 2 files changed, 11 insertions(+), 5 deletions(-) rename src/libs/Navigation/AppNavigator/Navigators/{Overlay.js => Overlay/index.js} (86%) create mode 100644 src/libs/Navigation/AppNavigator/Navigators/Overlay/index.native.js diff --git a/src/libs/Navigation/AppNavigator/Navigators/Overlay.js b/src/libs/Navigation/AppNavigator/Navigators/Overlay/index.js similarity index 86% rename from src/libs/Navigation/AppNavigator/Navigators/Overlay.js rename to src/libs/Navigation/AppNavigator/Navigators/Overlay/index.js index ed3c27aed673..ad35484d21d8 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/Overlay.js +++ b/src/libs/Navigation/AppNavigator/Navigators/Overlay/index.js @@ -1,14 +1,13 @@ import React from 'react'; import {Animated, View} from 'react-native'; -// TODO: migrate import {useCardAnimation} from '@react-navigation/stack'; import PropTypes from 'prop-types'; -import styles from '../../../../styles/styles'; +import styles from '../../../../../styles/styles'; -import PressableWithoutFeedback from '../../../../components/Pressable/PressableWithoutFeedback'; -import useLocalize from '../../../../hooks/useLocalize'; -import CONST from '../../../../CONST'; +import PressableWithoutFeedback from '../../../../../components/Pressable/PressableWithoutFeedback'; +import useLocalize from '../../../../../hooks/useLocalize'; +import CONST from '../../../../../CONST'; const propTypes = { /* Callback to close the modal */ diff --git a/src/libs/Navigation/AppNavigator/Navigators/Overlay/index.native.js b/src/libs/Navigation/AppNavigator/Navigators/Overlay/index.native.js new file mode 100644 index 000000000000..30651e32cbd6 --- /dev/null +++ b/src/libs/Navigation/AppNavigator/Navigators/Overlay/index.native.js @@ -0,0 +1,7 @@ +function Overlay() { + return null; +} + +Overlay.displayName = 'Overlay'; + +export default Overlay; From 18d46518d7626e8f0966bc51c408aaa6392fbe09 Mon Sep 17 00:00:00 2001 From: s-alves10 Date: Wed, 22 Nov 2023 00:55:01 -0600 Subject: [PATCH 009/402] fix: check if the parent report is the archived room --- src/libs/actions/Task.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index c91f6d1a2eec..e59debd08a1d 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -877,6 +877,11 @@ function canModifyTask(taskReport, sessionAccountID) { return false; } + const parentReport = ReportUtils.getParentReport(taskReport); + if (ReportUtils.isArchivedRoom(parentReport)) { + return false; + } + if (sessionAccountID === getTaskOwnerAccountID(taskReport) || sessionAccountID === getTaskAssigneeAccountID(taskReport)) { return true; } @@ -884,7 +889,6 @@ function canModifyTask(taskReport, sessionAccountID) { // If you don't have access to the task report (maybe haven't opened it yet), check if you can access the parent report // - If the parent report is an #admins only room // - If you are a policy admin - const parentReport = ReportUtils.getParentReport(taskReport); return ReportUtils.isAllowedToComment(parentReport); } From dded6349ee50d371ed5c920a4c3334700ad1d537 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Tue, 19 Dec 2023 21:55:13 +0530 Subject: [PATCH 010/402] test map when image is not loaded in request view --- .../ReportActionItem/MoneyRequestPreview.js | 10 +++++++++- src/components/ReportActionItem/MoneyRequestView.js | 13 ++++++++++++- src/libs/actions/IOU.js | 3 +++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestPreview.js b/src/components/ReportActionItem/MoneyRequestPreview.js index 12c6d0629370..1d798130dfd5 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.js +++ b/src/components/ReportActionItem/MoneyRequestPreview.js @@ -38,6 +38,7 @@ import CONST from '@src/CONST'; import * as Localize from '@src/libs/Localize'; import ONYXKEYS from '@src/ONYXKEYS'; import ReportActionItemImages from './ReportActionItemImages'; +import ConfirmedRoute from '@components/ConfirmedRoute'; const propTypes = { /** The active IOUReport, used for Onyx subscription */ @@ -169,6 +170,8 @@ function MoneyRequestPreview(props) { const hasPendingWaypoints = lodashGet(props.transaction, 'pendingFields.waypoints', null); + const showMapAsImage = isDistanceRequest && hasPendingWaypoints; + const getSettledMessage = () => { if (isExpensifyCardTransaction) { return translate('common.done'); @@ -257,7 +260,12 @@ function MoneyRequestPreview(props) { !props.onPreviewPressed ? [styles.moneyRequestPreviewBox, ...props.containerStyles] : {}, ]} > - {hasReceipt && ( + {showMapAsImage && ( + + + + )} + {!showMapAsImage && hasReceipt && ( - {hasReceipt && ( + {showMapAsImage && ( + + + + + + )} + {!showMapAsImage && hasReceipt && ( Date: Wed, 20 Dec 2023 04:54:23 -0600 Subject: [PATCH 011/402] fix: remove redundant code --- src/libs/actions/Task.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index 03b94d5f82de..50172158efdb 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -882,8 +882,6 @@ function canModifyTask(taskReport, sessionAccountID, policyRole = '') { return true; } - const parentReport = ReportUtils.getParentReport(taskReport); - if (policyRole && (ReportUtils.isChatRoom(parentReport) || ReportUtils.isPolicyExpenseChat(parentReport)) && policyRole !== CONST.POLICY.ROLE.ADMIN) { return false; } From b14c40dfd3a5161b4d1828a040ae6c5e57888e45 Mon Sep 17 00:00:00 2001 From: Antony Kithinzi Date: Tue, 2 Jan 2024 21:31:37 +0100 Subject: [PATCH 012/402] fix: adding backTo param to ReportParticipantsPage.js --- src/pages/ReportParticipantsPage.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pages/ReportParticipantsPage.js b/src/pages/ReportParticipantsPage.js index e04ffbb352fc..1e84bd3dcbbb 100755 --- a/src/pages/ReportParticipantsPage.js +++ b/src/pages/ReportParticipantsPage.js @@ -121,7 +121,12 @@ function ReportParticipantsPage(props) { }, ]} onSelectRow={(option) => { - Navigation.navigate(ROUTES.PROFILE.getRoute(option.accountID)); + Navigation.navigate( + ROUTES.PROFILE.getRoute( + option.accountID, + ROUTES.REPORT_PARTICIPANTS.getRoute(props.report.reportID) + ) + ); }} hideSectionHeaders showTitleTooltip From ef26aaa1d61d8b25ae43ed2deb9cbc0c589d74b5 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Tue, 9 Jan 2024 17:49:16 +0530 Subject: [PATCH 013/402] test changes --- src/components/ReportActionItem/MoneyRequestPreview.js | 2 +- src/components/ReportActionItem/MoneyRequestView.js | 2 +- src/libs/TransactionUtils.ts | 7 +++++-- src/libs/actions/IOU.js | 4 +++- src/types/onyx/Transaction.ts | 6 ++++-- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestPreview.js b/src/components/ReportActionItem/MoneyRequestPreview.js index 0663441c3834..9bba3aed205a 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.js +++ b/src/components/ReportActionItem/MoneyRequestPreview.js @@ -38,8 +38,8 @@ import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; import * as Localize from '@src/libs/Localize'; import ONYXKEYS from '@src/ONYXKEYS'; -import ReportActionItemImages from './ReportActionItemImages'; import ConfirmedRoute from '@components/ConfirmedRoute'; +import ReportActionItemImages from './ReportActionItemImages'; const propTypes = { /** The active IOUReport, used for Onyx subscription */ diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index eb658a7f8a1b..fc70b47d7101 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -42,8 +42,8 @@ import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import ReportActionItemImage from './ReportActionItemImage'; import ConfirmedRoute from '@components/ConfirmedRoute'; +import ReportActionItemImage from './ReportActionItemImage'; const violationNames = lodashValues(CONST.VIOLATIONS); diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index c34a6753c1d5..b8fad803d957 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -1,5 +1,6 @@ import lodashHas from 'lodash/has'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import type {EmptyObject} from '@src/types/utils/EmptyObject'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; @@ -7,8 +8,8 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {RecentWaypoint, Report, ReportAction, Transaction} from '@src/types/onyx'; import type {PolicyTaxRates} from '@src/types/onyx/PolicyTaxRates'; import type PolicyTaxRate from '@src/types/onyx/PolicyTaxRates'; -import type {Comment, Receipt, Waypoint, WaypointCollection} from '@src/types/onyx/Transaction'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; +import type {Comment, PendingFieldsCollection, Receipt, Waypoint, WaypointCollection} from '@src/types/onyx/Transaction'; +import {isNotEmptyObject} from '@src/types/utils/EmptyObject'; import {isCorporateCard, isExpensifyCard} from './CardUtils'; import DateUtils from './DateUtils'; import * as NumberUtils from './NumberUtils'; @@ -98,6 +99,7 @@ function buildOptimisticTransaction( category = '', tag = '', billable = false, + pendingFields: PendingFieldsCollection | null = null, ): Transaction { // transactionIDs are random, positive, 64-bit numeric strings. // Because JS can only handle 53-bit numbers, transactionIDs are strings in the front-end (just like reportActionID) @@ -112,6 +114,7 @@ function buildOptimisticTransaction( } return { + ...(isNotEmptyObject(pendingFields) ? {pendingFields} : {}), transactionID, amount, currency, diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 4e1a45101732..f4ff83e995c8 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -721,6 +721,8 @@ function getMoneyRequestInformation( receiptObject.state = receipt.state || CONST.IOU.RECEIPT_STATE.SCANREADY; filename = receipt.name; } + const existingTransaction = allTransactionDrafts[`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`]; + const isDistanceRequest = existingTransaction && existingTransaction.iouRequestType === CONST.IOU.REQUEST_TYPE.DISTANCE let optimisticTransaction = TransactionUtils.buildOptimisticTransaction( ReportUtils.isExpenseReport(iouReport) ? -amount : amount, currency, @@ -736,6 +738,7 @@ function getMoneyRequestInformation( category, tag, billable, + isDistanceRequest ? {waypoints: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD} : null, ); const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(iouReport.policyID, category); @@ -747,7 +750,6 @@ function getMoneyRequestInformation( // data. This is a big can of worms to change it to `Onyx.merge()` as explored in https://expensify.slack.com/archives/C05DWUDHVK7/p1692139468252109. // I want to clean this up at some point, but it's possible this will live in the code for a while so I've created https://github.com/Expensify/App/issues/25417 // to remind me to do this. - const existingTransaction = allTransactionDrafts[`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`]; if (existingTransaction && existingTransaction.iouRequestType === CONST.IOU.REQUEST_TYPE.DISTANCE) { optimisticTransaction = OnyxUtils.fastMerge(existingTransaction, optimisticTransaction); // pendingFields: { diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 8b7e26280305..a6b65f7cba38 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -49,6 +49,8 @@ type Route = { type Routes = Record; +type PendingFieldsCollection = Partial<{[K in keyof Transaction | keyof Comment]: ValueOf}>; + type Transaction = { amount: number; billable: boolean; @@ -76,7 +78,7 @@ type Transaction = { routes?: Routes; transactionID: string; tag: string; - pendingFields?: Partial<{[K in keyof Transaction | keyof Comment]: ValueOf}>; + pendingFields?: PendingFieldsCollection; /** Card Transactions */ @@ -97,4 +99,4 @@ type Transaction = { }; export default Transaction; -export type {WaypointCollection, Comment, Receipt, Waypoint}; +export type {WaypointCollection, Comment, Receipt, Waypoint, PendingFieldsCollection}; From 54a63d643b9ad602ace915090c3c18cae28af5b0 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Tue, 9 Jan 2024 18:07:40 +0530 Subject: [PATCH 014/402] fix lint --- src/components/ReportActionItem/MoneyRequestPreview.js | 2 +- src/components/ReportActionItem/MoneyRequestView.js | 2 +- src/libs/TransactionUtils.ts | 2 +- src/libs/actions/IOU.js | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestPreview.js b/src/components/ReportActionItem/MoneyRequestPreview.js index 9bba3aed205a..84ed317be294 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.js +++ b/src/components/ReportActionItem/MoneyRequestPreview.js @@ -5,6 +5,7 @@ import React from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; +import ConfirmedRoute from '@components/ConfirmedRoute'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import MoneyRequestSkeletonView from '@components/MoneyRequestSkeletonView'; @@ -38,7 +39,6 @@ import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; import * as Localize from '@src/libs/Localize'; import ONYXKEYS from '@src/ONYXKEYS'; -import ConfirmedRoute from '@components/ConfirmedRoute'; import ReportActionItemImages from './ReportActionItemImages'; const propTypes = { diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index fc70b47d7101..04b8d35e3448 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -5,6 +5,7 @@ import React, {useCallback} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import categoryPropTypes from '@components/categoryPropTypes'; +import ConfirmedRoute from '@components/ConfirmedRoute'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -42,7 +43,6 @@ import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import ConfirmedRoute from '@components/ConfirmedRoute'; import ReportActionItemImage from './ReportActionItemImage'; const violationNames = lodashValues(CONST.VIOLATIONS); diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index b8fad803d957..4155fe426f5a 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -1,6 +1,5 @@ import lodashHas from 'lodash/has'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; @@ -9,6 +8,7 @@ import type {RecentWaypoint, Report, ReportAction, Transaction} from '@src/types import type {PolicyTaxRates} from '@src/types/onyx/PolicyTaxRates'; import type PolicyTaxRate from '@src/types/onyx/PolicyTaxRates'; import type {Comment, PendingFieldsCollection, Receipt, Waypoint, WaypointCollection} from '@src/types/onyx/Transaction'; +import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isNotEmptyObject} from '@src/types/utils/EmptyObject'; import {isCorporateCard, isExpensifyCard} from './CardUtils'; import DateUtils from './DateUtils'; diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index f4ff83e995c8..f004b44feca2 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -722,7 +722,7 @@ function getMoneyRequestInformation( filename = receipt.name; } const existingTransaction = allTransactionDrafts[`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`]; - const isDistanceRequest = existingTransaction && existingTransaction.iouRequestType === CONST.IOU.REQUEST_TYPE.DISTANCE + const isDistanceRequest = existingTransaction && existingTransaction.iouRequestType === CONST.IOU.REQUEST_TYPE.DISTANCE; let optimisticTransaction = TransactionUtils.buildOptimisticTransaction( ReportUtils.isExpenseReport(iouReport) ? -amount : amount, currency, @@ -752,9 +752,9 @@ function getMoneyRequestInformation( // to remind me to do this. if (existingTransaction && existingTransaction.iouRequestType === CONST.IOU.REQUEST_TYPE.DISTANCE) { optimisticTransaction = OnyxUtils.fastMerge(existingTransaction, optimisticTransaction); -// pendingFields: { -// waypoints: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, -// } + // pendingFields: { + // waypoints: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + // } } // STEP 4: Build optimistic reportActions. We need: From 46141e379c495127f8287f3ca15a771c4eba63df Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Tue, 9 Jan 2024 18:09:37 +0530 Subject: [PATCH 015/402] fix lint --- src/libs/actions/IOU.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index f004b44feca2..d68b33e6b5ef 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -750,11 +750,8 @@ function getMoneyRequestInformation( // data. This is a big can of worms to change it to `Onyx.merge()` as explored in https://expensify.slack.com/archives/C05DWUDHVK7/p1692139468252109. // I want to clean this up at some point, but it's possible this will live in the code for a while so I've created https://github.com/Expensify/App/issues/25417 // to remind me to do this. - if (existingTransaction && existingTransaction.iouRequestType === CONST.IOU.REQUEST_TYPE.DISTANCE) { + if (isDistanceRequest) { optimisticTransaction = OnyxUtils.fastMerge(existingTransaction, optimisticTransaction); - // pendingFields: { - // waypoints: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - // } } // STEP 4: Build optimistic reportActions. We need: From b7045ec7091f7aba9d94939ea654c3ba5cc59b06 Mon Sep 17 00:00:00 2001 From: Antony Kithinzi Date: Fri, 12 Jan 2024 16:05:21 -0500 Subject: [PATCH 016/402] fix: adding backTo param to ReportParticipantsPage.js --- src/pages/ReportParticipantsPage.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pages/ReportParticipantsPage.js b/src/pages/ReportParticipantsPage.js index 7dbc1c7036c4..9e480d2f0516 100755 --- a/src/pages/ReportParticipantsPage.js +++ b/src/pages/ReportParticipantsPage.js @@ -121,7 +121,12 @@ function ReportParticipantsPage(props) { }, ]} onSelectRow={(option) => { - Navigation.navigate(ROUTES.PROFILE.getRoute(option.accountID)); + Navigation.navigate( + ROUTES.PROFILE.getRoute( + option.accountID, + ROUTES.REPORT_PARTICIPANTS.getRoute(props.report.reportID) + ) + ); }} hideSectionHeaders showTitleTooltip From e7b85131efe677ac38f177262f3c51b512643b82 Mon Sep 17 00:00:00 2001 From: Antony Kithinzi Date: Sat, 13 Jan 2024 04:50:32 +0100 Subject: [PATCH 017/402] fmt: prettier --- src/pages/ReportParticipantsPage.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/pages/ReportParticipantsPage.js b/src/pages/ReportParticipantsPage.js index 9e480d2f0516..3bc2783db981 100755 --- a/src/pages/ReportParticipantsPage.js +++ b/src/pages/ReportParticipantsPage.js @@ -121,12 +121,7 @@ function ReportParticipantsPage(props) { }, ]} onSelectRow={(option) => { - Navigation.navigate( - ROUTES.PROFILE.getRoute( - option.accountID, - ROUTES.REPORT_PARTICIPANTS.getRoute(props.report.reportID) - ) - ); + Navigation.navigate(ROUTES.PROFILE.getRoute(option.accountID, ROUTES.REPORT_PARTICIPANTS.getRoute(props.report.reportID))); }} hideSectionHeaders showTitleTooltip From 31581f9c234322f98b4274de43f15a85c924f260 Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Thu, 18 Jan 2024 16:24:22 +0000 Subject: [PATCH 018/402] refactor(typescript): migrate workspacenewroompage --- src/components/ScreenWrapper.tsx | 14 +- src/libs/ErrorUtils.ts | 5 +- src/libs/ValidationUtils.ts | 8 +- ...ewRoomPage.js => WorkspaceNewRoomPage.tsx} | 194 +++++++----------- 4 files changed, 96 insertions(+), 125 deletions(-) rename src/pages/workspace/{WorkspaceNewRoomPage.js => WorkspaceNewRoomPage.tsx} (73%) diff --git a/src/components/ScreenWrapper.tsx b/src/components/ScreenWrapper.tsx index 0653e2ff8577..b2815d02dcd6 100644 --- a/src/components/ScreenWrapper.tsx +++ b/src/components/ScreenWrapper.tsx @@ -24,7 +24,7 @@ import SafeAreaConsumer from './SafeAreaConsumer'; import TestToolsModal from './TestToolsModal'; type ChildrenProps = { - insets?: EdgeInsets; + insets: EdgeInsets; safeAreaPaddingBottomStyle?: { paddingBottom?: DimensionValue; }; @@ -190,7 +190,17 @@ function ScreenWrapper( return ( - {({insets, paddingTop, paddingBottom, safeAreaPaddingBottomStyle}) => { + {({ + insets = { + top: 0, + bottom: 0, + left: 0, + right: 0, + }, + paddingTop, + paddingBottom, + safeAreaPaddingBottomStyle, + }) => { const paddingStyle: StyleProp = {}; if (includePaddingTop) { diff --git a/src/libs/ErrorUtils.ts b/src/libs/ErrorUtils.ts index 159a5817189b..68bfbe706ac6 100644 --- a/src/libs/ErrorUtils.ts +++ b/src/libs/ErrorUtils.ts @@ -1,9 +1,10 @@ import CONST from '@src/CONST'; -import type {TranslationFlatObject, TranslationPaths} from '@src/languages/types'; +import type {TranslationFlatObject} from '@src/languages/types'; import type {ErrorFields, Errors} from '@src/types/onyx/OnyxCommon'; import type Response from '@src/types/onyx/Response'; import DateUtils from './DateUtils'; import * as Localize from './Localize'; +import type {MaybePhraseKey} from './Localize'; function getAuthenticateErrorMessage(response: Response): keyof TranslationFlatObject { switch (response.jsonCode) { @@ -101,7 +102,7 @@ type ErrorsList = Record; * @param errorList - An object containing current errors in the form * @param message - Message to assign to the inputID errors */ -function addErrorMessage(errors: ErrorsList, inputID?: string, message?: TKey) { +function addErrorMessage(errors: ErrorsList, inputID?: string, message?: MaybePhraseKey) { if (!message || !inputID) { return; } diff --git a/src/libs/ValidationUtils.ts b/src/libs/ValidationUtils.ts index 9ba11fb16d6a..4a98a2e99f06 100644 --- a/src/libs/ValidationUtils.ts +++ b/src/libs/ValidationUtils.ts @@ -3,6 +3,7 @@ import {URL_REGEX_WITH_REQUIRED_PROTOCOL} from 'expensify-common/lib/Url'; import isDate from 'lodash/isDate'; import isEmpty from 'lodash/isEmpty'; import isObject from 'lodash/isObject'; +import type {OnyxCollection} from 'react-native-onyx'; import CONST from '@src/CONST'; import type {Report} from '@src/types/onyx'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; @@ -354,8 +355,11 @@ function isReservedRoomName(roomName: string): boolean { /** * Checks if the room name already exists. */ -function isExistingRoomName(roomName: string, reports: Record, policyID: string): boolean { - return Object.values(reports).some((report) => report && report.policyID === policyID && report.reportName === roomName); +function isExistingRoomName(roomName: string, reports: OnyxCollection, policyID: string): boolean { + if (!reports) { + return false; + } + return Object.values(reports).some((report) => report?.policyID === policyID && report?.reportName === roomName); } /** diff --git a/src/pages/workspace/WorkspaceNewRoomPage.js b/src/pages/workspace/WorkspaceNewRoomPage.tsx similarity index 73% rename from src/pages/workspace/WorkspaceNewRoomPage.js rename to src/pages/workspace/WorkspaceNewRoomPage.tsx index 35fab36e5d41..318c3a4d744b 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.js +++ b/src/pages/workspace/WorkspaceNewRoomPage.tsx @@ -1,8 +1,9 @@ -import PropTypes from 'prop-types'; +import {useIsFocused} from '@react-navigation/core'; import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; import BlockingView from '@components/BlockingViews/BlockingView'; import Button from '@components/Button'; import FormProvider from '@components/Form/FormProvider'; @@ -14,14 +15,12 @@ import RoomNameInput from '@components/RoomNameInput'; import ScreenWrapper from '@components/ScreenWrapper'; import TextInput from '@components/TextInput'; import ValuePicker from '@components/ValuePicker'; -import withNavigationFocus from '@components/withNavigationFocus'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import compose from '@libs/compose'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; @@ -32,98 +31,58 @@ import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type {Account, Form, Policy, Report as ReportType, Session} from '@src/types/onyx'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; -const propTypes = { - /** All reports shared with the user */ - reports: PropTypes.shape({ - /** The report name */ - reportName: PropTypes.string, - - /** The report type */ - type: PropTypes.string, - - /** ID of the policy */ - policyID: PropTypes.string, - }), - - /** The list of policies the user has access to. */ - policies: PropTypes.objectOf( - PropTypes.shape({ - /** The policy type */ - type: PropTypes.oneOf(_.values(CONST.POLICY.TYPE)), - - /** The name of the policy */ - name: PropTypes.string, - - /** The ID of the policy */ - id: PropTypes.string, - }), - ), - - /** Whether navigation is focused */ - isFocused: PropTypes.bool.isRequired, - - /** Form state for NEW_ROOM_FORM */ - formState: PropTypes.shape({ - /** Loading state for the form */ - isLoading: PropTypes.bool, - - /** Field errors in the form */ - errorFields: PropTypes.objectOf(PropTypes.objectOf(PropTypes.string)), - }), - - /** Session details for the user */ - session: PropTypes.shape({ - /** accountID of current user */ - accountID: PropTypes.number, - }), - - /** policyID for main workspace */ - activePolicyID: PropTypes.string, +type FormValues = { + welcomeMessage: string; + roomName: string; + policyID: string | null; + writeCapability: ValueOf; + visibility: ValueOf; }; -const defaultProps = { - reports: {}, - policies: {}, - formState: { - isLoading: false, - errorFields: {}, - }, - session: { - accountID: 0, - }, - activePolicyID: null, + +type WorkspaceNewRoomPageOnyxProps = { + policies: OnyxCollection; + reports: OnyxCollection; + formState: OnyxEntry
; + session: OnyxEntry; + activePolicyID: OnyxEntry['activePolicyID']>; }; -function WorkspaceNewRoomPage(props) { +type WorkspaceNewRoomPageProps = WorkspaceNewRoomPageOnyxProps; + +function WorkspaceNewRoomPage({policies, reports, formState, session, activePolicyID}: WorkspaceNewRoomPageProps) { const styles = useThemeStyles(); + const isFocused = useIsFocused(); const {translate} = useLocalize(); const {isOffline} = useNetwork(); const {isSmallScreenWidth} = useWindowDimensions(); - const [visibility, setVisibility] = useState(CONST.REPORT.VISIBILITY.RESTRICTED); - const [policyID, setPolicyID] = useState(props.activePolicyID); - const [writeCapability, setWriteCapability] = useState(CONST.REPORT.WRITE_CAPABILITIES.ALL); - const wasLoading = usePrevious(props.formState.isLoading); + const [visibility, setVisibility] = useState(CONST.REPORT.VISIBILITY.RESTRICTED); + const [policyID, setPolicyID] = useState(activePolicyID); + const [writeCapability, setWriteCapability] = useState(CONST.REPORT.WRITE_CAPABILITIES.ALL); + const wasLoading = usePrevious(!!formState?.isLoading); const visibilityDescription = useMemo(() => translate(`newRoomPage.${visibility}Description`), [translate, visibility]); const isPolicyAdmin = useMemo(() => { if (!policyID) { return false; } - return ReportUtils.isPolicyAdmin(policyID, props.policies); - }, [policyID, props.policies]); - const [newRoomReportID, setNewRoomReportID] = useState(undefined); + return ReportUtils.isPolicyAdmin(policyID, policies); + }, [policyID, policies]); + const [newRoomReportID, setNewRoomReportID] = useState(); /** - * @param {Object} values - form input values passed by the Form component + * @param values - form input values passed by the Form component */ - const submit = (values) => { - const participants = [props.session.accountID]; + const submit = (values: FormValues) => { + const participants = session ? [session.accountID ?? -1] : []; const parsedWelcomeMessage = ReportUtils.getParsedComment(values.welcomeMessage); const policyReport = ReportUtils.buildOptimisticChatReport( participants, values.roomName, CONST.REPORT.CHAT_TYPE.POLICY_ROOM, - policyID, + policyID ?? undefined, CONST.REPORT.OWNER_ACCOUNT_ID_FAKE, false, '', @@ -146,16 +105,16 @@ function WorkspaceNewRoomPage(props) { if (policyID) { return; } - setPolicyID(props.activePolicyID); - }, [props.activePolicyID, policyID]); + setPolicyID(activePolicyID); + }, [activePolicyID, policyID]); useEffect(() => { - if (!(((wasLoading && !props.formState.isLoading) || (isOffline && props.formState.isLoading)) && _.isEmpty(props.formState.errorFields))) { + if (!(((wasLoading && !formState?.isLoading) || (isOffline && formState?.isLoading)) && isEmptyObject(formState?.errorFields))) { return; } Navigation.dismissModal(newRoomReportID); // eslint-disable-next-line react-hooks/exhaustive-deps -- we just want this to update on changing the form State - }, [props.formState]); + }, [formState]); useEffect(() => { if (isPolicyAdmin) { @@ -170,8 +129,8 @@ function WorkspaceNewRoomPage(props) { * @returns {Boolean} */ const validate = useCallback( - (values) => { - const errors = {}; + (values: FormValues) => { + const errors: Record = {}; if (!values.roomName || values.roomName === CONST.POLICY.ROOM_PREFIX) { // We error if the user doesn't enter a room name or left blank @@ -182,7 +141,7 @@ function WorkspaceNewRoomPage(props) { } else if (ValidationUtils.isReservedRoomName(values.roomName)) { // Certain names are reserved for default rooms and should not be used for policy rooms. ErrorUtils.addErrorMessage(errors, 'roomName', ['newRoomPage.roomNameReservedError', {reservedName: values.roomName}]); - } else if (ValidationUtils.isExistingRoomName(values.roomName, props.reports, values.policyID)) { + } else if (ValidationUtils.isExistingRoomName(values.roomName, reports, values.policyID ?? '')) { // Certain names are reserved for default rooms and should not be used for policy rooms. ErrorUtils.addErrorMessage(errors, 'roomName', 'newRoomPage.roomAlreadyExistsError'); } @@ -193,22 +152,22 @@ function WorkspaceNewRoomPage(props) { return errors; }, - [props.reports], + [reports], ); const workspaceOptions = useMemo( () => - _.map(PolicyUtils.getActivePolicies(props.policies), (policy) => ({ + PolicyUtils.getActivePolicies(policies)?.map((policy) => ({ label: policy.name, key: policy.id, value: policy.id, - })), - [props.policies], + })) ?? [], + [policies], ); const writeCapabilityOptions = useMemo( () => - _.map(CONST.REPORT.WRITE_CAPABILITIES, (value) => ({ + Object.values(CONST.REPORT.WRITE_CAPABILITIES).map((value) => ({ value, label: translate(`writeCapabilityPage.writeCapability.${value}`), })), @@ -217,14 +176,13 @@ function WorkspaceNewRoomPage(props) { const visibilityOptions = useMemo( () => - _.map( - _.filter(_.values(CONST.REPORT.VISIBILITY), (visibilityOption) => visibilityOption !== CONST.REPORT.VISIBILITY.PUBLIC_ANNOUNCE), - (visibilityOption) => ({ + Object.values(CONST.REPORT.VISIBILITY) + .filter((visibilityOption) => visibilityOption !== CONST.REPORT.VISIBILITY.PUBLIC_ANNOUNCE) + .map((visibilityOption) => ({ label: translate(`newRoomPage.visibilityOptions.${visibilityOption}`), value: visibilityOption, description: translate(`newRoomPage.${visibilityOption}Description`), - }), - ), + })), [translate], ); @@ -270,6 +228,7 @@ function WorkspaceNewRoomPage(props) { // This is because when wrapping whole screen the screen was freezing when changing Tabs. keyboardVerticalOffset={variables.contentHeaderHeight + variables.tabSelectorButtonHeight + variables.tabSelectorButtonPadding + insets.top} > + {/** @ts-expect-error TODO: Remove this once FormProvider (https://github.com/Expensify/App/issues/31972) is migrated to TypeScript. */} (account && account.activePolicyID) || null, - initialValue: null, - }, - }), -)(WorkspaceNewRoomPage); +export default withOnyx({ + policies: { + key: ONYXKEYS.COLLECTION.POLICY, + }, + reports: { + key: ONYXKEYS.COLLECTION.REPORT, + }, + formState: { + key: ONYXKEYS.FORMS.NEW_ROOM_FORM, + }, + session: { + key: ONYXKEYS.SESSION, + }, + activePolicyID: { + key: ONYXKEYS.ACCOUNT, + selector: (account) => account?.activePolicyID ?? null, + initialValue: null, + }, +})(WorkspaceNewRoomPage); From d1b5b355b1986240abe3e76c60977324efc24a1a Mon Sep 17 00:00:00 2001 From: Antony Kithinzi Date: Thu, 18 Jan 2024 23:55:12 +0100 Subject: [PATCH 019/402] fix: added onBackButtonPress prop --- src/pages/ReportParticipantsPage.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/pages/ReportParticipantsPage.js b/src/pages/ReportParticipantsPage.js index 9e480d2f0516..80253c1f1793 100755 --- a/src/pages/ReportParticipantsPage.js +++ b/src/pages/ReportParticipantsPage.js @@ -99,6 +99,7 @@ function ReportParticipantsPage(props) { {({safeAreaPaddingBottomStyle}) => ( Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(props.report.reportID))} title={props.translate( ReportUtils.isChatRoom(props.report) || ReportUtils.isPolicyExpenseChat(props.report) || @@ -121,12 +122,7 @@ function ReportParticipantsPage(props) { }, ]} onSelectRow={(option) => { - Navigation.navigate( - ROUTES.PROFILE.getRoute( - option.accountID, - ROUTES.REPORT_PARTICIPANTS.getRoute(props.report.reportID) - ) - ); + Navigation.navigate(ROUTES.PROFILE.getRoute(option.accountID, ROUTES.REPORT_PARTICIPANTS.getRoute(props.report.reportID))); }} hideSectionHeaders showTitleTooltip From d9fb612f3a622ce1e205c915e05fec790ec62d17 Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Fri, 19 Jan 2024 17:17:18 +0000 Subject: [PATCH 020/402] refactor(typescript): apply pull request feedback --- src/pages/workspace/WorkspaceNewRoomPage.tsx | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/pages/workspace/WorkspaceNewRoomPage.tsx b/src/pages/workspace/WorkspaceNewRoomPage.tsx index 318c3a4d744b..27063d01fe33 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.tsx +++ b/src/pages/workspace/WorkspaceNewRoomPage.tsx @@ -32,6 +32,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Account, Form, Policy, Report as ReportType, Session} from '@src/types/onyx'; +import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; type FormValues = { @@ -43,10 +44,19 @@ type FormValues = { }; type WorkspaceNewRoomPageOnyxProps = { + /** The list of policies the user has access to. */ policies: OnyxCollection; + + /** All reports shared with the user */ reports: OnyxCollection; + + /** Form state for NEW_ROOM_FORM */ formState: OnyxEntry; + + /** Session details for the user */ session: OnyxEntry; + + /** policyID for main workspace */ activePolicyID: OnyxEntry['activePolicyID']>; }; @@ -76,7 +86,7 @@ function WorkspaceNewRoomPage({policies, reports, formState, session, activePoli * @param values - form input values passed by the Form component */ const submit = (values: FormValues) => { - const participants = session ? [session.accountID ?? -1] : []; + const participants = session?.accountID ? [session.accountID] : []; const parsedWelcomeMessage = ReportUtils.getParsedComment(values.welcomeMessage); const policyReport = ReportUtils.buildOptimisticChatReport( participants, @@ -125,12 +135,12 @@ function WorkspaceNewRoomPage({policies, reports, formState, session, activePoli }, [isPolicyAdmin]); /** - * @param {Object} values - form input values passed by the Form component - * @returns {Boolean} + * @param values - form input values passed by the Form component + * @returns an object containing validation errors, if any were found during validation */ const validate = useCallback( - (values: FormValues) => { - const errors: Record = {}; + (values: FormValues): OnyxCommon.Errors => { + const errors: OnyxCommon.Errors = {}; if (!values.roomName || values.roomName === CONST.POLICY.ROOM_PREFIX) { // We error if the user doesn't enter a room name or left blank From be5367fc5e9078c60afeefa7477889ada34a6838 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Mon, 22 Jan 2024 20:13:45 +0530 Subject: [PATCH 021/402] ts fixes --- src/libs/TransactionUtils.ts | 6 +++--- tests/ui/UnreadIndicatorsTest.js | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 2e8207db860a..813bb128cb84 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -9,7 +9,7 @@ import type {PolicyTaxRates} from '@src/types/onyx/PolicyTaxRates'; import type PolicyTaxRate from '@src/types/onyx/PolicyTaxRates'; import type {Comment, PendingFieldsCollection, Receipt, Waypoint, WaypointCollection} from '@src/types/onyx/Transaction'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; -import {isNotEmptyObject} from '@src/types/utils/EmptyObject'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import {isCorporateCard, isExpensifyCard} from './CardUtils'; import DateUtils from './DateUtils'; import * as NumberUtils from './NumberUtils'; @@ -100,7 +100,7 @@ function buildOptimisticTransaction( category = '', tag = '', billable = false, - pendingFields: PendingFieldsCollection | null = null, + pendingFields: PendingFieldsCollection | undefined = undefined, ): Transaction { // transactionIDs are random, positive, 64-bit numeric strings. // Because JS can only handle 53-bit numbers, transactionIDs are strings in the front-end (just like reportActionID) @@ -115,7 +115,7 @@ function buildOptimisticTransaction( } return { - ...(isNotEmptyObject(pendingFields) ? {pendingFields} : {}), + ...(!isEmptyObject(pendingFields) ? {pendingFields} : {}), transactionID, amount, currency, diff --git a/tests/ui/UnreadIndicatorsTest.js b/tests/ui/UnreadIndicatorsTest.js index e4d4d877f66b..88576f3dc89b 100644 --- a/tests/ui/UnreadIndicatorsTest.js +++ b/tests/ui/UnreadIndicatorsTest.js @@ -24,12 +24,16 @@ import appSetup from '../../src/setup'; import * as TestHelper from '../utils/TestHelper'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct'; +import PendingMapView from '../../src/components/MapView/PendingMapView'; // We need a large timeout here as we are lazy loading React Navigation screens and this test is running against the entire mounted App jest.setTimeout(30000); jest.mock('../../src/libs/Notification/LocalNotification'); jest.mock('../../src/components/Icon/Expensicons'); +jest.mock('../../src/components/ConfirmedRoute.tsx', () => ( + +)); // Needed for: https://stackoverflow.com/questions/76903168/mocking-libraries-in-jest jest.mock('react-native/Libraries/LogBox/LogBox', () => ({ From e255ec88eb5f77c8e5b0a77a05ec4693afd66199 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Mon, 22 Jan 2024 20:25:05 +0530 Subject: [PATCH 022/402] test fix --- tests/ui/UnreadIndicatorsTest.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/ui/UnreadIndicatorsTest.js b/tests/ui/UnreadIndicatorsTest.js index 88576f3dc89b..01a3d735104b 100644 --- a/tests/ui/UnreadIndicatorsTest.js +++ b/tests/ui/UnreadIndicatorsTest.js @@ -24,16 +24,13 @@ import appSetup from '../../src/setup'; import * as TestHelper from '../utils/TestHelper'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct'; -import PendingMapView from '../../src/components/MapView/PendingMapView'; // We need a large timeout here as we are lazy loading React Navigation screens and this test is running against the entire mounted App jest.setTimeout(30000); jest.mock('../../src/libs/Notification/LocalNotification'); jest.mock('../../src/components/Icon/Expensicons'); -jest.mock('../../src/components/ConfirmedRoute.tsx', () => ( - -)); +jest.mock('../../src/components/ConfirmedRoute.tsx'); // Needed for: https://stackoverflow.com/questions/76903168/mocking-libraries-in-jest jest.mock('react-native/Libraries/LogBox/LogBox', () => ({ From 17cb86a947002e25768b4cd2db2eac268cb4a569 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Mon, 22 Jan 2024 20:29:58 +0530 Subject: [PATCH 023/402] test fix attempt 2 --- tests/ui/UnreadIndicatorsTest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/UnreadIndicatorsTest.js b/tests/ui/UnreadIndicatorsTest.js index 01a3d735104b..2241711497ff 100644 --- a/tests/ui/UnreadIndicatorsTest.js +++ b/tests/ui/UnreadIndicatorsTest.js @@ -30,7 +30,7 @@ jest.setTimeout(30000); jest.mock('../../src/libs/Notification/LocalNotification'); jest.mock('../../src/components/Icon/Expensicons'); -jest.mock('../../src/components/ConfirmedRoute.tsx'); +jest.mock('../../src/components/ConfirmedRoute.tsx', (props) => props.children); // Needed for: https://stackoverflow.com/questions/76903168/mocking-libraries-in-jest jest.mock('react-native/Libraries/LogBox/LogBox', () => ({ From c7165ddb967bf655bfb8843d50d051bbd5b72e53 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Mon, 22 Jan 2024 20:45:19 +0530 Subject: [PATCH 024/402] test fix attempt 3 --- tests/ui/UnreadIndicatorsTest.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/ui/UnreadIndicatorsTest.js b/tests/ui/UnreadIndicatorsTest.js index 2241711497ff..363c899d4328 100644 --- a/tests/ui/UnreadIndicatorsTest.js +++ b/tests/ui/UnreadIndicatorsTest.js @@ -30,7 +30,10 @@ jest.setTimeout(30000); jest.mock('../../src/libs/Notification/LocalNotification'); jest.mock('../../src/components/Icon/Expensicons'); -jest.mock('../../src/components/ConfirmedRoute.tsx', (props) => props.children); +jest.doMock('../../src/components/ConfirmedRoute.tsx', () => { + const Comp = (props) => props.children; + return Comp; +}); // Needed for: https://stackoverflow.com/questions/76903168/mocking-libraries-in-jest jest.mock('react-native/Libraries/LogBox/LogBox', () => ({ From b0068d381f1e212f131fe04a7ca3107a5bf2b274 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Mon, 22 Jan 2024 20:50:55 +0530 Subject: [PATCH 025/402] perf-test fix --- tests/perf-test/ReportActionsList.perf-test.js | 5 +++++ tests/perf-test/ReportScreen.perf-test.js | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/tests/perf-test/ReportActionsList.perf-test.js b/tests/perf-test/ReportActionsList.perf-test.js index 8e3312cfa4c7..515447a7a2ed 100644 --- a/tests/perf-test/ReportActionsList.perf-test.js +++ b/tests/perf-test/ReportActionsList.perf-test.js @@ -44,6 +44,11 @@ jest.mock('@react-navigation/native', () => { }; }); +jest.doMock('../../src/components/ConfirmedRoute.tsx', () => { + const Comp = (props) => props.children; + return Comp; +}); + beforeAll(() => Onyx.init({ keys: ONYXKEYS, diff --git a/tests/perf-test/ReportScreen.perf-test.js b/tests/perf-test/ReportScreen.perf-test.js index d58f71fa7ab4..97ea04ff55fb 100644 --- a/tests/perf-test/ReportScreen.perf-test.js +++ b/tests/perf-test/ReportScreen.perf-test.js @@ -29,6 +29,11 @@ jest.mock('react-native-reanimated', () => ({ useAnimatedRef: jest.fn, })); +jest.doMock('../../src/components/ConfirmedRoute.tsx', () => { + const Comp = (props) => props.children; + return Comp; +}); + jest.mock('../../src/components/withNavigationFocus', () => (Component) => { function WithNavigationFocus(props) { return ( From 365d37967684c85c0c059744637748346c4f82a6 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Mon, 22 Jan 2024 23:06:12 +0530 Subject: [PATCH 026/402] perf-test fix attempt 2 --- src/components/__mocks__/ConfirmedRoute.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/components/__mocks__/ConfirmedRoute.tsx diff --git a/src/components/__mocks__/ConfirmedRoute.tsx b/src/components/__mocks__/ConfirmedRoute.tsx new file mode 100644 index 000000000000..a759a2c1e193 --- /dev/null +++ b/src/components/__mocks__/ConfirmedRoute.tsx @@ -0,0 +1,8 @@ +import {View} from "react-native"; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any +function ConfirmedRoute(props: any){ + return +} + +export default ConfirmedRoute; \ No newline at end of file From fb5a70ef1a038cffa40e8c001c020b489f04c5e3 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Mon, 22 Jan 2024 23:30:26 +0530 Subject: [PATCH 027/402] perf-test fix attempt 3 --- jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index de7ed4b1f974..b5335f07482f 100644 --- a/jest.config.js +++ b/jest.config.js @@ -8,7 +8,7 @@ module.exports = { `/?(*.)+(spec|test).${testFileExtension}`, ], transform: { - '^.+\\.jsx?$': 'babel-jest', + '^.+\\.[jt]sx?$': 'babel-jest', '^.+\\.svg?$': 'jest-transformer-svg', }, transformIgnorePatterns: ['/node_modules/(?!react-native)/'], From adb33f65224f4c82df7a375fdb0628e71a768010 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Tue, 23 Jan 2024 07:25:37 +0530 Subject: [PATCH 028/402] perf-test fix attempt 4 --- tests/perf-test/ReportActionsList.perf-test.js | 5 +---- tests/perf-test/ReportScreen.perf-test.js | 5 +---- tests/ui/UnreadIndicatorsTest.js | 5 +---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/tests/perf-test/ReportActionsList.perf-test.js b/tests/perf-test/ReportActionsList.perf-test.js index 515447a7a2ed..c81c4aa51df8 100644 --- a/tests/perf-test/ReportActionsList.perf-test.js +++ b/tests/perf-test/ReportActionsList.perf-test.js @@ -44,10 +44,7 @@ jest.mock('@react-navigation/native', () => { }; }); -jest.doMock('../../src/components/ConfirmedRoute.tsx', () => { - const Comp = (props) => props.children; - return Comp; -}); +jest.mock('../../src/components/ConfirmedRoute.tsx'); beforeAll(() => Onyx.init({ diff --git a/tests/perf-test/ReportScreen.perf-test.js b/tests/perf-test/ReportScreen.perf-test.js index 97ea04ff55fb..faa72fd3a367 100644 --- a/tests/perf-test/ReportScreen.perf-test.js +++ b/tests/perf-test/ReportScreen.perf-test.js @@ -29,10 +29,7 @@ jest.mock('react-native-reanimated', () => ({ useAnimatedRef: jest.fn, })); -jest.doMock('../../src/components/ConfirmedRoute.tsx', () => { - const Comp = (props) => props.children; - return Comp; -}); +jest.mock('../../src/components/ConfirmedRoute.tsx'); jest.mock('../../src/components/withNavigationFocus', () => (Component) => { function WithNavigationFocus(props) { diff --git a/tests/ui/UnreadIndicatorsTest.js b/tests/ui/UnreadIndicatorsTest.js index 363c899d4328..01a3d735104b 100644 --- a/tests/ui/UnreadIndicatorsTest.js +++ b/tests/ui/UnreadIndicatorsTest.js @@ -30,10 +30,7 @@ jest.setTimeout(30000); jest.mock('../../src/libs/Notification/LocalNotification'); jest.mock('../../src/components/Icon/Expensicons'); -jest.doMock('../../src/components/ConfirmedRoute.tsx', () => { - const Comp = (props) => props.children; - return Comp; -}); +jest.mock('../../src/components/ConfirmedRoute.tsx'); // Needed for: https://stackoverflow.com/questions/76903168/mocking-libraries-in-jest jest.mock('react-native/Libraries/LogBox/LogBox', () => ({ From 94c115adec14ae658d098d2d4e0a794e7da411fd Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Tue, 23 Jan 2024 08:16:55 +0530 Subject: [PATCH 029/402] fix lint --- src/components/__mocks__/ConfirmedRoute.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/__mocks__/ConfirmedRoute.tsx b/src/components/__mocks__/ConfirmedRoute.tsx index a759a2c1e193..3c78e764ebea 100644 --- a/src/components/__mocks__/ConfirmedRoute.tsx +++ b/src/components/__mocks__/ConfirmedRoute.tsx @@ -1,8 +1,8 @@ -import {View} from "react-native"; +import {View} from 'react-native'; // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any -function ConfirmedRoute(props: any){ - return +function ConfirmedRoute(props: any) { + return ; } -export default ConfirmedRoute; \ No newline at end of file +export default ConfirmedRoute; From c1ae7b2d926a9b0eeb7a979316613f61bed16b93 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Wed, 24 Jan 2024 08:32:01 +0100 Subject: [PATCH 030/402] start migrating WorkspaceMembersPage to TypeScript --- ...embersPage.js => WorkspaceMembersPage.tsx} | 223 ++++++++++-------- 1 file changed, 127 insertions(+), 96 deletions(-) rename src/pages/workspace/{WorkspaceMembersPage.js => WorkspaceMembersPage.tsx} (67%) diff --git a/src/pages/workspace/WorkspaceMembersPage.js b/src/pages/workspace/WorkspaceMembersPage.tsx similarity index 67% rename from src/pages/workspace/WorkspaceMembersPage.js rename to src/pages/workspace/WorkspaceMembersPage.tsx index 92bc5ecc8e9c..7d29d6e72978 100644 --- a/src/pages/workspace/WorkspaceMembersPage.js +++ b/src/pages/workspace/WorkspaceMembersPage.tsx @@ -1,8 +1,11 @@ import {useIsFocused} from '@react-navigation/native'; +import type {StackScreenProps} from '@react-navigation/stack'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import type {TextInput} from 'react-native'; import {InteractionManager, View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; @@ -10,20 +13,20 @@ import Button from '@components/Button'; import ConfirmModal from '@components/ConfirmModal'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import MessagesRow from '@components/MessagesRow'; -import networkPropTypes from '@components/networkPropTypes'; -import {withNetwork} from '@components/OnyxProvider'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import Text from '@components/Text'; +import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; -import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; +import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; import compose from '@libs/compose'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; +import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; @@ -33,8 +36,12 @@ import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; +import type {PersonalDetailsList, PolicyMembers, Session} from '@src/types/onyx'; +import type {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; import SearchInputManager from './SearchInputManager'; import {policyDefaultProps, policyPropTypes} from './withPolicy'; +import type {WithPolicyAndFullscreenLoadingProps} from './withPolicyAndFullscreenLoading'; import withPolicyAndFullscreenLoading from './withPolicyAndFullscreenLoading'; const propTypes = { @@ -58,10 +65,32 @@ const propTypes = { isLoadingReportData: PropTypes.bool, ...policyPropTypes, - ...withLocalizePropTypes, - ...windowDimensionsPropTypes, ...withCurrentUserPersonalDetailsPropTypes, - network: networkPropTypes.isRequired, +}; + +type WorkspaceMembersPageOnyxProps = { + personalDetails: OnyxEntry; + session: OnyxEntry; + isLoadingReportData: OnyxEntry; +}; + +type WorkspaceMembersPageProps = Omit & + WithCurrentUserPersonalDetailsProps & + WorkspaceMembersPageOnyxProps & + StackScreenProps; + +type MemberOption = { + keyForList: string; + accountID: number; + isSelected: boolean; + isDisabled: boolean; + text: string; + alternateText: string; + rightElement: React.ReactNode | null; + icons: Icon[]; + errors?: Errors; + pendingAction?: PendingAction; + invitedSecondaryLogin?: string; }; const defaultProps = { @@ -74,18 +103,20 @@ const defaultProps = { ...withCurrentUserPersonalDetailsDefaultProps, }; -function WorkspaceMembersPage(props) { +function WorkspaceMembersPage({policyMembers, personalDetails, route, policy, session, currentUserPersonalDetails, isLoadingReportData}: WorkspaceMembersPageProps) { const styles = useThemeStyles(); const [selectedEmployees, setSelectedEmployees] = useState([]); const [removeMembersConfirmModalVisible, setRemoveMembersConfirmModalVisible] = useState(false); const [errors, setErrors] = useState({}); const [searchValue, setSearchValue] = useState(''); - const prevIsOffline = usePrevious(props.network.isOffline); - const accountIDs = useMemo(() => _.map(_.keys(props.policyMembers), (accountID) => Number(accountID)), [props.policyMembers]); + const {isOffline} = useNetwork(); + const prevIsOffline = usePrevious(isOffline); + const accountIDs = useMemo(() => Object.keys(policyMembers ?? {}).map((accountID) => Number(accountID)), [policyMembers]); const prevAccountIDs = usePrevious(accountIDs); - const textInputRef = useRef(null); - const isOfflineAndNoMemberDataAvailable = _.isEmpty(props.policyMembers) && props.network.isOffline; - const prevPersonalDetails = usePrevious(props.personalDetails); + const textInputRef = useRef(null); + const isOfflineAndNoMemberDataAvailable = _.isEmpty(policyMembers) && isOffline; + const prevPersonalDetails = usePrevious(personalDetails); + const {translate, formatPhoneNumber, preferredLocale} = useLocalize(); const isFocusedScreen = useIsFocused(); @@ -93,51 +124,49 @@ function WorkspaceMembersPage(props) { setSearchValue(SearchInputManager.searchInput); }, [isFocusedScreen]); - useEffect(() => () => (SearchInputManager.searchInput = ''), []); + useEffect(() => { + SearchInputManager.searchInput = ''; + }, []); /** * Get filtered personalDetails list with current policyMembers - * @param {Object} policyMembers - * @param {Object} personalDetails - * @returns {Object} + * @param policyMembers + * @param personalDetails + * @returns */ - const filterPersonalDetails = (policyMembers, personalDetails) => - _.reduce( - _.keys(policyMembers), - (result, key) => { - if (personalDetails[key]) { - return { - ...result, - [key]: personalDetails[key], - }; - } - return result; - }, - {}, - ); + const filterPersonalDetails = (members: OnyxEntry, details: OnyxEntry) => + Object.keys(members ?? {}).reduce((result, key) => { + if (details?.[key]) { + return { + ...result, + [key]: details[key], + }; + } + return result; + }, {}); /** * Get members for the current workspace */ const getWorkspaceMembers = useCallback(() => { - Policy.openWorkspaceMembersPage(props.route.params.policyID, _.keys(PolicyUtils.getMemberAccountIDsForWorkspace(props.policyMembers, props.personalDetails))); - }, [props.route.params.policyID, props.policyMembers, props.personalDetails]); + Policy.openWorkspaceMembersPage(route.params.policyID, Object.keys(PolicyUtils.getMemberAccountIDsForWorkspace(policyMembers, personalDetails))); + }, [route.params.policyID, policyMembers, personalDetails]); /** * Check if the current selection includes members that cannot be removed */ const validateSelection = useCallback(() => { const newErrors = {}; - const ownerAccountID = _.first(PersonalDetailsUtils.getAccountIDsByLogins(props.policy.owner ? [props.policy.owner] : [])); + const ownerAccountID = PersonalDetailsUtils.getAccountIDsByLogins(policy?.owner ? [policy.owner] : [])[0]; _.each(selectedEmployees, (member) => { - if (member !== ownerAccountID && member !== props.session.accountID) { + if (member !== ownerAccountID && member !== session.accountID) { return; } - newErrors[member] = props.translate('workspace.people.error.cannotRemove'); + newErrors[member] = translate('workspace.people.error.cannotRemove'); }); setErrors(newErrors); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedEmployees, props.policy.owner, props.session.accountID]); + }, [selectedEmployees, policy?.owner, session?.accountID]); useEffect(() => { getWorkspaceMembers(); @@ -146,7 +175,7 @@ function WorkspaceMembersPage(props) { useEffect(() => { validateSelection(); - }, [props.preferredLocale, validateSelection]); + }, [preferredLocale, validateSelection]); useEffect(() => { if (removeMembersConfirmModalVisible && !_.isEqual(accountIDs, prevAccountIDs)) { @@ -154,32 +183,32 @@ function WorkspaceMembersPage(props) { } setSelectedEmployees((prevSelected) => { // Filter all personal details in order to use the elements needed for the current workspace - const currentPersonalDetails = filterPersonalDetails(props.policyMembers, props.personalDetails); + const currentPersonalDetails = filterPersonalDetails(policyMembers, personalDetails); // We need to filter the previous selected employees by the new personal details, since unknown/new user id's change when transitioning from offline to online const prevSelectedElements = _.map(prevSelected, (id) => { const prevItem = lodashGet(prevPersonalDetails, id); const res = _.find(_.values(currentPersonalDetails), (item) => lodashGet(prevItem, 'login') === lodashGet(item, 'login')); return lodashGet(res, 'accountID', id); }); - return _.intersection(prevSelectedElements, _.values(PolicyUtils.getMemberAccountIDsForWorkspace(props.policyMembers, props.personalDetails))); + return _.intersection(prevSelectedElements, _.values(PolicyUtils.getMemberAccountIDsForWorkspace(policyMembers, personalDetails))); }); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.policyMembers]); + }, [policyMembers]); useEffect(() => { - const isReconnecting = prevIsOffline && !props.network.isOffline; + const isReconnecting = prevIsOffline && !isOffline; if (!isReconnecting) { return; } getWorkspaceMembers(); - }, [props.network.isOffline, prevIsOffline, getWorkspaceMembers]); + }, [isOffline, prevIsOffline, getWorkspaceMembers]); /** * Open the modal to invite a user */ const inviteUser = () => { setSearchValue(''); - Navigation.navigate(ROUTES.WORKSPACE_INVITE.getRoute(props.route.params.policyID)); + Navigation.navigate(ROUTES.WORKSPACE_INVITE.getRoute(route.params.policyID)); }; /** @@ -191,9 +220,9 @@ function WorkspaceMembersPage(props) { } // Remove the admin from the list - const accountIDsToRemove = _.without(selectedEmployees, props.session.accountID); + const accountIDsToRemove = _.without(selectedEmployees, session.accountID); - Policy.removeMembers(accountIDsToRemove, props.route.params.policyID); + Policy.removeMembers(accountIDsToRemove, route.params.policyID); setSelectedEmployees([]); setRemoveMembersConfirmModalVisible(false); }; @@ -210,7 +239,7 @@ function WorkspaceMembersPage(props) { /** * Add or remove all users passed from the selectedEmployees list - * @param {Object} memberList + * @param memberList */ const toggleAllUsers = (memberList) => { const enabledAccounts = _.filter(memberList, (member) => !member.isDisabled); @@ -283,37 +312,39 @@ function WorkspaceMembersPage(props) { const dismissError = useCallback( (item) => { if (item.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { - Policy.clearDeleteMemberError(props.route.params.policyID, item.accountID); + Policy.clearDeleteMemberError(route.params.policyID, item.accountID); } else { - Policy.clearAddMemberError(props.route.params.policyID, item.accountID); + Policy.clearAddMemberError(route.params.policyID, item.accountID); } }, - [props.route.params.policyID], + [route.params.policyID], ); /** * Check if the policy member is deleted from the workspace * - * @param {Object} policyMember - * @returns {Boolean} + * @param policyMember + * @returns */ - const isDeletedPolicyMember = (policyMember) => !props.network.isOffline && policyMember.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && _.isEmpty(policyMember.errors); - const policyOwner = lodashGet(props.policy, 'owner'); - const currentUserLogin = lodashGet(props.currentUserPersonalDetails, 'login'); - const policyID = lodashGet(props.route, 'params.policyID'); - const policyName = lodashGet(props.policy, 'name'); - const invitedPrimaryToSecondaryLogins = _.invert(props.policy.primaryLoginsInvited); + const isDeletedPolicyMember = (policyMember) => !isOffline && policyMember.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && _.isEmpty(policyMember.errors); + const policyOwner = lodashGet(policy, 'owner'); + const currentUserLogin = lodashGet(currentUserPersonalDetails, 'login'); + const policyID = lodashGet(route, 'params.policyID'); + const policyName = lodashGet(policy, 'name'); + const invitedPrimaryToSecondaryLogins = _.invert(policy?.primaryLoginsInvited); const getMemberOptions = () => { - let result = []; + let result: MemberOption[] = []; - _.each(props.policyMembers, (policyMember, accountIDKey) => { + console.log('*** POLICY MEMBERS ***', policyMembers); + + Object.entries(policyMembers ?? {}).forEach(([accountIDKey, policyMember]) => { const accountID = Number(accountIDKey); if (isDeletedPolicyMember(policyMember)) { return; } - const details = props.personalDetails[accountID]; + const details = personalDetails?.[accountID]; if (!details) { Log.hmmm(`[WorkspaceMembersPage] no personal details found for policy member with accountID: ${accountID}`); @@ -347,34 +378,34 @@ function WorkspaceMembersPage(props) { // If this policy is owned by Expensify then show all support (expensify.com or team.expensify.com) emails // We don't want to show guides as policy members unless the user is a guide. Some customers get confused when they // see random people added to their policy, but guides having access to the policies help set them up. - if (PolicyUtils.isExpensifyTeam(details.login || details.displayName)) { + if (PolicyUtils.isExpensifyTeam(details?.login ?? details?.displayName ?? '')) { if (policyOwner && currentUserLogin && !PolicyUtils.isExpensifyTeam(policyOwner) && !PolicyUtils.isExpensifyTeam(currentUserLogin)) { return; } } - const isAdmin = props.session.email === details.login || policyMember.role === CONST.POLICY.ROLE.ADMIN; + const isAdmin = session?.email === details.login || policyMember.role === CONST.POLICY.ROLE.ADMIN; result.push({ keyForList: accountIDKey, accountID, - isSelected: _.contains(selectedEmployees, accountID), + isSelected: selectedEmployees.includes(accountID), isDisabled: - accountID === props.session.accountID || - details.login === props.policy.owner || + accountID === session?.accountID || + details.login === policy?.owner || policyMember.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || - !_.isEmpty(policyMember.errors), - text: props.formatPhoneNumber(PersonalDetailsUtils.getDisplayNameOrDefault(details)), - alternateText: props.formatPhoneNumber(details.login), + Object.keys(policyMember.errors ?? {}).length > 0, + text: formatPhoneNumber(PersonalDetailsUtils.getDisplayNameOrDefault(details)), + alternateText: formatPhoneNumber(details?.login ?? ''), rightElement: isAdmin ? ( - {props.translate('common.admin')} + {translate('common.admin')} ) : null, icons: [ { source: UserUtils.getAvatar(details.avatar, accountID), - name: props.formatPhoneNumber(details.login), + name: formatPhoneNumber(details?.login ?? ''), type: CONST.ICON_TYPE_AVATAR, id: accountID, }, @@ -383,11 +414,11 @@ function WorkspaceMembersPage(props) { pendingAction: policyMember.pendingAction, // Note which secondary login was used to invite this primary login - invitedSecondaryLogin: invitedPrimaryToSecondaryLogins[details.login] || '', + invitedSecondaryLogin: details?.login ? invitedPrimaryToSecondaryLogins[details.login] ?? '' : '', }); }); - result = _.sortBy(result, (value) => value.text.toLowerCase()); + result = result.sort((a, b) => a.text.localeCompare(b.text.toLowerCase())); return result; }; @@ -395,9 +426,9 @@ function WorkspaceMembersPage(props) { const getHeaderMessage = () => { if (isOfflineAndNoMemberDataAvailable) { - return props.translate('workspace.common.mustBeOnlineToViewMembers'); + return translate('workspace.common.mustBeOnlineToViewMembers'); } - return searchValue.trim() && !data.length ? props.translate('workspace.common.memberNotFound') : ''; + return searchValue.trim() && !data.length ? translate('workspace.common.memberNotFound') : ''; }; const getHeaderContent = () => { @@ -407,13 +438,12 @@ function WorkspaceMembersPage(props) { return ( Policy.dismissAddedWithPrimaryLoginMessages(policyID)} /> ); }; - return ( Navigation.goBack(ROUTES.SETTINGS_WORKSPACES)} > { setSearchValue(''); @@ -437,28 +467,28 @@ function WorkspaceMembersPage(props) { /> setRemoveMembersConfirmModalVisible(false)} - prompt={props.translate('workspace.people.removeMembersPrompt')} - confirmText={props.translate('common.remove')} - cancelText={props.translate('common.cancel')} - onModalHide={() => + prompt={translate('workspace.people.removeMembersPrompt')} + confirmText={translate('common.remove')} + cancelText={translate('common.cancel')} + onModalHide={() => { InteractionManager.runAfterInteractions(() => { if (!textInputRef.current) { return; } textInputRef.current.focus(); - }) - } + }); + }} />