diff --git a/.well-known/apple-app-site-association b/.well-known/apple-app-site-association index 28ee9ee23fbd..fcd7138da7e6 100644 --- a/.well-known/apple-app-site-association +++ b/.well-known/apple-app-site-association @@ -43,6 +43,10 @@ { "/": "/statements/*", "comment": "Wallet statements" + }, + { + "/": "/concierge/*", + "comment": "Concierge" } ] } diff --git a/android/app/build.gradle b/android/app/build.gradle index 225a526c3acb..1cdaa76a4443 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -156,8 +156,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001020600 - versionName "1.2.6-0" + versionCode 1001021000 + versionName "1.2.10-0" buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() if (isNewArchitectureEnabled()) { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 5b4e43c86366..eb109cc8a30e 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -61,6 +61,7 @@ + @@ -72,6 +73,7 @@ + diff --git a/desktop/dev.js b/desktop/dev.js new file mode 100644 index 000000000000..43a2122f6846 --- /dev/null +++ b/desktop/dev.js @@ -0,0 +1,9 @@ +// Development entry point of the desktop app with support for live reload +// We keep this file (dev.js) out Webpack bundling process and provide it raw to Electron +// Reloading Electron on change requires some native Node dependencies which cannot be +// easily bundled with webpack, and we don't really need them bundled for staging/prod +// We are free to load any bundled code from this file, and we load the bundled main.js from dist + +// The `dist` folder is not part of the source and does not exist until we build or start Desktop +// eslint-disable-next-line import/extensions +require('./dist/main'); diff --git a/desktop/start.js b/desktop/start.js index 79e62d7187ed..815be335c46f 100644 --- a/desktop/start.js +++ b/desktop/start.js @@ -9,25 +9,35 @@ portfinder.getPortPromise({ port: basePort, }).then((port) => { const devServer = `webpack-dev-server --config config/webpack/webpack.dev.js --port ${port} --env platform=desktop`; - const buildMain = 'webpack --config config/webpack/webpack.desktop.js --config-name desktop-main --mode=development'; + const buildMain = 'webpack watch --config config/webpack/webpack.desktop.js --config-name desktop-main --mode=development'; + + const env = { + PORT: port, + NODE_ENV: 'development', + }; const processes = [ + { + command: buildMain, + name: 'Main', + prefixColor: 'blue.dim', + env, + }, { command: devServer, name: 'Renderer', prefixColor: 'red.dim', + env, }, { - command: `${buildMain} && wait-port localhost:${port} && electron desktop/dist/main.js`, - name: 'Main', + command: `wait-port localhost:${port} && npx electronmon ./desktop/dev.js`, + name: 'Electron', prefixColor: 'cyan.dim', - env: { - PORT: port, - NODE_ENV: 'development', - }, + env, }, ]; - concurrently(processes, { + + return concurrently(processes, { inputStream: process.stdin, prefix: 'name', diff --git a/docs/_sass/_main.scss b/docs/_sass/_main.scss index 95a68ce345e1..2dd52ac40375 100644 --- a/docs/_sass/_main.scss +++ b/docs/_sass/_main.scss @@ -61,6 +61,7 @@ body { height: 100%; min-height: 100%; background: $color-white; + overflow: auto; } hr { diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index b9d8e2ec5dfb..d442f5fc21cd 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.2.6 + 1.2.10 CFBundleSignature ???? CFBundleURLTypes @@ -30,7 +30,7 @@ CFBundleVersion - 1.2.6.0 + 1.2.10.0 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index ebb0f6355386..319b0779092c 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.2.6 + 1.2.10 CFBundleSignature ???? CFBundleVersion - 1.2.6.0 + 1.2.10.0 diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 4bb2ef56eac2..4475434729bc 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -217,9 +217,9 @@ PODS: - nanopb/encode (= 2.30908.0) - nanopb/decode (2.30908.0) - nanopb/encode (2.30908.0) - - Onfido (25.1.0) - - onfido-react-native-sdk (5.4.0): - - Onfido (= 25.1.0) + - Onfido (26.0.1) + - onfido-react-native-sdk (6.0.0): + - Onfido (= 26.0.1) - React - OpenSSL-Universal (1.1.1100) - Permission-LocationAccuracy (3.6.1): @@ -941,8 +941,8 @@ SPEC CHECKSUMS: libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 libwebp: 60305b2e989864154bd9be3d772730f08fc6a59c nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 - Onfido: a1645279c7b6ca8de5bb95d27af53c523ffd731c - onfido-react-native-sdk: cfdfd1d4acf53e469a82b9684f92982d39744bb8 + Onfido: 91935f0dcee9a6647fdb386fa87cb4af9d9a3c70 + onfido-react-native-sdk: bc72114ac430a2636cd2f02972ddf5b8b3b397c6 OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c Permission-LocationAccuracy: 76df17de5c6b8bc2eee34e61ee92cdd7a864c73d Permission-LocationAlways: 8d99b025c9f73c696e0cdb367e42525f2e9a26f2 diff --git a/jest/setup.js b/jest/setup.js index 10918039ae25..549b02eb2546 100644 --- a/jest/setup.js +++ b/jest/setup.js @@ -75,3 +75,14 @@ jest.mock('../src/components/Icon/Expensicons', () => { return {...prev, [key]: fn}; }, {}); }); + +// Set up manual mocks for any Logging methods that are supposed hit the 'server', +// this is needed because before, the Logging queue would get flushed while tests were running, +// causing unexpected calls to HttpUtils.xhr() which would cause mock mismatches and flaky tests. +/* eslint-disable no-console */ +jest.mock('../src/libs/Log', () => ({ + info: message => console.log(`[info] ${message} (mocked)`), + alert: message => console.log(`[alert] ${message} (mocked)`), + warn: message => console.log(`[warn] ${message} (mocked)`), + hmmm: message => console.log(`[hmmm] ${message} (mocked)`), +})); diff --git a/package-lock.json b/package-lock.json index 0f4142fb61b0..589a57e2f85b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.2.6-0", + "version": "1.2.10-0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.2.6-0", + "version": "1.2.10-0", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -16,7 +16,7 @@ "@formatjs/intl-numberformat": "^6.2.5", "@formatjs/intl-pluralrules": "^4.0.13", "@oguzhnatly/react-native-image-manipulator": "github:Expensify/react-native-image-manipulator#c5f654fc9d0ad7cc5b89d50b34ecf8b0e3f4d050", - "@onfido/react-native-sdk": "github:Expensify/react-native-sdk#d70947f263b2e59899d82f8d1ef3841d053927ff", + "@onfido/react-native-sdk": "6.0.0", "@pieter-pot/react-native-fast-image": "8.5.11", "@react-native-async-storage/async-storage": "^1.17.10", "@react-native-community/cameraroll": "git+https://github.com/react-native-cameraroll/react-native-cameraroll.git#3f0aed96db68e134f199171c7b06c1b4d6cb382b", @@ -129,7 +129,6 @@ "electron": "^17.4.11", "electron-builder": "23.5.0", "electron-notarize": "^1.2.1", - "electron-reloader": "^1.2.1", "eslint": "^7.6.0", "eslint-config-expensify": "2.0.30", "eslint-loader": "^4.0.2", @@ -4154,12 +4153,11 @@ } }, "node_modules/@onfido/react-native-sdk": { - "version": "5.4.0", - "resolved": "git+ssh://git@github.com/Expensify/react-native-sdk.git#d70947f263b2e59899d82f8d1ef3841d053927ff", - "integrity": "sha512-xqQ9ERZhnJPkWVaaPBaoeBQ8UMHVy/cCPxEzow5rBuPuzmA1KYvrbu1FUqTMriUhVnLFR57TDCyZR8s/+u/eRg==", - "license": "MIT", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@onfido/react-native-sdk/-/react-native-sdk-6.0.0.tgz", + "integrity": "sha512-TJWeiEh0Vil37/Xm9H+uiOpiuYD2NhOhixdvaAkohTzLI3uUNiG7bph66jCUa+SpqFURQdCZMYWFwHwj2nXs2Q==", "peerDependencies": { - "react": ">=16.8.1 <=18.x.x", + "react": ">=16.8.1", "react-native": ">=0.60.0-rc.0 <1.0.x" } }, @@ -20430,18 +20428,6 @@ "url": "https://opencollective.com/date-fns" } }, - "node_modules/date-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz", - "integrity": "sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==", - "dev": true, - "dependencies": { - "time-zone": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/dayjs": { "version": "1.11.5", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.5.tgz", @@ -21378,12 +21364,6 @@ "node": ">=12" } }, - "node_modules/electron-is-dev": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-1.2.0.tgz", - "integrity": "sha512-R1oD5gMBPS7PVU8gJwH6CtT0e6VSoD0+SzSnYpNm+dBkcijgA+K7VAMHDfnRq/lkKPZArpzplTW6jfiMYosdzw==", - "dev": true - }, "node_modules/electron-notarize": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/electron-notarize/-/electron-notarize-1.2.1.tgz", @@ -21545,92 +21525,6 @@ "node": ">=8" } }, - "node_modules/electron-reloader": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/electron-reloader/-/electron-reloader-1.2.3.tgz", - "integrity": "sha512-aDnACAzNg0QvQhzw7LYOx/nVS10mEtbuG6M0QQvNQcLnJEwFs6is+EGRCnM+KQlQ4KcTbdwnt07nd7ZjHpY4iw==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "chokidar": "^3.5.0", - "date-time": "^3.1.0", - "electron-is-dev": "^1.2.0", - "find-up": "^5.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/electron-reloader/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/electron-reloader/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/electron-reloader/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/electron-reloader/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/electron-reloader/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/electron-reloader/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/electron-to-chromium": { "version": "1.4.223", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.223.tgz", @@ -40373,15 +40267,6 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, - "node_modules/time-zone": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", - "integrity": "sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/timers-browserify": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", @@ -46067,9 +45952,9 @@ } }, "@onfido/react-native-sdk": { - "version": "git+ssh://git@github.com/Expensify/react-native-sdk.git#d70947f263b2e59899d82f8d1ef3841d053927ff", - "integrity": "sha512-xqQ9ERZhnJPkWVaaPBaoeBQ8UMHVy/cCPxEzow5rBuPuzmA1KYvrbu1FUqTMriUhVnLFR57TDCyZR8s/+u/eRg==", - "from": "@onfido/react-native-sdk@github:Expensify/react-native-sdk#d70947f263b2e59899d82f8d1ef3841d053927ff", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@onfido/react-native-sdk/-/react-native-sdk-6.0.0.tgz", + "integrity": "sha512-TJWeiEh0Vil37/Xm9H+uiOpiuYD2NhOhixdvaAkohTzLI3uUNiG7bph66jCUa+SpqFURQdCZMYWFwHwj2nXs2Q==", "requires": {} }, "@pieter-pot/react-native-fast-image": { @@ -58567,15 +58452,6 @@ "integrity": "sha512-dlLD5rKaKxpFdnjrs+5azHDFOPEu4ANy/LTh04A1DTzMM7qoajmKCBc8pkKRFT41CNzw+4gQh79X5C+Jq27HAw==", "dev": true }, - "date-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz", - "integrity": "sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==", - "dev": true, - "requires": { - "time-zone": "^1.0.0" - } - }, "dayjs": { "version": "1.11.5", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.5.tgz", @@ -59323,12 +59199,6 @@ } } }, - "electron-is-dev": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-1.2.0.tgz", - "integrity": "sha512-R1oD5gMBPS7PVU8gJwH6CtT0e6VSoD0+SzSnYpNm+dBkcijgA+K7VAMHDfnRq/lkKPZArpzplTW6jfiMYosdzw==", - "dev": true - }, "electron-notarize": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/electron-notarize/-/electron-notarize-1.2.1.tgz", @@ -59456,70 +59326,6 @@ } } }, - "electron-reloader": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/electron-reloader/-/electron-reloader-1.2.3.tgz", - "integrity": "sha512-aDnACAzNg0QvQhzw7LYOx/nVS10mEtbuG6M0QQvNQcLnJEwFs6is+EGRCnM+KQlQ4KcTbdwnt07nd7ZjHpY4iw==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "chokidar": "^3.5.0", - "date-time": "^3.1.0", - "electron-is-dev": "^1.2.0", - "find-up": "^5.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "electron-to-chromium": { "version": "1.4.223", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.223.tgz", @@ -73964,12 +73770,6 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, - "time-zone": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", - "integrity": "sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==", - "dev": true - }, "timers-browserify": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", diff --git a/package.json b/package.json index 0ea810cf9c41..72db7454f40d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.2.6-0", + "version": "1.2.10-0", "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.", @@ -43,7 +43,7 @@ "@formatjs/intl-numberformat": "^6.2.5", "@formatjs/intl-pluralrules": "^4.0.13", "@oguzhnatly/react-native-image-manipulator": "github:Expensify/react-native-image-manipulator#c5f654fc9d0ad7cc5b89d50b34ecf8b0e3f4d050", - "@onfido/react-native-sdk": "github:Expensify/react-native-sdk#d70947f263b2e59899d82f8d1ef3841d053927ff", + "@onfido/react-native-sdk": "6.0.0", "@pieter-pot/react-native-fast-image": "8.5.11", "@react-native-async-storage/async-storage": "^1.17.10", "@react-native-community/cameraroll": "git+https://github.com/react-native-cameraroll/react-native-cameraroll.git#3f0aed96db68e134f199171c7b06c1b4d6cb382b", @@ -156,7 +156,6 @@ "electron": "^17.4.11", "electron-builder": "23.5.0", "electron-notarize": "^1.2.1", - "electron-reloader": "^1.2.1", "eslint": "^7.6.0", "eslint-config-expensify": "2.0.30", "eslint-loader": "^4.0.2", diff --git a/src/CONST.js b/src/CONST.js index d497376b5a9e..76e6151a37f8 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -69,9 +69,6 @@ const CONST = { }, VERIFICATIONS: { ERROR_MESSAGE: 'verifications.errorMessage', - EXTERNAL_API_RESPONSES: 'verifications.externalApiResponses', - REQUESTOR_IDENTITY_ID: 'verifications.externalApiResponses.requestorIdentityID', - REQUESTOR_IDENTITY_ONFIDO: 'verifications.externalApiResponses.requestorIdentityOnfido', THROTTLED: 'verifications.throttled', }, FIELDS_TYPE: { @@ -351,8 +348,6 @@ const CONST = { HOMEPAGE_REPORTS_LOADED: 'homepage_reports_loaded', SWITCH_REPORT: 'switch_report', SIDEBAR_LOADED: 'sidebar_loaded', - PERSONAL_DETAILS_FORMATTED: 'personal_details_formatted', - SIDEBAR_LINKS_FILTER_REPORTS: 'sidebar_links_filter_reports', COLD: 'cold', REPORT_ACTION_ITEM_LAYOUT_DEBOUNCE_TIME: 1500, TOOLTIP_SENSE: 1000, @@ -418,7 +413,7 @@ const CONST = { FREQUENTLY_USED_EMOJIS: 'expensify_frequentlyUsedEmojis', }, DEFAULT_TIME_ZONE: {automatic: true, selected: 'America/Los_Angeles'}, - DEFAULT_ACCOUNT_DATA: {errors: null, success: '', loading: false}, + DEFAULT_ACCOUNT_DATA: {errors: null, success: '', isLoading: false}, APP_STATE: { ACTIVE: 'active', BACKGROUND: 'background', @@ -460,6 +455,7 @@ const CONST = { NUMBER_PAD: 'number-pad', DECIMAL_PAD: 'decimal-pad', VISIBLE_PASSWORD: 'visible-password', + EMAIL_ADDRESS: 'email-address', }, ATTACHMENT_SOURCE_ATTRIBUTE: 'data-expensify-source', diff --git a/src/ONYXKEYS.js b/src/ONYXKEYS.js index ab94c4dff47c..713dfa3df45a 100755 --- a/src/ONYXKEYS.js +++ b/src/ONYXKEYS.js @@ -5,6 +5,9 @@ export default { // Holds information about the users account that is logging in ACCOUNT: 'account', + // Holds the reportID for the report between the user and their account manager + ACCOUNT_MANAGER_REPORT_ID: 'accountManagerReportID', + // Boolean flag only true when first set NVP_IS_FIRST_TIME_NEW_EXPENSIFY_USER: 'isFirstTimeNewExpensifyUser', @@ -67,6 +70,9 @@ export default { // Contains the user's payPalMe address NVP_PAYPAL_ME_ADDRESS: 'nvp_paypalMeAddress', + // Contains the user's payPalMe data + PAYPAL: 'paypal', + // Contains the user preference for the LHN priority mode NVP_PRIORITY_MODE: 'nvp_priorityMode', @@ -79,6 +85,9 @@ export default { // Token needed to initialize Plaid link PLAID_LINK_TOKEN: 'plaidLinkToken', + // Token needed to initialize Onfido + ONFIDO_TOKEN: 'onfidoToken', + // Collection Keys COLLECTION: { REPORT: 'report_', diff --git a/src/components/AddPaymentMethodMenu.js b/src/components/AddPaymentMethodMenu.js index c450642df1c8..e71e42e1d15a 100644 --- a/src/components/AddPaymentMethodMenu.js +++ b/src/components/AddPaymentMethodMenu.js @@ -9,6 +9,8 @@ import CONST from '../CONST'; import withWindowDimensions from './withWindowDimensions'; import Permissions from '../libs/Permissions'; import PopoverMenu from './PopoverMenu'; +import paypalMeDataPropTypes from './paypalMeDataPropTypes'; +import * as BankAccounts from '../libs/actions/BankAccounts'; const propTypes = { isVisible: PropTypes.bool.isRequired, @@ -18,8 +20,8 @@ const propTypes = { left: PropTypes.number, }), - /** Username for PayPal.Me */ - payPalMeUsername: PropTypes.string, + /** Account details for PayPal.Me */ + payPalMeData: paypalMeDataPropTypes, /** Should we show the Paypal option */ shouldShowPaypal: PropTypes.bool, @@ -32,7 +34,7 @@ const propTypes = { const defaultProps = { anchorPosition: {}, - payPalMeUsername: '', + payPalMeData: {}, shouldShowPaypal: true, betas: [], }; @@ -47,7 +49,10 @@ const AddPaymentMethodMenu = props => ( { text: props.translate('common.bankAccount'), icon: Expensicons.Bank, - onSelected: () => props.onItemSelected(CONST.PAYMENT_METHODS.BANK_ACCOUNT), + onSelected: () => { + BankAccounts.clearPlaid(); + props.onItemSelected(CONST.PAYMENT_METHODS.BANK_ACCOUNT); + }, }, ...(Permissions.canUseWallet(props.betas) ? [{ text: props.translate('common.debitCard'), @@ -55,7 +60,7 @@ const AddPaymentMethodMenu = props => ( onSelected: () => props.onItemSelected(CONST.PAYMENT_METHODS.DEBIT_CARD), }, ] : []), - ...(props.shouldShowPaypal && !props.payPalMeUsername ? [{ + ...(props.shouldShowPaypal && !props.payPalMeData.description ? [{ text: props.translate('common.payPalMe'), icon: Expensicons.PayPal, onSelected: () => props.onItemSelected(CONST.PAYMENT_METHODS.PAYPAL), @@ -73,8 +78,8 @@ export default compose( withWindowDimensions, withLocalize, withOnyx({ - payPalMeUsername: { - key: ONYXKEYS.NVP_PAYPAL_ME_ADDRESS, + payPalMeData: { + key: ONYXKEYS.PAYPAL, }, betas: { key: ONYXKEYS.BETAS, diff --git a/src/components/AddPlaidBankAccount.js b/src/components/AddPlaidBankAccount.js index c0f926430b0c..96fa3dee7559 100644 --- a/src/components/AddPlaidBankAccount.js +++ b/src/components/AddPlaidBankAccount.js @@ -58,7 +58,7 @@ const defaultProps = { bankName: '', plaidAccessToken: '', bankAccounts: [], - loading: false, + isLoading: false, error: '', }, plaidLinkToken: '', @@ -77,21 +77,14 @@ class AddPlaidBankAccount extends React.Component { this.selectAccount = this.selectAccount.bind(this); this.getPlaidLinkToken = this.getPlaidLinkToken.bind(this); - - this.state = { - selectedIndex: undefined, - institution: {}, - }; } componentDidMount() { // If we're coming from Plaid OAuth flow then we need to reuse the existing plaidLinkToken - // Otherwise, clear the existing token and fetch a new one - if (this.props.receivedRedirectURI && this.props.plaidLinkOAuthToken) { + if ((this.props.receivedRedirectURI && this.props.plaidLinkOAuthToken) || !_.isEmpty(this.props.plaidData)) { return; } - BankAccounts.clearPlaid(); BankAccounts.openPlaidBankLogin(this.props.allowDebit, this.props.bankAccountID); } @@ -119,30 +112,30 @@ class AddPlaidBankAccount extends React.Component { /** * Triggered when user selects a Plaid bank account. - * @param {String} index + * @param {String} plaidAccountID */ - selectAccount(index) { - this.setState({selectedIndex: Number(index)}, () => { - const selectedPlaidBankAccount = this.getPlaidBankAccounts()[this.state.selectedIndex]; - selectedPlaidBankAccount.bankName = this.props.plaidData.bankName; - selectedPlaidBankAccount.plaidAccessToken = this.props.plaidData.plaidAccessToken; - this.props.onSelect({selectedPlaidBankAccount}); - }); + selectAccount(plaidAccountID) { + const selectedPlaidBankAccount = _.findWhere(this.getPlaidBankAccounts(), {plaidAccountID}); + selectedPlaidBankAccount.bankName = this.props.plaidData.bankName; + selectedPlaidBankAccount.plaidAccessToken = this.props.plaidData.plaidAccessToken; + this.props.onSelect({selectedPlaidBankAccount}); } render() { const plaidBankAccounts = this.getPlaidBankAccounts(); const token = this.getPlaidLinkToken(); - const options = _.map(plaidBankAccounts, (account, index) => ({ - value: index, label: `${account.addressName} ${account.mask}`, + const options = _.map(plaidBankAccounts, account => ({ + value: account.plaidAccountID, label: `${account.addressName} ${account.mask}`, })); - const {icon, iconSize} = getBankIcon(this.state.institution.name); + const institutionName = lodashGet(this.props, 'plaidData.institution.name', ''); + const selectedPlaidBankAccount = lodashGet(this.props, 'plaidData.selectedPlaidBankAccount', {}); + const {icon, iconSize} = getBankIcon(); // Plaid Link view if (!plaidBankAccounts.length) { return ( - {(!token || this.props.plaidData.loading) + {(!token || this.props.plaidData.isLoading) && ( @@ -159,7 +152,7 @@ class AddPlaidBankAccount extends React.Component { onSuccess={({publicToken, metadata}) => { Log.info('[PlaidLink] Success!'); BankAccounts.openPlaidBankAccountSelector(publicToken, metadata.institution.name, this.props.allowDebit); - this.setState({institution: metadata.institution}); + BankAccounts.updatePlaidData({institution: metadata.institution}); }} onError={(error) => { Log.hmmm('[PlaidLink] Error: ', error.message); @@ -187,18 +180,18 @@ class AddPlaidBankAccount extends React.Component { height={iconSize} width={iconSize} /> - {this.state.institution.name} + {institutionName} diff --git a/src/components/ArchivedReportFooter.js b/src/components/ArchivedReportFooter.js index bcb204c98369..c271b0f8c0bc 100644 --- a/src/components/ArchivedReportFooter.js +++ b/src/components/ArchivedReportFooter.js @@ -9,6 +9,7 @@ import compose from '../libs/compose'; import personalDetailsPropType from '../pages/personalDetailsPropType'; import ONYXKEYS from '../ONYXKEYS'; import * as ReportUtils from '../libs/ReportUtils'; +import reportPropTypes from '../pages/reportPropTypes'; const propTypes = { /** The reason this report was archived */ @@ -27,10 +28,7 @@ const propTypes = { }), /** The archived report */ - report: PropTypes.shape({ - /** The policy this report is attached to */ - policyID: PropTypes.string, - }).isRequired, + report: reportPropTypes.isRequired, /** Personal details of all users */ personalDetails: PropTypes.objectOf(personalDetailsPropType).isRequired, @@ -72,6 +70,7 @@ const ArchivedReportFooter = (props) => { policyName: `${ReportUtils.getPolicyName(props.report, props.policies)}`, })} shouldRenderHTML={archiveReason !== CONST.REPORT.ARCHIVE_REASON.DEFAULT} + shouldShowIcon /> ); }; diff --git a/src/components/AvatarCropModal/AvatarCropModal.js b/src/components/AvatarCropModal/AvatarCropModal.js index 8fb25102cef6..a7df10467272 100644 --- a/src/components/AvatarCropModal/AvatarCropModal.js +++ b/src/components/AvatarCropModal/AvatarCropModal.js @@ -266,8 +266,7 @@ const AvatarCropModal = (props) => { diff --git a/src/components/Banner.js b/src/components/Banner.js index 87808c22af24..3b5a8d66e3aa 100644 --- a/src/components/Banner.js +++ b/src/components/Banner.js @@ -1,6 +1,7 @@ import React, {memo} from 'react'; import PropTypes from 'prop-types'; -import {View} from 'react-native'; +import {View, Pressable} from 'react-native'; +import compose from '../libs/compose'; import Hoverable from './Hoverable'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; @@ -9,17 +10,45 @@ import Text from './Text'; import styles from '../styles/styles'; import * as StyleUtils from '../styles/StyleUtils'; import getButtonState from '../libs/getButtonState'; +import Tooltip from './Tooltip'; +import withLocalize, {withLocalizePropTypes} from './withLocalize'; const propTypes = { /** Text to display in the banner. */ text: PropTypes.string.isRequired, + /** Should this component render the left-aligned exclamation icon? */ + shouldShowIcon: PropTypes.bool, + + /** Should this component render a close button? */ + shouldShowCloseButton: PropTypes.bool, + /** Should this component render the text as HTML? */ shouldRenderHTML: PropTypes.bool, + + /** Callback called when the close button is pressed */ + onClose: PropTypes.func, + + /** Callback called when the message is pressed */ + onPress: PropTypes.func, + + // eslint-disable-next-line react/forbid-prop-types + containerStyles: PropTypes.arrayOf(PropTypes.object), + + // eslint-disable-next-line react/forbid-prop-types + textStyles: PropTypes.arrayOf(PropTypes.object), + + ...withLocalizePropTypes, }; const defaultProps = { shouldRenderHTML: false, + shouldShowIcon: false, + shouldShowCloseButton: false, + onClose: () => {}, + onPress: () => {}, + containerStyles: [], + textStyles: [], }; const Banner = props => ( @@ -32,19 +61,35 @@ const Banner = props => ( styles.borderRadiusNormal, isHovered ? styles.activeComponentBG : styles.hoveredComponentBG, styles.breakAll, + ...props.containerStyles, ]} > - - + + {props.shouldShowIcon && ( + + + + )} + { + props.shouldRenderHTML + ? + : {props.text} + } - { - props.shouldRenderHTML - ? - : {props.text} - } + {props.shouldShowCloseButton && ( + + + + + + )} )} @@ -54,4 +99,7 @@ Banner.propTypes = propTypes; Banner.defaultProps = defaultProps; Banner.displayName = 'Banner'; -export default memo(Banner); +export default compose( + withLocalize, + memo, +)(Banner); diff --git a/src/components/BigNumberPad.js b/src/components/BigNumberPad.js index 56dc37d5a440..867b946f25bc 100644 --- a/src/components/BigNumberPad.js +++ b/src/components/BigNumberPad.js @@ -78,6 +78,7 @@ class BigNumberPad extends React.Component { ControlSelection.unblock(); this.props.longPressHandlerStateChanged(false); }} + textSelectable={false} /> ); })} diff --git a/src/components/Button.js b/src/components/Button.js index e50d13abcbb0..e71652d52382 100644 --- a/src/components/Button.js +++ b/src/components/Button.js @@ -106,6 +106,9 @@ const propTypes = { /** Id to use for this button */ nativeID: PropTypes.string, + + /** Whether text in Button should selectable */ + textSelectable: PropTypes.bool, }; const defaultProps = { @@ -136,6 +139,7 @@ const defaultProps = { shouldRemoveLeftBorderRadius: false, shouldEnableHapticFeedback: false, nativeID: '', + textSelectable: true, }; class Button extends Component { @@ -179,7 +183,7 @@ class Button extends Component { const textComponent = ( {this.props.text} diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.js b/src/components/EmojiPicker/EmojiPickerMenu/index.js index 470f570603f3..cee52c9b6272 100755 --- a/src/components/EmojiPicker/EmojiPickerMenu/index.js +++ b/src/components/EmojiPicker/EmojiPickerMenu/index.js @@ -439,7 +439,13 @@ class EmojiPickerMenu extends Component { return ( this.addToFrequentAndSelectEmoji(emoji, item)} - onHover={() => this.setState({highlightedIndex: index})} + onHoverIn={() => this.setState({highlightedIndex: index})} + onHoverOut={() => { + if (this.state.arePointerEventsDisabled) { + return; + } + this.setState({highlightedIndex: -1}); + }} emoji={emojiCode} isHighlighted={index === this.state.highlightedIndex} /> diff --git a/src/components/EmojiPicker/EmojiPickerMenuItem.js b/src/components/EmojiPicker/EmojiPickerMenuItem.js index ee24d689f87d..929930efe27e 100644 --- a/src/components/EmojiPicker/EmojiPickerMenuItem.js +++ b/src/components/EmojiPicker/EmojiPickerMenuItem.js @@ -4,7 +4,6 @@ import {Pressable} from 'react-native'; import styles from '../../styles/styles'; import * as StyleUtils from '../../styles/StyleUtils'; import getButtonState from '../../libs/getButtonState'; -import Hoverable from '../Hoverable'; import Text from '../Text'; const propTypes = { @@ -15,7 +14,10 @@ const propTypes = { onPress: PropTypes.func.isRequired, /** Handles what to do when we hover over this item with our cursor */ - onHover: PropTypes.func, + onHoverIn: PropTypes.func, + + /** Handles what to do when the hover is out */ + onHoverOut: PropTypes.func, /** Whether this menu item is currently highlighted or not */ isHighlighted: PropTypes.bool, @@ -24,6 +26,8 @@ const propTypes = { const EmojiPickerMenuItem = props => ( props.onPress(props.emoji)} + onHoverIn={props.onHoverIn} + onHoverOut={props.onHoverOut} style={({ pressed, }) => ([ @@ -33,11 +37,9 @@ const EmojiPickerMenuItem = props => ( styles.emojiItem, ])} > - - - {props.emoji} - - + + {props.emoji} + ); @@ -45,7 +47,8 @@ EmojiPickerMenuItem.propTypes = propTypes; EmojiPickerMenuItem.displayName = 'EmojiPickerMenuItem'; EmojiPickerMenuItem.defaultProps = { isHighlighted: false, - onHover: () => {}, + onHoverIn: () => {}, + onHoverOut: () => {}, }; // Significantly speeds up re-renders of the EmojiPickerMenu's FlatList diff --git a/src/components/EmojiPicker/EmojiSkinToneList.js b/src/components/EmojiPicker/EmojiSkinToneList.js index 45dc28755592..d8efbcc0ec21 100644 --- a/src/components/EmojiPicker/EmojiSkinToneList.js +++ b/src/components/EmojiPicker/EmojiSkinToneList.js @@ -84,7 +84,8 @@ class EmojiSkinToneList extends Component { _.map(Emojis.skinTones, skinToneEmoji => ( this.updateSelectedSkinTone(skinToneEmoji)} - onHover={() => this.setState({highlightedIndex: skinToneEmoji.skinTone})} + onHoverIn={() => this.setState({highlightedIndex: skinToneEmoji.skinTone})} + onHoverOut={() => this.setState({highlightedIndex: -1})} key={skinToneEmoji.code} emoji={skinToneEmoji.code} isHighlighted={skinToneEmoji.skinTone === this.state.highlightedIndex} diff --git a/src/components/FormAlertWithSubmitButton.js b/src/components/FormAlertWithSubmitButton.js index ca1e86d817ea..c3ef4b464c9a 100644 --- a/src/components/FormAlertWithSubmitButton.js +++ b/src/components/FormAlertWithSubmitButton.js @@ -32,6 +32,9 @@ const propTypes = { /** Submit function */ onSubmit: PropTypes.func.isRequired, + + /** Should the button be enabled when offline */ + enabledWhenOffline: PropTypes.bool, }; const defaultProps = { @@ -41,6 +44,7 @@ const defaultProps = { containerStyles: [], isLoading: false, onFixTheErrorsPressed: () => {}, + enabledWhenOffline: false, }; const FormAlertWithSubmitButton = props => ( @@ -51,7 +55,7 @@ const FormAlertWithSubmitButton = props => ( message={props.message} onFixTheErrorsPressed={props.onFixTheErrorsPressed} > - {isOffline => (isOffline ? ( + {isOffline => ((isOffline && !props.enabledWhenOffline) ? (