diff --git a/.github/workflows/testBuild.yml b/.github/workflows/testBuild.yml
index 6ded44d7059f..755ab6dbaa60 100644
--- a/.github/workflows/testBuild.yml
+++ b/.github/workflows/testBuild.yml
@@ -354,6 +354,6 @@ jobs:
IOS: ${{ needs.iOS.result }}
WEB: ${{ needs.web.result }}
ANDROID_LINK: ${{steps.get_android_path.outputs.android_path}}
- DESKTOP_LINK: https://ad-hoc-expensify-cash.s3.amazonaws.com/desktop/${{ env.PULL_REQUEST_NUMBER }}/NewExpensify.dmg
+ DESKTOP_LINK: https://ad-hoc-expensify-cash.s3.amazonaws.com/desktop/${{ env.PULL_REQUEST_NUMBER }}/NewExpensifyAdHoc.dmg
IOS_LINK: ${{steps.get_ios_path.outputs.ios_path}}
WEB_LINK: https://${{ env.PULL_REQUEST_NUMBER }}.pr-testing.expensify.com
diff --git a/.storybook/theme.js b/.storybook/theme.js
index 0867f6a830b5..96631764726f 100644
--- a/.storybook/theme.js
+++ b/.storybook/theme.js
@@ -7,17 +7,17 @@ export default create({
fontBase: 'ExpensifyNeue-Regular',
fontCode: 'monospace',
base: 'dark',
- appBg: colors.greenHighlightBackground,
- colorPrimary: colors.greenDefaultButton,
+ appBg: colors.darkHighlightBackground,
+ colorPrimary: colors.darkDefaultButton,
colorSecondary: colors.green,
- appContentBg: colors.greenAppBackground,
- textColor: colors.white,
- barTextColor: colors.white,
+ appContentBg: colors.darkAppBackground,
+ textColor: colors.darkPrimaryText,
+ barTextColor: colors.darkPrimaryText,
barSelectedColor: colors.green,
- barBg: colors.greenAppBackground,
- appBorderColor: colors.greenBorders,
- inputBg: colors.greenHighlightBackground,
- inputBorder: colors.greenBorders,
+ barBg: colors.darkAppBackground,
+ appBorderColor: colors.darkBorders,
+ inputBg: colors.darkHighlightBackground,
+ inputBorder: colors.darkBorders,
appBorderRadius: 8,
inputBorderRadius: 8,
});
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 62c95d20e3ca..1867a8cf85d2 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -90,8 +90,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1001036901
- versionName "1.3.69-1"
+ versionCode 1001037002
+ versionName "1.3.70-2"
}
flavorDimensions "default"
diff --git a/config/electronBuilder.config.js b/config/electronBuilder.config.js
index da87c93ee367..a5478dbd8f78 100644
--- a/config/electronBuilder.config.js
+++ b/config/electronBuilder.config.js
@@ -21,6 +21,24 @@ const macIcon = {
adhoc: './desktop/icon-adhoc.png',
};
+const appIds = {
+ production: 'com.expensifyreactnative.chat',
+ staging: 'com.expensifyreactnative.dev.chat',
+ adhoc: 'com.expensifyreactnative.adhoc.chat',
+};
+
+const productNames = {
+ production: 'New Expensify',
+ staging: 'New Expensify Dev',
+ adhoc: 'New Expensify AdHoc',
+};
+
+const artifactNames = {
+ production: 'NewExpensify.dmg',
+ staging: 'NewExpensifyDev.dmg',
+ adhoc: 'NewExpensifyAdHoc.dmg',
+};
+
const isCorrectElectronEnv = ['production', 'staging', 'adhoc'].includes(process.env.ELECTRON_ENV);
if (!isCorrectElectronEnv) {
@@ -32,8 +50,8 @@ if (!isCorrectElectronEnv) {
* It can be used to create local builds of the same, by omitting the `--publish` flag
*/
module.exports = {
- appId: 'com.expensifyreactnative.chat',
- productName: 'New Expensify',
+ appId: appIds[process.env.ELECTRON_ENV],
+ productName: productNames[process.env.ELECTRON_ENV],
extraMetadata: {
version,
},
@@ -46,8 +64,8 @@ module.exports = {
type: 'distribution',
},
dmg: {
- title: 'New Expensify',
- artifactName: 'NewExpensify.dmg',
+ title: productNames[process.env.ELECTRON_ENV],
+ artifactName: artifactNames[process.env.ELECTRON_ENV],
internetEnabled: true,
},
publish: [
@@ -65,7 +83,7 @@ module.exports = {
output: 'desktop-build',
},
protocols: {
- name: 'New Expensify',
+ name: productNames[process.env.ELECTRON_ENV],
schemes: ['new-expensify'],
},
};
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index c9ba137b13de..9e4501eddea5 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -19,7 +19,7 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 1.3.69
+ 1.3.70
CFBundleSignature
????
CFBundleURLTypes
@@ -40,7 +40,7 @@
CFBundleVersion
- 1.3.69.1
+ 1.3.70.2
ITSAppUsesNonExemptEncryption
LSApplicationQueriesSchemes
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index e802856c4dd5..fd93684a1da3 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -15,10 +15,10 @@
CFBundlePackageType
BNDL
CFBundleShortVersionString
- 1.3.69
+ 1.3.70
CFBundleSignature
????
CFBundleVersion
- 1.3.69.1
+ 1.3.70.2
diff --git a/package-lock.json b/package-lock.json
index 169ef273b29b..0ba372b22745 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "new.expensify",
- "version": "1.3.69-1",
+ "version": "1.3.70-2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "1.3.69-1",
+ "version": "1.3.70-2",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@@ -65,7 +65,7 @@
"patch-package": "^8.0.0",
"process": "^0.11.10",
"prop-types": "^15.7.2",
- "pusher-js": "7.4.0",
+ "pusher-js": "8.3.0",
"react": "18.2.0",
"react-collapse": "^5.1.0",
"react-content-loader": "^6.1.0",
@@ -39745,10 +39745,10 @@
}
},
"node_modules/pusher-js": {
- "version": "7.4.0",
- "license": "MIT",
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/pusher-js/-/pusher-js-8.3.0.tgz",
+ "integrity": "sha512-6GohP06WlVeomAQQe9qWh1IDzd3+InluWt+ZUOcecVK1SEQkg6a8uYVsvxSJm7cbccfmHhE0jDkmhKIhue8vmA==",
"dependencies": {
- "@types/node": "^14.14.31",
"tweetnacl": "^1.0.3"
}
},
@@ -39760,11 +39760,6 @@
"node": ">=4.2.4"
}
},
- "node_modules/pusher-js/node_modules/@types/node": {
- "version": "14.18.56",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.56.tgz",
- "integrity": "sha512-+k+57NVS9opgrEn5l9c0gvD1r6C+PtyhVE4BTnMMRwiEA8ZO8uFcs6Yy2sXIy0eC95ZurBtRSvhZiHXBysbl6w=="
- },
"node_modules/qrcode": {
"version": "1.5.3",
"license": "MIT",
@@ -75499,17 +75494,11 @@
"integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA=="
},
"pusher-js": {
- "version": "7.4.0",
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/pusher-js/-/pusher-js-8.3.0.tgz",
+ "integrity": "sha512-6GohP06WlVeomAQQe9qWh1IDzd3+InluWt+ZUOcecVK1SEQkg6a8uYVsvxSJm7cbccfmHhE0jDkmhKIhue8vmA==",
"requires": {
- "@types/node": "^14.14.31",
"tweetnacl": "^1.0.3"
- },
- "dependencies": {
- "@types/node": {
- "version": "14.18.56",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.56.tgz",
- "integrity": "sha512-+k+57NVS9opgrEn5l9c0gvD1r6C+PtyhVE4BTnMMRwiEA8ZO8uFcs6Yy2sXIy0eC95ZurBtRSvhZiHXBysbl6w=="
- }
}
},
"pusher-js-mock": {
diff --git a/package.json b/package.json
index 256ef013b0d7..97621503eb6f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "1.3.69-1",
+ "version": "1.3.70-2",
"author": "Expensify, Inc.",
"homepage": "https://new.expensify.com",
"description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
@@ -107,7 +107,7 @@
"patch-package": "^8.0.0",
"process": "^0.11.10",
"prop-types": "^15.7.2",
- "pusher-js": "7.4.0",
+ "pusher-js": "8.3.0",
"react": "18.2.0",
"react-collapse": "^5.1.0",
"react-content-loader": "^6.1.0",
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index 3bbdf4709cfc..1133dcec8e9a 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -71,7 +71,7 @@ export default {
NEW_CHAT: 'new/chat',
NEW_TASK,
REPORT,
- REPORT_WITH_ID: 'r/:reportID/:reportActionID?',
+ REPORT_WITH_ID: 'r/:reportID?/:reportActionID?',
EDIT_REQUEST: 'r/:threadReportID/edit/:field',
getEditRequestRoute: (threadReportID: string, field: ValueOf) => `r/${threadReportID}/edit/${field}`,
EDIT_CURRENCY_REQUEST: 'r/:threadReportID/edit/currency',
@@ -104,6 +104,7 @@ export default {
MONEY_REQUEST_SCAN_TAB: ':iouType/new/:reportID?/scan',
MONEY_REQUEST_DISTANCE_TAB: ':iouType/new/:reportID?/distance',
MONEY_REQUEST_WAYPOINT: ':iouType/new/waypoint/:waypointIndex',
+ MONEY_REQUEST_ADDRESS: ':iouType/new/address/:reportID?',
IOU_SEND_ADD_BANK_ACCOUNT: `${IOU_SEND}/add-bank-account`,
IOU_SEND_ADD_DEBIT_CARD: `${IOU_SEND}/add-debit-card`,
IOU_SEND_ENABLE_PAYMENTS: `${IOU_SEND}/enable-payments`,
@@ -118,6 +119,7 @@ export default {
getMoneyRequestMerchantRoute: (iouType: string, reportID = '') => `${iouType}/new/merchant/${reportID}`,
getMoneyRequestDistanceTabRoute: (iouType: string, reportID = '') => `${iouType}/new/${reportID}/distance`,
getMoneyRequestWaypointRoute: (iouType: string, waypointIndex: number) => `${iouType}/new/waypoint/${waypointIndex}`,
+ getMoneyRequestAddressRoute: (iouType: string, reportID = '') => `${iouType}/new/address/${reportID}`,
getMoneyRequestTagRoute: (iouType: string, reportID = '') => `${iouType}/new/tag/${reportID}`,
SPLIT_BILL_DETAILS: `r/:reportID/split/:reportActionID`,
getSplitBillDetailsRoute: (reportID: string, reportActionID: string) => `r/${reportID}/split/${reportActionID}`,
diff --git a/src/components/AddPlaidBankAccount.js b/src/components/AddPlaidBankAccount.js
index dbe7e46ff6aa..e2843ba7fae8 100644
--- a/src/components/AddPlaidBankAccount.js
+++ b/src/components/AddPlaidBankAccount.js
@@ -1,6 +1,7 @@
import _ from 'underscore';
-import React, {useEffect, useRef, useCallback} from 'react';
+import React, {useEffect, useRef, useCallback, useMemo} from 'react';
import {ActivityIndicator, View} from 'react-native';
+import {useIsFocused} from '@react-navigation/native';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
import lodashGet from 'lodash/get';
@@ -38,6 +39,9 @@ const propTypes = {
/** Fired when the user exits the Plaid flow */
onExitPlaid: PropTypes.func,
+ /** Fired when the screen is blurred */
+ onBlurPlaid: PropTypes.func,
+
/** Fired when the user selects an account */
onSelect: PropTypes.func,
@@ -61,6 +65,7 @@ const defaultProps = {
selectedPlaidAccountID: '',
plaidLinkToken: '',
onExitPlaid: () => {},
+ onBlurPlaid: () => {},
onSelect: () => {},
text: '',
receivedRedirectURI: null,
@@ -75,6 +80,7 @@ function AddPlaidBankAccount({
selectedPlaidAccountID,
plaidLinkToken,
onExitPlaid,
+ onBlurPlaid,
onSelect,
text,
receivedRedirectURI,
@@ -88,6 +94,7 @@ function AddPlaidBankAccount({
const {translate} = useLocalize();
const {isOffline} = useNetwork();
+ const isFocused = useIsFocused();
/**
* @returns {String}
@@ -102,6 +109,11 @@ function AddPlaidBankAccount({
}
};
+ /**
+ * @returns {Array}
+ */
+ const plaidBankAccounts = useMemo(() => lodashGet(plaidData, 'bankAccounts') || [], [plaidData]);
+
/**
* @returns {Boolean}
* I'm using useCallback so the useEffect which uses this function doesn't run on every render.
@@ -151,6 +163,13 @@ function AddPlaidBankAccount({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
+ useEffect(() => {
+ if (isFocused || plaidBankAccounts.length) {
+ return;
+ }
+ onBlurPlaid();
+ }, [isFocused, onBlurPlaid, plaidBankAccounts.length]);
+
useEffect(() => {
// If we are coming back from offline and we haven't authenticated with Plaid yet, we need to re-run our call to kick off Plaid
// previousNetworkState.current also makes sure that this doesn't run on the first render.
@@ -160,7 +179,6 @@ function AddPlaidBankAccount({
previousNetworkState.current = isOffline;
}, [allowDebit, bankAccountID, isAuthenticatedWithPlaid, isOffline]);
- const plaidBankAccounts = lodashGet(plaidData, 'bankAccounts') || [];
const token = getPlaidLinkToken();
const options = _.map(plaidBankAccounts, (account) => ({
value: account.plaidAccountID,
diff --git a/src/components/ConfirmedRoute.js b/src/components/ConfirmedRoute.js
index 6790e8ae4d65..4bcdc4738a3c 100644
--- a/src/components/ConfirmedRoute.js
+++ b/src/components/ConfirmedRoute.js
@@ -16,7 +16,7 @@ import transactionPropTypes from './transactionPropTypes';
import BlockingView from './BlockingViews/BlockingView';
import useNetwork from '../hooks/useNetwork';
import useLocalize from '../hooks/useLocalize';
-import MapView from './MapView';
+import DistanceMapView from './DistanceMapView';
const propTypes = {
/** Transaction that stores the distance request data */
@@ -90,7 +90,7 @@ function ConfirmedRoute({mapboxAccessToken, transaction}) {
return (
<>
{!isOffline && Boolean(mapboxAccessToken.token) ? (
-
diff --git a/src/components/DistanceMapView/distanceMapViewPropTypes.js b/src/components/DistanceMapView/distanceMapViewPropTypes.js
new file mode 100644
index 000000000000..05068cbc9b34
--- /dev/null
+++ b/src/components/DistanceMapView/distanceMapViewPropTypes.js
@@ -0,0 +1,55 @@
+import PropTypes from 'prop-types';
+
+const propTypes = {
+ // Public access token to be used to fetch map data from Mapbox.
+ accessToken: PropTypes.string.isRequired,
+
+ // Style applied to MapView component. Note some of the View Style props are not available on ViewMap
+ style: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]),
+
+ // Link to the style JSON document.
+ styleURL: PropTypes.string,
+
+ // Whether map can tilt in the vertical direction.
+ pitchEnabled: PropTypes.bool,
+
+ // Padding to apply when the map is adjusted to fit waypoints and directions
+ mapPadding: PropTypes.number,
+
+ // Initial coordinate and zoom level
+ initialState: PropTypes.shape({
+ location: PropTypes.arrayOf(PropTypes.number).isRequired,
+ zoom: PropTypes.number.isRequired,
+ }),
+
+ // Locations on which to put markers
+ waypoints: PropTypes.arrayOf(
+ PropTypes.shape({
+ id: PropTypes.string,
+ coordinate: PropTypes.arrayOf(PropTypes.number),
+ markerComponent: PropTypes.oneOfType([PropTypes.element, PropTypes.func]),
+ }),
+ ),
+
+ // List of coordinates which together forms a direction.
+ directionCoordinates: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.number)),
+
+ // Callback to call when the map is idle / ready
+ onMapReady: PropTypes.func,
+
+ // Optional additional styles to be applied to the overlay
+ overlayStyle: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]),
+};
+
+const defaultProps = {
+ styleURL: undefined,
+ pitchEnabled: false,
+ mapPadding: 0,
+ initialState: undefined,
+ waypoints: undefined,
+ directionCoordinates: undefined,
+ onMapReady: () => {},
+ overlayStyle: undefined,
+};
+
+export {propTypes, defaultProps};
diff --git a/src/components/DistanceMapView/index.android.js b/src/components/DistanceMapView/index.android.js
new file mode 100644
index 000000000000..ea72fb4de299
--- /dev/null
+++ b/src/components/DistanceMapView/index.android.js
@@ -0,0 +1,48 @@
+import React, {useState} from 'react';
+import {View} from 'react-native';
+import _ from 'underscore';
+import BlockingView from '../BlockingViews/BlockingView';
+import MapView from '../MapView';
+import styles from '../../styles/styles';
+import useNetwork from '../../hooks/useNetwork';
+import useLocalize from '../../hooks/useLocalize';
+import * as Expensicons from '../Icon/Expensicons';
+import * as StyleUtils from '../../styles/StyleUtils';
+import * as distanceMapViewPropTypes from './distanceMapViewPropTypes';
+
+function DistanceMapView(props) {
+ const [isMapReady, setIsMapReady] = useState(false);
+ const {isOffline} = useNetwork();
+ const {translate} = useLocalize();
+
+ return (
+ <>
+ {
+ if (isMapReady) {
+ return;
+ }
+ setIsMapReady(true);
+ }}
+ />
+ {!isMapReady && (
+
+
+
+ )}
+ >
+ );
+}
+
+DistanceMapView.propTypes = distanceMapViewPropTypes.propTypes;
+DistanceMapView.defaultProps = distanceMapViewPropTypes.defaultProps;
+DistanceMapView.displayName = 'DistanceMapView';
+
+export default DistanceMapView;
diff --git a/src/components/DistanceMapView/index.js b/src/components/DistanceMapView/index.js
new file mode 100644
index 000000000000..24bdf99382d1
--- /dev/null
+++ b/src/components/DistanceMapView/index.js
@@ -0,0 +1,15 @@
+import React from 'react';
+import _ from 'underscore';
+import MapView from '../MapView';
+import * as distanceMapViewPropTypes from './distanceMapViewPropTypes';
+
+function DistanceMapView(props) {
+ // eslint-disable-next-line react/jsx-props-no-spreading
+ return ;
+}
+
+DistanceMapView.propTypes = distanceMapViewPropTypes.propTypes;
+DistanceMapView.defaultProps = distanceMapViewPropTypes.defaultProps;
+DistanceMapView.displayName = 'DistanceMapView';
+
+export default DistanceMapView;
diff --git a/src/components/DistanceRequest.js b/src/components/DistanceRequest.js
index 9de98f365475..bf5a4cb9548b 100644
--- a/src/components/DistanceRequest.js
+++ b/src/components/DistanceRequest.js
@@ -26,9 +26,10 @@ import Navigation from '../libs/Navigation/Navigation';
import * as MapboxToken from '../libs/actions/MapboxToken';
import * as Transaction from '../libs/actions/Transaction';
import * as TransactionUtils from '../libs/TransactionUtils';
+import * as IOUUtils from '../libs/IOUUtils';
import Button from './Button';
-import MapView from './MapView';
+import DistanceMapView from './DistanceMapView';
import LinearGradient from './LinearGradient';
import * as Expensicons from './Icon/Expensicons';
import BlockingView from './BlockingViews/BlockingView';
@@ -38,6 +39,9 @@ import {iouPropTypes} from '../pages/iou/propTypes';
import reportPropTypes from '../pages/reportPropTypes';
import * as IOU from '../libs/actions/IOU';
import * as StyleUtils from '../styles/StyleUtils';
+import ScreenWrapper from './ScreenWrapper';
+import FullPageNotFoundView from './BlockingViews/FullPageNotFoundView';
+import HeaderWithBackButton from './HeaderWithBackButton';
const MAX_WAYPOINTS = 25;
const MAX_WAYPOINTS_TO_DISPLAY = 4;
@@ -63,6 +67,18 @@ const propTypes = {
/** Time when the token will expire in ISO 8601 */
expiration: PropTypes.string,
}),
+
+ /** React Navigation route */
+ route: PropTypes.shape({
+ /** Params from the route */
+ params: PropTypes.shape({
+ /** The type of IOU report, i.e. bill, request, send */
+ iouType: PropTypes.string,
+
+ /** The report ID of the IOU */
+ reportID: PropTypes.string,
+ }),
+ }).isRequired,
};
const defaultProps = {
@@ -75,13 +91,14 @@ const defaultProps = {
},
};
-function DistanceRequest({iou, iouType, report, transaction, mapboxAccessToken}) {
+function DistanceRequest({iou, iouType, report, transaction, mapboxAccessToken, route}) {
const [shouldShowGradient, setShouldShowGradient] = useState(false);
const [scrollContainerHeight, setScrollContainerHeight] = useState(0);
const [scrollContentHeight, setScrollContentHeight] = useState(0);
const {isOffline} = useNetwork();
const {translate} = useLocalize();
+ const isEditing = lodashGet(route, 'path', '').includes('address');
const reportID = lodashGet(report, 'reportID', '');
const waypoints = useMemo(() => lodashGet(transaction, 'comment.waypoints', {}), [transaction]);
const previousWaypoints = usePrevious(waypoints);
@@ -170,7 +187,20 @@ function DistanceRequest({iou, iouType, report, transaction, mapboxAccessToken})
useEffect(updateGradientVisibility, [scrollContainerHeight, scrollContentHeight]);
- return (
+ const navigateBack = () => {
+ Navigation.goBack(isEditing ? ROUTES.getMoneyRequestConfirmationRoute(iouType, reportID) : null);
+ };
+
+ const navigateToNextPage = () => {
+ if (isEditing) {
+ Navigation.goBack(ROUTES.getMoneyRequestConfirmationRoute(iouType, reportID));
+ return;
+ }
+
+ IOU.navigateToNextPage(iou, iouType, reportID, report);
+ };
+
+ const content = (
{!isOffline && Boolean(mapboxAccessToken.token) ? (
-
) : (
@@ -265,12 +296,35 @@ function DistanceRequest({iou, iouType, report, transaction, mapboxAccessToken})
);
+
+ if (!isEditing) {
+ return content;
+ }
+
+ return (
+
+ {({safeAreaPaddingBottomStyle}) => (
+
+
+
+ {content}
+
+
+ )}
+
+ );
}
DistanceRequest.displayName = 'DistanceRequest';
diff --git a/src/components/DotIndicatorMessage.js b/src/components/DotIndicatorMessage.js
index ac550f34de3f..b3528b43dc75 100644
--- a/src/components/DotIndicatorMessage.js
+++ b/src/components/DotIndicatorMessage.js
@@ -5,7 +5,7 @@ import {View} from 'react-native';
import styles from '../styles/styles';
import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';
-import colors from '../styles/colors';
+import themeColors from '../styles/themes/default';
import Text from './Text';
import * as Localize from '../libs/Localize';
@@ -57,7 +57,7 @@ function DotIndicatorMessage(props) {
diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.js b/src/components/EmojiPicker/EmojiPickerMenu/index.js
index 61f6981edbbe..d3268ebc54b0 100755
--- a/src/components/EmojiPicker/EmojiPickerMenu/index.js
+++ b/src/components/EmojiPicker/EmojiPickerMenu/index.js
@@ -21,7 +21,6 @@ import * as EmojiUtils from '../../../libs/EmojiUtils';
import CategoryShortcutBar from '../CategoryShortcutBar';
import TextInput from '../../TextInput';
import isEnterWhileComposition from '../../../libs/KeyboardShortcut/isEnterWhileComposition';
-import canFocusInputOnScreenFocus from '../../../libs/canFocusInputOnScreenFocus';
const propTypes = {
/** Function to add the selected emoji to the main compose text input */
@@ -59,10 +58,6 @@ class EmojiPickerMenu extends Component {
// Ref for emoji FlatList
this.emojiList = undefined;
- // We want consistent auto focus behavior on input between native and mWeb so we have some auto focus management code that will
- // prevent auto focus when open picker for mobile device
- this.shouldFocusInputOnScreenFocus = canFocusInputOnScreenFocus();
-
this.filterEmojis = _.debounce(this.filterEmojis.bind(this), 300);
this.highlightAdjacentEmoji = this.highlightAdjacentEmoji.bind(this);
this.setupEventHandlers = this.setupEventHandlers.bind(this);
@@ -101,7 +96,7 @@ class EmojiPickerMenu extends Component {
// get a ref to the inner textInput element e.g. if we do
// this.textInput = el} /> this will not
// return a ref to the component, but rather the HTML element by default
- if (this.shouldFocusInputOnScreenFocus && this.props.forwardedRef && _.isFunction(this.props.forwardedRef)) {
+ if (this.props.forwardedRef && _.isFunction(this.props.forwardedRef)) {
this.props.forwardedRef(this.searchInput);
}
this.setupEventHandlers();
@@ -507,7 +502,6 @@ class EmojiPickerMenu extends Component {
onChangeText={this.filterEmojis}
defaultValue=""
ref={(el) => (this.searchInput = el)}
- autoFocus={this.shouldFocusInputOnScreenFocus}
selectTextOnFocus={this.state.selectTextOnFocus}
onSelectionChange={this.onSelectionChange}
onFocus={() => this.setState({isFocused: true, highlightedIndex: -1, isUsingKeyboardMovement: false})}
diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.native.js b/src/components/EmojiPicker/EmojiPickerMenu/index.native.js
index bfdaf1c13d1b..5cd956dae56b 100644
--- a/src/components/EmojiPicker/EmojiPickerMenu/index.native.js
+++ b/src/components/EmojiPicker/EmojiPickerMenu/index.native.js
@@ -25,6 +25,9 @@ const propTypes = {
/** Function to add the selected emoji to the main compose text input */
onEmojiSelected: PropTypes.func.isRequired,
+ /** The ref to the search input (may be null on small screen widths) */
+ forwardedRef: PropTypes.func,
+
/** Stores user's preferred skin tone */
preferredSkinTone: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
@@ -37,11 +40,12 @@ const propTypes = {
};
const defaultProps = {
+ forwardedRef: () => {},
preferredSkinTone: CONST.EMOJI_DEFAULT_SKIN_TONE,
frequentlyUsedEmojis: [],
};
-function EmojiPickerMenu({preferredLocale, onEmojiSelected, preferredSkinTone, translate, frequentlyUsedEmojis}) {
+function EmojiPickerMenu({preferredLocale, onEmojiSelected, preferredSkinTone, translate, frequentlyUsedEmojis, forwardedRef}) {
const emojiList = useAnimatedRef();
// eslint-disable-next-line react-hooks/exhaustive-deps
const allEmojis = useMemo(() => EmojiUtils.mergeEmojisWithFrequentlyUsedEmojis(emojis), [frequentlyUsedEmojis]);
@@ -168,6 +172,7 @@ function EmojiPickerMenu({preferredLocale, onEmojiSelected, preferredSkinTone, t
accessibilityLabel={translate('common.search')}
accessibilityRole={CONST.ACCESSIBILITY_ROLE.TEXT}
onChangeText={filterEmojis}
+ ref={forwardedRef}
/>
{!isFiltered && (
diff --git a/src/components/FormHelpMessage.js b/src/components/FormHelpMessage.js
index df8befe5af30..f7366f8dfef6 100644
--- a/src/components/FormHelpMessage.js
+++ b/src/components/FormHelpMessage.js
@@ -5,7 +5,7 @@ import {View} from 'react-native';
import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';
import Text from './Text';
-import colors from '../styles/colors';
+import themeColors from '../styles/themes/default';
import styles from '../styles/styles';
import stylePropTypes from '../styles/stylePropTypes';
import * as Localize from '../libs/Localize';
@@ -42,7 +42,7 @@ function FormHelpMessage(props) {
{props.isError && (
)}
diff --git a/src/components/GrowlNotification/index.js b/src/components/GrowlNotification/index.js
index 60bd1bf00587..70cadd5efd8e 100644
--- a/src/components/GrowlNotification/index.js
+++ b/src/components/GrowlNotification/index.js
@@ -1,7 +1,7 @@
import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react';
import {Directions, FlingGestureHandler, State} from 'react-native-gesture-handler';
import {View, Animated} from 'react-native';
-import colors from '../../styles/colors';
+import themeColors from '../../styles/themes/default';
import Text from '../Text';
import Icon from '../Icon';
import * as Expensicons from '../Icon/Expensicons';
@@ -14,15 +14,15 @@ import * as Pressables from '../Pressable';
const types = {
[CONST.GROWL.SUCCESS]: {
icon: Expensicons.Checkmark,
- iconColor: colors.green,
+ iconColor: themeColors.success,
},
[CONST.GROWL.ERROR]: {
icon: Expensicons.Exclamation,
- iconColor: colors.red,
+ iconColor: themeColors.danger,
},
[CONST.GROWL.WARNING]: {
icon: Expensicons.Exclamation,
- iconColor: colors.yellow,
+ iconColor: themeColors.warning,
},
};
diff --git a/src/components/LHNOptionsList/OptionRowLHN.js b/src/components/LHNOptionsList/OptionRowLHN.js
index 75be8c67fbf1..f5a293701454 100644
--- a/src/components/LHNOptionsList/OptionRowLHN.js
+++ b/src/components/LHNOptionsList/OptionRowLHN.js
@@ -12,7 +12,6 @@ import * as Expensicons from '../Icon/Expensicons';
import MultipleAvatars from '../MultipleAvatars';
import Hoverable from '../Hoverable';
import DisplayNames from '../DisplayNames';
-import colors from '../../styles/colors';
import Text from '../Text';
import SubscriptAvatar from '../SubscriptAvatar';
import CONST from '../../CONST';
@@ -246,7 +245,7 @@ function OptionRowLHN(props) {
)}
diff --git a/src/components/LHNOptionsList/OptionRowLHNData.js b/src/components/LHNOptionsList/OptionRowLHNData.js
index 4c7bd54efa18..2c51d6332946 100644
--- a/src/components/LHNOptionsList/OptionRowLHNData.js
+++ b/src/components/LHNOptionsList/OptionRowLHNData.js
@@ -33,21 +33,25 @@ const propTypes = {
// eslint-disable-next-line react/forbid-prop-types
fullReport: PropTypes.object,
- /** The policies which the user has access to and which the report could be tied to */
- policies: PropTypes.objectOf(
- PropTypes.shape({
- /** The ID of the policy */
- id: PropTypes.string,
- /** Name of the policy */
- name: PropTypes.string,
- /** Avatar of the policy */
- avatar: PropTypes.string,
- }),
- ),
+ /** The policy which the user has access to and which the report could be tied to */
+ policy: PropTypes.shape({
+ /** The ID of the policy */
+ id: PropTypes.string,
+ /** Name of the policy */
+ name: PropTypes.string,
+ /** Avatar of the policy */
+ avatar: PropTypes.string,
+ }),
/** The actions from the parent report */
parentReportActions: PropTypes.objectOf(PropTypes.shape(reportActionPropTypes)),
+ /** The transaction from the parent report action */
+ transaction: PropTypes.shape({
+ /** The ID of the transaction */
+ transactionID: PropTypes.string,
+ }),
+
...withCurrentReportIDPropTypes,
...basePropTypes,
};
@@ -56,8 +60,9 @@ const defaultProps = {
shouldDisableFocusOptions: false,
personalDetails: {},
fullReport: {},
- policies: {},
+ policy: {},
parentReportActions: {},
+ transaction: {},
preferredLocale: CONST.LOCALES.DEFAULT,
...withCurrentReportIDDefaultProps,
...baseDefaultProps,
@@ -77,9 +82,10 @@ function OptionRowLHNData({
personalDetails,
preferredLocale,
comment,
- policies,
+ policy,
receiptTransactions,
parentReportActions,
+ transaction,
...propsToForward
}) {
const reportID = propsToForward.reportID;
@@ -87,8 +93,6 @@ function OptionRowLHNData({
// instead of a changing number (so we prevent unnecessary re-renders).
const isFocused = !shouldDisableFocusOptions && currentReportID === reportID;
- const policy = lodashGet(policies, [`${ONYXKEYS.COLLECTION.POLICY}${fullReport.policyID}`], '');
-
const parentReportAction = parentReportActions[fullReport.parentReportActionID];
const optionItemRef = useRef();
@@ -109,8 +113,9 @@ function OptionRowLHNData({
optionItemRef.current = item;
return item;
// Listen parentReportAction to update title of thread report when parentReportAction changed
+ // Listen to transaction to update title of transaction report when transaction changed
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [fullReport, linkedTransaction, reportActions, personalDetails, preferredLocale, policy, parentReportAction]);
+ }, [fullReport, linkedTransaction, reportActions, personalDetails, preferredLocale, policy, parentReportAction, transaction]);
useEffect(() => {
if (!optionItem || optionItem.hasDraftComment || !comment || comment.length <= 0 || isFocused) {
@@ -189,20 +194,26 @@ export default React.memo(
preferredLocale: {
key: ONYXKEYS.NVP_PREFERRED_LOCALE,
},
- policies: {
- key: ONYXKEYS.COLLECTION.POLICY,
- },
}),
withOnyx({
parentReportActions: {
key: ({fullReport}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${fullReport.parentReportID}`,
canEvict: false,
},
+ policy: {
+ key: ({fullReport}) => `${ONYXKEYS.COLLECTION.POLICY}${fullReport.policyID}`,
+ },
// Ideally, we aim to access only the last transaction for the current report by listening to changes in reportActions.
// In some scenarios, a transaction might be created after reportActions have been modified.
// This can lead to situations where `lastTransaction` doesn't update and retains the previous value.
// However, performance overhead of this is minimized by using memos inside the component.
receiptTransactions: {key: ONYXKEYS.COLLECTION.TRANSACTION},
}),
+ withOnyx({
+ transaction: {
+ key: ({fullReport, parentReportActions}) =>
+ `${ONYXKEYS.COLLECTION.TRANSACTION}${lodashGet(parentReportActions, [fullReport.parentReportActionID, 'originalMessage', 'IOUTransactionID'], '')}`,
+ },
+ }),
)(OptionRowLHNData),
);
diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx
index 0431a02695ec..7a2248ffafb9 100644
--- a/src/components/MapView/MapView.tsx
+++ b/src/components/MapView/MapView.tsx
@@ -11,7 +11,7 @@ import CONST from '../../CONST';
import {MapViewProps, MapViewHandle} from './MapViewTypes';
-const MapView = forwardRef(({accessToken, style, mapPadding, styleURL, pitchEnabled, initialState, waypoints, directionCoordinates}, ref) => {
+const MapView = forwardRef(({accessToken, style, mapPadding, styleURL, pitchEnabled, initialState, waypoints, directionCoordinates, onMapReady}, ref) => {
const cameraRef = useRef(null);
const [isIdle, setIsIdle] = useState(false);
@@ -56,6 +56,9 @@ const MapView = forwardRef(({accessToken, style, ma
const setMapIdle = (e: MapState) => {
if (e.gestures.isGestureActive) return;
setIsIdle(true);
+ if (onMapReady) {
+ onMapReady();
+ }
};
return (
diff --git a/src/components/MapView/MapViewTypes.ts b/src/components/MapView/MapViewTypes.ts
index cf5abeed02b2..de32528d077e 100644
--- a/src/components/MapView/MapViewTypes.ts
+++ b/src/components/MapView/MapViewTypes.ts
@@ -18,6 +18,8 @@ type MapViewProps = {
waypoints?: WayPoint[];
// List of coordinates which together forms a direction.
directionCoordinates?: Array<[number, number]>;
+ // Callback to call when the map is idle / ready.
+ onMapReady?: () => void;
};
type DirectionProps = {
diff --git a/src/components/MentionSuggestions.js b/src/components/MentionSuggestions.js
index 4b0129635269..11df8a597ded 100644
--- a/src/components/MentionSuggestions.js
+++ b/src/components/MentionSuggestions.js
@@ -3,6 +3,7 @@ import {View} from 'react-native';
import PropTypes from 'prop-types';
import _ from 'underscore';
import styles from '../styles/styles';
+import themeColors from '../styles/themes/default';
import * as StyleUtils from '../styles/StyleUtils';
import Text from './Text';
import CONST from '../CONST';
@@ -79,7 +80,7 @@ function MentionSuggestions(props) {
size={isIcon ? CONST.AVATAR_SIZE.MENTION_ICON : CONST.AVATAR_SIZE.SMALLER}
name={item.icons[0].name}
type={item.icons[0].type}
- fill={styles.success}
+ fill={themeColors.success}
/>
{
)}
diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js
index 966f5f4340a7..da98d324681e 100755
--- a/src/components/MoneyRequestConfirmationList.js
+++ b/src/components/MoneyRequestConfirmationList.js
@@ -496,7 +496,7 @@ function MoneyRequestConfirmationList(props) {
description={translate('common.distance')}
style={[styles.moneyRequestMenuItem, styles.mb2]}
titleStyle={styles.flex1}
- onPress={() => Navigation.navigate(ROUTES.getMoneyRequestRoute(props.iouType, props.reportID))}
+ onPress={() => Navigation.navigate(ROUTES.getMoneyRequestAddressRoute(props.iouType, props.reportID))}
disabled={didConfirm || props.isReadOnly || !isTypeRequest}
/>
) : (
diff --git a/src/components/MoneyRequestHeader.js b/src/components/MoneyRequestHeader.js
index f04a41ae1153..bdd7365b7893 100644
--- a/src/components/MoneyRequestHeader.js
+++ b/src/components/MoneyRequestHeader.js
@@ -15,19 +15,16 @@ import Navigation from '../libs/Navigation/Navigation';
import ROUTES from '../ROUTES';
import ONYXKEYS from '../ONYXKEYS';
import * as IOU from '../libs/actions/IOU';
-import * as ReportActionsUtils from '../libs/ReportActionsUtils';
import ConfirmModal from './ConfirmModal';
import useLocalize from '../hooks/useLocalize';
import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar';
import * as TransactionUtils from '../libs/TransactionUtils';
+import reportActionPropTypes from '../pages/home/report/reportActionPropTypes';
const propTypes = {
/** The report currently being looked at */
report: iouReportPropTypes.isRequired,
- /** The expense report or iou report (only will have a value if this is a transaction thread) */
- parentReport: iouReportPropTypes,
-
/** The policy which the report is tied to */
policy: PropTypes.shape({
/** Name of the policy */
@@ -37,12 +34,25 @@ const propTypes = {
/** Personal details so we can get the ones for the report participants */
personalDetails: PropTypes.objectOf(participantPropTypes).isRequired,
+ /** Onyx Props */
/** Session info for the currently logged in user. */
session: PropTypes.shape({
/** Currently logged in user email */
email: PropTypes.string,
}),
+ /** The expense report or iou report (only will have a value if this is a transaction thread) */
+ parentReport: iouReportPropTypes,
+
+ /** The report action the transaction is tied to from the parent report */
+ parentReportAction: PropTypes.shape(reportActionPropTypes),
+
+ /** The transaction from the parent report action */
+ transaction: PropTypes.shape({
+ /** The ID of the transaction */
+ transactionID: PropTypes.string,
+ }),
+
...windowDimensionsPropTypes,
};
@@ -51,6 +61,8 @@ const defaultProps = {
email: null,
},
parentReport: {},
+ parentReportAction: {},
+ transaction: {},
};
function MoneyRequestHeader(props) {
@@ -59,21 +71,18 @@ function MoneyRequestHeader(props) {
const moneyRequestReport = props.parentReport;
const isSettled = ReportUtils.isSettled(moneyRequestReport.reportID);
- const parentReportAction = ReportActionsUtils.getParentReportAction(props.report);
-
// Only the requestor can take delete the request, admins can only edit it.
- const isActionOwner = parentReportAction.actorAccountID === lodashGet(props.session, 'accountID', null);
+ const isActionOwner = props.parentReportAction.actorAccountID === lodashGet(props.session, 'accountID', null);
const report = props.report;
report.ownerAccountID = lodashGet(props, ['parentReport', 'ownerAccountID'], null);
report.ownerEmail = lodashGet(props, ['parentReport', 'ownerEmail'], '');
const deleteTransaction = useCallback(() => {
- IOU.deleteMoneyRequest(parentReportAction.originalMessage.IOUTransactionID, parentReportAction, true);
+ IOU.deleteMoneyRequest(props.parentReportAction.originalMessage.IOUTransactionID, props.parentReportAction, true);
setIsDeleteModalVisible(false);
- }, [parentReportAction, setIsDeleteModalVisible]);
+ }, [props.parentReportAction, setIsDeleteModalVisible]);
- const transaction = TransactionUtils.getLinkedTransaction(parentReportAction);
- const isScanning = TransactionUtils.hasReceipt(transaction) && TransactionUtils.isReceiptBeingScanned(transaction);
+ const isScanning = TransactionUtils.hasReceipt(props.transaction) && TransactionUtils.isReceiptBeingScanned(props.transaction);
return (
<>
@@ -85,7 +94,7 @@ function MoneyRequestHeader(props) {
threeDotsMenuItems={[
{
icon: Expensicons.Trashcan,
- text: translate('reportActionContextMenu.deleteAction', {action: parentReportAction}),
+ text: translate('reportActionContextMenu.deleteAction', {action: props.parentReportAction}),
onSelected: () => setIsDeleteModalVisible(true),
},
]}
@@ -125,5 +134,14 @@ export default compose(
parentReport: {
key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`,
},
+ parentReportAction: {
+ key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${(report.parentReportID, report.parentReportActionID)}`,
+ selector: (reportActions, props) => props && props.parentReport && reportActions && reportActions[props.parentReport.parentReportActionID],
+ canEvict: false,
+ },
+ transaction: {
+ key: ({report, parentReportActions}) =>
+ `${ONYXKEYS.COLLECTION.TRANSACTION}${lodashGet(parentReportActions, [report.parentReportActionID, 'originalMessage', 'IOUTransactionID'], '')}`,
+ },
}),
)(MoneyRequestHeader);
diff --git a/src/components/OptionsList/BaseOptionsList.js b/src/components/OptionsList/BaseOptionsList.js
index 252b015edd45..9c139a04dfbb 100644
--- a/src/components/OptionsList/BaseOptionsList.js
+++ b/src/components/OptionsList/BaseOptionsList.js
@@ -170,6 +170,18 @@ function BaseOptionsList({
*/
const renderItem = ({item, index, section}) => {
const isItemDisabled = isDisabled || section.isDisabled || !!item.isDisabled;
+ const isSelected = _.some(selectedOptions, (option) => {
+ if (option.accountID === item.accountID) {
+ return true;
+ }
+
+ if (_.isEmpty(option.name)) {
+ return false;
+ }
+
+ return option.name === item.searchText;
+ });
+
return (
option.accountID === item.accountID || option.name === item.searchText))}
+ isSelected={isSelected}
showSelectedState={canSelectMultipleOptions}
highlightSelected={highlightSelectedOptions}
boldStyle={boldStyle}
@@ -218,7 +230,7 @@ function BaseOptionsList({
return (
{isLoading ? (
-
+
) : (
<>
{headerMessage ? (
diff --git a/src/components/OptionsListSkeletonView.js b/src/components/OptionsListSkeletonView.js
index ffe9652e3945..15c66affe84d 100644
--- a/src/components/OptionsListSkeletonView.js
+++ b/src/components/OptionsListSkeletonView.js
@@ -60,8 +60,8 @@ class OptionsListSkeletonView extends React.Component {
key={`skeletonViewItems${i}`}
animate={this.props.shouldAnimate}
height={CONST.LHN_SKELETON_VIEW_ITEM_HEIGHT}
- backgroundColor={themeColors.borderLighter}
- foregroundColor={themeColors.border}
+ backgroundColor={themeColors.skeletonLHNIn}
+ foregroundColor={themeColors.skeletonLHNOut}
style={styles.mr5}
>
}
source={{uri: this.props.sourceURL}}
diff --git a/src/components/QRShare/index.js b/src/components/QRShare/index.js
index 05ae4165ea8e..d96024ad1046 100644
--- a/src/components/QRShare/index.js
+++ b/src/components/QRShare/index.js
@@ -50,7 +50,7 @@ class QRShare extends Component {
>
diff --git a/src/components/ReportActionItem/MoneyReportView.js b/src/components/ReportActionItem/MoneyReportView.js
index 5b4eaaa37884..68eecf11d5bf 100644
--- a/src/components/ReportActionItem/MoneyReportView.js
+++ b/src/components/ReportActionItem/MoneyReportView.js
@@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import reportPropTypes from '../../pages/reportPropTypes';
import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions';
import styles from '../../styles/styles';
+import themeColors from '../../styles/themes/default';
import * as ReportUtils from '../../libs/ReportUtils';
import * as StyleUtils from '../../styles/StyleUtils';
import CONST from '../../CONST';
@@ -53,7 +54,7 @@ function MoneyReportView(props) {
)}
diff --git a/src/components/ReportActionItem/MoneyRequestPreview.js b/src/components/ReportActionItem/MoneyRequestPreview.js
index 02da03225062..a5a499169cbd 100644
--- a/src/components/ReportActionItem/MoneyRequestPreview.js
+++ b/src/components/ReportActionItem/MoneyRequestPreview.js
@@ -33,7 +33,6 @@ import * as ReceiptUtils from '../../libs/ReceiptUtils';
import ReportActionItemImages from './ReportActionItemImages';
import transactionPropTypes from '../transactionPropTypes';
import * as StyleUtils from '../../styles/StyleUtils';
-import colors from '../../styles/colors';
import variables from '../../styles/variables';
import useWindowDimensions from '../../hooks/useWindowDimensions';
import MoneyRequestSkeletonView from '../MoneyRequestSkeletonView';
@@ -265,7 +264,7 @@ function MoneyRequestPreview(props) {
{hasFieldErrors && (
)}
diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js
index 6c2810ed4be8..1f60dddef6ec 100644
--- a/src/components/ReportActionItem/ReportPreview.js
+++ b/src/components/ReportActionItem/ReportPreview.js
@@ -30,7 +30,6 @@ import * as ReceiptUtils from '../../libs/ReceiptUtils';
import * as ReportActionUtils from '../../libs/ReportActionsUtils';
import * as TransactionUtils from '../../libs/TransactionUtils';
import ReportActionItemImages from './ReportActionItemImages';
-import colors from '../../styles/colors';
const propTypes = {
/** All the data of the action */
@@ -199,7 +198,7 @@ function ReportPreview(props) {
{hasErrors && (
)}
diff --git a/src/components/ReportActionsSkeletonView/SkeletonViewLines.js b/src/components/ReportActionsSkeletonView/SkeletonViewLines.js
index f87ae176e3ac..ddaa46e0b731 100644
--- a/src/components/ReportActionsSkeletonView/SkeletonViewLines.js
+++ b/src/components/ReportActionsSkeletonView/SkeletonViewLines.js
@@ -21,8 +21,8 @@ function SkeletonViewLines(props) {
- {translate('workspace.people.selectAll')}
+ {translate('workspace.people.selectAll')}
)}
diff --git a/src/components/SelectionList/UserListItem.js b/src/components/SelectionList/UserListItem.js
index dd90fc750510..a3c25f09af3b 100644
--- a/src/components/SelectionList/UserListItem.js
+++ b/src/components/SelectionList/UserListItem.js
@@ -21,7 +21,7 @@ function UserListItem({item, isFocused = false, showTooltip, onSelectRow, onDism
const avatar = (
-
- {item.isSelected && (
-
- )}
+
+
+ {item.isSelected && (
+
+ )}
+
{Boolean(item.avatar) &&
(showTooltip ? (
{avatar}
diff --git a/src/components/TabSelector/TabSelector.js b/src/components/TabSelector/TabSelector.js
index e1f8927bcb9b..3c69da78b860 100644
--- a/src/components/TabSelector/TabSelector.js
+++ b/src/components/TabSelector/TabSelector.js
@@ -79,10 +79,10 @@ const getBackgroundColor = (position, routesLength, tabIndex) => {
return position.interpolate({
inputRange,
- outputRange: _.map(inputRange, (i) => (i === tabIndex ? themeColors.midtone : themeColors.appBG)),
+ outputRange: _.map(inputRange, (i) => (i === tabIndex ? themeColors.border : themeColors.appBG)),
});
}
- return themeColors.midtone;
+ return themeColors.border;
};
function TabSelector({state, navigation, onTabPress, position}) {
diff --git a/src/components/ValidateCode/ExpiredValidateCodeModal.js b/src/components/ValidateCode/ExpiredValidateCodeModal.js
index 449462e88130..e31e0772a8c5 100644
--- a/src/components/ValidateCode/ExpiredValidateCodeModal.js
+++ b/src/components/ValidateCode/ExpiredValidateCodeModal.js
@@ -1,6 +1,6 @@
import React from 'react';
import {View} from 'react-native';
-import colors from '../../styles/colors';
+import themeColors from '../../styles/themes/default';
import styles from '../../styles/styles';
import Icon from '../Icon';
import withLocalize, {withLocalizePropTypes} from '../withLocalize';
@@ -33,7 +33,7 @@ function ExpiredValidateCodeModal(props) {
diff --git a/src/components/ValidateCode/JustSignedInModal.js b/src/components/ValidateCode/JustSignedInModal.js
index 40b25e4a19e0..e96505470eba 100644
--- a/src/components/ValidateCode/JustSignedInModal.js
+++ b/src/components/ValidateCode/JustSignedInModal.js
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import {View} from 'react-native';
-import colors from '../../styles/colors';
+import themeColors from '../../styles/themes/default';
import styles from '../../styles/styles';
import Icon from '../Icon';
import withLocalize, {withLocalizePropTypes} from '../withLocalize';
@@ -41,7 +41,7 @@ function JustSignedInModal(props) {
diff --git a/src/components/ValidateCode/ValidateCodeModal.js b/src/components/ValidateCode/ValidateCodeModal.js
index 344c3107b8cf..eabb21eea4a9 100644
--- a/src/components/ValidateCode/ValidateCodeModal.js
+++ b/src/components/ValidateCode/ValidateCodeModal.js
@@ -4,7 +4,7 @@ import {compose} from 'underscore';
import {withOnyx} from 'react-native-onyx';
import lodashGet from 'lodash/get';
import {View} from 'react-native';
-import colors from '../../styles/colors';
+import themeColors from '../../styles/themes/default';
import styles from '../../styles/styles';
import Icon from '../Icon';
import withLocalize, {withLocalizePropTypes} from '../withLocalize';
@@ -71,7 +71,7 @@ function ValidateCodeModal(props) {
diff --git a/src/libs/DistanceRequestUtils.js b/src/libs/DistanceRequestUtils.js
index 25f26cc92b52..51e37530465d 100644
--- a/src/libs/DistanceRequestUtils.js
+++ b/src/libs/DistanceRequestUtils.js
@@ -91,7 +91,8 @@ const getDistanceMerchant = (distanceInMeters, unit, rate, currency, translate)
*/
const getDistanceRequestAmount = (distance, unit, rate) => {
const convertedDistance = convertDistanceUnit(distance, unit);
- return convertedDistance * rate;
+ const roundedDistance = convertedDistance.toFixed(2);
+ return roundedDistance * rate;
};
export default {getDefaultMileageRate, getDistanceMerchant, getDistanceRequestAmount};
diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js
index 851f7aff3a8d..6b83fe2dcbb8 100644
--- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js
+++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js
@@ -132,6 +132,13 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator([
},
name: 'Money_Request_Waypoint',
},
+ {
+ getComponent: () => {
+ const DistanceRequestEditPage = require('../../../pages/iou/DistanceRequestPage').default;
+ return DistanceRequestEditPage;
+ },
+ name: 'Money_Request_Address',
+ },
]);
const SplitDetailsModalStackNavigator = createModalStackNavigator([
diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js
index 62aac8c48e25..a15daf992836 100644
--- a/src/libs/Navigation/linkingConfig.js
+++ b/src/libs/Navigation/linkingConfig.js
@@ -321,6 +321,7 @@ export default {
Money_Request_Tag: ROUTES.MONEY_REQUEST_TAG,
Money_Request_Merchant: ROUTES.MONEY_REQUEST_MERCHANT,
Money_Request_Waypoint: ROUTES.MONEY_REQUEST_WAYPOINT,
+ Money_Request_Address: ROUTES.MONEY_REQUEST_ADDRESS,
IOU_Send_Enable_Payments: ROUTES.IOU_SEND_ENABLE_PAYMENTS,
IOU_Send_Add_Bank_Account: ROUTES.IOU_SEND_ADD_BANK_ACCOUNT,
IOU_Send_Add_Debit_Card: ROUTES.IOU_SEND_ADD_DEBIT_CARD,
diff --git a/src/libs/PersonalDetailsUtils.js b/src/libs/PersonalDetailsUtils.js
index 1d4966826492..a401dea4b911 100644
--- a/src/libs/PersonalDetailsUtils.js
+++ b/src/libs/PersonalDetailsUtils.js
@@ -4,6 +4,7 @@ import _ from 'underscore';
import ONYXKEYS from '../ONYXKEYS';
import * as Localize from './Localize';
import * as UserUtils from './UserUtils';
+import * as LocalePhoneNumber from './LocalePhoneNumber';
let personalDetails = [];
let allPersonalDetails = {};
@@ -115,7 +116,7 @@ function getNewPersonalDetailsOnyxData(logins, accountIDs) {
login,
accountID,
avatar: UserUtils.getDefaultAvatarURL(accountID),
- displayName: login,
+ displayName: LocalePhoneNumber.formatPhoneNumber(login),
};
/**
diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js
index 20337bd9802f..fde847bd9bfc 100644
--- a/src/libs/ReportActionsUtils.js
+++ b/src/libs/ReportActionsUtils.js
@@ -619,6 +619,17 @@ function getAllReportActions(reportID) {
return lodashGet(allReportActions, reportID, []);
}
+/**
+ * Check whether a report action is an attachment (a file, such as an image or a zip).
+ *
+ * @param {Object} reportAction report action
+ * @returns {Boolean}
+ */
+function isReportActionAttachment(reportAction) {
+ const message = _.first(lodashGet(reportAction, 'message', [{}]));
+ return _.has(reportAction, 'isAttachment') ? reportAction.isAttachment : isReportMessageAttachment(message);
+}
+
export {
getSortedReportActions,
getLastVisibleAction,
@@ -656,4 +667,5 @@ export {
isSplitBillAction,
isTaskAction,
getAllReportActions,
+ isReportActionAttachment,
};
diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js
index 3b3f7b976ba6..f24959c4bac2 100644
--- a/src/libs/ReportUtils.js
+++ b/src/libs/ReportUtils.js
@@ -1606,7 +1606,7 @@ function getReportName(report, policy = undefined) {
return getTransactionReportName(parentReportAction);
}
- const isAttachment = _.has(parentReportAction, 'isAttachment') ? parentReportAction.isAttachment : isReportMessageAttachment(_.last(lodashGet(parentReportAction, 'message', [{}])));
+ const isAttachment = ReportActionsUtils.isReportActionAttachment(parentReportAction);
const parentReportActionMessage = lodashGet(parentReportAction, ['message', 0, 'text'], '').replace(/(\r\n|\n|\r)/gm, ' ');
if (isAttachment && parentReportActionMessage) {
return `[${Localize.translateLocal('common.attachment')}]`;
diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js
index c677fe8f6c28..c5c6b03f3d44 100644
--- a/src/libs/actions/IOU.js
+++ b/src/libs/actions/IOU.js
@@ -22,6 +22,7 @@ import * as UserUtils from '../UserUtils';
import * as Report from './Report';
import * as NumberUtils from '../NumberUtils';
import ReceiptGeneric from '../../../assets/images/receipt-generic.png';
+import * as LocalePhoneNumber from '../LocalePhoneNumber';
let allReports;
Onyx.connect({
@@ -492,7 +493,7 @@ function getMoneyRequestInformation(
[payerAccountID]: {
accountID: payerAccountID,
avatar: UserUtils.getDefaultAvatarURL(payerAccountID),
- displayName: participant.displayName || payerEmail,
+ displayName: LocalePhoneNumber.formatPhoneNumber(participant.displayName || payerEmail),
login: participant.login,
},
}
@@ -894,7 +895,7 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco
[accountID]: {
accountID,
avatar: UserUtils.getDefaultAvatarURL(accountID),
- displayName: participant.displayName || email,
+ displayName: LocalePhoneNumber.formatPhoneNumber(participant.displayName || email),
login: participant.login,
},
}
@@ -1033,12 +1034,50 @@ function editMoneyRequest(transactionID, transactionThreadReportID, transactionC
const transactionThread = allReports[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`];
const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`];
const iouReport = allReports[`${ONYXKEYS.COLLECTION.REPORT}${transactionThread.parentReportID}`];
+ const chatReport = allReports[`${ONYXKEYS.COLLECTION.REPORT}${iouReport.chatReportID}`];
const isFromExpenseReport = ReportUtils.isExpenseReport(iouReport);
// STEP 2: Build new modified expense report action.
const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(transactionThread, transaction, transactionChanges, isFromExpenseReport);
const updatedTransaction = TransactionUtils.getUpdatedTransaction(transaction, transactionChanges, isFromExpenseReport);
+
// STEP 3: Compute the IOU total and update the report preview message so LHN amount owed is correct
+ // Should only update if the transaction matches the currency of the report, else we wait for the update
+ // from the server with the currency conversion
+ let updatedMoneyRequestReport = {...iouReport};
+ const updatedChatReport = {...chatReport};
+ if (updatedTransaction.currency === iouReport.currency && updatedTransaction.modifiedAmount) {
+ const diff = TransactionUtils.getAmount(transaction, true) - TransactionUtils.getAmount(updatedTransaction, true);
+ if (ReportUtils.isExpenseReport(iouReport)) {
+ updatedMoneyRequestReport.total += diff;
+ } else {
+ updatedMoneyRequestReport = IOUUtils.updateIOUOwnerAndTotal(iouReport, updatedReportAction.actorAccountID, diff, TransactionUtils.getCurrency(transaction), false);
+ }
+
+ updatedMoneyRequestReport.cachedTotal = CurrencyUtils.convertToDisplayString(updatedMoneyRequestReport.total, updatedTransaction.currency);
+
+ // Update the last message of the IOU report
+ const lastMessage = ReportUtils.getIOUReportActionMessage(
+ iouReport.reportID,
+ CONST.IOU.REPORT_ACTION_TYPE.CREATE,
+ updatedMoneyRequestReport.total,
+ '',
+ updatedTransaction.currency,
+ '',
+ false,
+ );
+ updatedMoneyRequestReport.lastMessageText = lastMessage[0].text;
+ updatedMoneyRequestReport.lastMessageHtml = lastMessage[0].html;
+
+ // Update the last message of the chat report
+ const messageText = Localize.translateLocal('iou.payerOwesAmount', {
+ payer: updatedMoneyRequestReport.managerEmail,
+ amount: CurrencyUtils.convertToDisplayString(updatedMoneyRequestReport.total, updatedMoneyRequestReport.currency),
+ });
+ updatedChatReport.lastMessageText = messageText;
+ updatedChatReport.lastMessageHtml = messageText;
+ }
+
// STEP 4: Compose the optimistic data
const optimisticData = [
{
@@ -1053,6 +1092,16 @@ function editMoneyRequest(transactionID, transactionThreadReportID, transactionC
key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`,
value: updatedTransaction,
},
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`,
+ value: updatedMoneyRequestReport,
+ },
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.chatReportID}`,
+ value: updatedChatReport,
+ },
];
const successData = [
@@ -1076,6 +1125,11 @@ function editMoneyRequest(transactionID, transactionThreadReportID, transactionC
},
},
},
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`,
+ value: {pendingAction: null},
+ },
];
const failureData = [
@@ -1096,6 +1150,11 @@ function editMoneyRequest(transactionID, transactionThreadReportID, transactionC
key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`,
value: iouReport,
},
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.chatReportID}`,
+ value: chatReport,
+ },
];
// STEP 6: Call the API endpoint
diff --git a/src/libs/actions/OnyxUpdates.js b/src/libs/actions/OnyxUpdates.ts
similarity index 62%
rename from src/libs/actions/OnyxUpdates.js
rename to src/libs/actions/OnyxUpdates.ts
index 8e45e7dd2e66..50a4fdffc3ae 100644
--- a/src/libs/actions/OnyxUpdates.js
+++ b/src/libs/actions/OnyxUpdates.ts
@@ -1,28 +1,25 @@
-import Onyx from 'react-native-onyx';
-import _ from 'underscore';
+import Onyx, {OnyxEntry} from 'react-native-onyx';
+import {Merge} from 'type-fest';
import PusherUtils from '../PusherUtils';
import ONYXKEYS from '../../ONYXKEYS';
import * as QueuedOnyxUpdates from './QueuedOnyxUpdates';
import CONST from '../../CONST';
+import {OnyxUpdatesFromServer, OnyxUpdateEvent, Request} from '../../types/onyx';
+import Response from '../../types/onyx/Response';
// This key needs to be separate from ONYXKEYS.ONYX_UPDATES_FROM_SERVER so that it can be updated without triggering the callback when the server IDs are updated. If that
// callback were triggered it would lead to duplicate processing of server updates.
-let lastUpdateIDAppliedToClient = 0;
+let lastUpdateIDAppliedToClient: OnyxEntry = 0;
Onyx.connect({
key: ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT,
callback: (val) => (lastUpdateIDAppliedToClient = val),
});
-/**
- * @param {Object} request
- * @param {Object} response
- * @returns {Promise}
- */
-function applyHTTPSOnyxUpdates(request, response) {
+function applyHTTPSOnyxUpdates(request: Request, response: Response) {
console.debug('[OnyxUpdateManager] Applying https update');
// For most requests we can immediately update Onyx. For write requests we queue the updates and apply them after the sequential queue has flushed to prevent a replay effect in
// the UI. See https://github.com/Expensify/App/issues/12775 for more info.
- const updateHandler = request.data.apiRequestType === CONST.API_REQUEST_TYPE.WRITE ? QueuedOnyxUpdates.queueOnyxUpdates : Onyx.update;
+ const updateHandler = request?.data?.apiRequestType === CONST.API_REQUEST_TYPE.WRITE ? QueuedOnyxUpdates.queueOnyxUpdates : Onyx.update;
// First apply any onyx data updates that are being sent back from the API. We wait for this to complete and then
// apply successData or failureData. This ensures that we do not update any pending, loading, or other UI states contained
@@ -46,55 +43,45 @@ function applyHTTPSOnyxUpdates(request, response) {
});
}
-/**
- * @param {Array} updates
- * @returns {Promise}
- */
-function applyPusherOnyxUpdates(updates) {
+function applyPusherOnyxUpdates(updates: OnyxUpdateEvent[]) {
console.debug('[OnyxUpdateManager] Applying pusher update');
- const pusherEventPromises = _.map(updates, (update) => PusherUtils.triggerMultiEventHandler(update.eventType, update.data));
+ const pusherEventPromises = updates.map((update) => PusherUtils.triggerMultiEventHandler(update.eventType, update.data));
return Promise.all(pusherEventPromises).then(() => {
console.debug('[OnyxUpdateManager] Done applying Pusher update');
});
}
/**
- * @param {Object[]} updateParams
- * @param {String} updateParams.type
- * @param {Number} updateParams.lastUpdateID
- * @param {Object} [updateParams.request] Exists if updateParams.type === 'https'
- * @param {Object} [updateParams.response] Exists if updateParams.type === 'https'
- * @param {Object} [updateParams.updates] Exists if updateParams.type === 'pusher'
- * @returns {Promise}
+ * @param [updateParams.request] Exists if updateParams.type === 'https'
+ * @param [updateParams.response] Exists if updateParams.type === 'https'
+ * @param [updateParams.updates] Exists if updateParams.type === 'pusher'
*/
-function apply({lastUpdateID, type, request, response, updates}) {
+function apply({lastUpdateID, type, request, response, updates}: Merge): Promise;
+function apply({lastUpdateID, type, request, response, updates}: Merge): Promise;
+function apply({lastUpdateID, type, request, response, updates}: OnyxUpdatesFromServer): Promise | undefined {
console.debug(`[OnyxUpdateManager] Applying update type: ${type} with lastUpdateID: ${lastUpdateID}`, {request, response, updates});
- if (lastUpdateID && lastUpdateID < lastUpdateIDAppliedToClient) {
+ if (lastUpdateID && lastUpdateIDAppliedToClient && Number(lastUpdateID) < lastUpdateIDAppliedToClient) {
console.debug('[OnyxUpdateManager] Update received was older than current state, returning without applying the updates');
return Promise.resolve();
}
- if (lastUpdateID && lastUpdateID > lastUpdateIDAppliedToClient) {
- Onyx.merge(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT, lastUpdateID);
+ if (lastUpdateID && lastUpdateIDAppliedToClient && Number(lastUpdateID) > lastUpdateIDAppliedToClient) {
+ Onyx.merge(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT, Number(lastUpdateID));
}
- if (type === CONST.ONYX_UPDATE_TYPES.HTTPS) {
+ if (type === CONST.ONYX_UPDATE_TYPES.HTTPS && request && response) {
return applyHTTPSOnyxUpdates(request, response);
}
- if (type === CONST.ONYX_UPDATE_TYPES.PUSHER) {
+ if (type === CONST.ONYX_UPDATE_TYPES.PUSHER && updates) {
return applyPusherOnyxUpdates(updates);
}
}
/**
- * @param {Object[]} updateParams
- * @param {String} updateParams.type
- * @param {Object} [updateParams.request] Exists if updateParams.type === 'https'
- * @param {Object} [updateParams.response] Exists if updateParams.type === 'https'
- * @param {Object} [updateParams.updates] Exists if updateParams.type === 'pusher'
- * @param {Number} [updateParams.lastUpdateID]
- * @param {Number} [updateParams.previousUpdateID]
+ * @param [updateParams.request] Exists if updateParams.type === 'https'
+ * @param [updateParams.response] Exists if updateParams.type === 'https'
+ * @param [updateParams.updates] Exists if updateParams.type === 'pusher'
*/
-function saveUpdateInformation(updateParams) {
+function saveUpdateInformation(updateParams: OnyxUpdatesFromServer) {
// Always use set() here so that the updateParams are never merged and always unique to the request that came in
Onyx.set(ONYXKEYS.ONYX_UPDATES_FROM_SERVER, updateParams);
}
@@ -102,10 +89,9 @@ function saveUpdateInformation(updateParams) {
/**
* This function will receive the previousUpdateID from any request/pusher update that has it, compare to our current app state
* and return if an update is needed
- * @param {Number} previousUpdateID The previousUpdateID contained in the response object
- * @returns {Boolean}
+ * @param previousUpdateID The previousUpdateID contained in the response object
*/
-function doesClientNeedToBeUpdated(previousUpdateID = 0) {
+function doesClientNeedToBeUpdated(previousUpdateID = 0): boolean {
// If no previousUpdateID is sent, this is not a WRITE request so we don't need to update our current state
if (!previousUpdateID) {
return false;
diff --git a/src/libs/asyncOpenURL/index.js b/src/libs/asyncOpenURL/index.ts
similarity index 60%
rename from src/libs/asyncOpenURL/index.js
rename to src/libs/asyncOpenURL/index.ts
index e69777c5483c..5307049ee923 100644
--- a/src/libs/asyncOpenURL/index.js
+++ b/src/libs/asyncOpenURL/index.ts
@@ -1,6 +1,7 @@
import {Linking} from 'react-native';
+import AsyncOpenURL from './types';
-export default function asyncOpenURL(promise, url) {
+const asyncOpenURL: AsyncOpenURL = (promise, url) => {
if (!url) {
return;
}
@@ -8,4 +9,6 @@ export default function asyncOpenURL(promise, url) {
promise.then((params) => {
Linking.openURL(typeof url === 'string' ? url : url(params));
});
-}
+};
+
+export default asyncOpenURL;
diff --git a/src/libs/asyncOpenURL/index.website.js b/src/libs/asyncOpenURL/index.website.ts
similarity index 61%
rename from src/libs/asyncOpenURL/index.website.js
rename to src/libs/asyncOpenURL/index.website.ts
index e1c491450c18..d503644c1392 100644
--- a/src/libs/asyncOpenURL/index.website.js
+++ b/src/libs/asyncOpenURL/index.website.ts
@@ -1,13 +1,11 @@
import {Linking} from 'react-native';
+import AsyncOpenURL from './types';
/**
* Prevents Safari from blocking pop-up window when opened within async call.
- *
- * @param {Promise} promise
- * @param {string} url
- * @param {Boolean} shouldSkipCustomSafariLogic When true, we will use `Linking.openURL` even if the browser is Safari.
+ * @param shouldSkipCustomSafariLogic When true, we will use `Linking.openURL` even if the browser is Safari.
*/
-export default function asyncOpenURL(promise, url, shouldSkipCustomSafariLogic) {
+const asyncOpenURL: AsyncOpenURL = (promise, url, shouldSkipCustomSafariLogic) => {
if (!url) {
return;
}
@@ -22,8 +20,13 @@ export default function asyncOpenURL(promise, url, shouldSkipCustomSafariLogic)
const windowRef = window.open();
promise
.then((params) => {
+ if (!windowRef) {
+ return;
+ }
windowRef.location = typeof url === 'string' ? url : url(params);
})
- .catch(() => windowRef.close());
+ .catch(() => windowRef?.close());
}
-}
+};
+
+export default asyncOpenURL;
diff --git a/src/libs/asyncOpenURL/types.ts b/src/libs/asyncOpenURL/types.ts
new file mode 100644
index 000000000000..bf24756b0cc2
--- /dev/null
+++ b/src/libs/asyncOpenURL/types.ts
@@ -0,0 +1,3 @@
+type AsyncOpenURL = (promise: Promise, url: string | ((params: T) => string), shouldSkipCustomSafariLogic?: boolean) => void;
+
+export default AsyncOpenURL;
diff --git a/src/libs/searchCountryOptions.js b/src/libs/searchCountryOptions.js
deleted file mode 100644
index 9b0357a17a65..000000000000
--- a/src/libs/searchCountryOptions.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import _ from 'lodash';
-import StringUtils from './StringUtils';
-
-/**
- * Searches the countries/states data and returns sorted results based on the search query
- * @param {String} searchValue
- * @param {Object[]} countriesData - An array of country data objects
- * @returns {Object[]} An array of countries/states sorted based on the search query
- */
-function searchCountryOptions(searchValue, countriesData) {
- if (_.isEmpty(searchValue)) {
- return countriesData;
- }
-
- const trimmedSearchValue = StringUtils.sanitizeString(searchValue);
- if (_.isEmpty(trimmedSearchValue)) {
- return [];
- }
-
- const filteredData = _.filter(countriesData, (country) => _.includes(country.searchValue, trimmedSearchValue));
-
- // sort by country code
- return _.sortBy(filteredData, (country) => (_.toLower(country.value) === trimmedSearchValue ? -1 : 1));
-}
-
-export default searchCountryOptions;
diff --git a/src/libs/searchCountryOptions.ts b/src/libs/searchCountryOptions.ts
new file mode 100644
index 000000000000..8fb1cc9c37f3
--- /dev/null
+++ b/src/libs/searchCountryOptions.ts
@@ -0,0 +1,39 @@
+import StringUtils from './StringUtils';
+
+type CountryData = {
+ value: string;
+ keyForList: string;
+ text: string;
+ isSelected: boolean;
+ searchValue: string;
+};
+
+/**
+ * Searches the countries/states data and returns sorted results based on the search query
+ * @param countriesData - An array of country data objects
+ * @returns An array of countries/states sorted based on the search query
+ */
+function searchCountryOptions(searchValue: string, countriesData: CountryData[]): CountryData[] {
+ if (!searchValue) {
+ return countriesData;
+ }
+
+ const trimmedSearchValue = StringUtils.sanitizeString(searchValue);
+ if (!trimmedSearchValue) {
+ return [];
+ }
+
+ const filteredData = countriesData.filter((country) => country.searchValue.includes(trimmedSearchValue));
+
+ return filteredData.sort((a, b) => {
+ if (a.value.toLowerCase() === trimmedSearchValue) {
+ return -1;
+ }
+ if (b.value.toLowerCase() === trimmedSearchValue) {
+ return 1;
+ }
+ return 0;
+ });
+}
+
+export default searchCountryOptions;
diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js
index 266515e29c2c..0094d174df21 100644
--- a/src/pages/EditRequestPage.js
+++ b/src/pages/EditRequestPage.js
@@ -89,7 +89,9 @@ function EditRequestPage({report, route, parentReport, policy, session}) {
if (canEdit) {
return;
}
- Navigation.dismissModal();
+ Navigation.isNavigationReady().then(() => {
+ Navigation.dismissModal();
+ });
}, [canEdit]);
// Update the transaction object and close the modal
diff --git a/src/pages/LogInWithShortLivedAuthTokenPage.js b/src/pages/LogInWithShortLivedAuthTokenPage.js
index 1679d3aa8b5d..62eff262611d 100644
--- a/src/pages/LogInWithShortLivedAuthTokenPage.js
+++ b/src/pages/LogInWithShortLivedAuthTokenPage.js
@@ -8,7 +8,7 @@ import * as Session from '../libs/actions/Session';
import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator';
import Navigation from '../libs/Navigation/Navigation';
import styles from '../styles/styles';
-import colors from '../styles/colors';
+import themeColors from '../styles/themes/default';
import Icon from '../components/Icon';
import * as Expensicons from '../components/Icon/Expensicons';
import * as Illustrations from '../components/Icon/Illustrations';
@@ -93,7 +93,7 @@ function LogInWithShortLivedAuthTokenPage(props) {
diff --git a/src/pages/ReimbursementAccount/BankAccountPlaidStep.js b/src/pages/ReimbursementAccount/BankAccountPlaidStep.js
index 1168a3ed271d..80c9257d367a 100644
--- a/src/pages/ReimbursementAccount/BankAccountPlaidStep.js
+++ b/src/pages/ReimbursementAccount/BankAccountPlaidStep.js
@@ -103,6 +103,7 @@ function BankAccountPlaidStep(props) {
}}
plaidData={plaidData}
onExitPlaid={() => BankAccounts.setBankAccountSubStep(null)}
+ onBlurPlaid={() => BankAccounts.setBankAccountSubStep(null)}
receivedRedirectURI={receivedRedirectURI}
plaidLinkOAuthToken={plaidLinkOAuthToken}
allowDebit
diff --git a/src/pages/ReimbursementAccount/BankAccountStep.js b/src/pages/ReimbursementAccount/BankAccountStep.js
index 8e718e193efe..74482ef612ed 100644
--- a/src/pages/ReimbursementAccount/BankAccountStep.js
+++ b/src/pages/ReimbursementAccount/BankAccountStep.js
@@ -11,7 +11,7 @@ import * as Expensicons from '../../components/Icon/Expensicons';
import styles from '../../styles/styles';
import TextLink from '../../components/TextLink';
import Icon from '../../components/Icon';
-import colors from '../../styles/colors';
+import themeColors from '../../styles/themes/default';
import CONST from '../../CONST';
import withLocalize from '../../components/withLocalize';
import Text from '../../components/Text';
@@ -146,7 +146,7 @@ function BankAccountStep(props) {
{props.translate('bankAccount.validateAccountError')}
@@ -164,7 +164,7 @@ function BankAccountStep(props) {
diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js
index 74994e4dc9d0..8d389a8c8581 100644
--- a/src/pages/home/HeaderView.js
+++ b/src/pages/home/HeaderView.js
@@ -20,7 +20,7 @@ import CONST from '../../CONST';
import * as ReportUtils from '../../libs/ReportUtils';
import Text from '../../components/Text';
import Tooltip from '../../components/Tooltip';
-import colors from '../../styles/colors';
+import themeColors from '../../styles/themes/default';
import reportPropTypes from '../reportPropTypes';
import ONYXKEYS from '../../ONYXKEYS';
import ThreeDotsMenu from '../../components/ThreeDotsMenu';
@@ -209,7 +209,7 @@ function HeaderView(props) {
)}
diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js
index 0e0f5944d3d8..004087c22308 100644
--- a/src/pages/home/ReportScreen.js
+++ b/src/pages/home/ReportScreen.js
@@ -247,9 +247,10 @@ function ReportScreen({
useEffect(() => {
const unsubscribeVisibilityListener = Visibility.onVisibilityChange(() => {
+ const isTopMostReportID = Navigation.getTopmostReportId() === getReportID(route);
// If the report is not fully visible (AKA on small screen devices and LHR is open) or the report is optimistic (AKA not yet created)
// we don't need to call openReport
- if (!getIsReportFullyVisible(isTopMostReportId) || report.isOptimisticReport) {
+ if (!getIsReportFullyVisible(isTopMostReportID) || report.isOptimisticReport) {
return;
}
diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js
index bac79e1863e5..c974b801dbd9 100644
--- a/src/pages/home/report/ContextMenu/ContextMenuActions.js
+++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js
@@ -95,9 +95,9 @@ export default [
successTextTranslateKey: 'common.download',
successIcon: Expensicons.Download,
shouldShow: (type, reportAction) => {
- const message = _.last(lodashGet(reportAction, 'message', [{}]));
- const isAttachment = _.has(reportAction, 'isAttachment') ? reportAction.isAttachment : ReportUtils.isReportMessageAttachment(message);
- return isAttachment && message.html !== CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML && reportAction.reportActionID && !ReportActionsUtils.isMessageDeleted(reportAction);
+ const isAttachment = ReportActionsUtils.isReportActionAttachment(reportAction);
+ const messageHtml = lodashGet(reportAction, ['message', 0, 'html']);
+ return isAttachment && messageHtml !== CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML && reportAction.reportActionID && !ReportActionsUtils.isMessageDeleted(reportAction);
},
onPress: (closePopover, {reportAction}) => {
const message = _.last(lodashGet(reportAction, 'message', [{}]));
@@ -175,9 +175,7 @@ export default [
successTextTranslateKey: 'reportActionContextMenu.copied',
successIcon: Expensicons.Checkmark,
shouldShow: (type, reportAction) =>
- type === CONTEXT_MENU_TYPES.REPORT_ACTION &&
- !ReportUtils.isReportMessageAttachment(_.last(lodashGet(reportAction, ['message'], [{}]))) &&
- !ReportActionsUtils.isMessageDeleted(reportAction),
+ type === CONTEXT_MENU_TYPES.REPORT_ACTION && !ReportActionsUtils.isReportActionAttachment(reportAction) && !ReportActionsUtils.isMessageDeleted(reportAction),
// If return value is true, we switch the `text` and `icon` on
// `ContextMenuItem` with `successText` and `successIcon` which will fallback to
@@ -189,7 +187,7 @@ export default [
const originalMessage = _.get(reportAction, 'originalMessage', {});
const messageHtml = isTaskAction ? lodashGet(originalMessage, 'html', '') : lodashGet(message, 'html', '');
- const isAttachment = _.has(reportAction, 'isAttachment') ? reportAction.isAttachment : ReportUtils.isReportMessageAttachment(message);
+ const isAttachment = ReportActionsUtils.isReportActionAttachment(reportAction);
if (!isAttachment) {
const content = selection || messageHtml;
if (isReportPreviewAction) {
@@ -208,9 +206,8 @@ export default [
Clipboard.setHtml(content, plainText);
}
}
- } else {
- Clipboard.setString(messageHtml);
}
+
if (closePopover) {
hideContextMenu(true, ReportActionComposeFocusManager.focus);
}
@@ -225,8 +222,7 @@ export default [
successIcon: Expensicons.Checkmark,
successTextTranslateKey: 'reportActionContextMenu.copied',
shouldShow: (type, reportAction, isArchivedRoom, betas, menuTarget) => {
- const message = _.last(lodashGet(reportAction, 'message', [{}]));
- const isAttachment = _.has(reportAction, 'isAttachment') ? reportAction.isAttachment : ReportUtils.isReportMessageAttachment(message);
+ const isAttachment = ReportActionsUtils.isReportActionAttachment(reportAction);
// Only hide the copylink menu item when context menu is opened over img element.
const isAttachmentTarget = lodashGet(menuTarget, 'tagName') === 'IMG' && isAttachment;
diff --git a/src/pages/home/report/LinkPreviewer.js b/src/pages/home/report/LinkPreviewer.js
index 7c88e9d54126..4fcbb0dc0569 100644
--- a/src/pages/home/report/LinkPreviewer.js
+++ b/src/pages/home/report/LinkPreviewer.js
@@ -8,7 +8,7 @@ import TextLink from '../../../components/TextLink';
import * as StyleUtils from '../../../styles/StyleUtils';
import styles from '../../../styles/styles';
import variables from '../../../styles/variables';
-import colors from '../../../styles/colors';
+import themeColors from '../../../styles/themes/default';
const IMAGE_TYPES = ['jpg', 'jpeg', 'png'];
const MAX_IMAGE_HEIGHT = 180;
@@ -99,7 +99,7 @@ function LinkPreviewer(props) {
{!_.isEmpty(title) && (
{title}
diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js
index 061f66513932..a5c412e17a50 100644
--- a/src/pages/home/report/ReportActionItem.js
+++ b/src/pages/home/report/ReportActionItem.js
@@ -599,7 +599,7 @@ function ReportActionItem(props) {
needsOffscreenAlphaCompositing={ReportActionsUtils.isMoneyRequestAction(props.action)}
>
{isWhisper && (
-
+
diff --git a/src/pages/home/sidebar/SidebarLinks.js b/src/pages/home/sidebar/SidebarLinks.js
index c91509e62aba..c38ac9e01ccb 100644
--- a/src/pages/home/sidebar/SidebarLinks.js
+++ b/src/pages/home/sidebar/SidebarLinks.js
@@ -157,7 +157,7 @@ class SidebarLinks extends React.PureComponent {
diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js
index b96ad4f4bfef..cb77a832705c 100644
--- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js
+++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js
@@ -224,11 +224,6 @@ function FloatingActionButtonAndPopover(props) {
text: props.translate('iou.requestMoney'),
onSelected: () => interceptAnonymousUser(() => IOU.startMoneyRequest(CONST.IOU.MONEY_REQUEST_TYPE.REQUEST)),
},
- {
- icon: Expensicons.Heart,
- text: props.translate('sidebarScreen.saveTheWorld'),
- onSelected: () => interceptAnonymousUser(() => Navigation.navigate(ROUTES.TEACHERS_UNITE)),
- },
{
icon: Expensicons.Receipt,
text: props.translate('iou.splitBill'),
diff --git a/src/pages/iou/DistanceRequestPage.js b/src/pages/iou/DistanceRequestPage.js
index d6eae999e2dc..39b068975c77 100644
--- a/src/pages/iou/DistanceRequestPage.js
+++ b/src/pages/iou/DistanceRequestPage.js
@@ -57,6 +57,7 @@ function DistanceRequestPage({iou, report, route}) {
iou={iou}
iouType={iouType}
report={report}
+ route={route}
transactionID={iou.transactionID}
/>
);
diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js
index b44b956ac547..84ea24f4da6b 100644
--- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js
+++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js
@@ -10,14 +10,13 @@ import MoneyRequestParticipantsSplitSelector from './MoneyRequestParticipantsSpl
import MoneyRequestParticipantsSelector from './MoneyRequestParticipantsSelector';
import styles from '../../../../styles/styles';
import ScreenWrapper from '../../../../components/ScreenWrapper';
-import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize';
import Navigation from '../../../../libs/Navigation/Navigation';
-import compose from '../../../../libs/compose';
import * as DeviceCapabilities from '../../../../libs/DeviceCapabilities';
import HeaderWithBackButton from '../../../../components/HeaderWithBackButton';
import * as IOU from '../../../../libs/actions/IOU';
import * as MoneyRequestUtils from '../../../../libs/MoneyRequestUtils';
import {iouPropTypes, iouDefaultProps} from '../../propTypes';
+import useLocalize from '../../../../hooks/useLocalize';
const propTypes = {
/** React Navigation route */
@@ -37,8 +36,6 @@ const propTypes = {
/** The current tab we have navigated to in the request modal. String that corresponds to the request type. */
selectedTab: PropTypes.oneOf([CONST.TAB.DISTANCE, CONST.TAB.MANUAL, CONST.TAB.SCAN]).isRequired,
-
- ...withLocalizePropTypes,
};
const defaultProps = {
@@ -46,16 +43,32 @@ const defaultProps = {
};
function MoneyRequestParticipantsPage(props) {
+ const {translate} = useLocalize();
const prevMoneyRequestId = useRef(props.iou.id);
const iouType = useRef(lodashGet(props.route, 'params.iouType', ''));
const reportID = useRef(lodashGet(props.route, 'params.reportID', ''));
+ const isNewReportIDSelectedLocally = useRef(false);
const optionsSelectorRef = useRef();
const isDistanceRequest = MoneyRequestUtils.isDistanceRequest(iouType.current, props.selectedTab);
- const navigateToNextStep = () => {
+ const splitNavigateToNextStep = () => {
Navigation.navigate(ROUTES.getMoneyRequestConfirmationRoute(iouType.current, reportID.current));
};
+ const moneyRequestNavigateToNextStep = (option) => {
+ isNewReportIDSelectedLocally.current = true;
+
+ if (!option.reportID) {
+ IOU.setMoneyRequestId(iouType.current);
+ Navigation.navigate(ROUTES.getMoneyRequestConfirmationRoute(iouType.current, reportID.current));
+
+ return;
+ }
+
+ IOU.setMoneyRequestId(`${iouType.current}${option.reportID}`);
+ Navigation.navigate(ROUTES.getMoneyRequestConfirmationRoute(iouType.current, option.reportID));
+ };
+
const navigateBack = (forceFallback = false) => {
Navigation.goBack(ROUTES.getMoneyRequestRoute(iouType.current, reportID.current), forceFallback);
};
@@ -64,7 +77,7 @@ function MoneyRequestParticipantsPage(props) {
// ID in Onyx could change by initiating a new request in a separate browser tab or completing a request
if (prevMoneyRequestId.current !== props.iou.id) {
// The ID is cleared on completing a request. In that case, we will do nothing
- if (!isDistanceRequest && props.iou.id) {
+ if (!isNewReportIDSelectedLocally.current && !isDistanceRequest && props.iou.id) {
navigateBack(true);
}
return;
@@ -72,7 +85,7 @@ function MoneyRequestParticipantsPage(props) {
// Reset the money request Onyx if the ID in Onyx does not match the ID from params
const moneyRequestId = `${iouType.current}${reportID.current}`;
- const shouldReset = props.iou.id !== moneyRequestId;
+ const shouldReset = props.iou.id !== moneyRequestId && !isNewReportIDSelectedLocally.current;
if (shouldReset) {
IOU.resetMoneyRequestInfo(moneyRequestId);
}
@@ -94,12 +107,12 @@ function MoneyRequestParticipantsPage(props) {
{({safeAreaPaddingBottomStyle}) => (
{iouType.current === CONST.IOU.MONEY_REQUEST_TYPE.SPLIT ? (
(optionsSelectorRef.current = el)}
- onStepComplete={navigateToNextStep}
+ onStepComplete={moneyRequestNavigateToNextStep}
onAddParticipants={IOU.setMoneyRequestParticipants}
safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle}
iouType={iouType.current}
@@ -124,12 +137,11 @@ MoneyRequestParticipantsPage.displayName = 'IOUParticipantsPage';
MoneyRequestParticipantsPage.propTypes = propTypes;
MoneyRequestParticipantsPage.defaultProps = defaultProps;
-export default compose(
- withLocalize,
- withOnyx({
- iou: {key: ONYXKEYS.IOU},
- selectedTab: {
- key: `${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.RECEIPT_TAB_ID}`,
- },
- }),
-)(MoneyRequestParticipantsPage);
+export default withOnyx({
+ iou: {
+ key: ONYXKEYS.IOU,
+ },
+ selectedTab: {
+ key: `${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.RECEIPT_TAB_ID}`,
+ },
+})(MoneyRequestParticipantsPage);
diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js
index 693a55b14e07..e12672650a54 100755
--- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js
+++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js
@@ -153,7 +153,7 @@ class MoneyRequestParticipantsSelector extends Component {
*/
addSingleParticipant(option) {
this.props.onAddParticipants([{accountID: option.accountID, login: option.login, isPolicyExpenseChat: option.isPolicyExpenseChat, reportID: option.reportID, selected: true}]);
- this.props.onStepComplete();
+ this.props.onStepComplete(option);
}
render() {
diff --git a/src/pages/iou/steps/NewRequestAmountPage.js b/src/pages/iou/steps/NewRequestAmountPage.js
index 0d154809cd4d..0179c211ee55 100644
--- a/src/pages/iou/steps/NewRequestAmountPage.js
+++ b/src/pages/iou/steps/NewRequestAmountPage.js
@@ -182,7 +182,7 @@ function NewRequestAmountPage({route, iou, report, selectedTab}) {
{content}
diff --git a/src/pages/settings/Profile/CustomStatus/StatusPage.js b/src/pages/settings/Profile/CustomStatus/StatusPage.js
index 0d7e1c09454d..f178b25fd0fb 100644
--- a/src/pages/settings/Profile/CustomStatus/StatusPage.js
+++ b/src/pages/settings/Profile/CustomStatus/StatusPage.js
@@ -68,7 +68,7 @@ function StatusPage({draftStatus, currentUserPersonalDetails}) {
diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts
index 0701adb83313..ec06bb07c3fe 100644
--- a/src/styles/StyleUtils.ts
+++ b/src/styles/StyleUtils.ts
@@ -13,7 +13,6 @@ import spacing from './utilities/spacing';
import * as UserUtils from '../libs/UserUtils';
import * as Browser from '../libs/Browser';
import cursor from './utilities/cursor';
-import * as NumberUtils from '../libs/NumberUtils';
type ColorValue = ValueOf;
type AvatarSizeName = ValueOf;
@@ -572,36 +571,6 @@ function getEmojiPickerStyle(isSmallScreenWidth: boolean): ViewStyle | CSSProper
};
}
-/**
- * Get the random promo color and image for Login page
- */
-function getLoginPagePromoStyle(): ViewStyle | CSSProperties {
- const promos = [
- {
- backgroundColor: colors.green,
- backgroundImageUri: `${CONST.CLOUDFRONT_URL}/images/homepage/brand-stories/freeplan_green.svg`,
- },
- {
- backgroundColor: colors.orange,
- backgroundImageUri: `${CONST.CLOUDFRONT_URL}/images/homepage/brand-stories/freeplan_orange.svg`,
- },
- {
- backgroundColor: colors.pink,
- backgroundImageUri: `${CONST.CLOUDFRONT_URL}/images/homepage/brand-stories/freeplan_pink.svg`,
- },
- {
- backgroundColor: colors.blue,
- backgroundImageUri: `${CONST.CLOUDFRONT_URL}/images/homepage/brand-stories/freeplan_blue.svg`,
- },
- {
- backgroundColor: colors.ivory,
- backgroundImageUri: `${CONST.CLOUDFRONT_URL}/images/homepage/brand-stories/cpa-card.svg`,
- redirectUri: `${CONST.USE_EXPENSIFY_URL}/accountants`,
- },
- ];
- return promos[NumberUtils.generateRandomInt(0, 4)];
-}
-
/**
* Generate the styles for the ReportActionItem wrapper view.
*/
@@ -614,7 +583,7 @@ function getReportActionItemStyle(isHovered = false, isLoading = false): ViewSty
backgroundColor: isHovered
? themeColors.hoverComponentBG
: // Warning: Setting this to a non-transparent color will cause unread indicator to break on Android
- colors.transparent,
+ themeColors.transparent,
opacity: isLoading ? 0.5 : 1,
...styles.cursorInitial,
};
@@ -987,7 +956,7 @@ function getAutoCompleteSuggestionContainerStyle(itemsHeight: number, shouldIncl
* Select the correct color for text.
*/
function getColoredBackgroundStyle(isColored: boolean): ViewStyle | CSSProperties {
- return {backgroundColor: isColored ? colors.blueLink : undefined};
+ return {backgroundColor: isColored ? themeColors.link : undefined};
}
function getEmojiReactionBubbleStyle(isHovered: boolean, hasUserReacted: boolean, isContextMenu = false): ViewStyle | CSSProperties {
@@ -1035,7 +1004,7 @@ function getEmojiReactionCounterTextStyle(hasUserReacted: boolean): TextStyle |
return {color: themeColors.reactionActiveText};
}
- return {color: themeColors.textLight};
+ return {color: themeColors.text};
}
/**
@@ -1272,7 +1241,6 @@ export {
getModalPaddingStyles,
getFontFamilyMonospace,
getEmojiPickerStyle,
- getLoginPagePromoStyle,
getReportActionItemStyle,
getMiniReportActionContextMenuWrapperStyle,
getKeyboardShortcutsModalWidth,
diff --git a/src/styles/colors.js b/src/styles/colors.js
index 2c70cb4a8a78..9ac3226a1b80 100644
--- a/src/styles/colors.js
+++ b/src/styles/colors.js
@@ -2,44 +2,44 @@
* DO NOT import colors.js into files. Use ../themes/default.js instead.
*/
export default {
- dark: '#0b1b34',
black: '#000000',
- blue: '#0185ff',
- blueHover: '#B0D9FF',
+ white: '#FFFFFF',
ivory: '#fffaf0',
- orange: '#FF7101',
- pink: '#F68DFE',
green: '#03D47C',
greenHover: '#00C271',
greenPressed: '#35DD96',
- greenDisabled: '80E9BD',
red: '#F25730',
redHover: '#DE4822',
redPressed: '#F57959',
- redDisabled: '#F8AA97',
- yellow: '#FED607',
transparent: 'transparent',
// Dark Mode Theme Colors
- greenAppBackground: '#061B09',
- greenHighlightBackground: '#07271F',
- greenBorders: '#1A3D32',
- greenBordersLighter: '#2B5548',
- greenIcons: '#8B9C8F',
- greenSupportingText: '#AFBBB0',
- white: '#E7ECE9',
- blueLink: '#5AB0FF',
- blueLinkHover: '#B0D9FF',
- blueLinkPreview: '#2EAAE2',
- greenDefaultButton: '#184E3D',
- greenDefaultButtonHover: '#2C6755',
- greenDefaultButtonPressed: '#467164',
- greenDefaultButtonDisabled: '#8BA69E',
- midnight: '#002140',
+ darkAppBackground: '#061B09',
+ darkHighlightBackground: '#07271F',
+ darkBorders: '#1A3D32',
+ darkIcons: '#8B9C8F',
+ darkSupportingText: '#AFBBB0',
+ darkPrimaryText: '#E7ECE9',
+ darkDefaultButton: '#184E3D',
+ darkDefaultButtonHover: '#2C6755',
+ darkDefaultButtonPressed: '#467164',
+
+ // Light Mode Theme Colors
+ lightAppBackground: '#FCFBF9',
+ lightHighlightBackground: '#F8F4F0',
+ lightBorders: '#EBE6DF',
+ lightBordersLighter: '#2B5548',
+ lightIcons: '#A2A9A3',
+ lightSupportingText: '#76847E',
+ lightPrimaryText: '#002E22',
+ lightDefaultButton: '#EEEBE7',
+ lightDefaultButtonHover: '#E3DFD9',
+ lightDefaultButtonPressed: '#D2CCC3',
// Brand Colors from Figma
blue100: '#B0D9FF',
blue200: '#8DC8FF',
+ blue300: '#5AB0FF',
blue400: '#0185FF',
blue500: '#0676DE',
blue600: '#0164BF',
@@ -48,41 +48,46 @@ export default {
green100: '#B1F2D6',
green200: '#8EECC4',
+ green300: '#5BE3AA',
green400: '#03D47C',
+ green500: '#00B268',
green600: '#008C59',
green700: '#085239',
green800: '#002E22',
+ yellow100: '#FFF2B2',
yellow200: '#FFED8F',
+ yellow300: '#FEE45E',
yellow400: '#FED607',
+ yellow500: '#E4BC07',
+ yellow600: '#D18000',
yellow700: '#722B03',
yellow800: '#401102',
+ tangerine100: '#FFD7B0',
tangerine200: '#FFC68C',
+ tangerine300: '#FFA75A',
tangerine400: '#FF7101',
+ tangerine500: '#F25730',
+ tangerine600: '#BF3013',
tangerine700: '#780505',
tangerine800: '#400000',
+ pink100: '#FCDCFF',
pink200: '#FBCCFF',
+ pink300: '#F9B5FE',
pink400: '#F68DFE',
+ pink500: '#E96DF2',
pink600: '#CF4CD9',
pink700: '#712A76',
pink800: '#49225B',
+ ice100: '#DFFDFE',
ice200: '#CCF7FF',
+ ice300: '#A5FBFF',
ice400: '#50EEF6',
ice500: '#4ED7DE',
+ ice600: '#4BA6A6',
ice700: '#28736D',
ice800: '#134038',
-
- orange800: '#400000',
-
- // DEPRECATED COLORS. Do not reference these colors. Will be deleted in color switch PR.
- gray1: '#FAFAFA',
- gray2: '#ECECEC',
- gray3: '#C6C9CA',
- gray4: '#7D8B8F',
- oldRed: '#fc3826',
- oldRedHover: '#e13826',
- oldRedDisabled: '#fea29a',
};
diff --git a/src/styles/getTooltipStyles.ts b/src/styles/getTooltipStyles.ts
index 3f9de9c78b97..97402467ab4c 100644
--- a/src/styles/getTooltipStyles.ts
+++ b/src/styles/getTooltipStyles.ts
@@ -2,7 +2,6 @@ import {CSSProperties} from 'react';
import {TextStyle, View, ViewStyle} from 'react-native';
import spacing from './utilities/spacing';
import styles from './styles';
-import colors from './colors';
import themeColors from './themes/default';
import fontFamily from './fontFamily';
import variables from './variables';
@@ -267,13 +266,13 @@ export default function getTooltipStyles(
pointerStyle: {
width: 0,
height: 0,
- backgroundColor: colors.transparent,
+ backgroundColor: themeColors.transparent,
borderStyle: 'solid',
borderLeftWidth: POINTER_WIDTH / 2,
borderRightWidth: POINTER_WIDTH / 2,
borderTopWidth: POINTER_HEIGHT,
- borderLeftColor: colors.transparent,
- borderRightColor: colors.transparent,
+ borderLeftColor: themeColors.transparent,
+ borderRightColor: themeColors.transparent,
borderTopColor: themeColors.heading,
...pointerAdditionalStyle,
},
diff --git a/src/styles/styles.js b/src/styles/styles.js
index e81e03726c78..0fba61f1e8d9 100644
--- a/src/styles/styles.js
+++ b/src/styles/styles.js
@@ -26,7 +26,6 @@ import * as Browser from '../libs/Browser';
import cursor from './utilities/cursor';
import userSelect from './utilities/userSelect';
import textUnderline from './utilities/textUnderline';
-import Colors from './colors';
// touchCallout is an iOS safari only property that controls the display of the callout information when you touch and hold a target
const touchCalloutNone = Browser.isMobileSafari() ? {WebkitTouchCallout: 'none'} : {};
@@ -815,8 +814,8 @@ const styles = (theme) => ({
borderRadius: 28,
borderStyle: 'solid',
borderWidth: 8,
- backgroundColor: Colors.greenHighlightBackground,
- borderColor: Colors.greenAppBackground,
+ backgroundColor: theme.highlightBG,
+ borderColor: theme.appBG,
},
permissionView: {
@@ -1550,7 +1549,7 @@ const styles = (theme) => ({
top: 0,
bottom: 0,
right: 0,
- backgroundColor: Colors.black,
+ backgroundColor: theme.shadow,
opacity: current.progress.interpolate({
inputRange: [0, 1],
outputRange: [0, variables.overlayOpacity],
@@ -3429,7 +3428,7 @@ const styles = (theme) => ({
},
fontColorReactionLabel: {
- color: '#586A64',
+ color: theme.tooltipSupportingText,
},
reactionEmojiTitle: {
@@ -3438,7 +3437,7 @@ const styles = (theme) => ({
},
textReactionSenders: {
- color: theme.dark,
+ color: theme.tooltipPrimaryText,
...wordBreak.breakWord,
},
@@ -3931,19 +3930,31 @@ const styles = (theme) => ({
overflow: 'hidden',
},
+ mapViewOverlay: {
+ flex: 1,
+ position: 'absolute',
+ left: 0,
+ top: 0,
+ borderRadius: variables.componentBorderRadiusLarge,
+ overflow: 'hidden',
+ backgroundColor: theme.highlightBG,
+ ...sizing.w100,
+ ...sizing.h100,
+ },
+
confirmationListMapItem: {
...spacing.m5,
height: 200,
},
mapDirection: {
- lineColor: Colors.green,
+ lineColor: theme.success,
lineWidth: 7,
},
mapDirectionLayer: {
layout: {'line-join': 'round', 'line-cap': 'round'},
- paint: {'line-color': Colors.green, 'line-width': 7},
+ paint: {'line-color': theme.success, 'line-width': 7},
},
mapPendingView: {
diff --git a/src/styles/themes/default.js b/src/styles/themes/default.js
index 5f69a7746cdc..c101a668666b 100644
--- a/src/styles/themes/default.js
+++ b/src/styles/themes/default.js
@@ -5,71 +5,68 @@ import ROUTES from '../../ROUTES';
const darkTheme = {
// Figma keys
- appBG: colors.greenAppBackground,
- splashBG: colors.green,
- highlightBG: colors.greenHighlightBackground,
- border: colors.greenBorders,
- borderLighter: colors.greenBordersLighter,
- borderFocus: colors.green,
- icon: colors.greenIcons,
- iconMenu: colors.green,
- iconHovered: colors.white,
- iconSuccessFill: colors.green,
- iconReversed: colors.greenAppBackground,
+ appBG: colors.darkAppBackground,
+ splashBG: colors.green400,
+ highlightBG: colors.darkHighlightBackground,
+ border: colors.darkBorders,
+ borderLighter: colors.darkDefaultButton,
+ borderFocus: colors.green400,
+ icon: colors.darkIcons,
+ iconMenu: colors.green400,
+ iconHovered: colors.darkPrimaryText,
+ iconSuccessFill: colors.green400,
+ iconReversed: colors.darkAppBackground,
iconColorfulBackground: `${colors.ivory}cc`,
- textSupporting: colors.greenSupportingText,
- text: colors.white,
+ textSupporting: colors.darkSupportingText,
+ text: colors.darkPrimaryText,
textColorfulBackground: colors.ivory,
- link: colors.blueLink,
- linkHover: colors.blueLinkHover,
- buttonDefaultBG: colors.greenDefaultButton,
- buttonDisabledBG: colors.greenDefaultButtonDisabled,
- buttonHoveredBG: colors.greenDefaultButtonHover,
- buttonPressedBG: colors.greenDefaultButtonPressed,
+ link: colors.blue300,
+ linkHover: colors.blue100,
+ buttonDefaultBG: colors.darkDefaultButton,
+ buttonHoveredBG: colors.darkDefaultButtonHover,
+ buttonPressedBG: colors.darkDefaultButtonPressed,
danger: colors.red,
dangerHover: colors.redHover,
dangerPressed: colors.redHover,
- warning: colors.yellow,
- success: colors.green,
+ warning: colors.yellow400,
+ success: colors.green400,
successHover: colors.greenHover,
successPressed: colors.greenPressed,
transparent: colors.transparent,
- midtone: colors.green700,
signInPage: colors.green800,
- dark: colors.midnight,
// Additional keys
- overlay: colors.greenHighlightBackground,
- inverse: colors.white,
+ overlay: colors.darkHighlightBackground,
+ inverse: colors.darkPrimaryText,
shadow: colors.black,
- componentBG: colors.greenAppBackground,
- hoverComponentBG: colors.greenHighlightBackground,
- activeComponentBG: colors.greenBorders,
+ componentBG: colors.darkAppBackground,
+ hoverComponentBG: colors.darkHighlightBackground,
+ activeComponentBG: colors.darkBorders,
signInSidebar: colors.green800,
- sidebar: colors.greenHighlightBackground,
- sidebarHover: colors.greenAppBackground,
- heading: colors.white,
- textLight: colors.white,
- textDark: colors.greenAppBackground,
- textReversed: colors.greenAppBackground,
- textBackground: colors.greenHighlightBackground,
- textMutedReversed: colors.greenIcons,
+ sidebar: colors.darkHighlightBackground,
+ sidebarHover: colors.darkAppBackground,
+ heading: colors.darkPrimaryText,
+ textLight: colors.darkPrimaryText,
+ textDark: colors.darkAppBackground,
+ textReversed: colors.lightPrimaryText,
+ textBackground: colors.darkHighlightBackground,
+ textMutedReversed: colors.darkIcons,
textError: colors.red,
- offline: colors.greenIcons,
- modalBackdrop: colors.greenHighlightBackground,
- modalBackground: colors.greenAppBackground,
- cardBG: colors.greenHighlightBackground,
- cardBorder: colors.greenHighlightBackground,
- spinner: colors.greenSupportingText,
- unreadIndicator: colors.green,
- placeholderText: colors.greenIcons,
- heroCard: colors.blue,
- uploadPreviewActivityIndicator: colors.greenHighlightBackground,
+ offline: colors.darkIcons,
+ modalBackdrop: colors.darkHighlightBackground,
+ modalBackground: colors.darkAppBackground,
+ cardBG: colors.darkHighlightBackground,
+ cardBorder: colors.darkHighlightBackground,
+ spinner: colors.darkSupportingText,
+ unreadIndicator: colors.green400,
+ placeholderText: colors.darkIcons,
+ heroCard: colors.blue400,
+ uploadPreviewActivityIndicator: colors.darkHighlightBackground,
dropUIBG: 'rgba(6,27,9,0.92)',
receiptDropUIBG: 'rgba(3, 212, 124, 0.84)',
- checkBox: colors.green,
- pickerOptionsTextColor: colors.white,
- imageCropBackgroundColor: colors.greenIcons,
+ checkBox: colors.green400,
+ pickerOptionsTextColor: colors.darkPrimaryText,
+ imageCropBackgroundColor: colors.darkIcons,
fallbackIconColor: colors.green700,
reactionActiveBackground: colors.green600,
reactionActiveText: colors.green100,
@@ -79,6 +76,11 @@ const darkTheme = {
mentionBG: colors.blue600,
ourMentionText: colors.green100,
ourMentionBG: colors.green600,
+ tooltipSupportingText: colors.lightSupportingText,
+ tooltipPrimaryText: colors.lightPrimaryText,
+ skeletonLHNIn: colors.darkBorders,
+ skeletonLHNOut: colors.darkDefaultButton,
+ QRLogo: colors.green400,
starDefaultBG: 'rgb(254, 228, 94)',
};
@@ -86,66 +88,9 @@ darkTheme.PAGE_BACKGROUND_COLORS = {
[SCREENS.HOME]: darkTheme.sidebar,
[SCREENS.SETTINGS.PREFERENCES]: colors.blue500,
[SCREENS.SETTINGS.WORKSPACES]: colors.pink800,
- [ROUTES.I_KNOW_A_TEACHER]: colors.orange800,
+ [ROUTES.SETTINGS_STATUS]: colors.green700,
+ [ROUTES.I_KNOW_A_TEACHER]: colors.tangerine800,
[ROUTES.SETTINGS_SECURITY]: colors.ice500,
};
-const oldTheme = {
- shadow: colors.black,
- link: colors.blue,
- linkHover: colors.blueHover,
- componentBG: colors.white,
- hoverComponentBG: colors.gray1,
- activeComponentBG: colors.gray2,
- appBG: colors.white,
- heading: colors.dark,
- sidebar: colors.gray1,
- sidebarHover: colors.white,
- border: colors.gray2,
- borderFocus: colors.blue,
- icon: colors.gray3,
- iconMenu: colors.gray3,
- iconHovered: colors.dark,
- iconSuccessFill: colors.green,
- iconReversed: colors.white,
- textSupporting: colors.gray4,
- text: colors.dark,
- textError: colors.oldRed,
- textBackground: colors.gray1,
- textReversed: colors.white,
- textMutedReversed: colors.gray3,
- buttonDefaultBG: colors.gray2,
- offline: colors.gray3,
- modalBackdrop: colors.gray3,
- modalBackground: colors.gray2,
- buttonDisabledBG: colors.gray2,
- buttonHoveredBG: colors.gray1,
- buttonPressedBG: colors.gray2,
- spinner: colors.gray4,
- unreadIndicator: colors.green,
- placeholderText: colors.gray3,
- heroCard: colors.blue,
- uploadPreviewActivityIndicator: colors.gray1,
- dropUIBG: 'rgba(6,27,9,0.92)',
- cardBG: colors.gray1,
- cardBorder: colors.gray1,
- checkBox: colors.blue,
- overlay: colors.gray1,
-
- // Merging new Keys for Dark Mode merge. Delete after new branding is implemented.
- highlightBG: colors.gray1,
- danger: colors.oldRed,
- dangerHover: colors.oldRedHover,
- dangerPressed: colors.oldRedHover,
- dangerDisabled: colors.oldRedDisabled,
- warning: colors.yellow,
- success: colors.green,
- successHover: colors.greenHover,
- successPressed: colors.greenPressed,
- transparent: colors.transparent,
- inverse: colors.dark,
- textLight: colors.white,
- textDark: colors.dark,
-};
-
export default darkTheme;
diff --git a/src/styles/themes/light.js b/src/styles/themes/light.js
new file mode 100644
index 000000000000..1a945cb84913
--- /dev/null
+++ b/src/styles/themes/light.js
@@ -0,0 +1,95 @@
+import colors from '../colors';
+import SCREENS from '../../SCREENS';
+import ROUTES from '../../ROUTES';
+
+const lightTheme = {
+ // Figma keys
+ appBG: colors.lightAppBackground,
+ splashBG: colors.green400,
+ highlightBG: colors.lightHighlightBackground,
+ border: colors.lightBorders,
+ borderLighter: colors.lightDefaultButtonPressed,
+ borderFocus: colors.green400,
+ icon: colors.lightIcons,
+ iconMenu: colors.green400,
+ iconHovered: colors.lightPrimaryText,
+ iconSuccessFill: colors.green400,
+ iconReversed: colors.lightAppBackground,
+ iconColorfulBackground: `${colors.ivory}cc`,
+ textColorfulBackground: colors.ivory,
+ textSupporting: colors.lightSupportingText,
+ text: colors.lightPrimaryText,
+ link: colors.blue600,
+ linkHover: colors.blue500,
+ buttonDefaultBG: colors.lightDefaultButton,
+ buttonHoveredBG: colors.lightDefaultButtonHover,
+ buttonPressedBG: colors.lightDefaultButtonPressed,
+ danger: colors.red,
+ dangerHover: colors.redHover,
+ dangerPressed: colors.redHover,
+ warning: colors.yellow400,
+ success: colors.green400,
+ successHover: colors.greenHover,
+ successPressed: colors.greenPressed,
+ transparent: colors.transparent,
+ signInPage: colors.green800,
+
+ // Additional keys
+ overlay: colors.lightHighlightBackground,
+ inverse: colors.lightPrimaryText,
+ shadow: colors.black,
+ componentBG: colors.lightAppBackground,
+ hoverComponentBG: colors.lightHighlightBackground,
+ activeComponentBG: colors.lightBorders,
+ signInSidebar: colors.green800,
+ sidebar: colors.lightHighlightBackground,
+ sidebarHover: colors.lightBorders,
+ heading: colors.lightPrimaryText,
+ textLight: colors.white,
+ textDark: colors.lightPrimaryText,
+ textReversed: colors.darkPrimaryText,
+ textBackground: colors.lightHighlightBackground,
+ textMutedReversed: colors.lightIcons,
+ textError: colors.red,
+ offline: colors.lightIcons,
+ modalBackdrop: colors.lightHighlightBackground,
+ modalBackground: colors.lightAppBackground,
+ cardBG: colors.lightHighlightBackground,
+ cardBorder: colors.lightHighlightBackground,
+ spinner: colors.lightSupportingText,
+ unreadIndicator: colors.green400,
+ placeholderText: colors.lightIcons,
+ heroCard: colors.blue400,
+ uploadPreviewActivityIndicator: colors.lightHighlightBackground,
+ dropUIBG: 'rgba(252, 251, 249, 0.92)',
+ dropTransparentOverlay: 'rgba(255,255,255,0)',
+ checkBox: colors.green400,
+ pickerOptionsTextColor: colors.lightPrimaryText,
+ imageCropBackgroundColor: colors.lightIcons,
+ fallbackIconColor: colors.green700,
+ reactionActiveBackground: colors.green100,
+ reactionActiveText: colors.green600,
+ badgeAdHoc: colors.pink600,
+ badgeAdHocHover: colors.pink700,
+ mentionText: colors.blue600,
+ mentionBG: colors.blue100,
+ ourMentionText: colors.green600,
+ ourMentionBG: colors.green100,
+ tooltipSupportingText: colors.darkSupportingText,
+ tooltipPrimaryText: colors.darkPrimaryText,
+ skeletonLHNIn: colors.lightBorders,
+ skeletonLHNOut: colors.lightDefaultButtonPressed,
+ QRLogo: colors.green400,
+ starDefaultBG: 'rgb(254, 228, 94)',
+};
+
+lightTheme.PAGE_BACKGROUND_COLORS = {
+ [SCREENS.HOME]: lightTheme.sidebar,
+ [SCREENS.SETTINGS.PREFERENCES]: colors.blue500,
+ [SCREENS.SETTINGS.WORKSPACES]: colors.pink800,
+ [ROUTES.SETTINGS_STATUS]: colors.green700,
+ [ROUTES.I_KNOW_A_TEACHER]: colors.tangerine800,
+ [ROUTES.SETTINGS_SECURITY]: colors.ice500,
+};
+
+export default lightTheme;
diff --git a/src/styles/utilities/spacing.ts b/src/styles/utilities/spacing.ts
index 7147b1f2b7d4..a3667f05ac06 100644
--- a/src/styles/utilities/spacing.ts
+++ b/src/styles/utilities/spacing.ts
@@ -373,6 +373,10 @@ export default {
paddingRight: 8,
},
+ pr3: {
+ paddingRight: 12,
+ },
+
pr4: {
paddingRight: 16,
},
diff --git a/src/types/onyx/OnyxUpdatesFromServer.ts b/src/types/onyx/OnyxUpdatesFromServer.ts
index 02a96d4ce230..50b1503b90bd 100644
--- a/src/types/onyx/OnyxUpdatesFromServer.ts
+++ b/src/types/onyx/OnyxUpdatesFromServer.ts
@@ -2,13 +2,18 @@ import {OnyxUpdate} from 'react-native-onyx';
import Request from './Request';
import Response from './Response';
+type OnyxUpdateEvent = {
+ eventType: string;
+ data: OnyxUpdate[];
+};
+
type OnyxUpdatesFromServer = {
type: 'https' | 'pusher';
lastUpdateID: number | string;
previousUpdateID: number | string;
request?: Request;
response?: Response;
- updates?: OnyxUpdate[];
+ updates?: OnyxUpdateEvent[];
};
-export default OnyxUpdatesFromServer;
+export type {OnyxUpdatesFromServer, OnyxUpdateEvent};
diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts
index a980e086aff5..a7bbaf848265 100644
--- a/src/types/onyx/index.ts
+++ b/src/types/onyx/index.ts
@@ -33,7 +33,7 @@ import ReimbursementAccountDraft from './ReimbursementAccountDraft';
import WalletTransfer from './WalletTransfer';
import ReceiptModal from './ReceiptModal';
import MapboxAccessToken from './MapboxAccessToken';
-import OnyxUpdatesFromServer from './OnyxUpdatesFromServer';
+import {OnyxUpdatesFromServer, OnyxUpdateEvent} from './OnyxUpdatesFromServer';
import Download from './Download';
import PolicyMember from './PolicyMember';
import Policy from './Policy';
@@ -97,6 +97,7 @@ export type {
Form,
AddDebitCardForm,
OnyxUpdatesFromServer,
+ OnyxUpdateEvent,
RecentWaypoints,
RecentlyUsedCategories,
RecentlyUsedTags,