diff --git a/.github/actions/getMergeCommitForPullRequest/getMergeCommitForPullRequest.js b/.github/actions/getMergeCommitForPullRequest/getMergeCommitForPullRequest.js
index 70e31e22966a..7d0e0b1f0e17 100644
--- a/.github/actions/getMergeCommitForPullRequest/getMergeCommitForPullRequest.js
+++ b/.github/actions/getMergeCommitForPullRequest/getMergeCommitForPullRequest.js
@@ -9,8 +9,8 @@ const DEFAULT_PAYLOAD = {
};
const pullRequestNumber = ActionUtils.getJSONInput('PULL_REQUEST_NUMBER', {required: false}, null);
-const user = ActionUtils.getJSONInput('USER', {required: false}, null);
-const titleRegex = ActionUtils.getJSONInput('TITLE_REGEX', {required: false}, null);
+const user = core.getInput('USER', {required: false});
+let titleRegex = core.getInput('TITLE_REGEX', {required: false});
if (pullRequestNumber) {
console.log(`Looking for pull request w/ number: ${pullRequestNumber}`);
@@ -21,6 +21,7 @@ if (user) {
}
if (titleRegex) {
+ titleRegex = new RegExp(titleRegex);
console.log(`Looking for pull request w/ title matching: ${titleRegex.toString()}`);
}
@@ -58,7 +59,10 @@ if (pullRequestNumber) {
.then(({data}) => outputMergeCommitHash(data))
.catch(handleUnknownError);
} else {
- GithubUtils.octokit.pulls.list(DEFAULT_PAYLOAD)
+ GithubUtils.octokit.pulls.list({
+ ...DEFAULT_PAYLOAD,
+ state: 'all',
+ })
.then(({data}) => {
const matchingPR = _.find(data, PR => PR.user.login === user && titleRegex.test(PR.title));
outputMergeCommitHash(matchingPR);
diff --git a/.github/actions/getMergeCommitForPullRequest/index.js b/.github/actions/getMergeCommitForPullRequest/index.js
index 091d750852f3..5eaa9ed6f954 100644
--- a/.github/actions/getMergeCommitForPullRequest/index.js
+++ b/.github/actions/getMergeCommitForPullRequest/index.js
@@ -19,8 +19,8 @@ const DEFAULT_PAYLOAD = {
};
const pullRequestNumber = ActionUtils.getJSONInput('PULL_REQUEST_NUMBER', {required: false}, null);
-const user = ActionUtils.getJSONInput('USER', {required: false}, null);
-const titleRegex = ActionUtils.getJSONInput('TITLE_REGEX', {required: false}, null);
+const user = core.getInput('USER', {required: false});
+let titleRegex = core.getInput('TITLE_REGEX', {required: false});
if (pullRequestNumber) {
console.log(`Looking for pull request w/ number: ${pullRequestNumber}`);
@@ -31,6 +31,7 @@ if (user) {
}
if (titleRegex) {
+ titleRegex = new RegExp(titleRegex);
console.log(`Looking for pull request w/ title matching: ${titleRegex.toString()}`);
}
@@ -68,7 +69,10 @@ if (pullRequestNumber) {
.then(({data}) => outputMergeCommitHash(data))
.catch(handleUnknownError);
} else {
- GithubUtils.octokit.pulls.list(DEFAULT_PAYLOAD)
+ GithubUtils.octokit.pulls.list({
+ ...DEFAULT_PAYLOAD,
+ state: 'all',
+ })
.then(({data}) => {
const matchingPR = _.find(data, PR => PR.user.login === user && titleRegex.test(PR.title));
outputMergeCommitHash(matchingPR);
diff --git a/.github/workflows/cherryPick.yml b/.github/workflows/cherryPick.yml
index dfc2f8703cfc..b3d1ad81d304 100644
--- a/.github/workflows/cherryPick.yml
+++ b/.github/workflows/cherryPick.yml
@@ -71,6 +71,7 @@ jobs:
- name: Create branch for new pull request
run: |
+ git config user.name ${{ github.actor }}
git checkout -b cherry-pick-staging-${{ github.event.inputs.PULL_REQUEST_NUMBER }}
git push --set-upstream origin cherry-pick-staging-${{ github.event.inputs.PULL_REQUEST_NUMBER }}
@@ -99,9 +100,11 @@ jobs:
USER: OSBotify
TITLE_REGEX: Update version to ${{ env.NEW_VERSION }}
- - name: Cherry-pick the merge commit to new branch
+ - name: Cherry-pick the merge commits to new branch
id: cherryPick
- run: git cherry-pick ${{ steps.getCPMergeCommit.outputs.MERGE_COMMIT_SHA }} ${{ steps.getVersionBumpMergeCommit.MERGE_COMMIT_SHA }} --mainline 1
+ run: |
+ git fetch
+ git cherry-pick ${{ steps.getCPMergeCommit.outputs.MERGE_COMMIT_SHA }} ${{ steps.getVersionBumpMergeCommit.outputs.MERGE_COMMIT_SHA }} --mainline 1
continue-on-error: true
# If there is a merge conflict, we'll just commit what we have,
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 019db8a31492..3dd124d7a524 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -148,8 +148,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1001006100
- versionName "1.0.61-0"
+ versionCode 1001006300
+ versionName "1.0.63-0"
}
splits {
abi {
diff --git a/desktop/main.js b/desktop/main.js
index 07282bd0109c..3e7b7db379e8 100644
--- a/desktop/main.js
+++ b/desktop/main.js
@@ -130,7 +130,6 @@ const mainWindow = (() => {
width: 1200,
height: 900,
webPreferences: {
- enableRemoteModule: true,
nodeIntegration: true,
},
titleBarStyle: 'hidden',
@@ -229,7 +228,7 @@ const mainWindow = (() => {
ipcMain.on(ELECTRON_EVENTS.REQUEST_VISIBILITY, (event) => {
// This is how synchronous messages work in Electron
// eslint-disable-next-line no-param-reassign
- event.returnValue = browserWindow.isFocused();
+ event.returnValue = browserWindow && browserWindow.isFocused();
});
// This allows the renderer process to bring the app
diff --git a/ios/ExpensifyCash/Info.plist b/ios/ExpensifyCash/Info.plist
index b903c22c401f..403a68a328bf 100644
--- a/ios/ExpensifyCash/Info.plist
+++ b/ios/ExpensifyCash/Info.plist
@@ -17,7 +17,7 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 1.0.61
+ 1.0.63
CFBundleSignature
????
CFBundleURLTypes
@@ -30,7 +30,7 @@
CFBundleVersion
- 1.0.61.0
+ 1.0.63.0
ITSAppUsesNonExemptEncryption
LSApplicationQueriesSchemes
diff --git a/ios/ExpensifyCashTests/Info.plist b/ios/ExpensifyCashTests/Info.plist
index 403d6368bd7f..f09a35ebe0c0 100644
--- a/ios/ExpensifyCashTests/Info.plist
+++ b/ios/ExpensifyCashTests/Info.plist
@@ -15,10 +15,10 @@
CFBundlePackageType
BNDL
CFBundleShortVersionString
- 1.0.61
+ 1.0.63
CFBundleSignature
????
CFBundleVersion
- 1.0.61.0
+ 1.0.63.0
diff --git a/package-lock.json b/package-lock.json
index da11ce8b7825..654b62486c5f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "expensify.cash",
- "version": "1.0.61-0",
+ "version": "1.0.63-0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -2393,9 +2393,9 @@
}
},
"@electron/get": {
- "version": "1.12.2",
- "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.12.2.tgz",
- "integrity": "sha512-vAuHUbfvBQpYTJ5wB7uVIDq5c/Ry0fiTBMs7lnEYAo/qXXppIVcWdfBr57u6eRnKdVso7KSiH6p/LbQAG6Izrg==",
+ "version": "1.12.4",
+ "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.12.4.tgz",
+ "integrity": "sha512-6nr9DbJPUR9Xujw6zD3y+rS95TyItEVM0NVjt1EehY2vUWfIgPiIPVHxCvaTS0xr2B+DRxovYVKbuOWqC35kjg==",
"dev": true,
"requires": {
"debug": "^4.1.1",
@@ -2405,7 +2405,7 @@
"global-tunnel-ng": "^2.7.1",
"got": "^9.6.0",
"progress": "^2.0.3",
- "sanitize-filename": "^1.6.2",
+ "semver": "^6.2.0",
"sumchecker": "^3.0.1"
},
"dependencies": {
@@ -2429,6 +2429,12 @@
"graceful-fs": "^4.1.6"
}
},
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ },
"universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
@@ -15325,9 +15331,9 @@
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
},
"boolean": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.0.1.tgz",
- "integrity": "sha512-HRZPIjPcbwAVQvOTxR4YE3o8Xs98NqbbL1iEZDCz7CL8ql0Lt5iOyJFxfnAB0oFs8Oh02F/lLlg30Mexv46LjA==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.1.0.tgz",
+ "integrity": "sha512-K6r5tvO1ykeYerI7jIyTvSFw2l6D6DzqkljGj2E2uyYAAdDo2SV4qGJIV75cHIQpTFyb6BB0BEHiDdDrFsNI+g==",
"dev": true,
"optional": true
},
@@ -16800,9 +16806,9 @@
}
},
"config-chain": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz",
- "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==",
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
+ "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
"dev": true,
"optional": true,
"requires": {
@@ -18526,9 +18532,9 @@
}
},
"electron": {
- "version": "9.3.2",
- "resolved": "https://registry.npmjs.org/electron/-/electron-9.3.2.tgz",
- "integrity": "sha512-0lleEf9msAXGDi2GukAuiGdw3VDgSTlONOnJgqDEz1fuSEVsXz5RX+hNPKDsVDerLTFg/C34RuJf4LwHvkKcBA==",
+ "version": "11.4.8",
+ "resolved": "https://registry.npmjs.org/electron/-/electron-11.4.8.tgz",
+ "integrity": "sha512-NrxlDZN1sWiDCWWOm5aX+tPGtiLgsCUwNqNFP3eJfY+RPdYLsxYRJDFa1vc4GcuCZEp9kZusINjmpPWsvJdspQ==",
"dev": true,
"requires": {
"@electron/get": "^1.0.1",
@@ -18537,9 +18543,9 @@
},
"dependencies": {
"@types/node": {
- "version": "12.12.68",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.68.tgz",
- "integrity": "sha512-3RW2s24ewB7F9dAHvgb9FRvNHn6nO9IK6Eaknbz7HTOe2a5GVne5XbUh5+YA+kcCn67glyHhClUUdFP73LWrgQ==",
+ "version": "12.20.14",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.14.tgz",
+ "integrity": "sha512-iFJOS5Q470FF+r4Ol2pSley7/wCNVqf+jgjhtxLLaJcDs+To2iCxlXIkJXrGLD9w9G/oJ9ibySu7z92DCwr7Pg==",
"dev": true
}
}
@@ -19038,9 +19044,9 @@
"integrity": "sha512-8zDbrc7ocusTL1ZGmvgy0cTwdyCaM7sGZoYLRmnWJalLQzmftDtce+uDU91gafOTo9MCtgjSIxyMv/F4+Hcchw=="
},
"env-paths": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz",
- "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==",
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+ "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
"dev": true
},
"envinfo": {
@@ -22266,9 +22272,9 @@
}
},
"global-agent": {
- "version": "2.1.12",
- "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-2.1.12.tgz",
- "integrity": "sha512-caAljRMS/qcDo69X9BfkgrihGUgGx44Fb4QQToNQjsiWh+YlQ66uqYVAdA8Olqit+5Ng0nkz09je3ZzANMZcjg==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-2.2.0.tgz",
+ "integrity": "sha512-+20KpaW6DDLqhG7JDiJpD1JvNvb8ts+TNl7BPOYcURqCrXqnN1Vf+XVOrkKJAFPqfX+oEhsdzOj1hLWkBTdNJg==",
"dev": true,
"optional": true,
"requires": {
@@ -22282,9 +22288,9 @@
},
"dependencies": {
"core-js": {
- "version": "3.6.5",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz",
- "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==",
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.13.1.tgz",
+ "integrity": "sha512-JqveUc4igkqwStL2RTRn/EPFGBOfEZHxJl/8ej1mXJR75V3go2mFF4bmUYkEIT1rveHKnkUlcJX/c+f1TyIovQ==",
"dev": true,
"optional": true
},
@@ -23118,10 +23124,9 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"ini": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
- "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
- "dev": true
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
},
"inline-style-parser": {
"version": "0.1.1",
@@ -30482,7 +30487,8 @@
"normalize-url": {
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz",
- "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA=="
+ "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==",
+ "dev": true
},
"npm-conf": {
"version": "1.1.3",
diff --git a/package.json b/package.json
index 40efa81d9eb7..e6ea00966653 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "expensify.cash",
- "version": "1.0.61-0",
+ "version": "1.0.63-0",
"author": "Expensify, Inc.",
"homepage": "https://expensify.cash",
"description": "Expensify.cash is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
@@ -133,7 +133,7 @@
"css-loader": "^5.2.4",
"detox": "^17.8.3",
"diff-so-fancy": "^1.3.0",
- "electron": "^9.2.0",
+ "electron": "^11.4.8",
"electron-builder": "^22.8.0",
"electron-notarize": "^1.0.0",
"electron-reloader": "^1.2.0",
diff --git a/src/CONST.js b/src/CONST.js
index 9b573e97e394..ccdbe11abab4 100755
--- a/src/CONST.js
+++ b/src/CONST.js
@@ -10,6 +10,7 @@ const CONST = {
ALL: 'all',
CHRONOS_IN_CASH: 'chronosInCash',
IOU: 'IOU',
+ PAY_WITH_EXPENSIFY: 'payWithExpensify',
},
BUTTON_STATES: {
DEFAULT: 'default',
@@ -211,6 +212,7 @@ const CONST = {
// not be changed.
PAYMENT_TYPE: {
ELSEWHERE: 'Elsewhere',
+ EXPENSIFY: 'Expensify',
PAYPAL_ME: 'PayPal.me',
VENMO: 'Venmo',
},
diff --git a/src/ONYXKEYS.js b/src/ONYXKEYS.js
index 84f7dbad828d..d7fcd65074f3 100755
--- a/src/ONYXKEYS.js
+++ b/src/ONYXKEYS.js
@@ -81,6 +81,7 @@ export default {
REPORT_ACTIONS_DRAFTS: 'reportActionsDrafts_',
REPORT_USER_IS_TYPING: 'reportUserIsTyping_',
REPORT_IOUS: 'reportIOUs_',
+ POLICY: 'policy_',
},
// Indicates which locale should be used
diff --git a/src/components/FAB/FAB.js b/src/components/FAB/FAB.js
index e6f675242ab8..635dc6a86d99 100644
--- a/src/components/FAB/FAB.js
+++ b/src/components/FAB/FAB.js
@@ -59,6 +59,8 @@ class FAB extends PureComponent {
return (
(
(
@@ -21,5 +25,6 @@ const GrowlNotificationContainer = ({children, translateY}) => (
);
GrowlNotificationContainer.propTypes = propTypes;
+GrowlNotificationContainer.displayName = 'GrowlNotificationContainer';
export default GrowlNotificationContainer;
diff --git a/src/components/HeaderWithCloseButton.js b/src/components/HeaderWithCloseButton.js
index 45930710da7b..a56dac010ba0 100755
--- a/src/components/HeaderWithCloseButton.js
+++ b/src/components/HeaderWithCloseButton.js
@@ -7,6 +7,8 @@ import styles from '../styles/styles';
import Header from './Header';
import Icon from './Icon';
import {Close, Download, BackArrow} from './Icon/Expensicons';
+import compose from '../libs/compose';
+import withLocalize, {withLocalizePropTypes} from './withLocalize';
const propTypes = {
/** Title of the Header */
@@ -29,6 +31,8 @@ const propTypes = {
/** Whether we should show a download button */
shouldShowDownloadButton: PropTypes.bool,
+
+ ...withLocalizePropTypes,
};
const defaultProps = {
@@ -76,6 +80,8 @@ const HeaderWithCloseButton = props => (
@@ -88,4 +94,4 @@ HeaderWithCloseButton.propTypes = propTypes;
HeaderWithCloseButton.defaultProps = defaultProps;
HeaderWithCloseButton.displayName = 'HeaderWithCloseButton';
-export default HeaderWithCloseButton;
+export default compose(withLocalize)(HeaderWithCloseButton);
diff --git a/src/components/IOUConfirmationList.js b/src/components/IOUConfirmationList.js
index a7ef2f9ac307..0dcecf63a326 100755
--- a/src/components/IOUConfirmationList.js
+++ b/src/components/IOUConfirmationList.js
@@ -252,6 +252,7 @@ class IOUConfirmationList extends Component {
}]}
sections={this.getSections()}
disableArrowKeysActions
+ disableRowInteractivity
hideAdditionalOptionStates
forceTextUnreadStyle
canSelectMultipleOptions={this.props.hasMultipleParticipants}
diff --git a/src/components/OptionsList.js b/src/components/OptionsList.js
index 96c06425fbb9..9f21639d82bd 100644
--- a/src/components/OptionsList.js
+++ b/src/components/OptionsList.js
@@ -74,6 +74,9 @@ const propTypes = {
/** Toggle between compact and default view of the option */
optionMode: PropTypes.oneOf(['compact', 'default']),
+
+ /** Whether to disable the interactivity of the list's option row(s) */
+ disableRowInteractivity: PropTypes.bool,
};
const defaultProps = {
@@ -94,6 +97,7 @@ const defaultProps = {
innerRef: null,
showTitleTooltip: false,
optionMode: undefined,
+ disableRowInteractivity: false,
};
class OptionsList extends Component {
@@ -170,6 +174,7 @@ class OptionsList extends Component {
showSelectedState={this.props.canSelectMultipleOptions}
hideAdditionalOptionStates={this.props.hideAdditionalOptionStates}
forceTextUnreadStyle={this.props.forceTextUnreadStyle}
+ disableRowInteractivity={this.props.disableRowInteractivity}
/>
);
}
diff --git a/src/components/ReportActionItemIOUAction.js b/src/components/ReportActionItemIOUAction.js
index d7332d2f0ec1..6921735155af 100644
--- a/src/components/ReportActionItemIOUAction.js
+++ b/src/components/ReportActionItemIOUAction.js
@@ -1,7 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
-import lodashGet from 'lodash/get';
import ONYXKEYS from '../ONYXKEYS';
import ReportActionItemIOUQuote from './ReportActionItemIOUQuote';
import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes';
@@ -44,22 +43,22 @@ const defaultProps = {
const ReportActionItemIOUAction = ({
action,
chatReportID,
- chatReport,
iouReport,
isMostRecentIOUReportAction,
}) => {
const launchDetailsModal = () => {
Navigation.navigate(ROUTES.getIouDetailsRoute(chatReportID, action.originalMessage.IOUReportID));
};
- const hasMultipleParticipants = lodashGet(chatReport, 'participants', []).length >= 2;
return (
<>
- {isMostRecentIOUReportAction && (iouReport.hasOutstandingIOU) && (
+ {isMostRecentIOUReportAction
+ && iouReport.hasOutstandingIOU
+ && Boolean(action.originalMessage.IOUReportID) && (
} participantNames
+ * @param {Set} [participantNames]
* @returns {Boolean}
*/
-function isSearchStringMatch(searchValue, searchText, participantNames) {
+function isSearchStringMatch(searchValue, searchText, participantNames = new Set()) {
const searchWords = searchValue
.replace(/,/g, ' ')
.split(' ')
diff --git a/src/libs/Permissions.js b/src/libs/Permissions.js
index f554fa45bb43..dc9f24f007cc 100644
--- a/src/libs/Permissions.js
+++ b/src/libs/Permissions.js
@@ -32,7 +32,15 @@ function canUseIOU() {
return _.contains(betas, CONST.BETAS.IOU) || canUseAllBetas();
}
+/**
+ * @returns {Boolean}
+ */
+function canUsePayWithExpensify() {
+ return _.contains(betas, CONST.BETAS.PAY_WITH_EXPENSIFY) || canUseAllBetas();
+}
+
export default {
canUseChronos,
canUseIOU,
+ canUsePayWithExpensify,
};
diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js
index 12bda38ca7f0..56a723c5fd56 100644
--- a/src/libs/actions/IOU.js
+++ b/src/libs/actions/IOU.js
@@ -2,9 +2,11 @@ import Onyx from 'react-native-onyx';
import _ from 'underscore';
import CONST from '../../CONST';
import ONYXKEYS from '../../ONYXKEYS';
+import ROUTES from '../../ROUTES';
import * as API from '../API';
import {getSimplifiedIOUReport, fetchChatReportsByIDs, fetchIOUReportByIDAndUpdateChatReport} from './Report';
import openURLInNewTab from '../openURLInNewTab';
+import Navigation from '../Navigation/Navigation';
/**
* Retrieve the users preferred currency
@@ -73,7 +75,10 @@ function getIOUReportsForNewTransaction(requestParams) {
function createIOUTransaction(params) {
Onyx.merge(ONYXKEYS.IOU, {loading: true, creatingIOUTransaction: true, error: false});
API.CreateIOUTransaction(params)
- .then(data => getIOUReportsForNewTransaction([data]));
+ .then((data) => {
+ getIOUReportsForNewTransaction([data]);
+ Navigation.navigate(ROUTES.getReportRoute(data.chatReportID));
+ });
}
/**
@@ -87,14 +92,18 @@ function createIOUTransaction(params) {
function createIOUSplit(params) {
Onyx.merge(ONYXKEYS.IOU, {loading: true, creatingIOUTransaction: true, error: false});
+ let chatReportID;
API.CreateChatReport({
emailList: params.splits.map(participant => participant.email).join(','),
})
- .then(data => API.CreateIOUSplit({
- ...params,
- splits: JSON.stringify(params.splits),
- reportID: data.reportID,
- }))
+ .then((data) => {
+ chatReportID = data.reportID;
+ return API.CreateIOUSplit({
+ ...params,
+ splits: JSON.stringify(params.splits),
+ reportID: data.reportID,
+ });
+ })
.then((data) => {
// This data needs to go from this:
// {reportIDList: [1, 2], chatReportIDList: [3, 4]}
@@ -110,6 +119,7 @@ function createIOUSplit(params) {
});
}
getIOUReportsForNewTransaction(reportParams);
+ Navigation.navigate(ROUTES.getReportRoute(chatReportID));
});
}
@@ -186,10 +196,10 @@ function payIOUReport({
chatReportID, reportID, paymentMethodType, amount, currency, submitterPhoneNumber, submitterPayPalMeAddress,
}) {
Onyx.merge(ONYXKEYS.IOU, {loading: true, error: false});
- API.PayIOU({
- reportID,
- paymentMethodType,
- })
+ const payIOUPromise = paymentMethodType === CONST.IOU.PAYMENT_TYPE.EXPENSIFY
+ ? API.PayWithWallet({reportID})
+ : API.PayIOU({reportID, paymentMethodType});
+ payIOUPromise
.then((response) => {
if (response.jsonCode !== 200) {
throw new Error(response.message);
@@ -204,7 +214,7 @@ function payIOUReport({
fetchIOUReportByIDAndUpdateChatReport(reportID, chatReportID);
// Once we have successfully paid the IOU we will transfer the user to their platform of choice if they have
- // selected something other than a manual settlement e.g. Venmo or PayPal.me
+ // selected something other than a manual settlement or Expensify Wallet e.g. Venmo or PayPal.me
if (paymentMethodType === CONST.IOU.PAYMENT_TYPE.PAYPAL_ME) {
openURLInNewTab(buildPayPalPaymentUrl(amount, submitterPayPalMeAddress, currency));
} else if (paymentMethodType === CONST.IOU.PAYMENT_TYPE.VENMO) {
diff --git a/src/libs/actions/Policy.js b/src/libs/actions/Policy.js
new file mode 100644
index 000000000000..af04011c3c94
--- /dev/null
+++ b/src/libs/actions/Policy.js
@@ -0,0 +1,36 @@
+import _ from 'underscore';
+import Onyx from 'react-native-onyx';
+import {GetPolicySummaryList} from '../API';
+import ONYXKEYS from '../../ONYXKEYS';
+
+/**
+ * Takes a full policy summary that is returned from the policySummaryList and simplifies it so we are only storing
+ * the pieces of data that we need to in Onyx
+ *
+ * @param {Object} fullPolicy
+ * @param {String} fullPolicy.name
+ * @returns {Object}
+ */
+function getSimplifiedPolicyObject(fullPolicy) {
+ return {
+ name: fullPolicy.name,
+ };
+}
+
+function getPolicySummaries() {
+ GetPolicySummaryList()
+ .then((data) => {
+ if (data.jsonCode === 200) {
+ const policyDataToStore = _.reduce(data.policySummaryList, (memo, policy) => ({
+ ...memo,
+ [`${ONYXKEYS.COLLECTION.POLICY}${policy.id}`]: getSimplifiedPolicyObject(policy),
+ }), {});
+ Onyx.mergeCollection(ONYXKEYS.COLLECTION.POLICY, policyDataToStore);
+ }
+ });
+}
+
+export {
+ // eslint-disable-next-line import/prefer-default-export
+ getPolicySummaries,
+};
diff --git a/src/pages/home/sidebar/OptionRow.js b/src/pages/home/sidebar/OptionRow.js
index 5b85d6cce96a..c75b7dd5e2f1 100644
--- a/src/pages/home/sidebar/OptionRow.js
+++ b/src/pages/home/sidebar/OptionRow.js
@@ -56,9 +56,12 @@ const propTypes = {
/** Toggle between compact and default view */
mode: PropTypes.oneOf(['compact', 'default']),
- // Whether this option should be disabled
+ /** Whether this option should be disabled */
isDisabled: PropTypes.bool,
+ /** Whether to disable the interactivity of this row */
+ disableRowInteractivity: PropTypes.bool,
+
...withLocalizePropTypes,
};
@@ -74,6 +77,7 @@ const defaultProps = {
onSelectRow: null,
isDisabled: false,
optionIsFocused: false,
+ disableRowInteractivity: false,
};
const OptionRow = ({
@@ -89,6 +93,7 @@ const OptionRow = ({
showTitleTooltip,
isDisabled,
mode,
+ disableRowInteractivity,
toLocalPhone,
}) => {
const textStyle = optionIsFocused
@@ -139,6 +144,7 @@ const OptionRow = ({
{hovered => (
onSelectRow(option)}
+ disabled={disableRowInteractivity}
activeOpacity={0.8}
style={[
styles.flexRow,
diff --git a/src/pages/home/sidebar/SidebarLinks.js b/src/pages/home/sidebar/SidebarLinks.js
index 30175cf49d98..f2308ee4702c 100644
--- a/src/pages/home/sidebar/SidebarLinks.js
+++ b/src/pages/home/sidebar/SidebarLinks.js
@@ -20,6 +20,8 @@ import KeyboardSpacer from '../../../components/KeyboardSpacer';
import CONST from '../../../CONST';
import {participantPropTypes} from './optionPropTypes';
import themeColors from '../../../styles/themes/default';
+import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
+
const propTypes = {
/** Toggles the navigation menu open and closed */
@@ -74,6 +76,8 @@ const propTypes = {
// Whether we are syncing app data
isSyncingData: PropTypes.bool,
+
+ ...withLocalizePropTypes,
};
const defaultProps = {
@@ -132,16 +136,22 @@ class SidebarLinks extends React.Component {
>
diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js
index 9c6c313f07a6..c0e8548fc724 100644
--- a/src/pages/iou/IOUDetailsModal.js
+++ b/src/pages/iou/IOUDetailsModal.js
@@ -21,6 +21,7 @@ import CONST from '../../CONST';
import CreateMenu from '../../components/CreateMenu';
import isAppInstalled from '../../libs/isAppInstalled';
import Button from '../../components/Button';
+import Permissions from '../../libs/Permissions';
const propTypes = {
/** URL Route params */
@@ -102,6 +103,7 @@ class IOUDetailsModal extends Component {
this.isComponentMounted = true;
fetchIOUReportByID(this.props.route.params.iouReportID, this.props.route.params.chatReportID);
this.addVenmoPaymentOptionIfAvailable();
+ this.addExpensifyPaymentOptionIfAvailable();
}
componentWillUnmount() {
@@ -173,10 +175,28 @@ class IOUDetailsModal extends Component {
});
}
+ /**
+ * Checks to see if we can use Expensify Wallet to pay for this IOU report.
+ * The IOU report currency must be USD.
+ */
+ addExpensifyPaymentOptionIfAvailable() {
+ if (lodashGet(this.props, 'iouReport.currency') !== CONST.CURRENCY.USD
+ || !Permissions.canUsePayWithExpensify()) {
+ return;
+ }
+
+ // Make it the first payment option and set it as the default.
+ this.setState(prevState => ({
+ paymentOptions: [CONST.IOU.PAYMENT_TYPE.EXPENSIFY, ...prevState.paymentOptions],
+ paymentType: CONST.IOU.PAYMENT_TYPE.EXPENSIFY,
+ }));
+ }
+
render() {
const sessionEmail = lodashGet(this.props.session, 'email', null);
const reportIsLoading = _.isUndefined(this.props.iouReport);
const paymentTypeTextOptions = {
+ [CONST.IOU.PAYMENT_TYPE.EXPENSIFY]: this.props.translate('iou.settleExpensify'),
[CONST.IOU.PAYMENT_TYPE.VENMO]: this.props.translate('iou.settleVenmo'),
[CONST.IOU.PAYMENT_TYPE.PAYPAL_ME]: this.props.translate('iou.settlePaypalMe'),
[CONST.IOU.PAYMENT_TYPE.ELSEWHERE]: this.props.translate('iou.settleElsewhere'),
diff --git a/src/pages/iou/IOUModal.js b/src/pages/iou/IOUModal.js
index 0c1e0982f96c..4cf2600c2c77 100755
--- a/src/pages/iou/IOUModal.js
+++ b/src/pages/iou/IOUModal.js
@@ -294,6 +294,8 @@ class IOUModal extends Component {
Navigation.dismissModal()}
style={[styles.touchableButtonImage]}
+ accessibilityRole="button"
+ accessibilityLabel={this.props.translate('common.close')}
>