diff --git a/android/app/build.gradle b/android/app/build.gradle index b792f7830ea4..e6b32dcd4d4b 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -98,8 +98,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001045103 - versionName "1.4.51-3" + versionCode 1001045200 + versionName "1.4.52-0" } flavorDimensions "default" diff --git a/assets/images/document-slash.svg b/assets/images/document-slash.svg index 25a4c96038b4..ebb183142e40 100644 --- a/assets/images/document-slash.svg +++ b/assets/images/document-slash.svg @@ -1,6 +1 @@ - - - - - - + diff --git a/assets/images/qrcode.svg b/assets/images/qrcode.svg index f506a944d54e..42c49c958246 100644 --- a/assets/images/qrcode.svg +++ b/assets/images/qrcode.svg @@ -1 +1,14 @@ - \ No newline at end of file + + + + + + + + + + + + diff --git a/assets/images/tax.svg b/assets/images/tax.svg new file mode 100644 index 000000000000..aa3c68e72ea8 --- /dev/null +++ b/assets/images/tax.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/contributingGuides/APPLE_GOOGLE_SIGNIN.md b/contributingGuides/APPLE_GOOGLE_SIGNIN.md index 4bb86e31b486..5c95dfb60950 100644 --- a/contributingGuides/APPLE_GOOGLE_SIGNIN.md +++ b/contributingGuides/APPLE_GOOGLE_SIGNIN.md @@ -98,27 +98,6 @@ Due to some technical constraints, Apple and Google sign-in may require addition ## Apple -### iOS/Android - -The iOS and Android implementations do not require extra steps to test, aside from signing into an Apple account on the iOS device before being able to use Sign in with Apple. - -### Web and desktop - -#### Render the web Sign In with Apple button in development - -The Google Sign In button renders differently in development mode. To prevent confusion -for developers about a possible regression, we decided to not render third party buttons in -development mode. - -To show the Apple Sign In button in development mode, you can comment out the following code in the -LoginForm.js file: - -```js -if (CONFIG.ENVIRONMENT === CONST.ENVIRONMENT.DEV) { - return; -} -``` - #### Port requirements The Sign in with Apple process will break after the user signs in if the pop-up process is not started from a page at an HTTPS domain registered with Apple. To fix this, you could make a new configuration with your own HTTPS domain, but then the Apple configuration won't match that of Expensify's backend. @@ -240,53 +219,49 @@ open desktop-build 2. Even with this build, the deep link may not be handled by the correct app, as the development Electron config seems to intercept it sometimes. To manage this, install [SwiftDefaultApps](https://github.com/Lord-Kamina/SwiftDefaultApps), which adds a preference pane that can be used to configure which app should handle deep links. -## Google - -### Web - -#### Render the web Sign In with Google button in Development +### Test the Apple / Google SSO buttons in development environment -The Google Sign In button renders differently in development mode. To prevent confusion +The Apple/Google Sign In button renders differently in development mode. To prevent confusion for developers about a possible regression, we decided to not render third party buttons in development mode. -To show the Google Sign In button in development mode, you can comment out the following code in the -LoginForm.js file: - -```js -if (CONFIG.ENVIRONMENT === CONST.ENVIRONMENT.DEV) { - return; -} -``` - -#### Host/Port requirements - -Google allows the web app to be hosted at localhost, but according to the -current Google console configuration for the Expensify client ID, it must be -hosted on port 8082. - -Also note that you'll need to update the webpack.dev.js config to change `host` from `dev.new.expensify.com` to `localhost` and server type from `https` to `http`. The reason for this is that Google Sign In allows localhost, but `dev.new.expensify.com` is not a registered Google Sign In domain. - -```diff -diff --git a/config/webpack/webpack.dev.js b/config/webpack/webpack.dev.js -index e28383eff5..b14f6f34aa 100644 ---- a/config/webpack/webpack.dev.js -+++ b/config/webpack/webpack.dev.js -@@ -44,9 +44,9 @@ module.exports = (env = {}) => - ...proxySettings, - historyApiFallback: true, - port, -- host: 'dev.new.expensify.com', -+ host: 'localhost', - server: { -- type: 'https', -+ type: 'http', - options: { - key: path.join(__dirname, 'key.pem'), - cert: path.join(__dirname, 'certificate.pem'), -``` - -### Desktop +Here's how you can re-enable the SSO buttons in development mode: + +- Remove this [condition](https://github.com/Expensify/App/blob/c2a718c9100e704c89ad9564301348bc53a49777/src/pages/signin/LoginForm/BaseLoginForm.tsx#L300) so that we always render the SSO button components + ```diff + diff --git a/src/pages/signin/LoginForm/BaseLoginForm.tsx b/src/pages/signin/LoginForm/BaseLoginForm.tsx + index 4286a26033..850f8944ca 100644 + --- a/src/pages/signin/LoginForm/BaseLoginForm.tsx + +++ b/src/pages/signin/LoginForm/BaseLoginForm.tsx + @@ -288,7 +288,7 @@ function BaseLoginForm({account, credentials, closeAccount, blurOnSubmit = false + // for developers about possible regressions, we won't render buttons in development mode. + // For more information about these differences and how to test in development mode, + // see`Expensify/App/contributingGuides/APPLE_GOOGLE_SIGNIN.md` + - CONFIG.ENVIRONMENT !== CONST.ENVIRONMENT.DEV && ( + + ( + + + ...proxySettings, + historyApiFallback: true, + port, + - host: 'dev.new.expensify.com', + + host: 'localhost', + server: { + - type: 'https', + + type: 'http', + options: { + key: path.join(__dirname, 'key.pem'), + cert: path.join(__dirname, 'certificate.pem'), + ``` #### Set Environment to something other than "Development" diff --git a/docs/articles/expensify-classic/domains/Claim-And-Verify-A-Domain.md b/docs/articles/expensify-classic/domains/Claim-And-Verify-A-Domain.md index bb4b21547892..509961b026e5 100644 --- a/docs/articles/expensify-classic/domains/Claim-And-Verify-A-Domain.md +++ b/docs/articles/expensify-classic/domains/Claim-And-Verify-A-Domain.md @@ -31,7 +31,8 @@ You can claim and verify private domains only. Public domains (like gmail.com) c # Step 2: Verify domain ownership {% include info.html %} -To complete this step, you must have a Control workspace, and you’ll need access to your domain provider account (GoDaddy, Wix, GSuite, etc.). If you don’t verify the domain, you will still have access to the domain to add and manage credit card expenses and domain admins, but you will not be able to invite members, add groups, use domain reporting tools, set delegates for employees on vacation, or enable SAML SSO. For more guidance on how to complete this process for a specific provider, check the provider’s website.{% include end-info.html %} +To complete this step, you must have a Control workspace, and you’ll need access to your domain provider account (GoDaddy, Wix, GSuite, etc.). If you don’t verify the domain, you will still have access to the domain to add and manage credit card expenses and domain admins, but you will not be able to invite members, add groups, use domain reporting tools, set delegates for employees on vacation, or enable SAML SSO. For more guidance on how to complete this process for a specific provider, check the provider’s website. +{% include end-info.html %}
  1. Log in to your DNS service provider (which may be the website you purchased the domain from or that currently hosts the domain, like NameCheap, GoDaddy, DNSMadeEasy, or Amazon Route53. You may need to contact your company’s IT department if your domain is managed internally).
  2. diff --git a/docs/expensify-classic/hubs/billing-and-subscriptions/index.html b/docs/expensify-classic/hubs/expensify-billing/index.html similarity index 100% rename from docs/expensify-classic/hubs/billing-and-subscriptions/index.html rename to docs/expensify-classic/hubs/expensify-billing/index.html diff --git a/ios/NewApp_AdHoc.mobileprovision.gpg b/ios/NewApp_AdHoc.mobileprovision.gpg index c4412cf650ee..2c5350cec2aa 100644 Binary files a/ios/NewApp_AdHoc.mobileprovision.gpg and b/ios/NewApp_AdHoc.mobileprovision.gpg differ diff --git a/ios/NewApp_AdHoc_Notification_Service.mobileprovision.gpg b/ios/NewApp_AdHoc_Notification_Service.mobileprovision.gpg index 999442b550da..bae3cd9f3e21 100644 Binary files a/ios/NewApp_AdHoc_Notification_Service.mobileprovision.gpg and b/ios/NewApp_AdHoc_Notification_Service.mobileprovision.gpg differ diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index ab5d359a5460..3991493df583 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.4.51 + 1.4.52 CFBundleSignature ???? CFBundleURLTypes @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.51.3 + 1.4.52.0 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index ca9200c78376..c04d2b7d3802 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.4.51 + 1.4.52 CFBundleSignature ???? CFBundleVersion - 1.4.51.3 + 1.4.52.0 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index f20b520b1480..7d5db6a4159b 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -11,9 +11,9 @@ CFBundleName $(PRODUCT_NAME) CFBundleShortVersionString - 1.4.51 + 1.4.52 CFBundleVersion - 1.4.51.3 + 1.4.52.0 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index 3083c15c0196..5529a99e861d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.51-3", + "version": "1.4.52-0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.51-3", + "version": "1.4.52-0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index dc5b261c3b40..1f6e6ce85450 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.51-3", + "version": "1.4.52-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.", diff --git a/src/CONST.ts b/src/CONST.ts index d4fbd0ff6ef3..fa44cda20720 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -993,8 +993,6 @@ const CONST = { MAGIC_CODE_LENGTH: 6, MAGIC_CODE_EMPTY_CHAR: ' ', - RECOVERY_CODE_LENGTH: 8, - KEYBOARD_TYPE: { VISIBLE_PASSWORD: 'visible-password', ASCII_CAPABLE: 'ascii-capable', @@ -1414,6 +1412,11 @@ const CONST = { MAKE_MEMBER: 'makeMember', MAKE_ADMIN: 'makeAdmin', }, + CATEGORIES_BULK_ACTION_TYPES: { + DELETE: 'delete', + DISABLE: 'disable', + ENABLE: 'enable', + }, DISTANCE_RATES_BULK_ACTION_TYPES: { DELETE: 'delete', DISABLE: 'disable', diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 8c48cbad561f..820937b1eb89 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -330,8 +330,8 @@ const ONYXKEYS = { ADD_DEBIT_CARD_FORM: 'addDebitCardForm', ADD_DEBIT_CARD_FORM_DRAFT: 'addDebitCardFormDraft', WORKSPACE_SETTINGS_FORM: 'workspaceSettingsForm', - WORKSPACE_CATEGORY_CREATE_FORM: 'workspaceCategoryCreate', - WORKSPACE_CATEGORY_CREATE_FORM_DRAFT: 'workspaceCategoryCreateDraft', + WORKSPACE_CATEGORY_FORM: 'workspaceCategoryForm', + WORKSPACE_CATEGORY_FORM_DRAFT: 'workspaceCategoryFormDraft', WORKSPACE_TAG_CREATE_FORM: 'workspaceTagCreate', WORKSPACE_TAG_CREATE_FORM_DRAFT: 'workspaceTagCreateDraft', WORKSPACE_SETTINGS_FORM_DRAFT: 'workspaceSettingsFormDraft', @@ -417,7 +417,7 @@ type AllOnyxKeys = DeepValueOf; type OnyxFormValuesMapping = { [ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM]: FormTypes.AddDebitCardForm; [ONYXKEYS.FORMS.WORKSPACE_SETTINGS_FORM]: FormTypes.WorkspaceSettingsForm; - [ONYXKEYS.FORMS.WORKSPACE_CATEGORY_CREATE_FORM]: FormTypes.WorkspaceCategoryCreateForm; + [ONYXKEYS.FORMS.WORKSPACE_CATEGORY_FORM]: FormTypes.WorkspaceCategoryForm; [ONYXKEYS.FORMS.WORKSPACE_TAG_CREATE_FORM]: FormTypes.WorkspaceTagCreateForm; [ONYXKEYS.FORMS.WORKSPACE_RATE_AND_UNIT_FORM]: FormTypes.WorkspaceRateAndUnitForm; [ONYXKEYS.FORMS.CLOSE_ACCOUNT_FORM]: FormTypes.CloseAccountForm; diff --git a/src/ROUTES.ts b/src/ROUTES.ts index defb945ba8c2..5739aad8baa6 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -569,6 +569,10 @@ const ROUTES = { route: 'settings/workspaces/:policyID/categories/new', getRoute: (policyID: string) => `settings/workspaces/${policyID}/categories/new` as const, }, + WORKSPACE_CATEGORY_EDIT: { + route: 'workspace/:policyID/categories/:categoryName/edit', + getRoute: (policyID: string, categoryName: string) => `workspace/${policyID}/categories/${encodeURI(categoryName)}/edit` as const, + }, WORKSPACE_TAGS: { route: 'settings/workspaces/:policyID/tags', getRoute: (policyID: string) => `settings/workspaces/${policyID}/tags` as const, @@ -585,6 +589,10 @@ const ROUTES = { route: 'settings/workspaces/:policyID/tags/edit', getRoute: (policyID: string) => `settings/workspaces/${policyID}/tags/edit` as const, }, + WORKSPACE_TAXES: { + route: 'settings/workspaces/:policyID/taxes', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/taxes` as const, + }, WORKSPACE_MEMBER_DETAILS: { route: 'settings/workspaces/:policyID/members/:accountID', getRoute: (policyID: string, accountID: number, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/members/${accountID}`, backTo), @@ -597,7 +605,6 @@ const ROUTES = { route: 'workspace/:policyID/distance-rates', getRoute: (policyID: string) => `workspace/${policyID}/distance-rates` as const, }, - // Referral program promotion REFERRAL_DETAILS_MODAL: { route: 'referral/:contentType', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 4db5fd9115a5..7ccb24aa19e5 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -219,6 +219,7 @@ const SCREENS = { TAGS: 'Workspace_Tags', TAGS_SETTINGS: 'Tags_Settings', TAGS_EDIT: 'Tags_Edit', + TAXES: 'Workspace_Taxes', TAG_CREATE: 'Tag_Create', CURRENCY: 'Workspace_Profile_Currency', WORKFLOWS: 'Workspace_Workflows', @@ -230,6 +231,7 @@ const SCREENS = { SHARE: 'Workspace_Profile_Share', NAME: 'Workspace_Profile_Name', CATEGORY_CREATE: 'Category_Create', + CATEGORY_EDIT: 'Category_Edit', CATEGORY_SETTINGS: 'Category_Settings', CATEGORIES_SETTINGS: 'Categories_Settings', MORE_FEATURES: 'Workspace_More_Features', diff --git a/src/components/AddPlaidBankAccount.js b/src/components/AddPlaidBankAccount.tsx similarity index 71% rename from src/components/AddPlaidBankAccount.js rename to src/components/AddPlaidBankAccount.tsx index b676722f3c6c..e1f686cea6a7 100644 --- a/src/components/AddPlaidBankAccount.js +++ b/src/components/AddPlaidBankAccount.tsx @@ -1,20 +1,19 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import {ActivityIndicator, View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import KeyboardShortcut from '@libs/KeyboardShortcut'; import Log from '@libs/Log'; -import {plaidDataPropTypes} from '@pages/ReimbursementAccount/plaidDataPropTypes'; import * as App from '@userActions/App'; import * as BankAccounts from '@userActions/BankAccounts'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {PlaidData} from '@src/types/onyx'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import FullPageOfflineBlockingView from './BlockingViews/FullPageOfflineBlockingView'; import FormHelpMessage from './FormHelpMessage'; import Icon from './Icon'; @@ -24,103 +23,82 @@ import PlaidLink from './PlaidLink'; import RadioButtons from './RadioButtons'; import Text from './Text'; -const propTypes = { +type AddPlaidBankAccountOnyxProps = { /** If the user has been throttled from Plaid */ - isPlaidDisabled: PropTypes.bool, + isPlaidDisabled: OnyxEntry; + /** Plaid SDK token to use to initialize the widget */ + plaidLinkToken: OnyxEntry; +}; + +type AddPlaidBankAccountProps = AddPlaidBankAccountOnyxProps & { /** Contains plaid data */ - plaidData: plaidDataPropTypes.isRequired, + plaidData: OnyxEntry; /** Selected account ID from the Picker associated with the end of the Plaid flow */ - selectedPlaidAccountID: PropTypes.string, - - /** Plaid SDK token to use to initialize the widget */ - plaidLinkToken: PropTypes.string, + selectedPlaidAccountID?: string; /** Fired when the user exits the Plaid flow */ - onExitPlaid: PropTypes.func, + onExitPlaid?: () => void; /** Fired when the user selects an account */ - onSelect: PropTypes.func, + onSelect?: (plaidAccountID: string) => void; /** Additional text to display */ - text: PropTypes.string, + text?: string; /** The OAuth URI + stateID needed to re-initialize the PlaidLink after the user logs into their bank */ - receivedRedirectURI: PropTypes.string, + receivedRedirectURI?: string; /** During the OAuth flow we need to use the plaidLink token that we initially connected with */ - plaidLinkOAuthToken: PropTypes.string, + plaidLinkOAuthToken?: string; /** If we're updating an existing bank account, what's its bank account ID? */ - bankAccountID: PropTypes.number, + bankAccountID?: number; /** Are we adding a withdrawal account? */ - allowDebit: PropTypes.bool, + allowDebit?: boolean; /** Is displayed in new VBBA */ - isDisplayedInNewVBBA: PropTypes.bool, + isDisplayedInNewVBBA?: boolean; /** Text to display on error message */ - errorText: PropTypes.string, + errorText?: string; /** Function called whenever radio button value changes */ - onInputChange: PropTypes.func, -}; - -const defaultProps = { - selectedPlaidAccountID: '', - plaidLinkToken: '', - onExitPlaid: () => {}, - onSelect: () => {}, - text: '', - receivedRedirectURI: null, - plaidLinkOAuthToken: '', - allowDebit: false, - bankAccountID: 0, - isPlaidDisabled: false, - isDisplayedInNewVBBA: false, - errorText: '', - onInputChange: () => {}, + onInputChange?: (plaidAccountID: string) => void; }; function AddPlaidBankAccount({ plaidData, - selectedPlaidAccountID, + selectedPlaidAccountID = '', plaidLinkToken, - onExitPlaid, - onSelect, - text, + onExitPlaid = () => {}, + onSelect = () => {}, + text = '', receivedRedirectURI, - plaidLinkOAuthToken, - bankAccountID, - allowDebit, + plaidLinkOAuthToken = '', + bankAccountID = 0, + allowDebit = false, isPlaidDisabled, - isDisplayedInNewVBBA, - errorText, - onInputChange, -}) { + isDisplayedInNewVBBA = false, + errorText = '', + onInputChange = () => {}, +}: AddPlaidBankAccountProps) { const theme = useTheme(); const styles = useThemeStyles(); - const plaidBankAccounts = lodashGet(plaidData, 'bankAccounts', []); - const defaultSelectedPlaidAccount = _.find(plaidBankAccounts, (account) => account.plaidAccountID === selectedPlaidAccountID); - const defaultSelectedPlaidAccountID = lodashGet(defaultSelectedPlaidAccount, 'plaidAccountID', ''); - const defaultSelectedPlaidAccountMask = lodashGet( - _.find(plaidBankAccounts, (account) => account.plaidAccountID === selectedPlaidAccountID), - 'mask', - '', - ); - const subscribedKeyboardShortcuts = useRef([]); - const previousNetworkState = useRef(); + const plaidBankAccounts = plaidData?.bankAccounts ?? []; + const defaultSelectedPlaidAccount = plaidBankAccounts.find((account) => account.plaidAccountID === selectedPlaidAccountID); + const defaultSelectedPlaidAccountID = defaultSelectedPlaidAccount?.plaidAccountID ?? ''; + const defaultSelectedPlaidAccountMask = plaidBankAccounts.find((account) => account.plaidAccountID === selectedPlaidAccountID)?.mask ?? ''; + const subscribedKeyboardShortcuts = useRef void>>([]); + const previousNetworkState = useRef(); const [selectedPlaidAccountMask, setSelectedPlaidAccountMask] = useState(defaultSelectedPlaidAccountMask); const {translate} = useLocalize(); const {isOffline} = useNetwork(); - /** - * @returns {String} - */ - const getPlaidLinkToken = () => { + const getPlaidLinkToken = (): string | undefined => { if (plaidLinkToken) { return plaidLinkToken; } @@ -135,7 +113,7 @@ function AddPlaidBankAccount({ * I'm using useCallback so the useEffect which uses this function doesn't run on every render. */ const isAuthenticatedWithPlaid = useCallback( - () => (receivedRedirectURI && plaidLinkOAuthToken) || !_.isEmpty(lodashGet(plaidData, 'bankAccounts')) || !_.isEmpty(lodashGet(plaidData, 'errors')), + () => (!!receivedRedirectURI && !!plaidLinkOAuthToken) || !!plaidData?.bankAccounts?.length || !isEmptyObject(plaidData?.errors), [plaidData, plaidLinkOAuthToken, receivedRedirectURI], ); @@ -144,15 +122,15 @@ function AddPlaidBankAccount({ */ const subscribeToNavigationShortcuts = () => { // find and block the shortcuts - const shortcutsToBlock = _.filter(CONST.KEYBOARD_SHORTCUTS, (x) => x.type === CONST.KEYBOARD_SHORTCUTS_TYPES.NAVIGATION_SHORTCUT); - subscribedKeyboardShortcuts.current = _.map(shortcutsToBlock, (shortcut) => + const shortcutsToBlock = Object.values(CONST.KEYBOARD_SHORTCUTS).filter((shortcut) => 'type' in shortcut && shortcut.type === CONST.KEYBOARD_SHORTCUTS_TYPES.NAVIGATION_SHORTCUT); + subscribedKeyboardShortcuts.current = shortcutsToBlock.map((shortcut) => KeyboardShortcut.subscribe( shortcut.shortcutKey, () => {}, // do nothing shortcut.descriptionKey, shortcut.modifiers, false, - () => lodashGet(plaidData, 'bankAccounts', []).length > 0, // start bubbling when there are bank accounts + () => (plaidData?.bankAccounts ?? []).length > 0, // start bubbling when there are bank accounts ), ); }; @@ -161,7 +139,7 @@ function AddPlaidBankAccount({ * Unblocks the keyboard shortcuts that can navigate */ const unsubscribeToNavigationShortcuts = () => { - _.each(subscribedKeyboardShortcuts.current, (unsubscribe) => unsubscribe()); + subscribedKeyboardShortcuts.current.forEach((unsubscribe) => unsubscribe()); subscribedKeyboardShortcuts.current = []; }; @@ -189,22 +167,21 @@ function AddPlaidBankAccount({ }, [allowDebit, bankAccountID, isAuthenticatedWithPlaid, isOffline]); const token = getPlaidLinkToken(); - const options = _.map(plaidBankAccounts, (account) => ({ + const options = plaidBankAccounts.map((account) => ({ value: account.plaidAccountID, - label: account.addressName, + label: account.addressName ?? '', })); const {icon, iconSize, iconStyles} = getBankIcon({styles}); - const plaidErrors = lodashGet(plaidData, 'errors'); - const plaidDataErrorMessage = !_.isEmpty(plaidErrors) ? _.chain(plaidErrors).values().first().value() : ''; - const bankName = lodashGet(plaidData, 'bankName'); + const plaidErrors = plaidData?.errors; + const plaidDataErrorMessage = !isEmptyObject(plaidErrors) ? (Object.values(plaidErrors)[0] as string) : ''; + const bankName = plaidData?.bankName; /** - * @param {String} plaidAccountID * * When user selects one of plaid accounts we need to set the mask in order to display it on UI */ - const handleSelectingPlaidAccount = (plaidAccountID) => { - const mask = _.find(plaidBankAccounts, (account) => account.plaidAccountID === plaidAccountID).mask; + const handleSelectingPlaidAccount = (plaidAccountID: string) => { + const mask = plaidBankAccounts.find((account) => account.plaidAccountID === plaidAccountID)?.mask ?? ''; setSelectedPlaidAccountMask(mask); onSelect(plaidAccountID); onInputChange(plaidAccountID); @@ -219,26 +196,26 @@ function AddPlaidBankAccount({ } const onError = useCallback((error) => { - Log.hmmm('[PlaidLink] Error: ', error.message); + Log.hmmm('[PlaidLink] Error: ', error?.message); }, []); const renderPlaidLink = () => { - if (Boolean(token) && !bankName) { + if (!!token && !bankName) { return ( { Log.info('[PlaidLink] Success!'); - BankAccounts.openPlaidBankAccountSelector(publicToken, metadata.institution.name, allowDebit, bankAccountID); + BankAccounts.openPlaidBankAccountSelector(publicToken, metadata?.institution?.name ?? '', allowDebit, bankAccountID); }} onError={onError} onEvent={(event, metadata) => { BankAccounts.setPlaidEvent(event); // Handle Plaid login errors (will potentially reset plaid token and item depending on the error) if (event === 'ERROR') { - Log.hmmm('[PlaidLink] Error: ', metadata); - if (bankAccountID && metadata && metadata.error_code) { - BankAccounts.handlePlaidError(bankAccountID, metadata.error_code, metadata.error_message, metadata.request_id); + Log.hmmm('[PlaidLink] Error: ', {...metadata}); + if (bankAccountID && metadata && 'error_code' in metadata) { + BankAccounts.handlePlaidError(bankAccountID, metadata.error_code ?? '', metadata.error_message ?? '', metadata.request_id); } } @@ -259,7 +236,7 @@ function AddPlaidBankAccount({ return {plaidDataErrorMessage}; } - if (lodashGet(plaidData, 'isLoading')) { + if (plaidData?.isLoading) { return ( {translate('bankAccount.chooseAnAccount')} - {!_.isEmpty(text) && {text}} + {!!text && {text}} - {!_.isEmpty(text) && {text}} + {!!text && {text}} ({ plaidLinkToken: { key: ONYXKEYS.PLAID_LINK_TOKEN, initWithStoredValues: false, diff --git a/src/components/AttachmentModal.tsx b/src/components/AttachmentModal.tsx index 1ed7b6d188a0..0c047ce52dc8 100755 --- a/src/components/AttachmentModal.tsx +++ b/src/components/AttachmentModal.tsx @@ -569,6 +569,7 @@ function AttachmentModal({