diff --git a/src/CONST.ts b/src/CONST.ts index 43921d9a4457..3d69c83c5c22 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -963,6 +963,7 @@ const CONST = { GUIDES_DOMAIN: 'team.expensify.com', HELP: 'help@expensify.com', INTEGRATION_TESTING_CREDS: 'integrationtestingcreds@expensify.com', + NOTIFICATIONS: 'notifications@expensify.com', PAYROLL: 'payroll@expensify.com', QA: 'qa@expensify.com', QA_TRAVIS: 'qa+travisreceipts@expensify.com', @@ -982,6 +983,7 @@ const CONST = { FIRST_RESPONDER: Number(Config?.EXPENSIFY_ACCOUNT_ID_FIRST_RESPONDER ?? 9375152), HELP: Number(Config?.EXPENSIFY_ACCOUNT_ID_HELP ?? -1), INTEGRATION_TESTING_CREDS: Number(Config?.EXPENSIFY_ACCOUNT_ID_INTEGRATION_TESTING_CREDS ?? -1), + NOTIFICATIONS: Number(Config?.EXPENSIFY_ACCOUNT_ID_NOTIFICATIONS ?? 11665625), PAYROLL: Number(Config?.EXPENSIFY_ACCOUNT_ID_PAYROLL ?? 9679724), QA: Number(Config?.EXPENSIFY_ACCOUNT_ID_QA ?? 3126513), QA_TRAVIS: Number(Config?.EXPENSIFY_ACCOUNT_ID_QA_TRAVIS ?? 8595733), @@ -1408,6 +1410,7 @@ const CONST = { this.EMAIL.FIRST_RESPONDER, this.EMAIL.HELP, this.EMAIL.INTEGRATION_TESTING_CREDS, + this.EMAIL.NOTIFICATIONS, this.EMAIL.PAYROLL, this.EMAIL.QA, this.EMAIL.QA_TRAVIS, diff --git a/src/components/FormSubmit/formSubmitPropTypes.js b/src/components/FormSubmit/formSubmitPropTypes.js deleted file mode 100644 index 306b3544cc11..000000000000 --- a/src/components/FormSubmit/formSubmitPropTypes.js +++ /dev/null @@ -1,27 +0,0 @@ -import PropTypes from 'prop-types'; -import stylePropTypes from '@styles/stylePropTypes'; - -const propTypes = { - /** A reference forwarded to the inner View */ - innerRef: PropTypes.oneOfType([ - PropTypes.func, - // eslint-disable-next-line react/forbid-prop-types - PropTypes.shape({current: PropTypes.any}), - ]), - - /* A function to execute when form is submitted with ENTER */ - onSubmit: PropTypes.func.isRequired, - - /** Children to wrap with FormSubmit. */ - children: PropTypes.node.isRequired, - - /** Container styles */ - style: stylePropTypes, -}; - -const defaultProps = { - innerRef: undefined, - style: [], -}; - -export {propTypes, defaultProps}; diff --git a/src/components/FormSubmit/index.native.js b/src/components/FormSubmit/index.native.js deleted file mode 100644 index 29a8678695d5..000000000000 --- a/src/components/FormSubmit/index.native.js +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; -import {View} from 'react-native'; -import * as formSubmitPropTypes from './formSubmitPropTypes'; - -const FormSubmit = React.forwardRef((props, ref) => ( - - {props.children} - -)); - -FormSubmit.propTypes = formSubmitPropTypes.propTypes; -FormSubmit.defaultProps = formSubmitPropTypes.defaultProps; -FormSubmit.displayName = 'FormSubmit'; - -export default FormSubmit; diff --git a/src/components/FormSubmit/index.native.tsx b/src/components/FormSubmit/index.native.tsx new file mode 100644 index 000000000000..22bf5353949d --- /dev/null +++ b/src/components/FormSubmit/index.native.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import {View} from 'react-native'; +import {FormSubmitProps, FormSubmitRef} from './types'; + +function FormSubmit({style, children}: FormSubmitProps, ref: FormSubmitRef) { + return ( + + {children} + + ); +} + +FormSubmit.displayName = 'FormSubmit'; + +export default React.forwardRef(FormSubmit); diff --git a/src/components/FormSubmit/index.js b/src/components/FormSubmit/index.tsx similarity index 65% rename from src/components/FormSubmit/index.js rename to src/components/FormSubmit/index.tsx index a4e9e960291c..ef3f3c39bbaa 100644 --- a/src/components/FormSubmit/index.js +++ b/src/components/FormSubmit/index.tsx @@ -1,23 +1,23 @@ -import lodashGet from 'lodash/get'; -import React, {useEffect} from 'react'; +import React, {KeyboardEvent, useEffect} from 'react'; import {View} from 'react-native'; import * as ComponentUtils from '@libs/ComponentUtils'; import isEnterWhileComposition from '@libs/KeyboardShortcut/isEnterWhileComposition'; import CONST from '@src/CONST'; -import * as formSubmitPropTypes from './formSubmitPropTypes'; +import {FormSubmitProps, FormSubmitRef} from './types'; -function FormSubmit({innerRef, children, onSubmit, style}) { +function FormSubmit({children, onSubmit, style}: FormSubmitProps, ref: FormSubmitRef) { /** * Calls the submit callback when ENTER is pressed on a form element. - * @param {Object} event */ - const submitForm = (event) => { + const submitForm = (event: KeyboardEvent) => { // ENTER is pressed with modifier key or during text composition, do not submit the form if (event.shiftKey || event.key !== CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey || isEnterWhileComposition(event)) { return; } - const tagName = lodashGet(event, 'target.tagName', ''); + const eventTarget = event.target as HTMLElement; + + const tagName = eventTarget?.tagName ?? ''; // ENTER is pressed on INPUT or SELECT element, call the submit callback. if (tagName === 'INPUT' || tagName === 'SELECT') { @@ -26,22 +26,30 @@ function FormSubmit({innerRef, children, onSubmit, style}) { } // Pressing Enter on TEXTAREA element adds a new line. When `dataset.submitOnEnter` prop is passed, call the submit callback. - if (tagName === 'TEXTAREA' && lodashGet(event, 'target.dataset.submitOnEnter', 'false') === 'true') { + if (tagName === 'TEXTAREA' && (eventTarget?.dataset?.submitOnEnter ?? 'false') === 'true') { event.preventDefault(); onSubmit(); return; } // ENTER is pressed on checkbox element, call the submit callback. - if (lodashGet(event, 'target.role') === 'checkbox') { + if (eventTarget?.role === 'checkbox') { onSubmit(); } }; - const preventDefaultFormBehavior = (e) => e.preventDefault(); + const preventDefaultFormBehavior = (e: SubmitEvent) => e.preventDefault(); useEffect(() => { - const form = innerRef.current; + if (!(ref && 'current' in ref)) { + return; + } + + const form = ref.current as HTMLFormElement | null; + + if (!form) { + return; + } // Prevent the browser from applying its own validation, which affects the email input form.setAttribute('novalidate', ''); @@ -55,7 +63,7 @@ function FormSubmit({innerRef, children, onSubmit, style}) { form.removeEventListener('submit', preventDefaultFormBehavior); }; - }, [innerRef]); + }, [ref]); return ( // React-native-web prevents event bubbling on TextInput for key presses @@ -63,7 +71,7 @@ function FormSubmit({innerRef, children, onSubmit, style}) { // Thus use capture phase. @@ -72,17 +80,6 @@ function FormSubmit({innerRef, children, onSubmit, style}) { ); } -FormSubmit.propTypes = formSubmitPropTypes.propTypes; -FormSubmit.defaultProps = formSubmitPropTypes.defaultProps; - -const FormSubmitWithRef = React.forwardRef((props, ref) => ( - -)); - -FormSubmitWithRef.displayName = 'FormSubmitWithRef'; +FormSubmit.displayName = 'FormSubmitWithRef'; -export default FormSubmitWithRef; +export default React.forwardRef(FormSubmit); diff --git a/src/components/FormSubmit/types.ts b/src/components/FormSubmit/types.ts new file mode 100644 index 000000000000..20910647ecb8 --- /dev/null +++ b/src/components/FormSubmit/types.ts @@ -0,0 +1,12 @@ +import React, {ForwardedRef} from 'react'; +import {StyleProp, View, ViewStyle} from 'react-native'; + +type FormSubmitProps = { + children: React.ReactNode; + onSubmit: () => void; + style?: StyleProp; +}; + +type FormSubmitRef = ForwardedRef; + +export type {FormSubmitProps, FormSubmitRef}; diff --git a/src/libs/KeyboardShortcut/isEnterWhileComposition.ts b/src/libs/KeyboardShortcut/isEnterWhileComposition.ts index 51e198f1c2d1..a752f8a24811 100644 --- a/src/libs/KeyboardShortcut/isEnterWhileComposition.ts +++ b/src/libs/KeyboardShortcut/isEnterWhileComposition.ts @@ -1,3 +1,4 @@ +import React from 'react'; import {NativeSyntheticEvent} from 'react-native'; import * as Browser from '@libs/Browser'; import CONST from '@src/CONST'; @@ -6,7 +7,7 @@ import CONST from '@src/CONST'; * Check if the Enter key was pressed during IME confirmation (i.e. while the text is being composed). * See {@link https://en.wikipedia.org/wiki/Input_method} */ -const isEnterWhileComposition = (event: KeyboardEvent): boolean => { +const isEnterWhileComposition = (event: KeyboardEvent | React.KeyboardEvent): boolean => { // if on mobile chrome, the enter key event is never fired when the enter key is pressed while composition. if (Browser.isMobileChrome()) { return false; diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 5fed34cc8180..ddd3fd6e4f87 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -1218,7 +1218,7 @@ function getOptions( } // Exclude the current user from the personal details list - const optionsToExclude = [{login: currentUserLogin}]; + const optionsToExclude = [{login: currentUserLogin}, {login: CONST.EMAIL.NOTIFICATIONS}]; // If we're including selected options from the search results, we only want to exclude them if the search input is empty // This is because on certain pages, we show the selected options at the top when the search input is empty @@ -1240,6 +1240,11 @@ function getOptions( break; } + // Skip notifications@expensify.com + if (reportOption.login === CONST.EMAIL.NOTIFICATIONS) { + continue; + } + const isCurrentUserOwnedPolicyExpenseChatThatCouldShow = reportOption.isPolicyExpenseChat && reportOption.ownerAccountID === currentUserAccountID && includeOwnedWorkspaceChats && !reportOption.isArchivedRoom; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index bac1736f07f7..70fd41937fa0 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3430,6 +3430,8 @@ function shouldReportBeInOptionList( report?.reportName === undefined || // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing report?.isHidden || + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + report?.participantAccountIDs?.includes(CONST.ACCOUNT_ID.NOTIFICATIONS) || (report?.participantAccountIDs?.length === 0 && !isChatThread(report) && !isPublicRoom(report) && diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index c50f868cae99..39bce4bf067a 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -156,7 +156,9 @@ function ReportActionItemSingle(props) { }, [isWorkspaceActor, reportID, actorAccountID, props.action.delegateAccountID, iouReportID, displayAllActors]); const shouldDisableDetailPage = useMemo( - () => !isWorkspaceActor && ReportUtils.isOptimisticPersonalDetail(props.action.delegateAccountID ? props.action.delegateAccountID : actorAccountID), + () => + actorAccountID === CONST.ACCOUNT_ID.NOTIFICATIONS || + (!isWorkspaceActor && ReportUtils.isOptimisticPersonalDetail(props.action.delegateAccountID ? props.action.delegateAccountID : actorAccountID)), [props.action, isWorkspaceActor, actorAccountID], ); diff --git a/src/pages/iou/WaypointEditor.js b/src/pages/iou/WaypointEditor.js index f50ae766ac88..7d5362251715 100644 --- a/src/pages/iou/WaypointEditor.js +++ b/src/pages/iou/WaypointEditor.js @@ -2,13 +2,13 @@ import {useNavigation} from '@react-navigation/native'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useMemo, useRef, useState} from 'react'; -import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import AddressSearch from '@components/AddressSearch'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import ConfirmModal from '@components/ConfirmModal'; -import Form from '@components/Form'; +import FormProvider from '@components/Form/FormProvider'; +import InputWrapper from '@components/Form/InputWrapper'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; import ScreenWrapper from '@components/ScreenWrapper'; @@ -209,7 +209,7 @@ function WaypointEditor({route: {params: {iouType = '', transactionID = '', wayp cancelText={translate('common.cancel')} danger /> -
- - (textInput.current = e)} - hint={!isOffline ? 'distance.errors.selectSuggestedAddress' : ''} - containerStyles={[styles.mt3]} - label={translate('distance.address')} - defaultValue={waypointAddress} - onPress={selectWaypoint} - maxInputLength={CONST.FORM_CHARACTER_LIMIT} - renamedInputKeys={{ - address: `waypoint${waypointIndex}`, - city: null, - country: null, - street: null, - street2: null, - zipCode: null, - lat: null, - lng: null, - state: null, - }} - predefinedPlaces={recentWaypoints} - resultTypes="" - /> - -
+ (textInput.current = e)} + hint={!isOffline ? 'distance.errors.selectSuggestedAddress' : ''} + containerStyles={[styles.mt3]} + label={translate('distance.address')} + defaultValue={waypointAddress} + onPress={selectWaypoint} + maxInputLength={CONST.FORM_CHARACTER_LIMIT} + renamedInputKeys={{ + address: `waypoint${waypointIndex}`, + city: null, + country: null, + street: null, + street2: null, + zipCode: null, + lat: null, + lng: null, + state: null, + }} + predefinedPlaces={recentWaypoints} + resultTypes="" + /> + );