From 2ddefdea2847ce336a6927d00da36dc95cf70f22 Mon Sep 17 00:00:00 2001 From: aimane-chnaif Date: Thu, 20 Oct 2022 14:11:14 +1000 Subject: [PATCH 001/936] fix "Downloaded photo is not appearing in the photo album" in android --- src/libs/fileDownload/index.android.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libs/fileDownload/index.android.js b/src/libs/fileDownload/index.android.js index 3b57d0e23496..fe9f9db955c7 100644 --- a/src/libs/fileDownload/index.android.js +++ b/src/libs/fileDownload/index.android.js @@ -56,7 +56,17 @@ function handleDownload(url, fileName) { return; } - FileUtils.showSuccessAlert(); + RNFetchBlob.MediaCollection.copyToMediaStore({ + name: attachmentName, + parentFolder: 'Expensify', + mimeType: null, + }, 'Download', attachment.path()).then(() => { + RNFetchBlob.fs.unlink(attachment.path()); + FileUtils.showSuccessAlert(); + }).catch(() => { + RNFetchBlob.fs.unlink(attachment.path()); + FileUtils.showGeneralErrorAlert(); + }); }).catch(() => { FileUtils.showGeneralErrorAlert(); }).finally(() => resolve()); From 2ae0092b3bf9192445faa8abfc44c1d52824a084 Mon Sep 17 00:00:00 2001 From: aimane-chnaif Date: Fri, 21 Oct 2022 01:03:16 +1000 Subject: [PATCH 002/936] chain promise --- src/libs/fileDownload/index.android.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/libs/fileDownload/index.android.js b/src/libs/fileDownload/index.android.js index fe9f9db955c7..15ce302d3dd9 100644 --- a/src/libs/fileDownload/index.android.js +++ b/src/libs/fileDownload/index.android.js @@ -50,23 +50,22 @@ function handleDownload(url, fileName) { }, }).fetch('GET', url); + let attachmentPath; + // Resolving the fetched attachment fetchedAttachment.then((attachment) => { if (!attachment || !attachment.info()) { return; } - - RNFetchBlob.MediaCollection.copyToMediaStore({ + attachmentPath = attachment.path(); + return RNFetchBlob.MediaCollection.copyToMediaStore({ name: attachmentName, parentFolder: 'Expensify', mimeType: null, - }, 'Download', attachment.path()).then(() => { - RNFetchBlob.fs.unlink(attachment.path()); - FileUtils.showSuccessAlert(); - }).catch(() => { - RNFetchBlob.fs.unlink(attachment.path()); - FileUtils.showGeneralErrorAlert(); - }); + }, 'Download', attachmentPath); + }).then(() => { + FileUtils.showSuccessAlert(); + RNFetchBlob.fs.unlink(attachmentPath); }).catch(() => { FileUtils.showGeneralErrorAlert(); }).finally(() => resolve()); From 91813f863c947ed163a2244d14a4148a5ffc31b2 Mon Sep 17 00:00:00 2001 From: aimane-chnaif Date: Fri, 21 Oct 2022 01:08:41 +1000 Subject: [PATCH 003/936] handle case of null attachment --- src/libs/fileDownload/index.android.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/fileDownload/index.android.js b/src/libs/fileDownload/index.android.js index 15ce302d3dd9..f1cf5e6b4150 100644 --- a/src/libs/fileDownload/index.android.js +++ b/src/libs/fileDownload/index.android.js @@ -55,7 +55,7 @@ function handleDownload(url, fileName) { // Resolving the fetched attachment fetchedAttachment.then((attachment) => { if (!attachment || !attachment.info()) { - return; + return Promise.reject(); } attachmentPath = attachment.path(); return RNFetchBlob.MediaCollection.copyToMediaStore({ From 35cc9b3e4d1d79b2415ab57c147bb557408c5f7f Mon Sep 17 00:00:00 2001 From: aimane-chnaif Date: Fri, 21 Oct 2022 01:45:01 +1000 Subject: [PATCH 004/936] swap lines between unlink and success alert --- src/libs/fileDownload/index.android.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/fileDownload/index.android.js b/src/libs/fileDownload/index.android.js index f1cf5e6b4150..30cb2716318a 100644 --- a/src/libs/fileDownload/index.android.js +++ b/src/libs/fileDownload/index.android.js @@ -64,8 +64,8 @@ function handleDownload(url, fileName) { mimeType: null, }, 'Download', attachmentPath); }).then(() => { - FileUtils.showSuccessAlert(); RNFetchBlob.fs.unlink(attachmentPath); + FileUtils.showSuccessAlert(); }).catch(() => { FileUtils.showGeneralErrorAlert(); }).finally(() => resolve()); From 61ba698238e652bf6e2c63fb7ad72bb111420e64 Mon Sep 17 00:00:00 2001 From: azimgd Date: Thu, 2 Feb 2023 21:05:28 +0500 Subject: [PATCH 005/936] enable keycommands on native devices --- .../java/com/expensify/chat/MainActivity.java | 12 +++ ios/Podfile.lock | 6 ++ package-lock.json | 59 ++++++++++++++ package.json | 1 + src/CONST.js | 38 +++++++++ src/components/KeyboardShortcutsModal.js | 6 +- src/libs/KeyboardShortcut/index.js | 79 +++---------------- src/libs/KeyboardShortcut/index.native.js | 12 --- src/libs/KeyboardShortcut/keyboard.js | 77 ++++++++++++++++++ src/libs/KeyboardShortcut/keyboard.native.js | 59 ++++++++++++++ .../getModalStyles/getBaseModalStyles.js | 1 - src/styles/styles.js | 1 - 12 files changed, 265 insertions(+), 86 deletions(-) delete mode 100644 src/libs/KeyboardShortcut/index.native.js create mode 100644 src/libs/KeyboardShortcut/keyboard.js create mode 100644 src/libs/KeyboardShortcut/keyboard.native.js diff --git a/android/app/src/main/java/com/expensify/chat/MainActivity.java b/android/app/src/main/java/com/expensify/chat/MainActivity.java index 24dcf36a177b..42966aab23ad 100644 --- a/android/app/src/main/java/com/expensify/chat/MainActivity.java +++ b/android/app/src/main/java/com/expensify/chat/MainActivity.java @@ -2,7 +2,9 @@ import android.os.Bundle; import android.content.pm.ActivityInfo; +import android.view.KeyEvent; import com.expensify.chat.bootsplash.BootSplash; +import com.expensify.reactnativekeycommand.KeyCommandModule; import com.facebook.react.ReactActivity; import com.facebook.react.ReactActivityDelegate; import com.facebook.react.ReactRootView; @@ -57,4 +59,14 @@ protected void onCreate(Bundle savedInstanceState) { } BootSplash.init(R.drawable.bootsplash, MainActivity.this); // <- display the generated bootsplash.xml drawable over our MainActivity } + + /** + * This method is called when a key down event has occurred. + * Forwards the event to the KeyCommandModule + */ + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + KeyCommandModule.getInstance().onKeyDownEvent(keyCode, event); + return super.onKeyDown(keyCode, event); + } } diff --git a/ios/Podfile.lock b/ios/Podfile.lock index fbccc4067560..a01518056f5c 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -474,6 +474,8 @@ PODS: - React - react-native-image-picker (5.0.1): - React-Core + - react-native-key-command (0.5.0): + - React-Core - react-native-netinfo (8.3.1): - React-Core - react-native-pdf (6.6.2): @@ -704,6 +706,7 @@ DEPENDENCIES: - react-native-flipper (from `../node_modules/react-native-flipper`) - "react-native-image-manipulator (from `../node_modules/@oguzhnatly/react-native-image-manipulator`)" - react-native-image-picker (from `../node_modules/react-native-image-picker`) + - react-native-key-command (from `../node_modules/react-native-key-command`) - "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)" - react-native-pdf (from `../node_modules/react-native-pdf`) - react-native-performance (from `../node_modules/react-native-performance`) @@ -849,6 +852,8 @@ EXTERNAL SOURCES: :path: "../node_modules/@oguzhnatly/react-native-image-manipulator" react-native-image-picker: :path: "../node_modules/react-native-image-picker" + react-native-key-command: + :path: "../node_modules/react-native-key-command" react-native-netinfo: :path: "../node_modules/@react-native-community/netinfo" react-native-pdf: @@ -993,6 +998,7 @@ SPEC CHECKSUMS: react-native-flipper: dc5290261fbeeb2faec1bdc57ae6dd8d562e1de4 react-native-image-manipulator: c48f64221cfcd46e9eec53619c4c0374f3328a56 react-native-image-picker: 8cb4280e2c1efc3daeb2d9d597f9429a60472e40 + react-native-key-command: bdc18250128146769441ddc8f77e4bc9e625bde1 react-native-netinfo: 1a6035d3b9780221d407c277ebfb5722ace00658 react-native-pdf: 33c622cbdf776a649929e8b9d1ce2d313347c4fa react-native-performance: 224bd53e6a835fda4353302cf891d088a0af7406 diff --git a/package-lock.json b/package-lock.json index 7e91c3818c1b..06cfa0c0ff59 100644 --- a/package-lock.json +++ b/package-lock.json @@ -68,6 +68,7 @@ "react-native-image-pan-zoom": "^2.1.12", "react-native-image-picker": "^5.0.1", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#6b5ab5110dc3ed554f8eafbc38d7d87c17147972", + "react-native-key-command": "^0.5.0", "react-native-modal": "^13.0.0", "react-native-onyx": "1.0.36", "react-native-pdf": "^6.6.2", @@ -35615,6 +35616,21 @@ "integrity": "sha512-jNNpW5byieb7pb/l0HRvmCav4BtzpTzgC+ybT+Wbi2yyroOukveVvnjwWnmoOeuGynsYB4Yt5eGrWZnPnJSwqQ==", "license": "MIT" }, + "node_modules/react-native-key-command": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/react-native-key-command/-/react-native-key-command-0.5.0.tgz", + "integrity": "sha512-FPpTrcSl/+D/yPmvSUvLFqMvqZ/wkgGNttfIbs4kRna0xAA0Wzg3JBs3N6ofLORxlT4gJFL9sRAkCEqzB50Faw==", + "dependencies": { + "events": "^3.3.0", + "underscore": "^1.13.4" + }, + "peerDependencies": { + "react": "18.1.0", + "react-dom": "18.1.0", + "react-native": "0.70.4", + "react-native-web": "0.18.1" + } + }, "node_modules/react-native-modal": { "version": "13.0.1", "resolved": "https://registry.npmjs.org/react-native-modal/-/react-native-modal-13.0.1.tgz", @@ -35850,6 +35866,25 @@ "react-native-svg": ">=12.0.0" } }, + "node_modules/react-native-web": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.18.1.tgz", + "integrity": "sha512-kWZKJU/OIvVriC7R7WN2Wpai6QllD9f7RDUuaBj2G9FdXaybSQFgmuhey4n6naqbLnj720im2PtcnH54Cn/UXw==", + "peer": true, + "dependencies": { + "create-react-class": "^15.7.0", + "fbjs": "^3.0.0", + "inline-style-prefixer": "^6.0.0", + "normalize-css-color": "^1.0.2", + "postcss-value-parser": "^4.2.0", + "prop-types": "^15.6.0", + "styleq": "^0.1.2" + }, + "peerDependencies": { + "react": ">=17.0.2", + "react-dom": ">=17.0.2" + } + }, "node_modules/react-native-webview": { "version": "11.23.0", "resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-11.23.0.tgz", @@ -70034,6 +70069,15 @@ "integrity": "sha512-jNNpW5byieb7pb/l0HRvmCav4BtzpTzgC+ybT+Wbi2yyroOukveVvnjwWnmoOeuGynsYB4Yt5eGrWZnPnJSwqQ==", "from": "react-native-image-size@git+https://github.com/Expensify/react-native-image-size#6b5ab5110dc3ed554f8eafbc38d7d87c17147972" }, + "react-native-key-command": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/react-native-key-command/-/react-native-key-command-0.5.0.tgz", + "integrity": "sha512-FPpTrcSl/+D/yPmvSUvLFqMvqZ/wkgGNttfIbs4kRna0xAA0Wzg3JBs3N6ofLORxlT4gJFL9sRAkCEqzB50Faw==", + "requires": { + "events": "^3.3.0", + "underscore": "^1.13.4" + } + }, "react-native-modal": { "version": "13.0.1", "resolved": "https://registry.npmjs.org/react-native-modal/-/react-native-modal-13.0.1.tgz", @@ -70189,6 +70233,21 @@ "path-dirname": "^1.0.2" } }, + "react-native-web": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.18.1.tgz", + "integrity": "sha512-kWZKJU/OIvVriC7R7WN2Wpai6QllD9f7RDUuaBj2G9FdXaybSQFgmuhey4n6naqbLnj720im2PtcnH54Cn/UXw==", + "peer": true, + "requires": { + "create-react-class": "^15.7.0", + "fbjs": "^3.0.0", + "inline-style-prefixer": "^6.0.0", + "normalize-css-color": "^1.0.2", + "postcss-value-parser": "^4.2.0", + "prop-types": "^15.6.0", + "styleq": "^0.1.2" + } + }, "react-native-webview": { "version": "11.23.0", "resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-11.23.0.tgz", diff --git a/package.json b/package.json index db52903f6d17..8790d1278cb5 100644 --- a/package.json +++ b/package.json @@ -99,6 +99,7 @@ "react-native-image-pan-zoom": "^2.1.12", "react-native-image-picker": "^5.0.1", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#6b5ab5110dc3ed554f8eafbc38d7d87c17147972", + "react-native-key-command": "^0.5.0", "react-native-modal": "^13.0.0", "react-native-onyx": "1.0.36", "react-native-pdf": "^6.6.2", diff --git a/src/CONST.js b/src/CONST.js index ae3ffca7f025..66ea74817807 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -1,6 +1,7 @@ import lodashGet from 'lodash/get'; import Config from 'react-native-config'; import * as Url from './libs/Url'; +import * as KeyCommand from 'react-native-key-command'; const CLOUDFRONT_URL = 'https://d2k5nsl2zxldvw.cloudfront.net'; const ACTIVE_EXPENSIFY_URL = Url.addTrailingForwardSlash(lodashGet(Config, 'NEW_EXPENSIFY_URL', 'https://new.expensify.com')); @@ -199,46 +200,83 @@ const CONST = { descriptionKey: 'search', shortcutKey: 'K', modifiers: ['CTRL'], + trigger: { + DEFAULT: {input: 'k', modifierFlags: KeyCommand.constants.keyModifierControl}, + [PLATFORM_OS_MACOS]: {input: 'k', modifierFlags: KeyCommand.constants.keyModifierCommand}, + }, }, NEW_GROUP: { descriptionKey: 'newGroup', shortcutKey: 'K', modifiers: ['CTRL', 'SHIFT'], + trigger: { + // fix library bug, web: keyModifierControlShift, android: keyModifierShiftControl + DEFAULT: {input: 'k', modifierFlags: KeyCommand.constants.keyModifierShiftControl}, + [PLATFORM_OS_MACOS]: {input: 'k', modifierFlags: KeyCommand.constants.keyModifierShiftCommand}, + }, }, SHORTCUT_MODAL: { descriptionKey: 'openShortcutDialog', shortcutKey: 'I', modifiers: ['CTRL'], + trigger: { + DEFAULT: {input: 'i', modifierFlags: KeyCommand.constants.keyModifierControl}, + [PLATFORM_OS_MACOS]: {input: 'i', modifierFlags: KeyCommand.constants.keyModifierCommand}, + }, }, ESCAPE: { descriptionKey: 'escape', shortcutKey: 'Escape', modifiers: [], + trigger: { + DEFAULT: {input: KeyCommand.constants.keyInputEscape}, + [PLATFORM_OS_MACOS]: {input: KeyCommand.constants.keyInputEscape}, + }, }, ENTER: { descriptionKey: null, shortcutKey: 'Enter', modifiers: [], + trigger: { + DEFAULT: {input: KeyCommand.constants.keyInputEnter}, + [PLATFORM_OS_MACOS]: {input: KeyCommand.constants.keyInputEnter}, + }, }, CTRL_ENTER: { descriptionKey: null, shortcutKey: 'Enter', modifiers: ['CTRL'], + trigger: { + DEFAULT: {input: KeyCommand.constants.keyInputEnter, modifierFlags: KeyCommand.constants.keyModifierControl}, + [PLATFORM_OS_MACOS]: {input: KeyCommand.constants.keyInputEnter, modifierFlags: KeyCommand.constants.keyModifierCommand}, + }, }, COPY: { descriptionKey: 'copy', shortcutKey: 'C', modifiers: ['CTRL'], + trigger: { + DEFAULT: {input: 'c', modifierFlags: KeyCommand.constants.keyModifierControl}, + [PLATFORM_OS_MACOS]: {input: 'c', modifierFlags: KeyCommand.constants.keyModifierCommand}, + }, }, ARROW_UP: { descriptionKey: null, shortcutKey: 'ArrowUp', modifiers: [], + trigger: { + DEFAULT: {input: KeyCommand.constants.keyInputUpArrow}, + [PLATFORM_OS_MACOS]: {input: KeyCommand.constants.keyInputUpArrow}, + }, }, ARROW_DOWN: { descriptionKey: null, shortcutKey: 'ArrowDown', modifiers: [], + trigger: { + DEFAULT: {input: KeyCommand.constants.keyInputDownArrow}, + [PLATFORM_OS_MACOS]: {input: KeyCommand.constants.keyInputDownArrow}, + }, }, }, KEYBOARD_SHORTCUT_KEY_DISPLAY_NAME: { diff --git a/src/components/KeyboardShortcutsModal.js b/src/components/KeyboardShortcutsModal.js index c45ced82f11b..b1b1a91f01aa 100644 --- a/src/components/KeyboardShortcutsModal.js +++ b/src/components/KeyboardShortcutsModal.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import {View} from 'react-native'; +import {View, ScrollView} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import HeaderWithCloseButton from './HeaderWithCloseButton'; @@ -54,7 +54,7 @@ class KeyboardShortcutsModal extends React.Component { */ renderRow(shortcut, isFirstRow) { return ( - {this.props.translate(`keyboardShortcutModal.shortcuts.${shortcut.descriptionKey}`)} - + ); } diff --git a/src/libs/KeyboardShortcut/index.js b/src/libs/KeyboardShortcut/index.js index f948d6991461..943ca551e0db 100644 --- a/src/libs/KeyboardShortcut/index.js +++ b/src/libs/KeyboardShortcut/index.js @@ -1,9 +1,13 @@ import _ from 'underscore'; import lodashGet from 'lodash/get'; import Str from 'expensify-common/lib/str'; +import * as KeyCommand from 'react-native-key-command'; +import * as Keyboard from './keyboard.native'; import getOperatingSystem from '../getOperatingSystem'; import CONST from '../../CONST'; +const operatingSystem = getOperatingSystem(); + // Handlers for the various keyboard listeners we set up const eventHandlers = {}; @@ -17,29 +21,6 @@ function getDocumentedShortcuts() { return _.values(documentedShortcuts); } -/** - * Gets modifiers from a keyboard event. - * - * @param {Event} event - * @returns {Array} - */ -function getKeyEventModifiers(event) { - const modifiers = []; - if (event.shiftKey) { - modifiers.push('SHIFT'); - } - if (event.ctrlKey) { - modifiers.push('CONTROL'); - } - if (event.altKey) { - modifiers.push('ALT'); - } - if (event.metaKey) { - modifiers.push('META'); - } - return modifiers; -} - /** * Generates the normalized display name for keyboard shortcuts. * @@ -60,51 +41,12 @@ function getDisplayName(key, modifiers) { return displayName.join(' + '); } -/** - * Checks if an event for that key is configured and if so, runs it. - * @param {Event} event - * @private - */ -function bindHandlerToKeydownEvent(event) { - if (!(event instanceof KeyboardEvent)) { - return; - } - - const eventModifiers = getKeyEventModifiers(event); - const displayName = getDisplayName(event.key, eventModifiers); - - // Loop over all the callbacks - _.every(eventHandlers[displayName], (callback) => { - // If configured to do so, prevent input text control to trigger this event - if (!callback.captureOnInputs && ( - event.target.nodeName === 'INPUT' - || event.target.nodeName === 'TEXTAREA' - || event.target.contentEditable === 'true' - )) { - return true; - } - - // Determine if the event should bubble before executing the callback (which may have side-effects) - let shouldBubble = callback.shouldBubble || false; - if (_.isFunction(callback.shouldBubble)) { - shouldBubble = callback.shouldBubble(); - } - - if (_.isFunction(callback.callback)) { - callback.callback(event); - } - if (callback.shouldPreventDefault) { - event.preventDefault(); - } - - // If the event should not bubble, short-circuit the loop - return shouldBubble; - }); -} - -// Make sure we don't add multiple listeners -document.removeEventListener('keydown', bindHandlerToKeydownEvent, {capture: true}); -document.addEventListener('keydown', bindHandlerToKeydownEvent, {capture: true}); +_.each(CONST.KEYBOARD_SHORTCUTS, (shortcut) => { + KeyCommand.addListener( + shortcut.trigger[operatingSystem] || shortcut.trigger.DEFAULT, + (keycommandEvent, event) => Keyboard.bindHandlerToKeydownEvent(getDisplayName, eventHandlers, keycommandEvent, event), + ); +}); /** * Unsubscribes a keyboard event handler. @@ -124,7 +66,6 @@ function unsubscribe(displayName, callbackID) { * @returns {Array} */ function getPlatformEquivalentForKeys(keys) { - const operatingSystem = getOperatingSystem(); return _.map(keys, (key) => { if (!_.has(CONST.PLATFORM_SPECIFIC_KEYS, key)) { return key; diff --git a/src/libs/KeyboardShortcut/index.native.js b/src/libs/KeyboardShortcut/index.native.js deleted file mode 100644 index 8c97f2daf343..000000000000 --- a/src/libs/KeyboardShortcut/index.native.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * This is a no-op component for native devices because they wouldn't be able to support keyboard shortcuts like - * a website. - */ -const KeyboardShortcut = { - subscribe() { - return () => {}; - }, - getDocumentedShortcuts() { return []; }, -}; - -export default KeyboardShortcut; diff --git a/src/libs/KeyboardShortcut/keyboard.js b/src/libs/KeyboardShortcut/keyboard.js new file mode 100644 index 000000000000..eedd2bcd4e63 --- /dev/null +++ b/src/libs/KeyboardShortcut/keyboard.js @@ -0,0 +1,77 @@ +import _ from 'underscore'; + +/** + * Gets modifiers from a keyboard event. + * + * @param {Event} event + * @returns {Array} + */ +function getKeyEventModifiers(event) { + const modifiers = []; + + if ((event instanceof KeyboardEvent)) { + if (event.shiftKey) { + modifiers.push('SHIFT'); + } + if (event.ctrlKey) { + modifiers.push('CONTROL'); + } + if (event.metaKey) { + modifiers.push('META'); + } + + return modifiers; + } + + return []; +} + +/** + * Checks if an event for that key is configured and if so, runs it. + * @param {Function} getDisplayName + * @param {Object} eventHandlers + * @param {Object} keycommandEvent + * @param {Event} event + * @private + */ +function bindHandlerToKeydownEvent(getDisplayName, eventHandlers, keycommandEvent, event) { + if (!(event instanceof KeyboardEvent)) { + return; + } + + const eventModifiers = getKeyEventModifiers(event); + const displayName = getDisplayName(event.key, eventModifiers); + + // Loop over all the callbacks + _.every(eventHandlers[displayName], (callback) => { + // If configured to do so, prevent input text control to trigger this event + if (!callback.captureOnInputs && ( + event.target.nodeName === 'INPUT' + || event.target.nodeName === 'TEXTAREA' + || event.target.contentEditable === 'true' + )) { + return true; + } + + // Determine if the event should bubble before executing the callback (which may have side-effects) + let shouldBubble = callback.shouldBubble || false; + if (_.isFunction(callback.shouldBubble)) { + shouldBubble = callback.shouldBubble(); + } + + if (_.isFunction(callback.callback)) { + callback.callback(event); + } + if (callback.shouldPreventDefault) { + event.preventDefault(); + } + + // If the event should not bubble, short-circuit the loop + return shouldBubble; + }); +} + +export { + getKeyEventModifiers, + bindHandlerToKeydownEvent, +}; diff --git a/src/libs/KeyboardShortcut/keyboard.native.js b/src/libs/KeyboardShortcut/keyboard.native.js new file mode 100644 index 000000000000..f0a43c400cb1 --- /dev/null +++ b/src/libs/KeyboardShortcut/keyboard.native.js @@ -0,0 +1,59 @@ +import _ from 'underscore'; +import * as KeyCommand from 'react-native-key-command'; + +/** + * Gets modifiers from a keyboard event. + * + * @param {Event} event + * @returns {Array} + */ +function getKeyEventModifiers(event) { + if (event.modifierFlags === KeyCommand.constants.keyModifierControl) { + return ['CONTROL']; + } + if (event.modifierFlags === KeyCommand.constants.keyModifierCommand) { + return ['META']; + } + if (event.modifierFlags === KeyCommand.constants.keyModifierShiftControl) { + return ['SHIFT', 'CONTROL']; + } + if (event.modifierFlags === KeyCommand.constants.keyModifierShiftCommand) { + return ['META', 'CONTROL']; + } + + return []; +} + +/** + * Checks if an event for that key is configured and if so, runs it. + * @param {Function} getDisplayName + * @param {Object} eventHandlers + * @param {Object} keycommandEvent + * @param {Event} event + * @private + */ +function bindHandlerToKeydownEvent(getDisplayName, eventHandlers, keycommandEvent, event) { + const eventModifiers = getKeyEventModifiers(keycommandEvent); + const displayName = getDisplayName(keycommandEvent.input, eventModifiers); + + // Loop over all the callbacks + _.every(eventHandlers[displayName], (callback) => { + // Determine if the event should bubble before executing the callback (which may have side-effects) + let shouldBubble = callback.shouldBubble || false; + if (_.isFunction(callback.shouldBubble)) { + shouldBubble = callback.shouldBubble(); + } + + if (_.isFunction(callback.callback)) { + callback.callback(event); + } + + // If the event should not bubble, short-circuit the loop + return shouldBubble; + }); +} + +export { + getKeyEventModifiers, + bindHandlerToKeydownEvent, +}; diff --git a/src/styles/getModalStyles/getBaseModalStyles.js b/src/styles/getModalStyles/getBaseModalStyles.js index aea136f6818a..1e74188db9ef 100644 --- a/src/styles/getModalStyles/getBaseModalStyles.js +++ b/src/styles/getModalStyles/getBaseModalStyles.js @@ -107,7 +107,6 @@ export default (type, windowDimensions, popoverAnchorPosition = {}, containerSty shadowOpacity: 0.1, shadowRadius: 5, - flex: 1, marginTop: isSmallScreenWidth ? 0 : 20, marginBottom: isSmallScreenWidth ? 0 : 20, borderRadius: isSmallScreenWidth ? 0 : 12, diff --git a/src/styles/styles.js b/src/styles/styles.js index 65d97d416a0f..ca1373bff0fc 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -2673,7 +2673,6 @@ const styles = { keyboardShortcutModalContainer: { maxHeight: '100%', - flex: '0 0 auto', }, keyboardShortcutTableWrapper: { From f71d9aa1113956110454875569ed349fa87cc356 Mon Sep 17 00:00:00 2001 From: azimgd Date: Thu, 2 Feb 2023 21:07:57 +0500 Subject: [PATCH 006/936] merge main --- android/app/build.gradle | 4 +- .../simple-illustration__shield.svg | 77 +++++++++++++++++++ ios/NewExpensify/Info.plist | 4 +- ios/NewExpensifyTests/Info.plist | 4 +- package-lock.json | 4 +- package.json | 2 +- src/components/Icon/Illustrations.js | 2 + src/languages/en.js | 3 + src/languages/es.js | 3 + src/pages/LogOutPreviousUserPage.js | 12 +++ .../ReimbursementAccount/Enable2FAPrompt.js | 45 +++++++++++ .../ReimbursementAccount/ValidationStep.js | 45 +++++++++-- 12 files changed, 190 insertions(+), 15 deletions(-) create mode 100644 assets/images/simple-illustrations/simple-illustration__shield.svg create mode 100644 src/pages/ReimbursementAccount/Enable2FAPrompt.js diff --git a/android/app/build.gradle b/android/app/build.gradle index f41e0e7f8fec..0a170feca730 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 1001026301 - versionName "1.2.63-1" + versionCode 1001026401 + versionName "1.2.64-1" buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() if (isNewArchitectureEnabled()) { diff --git a/assets/images/simple-illustrations/simple-illustration__shield.svg b/assets/images/simple-illustrations/simple-illustration__shield.svg new file mode 100644 index 000000000000..5d56b9c3acb2 --- /dev/null +++ b/assets/images/simple-illustrations/simple-illustration__shield.svg @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 6d5e5b26c6f3..f7f3b511ef98 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.2.63 + 1.2.64 CFBundleSignature ???? CFBundleURLTypes @@ -30,7 +30,7 @@ CFBundleVersion - 1.2.63.1 + 1.2.64.1 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 4a1d8af8ac9e..87b043a3880f 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.2.63 + 1.2.64 CFBundleSignature ???? CFBundleVersion - 1.2.63.1 + 1.2.64.1 diff --git a/package-lock.json b/package-lock.json index 06cfa0c0ff59..a73e49c7e8fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.2.63-1", + "version": "1.2.64-1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.2.63-1", + "version": "1.2.64-1", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 8790d1278cb5..367b5562c68f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.2.63-1", + "version": "1.2.64-1", "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.", diff --git a/src/components/Icon/Illustrations.js b/src/components/Icon/Illustrations.js index db6660717185..329e9f2b028f 100644 --- a/src/components/Icon/Illustrations.js +++ b/src/components/Icon/Illustrations.js @@ -18,6 +18,7 @@ import RocketOrange from '../../../assets/images/product-illustrations/rocket--o import TadaYellow from '../../../assets/images/product-illustrations/tada--yellow.svg'; import TadaBlue from '../../../assets/images/product-illustrations/tada--blue.svg'; import GpsTrackOrange from '../../../assets/images/product-illustrations/gps-track--orange.svg'; +import ShieldYellow from '../../../assets/images/simple-illustrations/simple-illustration__shield.svg'; import MoneyReceipts from '../../../assets/images/simple-illustrations/simple-illustration__money-receipts.svg'; import PinkBill from '../../../assets/images/simple-illustrations/simple-illustration__bill.svg'; import CreditCardsNew from '../../../assets/images/simple-illustrations/simple-illustration__credit-cards.svg'; @@ -56,6 +57,7 @@ export { TadaYellow, TadaBlue, GpsTrackOrange, + ShieldYellow, MoneyReceipts, PinkBill, CreditCardsNew, diff --git a/src/languages/en.js b/src/languages/en.js index 69ae592a7b3b..dddf50173b61 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -827,6 +827,9 @@ export default { letsChatCTA: 'Yes, let\'s chat', letsChatText: 'Thanks for doing that. We need your help verifying a few pieces of information, but we can work this out quickly over chat. Ready?', letsChatTitle: 'Let\'s chat!', + enable2FATitle: 'Prevent fraud, enable two-factor authentication!', + enable2FAText: 'We take your security seriously, so please set up two-factor authentication for your account now. That will allow us to dispute Expensify Card digital transactions, and will reduce your risk for fraud.', + secureYourAccount: 'Secure your account', }, beneficialOwnersStep: { additionalInformation: 'Additional information', diff --git a/src/languages/es.js b/src/languages/es.js index 8b1ac18ef8e4..ef5bf45b1a10 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -829,6 +829,9 @@ export default { letsChatCTA: 'Sí, vamos a chatear', letsChatText: 'Gracias. Necesitamos tu ayuda para verificar la información, pero podemos hacerlo rápidamente a través del chat. ¿Estás listo?', letsChatTitle: '¡Vamos a chatear!', + enable2FATitle: 'Evita fraudes, activa la autenticación de dos factores!', + enable2FAText: 'Tu seguridad es importante para nosotros, por favor configura ahora la autenticación de dos factores. Eso nos permitirá disputar las transacciones de la Tarjeta Expensify y reducirá tu riesgo de fraude.', + secureYourAccount: 'Asegura tu cuenta', }, beneficialOwnersStep: { additionalInformation: 'Información adicional', diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index d9d36602721d..843930b14e8a 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -22,9 +22,21 @@ class LogOutPreviousUserPage extends Component { .then((transitionURL) => { const sessionEmail = lodashGet(this.props.session, 'email', ''); const isLoggingInAsNewUser = SessionUtils.isLoggingInAsNewUser(transitionURL, sessionEmail); + if (isLoggingInAsNewUser) { Session.signOutAndRedirectToSignIn(); } + + // We need to signin and fetch a new authToken, if a user was already authenticated in NewDot, and was redirected to OldDot + // and their authToken stored in Onyx becomes invalid. + // This workflow is triggered while setting up VBBA. User is redirected from NewDot to OldDot to set up 2FA, and then redirected back to NewDot + // On Enabling 2FA, authToken stored in Onyx becomes expired and hence we need to fetch new authToken + const shouldForceLogin = lodashGet(this.props, 'route.params.shouldForceLogin', '') === 'true'; + if (shouldForceLogin) { + const email = lodashGet(this.props, 'route.params.email', ''); + const shortLivedAuthToken = lodashGet(this.props, 'route.params.shortLivedAuthToken', ''); + Session.signInWithShortLivedAuthToken(email, shortLivedAuthToken); + } }); } diff --git a/src/pages/ReimbursementAccount/Enable2FAPrompt.js b/src/pages/ReimbursementAccount/Enable2FAPrompt.js new file mode 100644 index 000000000000..51c62b8957c1 --- /dev/null +++ b/src/pages/ReimbursementAccount/Enable2FAPrompt.js @@ -0,0 +1,45 @@ +import React from 'react'; +import {View} from 'react-native'; +import Text from '../../components/Text'; +import styles from '../../styles/styles'; +import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; +import * as Expensicons from '../../components/Icon/Expensicons'; +import * as Illustrations from '../../components/Icon/Illustrations'; +import Section from '../../components/Section'; +import * as Link from '../../libs/actions/Link'; +import ROUTES from '../../ROUTES'; +import themeColors from '../../styles/themes/default'; + +const propTypes = { + ...withLocalizePropTypes, +}; +const Enable2FAPrompt = props => ( +
{ + Link.openOldDotLink(encodeURI(`settings?param={"section":"account","action":"enableTwoFactorAuth","exitTo":"${ROUTES.getBankAccountRoute()}","isFromNewDot":"true"}`)); + }, + icon: Expensicons.Shield, + shouldShowRightIcon: true, + iconRight: Expensicons.NewWindow, + iconFill: themeColors.success, + wrapperStyle: [styles.cardMenuItem], + }, + ]} + > + + + {props.translate('validationStep.enable2FAText')} + + +
+); + +Enable2FAPrompt.propTypes = propTypes; +Enable2FAPrompt.displayName = 'Enable2FAPrompt'; + +export default withLocalize(Enable2FAPrompt); diff --git a/src/pages/ReimbursementAccount/ValidationStep.js b/src/pages/ReimbursementAccount/ValidationStep.js index 282c1c0fa974..ea3d5a93c115 100644 --- a/src/pages/ReimbursementAccount/ValidationStep.js +++ b/src/pages/ReimbursementAccount/ValidationStep.js @@ -1,11 +1,13 @@ import lodashGet from 'lodash/get'; import React from 'react'; -import {View} from 'react-native'; +import {ScrollView, View} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; import Str from 'expensify-common/lib/str'; import _ from 'underscore'; import PropTypes from 'prop-types'; import styles from '../../styles/styles'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; +import compose from '../../libs/compose'; import * as BankAccounts from '../../libs/actions/BankAccounts'; import * as Report from '../../libs/actions/Report'; import HeaderWithCloseButton from '../../components/HeaderWithCloseButton'; @@ -25,6 +27,7 @@ import Section from '../../components/Section'; import CONST from '../../CONST'; import Button from '../../components/Button'; import MenuItem from '../../components/MenuItem'; +import Enable2FAPrompt from './Enable2FAPrompt'; const propTypes = { ...withLocalizePropTypes, @@ -33,6 +36,19 @@ const propTypes = { reimbursementAccount: ReimbursementAccountProps.reimbursementAccountPropTypes.isRequired, onBackButtonPress: PropTypes.func.isRequired, + + /** User's account who is setting up bank account */ + account: PropTypes.shape({ + + /** If user has Two factor authentication enabled */ + requiresTwoFactorAuth: PropTypes.bool, + }), +}; + +const defaultProps = { + account: { + requiresTwoFactorAuth: false, + }, }; class ValidationStep extends React.Component { @@ -109,6 +125,7 @@ class ValidationStep extends React.Component { const maxAttemptsReached = lodashGet(this.props.reimbursementAccount, 'maxAttemptsReached'); const isVerifying = !maxAttemptsReached && state === BankAccount.STATE.VERIFYING; + const requiresTwoFactorAuth = lodashGet(this.props, 'account.requiresTwoFactorAuth'); return ( @@ -152,7 +169,7 @@ class ValidationStep extends React.Component { {this.props.translate('validationStep.descriptionCTA')} - + + {!requiresTwoFactorAuth && ( + + + + )} )} {isVerifying && ( - +
-
+ {!requiresTwoFactorAuth && ( + + )} + )}
); @@ -212,5 +237,13 @@ class ValidationStep extends React.Component { } ValidationStep.propTypes = propTypes; - -export default withLocalize(ValidationStep); +ValidationStep.defaultProps = defaultProps; + +export default compose( + withLocalize, + withOnyx({ + account: { + key: ONYXKEYS.ACCOUNT, + }, + }), +)(ValidationStep); From fd695f29e5705337dd109d1e540cf6cdd6e54807 Mon Sep 17 00:00:00 2001 From: azimgd Date: Thu, 2 Feb 2023 22:09:20 +0500 Subject: [PATCH 007/936] fix shortcuts modal view --- src/components/KeyboardShortcutsModal.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/KeyboardShortcutsModal.js b/src/components/KeyboardShortcutsModal.js index b1b1a91f01aa..cacaf5270b2f 100644 --- a/src/components/KeyboardShortcutsModal.js +++ b/src/components/KeyboardShortcutsModal.js @@ -54,7 +54,7 @@ class KeyboardShortcutsModal extends React.Component { */ renderRow(shortcut, isFirstRow) { return ( - {this.props.translate(`keyboardShortcutModal.shortcuts.${shortcut.descriptionKey}`)} - + ); } @@ -83,7 +83,7 @@ class KeyboardShortcutsModal extends React.Component { onClose={KeyboardShortcutsActions.hideKeyboardShortcutModal} > - + {this.props.translate('keyboardShortcutModal.subtitle')} @@ -93,7 +93,7 @@ class KeyboardShortcutsModal extends React.Component { })} - + ); } From c39fab6241f213253eecb227fb9f04ec85f71c20 Mon Sep 17 00:00:00 2001 From: azimgd Date: Fri, 3 Feb 2023 00:11:44 +0500 Subject: [PATCH 008/936] unify keycommands on web and native --- src/CONST.js | 1 - .../index.js} | 36 +++---------------- .../index.native.js} | 30 ++-------------- .../KeyboardShortcut/getKeyEventModifiers.js | 26 ++++++++++++++ src/libs/KeyboardShortcut/index.js | 15 ++++++-- 5 files changed, 44 insertions(+), 64 deletions(-) rename src/libs/KeyboardShortcut/{keyboard.js => bindHandlerToKeydownEvent/index.js} (67%) rename src/libs/KeyboardShortcut/{keyboard.native.js => bindHandlerToKeydownEvent/index.native.js} (58%) create mode 100644 src/libs/KeyboardShortcut/getKeyEventModifiers.js diff --git a/src/CONST.js b/src/CONST.js index 66ea74817807..7bb35796cfa1 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -210,7 +210,6 @@ const CONST = { shortcutKey: 'K', modifiers: ['CTRL', 'SHIFT'], trigger: { - // fix library bug, web: keyModifierControlShift, android: keyModifierShiftControl DEFAULT: {input: 'k', modifierFlags: KeyCommand.constants.keyModifierShiftControl}, [PLATFORM_OS_MACOS]: {input: 'k', modifierFlags: KeyCommand.constants.keyModifierShiftCommand}, }, diff --git a/src/libs/KeyboardShortcut/keyboard.js b/src/libs/KeyboardShortcut/bindHandlerToKeydownEvent/index.js similarity index 67% rename from src/libs/KeyboardShortcut/keyboard.js rename to src/libs/KeyboardShortcut/bindHandlerToKeydownEvent/index.js index eedd2bcd4e63..339904f0bbef 100644 --- a/src/libs/KeyboardShortcut/keyboard.js +++ b/src/libs/KeyboardShortcut/bindHandlerToKeydownEvent/index.js @@ -1,30 +1,5 @@ import _ from 'underscore'; - -/** - * Gets modifiers from a keyboard event. - * - * @param {Event} event - * @returns {Array} - */ -function getKeyEventModifiers(event) { - const modifiers = []; - - if ((event instanceof KeyboardEvent)) { - if (event.shiftKey) { - modifiers.push('SHIFT'); - } - if (event.ctrlKey) { - modifiers.push('CONTROL'); - } - if (event.metaKey) { - modifiers.push('META'); - } - - return modifiers; - } - - return []; -} +import getKeyEventModifiers from '../getKeyEventModifiers'; /** * Checks if an event for that key is configured and if so, runs it. @@ -39,8 +14,8 @@ function bindHandlerToKeydownEvent(getDisplayName, eventHandlers, keycommandEven return; } - const eventModifiers = getKeyEventModifiers(event); - const displayName = getDisplayName(event.key, eventModifiers); + const eventModifiers = getKeyEventModifiers(keycommandEvent); + const displayName = getDisplayName(keycommandEvent.input, eventModifiers); // Loop over all the callbacks _.every(eventHandlers[displayName], (callback) => { @@ -71,7 +46,4 @@ function bindHandlerToKeydownEvent(getDisplayName, eventHandlers, keycommandEven }); } -export { - getKeyEventModifiers, - bindHandlerToKeydownEvent, -}; +export default bindHandlerToKeydownEvent; diff --git a/src/libs/KeyboardShortcut/keyboard.native.js b/src/libs/KeyboardShortcut/bindHandlerToKeydownEvent/index.native.js similarity index 58% rename from src/libs/KeyboardShortcut/keyboard.native.js rename to src/libs/KeyboardShortcut/bindHandlerToKeydownEvent/index.native.js index f0a43c400cb1..de59c819c504 100644 --- a/src/libs/KeyboardShortcut/keyboard.native.js +++ b/src/libs/KeyboardShortcut/bindHandlerToKeydownEvent/index.native.js @@ -1,28 +1,5 @@ import _ from 'underscore'; -import * as KeyCommand from 'react-native-key-command'; - -/** - * Gets modifiers from a keyboard event. - * - * @param {Event} event - * @returns {Array} - */ -function getKeyEventModifiers(event) { - if (event.modifierFlags === KeyCommand.constants.keyModifierControl) { - return ['CONTROL']; - } - if (event.modifierFlags === KeyCommand.constants.keyModifierCommand) { - return ['META']; - } - if (event.modifierFlags === KeyCommand.constants.keyModifierShiftControl) { - return ['SHIFT', 'CONTROL']; - } - if (event.modifierFlags === KeyCommand.constants.keyModifierShiftCommand) { - return ['META', 'CONTROL']; - } - - return []; -} +import getKeyEventModifiers from '../getKeyEventModifiers'; /** * Checks if an event for that key is configured and if so, runs it. @@ -53,7 +30,4 @@ function bindHandlerToKeydownEvent(getDisplayName, eventHandlers, keycommandEven }); } -export { - getKeyEventModifiers, - bindHandlerToKeydownEvent, -}; +export default bindHandlerToKeydownEvent; diff --git a/src/libs/KeyboardShortcut/getKeyEventModifiers.js b/src/libs/KeyboardShortcut/getKeyEventModifiers.js new file mode 100644 index 000000000000..9d3e5defce84 --- /dev/null +++ b/src/libs/KeyboardShortcut/getKeyEventModifiers.js @@ -0,0 +1,26 @@ +import * as KeyCommand from 'react-native-key-command'; + +/** + * Gets modifiers from a keyboard event. + * + * @param {Event} event + * @returns {Array} + */ +function getKeyEventModifiers(event) { + if (event.modifierFlags === KeyCommand.constants.keyModifierControl) { + return ['CONTROL']; + } + if (event.modifierFlags === KeyCommand.constants.keyModifierCommand) { + return ['META']; + } + if (event.modifierFlags === KeyCommand.constants.keyModifierShiftControl) { + return ['SHIFT', 'CONTROL']; + } + if (event.modifierFlags === KeyCommand.constants.keyModifierShiftCommand) { + return ['META', 'CONTROL']; + } + + return []; +} + +export default getKeyEventModifiers; diff --git a/src/libs/KeyboardShortcut/index.js b/src/libs/KeyboardShortcut/index.js index 943ca551e0db..0931e383250d 100644 --- a/src/libs/KeyboardShortcut/index.js +++ b/src/libs/KeyboardShortcut/index.js @@ -2,7 +2,7 @@ import _ from 'underscore'; import lodashGet from 'lodash/get'; import Str from 'expensify-common/lib/str'; import * as KeyCommand from 'react-native-key-command'; -import * as Keyboard from './keyboard.native'; +import bindHandlerToKeydownEvent from './bindHandlerToKeydownEvent'; import getOperatingSystem from '../getOperatingSystem'; import CONST from '../../CONST'; @@ -29,7 +29,16 @@ function getDocumentedShortcuts() { * @returns {String} */ function getDisplayName(key, modifiers) { - let displayName = [key.toUpperCase()]; + let displayName = (() => { + if (key === KeyCommand.constants.keyInputEnter) { + return ['ENTER']; + } + if (key === KeyCommand.constants.keyInputEscape) { + return ['ESCAPE']; + } + return [key.toUpperCase()]; + })(); + if (_.isString(modifiers)) { displayName.unshift(modifiers); } else if (_.isArray(modifiers)) { @@ -44,7 +53,7 @@ function getDisplayName(key, modifiers) { _.each(CONST.KEYBOARD_SHORTCUTS, (shortcut) => { KeyCommand.addListener( shortcut.trigger[operatingSystem] || shortcut.trigger.DEFAULT, - (keycommandEvent, event) => Keyboard.bindHandlerToKeydownEvent(getDisplayName, eventHandlers, keycommandEvent, event), + (keycommandEvent, event) => bindHandlerToKeydownEvent(getDisplayName, eventHandlers, keycommandEvent, event), ); }); From cc1122620d168575ef0551f2b9194c0d4ef77dad Mon Sep 17 00:00:00 2001 From: azimgd Date: Mon, 6 Feb 2023 21:45:06 +0500 Subject: [PATCH 009/936] bump keycommand package --- ios/Podfile.lock | 4 ++-- package-lock.json | 14 +++++++------- package.json | 2 +- src/libs/KeyboardShortcut/getKeyEventModifiers.js | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index a01518056f5c..f1cdd24809d7 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -474,7 +474,7 @@ PODS: - React - react-native-image-picker (5.0.1): - React-Core - - react-native-key-command (0.5.0): + - react-native-key-command (0.6.0): - React-Core - react-native-netinfo (8.3.1): - React-Core @@ -998,7 +998,7 @@ SPEC CHECKSUMS: react-native-flipper: dc5290261fbeeb2faec1bdc57ae6dd8d562e1de4 react-native-image-manipulator: c48f64221cfcd46e9eec53619c4c0374f3328a56 react-native-image-picker: 8cb4280e2c1efc3daeb2d9d597f9429a60472e40 - react-native-key-command: bdc18250128146769441ddc8f77e4bc9e625bde1 + react-native-key-command: 4e6264bd9bb31d7d1453e1306553300f5c5411e4 react-native-netinfo: 1a6035d3b9780221d407c277ebfb5722ace00658 react-native-pdf: 33c622cbdf776a649929e8b9d1ce2d313347c4fa react-native-performance: 224bd53e6a835fda4353302cf891d088a0af7406 diff --git a/package-lock.json b/package-lock.json index 098e53ad2501..7d4f860c6686 100644 --- a/package-lock.json +++ b/package-lock.json @@ -68,7 +68,7 @@ "react-native-image-pan-zoom": "^2.1.12", "react-native-image-picker": "^5.0.1", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#6b5ab5110dc3ed554f8eafbc38d7d87c17147972", - "react-native-key-command": "^0.5.0", + "react-native-key-command": "^0.6.0", "react-native-modal": "^13.0.0", "react-native-onyx": "1.0.36", "react-native-pdf": "^6.6.2", @@ -35617,9 +35617,9 @@ "license": "MIT" }, "node_modules/react-native-key-command": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/react-native-key-command/-/react-native-key-command-0.5.0.tgz", - "integrity": "sha512-FPpTrcSl/+D/yPmvSUvLFqMvqZ/wkgGNttfIbs4kRna0xAA0Wzg3JBs3N6ofLORxlT4gJFL9sRAkCEqzB50Faw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/react-native-key-command/-/react-native-key-command-0.6.0.tgz", + "integrity": "sha512-hQxg8odkiL5B/W/vMRe6Za6Q22b84Mqs6Bn0zURLnUExtWdCywzj2dKpCmT7B0yKixjHo7NEf3pWKTYgxj5NYw==", "dependencies": { "events": "^3.3.0", "underscore": "^1.13.4" @@ -70070,9 +70070,9 @@ "from": "react-native-image-size@git+https://github.com/Expensify/react-native-image-size#6b5ab5110dc3ed554f8eafbc38d7d87c17147972" }, "react-native-key-command": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/react-native-key-command/-/react-native-key-command-0.5.0.tgz", - "integrity": "sha512-FPpTrcSl/+D/yPmvSUvLFqMvqZ/wkgGNttfIbs4kRna0xAA0Wzg3JBs3N6ofLORxlT4gJFL9sRAkCEqzB50Faw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/react-native-key-command/-/react-native-key-command-0.6.0.tgz", + "integrity": "sha512-hQxg8odkiL5B/W/vMRe6Za6Q22b84Mqs6Bn0zURLnUExtWdCywzj2dKpCmT7B0yKixjHo7NEf3pWKTYgxj5NYw==", "requires": { "events": "^3.3.0", "underscore": "^1.13.4" diff --git a/package.json b/package.json index c3941def6fe9..f91a4c2609f7 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "react-native-image-pan-zoom": "^2.1.12", "react-native-image-picker": "^5.0.1", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#6b5ab5110dc3ed554f8eafbc38d7d87c17147972", - "react-native-key-command": "^0.5.0", + "react-native-key-command": "^0.6.0", "react-native-modal": "^13.0.0", "react-native-onyx": "1.0.36", "react-native-pdf": "^6.6.2", diff --git a/src/libs/KeyboardShortcut/getKeyEventModifiers.js b/src/libs/KeyboardShortcut/getKeyEventModifiers.js index 9d3e5defce84..cf96348b4490 100644 --- a/src/libs/KeyboardShortcut/getKeyEventModifiers.js +++ b/src/libs/KeyboardShortcut/getKeyEventModifiers.js @@ -14,10 +14,10 @@ function getKeyEventModifiers(event) { return ['META']; } if (event.modifierFlags === KeyCommand.constants.keyModifierShiftControl) { - return ['SHIFT', 'CONTROL']; + return ['CMD', 'Shift']; } if (event.modifierFlags === KeyCommand.constants.keyModifierShiftCommand) { - return ['META', 'CONTROL']; + return ['CMD', 'Shift']; } return []; From d073edde7d6f54a38905859c5e702062fdb919ed Mon Sep 17 00:00:00 2001 From: azimgd Date: Mon, 6 Feb 2023 22:04:12 +0500 Subject: [PATCH 010/936] register arrow keys --- src/libs/KeyboardShortcut/index.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libs/KeyboardShortcut/index.js b/src/libs/KeyboardShortcut/index.js index 0931e383250d..4f8e1ba60491 100644 --- a/src/libs/KeyboardShortcut/index.js +++ b/src/libs/KeyboardShortcut/index.js @@ -36,6 +36,12 @@ function getDisplayName(key, modifiers) { if (key === KeyCommand.constants.keyInputEscape) { return ['ESCAPE']; } + if (key === KeyCommand.constants.keyInputUpArrow) { + return ['ARROWUP']; + } + if (key === KeyCommand.constants.keyInputDownArrow) { + return ['ARROWDOWN']; + } return [key.toUpperCase()]; })(); From 24c461be3b5d7c15b7df6e2bf2a7a7c2a566857b Mon Sep 17 00:00:00 2001 From: azimgd Date: Mon, 6 Feb 2023 23:17:49 +0500 Subject: [PATCH 011/936] update keycommand import order --- src/CONST.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CONST.js b/src/CONST.js index 7bb35796cfa1..98bcd9c2a0e6 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -1,7 +1,7 @@ import lodashGet from 'lodash/get'; import Config from 'react-native-config'; -import * as Url from './libs/Url'; import * as KeyCommand from 'react-native-key-command'; +import * as Url from './libs/Url'; const CLOUDFRONT_URL = 'https://d2k5nsl2zxldvw.cloudfront.net'; const ACTIVE_EXPENSIFY_URL = Url.addTrailingForwardSlash(lodashGet(Config, 'NEW_EXPENSIFY_URL', 'https://new.expensify.com')); From 4e47695c09be59fbf058826ea343f54567ba14ca Mon Sep 17 00:00:00 2001 From: azimgd Date: Mon, 6 Feb 2023 23:41:18 +0500 Subject: [PATCH 012/936] adjust non apple environment shortcuts --- src/libs/KeyboardShortcut/getKeyEventModifiers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/KeyboardShortcut/getKeyEventModifiers.js b/src/libs/KeyboardShortcut/getKeyEventModifiers.js index cf96348b4490..2d0839d2c35d 100644 --- a/src/libs/KeyboardShortcut/getKeyEventModifiers.js +++ b/src/libs/KeyboardShortcut/getKeyEventModifiers.js @@ -14,10 +14,10 @@ function getKeyEventModifiers(event) { return ['META']; } if (event.modifierFlags === KeyCommand.constants.keyModifierShiftControl) { - return ['CMD', 'Shift']; + return ['CONTROL', 'Shift']; } if (event.modifierFlags === KeyCommand.constants.keyModifierShiftCommand) { - return ['CMD', 'Shift']; + return ['META', 'Shift']; } return []; From bafa42aeec1886b65da893d6ff8c6ca7c54dbe46 Mon Sep 17 00:00:00 2001 From: azimgd Date: Tue, 7 Feb 2023 02:03:49 +0500 Subject: [PATCH 013/936] package version bump --- ios/Podfile.lock | 4 ++-- package-lock.json | 14 +++++++------- package.json | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index f1cdd24809d7..d2f76248f41b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -474,7 +474,7 @@ PODS: - React - react-native-image-picker (5.0.1): - React-Core - - react-native-key-command (0.6.0): + - react-native-key-command (0.7.0): - React-Core - react-native-netinfo (8.3.1): - React-Core @@ -998,7 +998,7 @@ SPEC CHECKSUMS: react-native-flipper: dc5290261fbeeb2faec1bdc57ae6dd8d562e1de4 react-native-image-manipulator: c48f64221cfcd46e9eec53619c4c0374f3328a56 react-native-image-picker: 8cb4280e2c1efc3daeb2d9d597f9429a60472e40 - react-native-key-command: 4e6264bd9bb31d7d1453e1306553300f5c5411e4 + react-native-key-command: bfa17378a6c49090b83e904ddcfc9ffb261448b2 react-native-netinfo: 1a6035d3b9780221d407c277ebfb5722ace00658 react-native-pdf: 33c622cbdf776a649929e8b9d1ce2d313347c4fa react-native-performance: 224bd53e6a835fda4353302cf891d088a0af7406 diff --git a/package-lock.json b/package-lock.json index 129cef2bca67..aed7a5f13270 100644 --- a/package-lock.json +++ b/package-lock.json @@ -68,7 +68,7 @@ "react-native-image-pan-zoom": "^2.1.12", "react-native-image-picker": "^5.0.1", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#6b5ab5110dc3ed554f8eafbc38d7d87c17147972", - "react-native-key-command": "^0.6.0", + "react-native-key-command": "^0.7.0", "react-native-modal": "^13.0.0", "react-native-onyx": "1.0.36", "react-native-pdf": "^6.6.2", @@ -35617,9 +35617,9 @@ "license": "MIT" }, "node_modules/react-native-key-command": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/react-native-key-command/-/react-native-key-command-0.6.0.tgz", - "integrity": "sha512-hQxg8odkiL5B/W/vMRe6Za6Q22b84Mqs6Bn0zURLnUExtWdCywzj2dKpCmT7B0yKixjHo7NEf3pWKTYgxj5NYw==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/react-native-key-command/-/react-native-key-command-0.7.0.tgz", + "integrity": "sha512-iFFXfsyTWJoo3F7A1cy01I66RldcmJOfys1NQQs2EKz9ZVY8LSOmHuJUTqgpPu96bGV4l2fHqyNfI98qc4DMCA==", "dependencies": { "events": "^3.3.0", "underscore": "^1.13.4" @@ -70070,9 +70070,9 @@ "from": "react-native-image-size@git+https://github.com/Expensify/react-native-image-size#6b5ab5110dc3ed554f8eafbc38d7d87c17147972" }, "react-native-key-command": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/react-native-key-command/-/react-native-key-command-0.6.0.tgz", - "integrity": "sha512-hQxg8odkiL5B/W/vMRe6Za6Q22b84Mqs6Bn0zURLnUExtWdCywzj2dKpCmT7B0yKixjHo7NEf3pWKTYgxj5NYw==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/react-native-key-command/-/react-native-key-command-0.7.0.tgz", + "integrity": "sha512-iFFXfsyTWJoo3F7A1cy01I66RldcmJOfys1NQQs2EKz9ZVY8LSOmHuJUTqgpPu96bGV4l2fHqyNfI98qc4DMCA==", "requires": { "events": "^3.3.0", "underscore": "^1.13.4" diff --git a/package.json b/package.json index f51bb72d9c78..fa7193bbb628 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "react-native-image-pan-zoom": "^2.1.12", "react-native-image-picker": "^5.0.1", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#6b5ab5110dc3ed554f8eafbc38d7d87c17147972", - "react-native-key-command": "^0.6.0", + "react-native-key-command": "^0.7.0", "react-native-modal": "^13.0.0", "react-native-onyx": "1.0.36", "react-native-pdf": "^6.6.2", From e9f04a15c02f39d5ccfedf7597c3d78fc8bc111a Mon Sep 17 00:00:00 2001 From: azimgd Date: Fri, 10 Feb 2023 01:03:57 +0500 Subject: [PATCH 014/936] CR request, putting back flex:1 on unswipeable modal --- src/styles/getModalStyles/getBaseModalStyles.js | 1 + src/styles/styles.js | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/styles/getModalStyles/getBaseModalStyles.js b/src/styles/getModalStyles/getBaseModalStyles.js index 1e74188db9ef..aea136f6818a 100644 --- a/src/styles/getModalStyles/getBaseModalStyles.js +++ b/src/styles/getModalStyles/getBaseModalStyles.js @@ -107,6 +107,7 @@ export default (type, windowDimensions, popoverAnchorPosition = {}, containerSty shadowOpacity: 0.1, shadowRadius: 5, + flex: 1, marginTop: isSmallScreenWidth ? 0 : 20, marginBottom: isSmallScreenWidth ? 0 : 20, borderRadius: isSmallScreenWidth ? 0 : 12, diff --git a/src/styles/styles.js b/src/styles/styles.js index 7d8fcbab016f..ba2a4638b1de 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -2673,6 +2673,9 @@ const styles = { keyboardShortcutModalContainer: { maxHeight: '100%', + flexShrink: 0, + flexGrow: 0, + flexBasis: 'auto', }, keyboardShortcutTableWrapper: { From f67f793f09d36bc79ae5f81c0258fcfc1ff3deab Mon Sep 17 00:00:00 2001 From: azimgd Date: Fri, 10 Feb 2023 01:58:49 +0500 Subject: [PATCH 015/936] fix shortcut comparison operator --- src/libs/KeyboardShortcut/index.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libs/KeyboardShortcut/index.js b/src/libs/KeyboardShortcut/index.js index 4f8e1ba60491..beed3518a698 100644 --- a/src/libs/KeyboardShortcut/index.js +++ b/src/libs/KeyboardShortcut/index.js @@ -30,16 +30,17 @@ function getDocumentedShortcuts() { */ function getDisplayName(key, modifiers) { let displayName = (() => { - if (key === KeyCommand.constants.keyInputEnter) { + // typeof key -> string, typeof KeyCommand.constants.* -> number | string + if (_.isEqual(key, KeyCommand.constants.keyInputEnter.toString())) { return ['ENTER']; } - if (key === KeyCommand.constants.keyInputEscape) { + if (_.isEqual(key, KeyCommand.constants.keyInputEscape.toString())) { return ['ESCAPE']; } - if (key === KeyCommand.constants.keyInputUpArrow) { + if (_.isEqual(key, KeyCommand.constants.keyInputUpArrow.toString())) { return ['ARROWUP']; } - if (key === KeyCommand.constants.keyInputDownArrow) { + if (_.isEqual(key, KeyCommand.constants.keyInputDownArrow.toString())) { return ['ARROWDOWN']; } return [key.toUpperCase()]; From 3c2242ed49868fa5a577bdfb16150fd87bbd10a7 Mon Sep 17 00:00:00 2001 From: azimgd Date: Fri, 10 Feb 2023 02:30:08 +0500 Subject: [PATCH 016/936] enable aboutPage.viewKeyboardShortcuts link on native --- src/pages/settings/AboutPage/AboutPage.js | 10 ++++++---- .../getPlatformSpecificMenuItems/index.js | 15 --------------- .../getPlatformSpecificMenuItems/index.native.js | 1 - 3 files changed, 6 insertions(+), 20 deletions(-) delete mode 100644 src/pages/settings/AboutPage/getPlatformSpecificMenuItems/index.js delete mode 100644 src/pages/settings/AboutPage/getPlatformSpecificMenuItems/index.native.js diff --git a/src/pages/settings/AboutPage/AboutPage.js b/src/pages/settings/AboutPage/AboutPage.js index a9a809c6d0a2..317c7169a503 100644 --- a/src/pages/settings/AboutPage/AboutPage.js +++ b/src/pages/settings/AboutPage/AboutPage.js @@ -17,8 +17,8 @@ import Logo from '../../../../assets/images/new-expensify.svg'; import pkg from '../../../../package.json'; import * as Report from '../../../libs/actions/Report'; import * as Link from '../../../libs/actions/Link'; -import getPlatformSpecificMenuItems from './getPlatformSpecificMenuItems'; import compose from '../../../libs/compose'; +import * as KeyboardShortcuts from '../../../libs/actions/KeyboardShortcuts'; const propTypes = { ...withLocalizePropTypes, @@ -26,8 +26,6 @@ const propTypes = { }; const AboutPage = (props) => { - const platformSpecificMenuItems = getPlatformSpecificMenuItems(props.isSmallScreenWidth); - const menuItems = [ { translationKey: 'initialSettingsPage.aboutPage.appDownloadLinks', @@ -36,7 +34,11 @@ const AboutPage = (props) => { Navigation.navigate(ROUTES.SETTINGS_APP_DOWNLOAD_LINKS); }, }, - ...platformSpecificMenuItems, + { + translationKey: 'initialSettingsPage.aboutPage.viewKeyboardShortcuts', + icon: Expensicons.Keyboard, + action: KeyboardShortcuts.showKeyboardShortcutModal, + }, { translationKey: 'initialSettingsPage.aboutPage.viewTheCode', icon: Expensicons.Eye, diff --git a/src/pages/settings/AboutPage/getPlatformSpecificMenuItems/index.js b/src/pages/settings/AboutPage/getPlatformSpecificMenuItems/index.js deleted file mode 100644 index 52f8ffa2250f..000000000000 --- a/src/pages/settings/AboutPage/getPlatformSpecificMenuItems/index.js +++ /dev/null @@ -1,15 +0,0 @@ -import * as KeyboardShortcuts from '../../../../libs/actions/KeyboardShortcuts'; -import * as Expensicons from '../../../../components/Icon/Expensicons'; - -export default (isSmallScreenWidth) => { - if (isSmallScreenWidth) { - return []; - } - return [ - { - translationKey: 'initialSettingsPage.aboutPage.viewKeyboardShortcuts', - icon: Expensicons.Keyboard, - action: KeyboardShortcuts.showKeyboardShortcutModal, - }, - ]; -}; diff --git a/src/pages/settings/AboutPage/getPlatformSpecificMenuItems/index.native.js b/src/pages/settings/AboutPage/getPlatformSpecificMenuItems/index.native.js deleted file mode 100644 index 4ba9480748fc..000000000000 --- a/src/pages/settings/AboutPage/getPlatformSpecificMenuItems/index.native.js +++ /dev/null @@ -1 +0,0 @@ -export default () => []; From c5f466ca0ebc8352714bad142c164db807f0ee55 Mon Sep 17 00:00:00 2001 From: azimgd Date: Fri, 10 Feb 2023 02:42:11 +0500 Subject: [PATCH 017/936] cr comment Co-authored-by: Rajat Parashar --- src/libs/KeyboardShortcut/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/KeyboardShortcut/index.js b/src/libs/KeyboardShortcut/index.js index beed3518a698..f095f4ab8d11 100644 --- a/src/libs/KeyboardShortcut/index.js +++ b/src/libs/KeyboardShortcut/index.js @@ -30,7 +30,7 @@ function getDocumentedShortcuts() { */ function getDisplayName(key, modifiers) { let displayName = (() => { - // typeof key -> string, typeof KeyCommand.constants.* -> number | string + // Type of key is string and the type of KeyCommand.constants.* is number | string. Use _.isEqual to match different types. if (_.isEqual(key, KeyCommand.constants.keyInputEnter.toString())) { return ['ENTER']; } From f2a9ed9735d9b0c14d6f6ce8c6e560c53cd97b45 Mon Sep 17 00:00:00 2001 From: azimgd Date: Sun, 12 Feb 2023 20:06:08 +0500 Subject: [PATCH 018/936] keycommand library mock --- __mocks__/react-native-key-command.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 __mocks__/react-native-key-command.js diff --git a/__mocks__/react-native-key-command.js b/__mocks__/react-native-key-command.js new file mode 100644 index 000000000000..092ab120a142 --- /dev/null +++ b/__mocks__/react-native-key-command.js @@ -0,0 +1,13 @@ +const registerKeyCommands = () => {}; +const unregisterKeyCommands = () => {}; +const constants = {}; +const eventEmitter = () => {}; +const addListener = () => {}; + +export { + registerKeyCommands, + unregisterKeyCommands, + constants, + eventEmitter, + addListener, +}; From 99df6d735deffdf05cbf1a433bbcc2b1f7cd1acf Mon Sep 17 00:00:00 2001 From: Aldo Canepa Date: Wed, 22 Feb 2023 17:54:37 -0800 Subject: [PATCH 019/936] Show error in workspace's bank account page --- .../resetFreePlanBankAccount.js | 50 ++++++---- src/pages/ReimbursementAccount/EnableStep.js | 92 ++++++++++++------- 2 files changed, 90 insertions(+), 52 deletions(-) diff --git a/src/libs/actions/ReimbursementAccount/resetFreePlanBankAccount.js b/src/libs/actions/ReimbursementAccount/resetFreePlanBankAccount.js index 94799fa6b3c6..98121d5db910 100644 --- a/src/libs/actions/ReimbursementAccount/resetFreePlanBankAccount.js +++ b/src/libs/actions/ReimbursementAccount/resetFreePlanBankAccount.js @@ -24,6 +24,26 @@ function resetFreePlanBankAccount(bankAccountID) { }, { optimisticData: [ + // This bank account is present in ONYXKEYS.BANK_ACCOUNT_LIST + { + onyxMethod: CONST.ONYX.METHOD.MERGE, + key: ONYXKEYS.BANK_ACCOUNT_LIST, + value: {[bankAccountID]: {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}}, + }, + { + onyxMethod: CONST.ONYX.METHOD.MERGE, + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + value: {shouldShowResetModal: false}, + }, + + // TODO: What is this loading state? doesn't seem to be used + // { + // onyxMethod: CONST.ONYX.METHOD.MERGE, + // key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + // value: {isLoading: true}, + // }, + ], + successData: [ { onyxMethod: CONST.ONYX.METHOD.SET, key: ONYXKEYS.ONFIDO_TOKEN, @@ -44,30 +64,26 @@ function resetFreePlanBankAccount(bankAccountID) { key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, value: ReimbursementAccountProps.reimbursementAccountDefaultProps, }, - { - onyxMethod: CONST.ONYX.METHOD.MERGE, - key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, - value: {isLoading: true}, - }, { onyxMethod: CONST.ONYX.METHOD.SET, key: ONYXKEYS.REIMBURSEMENT_ACCOUNT_DRAFT, value: {}, }, - ], - successData: [ - { - onyxMethod: CONST.ONYX.METHOD.MERGE, - key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, - value: {isLoading: false}, - }, + + // TODO: What is this loading state? doesn't seem to be used + // { + // onyxMethod: CONST.ONYX.METHOD.MERGE, + // key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + // value: {isLoading: false}, + // }, ], failureData: [ - { - onyxMethod: CONST.ONYX.METHOD.MERGE, - key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, - value: {isLoading: false}, - }, + // TODO: What is this loading state? doesn't seem to be used + // { + // onyxMethod: CONST.ONYX.METHOD.MERGE, + // key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + // value: {isLoading: false}, + // }, ], }); } diff --git a/src/pages/ReimbursementAccount/EnableStep.js b/src/pages/ReimbursementAccount/EnableStep.js index b8b7dd1a029f..c6b2fb21a941 100644 --- a/src/pages/ReimbursementAccount/EnableStep.js +++ b/src/pages/ReimbursementAccount/EnableStep.js @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import {ScrollView} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; @@ -13,6 +14,8 @@ import CONST from '../../CONST'; import Button from '../../components/Button'; import * as Expensicons from '../../components/Icon/Expensicons'; import MenuItem from '../../components/MenuItem'; +import OfflineWithFeedback from '../../components/OfflineWithFeedback'; +import bankAccountPropTypes from '../../components/bankAccountPropTypes'; import getBankIcon from '../../components/Icon/BankIcons'; import * as ReimbursementAccountProps from './reimbursementAccountPropTypes'; import userPropTypes from '../settings/userPropTypes'; @@ -23,11 +26,15 @@ import * as User from '../../libs/actions/User'; import ScreenWrapper from '../../components/ScreenWrapper'; import * as BankAccounts from '../../libs/actions/ReimbursementAccount'; import WorkspaceResetBankAccountModal from '../workspace/WorkspaceResetBankAccountModal'; +import * as PaymentMethods from '../../libs/actions/PaymentMethods'; const propTypes = { /** Bank account currently in setup */ reimbursementAccount: ReimbursementAccountProps.reimbursementAccountPropTypes.isRequired, + /** List of bank accounts */ + bankAccountList: PropTypes.objectOf(bankAccountPropTypes).isRequired, + /* Onyx Props */ user: userPropTypes.isRequired, @@ -45,6 +52,8 @@ const EnableStep = (props) => { : ''; const bankName = achData.addressName; + const errors = lodashGet(props.bankAccountList, [achData.bankAccountID, 'errors'], {}); + const pendingAction = lodashGet(props.bankAccountList, [achData.bankAccountID, 'pendingAction'], {}); return ( { title={!isUsingExpensifyCard ? props.translate('workspace.bankAccount.oneMoreThing') : props.translate('workspace.bankAccount.allSet')} icon={!isUsingExpensifyCard ? Illustrations.ConciergeNew : Illustrations.ThumbsUpStars} > - - - {!isUsingExpensifyCard - ? props.translate('workspace.bankAccount.accountDescriptionNoCards') - : props.translate('workspace.bankAccount.accountDescriptionWithCards')} - - {!isUsingExpensifyCard && ( -