From 5d914f18ea23782ebaf05997b3b2a19cdba1a04f Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Sat, 19 Aug 2023 14:27:10 +0200 Subject: [PATCH 01/97] Refactor comment message rendering ...so the logic for rendering attachments is clearly separated from the logic for rendering textual comments. This fixes #25415 --- .../home/report/ReportActionItemFragment.js | 81 ++++++---------- .../home/report/ReportActionItemMessage.js | 8 +- .../comment/AttachmentCommentFragment.js | 32 ++++++ .../home/report/comment/RenderCommentHTML.js | 22 +++++ .../report/comment/TextCommentFragment.js | 97 +++++++++++++++++++ 5 files changed, 184 insertions(+), 56 deletions(-) create mode 100644 src/pages/home/report/comment/AttachmentCommentFragment.js create mode 100644 src/pages/home/report/comment/RenderCommentHTML.js create mode 100644 src/pages/home/report/comment/TextCommentFragment.js diff --git a/src/pages/home/report/ReportActionItemFragment.js b/src/pages/home/report/ReportActionItemFragment.js index 24501e307759..b50592c20af7 100644 --- a/src/pages/home/report/ReportActionItemFragment.js +++ b/src/pages/home/report/ReportActionItemFragment.js @@ -1,23 +1,19 @@ import React, {memo} from 'react'; import PropTypes from 'prop-types'; -import Str from 'expensify-common/lib/str'; import reportActionFragmentPropTypes from './reportActionFragmentPropTypes'; import styles from '../../../styles/styles'; -import variables from '../../../styles/variables'; -import themeColors from '../../../styles/themes/default'; import RenderHTML from '../../../components/RenderHTML'; import Text from '../../../components/Text'; -import * as EmojiUtils from '../../../libs/EmojiUtils'; import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions'; import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; -import * as DeviceCapabilities from '../../../libs/DeviceCapabilities'; import compose from '../../../libs/compose'; -import convertToLTR from '../../../libs/convertToLTR'; import {withNetwork} from '../../../components/OnyxProvider'; import CONST from '../../../CONST'; -import editedLabelStyles from '../../../styles/editedLabelStyles'; import UserDetailsTooltip from '../../../components/UserDetailsTooltip'; import avatarPropTypes from '../../../components/avatarPropTypes'; +import * as ReportUtils from '../../../libs/ReportUtils'; +import AttachmentCommentFragment from './comment/AttachmentCommentFragment'; +import TextCommentFragment from './comment/TextCommentFragment'; const propTypes = { /** Users accountID */ @@ -62,6 +58,9 @@ const propTypes = { /** Whether the comment is a thread parent message/the first message in a thread */ isThreadParentMessage: PropTypes.bool, + /** Should the comment have the appearance of being grouped with the previous comment? */ + displayAsGroup: PropTypes.bool.isRequired, + ...windowDimensionsPropTypes, /** localization props */ @@ -85,62 +84,40 @@ const defaultProps = { }; function ReportActionItemFragment(props) { - switch (props.fragment.type) { + const fragment = props.fragment; + + switch (fragment.type) { case 'COMMENT': { - const {html, text} = props.fragment; - const isPendingDelete = props.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && props.network.isOffline; + const isPendingDelete = props.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; // Threaded messages display "[Deleted message]" instead of being hidden altogether. // While offline we display the previous message with a strikethrough style. Once online we want to // immediately display "[Deleted message]" while the delete action is pending. - if ((!props.network.isOffline && props.isThreadParentMessage && props.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) || props.fragment.isDeletedParentAction) { + if ((!props.network.isOffline && props.isThreadParentMessage && isPendingDelete) || props.fragment.isDeletedParentAction) { return ${props.translate('parentReportAction.deletedMessage')}`} />; } - // If the only difference between fragment.text and fragment.html is
tags - // we render it as text, not as html. - // This is done to render emojis with line breaks between them as text. - const differByLineBreaksOnly = Str.replaceAll(html, '
', '\n') === text; - - // Only render HTML if we have html in the fragment - if (!differByLineBreaksOnly) { - const editedTag = props.fragment.isEdited ? `` : ''; - const htmlContent = isPendingDelete ? `${html}` : html; - - const htmlWithTag = editedTag ? `${htmlContent}${editedTag}` : htmlContent; - - return ${htmlWithTag}` : `${htmlWithTag}`} />; + // Does the fragment content represent an attachment? + const isFragmentAttachment = ReportUtils.isReportMessageAttachment(fragment); + + if (isFragmentAttachment) { + return ( + + ); } - const containsOnlyEmojis = EmojiUtils.containsOnlyEmojis(text); return ( - - - {convertToLTR(props.iouMessage || text)} - - {Boolean(props.fragment.isEdited) && ( - <> - - {' '} - - - {props.translate('reportActionCompose.edited')} - - - )} - + ); } case 'TEXT': @@ -154,7 +131,7 @@ function ReportActionItemFragment(props) { numberOfLines={props.isSingleLine ? 1 : undefined} style={[styles.chatItemMessageHeaderSender, props.isSingleLine ? styles.pre : styles.preWrap]} > - {props.fragment.text} + {fragment.text} ); diff --git a/src/pages/home/report/ReportActionItemMessage.js b/src/pages/home/report/ReportActionItemMessage.js index a3d8494c38de..024ba9a9388f 100644 --- a/src/pages/home/report/ReportActionItemMessage.js +++ b/src/pages/home/report/ReportActionItemMessage.js @@ -36,8 +36,7 @@ const defaultProps = { }; function ReportActionItemMessage(props) { - const messages = _.compact(props.action.previousMessage || props.action.message); - const isAttachment = ReportUtils.isReportMessageAttachment(_.last(messages)); + const fragments = _.compact(props.action.previousMessage || props.action.message); const isIOUReport = ReportActionsUtils.isMoneyRequestAction(props.action); let iouMessage; if (isIOUReport) { @@ -48,9 +47,9 @@ function ReportActionItemMessage(props) { } return ( - + {!props.isHidden ? ( - _.map(messages, (fragment, index) => ( + _.map(fragments, (fragment, index) => ( )) diff --git a/src/pages/home/report/comment/AttachmentCommentFragment.js b/src/pages/home/report/comment/AttachmentCommentFragment.js new file mode 100644 index 000000000000..2d019c7d89e0 --- /dev/null +++ b/src/pages/home/report/comment/AttachmentCommentFragment.js @@ -0,0 +1,32 @@ +import React from 'react'; +import {View} from 'react-native'; +import PropTypes from 'prop-types'; +import styles from '../../../../styles/styles'; +import RenderCommentHTML from './RenderCommentHTML'; + +const propTypes = { + /** The reportAction's source */ + source: PropTypes.oneOf(['Chronos', 'email', 'ios', 'android', 'web', 'email', '']).isRequired, + + /** The message fragment's HTML */ + html: PropTypes.string.isRequired, + + /** Should extra margin be added on top of the component? */ + addExtraMargin: PropTypes.bool.isRequired, +}; + +function AttachmentCommentFragment(props) { + return ( + + + + ); +} + +AttachmentCommentFragment.propTypes = propTypes; +AttachmentCommentFragment.displayName = 'AttachmentCommentFragment'; + +export default AttachmentCommentFragment; diff --git a/src/pages/home/report/comment/RenderCommentHTML.js b/src/pages/home/report/comment/RenderCommentHTML.js new file mode 100644 index 000000000000..216fdfc58025 --- /dev/null +++ b/src/pages/home/report/comment/RenderCommentHTML.js @@ -0,0 +1,22 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import RenderHTML from '../../../../components/RenderHTML'; + +const propTypes = { + /** The reportAction's source */ + source: PropTypes.oneOf(['Chronos', 'email', 'ios', 'android', 'web', 'email', '']).isRequired, + + /** The comment's HTML */ + html: PropTypes.string.isRequired, +}; + +function RenderCommentHTML(props) { + const html = props.html; + + return ${html}` : `${html}`} />; +} + +RenderCommentHTML.propTypes = propTypes; +RenderCommentHTML.displayName = 'RenderCommentHTML'; + +export default RenderCommentHTML; diff --git a/src/pages/home/report/comment/TextCommentFragment.js b/src/pages/home/report/comment/TextCommentFragment.js new file mode 100644 index 000000000000..ef72707c317f --- /dev/null +++ b/src/pages/home/report/comment/TextCommentFragment.js @@ -0,0 +1,97 @@ +import React, {memo} from 'react'; +import PropTypes from 'prop-types'; +import Str from 'expensify-common/lib/str'; +import reportActionFragmentPropTypes from '../reportActionFragmentPropTypes'; +import styles from '../../../../styles/styles'; +import variables from '../../../../styles/variables'; +import themeColors from '../../../../styles/themes/default'; +import Text from '../../../../components/Text'; +import * as EmojiUtils from '../../../../libs/EmojiUtils'; +import withWindowDimensions, {windowDimensionsPropTypes} from '../../../../components/withWindowDimensions'; +import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; +import * as DeviceCapabilities from '../../../../libs/DeviceCapabilities'; +import compose from '../../../../libs/compose'; +import convertToLTR from '../../../../libs/convertToLTR'; +import CONST from '../../../../CONST'; +import editedLabelStyles from '../../../../styles/editedLabelStyles'; +import RenderCommentHTML from './RenderCommentHTML'; + +const propTypes = { + /** The reportAction's source */ + source: PropTypes.oneOf(['Chronos', 'email', 'ios', 'android', 'web', 'email', '']).isRequired, + + /** The message fragment needing to be displayed */ + fragment: reportActionFragmentPropTypes.isRequired, + + /** Should this message fragment be styled as deleted? */ + styleAsDeleted: PropTypes.bool.isRequired, + + /** Additional styles to add after local styles. */ + style: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]).isRequired, + + ...windowDimensionsPropTypes, + + /** localization props */ + ...withLocalizePropTypes, +}; + +function TextCommentFragment(props) { + const {fragment, styleAsDeleted} = props; + const {html, text} = fragment; + + // If the only difference between fragment.text and fragment.html is
tags + // we render it as text, not as html. + // This is done to render emojis with line breaks between them as text. + const differByLineBreaksOnly = Str.replaceAll(html, '
', '\n') === text; + + // Only render HTML if we have html in the fragment + if (!differByLineBreaksOnly) { + const editedTag = fragment.isEdited ? `` : ''; + const htmlContent = styleAsDeleted ? `${html}` : html; + + const htmlWithTag = editedTag ? `${htmlContent}${editedTag}` : htmlContent; + + return ( + + ); + } + + const containsOnlyEmojis = EmojiUtils.containsOnlyEmojis(text); + + return ( + + + {convertToLTR(props.iouMessage || text)} + + {Boolean(fragment.isEdited) && ( + <> + + {' '} + + + {props.translate('reportActionCompose.edited')} + + + )} + + ); +} + +TextCommentFragment.propTypes = propTypes; +TextCommentFragment.displayName = 'TextCommentFragment'; + +export default compose(withWindowDimensions, withLocalize)(memo(TextCommentFragment)); From 0e218bfcb59045a6dd41c9dfa0e07a43bd7667bf Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Mon, 2 Oct 2023 13:40:36 +0200 Subject: [PATCH 02/97] TextCommentFragment: Add iouMessage prop --- src/pages/home/report/ReportActionItemFragment.js | 1 + src/pages/home/report/comment/TextCommentFragment.js | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/pages/home/report/ReportActionItemFragment.js b/src/pages/home/report/ReportActionItemFragment.js index b50592c20af7..73fa448db1db 100644 --- a/src/pages/home/report/ReportActionItemFragment.js +++ b/src/pages/home/report/ReportActionItemFragment.js @@ -116,6 +116,7 @@ function ReportActionItemFragment(props) { source={props.source} fragment={fragment} styleAsDeleted={isPendingDelete && props.network.isOffline} + iouMessage={props.iouMessage} style={props.style} /> ); diff --git a/src/pages/home/report/comment/TextCommentFragment.js b/src/pages/home/report/comment/TextCommentFragment.js index ef72707c317f..c124728a49d1 100644 --- a/src/pages/home/report/comment/TextCommentFragment.js +++ b/src/pages/home/report/comment/TextCommentFragment.js @@ -26,6 +26,9 @@ const propTypes = { /** Should this message fragment be styled as deleted? */ styleAsDeleted: PropTypes.bool.isRequired, + /** Text of an IOU report action */ + iouMessage: PropTypes.string, + /** Additional styles to add after local styles. */ style: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]).isRequired, @@ -35,6 +38,10 @@ const propTypes = { ...withLocalizePropTypes, }; +const defaultProps = { + iouMessage: undefined, +} + function TextCommentFragment(props) { const {fragment, styleAsDeleted} = props; const {html, text} = fragment; @@ -92,6 +99,7 @@ function TextCommentFragment(props) { } TextCommentFragment.propTypes = propTypes; +TextCommentFragment.defaultProps = defaultProps; TextCommentFragment.displayName = 'TextCommentFragment'; export default compose(withWindowDimensions, withLocalize)(memo(TextCommentFragment)); From 4882e00c82b09f77ad8000bdf56752914b753f70 Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Mon, 2 Oct 2023 14:34:27 +0200 Subject: [PATCH 03/97] Add a missing semicolon --- src/pages/home/report/comment/TextCommentFragment.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/comment/TextCommentFragment.js b/src/pages/home/report/comment/TextCommentFragment.js index c124728a49d1..aed0730b8c59 100644 --- a/src/pages/home/report/comment/TextCommentFragment.js +++ b/src/pages/home/report/comment/TextCommentFragment.js @@ -40,7 +40,7 @@ const propTypes = { const defaultProps = { iouMessage: undefined, -} +}; function TextCommentFragment(props) { const {fragment, styleAsDeleted} = props; From 6fd4a81d0154b6080bbaee3f62988f403ea1640e Mon Sep 17 00:00:00 2001 From: Mahesh Date: Sat, 14 Oct 2023 00:58:14 +0530 Subject: [PATCH 04/97] update search icon & styles for search input --- docs/_sass/_search-bar.scss | 2 -- docs/assets/js/main.js | 37 ++++++++++++++++++------------------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/docs/_sass/_search-bar.scss b/docs/_sass/_search-bar.scss index e9c56835af50..c2185ef8f36a 100644 --- a/docs/_sass/_search-bar.scss +++ b/docs/_sass/_search-bar.scss @@ -210,9 +210,7 @@ label.search-label { width: auto; } -/* Change the path of the Google Search Button icon into Expensify icon */ .gsc-search-button.gsc-search-button-v2 svg path { - d: path('M8 1c3.9 0 7 3.1 7 7 0 1.4-.4 2.7-1.1 3.8l5.2 5.2c.6.6.6 1.5 0 2.1-.6.6-1.5.6-2.1 0l-5.2-5.2C10.7 14.6 9.4 15 8 15c-3.9 0-7-3.1-7-7s3.1-7 7-7zm0 3c2.2 0 4 1.8 4 4s-1.8 4-4 4-4-1.8-4-4 1.8-4 4-4z'); fill-rule: evenodd; clip-rule: evenodd; } diff --git a/docs/assets/js/main.js b/docs/assets/js/main.js index aebd0f5d4864..50ac9cfa7e41 100644 --- a/docs/assets/js/main.js +++ b/docs/assets/js/main.js @@ -120,25 +120,12 @@ function changeSVGViewBoxGoogle() { // Get all inline Google SVG elements on the page const svgsGoogle = document.querySelectorAll('svg'); - // Create a media query for screens wider than tablet - const mediaQuery = window.matchMedia('(min-width: 800px)'); - - // Check if the viewport is smaller than tablet - if (!mediaQuery.matches) { - Array.from(svgsGoogle).forEach((svg) => { - // Set the viewBox attribute to '0 0 13 13' to make the svg fit in the mobile view - svg.setAttribute('viewBox', '0 0 13 13'); - svg.setAttribute('height', '13'); - svg.setAttribute('width', '13'); - }); - } else { - Array.from(svgsGoogle).forEach((svg) => { - // Set the viewBox attribute to '0 0 20 20' to make the svg fit in the tablet-desktop view - svg.setAttribute('viewBox', '0 0 20 20'); - svg.setAttribute('height', '16'); - svg.setAttribute('width', '16'); - }); - } + Array.from(svgsGoogle).forEach((svg) => { + // Set the viewBox attribute to '0 0 13 13' to make the svg fit in the mobile view + svg.setAttribute('viewBox', '0 0 20 20'); + svg.setAttribute('height', '16'); + svg.setAttribute('width', '16'); + }); } // Function to insert element after another @@ -147,11 +134,23 @@ function insertElementAfter(referenceNode, newNode) { referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); } +// Update the ICON for search input. +/* Change the path of the Google Search Button icon into Expensify icon */ +function updateGoogleSearchIcon() { + const node = document.querySelector('.gsc-search-button.gsc-search-button-v2 svg path'); + node.setAttribute( + 'd', + 'M8 1c3.9 0 7 3.1 7 7 0 1.4-.4 2.7-1.1 3.8l5.2 5.2c.6.6.6 1.5 0 2.1-.6.6-1.5.6-2.1 0l-5.2-5.2C10.7 14.6 9.4 15 8 15c-3.9 0-7-3.1-7-7s3.1-7 7-7zm0 3c2.2 0 4 1.8 4 4s-1.8 4-4 4-4-1.8-4-4 1.8-4 4-4z', + ); +} + // Need to wait up until page is load, so the svg viewBox can be changed // And the search label can be inserted window.addEventListener('load', () => { changeSVGViewBoxGoogle(); + updateGoogleSearchIcon(); + // Add required into the search input const searchInput = document.getElementById('gsc-i-id1'); searchInput.setAttribute('required', ''); From d5186d4eeedc8262c2f74037aa86d7baa5d0e6d6 Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Mon, 16 Oct 2023 10:06:21 +0200 Subject: [PATCH 05/97] Extract the reportAction's source prop type to a separate file --- src/pages/home/report/comment/AttachmentCommentFragment.js | 3 ++- src/pages/home/report/comment/RenderCommentHTML.js | 3 ++- src/pages/home/report/comment/TextCommentFragment.js | 3 ++- src/pages/home/report/reportActionSourcePropType.js | 3 +++ 4 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 src/pages/home/report/reportActionSourcePropType.js diff --git a/src/pages/home/report/comment/AttachmentCommentFragment.js b/src/pages/home/report/comment/AttachmentCommentFragment.js index 2d019c7d89e0..3b03749f113e 100644 --- a/src/pages/home/report/comment/AttachmentCommentFragment.js +++ b/src/pages/home/report/comment/AttachmentCommentFragment.js @@ -3,10 +3,11 @@ import {View} from 'react-native'; import PropTypes from 'prop-types'; import styles from '../../../../styles/styles'; import RenderCommentHTML from './RenderCommentHTML'; +import reportActionSourcePropType from '../reportActionSourcePropType'; const propTypes = { /** The reportAction's source */ - source: PropTypes.oneOf(['Chronos', 'email', 'ios', 'android', 'web', 'email', '']).isRequired, + source: reportActionSourcePropType.isRequired, /** The message fragment's HTML */ html: PropTypes.string.isRequired, diff --git a/src/pages/home/report/comment/RenderCommentHTML.js b/src/pages/home/report/comment/RenderCommentHTML.js index 216fdfc58025..ef9d3ffcb8f1 100644 --- a/src/pages/home/report/comment/RenderCommentHTML.js +++ b/src/pages/home/report/comment/RenderCommentHTML.js @@ -1,10 +1,11 @@ import React from 'react'; import PropTypes from 'prop-types'; import RenderHTML from '../../../../components/RenderHTML'; +import reportActionSourcePropType from '../reportActionSourcePropType'; const propTypes = { /** The reportAction's source */ - source: PropTypes.oneOf(['Chronos', 'email', 'ios', 'android', 'web', 'email', '']).isRequired, + source: reportActionSourcePropType.isRequired, /** The comment's HTML */ html: PropTypes.string.isRequired, diff --git a/src/pages/home/report/comment/TextCommentFragment.js b/src/pages/home/report/comment/TextCommentFragment.js index aed0730b8c59..e51eabcec720 100644 --- a/src/pages/home/report/comment/TextCommentFragment.js +++ b/src/pages/home/report/comment/TextCommentFragment.js @@ -2,6 +2,7 @@ import React, {memo} from 'react'; import PropTypes from 'prop-types'; import Str from 'expensify-common/lib/str'; import reportActionFragmentPropTypes from '../reportActionFragmentPropTypes'; +import reportActionSourcePropType from '../reportActionSourcePropType'; import styles from '../../../../styles/styles'; import variables from '../../../../styles/variables'; import themeColors from '../../../../styles/themes/default'; @@ -18,7 +19,7 @@ import RenderCommentHTML from './RenderCommentHTML'; const propTypes = { /** The reportAction's source */ - source: PropTypes.oneOf(['Chronos', 'email', 'ios', 'android', 'web', 'email', '']).isRequired, + source: reportActionSourcePropType.isRequired, /** The message fragment needing to be displayed */ fragment: reportActionFragmentPropTypes.isRequired, diff --git a/src/pages/home/report/reportActionSourcePropType.js b/src/pages/home/report/reportActionSourcePropType.js new file mode 100644 index 000000000000..0ad9662eb693 --- /dev/null +++ b/src/pages/home/report/reportActionSourcePropType.js @@ -0,0 +1,3 @@ +import PropTypes from 'prop-types'; + +export default PropTypes.oneOf(['Chronos', 'email', 'ios', 'android', 'web', 'email', '']); From c0f2af25f40f3a2bbf637a07864f9064042dd76b Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Thu, 19 Oct 2023 14:20:47 +0200 Subject: [PATCH 06/97] Remove a left-over of local testing --- src/libs/Permissions.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index 9a5a538dda6f..13489c396c3c 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -43,7 +43,6 @@ function canUseCustomStatus(betas: Beta[]): boolean { } function canUseCategories(betas: Beta[]): boolean { - return true; return betas?.includes(CONST.BETAS.NEW_DOT_CATEGORIES) || canUseAllBetas(betas); } From eebfbf1a08cf5d2f3bf18964d5986a3404c8303a Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Fri, 20 Oct 2023 13:41:21 +0200 Subject: [PATCH 07/97] migrate withWindowDimensions directory to TypeScript --- .../{index.native.js => index.native.tsx} | 55 +++++++++---------- .../{index.js => index.tsx} | 54 +++++++++--------- src/components/withWindowDimensions/types.ts | 30 ++++++++++ 3 files changed, 82 insertions(+), 57 deletions(-) rename src/components/withWindowDimensions/{index.native.js => index.native.tsx} (65%) rename src/components/withWindowDimensions/{index.js => index.tsx} (68%) create mode 100644 src/components/withWindowDimensions/types.ts diff --git a/src/components/withWindowDimensions/index.native.js b/src/components/withWindowDimensions/index.native.tsx similarity index 65% rename from src/components/withWindowDimensions/index.native.js rename to src/components/withWindowDimensions/index.native.tsx index 363196b3fd4d..889397f2bab6 100644 --- a/src/components/withWindowDimensions/index.native.js +++ b/src/components/withWindowDimensions/index.native.tsx @@ -1,12 +1,14 @@ -import React, {forwardRef, createContext, useState, useEffect, useMemo} from 'react'; +import React, {createContext, useState, useEffect, useMemo, ComponentType, RefAttributes, ForwardedRef} from 'react'; import PropTypes from 'prop-types'; -import {Dimensions} from 'react-native'; +import {Dimensions, ScaledSize} from 'react-native'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; import getComponentDisplayName from '../../libs/getComponentDisplayName'; import variables from '../../styles/variables'; import getWindowHeightAdjustment from '../../libs/getWindowHeightAdjustment'; +import {WindowDimensionsContextData, WindowDimensionsProps} from './types'; +import ChildrenProps from '../../types/utils/ChildrenProps'; -const WindowDimensionsContext = createContext(null); +const WindowDimensionsContext = createContext(null); const windowDimensionsPropTypes = { // Width of the window windowWidth: PropTypes.number.isRequired, @@ -27,12 +29,7 @@ const windowDimensionsPropTypes = { isLargeScreenWidth: PropTypes.bool.isRequired, }; -const windowDimensionsProviderPropTypes = { - /* Actual content wrapped by this component */ - children: PropTypes.node.isRequired, -}; - -function WindowDimensionsProvider(props) { +function WindowDimensionsProvider(props: ChildrenProps) { const [windowDimension, setWindowDimension] = useState(() => { const initialDimensions = Dimensions.get('window'); return { @@ -42,9 +39,8 @@ function WindowDimensionsProvider(props) { }); useEffect(() => { - const onDimensionChange = (newDimensions) => { + const onDimensionChange = (newDimensions: {window: ScaledSize}) => { const {window} = newDimensions; - setWindowDimension({ windowHeight: window.height, windowWidth: window.width, @@ -76,30 +72,31 @@ function WindowDimensionsProvider(props) { return {props.children}; } -WindowDimensionsProvider.propTypes = windowDimensionsProviderPropTypes; WindowDimensionsProvider.displayName = 'WindowDimensionsProvider'; /** - * @param {React.Component} WrappedComponent - * @returns {React.Component} + * @param WrappedComponent + * @returns */ -export default function withWindowDimensions(WrappedComponent) { - const WithWindowDimensions = forwardRef((props, ref) => ( - - {(windowDimensionsProps) => ( - - )} - - )); +export default function withWindowDimensions(WrappedComponent: ComponentType>) { + function WithWindowDimensions(props: Omit, ref: ForwardedRef) { + return ( + + {(windowDimensionsProps) => ( + + )} + + ); + } WithWindowDimensions.displayName = `withWindowDimensions(${getComponentDisplayName(WrappedComponent)})`; - return WithWindowDimensions; + return React.forwardRef(WithWindowDimensions); } export {WindowDimensionsProvider, windowDimensionsPropTypes}; diff --git a/src/components/withWindowDimensions/index.js b/src/components/withWindowDimensions/index.tsx similarity index 68% rename from src/components/withWindowDimensions/index.js rename to src/components/withWindowDimensions/index.tsx index 16e5985e0985..6a5dfadc6a49 100644 --- a/src/components/withWindowDimensions/index.js +++ b/src/components/withWindowDimensions/index.tsx @@ -1,13 +1,15 @@ -import React, {forwardRef, createContext, useState, useEffect, useMemo} from 'react'; +import React, {createContext, useState, useEffect, useMemo, RefAttributes, ComponentType, ForwardedRef} from 'react'; import PropTypes from 'prop-types'; import lodashDebounce from 'lodash/debounce'; -import {Dimensions} from 'react-native'; +import {Dimensions, ScaledSize} from 'react-native'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; import getComponentDisplayName from '../../libs/getComponentDisplayName'; import variables from '../../styles/variables'; import getWindowHeightAdjustment from '../../libs/getWindowHeightAdjustment'; +import ChildrenProps from '../../types/utils/ChildrenProps'; +import {WindowDimensionsContextData, WindowDimensionsProps} from './types'; -const WindowDimensionsContext = createContext(null); +const WindowDimensionsContext = createContext(null); const windowDimensionsPropTypes = { // Width of the window windowWidth: PropTypes.number.isRequired, @@ -28,12 +30,7 @@ const windowDimensionsPropTypes = { isLargeScreenWidth: PropTypes.bool.isRequired, }; -const windowDimensionsProviderPropTypes = { - /* Actual content wrapped by this component */ - children: PropTypes.node.isRequired, -}; - -function WindowDimensionsProvider(props) { +function WindowDimensionsProvider(props: ChildrenProps) { const [windowDimension, setWindowDimension] = useState(() => { const initialDimensions = Dimensions.get('window'); return { @@ -43,7 +40,7 @@ function WindowDimensionsProvider(props) { }); useEffect(() => { - const onDimensionChange = (newDimensions) => { + const onDimensionChange = (newDimensions: {window: ScaledSize}) => { const {window} = newDimensions; setWindowDimension({ windowHeight: window.height, @@ -81,30 +78,31 @@ function WindowDimensionsProvider(props) { return {props.children}; } -WindowDimensionsProvider.propTypes = windowDimensionsProviderPropTypes; WindowDimensionsProvider.displayName = 'WindowDimensionsProvider'; /** - * @param {React.Component} WrappedComponent - * @returns {React.Component} + * @param WrappedComponent + * @returns */ -export default function withWindowDimensions(WrappedComponent) { - const WithWindowDimensions = forwardRef((props, ref) => ( - - {(windowDimensionsProps) => ( - - )} - - )); +export default function withWindowDimensions(WrappedComponent: ComponentType>) { + function WithWindowDimensions(props: Omit, ref: ForwardedRef) { + return ( + + {(windowDimensionsProps) => ( + + )} + + ); + } WithWindowDimensions.displayName = `withWindowDimensions(${getComponentDisplayName(WrappedComponent)})`; - return WithWindowDimensions; + return React.forwardRef(WithWindowDimensions); } export {WindowDimensionsProvider, windowDimensionsPropTypes}; diff --git a/src/components/withWindowDimensions/types.ts b/src/components/withWindowDimensions/types.ts new file mode 100644 index 000000000000..994c10fa4a52 --- /dev/null +++ b/src/components/withWindowDimensions/types.ts @@ -0,0 +1,30 @@ +type WindowDimensionsContextData = { + windowHeight: number; + windowWidth: number; + isExtraSmallScreenWidth: boolean; + isSmallScreenWidth: boolean; + isMediumScreenWidth: boolean; + isLargeScreenWidth: boolean; +}; + +type WindowDimensionsProps = WindowDimensionsContextData & { + // Width of the window + windowWidth: number; + + // Height of the window + windowHeight: number; + + // Is the window width extra narrow, like on a Fold mobile device? + isExtraSmallScreenWidth: boolean; + + // Is the window width narrow, like on a mobile device? + isSmallScreenWidth: boolean; + + // Is the window width medium sized, like on a tablet device? + isMediumScreenWidth: boolean; + + // Is the window width wide, like on a browser or desktop? + isLargeScreenWidth: boolean; +}; + +export type {WindowDimensionsContextData, WindowDimensionsProps}; From 8213caad111c8c59a26ccf3f92d967dc9debe2ab Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Fri, 20 Oct 2023 13:41:42 +0200 Subject: [PATCH 08/97] create types file for Modal --- src/components/Modal/types.ts | 69 +++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/components/Modal/types.ts diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts new file mode 100644 index 000000000000..b17f794374e6 --- /dev/null +++ b/src/components/Modal/types.ts @@ -0,0 +1,69 @@ +import {ValueOf} from 'type-fest'; +import {ModalProps} from 'react-native-modal'; +import ChildrenProps from '../../types/utils/ChildrenProps'; +import {WindowDimensionsProps} from '../withWindowDimensions/types'; +import CONST from '../../CONST'; + +type BaseModalProps = WindowDimensionsProps & + ChildrenProps & { + /** Decides whether the modal should cover fullscreen. FullScreen modal has backdrop */ + fullscreen?: boolean; + + /** Should we close modal on outside click */ + shouldCloseOnOutsideClick?: boolean; + + /** Should we announce the Modal visibility changes? */ + shouldSetModalVisibility?: boolean; + + /** Callback method fired when the user requests to close the modal */ + onClose: () => void; + + /** State that determines whether to display the modal or not */ + isVisible: boolean; + + /** Callback method fired when the user requests to submit the modal content. */ + onSubmit?: () => void; + + /** Callback method fired when the modal is hidden */ + onModalHide?: () => void; + + /** Callback method fired when the modal is shown */ + onModalShow?: () => void; + + /** Style of modal to display */ + // type: PropTypes.oneOf(_.values(CONST.MODAL.MODAL_TYPE)), + type?: ValueOf; + + /** A react-native-animatable animation definition for the modal display animation. */ + animationIn?: Pick; + + /** A react-native-animatable animation definition for the modal hide animation. */ + animationOut?: Pick; + + /** The anchor position of a popover modal. Has no effect on other modal types. */ + popoverAnchorPosition?: { + top: number; + right: number; + bottom: number; + left: number; + }; + + /** Modal container styles */ + innerContainerStyle?: Pick; + + /** Whether the modal should go under the system statusbar */ + statusBarTranslucent?: boolean; + + /** Whether the modal should avoid the keyboard */ + avoidKeyboard?: boolean; + + /** + * Whether the modal should hide its content while animating. On iOS, set to true + * if `useNativeDriver` is also true, to avoid flashes in the UI. + * + * See: https://github.com/react-native-modal/react-native-modal/pull/116 + * */ + hideModalContentWhileAnimating?: boolean; + }; + +export default BaseModalProps; From 04476a3504a3c128241dfd38e34aee32dd03b14b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Fri, 20 Oct 2023 18:32:39 +0200 Subject: [PATCH 09/97] chrome camera fix --- .../ReceiptSelector/NavigationAwareCamera.js | 48 +++++++++++++++++-- src/pages/iou/ReceiptSelector/index.js | 7 ++- 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js index e9cb81003979..2bdbbed30ede 100644 --- a/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js +++ b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js @@ -1,13 +1,20 @@ -import React, {useEffect, useRef} from 'react'; +import React, {useEffect, useRef, useState} from 'react'; import Webcam from 'react-webcam'; -import {useIsFocused} from '@react-navigation/native'; +import {useIsFocused, useNavigation} from '@react-navigation/native'; import PropTypes from 'prop-types'; import {View} from 'react-native'; +import {useTabAnimation} from '@react-navigation/material-top-tabs'; const propTypes = { /* Flag to turn on/off the torch/flashlight - if available */ torchOn: PropTypes.bool, + /* The index of the tab that contains this camera */ + cameraTabIndex: PropTypes.number.isRequired, + + /* Whether we're in a tab navigator */ + isInTabNavigator: PropTypes.bool.isRequired, + /* Callback function when media stream becomes available - user granted camera permissions and camera starts to work */ onUserMedia: PropTypes.func, @@ -21,10 +28,41 @@ const defaultProps = { torchOn: false, }; +function useTabNavigatorFocus({cameraTabIndex, isInTabNavigator}) { + // Get navigation to get initial isFocused value (only needed once during init!) + const navigation = useNavigation(); + const [isTabFocused, setIsTabFocused] = useState(navigation.isFocused()); + const isPageFocused = useIsFocused(); + + // Retrieve the animation value from the tab navigator, which ranges from 0 to the total number of pages displayed. + // Even a minimal scroll towards the camera page (e.g., a value of 0.001 at start) should activate the camera for immediate responsiveness. + + // STOP!!!!!!! This is not a pattern to be followed! We are conditionally rendering this hook becase when used in the edit flow we'll never be inside a tab navigator. + // eslint-disable-next-line react-hooks/rules-of-hooks + const tabPositionAnimation = isInTabNavigator ? useTabAnimation() : null; + + useEffect(() => { + if (!isInTabNavigator) { + return; + } + + const listenerId = tabPositionAnimation.addListener(({value}) => { + // Activate camera as soon the index is animating towards the `cameraTabIndex` + setIsTabFocused(value > cameraTabIndex - 1 && value < cameraTabIndex + 1); + }); + + return () => { + tabPositionAnimation.removeListener(listenerId); + }; + }, [cameraTabIndex, tabPositionAnimation, isInTabNavigator]); + + return isTabFocused && isPageFocused; +} + // Wraps a camera that will only be active when the tab is focused or as soon as it starts to become focused. -const NavigationAwareCamera = React.forwardRef(({torchOn, onTorchAvailability, ...props}, ref) => { +const NavigationAwareCamera = React.forwardRef(({torchOn, onTorchAvailability, cameraTabIndex, isInTabNavigator, ...props}, ref) => { const trackRef = useRef(null); - const isCameraActive = useIsFocused(); + const showCamera = useTabNavigatorFocus({cameraTabIndex, isInTabNavigator}); const handleOnUserMedia = (stream) => { if (props.onUserMedia) { @@ -51,7 +89,7 @@ const NavigationAwareCamera = React.forwardRef(({torchOn, onTorchAvailability, . }); }, [torchOn]); - if (!isCameraActive) { + if (!showCamera) { return null; } return ( diff --git a/src/pages/iou/ReceiptSelector/index.js b/src/pages/iou/ReceiptSelector/index.js index ca9fe90575e7..7b7f609ea6a2 100644 --- a/src/pages/iou/ReceiptSelector/index.js +++ b/src/pages/iou/ReceiptSelector/index.js @@ -67,8 +67,9 @@ const defaultProps = { isInTabNavigator: true, }; -function ReceiptSelector({route, transactionID, iou, report}) { +function ReceiptSelector({route, transactionID, iou, report, isInTabNavigator}) { const iouType = lodashGet(route, 'params.iouType', ''); + const pageIndex = lodashGet(route, 'params.pageIndex', 1); // Grouping related states const [isAttachmentInvalid, setIsAttachmentInvalid] = useState(false); @@ -82,7 +83,7 @@ function ReceiptSelector({route, transactionID, iou, report}) { const [cameraPermissionState, setCameraPermissionState] = useState('prompt'); const [isFlashLightOn, toggleFlashlight] = useReducer((state) => !state, false); - const [isTorchAvailable, setIsTorchAvailable] = useState(true); + const [isTorchAvailable, setIsTorchAvailable] = useState(false); const cameraRef = useRef(null); const hideReciptModal = () => { @@ -201,6 +202,8 @@ function ReceiptSelector({route, transactionID, iou, report}) { torchOn={isFlashLightOn} onTorchAvailability={setIsTorchAvailable} forceScreenshotSourceSize + cameraTabIndex={pageIndex} + isInTabNavigator={isInTabNavigator} />
From c09a37cb6c80cd80470cac82d6e0388cd0dda3dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Fri, 20 Oct 2023 21:01:34 +0200 Subject: [PATCH 10/97] little fix --- src/pages/iou/ReceiptSelector/NavigationAwareCamera.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js index 2bdbbed30ede..7aaf5f24658d 100644 --- a/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js +++ b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js @@ -31,8 +31,8 @@ const defaultProps = { function useTabNavigatorFocus({cameraTabIndex, isInTabNavigator}) { // Get navigation to get initial isFocused value (only needed once during init!) const navigation = useNavigation(); - const [isTabFocused, setIsTabFocused] = useState(navigation.isFocused()); const isPageFocused = useIsFocused(); + const [isTabFocused, setIsTabFocused] = useState(isPageFocused); // Retrieve the animation value from the tab navigator, which ranges from 0 to the total number of pages displayed. // Even a minimal scroll towards the camera page (e.g., a value of 0.001 at start) should activate the camera for immediate responsiveness. From e8664c0dc88699c336a057e23b5b4b965f6ee624 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Fri, 20 Oct 2023 21:02:43 +0200 Subject: [PATCH 11/97] linting --- src/pages/iou/ReceiptSelector/NavigationAwareCamera.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js index 7aaf5f24658d..dc6610575820 100644 --- a/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js +++ b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js @@ -1,6 +1,6 @@ import React, {useEffect, useRef, useState} from 'react'; import Webcam from 'react-webcam'; -import {useIsFocused, useNavigation} from '@react-navigation/native'; +import {useIsFocused} from '@react-navigation/native'; import PropTypes from 'prop-types'; import {View} from 'react-native'; import {useTabAnimation} from '@react-navigation/material-top-tabs'; @@ -30,7 +30,6 @@ const defaultProps = { function useTabNavigatorFocus({cameraTabIndex, isInTabNavigator}) { // Get navigation to get initial isFocused value (only needed once during init!) - const navigation = useNavigation(); const isPageFocused = useIsFocused(); const [isTabFocused, setIsTabFocused] = useState(isPageFocused); From 7307db82185f027084c84f9ba78e90f490b4997a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Fri, 20 Oct 2023 21:41:51 +0200 Subject: [PATCH 12/97] default tab focused to false --- src/pages/iou/ReceiptSelector/NavigationAwareCamera.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js index dc6610575820..99eea31a185e 100644 --- a/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js +++ b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js @@ -31,7 +31,7 @@ const defaultProps = { function useTabNavigatorFocus({cameraTabIndex, isInTabNavigator}) { // Get navigation to get initial isFocused value (only needed once during init!) const isPageFocused = useIsFocused(); - const [isTabFocused, setIsTabFocused] = useState(isPageFocused); + const [isTabFocused, setIsTabFocused] = useState(false); // Retrieve the animation value from the tab navigator, which ranges from 0 to the total number of pages displayed. // Even a minimal scroll towards the camera page (e.g., a value of 0.001 at start) should activate the camera for immediate responsiveness. @@ -84,7 +84,11 @@ const NavigationAwareCamera = React.forwardRef(({torchOn, onTorchAvailability, c } trackRef.current.applyConstraints({ - advanced: [{torch: torchOn}], + advanced: [ + { + torch: torchOn, + }, + ], }); }, [torchOn]); From 710cee10c3dbb147bf1750a2d938757de491ac0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Sat, 21 Oct 2023 21:30:32 +0200 Subject: [PATCH 13/97] fixed infinite loader on scan tab refresh --- .../ReceiptSelector/NavigationAwareCamera.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js index 99eea31a185e..3f57b7a4fd21 100644 --- a/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js +++ b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js @@ -41,15 +41,24 @@ function useTabNavigatorFocus({cameraTabIndex, isInTabNavigator}) { const tabPositionAnimation = isInTabNavigator ? useTabAnimation() : null; useEffect(() => { - if (!isInTabNavigator) { + if (!tabPositionAnimation) { return; } + const index = Number(cameraTabIndex); const listenerId = tabPositionAnimation.addListener(({value}) => { // Activate camera as soon the index is animating towards the `cameraTabIndex` - setIsTabFocused(value > cameraTabIndex - 1 && value < cameraTabIndex + 1); + setIsTabFocused(value > index - 1 && value < index + 1); }); + // We need to get the position animation value on component initialization to determine + // if the tab is focused or not. Since it's an Animated.Value the only synchronous way + // to retrieve the value is to use a private method. + // eslint-disable-next-line no-underscore-dangle + const initialTabPositionValue = tabPositionAnimation.__getValue(); + + setIsTabFocused(initialTabPositionValue > index - 1 && initialTabPositionValue < index + 1); + return () => { tabPositionAnimation.removeListener(listenerId); }; @@ -84,11 +93,7 @@ const NavigationAwareCamera = React.forwardRef(({torchOn, onTorchAvailability, c } trackRef.current.applyConstraints({ - advanced: [ - { - torch: torchOn, - }, - ], + advanced: [{torch: torchOn}], }); }, [torchOn]); From 9e87798a65099498e40f1b54d58e898db3b915fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Sat, 21 Oct 2023 21:33:06 +0200 Subject: [PATCH 14/97] const name change --- src/pages/iou/ReceiptSelector/NavigationAwareCamera.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js index 3f57b7a4fd21..77ef63891f34 100644 --- a/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js +++ b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js @@ -70,7 +70,10 @@ function useTabNavigatorFocus({cameraTabIndex, isInTabNavigator}) { // Wraps a camera that will only be active when the tab is focused or as soon as it starts to become focused. const NavigationAwareCamera = React.forwardRef(({torchOn, onTorchAvailability, cameraTabIndex, isInTabNavigator, ...props}, ref) => { const trackRef = useRef(null); - const showCamera = useTabNavigatorFocus({cameraTabIndex, isInTabNavigator}); + const shouldShowCamera = useTabNavigatorFocus({ + cameraTabIndex, + isInTabNavigator, + }); const handleOnUserMedia = (stream) => { if (props.onUserMedia) { @@ -97,7 +100,7 @@ const NavigationAwareCamera = React.forwardRef(({torchOn, onTorchAvailability, c }); }, [torchOn]); - if (!showCamera) { + if (!shouldShowCamera) { return null; } return ( From b3e8507e6d90ab211d713ff9f317c395b79afecc Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Mon, 23 Oct 2023 09:34:44 +0200 Subject: [PATCH 15/97] Run Prettier --- src/pages/home/report/comment/TextCommentFragment.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/comment/TextCommentFragment.js b/src/pages/home/report/comment/TextCommentFragment.js index 4bf1b4bec86e..c176266dc12f 100644 --- a/src/pages/home/report/comment/TextCommentFragment.js +++ b/src/pages/home/report/comment/TextCommentFragment.js @@ -16,7 +16,7 @@ import convertToLTR from '../../../../libs/convertToLTR'; import CONST from '../../../../CONST'; import editedLabelStyles from '../../../../styles/editedLabelStyles'; import RenderCommentHTML from './RenderCommentHTML'; -import * as Browser from "../../../../libs/Browser"; +import * as Browser from '../../../../libs/Browser'; const propTypes = { /** The reportAction's source */ From cf613ad591c4448573fe68ab142ff4b86bfc37ea Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 23 Oct 2023 13:33:37 +0200 Subject: [PATCH 16/97] start migrating BaseModal to TypeScript --- .../Modal/{BaseModal.js => BaseModal.tsx} | 79 ++++++++----------- src/components/Modal/types.ts | 13 +++ 2 files changed, 44 insertions(+), 48 deletions(-) rename src/components/Modal/{BaseModal.js => BaseModal.tsx} (86%) diff --git a/src/components/Modal/BaseModal.js b/src/components/Modal/BaseModal.tsx similarity index 86% rename from src/components/Modal/BaseModal.js rename to src/components/Modal/BaseModal.tsx index 051c4ba3f80a..ec60b8520180 100644 --- a/src/components/Modal/BaseModal.js +++ b/src/components/Modal/BaseModal.tsx @@ -15,43 +15,34 @@ import CONST from '../../CONST'; import ComposerFocusManager from '../../libs/ComposerFocusManager'; import useNativeDriver from '../../libs/useNativeDriver'; import usePrevious from '../../hooks/usePrevious'; - -const propTypes = { - ...modalPropTypes, - - /** The ref to the modal container */ - forwardedRef: PropTypes.func, -}; - -const defaultProps = { - ...modalDefaultProps, - forwardedRef: () => {}, -}; - -function BaseModal({ - isVisible, - onClose, - shouldSetModalVisibility, - onModalHide, - type, - popoverAnchorPosition, - innerContainerStyle, - outerStyle, - onModalShow, - propagateSwipe, - fullscreen, - animationIn, - animationOut, - useNativeDriver: useNativeDriverProp, - hideModalContentWhileAnimating, - animationInTiming, - animationOutTiming, - statusBarTranslucent, - onLayout, - avoidKeyboard, - forwardedRef, - children, -}) { +import BaseModalProps from './types'; + +function BaseModal( + { + isVisible, + onClose, + shouldSetModalVisibility, + onModalHide, + type, + popoverAnchorPosition, + innerContainerStyle, + outerStyle, + onModalShow, + propagateSwipe, + fullscreen, + animationIn, + animationOut, + useNativeDriver: useNativeDriverProp, + hideModalContentWhileAnimating, + animationInTiming, + animationOutTiming, + statusBarTranslucent, + onLayout, + avoidKeyboard, + children, + }: BaseModalProps, + ref: React.ForwardedRef, +) { const {windowWidth, windowHeight, isSmallScreenWidth} = useWindowDimensions(); const safeAreaInsets = useSafeAreaInsets(); @@ -69,7 +60,7 @@ function BaseModal({ Modal.setModalVisibility(false); } if (callHideCallback) { - onModalHide(); + onModalHide?.(); } Modal.onModalDidClose(); if (!fullscreen) { @@ -207,7 +198,7 @@ function BaseModal({ > {children} @@ -215,14 +206,6 @@ function BaseModal({ ); } -BaseModal.propTypes = propTypes; -BaseModal.defaultProps = defaultProps; BaseModal.displayName = 'BaseModal'; -export default forwardRef((props, ref) => ( - -)); +export default forwardRef(BaseModal); diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts index b17f794374e6..585df54b4b55 100644 --- a/src/components/Modal/types.ts +++ b/src/components/Modal/types.ts @@ -1,4 +1,5 @@ import {ValueOf} from 'type-fest'; +import {StyleProp, ViewStyle} from 'react-native'; import {ModalProps} from 'react-native-modal'; import ChildrenProps from '../../types/utils/ChildrenProps'; import {WindowDimensionsProps} from '../withWindowDimensions/types'; @@ -30,6 +31,8 @@ type BaseModalProps = WindowDimensionsProps & /** Callback method fired when the modal is shown */ onModalShow?: () => void; + propagateSwipe?: Pick; + /** Style of modal to display */ // type: PropTypes.oneOf(_.values(CONST.MODAL.MODAL_TYPE)), type?: ValueOf; @@ -40,6 +43,12 @@ type BaseModalProps = WindowDimensionsProps & /** A react-native-animatable animation definition for the modal hide animation. */ animationOut?: Pick; + useNativeDriver?: Pick; + + animationInTiming?: Pick; + + animationOutTiming?: Pick; + /** The anchor position of a popover modal. Has no effect on other modal types. */ popoverAnchorPosition?: { top: number; @@ -51,9 +60,13 @@ type BaseModalProps = WindowDimensionsProps & /** Modal container styles */ innerContainerStyle?: Pick; + outerStyle?: StyleProp; + /** Whether the modal should go under the system statusbar */ statusBarTranslucent?: boolean; + onLayout: Pick; + /** Whether the modal should avoid the keyboard */ avoidKeyboard?: boolean; From b3f0a85f2bc656d730fccb325626d58e24a65e8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Mon, 23 Oct 2023 19:47:34 +0200 Subject: [PATCH 17/97] delayed show camera state --- src/pages/iou/ReceiptSelector/NavigationAwareCamera.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js index 77ef63891f34..c95ce48a61b8 100644 --- a/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js +++ b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js @@ -48,7 +48,9 @@ function useTabNavigatorFocus({cameraTabIndex, isInTabNavigator}) { const listenerId = tabPositionAnimation.addListener(({value}) => { // Activate camera as soon the index is animating towards the `cameraTabIndex` - setIsTabFocused(value > index - 1 && value < index + 1); + requestAnimationFrame(() => { + setIsTabFocused(value > index - 1 && value < index + 1); + }); }); // We need to get the position animation value on component initialization to determine @@ -57,7 +59,9 @@ function useTabNavigatorFocus({cameraTabIndex, isInTabNavigator}) { // eslint-disable-next-line no-underscore-dangle const initialTabPositionValue = tabPositionAnimation.__getValue(); - setIsTabFocused(initialTabPositionValue > index - 1 && initialTabPositionValue < index + 1); + requestAnimationFrame(() => { + setIsTabFocused(initialTabPositionValue > index - 1 && initialTabPositionValue < index + 1); + }); return () => { tabPositionAnimation.removeListener(listenerId); From 686118c0b5b956677d378a3bd6bacc8cae370832 Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Tue, 24 Oct 2023 10:57:00 +0200 Subject: [PATCH 18/97] ReportActionItemMessage: Remove a duplicated passed property --- src/pages/home/report/ReportActionItemMessage.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItemMessage.js b/src/pages/home/report/ReportActionItemMessage.js index 44c314934fba..4ecb39efc986 100644 --- a/src/pages/home/report/ReportActionItemMessage.js +++ b/src/pages/home/report/ReportActionItemMessage.js @@ -59,7 +59,6 @@ function ReportActionItemMessage(props) { pendingAction={props.action.pendingAction} source={lodashGet(props.action, 'originalMessage.source')} accountID={props.action.actorAccountID} - displayAsGroup={props.displayAsGroup} style={props.style} displayAsGroup={props.displayAsGroup} /> From 408ef25d1139445bc451b3f0f809173f95ec2379 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Tue, 24 Oct 2023 13:53:15 +0200 Subject: [PATCH 19/97] add missing prop types --- src/components/Modal/types.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts index 585df54b4b55..03c6cee89fed 100644 --- a/src/components/Modal/types.ts +++ b/src/components/Modal/types.ts @@ -1,5 +1,5 @@ import {ValueOf} from 'type-fest'; -import {StyleProp, ViewStyle} from 'react-native'; +import {ViewStyle} from 'react-native'; import {ModalProps} from 'react-native-modal'; import ChildrenProps from '../../types/utils/ChildrenProps'; import {WindowDimensionsProps} from '../withWindowDimensions/types'; @@ -51,16 +51,16 @@ type BaseModalProps = WindowDimensionsProps & /** The anchor position of a popover modal. Has no effect on other modal types. */ popoverAnchorPosition?: { - top: number; - right: number; - bottom: number; - left: number; + top?: number; + right?: number; + bottom?: number; + left?: number; }; /** Modal container styles */ innerContainerStyle?: Pick; - outerStyle?: StyleProp; + outerStyle?: ViewStyle; /** Whether the modal should go under the system statusbar */ statusBarTranslucent?: boolean; From 1c1d092c1737c65138ef423d9e9e0dd3ec885513 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Tue, 24 Oct 2023 16:17:20 +0200 Subject: [PATCH 20/97] update types, update helper function types --- src/components/Modal/types.ts | 25 ++++--------------------- src/libs/actions/Modal.ts | 4 ++-- src/styles/getModalStyles.ts | 4 +--- 3 files changed, 7 insertions(+), 26 deletions(-) diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts index 03c6cee89fed..b1dfdaff640d 100644 --- a/src/components/Modal/types.ts +++ b/src/components/Modal/types.ts @@ -1,12 +1,11 @@ import {ValueOf} from 'type-fest'; import {ViewStyle} from 'react-native'; import {ModalProps} from 'react-native-modal'; -import ChildrenProps from '../../types/utils/ChildrenProps'; import {WindowDimensionsProps} from '../withWindowDimensions/types'; import CONST from '../../CONST'; type BaseModalProps = WindowDimensionsProps & - ChildrenProps & { + ModalProps & { /** Decides whether the modal should cover fullscreen. FullScreen modal has backdrop */ fullscreen?: boolean; @@ -31,24 +30,10 @@ type BaseModalProps = WindowDimensionsProps & /** Callback method fired when the modal is shown */ onModalShow?: () => void; - propagateSwipe?: Pick; - /** Style of modal to display */ // type: PropTypes.oneOf(_.values(CONST.MODAL.MODAL_TYPE)), type?: ValueOf; - /** A react-native-animatable animation definition for the modal display animation. */ - animationIn?: Pick; - - /** A react-native-animatable animation definition for the modal hide animation. */ - animationOut?: Pick; - - useNativeDriver?: Pick; - - animationInTiming?: Pick; - - animationOutTiming?: Pick; - /** The anchor position of a popover modal. Has no effect on other modal types. */ popoverAnchorPosition?: { top?: number; @@ -57,19 +42,17 @@ type BaseModalProps = WindowDimensionsProps & left?: number; }; - /** Modal container styles */ - innerContainerStyle?: Pick; - outerStyle?: ViewStyle; /** Whether the modal should go under the system statusbar */ statusBarTranslucent?: boolean; - onLayout: Pick; - /** Whether the modal should avoid the keyboard */ avoidKeyboard?: boolean; + /** Modal container styles */ + innerContainerStyle?: ViewStyle; + /** * Whether the modal should hide its content while animating. On iOS, set to true * if `useNativeDriver` is also true, to avoid flashes in the UI. diff --git a/src/libs/actions/Modal.ts b/src/libs/actions/Modal.ts index ff09731f59a7..7a443b94354f 100644 --- a/src/libs/actions/Modal.ts +++ b/src/libs/actions/Modal.ts @@ -1,13 +1,13 @@ import Onyx from 'react-native-onyx'; import ONYXKEYS from '../../ONYXKEYS'; -let closeModal: (isNavigating: boolean) => void; +let closeModal: ((isNavigating: boolean) => void) | null; let onModalClose: null | (() => void); /** * Allows other parts of the app to call modal close function */ -function setCloseModal(onClose: () => void) { +function setCloseModal(onClose: (() => void) | null) { closeModal = onClose; } diff --git a/src/styles/getModalStyles.ts b/src/styles/getModalStyles.ts index d52d29568c2d..e318e7b989b8 100644 --- a/src/styles/getModalStyles.ts +++ b/src/styles/getModalStyles.ts @@ -21,8 +21,6 @@ type WindowDimensions = { windowWidth: number; windowHeight: number; isSmallScreenWidth: boolean; - isMediumScreenWidth: boolean; - isLargeScreenWidth: boolean; }; type GetModalStyles = { @@ -39,7 +37,7 @@ type GetModalStyles = { }; export default function getModalStyles( - type: ModalType, + type: ModalType | undefined, windowDimensions: WindowDimensions, popoverAnchorPosition: ViewStyle = {}, innerContainerStyle: ViewStyle = {}, From 118415e0d938ad828401f6a5052beba72851b018 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Tue, 24 Oct 2023 16:17:42 +0200 Subject: [PATCH 21/97] reorganise types in StyleUtils --- src/styles/StyleUtils.ts | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index 62da2bf3be4b..68a2e61529bc 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -56,10 +56,10 @@ type ModalPaddingStylesParams = { safeAreaPaddingBottom: number; safeAreaPaddingLeft: number; safeAreaPaddingRight: number; - modalContainerStyleMarginTop: number; - modalContainerStyleMarginBottom: number; - modalContainerStylePaddingTop: number; - modalContainerStylePaddingBottom: number; + modalContainerStyleMarginTop: DimensionValue | undefined; + modalContainerStyleMarginBottom: DimensionValue | undefined; + modalContainerStylePaddingTop: DimensionValue | undefined; + modalContainerStylePaddingBottom: DimensionValue | undefined; insets: EdgeInsets; }; @@ -287,12 +287,19 @@ function getEReceiptColorStyles(colorCode: EReceiptColorName): EreceiptColorStyl return eReceiptColorStyles[colorCode]; } +type SafeAreaPadding = { + paddingTop: number; + paddingBottom: number; + paddingLeft: number; + paddingRight: number; +}; + /** * Takes safe area insets and returns padding to use for a View */ -function getSafeAreaPadding(insets?: EdgeInsets, insetsPercentage: number = variables.safeInsertPercentage): ViewStyle { +function getSafeAreaPadding(insets?: EdgeInsets, insetsPercentage: number = variables.safeInsertPercentage): SafeAreaPadding { return { - paddingTop: insets?.top, + paddingTop: insets?.top ?? 0, paddingBottom: (insets?.bottom ?? 0) * insetsPercentage, paddingLeft: (insets?.left ?? 0) * insetsPercentage, paddingRight: (insets?.right ?? 0) * insetsPercentage, @@ -568,6 +575,14 @@ function getWidthAndHeightStyle(width: number, height?: number): ViewStyle { }; } +function getCombinedSpacing(modalContainerValue: DimensionValue | undefined, safeAreaValue: number, shouldAddSafeAreaValue: boolean): number | DimensionValue | undefined { + if (typeof modalContainerValue === 'number') { + return (modalContainerValue ?? 0) + (shouldAddSafeAreaValue ? safeAreaValue : 0); + } + + return modalContainerValue; +} + function getModalPaddingStyles({ shouldAddBottomSafeAreaMargin, shouldAddTopSafeAreaMargin, @@ -585,12 +600,12 @@ function getModalPaddingStyles({ }: ModalPaddingStylesParams): ViewStyle { // use fallback value for safeAreaPaddingBottom to keep padding bottom consistent with padding top. // More info: issue #17376 - const safeAreaPaddingBottomWithFallback = insets.bottom === 0 ? modalContainerStylePaddingTop ?? 0 : safeAreaPaddingBottom; + const safeAreaPaddingBottomWithFallback = insets.bottom === 0 && typeof modalContainerStylePaddingTop === 'number' ? modalContainerStylePaddingTop ?? 0 : safeAreaPaddingBottom; return { - marginTop: (modalContainerStyleMarginTop ?? 0) + (shouldAddTopSafeAreaMargin ? safeAreaPaddingTop : 0), - marginBottom: (modalContainerStyleMarginBottom ?? 0) + (shouldAddBottomSafeAreaMargin ? safeAreaPaddingBottomWithFallback : 0), - paddingTop: shouldAddTopSafeAreaPadding ? (modalContainerStylePaddingTop ?? 0) + safeAreaPaddingTop : modalContainerStylePaddingTop ?? 0, - paddingBottom: shouldAddBottomSafeAreaPadding ? (modalContainerStylePaddingBottom ?? 0) + safeAreaPaddingBottomWithFallback : modalContainerStylePaddingBottom ?? 0, + marginTop: getCombinedSpacing(modalContainerStyleMarginTop, safeAreaPaddingTop, shouldAddTopSafeAreaMargin), + marginBottom: getCombinedSpacing(modalContainerStyleMarginBottom, safeAreaPaddingBottomWithFallback, shouldAddBottomSafeAreaMargin), + paddingTop: getCombinedSpacing(modalContainerStylePaddingTop, safeAreaPaddingTop, shouldAddTopSafeAreaPadding), + paddingBottom: getCombinedSpacing(modalContainerStylePaddingBottom, safeAreaPaddingBottomWithFallback, shouldAddBottomSafeAreaPadding), paddingLeft: safeAreaPaddingLeft ?? 0, paddingRight: safeAreaPaddingRight ?? 0, }; From 4c18fc0ba771b5c5f2995b97c24c9d388ae17b34 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Tue, 24 Oct 2023 16:18:01 +0200 Subject: [PATCH 22/97] fix typings in BaseModal --- src/components/Modal/BaseModal.tsx | 32 ++++++++++++------------------ 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index ec60b8520180..86fbbf896ac1 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -1,17 +1,14 @@ import React, {forwardRef, useCallback, useEffect, useMemo, useRef} from 'react'; import {View} from 'react-native'; -import PropTypes from 'prop-types'; import ReactNativeModal from 'react-native-modal'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; import styles from '../../styles/styles'; import * as Modal from '../../libs/actions/Modal'; import * as StyleUtils from '../../styles/StyleUtils'; import themeColors from '../../styles/themes/default'; -import {propTypes as modalPropTypes, defaultProps as modalDefaultProps} from './modalPropTypes'; import getModalStyles from '../../styles/getModalStyles'; import useWindowDimensions from '../../hooks/useWindowDimensions'; import variables from '../../styles/variables'; -import CONST from '../../CONST'; import ComposerFocusManager from '../../libs/ComposerFocusManager'; import useNativeDriver from '../../libs/useNativeDriver'; import usePrevious from '../../hooks/usePrevious'; @@ -21,24 +18,24 @@ function BaseModal( { isVisible, onClose, - shouldSetModalVisibility, - onModalHide, + shouldSetModalVisibility = true, + onModalHide = () => {}, type, - popoverAnchorPosition, - innerContainerStyle, + popoverAnchorPosition = {}, + innerContainerStyle = {}, outerStyle, - onModalShow, + onModalShow = () => {}, propagateSwipe, - fullscreen, + fullscreen = true, animationIn, animationOut, useNativeDriver: useNativeDriverProp, - hideModalContentWhileAnimating, + hideModalContentWhileAnimating = false, animationInTiming, animationOutTiming, - statusBarTranslucent, + statusBarTranslucent = true, onLayout, - avoidKeyboard, + avoidKeyboard = false, children, }: BaseModalProps, ref: React.ForwardedRef, @@ -101,13 +98,10 @@ function BaseModal( if (shouldSetModalVisibility) { Modal.setModalVisibility(true); } - onModalShow(); + onModalShow?.(); }; - const handleBackdropPress = (e) => { - if (e && e.key === CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey) { - return; - } + const handleBackdropPress = () => { onClose(); }; @@ -186,8 +180,8 @@ function BaseModal( style={modalStyle} deviceHeight={windowHeight} deviceWidth={windowWidth} - animationIn={animationIn || modalStyleAnimationIn} - animationOut={animationOut || modalStyleAnimationOut} + animationIn={animationIn ?? modalStyleAnimationIn} + animationOut={animationOut ?? modalStyleAnimationOut} useNativeDriver={useNativeDriverProp && useNativeDriver} hideModalContentWhileAnimating={hideModalContentWhileAnimating} animationInTiming={animationInTiming} From c016d8436219e39f8fd6da99d0544271c2fa393c Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Wed, 25 Oct 2023 12:27:07 +0200 Subject: [PATCH 23/97] migrate index.web.js to TypeScript --- .../Modal/{index.web.js => index.web.tsx} | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) rename src/components/Modal/{index.web.js => index.web.tsx} (63%) diff --git a/src/components/Modal/index.web.js b/src/components/Modal/index.web.tsx similarity index 63% rename from src/components/Modal/index.web.js rename to src/components/Modal/index.web.tsx index 065b3a9f210f..d5ed7e6369ba 100644 --- a/src/components/Modal/index.web.js +++ b/src/components/Modal/index.web.tsx @@ -1,14 +1,14 @@ import React, {useState} from 'react'; import withWindowDimensions from '../withWindowDimensions'; import BaseModal from './BaseModal'; -import {propTypes, defaultProps} from './modalPropTypes'; import * as StyleUtils from '../../styles/StyleUtils'; import themeColors from '../../styles/themes/default'; import StatusBar from '../../libs/StatusBar'; import CONST from '../../CONST'; +import BaseModalProps from './types'; -function Modal(props) { - const [previousStatusBarColor, setPreviousStatusBarColor] = useState(); +function Modal(props: BaseModalProps) { + const [previousStatusBarColor, setPreviousStatusBarColor] = useState(); const setStatusBarColor = (color = themeColors.appBG) => { if (!props.fullscreen) { @@ -25,12 +25,15 @@ function Modal(props) { const showModal = () => { const statusBarColor = StatusBar.getBackgroundColor(); - const isFullScreenModal = - props.type === CONST.MODAL.MODAL_TYPE.CENTERED || props.type === CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE || props.type === CONST.MODAL.MODAL_TYPE.RIGHT_DOCKED; - setPreviousStatusBarColor(statusBarColor); - // If it is a full screen modal then match it with appBG, otherwise we use the backdrop color - setStatusBarColor(isFullScreenModal ? themeColors.appBG : StyleUtils.getThemeBackgroundColor(statusBarColor)); - props.onModalShow(); + + if (statusBarColor) { + const isFullScreenModal = + props.type === CONST.MODAL.MODAL_TYPE.CENTERED || props.type === CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE || props.type === CONST.MODAL.MODAL_TYPE.RIGHT_DOCKED; + setPreviousStatusBarColor(statusBarColor); + // If it is a full screen modal then match it with appBG, otherwise we use the backdrop color + setStatusBarColor(isFullScreenModal ? themeColors.appBG : StyleUtils.getThemeBackgroundColor(statusBarColor)); + props.onModalShow(); + } }; return ( @@ -46,7 +49,5 @@ function Modal(props) { ); } -Modal.propTypes = propTypes; -Modal.defaultProps = defaultProps; Modal.displayName = 'Modal'; export default withWindowDimensions(Modal); From 5f3968d44bccd743a1b4739484771de7a52a0c5b Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Wed, 25 Oct 2023 12:35:39 +0200 Subject: [PATCH 24/97] migrate index.ios.js to TypeScript --- src/components/Modal/{index.ios.js => index.ios.tsx} | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) rename src/components/Modal/{index.ios.js => index.ios.tsx} (72%) diff --git a/src/components/Modal/index.ios.js b/src/components/Modal/index.ios.tsx similarity index 72% rename from src/components/Modal/index.ios.js rename to src/components/Modal/index.ios.tsx index d8206d12532d..8b82a67d8601 100644 --- a/src/components/Modal/index.ios.js +++ b/src/components/Modal/index.ios.tsx @@ -1,9 +1,9 @@ import React from 'react'; import withWindowDimensions from '../withWindowDimensions'; import BaseModal from './BaseModal'; -import {propTypes, defaultProps} from './modalPropTypes'; +import BaseModalProps from './types'; -function Modal(props) { +function Modal(props: BaseModalProps) { return ( Date: Wed, 25 Oct 2023 13:15:23 +0200 Subject: [PATCH 25/97] migrate index.android.js to TypeScript --- .../Modal/{index.android.js => index.android.tsx} | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) rename src/components/Modal/{index.android.js => index.android.tsx} (86%) diff --git a/src/components/Modal/index.android.js b/src/components/Modal/index.android.tsx similarity index 86% rename from src/components/Modal/index.android.js rename to src/components/Modal/index.android.tsx index b5f11a02650a..cf61452b7780 100644 --- a/src/components/Modal/index.android.js +++ b/src/components/Modal/index.android.tsx @@ -2,8 +2,8 @@ import React from 'react'; import {AppState} from 'react-native'; import withWindowDimensions from '../withWindowDimensions'; import BaseModal from './BaseModal'; -import {propTypes, defaultProps} from './modalPropTypes'; import ComposerFocusManager from '../../libs/ComposerFocusManager'; +import BaseModalProps from './types'; AppState.addEventListener('focus', () => { ComposerFocusManager.setReadyToFocus(); @@ -15,19 +15,17 @@ AppState.addEventListener('blur', () => { // Only want to use useNativeDriver on Android. It has strange flashes issue on IOS // https://github.com/react-native-modal/react-native-modal#the-modal-flashes-in-a-weird-way-when-animating -function Modal(props) { +function Modal(props: BaseModalProps) { return ( {props.children} ); } -Modal.propTypes = propTypes; -Modal.defaultProps = defaultProps; Modal.displayName = 'Modal'; export default withWindowDimensions(Modal); From a4378183f74b13825f67968607b3314f62c6bac5 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Wed, 25 Oct 2023 14:47:54 +0200 Subject: [PATCH 26/97] fix spacing calculation --- src/styles/StyleUtils.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index 68a2e61529bc..8d42a3810469 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -575,8 +575,16 @@ function getWidthAndHeightStyle(width: number, height?: number): ViewStyle { }; } +/** + * Combine margin/padding with safe area inset + * + * @param modalContainerValue - margin or padding value + * @param safeAreaValue - safe area inset + * @param shouldAddSafeAreaValue - indicator whether safe area inset should be applied + */ function getCombinedSpacing(modalContainerValue: DimensionValue | undefined, safeAreaValue: number, shouldAddSafeAreaValue: boolean): number | DimensionValue | undefined { - if (typeof modalContainerValue === 'number') { + // modalContainerValue can only be added to safe area inset if it's a number, otherwise it's returned as is + if (typeof modalContainerValue === 'number' || !modalContainerValue) { return (modalContainerValue ?? 0) + (shouldAddSafeAreaValue ? safeAreaValue : 0); } From 73859687f0883648cfa0ac1b68f6f6508f02bddb Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 26 Oct 2023 10:20:43 +0200 Subject: [PATCH 27/97] apply finishing touches --- src/components/Modal/BaseModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index 9074d9d9e53e..d845beb2e3a1 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -49,7 +49,7 @@ function BaseModal( /** * Hides modal - * @param {Boolean} [callHideCallback=true] Should we call the onModalHide callback + * @param callHideCallback - Should we call the onModalHide callback */ const hideModal = useCallback( (callHideCallback = true) => { From c26a716da26ed01036bd370c4a2a55b75791f9e4 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 26 Oct 2023 10:38:26 +0200 Subject: [PATCH 28/97] remove unnecessary conditional function calls --- src/components/Modal/BaseModal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index d845beb2e3a1..4c6532d16fb8 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -57,7 +57,7 @@ function BaseModal( Modal.setModalVisibility(false); } if (callHideCallback) { - onModalHide?.(); + onModalHide(); } Modal.onModalDidClose(); if (!fullscreen) { @@ -98,7 +98,7 @@ function BaseModal( if (shouldSetModalVisibility) { Modal.setModalVisibility(true); } - onModalShow?.(); + onModalShow(); }; const handleBackdropPress = () => { From a1adc3766af1546c69c0bb6ad1f387eb97abb5dc Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 26 Oct 2023 10:40:44 +0200 Subject: [PATCH 29/97] create common NewDimensions type --- src/components/withWindowDimensions/index.native.tsx | 6 +++--- src/components/withWindowDimensions/index.tsx | 6 +++--- src/components/withWindowDimensions/types.ts | 6 +++++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/components/withWindowDimensions/index.native.tsx b/src/components/withWindowDimensions/index.native.tsx index 889397f2bab6..f4ce0707c83e 100644 --- a/src/components/withWindowDimensions/index.native.tsx +++ b/src/components/withWindowDimensions/index.native.tsx @@ -1,11 +1,11 @@ import React, {createContext, useState, useEffect, useMemo, ComponentType, RefAttributes, ForwardedRef} from 'react'; import PropTypes from 'prop-types'; -import {Dimensions, ScaledSize} from 'react-native'; +import {Dimensions} from 'react-native'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; import getComponentDisplayName from '../../libs/getComponentDisplayName'; import variables from '../../styles/variables'; import getWindowHeightAdjustment from '../../libs/getWindowHeightAdjustment'; -import {WindowDimensionsContextData, WindowDimensionsProps} from './types'; +import {NewDimensions, WindowDimensionsContextData, WindowDimensionsProps} from './types'; import ChildrenProps from '../../types/utils/ChildrenProps'; const WindowDimensionsContext = createContext(null); @@ -39,7 +39,7 @@ function WindowDimensionsProvider(props: ChildrenProps) { }); useEffect(() => { - const onDimensionChange = (newDimensions: {window: ScaledSize}) => { + const onDimensionChange = (newDimensions: NewDimensions) => { const {window} = newDimensions; setWindowDimension({ windowHeight: window.height, diff --git a/src/components/withWindowDimensions/index.tsx b/src/components/withWindowDimensions/index.tsx index 6a5dfadc6a49..ab5c4d006751 100644 --- a/src/components/withWindowDimensions/index.tsx +++ b/src/components/withWindowDimensions/index.tsx @@ -1,13 +1,13 @@ import React, {createContext, useState, useEffect, useMemo, RefAttributes, ComponentType, ForwardedRef} from 'react'; import PropTypes from 'prop-types'; import lodashDebounce from 'lodash/debounce'; -import {Dimensions, ScaledSize} from 'react-native'; +import {Dimensions} from 'react-native'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; import getComponentDisplayName from '../../libs/getComponentDisplayName'; import variables from '../../styles/variables'; import getWindowHeightAdjustment from '../../libs/getWindowHeightAdjustment'; import ChildrenProps from '../../types/utils/ChildrenProps'; -import {WindowDimensionsContextData, WindowDimensionsProps} from './types'; +import {NewDimensions, WindowDimensionsContextData, WindowDimensionsProps} from './types'; const WindowDimensionsContext = createContext(null); const windowDimensionsPropTypes = { @@ -40,7 +40,7 @@ function WindowDimensionsProvider(props: ChildrenProps) { }); useEffect(() => { - const onDimensionChange = (newDimensions: {window: ScaledSize}) => { + const onDimensionChange = (newDimensions: NewDimensions) => { const {window} = newDimensions; setWindowDimension({ windowHeight: window.height, diff --git a/src/components/withWindowDimensions/types.ts b/src/components/withWindowDimensions/types.ts index 994c10fa4a52..514c86616b87 100644 --- a/src/components/withWindowDimensions/types.ts +++ b/src/components/withWindowDimensions/types.ts @@ -1,3 +1,5 @@ +import {ScaledSize} from 'react-native'; + type WindowDimensionsContextData = { windowHeight: number; windowWidth: number; @@ -27,4 +29,6 @@ type WindowDimensionsProps = WindowDimensionsContextData & { isLargeScreenWidth: boolean; }; -export type {WindowDimensionsContextData, WindowDimensionsProps}; +type NewDimensions = {window: ScaledSize}; + +export type {WindowDimensionsContextData, WindowDimensionsProps, NewDimensions}; From bc5c041c7f016a46ff02a207bd297697ca4d822d Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 26 Oct 2023 11:20:03 +0200 Subject: [PATCH 30/97] change index.web.tsx to index.tsx to fix linting --- src/components/Modal/{index.web.tsx => index.tsx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/components/Modal/{index.web.tsx => index.tsx} (100%) diff --git a/src/components/Modal/index.web.tsx b/src/components/Modal/index.tsx similarity index 100% rename from src/components/Modal/index.web.tsx rename to src/components/Modal/index.tsx From ac60a8e5c5eb5161e32ec6a3e8024c5cbb36b5d0 Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Thu, 26 Oct 2023 13:42:14 +0200 Subject: [PATCH 31/97] Pass a missing displayAsGroup property --- src/pages/home/report/ReportActionItemSingle.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index fc189a3aef36..c35cadc805e6 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -240,6 +240,7 @@ function ReportActionItemSingle(props) { delegateAccountID={props.action.delegateAccountID} isSingleLine actorIcon={icon} + displayAsGroup={false} /> ))} From 673d1207fb1653c823fdc76edfd5d9f86cad7e1d Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 26 Oct 2023 14:14:21 +0200 Subject: [PATCH 32/97] add event to backdrop press handler --- src/components/Modal/BaseModal.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index 4c6532d16fb8..7a4747718fb9 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -13,6 +13,7 @@ import ComposerFocusManager from '../../libs/ComposerFocusManager'; import useNativeDriver from '../../libs/useNativeDriver'; import usePrevious from '../../hooks/usePrevious'; import BaseModalProps from './types'; +import CONST from '../../CONST'; function BaseModal( { @@ -101,7 +102,11 @@ function BaseModal( onModalShow(); }; - const handleBackdropPress = () => { + const handleBackdropPress = (e?: KeyboardEvent) => { + if (e?.key === CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey) { + return; + } + onClose(); }; From 2d7d45bf6e5928b8490596888255b8e8f97597e8 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 26 Oct 2023 14:33:17 +0200 Subject: [PATCH 33/97] apply changes based on feedback from code review --- src/components/Modal/index.tsx | 8 +++++--- src/components/Modal/types.ts | 5 +++-- src/components/withWindowDimensions/index.native.tsx | 4 ---- src/components/withWindowDimensions/index.tsx | 4 ---- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx index d5ed7e6369ba..82f2aac9bbc2 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -26,14 +26,16 @@ function Modal(props: BaseModalProps) { const showModal = () => { const statusBarColor = StatusBar.getBackgroundColor(); + const isFullScreenModal = + props.type === CONST.MODAL.MODAL_TYPE.CENTERED || props.type === CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE || props.type === CONST.MODAL.MODAL_TYPE.RIGHT_DOCKED; + if (statusBarColor) { - const isFullScreenModal = - props.type === CONST.MODAL.MODAL_TYPE.CENTERED || props.type === CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE || props.type === CONST.MODAL.MODAL_TYPE.RIGHT_DOCKED; setPreviousStatusBarColor(statusBarColor); // If it is a full screen modal then match it with appBG, otherwise we use the backdrop color setStatusBarColor(isFullScreenModal ? themeColors.appBG : StyleUtils.getThemeBackgroundColor(statusBarColor)); - props.onModalShow(); } + + props.onModalShow(); }; return ( diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts index b1dfdaff640d..36ce5fa28781 100644 --- a/src/components/Modal/types.ts +++ b/src/components/Modal/types.ts @@ -4,6 +4,8 @@ import {ModalProps} from 'react-native-modal'; import {WindowDimensionsProps} from '../withWindowDimensions/types'; import CONST from '../../CONST'; +type ModalVariant = ValueOf; + type BaseModalProps = WindowDimensionsProps & ModalProps & { /** Decides whether the modal should cover fullscreen. FullScreen modal has backdrop */ @@ -31,8 +33,7 @@ type BaseModalProps = WindowDimensionsProps & onModalShow?: () => void; /** Style of modal to display */ - // type: PropTypes.oneOf(_.values(CONST.MODAL.MODAL_TYPE)), - type?: ValueOf; + type?: ModalVariant; /** The anchor position of a popover modal. Has no effect on other modal types. */ popoverAnchorPosition?: { diff --git a/src/components/withWindowDimensions/index.native.tsx b/src/components/withWindowDimensions/index.native.tsx index f4ce0707c83e..611efa60ee79 100644 --- a/src/components/withWindowDimensions/index.native.tsx +++ b/src/components/withWindowDimensions/index.native.tsx @@ -74,10 +74,6 @@ function WindowDimensionsProvider(props: ChildrenProps) { WindowDimensionsProvider.displayName = 'WindowDimensionsProvider'; -/** - * @param WrappedComponent - * @returns - */ export default function withWindowDimensions(WrappedComponent: ComponentType>) { function WithWindowDimensions(props: Omit, ref: ForwardedRef) { return ( diff --git a/src/components/withWindowDimensions/index.tsx b/src/components/withWindowDimensions/index.tsx index ab5c4d006751..d952f036fba0 100644 --- a/src/components/withWindowDimensions/index.tsx +++ b/src/components/withWindowDimensions/index.tsx @@ -80,10 +80,6 @@ function WindowDimensionsProvider(props: ChildrenProps) { WindowDimensionsProvider.displayName = 'WindowDimensionsProvider'; -/** - * @param WrappedComponent - * @returns - */ export default function withWindowDimensions(WrappedComponent: ComponentType>) { function WithWindowDimensions(props: Omit, ref: ForwardedRef) { return ( From a75557fcbeebdded218b6eb8752b58aa97c68ae9 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 26 Oct 2023 16:02:48 +0200 Subject: [PATCH 34/97] extract PopoverAnchorPosition to TypeScript --- src/components/Modal/types.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts index 36ce5fa28781..68bffbb60080 100644 --- a/src/components/Modal/types.ts +++ b/src/components/Modal/types.ts @@ -4,7 +4,12 @@ import {ModalProps} from 'react-native-modal'; import {WindowDimensionsProps} from '../withWindowDimensions/types'; import CONST from '../../CONST'; -type ModalVariant = ValueOf; +type PopoverAnchorPosition = { + top?: number; + right?: number; + bottom?: number; + left?: number; +}; type BaseModalProps = WindowDimensionsProps & ModalProps & { @@ -33,15 +38,10 @@ type BaseModalProps = WindowDimensionsProps & onModalShow?: () => void; /** Style of modal to display */ - type?: ModalVariant; + type?: ValueOf; /** The anchor position of a popover modal. Has no effect on other modal types. */ - popoverAnchorPosition?: { - top?: number; - right?: number; - bottom?: number; - left?: number; - }; + popoverAnchorPosition?: PopoverAnchorPosition; outerStyle?: ViewStyle; From f806a947ede5a8c3770d46ac756f608bbc6fa0ba Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 30 Oct 2023 15:58:14 +0700 Subject: [PATCH 35/97] validate login form whever the input is changed --- src/pages/signin/LoginForm/BaseLoginForm.js | 68 +++++++++++++++------ 1 file changed, 49 insertions(+), 19 deletions(-) diff --git a/src/pages/signin/LoginForm/BaseLoginForm.js b/src/pages/signin/LoginForm/BaseLoginForm.js index 9529d7fd0d60..2fa2360430a6 100644 --- a/src/pages/signin/LoginForm/BaseLoginForm.js +++ b/src/pages/signin/LoginForm/BaseLoginForm.js @@ -98,18 +98,52 @@ function LoginForm(props) { const [login, setLogin] = useState(() => Str.removeSMSDomain(props.credentials.login || '')); const [formError, setFormError] = useState(false); const prevIsVisible = usePrevious(props.isVisible); + const firstBlurred = useRef(!canFocusInputOnScreenFocus()); const {translate} = props; /** - * Handle text input and clear formError upon text change + * Validate the input value and set the error for formError + * + * @param {String} value + */ + const validate = useCallback( + (value) => { + const loginTrim = value.trim(); + if (!loginTrim) { + setFormError('common.pleaseEnterEmailOrPhoneNumber'); + return false; + } + + const phoneLogin = LoginUtils.appendCountryCode(LoginUtils.getPhoneNumberWithoutSpecialChars(loginTrim)); + const parsedPhoneNumber = parsePhoneNumber(phoneLogin); + + if (!Str.isValidEmail(loginTrim) && !parsedPhoneNumber.possible) { + if (ValidationUtils.isNumericWithSpecialChars(loginTrim)) { + setFormError('common.error.phoneNumber'); + } else { + setFormError('loginForm.error.invalidFormatEmailLogin'); + } + return false; + } + + setFormError(null); + return true; + }, + [setFormError], + ); + + /** + * Handle text input and validate the text input if it is blurred * * @param {String} text */ const onTextInput = useCallback( (text) => { setLogin(text); - setFormError(null); + if (firstBlurred.current) { + validate(text); + } if (props.account.errors || props.account.message) { Session.clearAccountMessages(); @@ -120,7 +154,7 @@ function LoginForm(props) { CloseAccount.setDefaultData(); } }, - [props.account, props.closeAccount, input, setFormError, setLogin], + [props.account, props.closeAccount, input, setLogin, validate], ); function getSignInWithStyles() { @@ -140,23 +174,11 @@ function LoginForm(props) { CloseAccount.setDefaultData(); } - const loginTrim = login.trim(); - if (!loginTrim) { - setFormError('common.pleaseEnterEmailOrPhoneNumber'); + if (!validate(login)) { return; } - const phoneLogin = LoginUtils.appendCountryCode(LoginUtils.getPhoneNumberWithoutSpecialChars(loginTrim)); - const parsedPhoneNumber = parsePhoneNumber(phoneLogin); - - if (!Str.isValidEmail(loginTrim) && !parsedPhoneNumber.possible) { - if (ValidationUtils.isNumericWithSpecialChars(loginTrim)) { - setFormError('common.error.phoneNumber'); - } else { - setFormError('loginForm.error.invalidFormatEmailLogin'); - } - return; - } + const loginTrim = login.trim(); // If the user has entered a guide email, then we are going to enable an experimental Onyx mode to help with performance if (PolicyUtils.isExpensifyGuideTeam(loginTrim)) { @@ -164,11 +186,12 @@ function LoginForm(props) { MemoryOnlyKeys.enable(); } - setFormError(null); + const phoneLogin = LoginUtils.appendCountryCode(LoginUtils.getPhoneNumberWithoutSpecialChars(loginTrim)); + const parsedPhoneNumber = parsePhoneNumber(phoneLogin); // Check if this login has an account associated with it or not Session.beginSignIn(parsedPhoneNumber.possible ? parsedPhoneNumber.number.e164 : loginTrim); - }, [login, props.account, props.closeAccount, props.network, setFormError]); + }, [login, props.account, props.closeAccount, props.network, validate]); useEffect(() => { // Just call clearAccountMessages on the login page (home route), because when the user is in the transition route and not yet authenticated, @@ -228,6 +251,13 @@ function LoginForm(props) { textContentType="username" nativeID="username" name="username" + onBlur={() => { + if (firstBlurred.current) { + return; + } + firstBlurred.current = true; + validate(login); + }} onChangeText={onTextInput} onSubmitEditing={validateAndSubmitForm} autoCapitalize="none" From e846414b398cd6e9bcb4cdfa16974cf188d9ffe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Mon, 30 Oct 2023 11:01:59 +0100 Subject: [PATCH 36/97] prettier --- src/pages/iou/ReceiptSelector/NavigationAwareCamera.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js index a2f5f0b7c561..783ae36f0e07 100644 --- a/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js +++ b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js @@ -1,10 +1,9 @@ -import React, {useEffect, useRef, useState} from 'react'; -import Webcam from 'react-webcam'; +import {useTabAnimation} from '@react-navigation/material-top-tabs'; import {useIsFocused} from '@react-navigation/native'; import PropTypes from 'prop-types'; -import React, {useEffect, useRef} from 'react'; +import React, {useEffect, useRef, useState} from 'react'; import {View} from 'react-native'; -import {useTabAnimation} from '@react-navigation/material-top-tabs'; +import Webcam from 'react-webcam'; const propTypes = { /* Flag to turn on/off the torch/flashlight - if available */ From da8b083bd138ff6468447e173b2b559cfadddcc4 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 30 Oct 2023 16:19:29 +0100 Subject: [PATCH 37/97] add return types to HOCs, fix linting --- src/components/Modal/BaseModal.tsx | 14 +++++++------- src/components/Modal/types.ts | 4 ++-- .../withWindowDimensions/index.native.tsx | 10 ++++++---- src/components/withWindowDimensions/index.tsx | 10 ++++++---- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index 71a89ddbfdf4..512ab65eb7af 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -3,18 +3,18 @@ import React, {forwardRef, useCallback, useEffect, useMemo, useRef} from 'react' import {View} from 'react-native'; import ReactNativeModal from 'react-native-modal'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; -import styles from '../../styles/styles'; +import CONST from '../../CONST'; +import usePrevious from '../../hooks/usePrevious'; +import useWindowDimensions from '../../hooks/useWindowDimensions'; import * as Modal from '../../libs/actions/Modal'; +import ComposerFocusManager from '../../libs/ComposerFocusManager'; +import useNativeDriver from '../../libs/useNativeDriver'; +import getModalStyles from '../../styles/getModalStyles'; +import styles from '../../styles/styles'; import * as StyleUtils from '../../styles/StyleUtils'; import themeColors from '../../styles/themes/default'; -import getModalStyles from '../../styles/getModalStyles'; -import useWindowDimensions from '../../hooks/useWindowDimensions'; import variables from '../../styles/variables'; -import ComposerFocusManager from '../../libs/ComposerFocusManager'; -import useNativeDriver from '../../libs/useNativeDriver'; -import usePrevious from '../../hooks/usePrevious'; import BaseModalProps from './types'; -import CONST from '../../CONST'; function BaseModal( { diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts index 68bffbb60080..dcb9edf910de 100644 --- a/src/components/Modal/types.ts +++ b/src/components/Modal/types.ts @@ -1,8 +1,8 @@ -import {ValueOf} from 'type-fest'; import {ViewStyle} from 'react-native'; import {ModalProps} from 'react-native-modal'; -import {WindowDimensionsProps} from '../withWindowDimensions/types'; +import {ValueOf} from 'type-fest'; import CONST from '../../CONST'; +import {WindowDimensionsProps} from '../withWindowDimensions/types'; type PopoverAnchorPosition = { top?: number; diff --git a/src/components/withWindowDimensions/index.native.tsx b/src/components/withWindowDimensions/index.native.tsx index 611efa60ee79..8e4611e6c5b0 100644 --- a/src/components/withWindowDimensions/index.native.tsx +++ b/src/components/withWindowDimensions/index.native.tsx @@ -1,12 +1,12 @@ -import React, {createContext, useState, useEffect, useMemo, ComponentType, RefAttributes, ForwardedRef} from 'react'; import PropTypes from 'prop-types'; +import React, {ComponentType, createContext, ForwardedRef, RefAttributes, useEffect, useMemo, useState} from 'react'; import {Dimensions} from 'react-native'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; import getComponentDisplayName from '../../libs/getComponentDisplayName'; -import variables from '../../styles/variables'; import getWindowHeightAdjustment from '../../libs/getWindowHeightAdjustment'; -import {NewDimensions, WindowDimensionsContextData, WindowDimensionsProps} from './types'; +import variables from '../../styles/variables'; import ChildrenProps from '../../types/utils/ChildrenProps'; +import {NewDimensions, WindowDimensionsContextData, WindowDimensionsProps} from './types'; const WindowDimensionsContext = createContext(null); const windowDimensionsPropTypes = { @@ -74,7 +74,9 @@ function WindowDimensionsProvider(props: ChildrenProps) { WindowDimensionsProvider.displayName = 'WindowDimensionsProvider'; -export default function withWindowDimensions(WrappedComponent: ComponentType>) { +export default function withWindowDimensions( + WrappedComponent: ComponentType>, +): (props: Omit & React.RefAttributes) => React.ReactElement | null { function WithWindowDimensions(props: Omit, ref: ForwardedRef) { return ( diff --git a/src/components/withWindowDimensions/index.tsx b/src/components/withWindowDimensions/index.tsx index c1b12d6f6156..508b9e2608fc 100644 --- a/src/components/withWindowDimensions/index.tsx +++ b/src/components/withWindowDimensions/index.tsx @@ -1,13 +1,13 @@ -import React, {createContext, useState, useEffect, useMemo, RefAttributes, ComponentType, ForwardedRef} from 'react'; -import PropTypes from 'prop-types'; import lodashDebounce from 'lodash/debounce'; +import PropTypes from 'prop-types'; +import React, {ComponentType, createContext, ForwardedRef, RefAttributes, useEffect, useMemo, useState} from 'react'; import {Dimensions} from 'react-native'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; +import getWindowHeightAdjustment from '@libs/getWindowHeightAdjustment'; import getComponentDisplayName from '../../libs/getComponentDisplayName'; import variables from '../../styles/variables'; import ChildrenProps from '../../types/utils/ChildrenProps'; import {NewDimensions, WindowDimensionsContextData, WindowDimensionsProps} from './types'; -import getWindowHeightAdjustment from '@libs/getWindowHeightAdjustment'; const WindowDimensionsContext = createContext(null); const windowDimensionsPropTypes = { @@ -80,7 +80,9 @@ function WindowDimensionsProvider(props: ChildrenProps) { WindowDimensionsProvider.displayName = 'WindowDimensionsProvider'; -export default function withWindowDimensions(WrappedComponent: ComponentType>) { +export default function withWindowDimensions( + WrappedComponent: ComponentType>, +): (props: Omit & React.RefAttributes) => React.ReactElement | null { function WithWindowDimensions(props: Omit, ref: ForwardedRef) { return ( From a6287edbcb47c65f6aff9cfe680f659475266dd5 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 30 Oct 2023 16:49:36 +0100 Subject: [PATCH 38/97] fix imports --- src/components/Modal/BaseModal.tsx | 23 +++++++++---------- src/components/Modal/types.ts | 4 ++-- .../withWindowDimensions/index.native.tsx | 8 +++---- src/components/withWindowDimensions/index.tsx | 6 ++--- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index 512ab65eb7af..89f2a5ba2a53 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -1,19 +1,18 @@ -import PropTypes from 'prop-types'; import React, {forwardRef, useCallback, useEffect, useMemo, useRef} from 'react'; import {View} from 'react-native'; import ReactNativeModal from 'react-native-modal'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; -import CONST from '../../CONST'; -import usePrevious from '../../hooks/usePrevious'; -import useWindowDimensions from '../../hooks/useWindowDimensions'; -import * as Modal from '../../libs/actions/Modal'; -import ComposerFocusManager from '../../libs/ComposerFocusManager'; -import useNativeDriver from '../../libs/useNativeDriver'; -import getModalStyles from '../../styles/getModalStyles'; -import styles from '../../styles/styles'; -import * as StyleUtils from '../../styles/StyleUtils'; -import themeColors from '../../styles/themes/default'; -import variables from '../../styles/variables'; +import usePrevious from '@hooks/usePrevious'; +import useWindowDimensions from '@hooks/useWindowDimensions'; +import ComposerFocusManager from '@libs/ComposerFocusManager'; +import useNativeDriver from '@libs/useNativeDriver'; +import getModalStyles from '@styles/getModalStyles'; +import styles from '@styles/styles'; +import * as StyleUtils from '@styles/StyleUtils'; +import themeColors from '@styles/themes/default'; +import variables from '@styles/variables'; +import * as Modal from '@userActions/Modal'; +import CONST from '@src/CONST'; import BaseModalProps from './types'; function BaseModal( diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts index dcb9edf910de..3fa60e6ac765 100644 --- a/src/components/Modal/types.ts +++ b/src/components/Modal/types.ts @@ -1,8 +1,8 @@ import {ViewStyle} from 'react-native'; import {ModalProps} from 'react-native-modal'; import {ValueOf} from 'type-fest'; -import CONST from '../../CONST'; -import {WindowDimensionsProps} from '../withWindowDimensions/types'; +import {WindowDimensionsProps} from '@components/withWindowDimensions/types'; +import CONST from '@src/CONST'; type PopoverAnchorPosition = { top?: number; diff --git a/src/components/withWindowDimensions/index.native.tsx b/src/components/withWindowDimensions/index.native.tsx index 8e4611e6c5b0..0c9f61a45c0b 100644 --- a/src/components/withWindowDimensions/index.native.tsx +++ b/src/components/withWindowDimensions/index.native.tsx @@ -2,10 +2,10 @@ import PropTypes from 'prop-types'; import React, {ComponentType, createContext, ForwardedRef, RefAttributes, useEffect, useMemo, useState} from 'react'; import {Dimensions} from 'react-native'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; -import getComponentDisplayName from '../../libs/getComponentDisplayName'; -import getWindowHeightAdjustment from '../../libs/getWindowHeightAdjustment'; -import variables from '../../styles/variables'; -import ChildrenProps from '../../types/utils/ChildrenProps'; +import getComponentDisplayName from '@libs/getComponentDisplayName'; +import getWindowHeightAdjustment from '@libs/getWindowHeightAdjustment'; +import variables from '@styles/variables'; +import ChildrenProps from '@src/types/utils/ChildrenProps'; import {NewDimensions, WindowDimensionsContextData, WindowDimensionsProps} from './types'; const WindowDimensionsContext = createContext(null); diff --git a/src/components/withWindowDimensions/index.tsx b/src/components/withWindowDimensions/index.tsx index 508b9e2608fc..1479450deec4 100644 --- a/src/components/withWindowDimensions/index.tsx +++ b/src/components/withWindowDimensions/index.tsx @@ -3,10 +3,10 @@ import PropTypes from 'prop-types'; import React, {ComponentType, createContext, ForwardedRef, RefAttributes, useEffect, useMemo, useState} from 'react'; import {Dimensions} from 'react-native'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; +import getComponentDisplayName from '@libs/getComponentDisplayName'; import getWindowHeightAdjustment from '@libs/getWindowHeightAdjustment'; -import getComponentDisplayName from '../../libs/getComponentDisplayName'; -import variables from '../../styles/variables'; -import ChildrenProps from '../../types/utils/ChildrenProps'; +import variables from '@styles/variables'; +import ChildrenProps from '@src/types/utils/ChildrenProps'; import {NewDimensions, WindowDimensionsContextData, WindowDimensionsProps} from './types'; const WindowDimensionsContext = createContext(null); From 803f9f955fcfe49fedffa56a91f8bd5af71b1d88 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 30 Oct 2023 23:43:47 +0700 Subject: [PATCH 39/97] fix validate on native --- src/pages/signin/LoginForm/BaseLoginForm.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/pages/signin/LoginForm/BaseLoginForm.js b/src/pages/signin/LoginForm/BaseLoginForm.js index 2fa2360430a6..e52389076f90 100644 --- a/src/pages/signin/LoginForm/BaseLoginForm.js +++ b/src/pages/signin/LoginForm/BaseLoginForm.js @@ -98,7 +98,7 @@ function LoginForm(props) { const [login, setLogin] = useState(() => Str.removeSMSDomain(props.credentials.login || '')); const [formError, setFormError] = useState(false); const prevIsVisible = usePrevious(props.isVisible); - const firstBlurred = useRef(!canFocusInputOnScreenFocus()); + const firstBlurred = useRef(false); const {translate} = props; @@ -174,6 +174,12 @@ function LoginForm(props) { CloseAccount.setDefaultData(); } + // For native, the single input doesn't lost focus when we click outside. + // So we need to change firstBlurred here to make the validate function is called whenever the text input is changed after the first validation. + if (!firstBlurred.current) { + firstBlurred.current = true; + } + if (!validate(login)) { return; } From 52ff093865e5f7a3914da7d84cb2aa6615bf6752 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Fri, 27 Oct 2023 15:38:00 +0200 Subject: [PATCH 40/97] [TS migration] Migrate 'MentionSuggestions.js' component to TypeScript --- src/components/AvatarTypes.ts | 13 +++ ...nSuggestions.js => MentionSuggestions.tsx} | 103 ++++++++---------- src/styles/StyleUtils.ts | 4 +- 3 files changed, 61 insertions(+), 59 deletions(-) create mode 100644 src/components/AvatarTypes.ts rename src/components/{MentionSuggestions.js => MentionSuggestions.tsx} (56%) diff --git a/src/components/AvatarTypes.ts b/src/components/AvatarTypes.ts new file mode 100644 index 000000000000..2e94904b0d7c --- /dev/null +++ b/src/components/AvatarTypes.ts @@ -0,0 +1,13 @@ +import CONST from '@src/CONST'; + +type AvatarFunctionType = () => void; + +type AvatarType = { + source: string | AvatarFunctionType; + type: typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; + name: string; + id: number | string; + fallbackIcon: string | AvatarFunctionType; +}; + +export default AvatarType; diff --git a/src/components/MentionSuggestions.js b/src/components/MentionSuggestions.tsx similarity index 56% rename from src/components/MentionSuggestions.js rename to src/components/MentionSuggestions.tsx index d18b8947e68d..fe7ce2896bfc 100644 --- a/src/components/MentionSuggestions.js +++ b/src/components/MentionSuggestions.tsx @@ -1,7 +1,5 @@ -import PropTypes from 'prop-types'; import React from 'react'; import {View} from 'react-native'; -import _ from 'underscore'; import getStyledTextArray from '@libs/GetStyledTextArray'; import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; @@ -9,65 +7,58 @@ import themeColors from '@styles/themes/default'; import CONST from '@src/CONST'; import AutoCompleteSuggestions from './AutoCompleteSuggestions'; import Avatar from './Avatar'; -import avatarPropTypes from './avatarPropTypes'; +import AvatarTypes from './AvatarTypes'; import Text from './Text'; -const propTypes = { - /** The index of the highlighted mention */ - highlightedMentionIndex: PropTypes.number, +type Mention = { + /** Display name of the user */ + text: string; - /** Array of suggested mentions */ - mentions: PropTypes.arrayOf( - PropTypes.shape({ - /** Display name of the user */ - text: PropTypes.string, + /** Email/phone number of the user */ + alternateText: string; + + /** Array of icons of the user. We use the first element of this array */ + icons: AvatarTypes[]; +}; - /** Email/phone number of the user */ - alternateText: PropTypes.string, +type MentionSuggestionsProps = { + /** The index of the highlighted mention */ + highlightedMentionIndex?: number; - /** Array of icons of the user. We use the first element of this array */ - icons: PropTypes.arrayOf(avatarPropTypes), - }), - ).isRequired, + /** Array of suggested mentions */ + mentions: Mention[]; /** Fired when the user selects an mention */ - onSelect: PropTypes.func.isRequired, + onSelect: () => void; /** Mention prefix that follows the @ sign */ - prefix: PropTypes.string.isRequired, + prefix: string; /** Show that we can use large mention picker. * Depending on available space and whether the input is expanded, we can have a small or large mention suggester. * When this value is false, the suggester will have a height of 2.5 items. When this value is true, the height can be up to 5 items. */ - isMentionPickerLarge: PropTypes.bool.isRequired, + isMentionPickerLarge: boolean; /** Meaures the parent container's position and dimensions. */ - measureParentContainer: PropTypes.func, -}; - -const defaultProps = { - highlightedMentionIndex: 0, - measureParentContainer: () => {}, + measureParentContainer: () => void; }; /** * Create unique keys for each mention item - * @param {Object} item - * @param {Number} index - * @returns {String} + * @param item + * @param index */ -const keyExtractor = (item) => item.alternateText; +const keyExtractor = (item: Mention) => item.alternateText; -function MentionSuggestions(props) { +function MentionSuggestions({prefix, mentions, highlightedMentionIndex = 0, onSelect, isMentionPickerLarge, measureParentContainer = () => {}}: MentionSuggestionsProps) { /** * Render a suggestion menu item component. - * @param {Object} item - * @returns {JSX.Element} + * @param item */ - const renderSuggestionMenuItem = (item) => { + const renderSuggestionMenuItem = (item: Mention) => { const isIcon = item.text === CONST.AUTO_COMPLETE_SUGGESTER.HERE_TEXT; - const styledDisplayName = getStyledTextArray(item.text, props.prefix); - const styledHandle = item.text === item.alternateText ? '' : getStyledTextArray(item.alternateText, props.prefix); + const styledDisplayName = getStyledTextArray(item.text, prefix); + const styledHandle = item.text === item.alternateText ? '' : getStyledTextArray(item.alternateText, prefix); return ( @@ -85,9 +76,9 @@ function MentionSuggestions(props) { style={[styles.mentionSuggestionsText, styles.flexShrink1]} numberOfLines={1} > - {_.map(styledDisplayName, ({text, isColored}, i) => ( + {styledDisplayName.map(({text, isColored}) => ( {text} @@ -98,18 +89,18 @@ function MentionSuggestions(props) { style={[styles.mentionSuggestionsText, styles.flex1]} numberOfLines={1} > - {_.map( - styledHandle, - ({text, isColored}, i) => - text !== '' && ( - - {text} - - ), - )} + {styledHandle && + styledHandle.map( + ({text, isColored}) => + text !== '' && ( + + {text} + + ), + )} ); @@ -117,20 +108,18 @@ function MentionSuggestions(props) { return ( ); } -MentionSuggestions.propTypes = propTypes; -MentionSuggestions.defaultProps = defaultProps; MentionSuggestions.displayName = 'MentionSuggestions'; export default MentionSuggestions; diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index f58d2c9a236d..77fe3dea9faa 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -1,5 +1,5 @@ import {CSSProperties} from 'react'; -import {Animated, DimensionValue, ImageStyle, PressableStateCallbackType, TextStyle, ViewStyle} from 'react-native'; +import {Animated, DimensionValue, ImageStyle, PressableStateCallbackType, StyleProp, TextStyle, ViewStyle} from 'react-native'; import {EdgeInsets} from 'react-native-safe-area-context'; import {ValueOf} from 'type-fest'; import * as Browser from '@libs/Browser'; @@ -1012,7 +1012,7 @@ function getAutoCompleteSuggestionContainerStyle(itemsHeight: number): ViewStyle /** * Select the correct color for text. */ -function getColoredBackgroundStyle(isColored: boolean): ViewStyle { +function getColoredBackgroundStyle(isColored: boolean): StyleProp | undefined { return {backgroundColor: isColored ? themeColors.link : undefined}; } From 7ce4f40b9c50b373cc335abff22b647a879ce4fe Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Tue, 31 Oct 2023 12:54:17 +0100 Subject: [PATCH 41/97] remove redundant styles --- src/styles/styles.ts | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/styles/styles.ts b/src/styles/styles.ts index 589c3756042f..51dd70e70f2e 100644 --- a/src/styles/styles.ts +++ b/src/styles/styles.ts @@ -326,10 +326,6 @@ const styles = (theme: ThemeDefault) => textAlign: 'left', }, - textUnderline: { - textDecorationLine: 'underline', - }, - label: { fontSize: variables.fontSizeLabel, lineHeight: variables.lineHeightLarge, @@ -382,10 +378,6 @@ const styles = (theme: ThemeDefault) => fontSize: variables.fontSizeLarge, }, - textXLarge: { - fontSize: variables.fontSizeXLarge, - }, - textXXLarge: { fontSize: variables.fontSizeXXLarge, }, @@ -405,11 +397,6 @@ const styles = (theme: ThemeDefault) => fontWeight: fontWeightBold, }, - textItalic: { - fontFamily: fontFamily.EXP_NEUE_ITALIC, - fontStyle: 'italic', - }, - textHeadline: { ...headlineFont, ...whiteSpace.preWrap, @@ -426,10 +413,6 @@ const styles = (theme: ThemeDefault) => lineHeight: variables.lineHeightSizeh1, }, - textDecorationNoLine: { - textDecorationLine: 'none', - }, - textWhite: { color: theme.textLight, }, @@ -438,10 +421,6 @@ const styles = (theme: ThemeDefault) => color: theme.link, }, - textUppercase: { - textTransform: 'uppercase', - }, - textNoWrap: { ...whiteSpace.noWrap, }, From d4ef364e86a07248b0d926d756a0343d810cf780 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 2 Nov 2023 12:04:03 +0700 Subject: [PATCH 42/97] add onMounseDown for sign icon --- src/components/SignInButtons/AppleSignIn/index.website.js | 2 ++ src/components/SignInButtons/GoogleSignIn/index.website.js | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/components/SignInButtons/AppleSignIn/index.website.js b/src/components/SignInButtons/AppleSignIn/index.website.js index adae0a691e13..587b6dbec983 100644 --- a/src/components/SignInButtons/AppleSignIn/index.website.js +++ b/src/components/SignInButtons/AppleSignIn/index.website.js @@ -93,6 +93,7 @@ function AppleSignInDiv({isDesktopFlow}) { data-width={CONST.SIGN_IN_FORM_WIDTH} data-height="52" style={{cursor: 'pointer'}} + onMouseDown={(event) => event.preventDefault()} /> ) : (
event.preventDefault()} /> ); } diff --git a/src/components/SignInButtons/GoogleSignIn/index.website.js b/src/components/SignInButtons/GoogleSignIn/index.website.js index 7f3a3019c318..be4b944822dd 100644 --- a/src/components/SignInButtons/GoogleSignIn/index.website.js +++ b/src/components/SignInButtons/GoogleSignIn/index.website.js @@ -76,6 +76,7 @@ function GoogleSignIn({translate, isDesktopFlow}) { id={desktopId} role="button" aria-label={translate('common.signInWithGoogle')} + onMouseDown={(event) => event.preventDefault()} /> ) : ( @@ -84,6 +85,7 @@ function GoogleSignIn({translate, isDesktopFlow}) { id={mainId} role="button" aria-label={translate('common.signInWithGoogle')} + onMouseDown={(event) => event.preventDefault()} /> ); From 5d42500bfc78c8a03fa82dcab00b987baf44d8b4 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 2 Nov 2023 17:12:51 +0700 Subject: [PATCH 43/97] fix bug on desktop --- src/components/SignInButtons/AppleSignIn/index.desktop.js | 7 +++++-- src/components/SignInButtons/GoogleSignIn/index.desktop.js | 2 +- src/components/SignInButtons/IconButton.js | 7 ++++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/components/SignInButtons/AppleSignIn/index.desktop.js b/src/components/SignInButtons/AppleSignIn/index.desktop.js index cad37d585de4..fac187c6fcc4 100644 --- a/src/components/SignInButtons/AppleSignIn/index.desktop.js +++ b/src/components/SignInButtons/AppleSignIn/index.desktop.js @@ -14,9 +14,12 @@ const appleSignInWebRouteForDesktopFlow = `${CONFIG.EXPENSIFY.NEW_EXPENSIFY_URL} */ function AppleSignIn() { return ( - + { + onPressIn={() => { window.open(appleSignInWebRouteForDesktopFlow); }} provider={CONST.SIGN_IN_METHOD.APPLE} diff --git a/src/components/SignInButtons/GoogleSignIn/index.desktop.js b/src/components/SignInButtons/GoogleSignIn/index.desktop.js index e69905dcad05..b2f3cb01a980 100644 --- a/src/components/SignInButtons/GoogleSignIn/index.desktop.js +++ b/src/components/SignInButtons/GoogleSignIn/index.desktop.js @@ -19,7 +19,7 @@ function GoogleSignIn() { return ( { + onPressIn={() => { window.open(googleSignInWebRouteForDesktopFlow); }} provider={CONST.SIGN_IN_METHOD.GOOGLE} diff --git a/src/components/SignInButtons/IconButton.js b/src/components/SignInButtons/IconButton.js index 0d18779ea9ba..49d7dccea59e 100644 --- a/src/components/SignInButtons/IconButton.js +++ b/src/components/SignInButtons/IconButton.js @@ -11,6 +11,9 @@ const propTypes = { /** The on press method */ onPress: PropTypes.func, + /** The on press in method */ + onPressIn: PropTypes.func, + /** Which provider you are using to sign in */ provider: PropTypes.string.isRequired, @@ -19,6 +22,7 @@ const propTypes = { const defaultProps = { onPress: () => {}, + onPressIn: () => {}, }; const providerData = { @@ -32,9 +36,10 @@ const providerData = { }, }; -function IconButton({onPress, translate, provider}) { +function IconButton({onPress, translate, provider, onPressIn}) { return ( Date: Thu, 2 Nov 2023 13:24:44 +0100 Subject: [PATCH 44/97] change namings, props --- src/components/AvatarType.ts | 14 ++++++++++++++ src/components/AvatarTypes.ts | 13 ------------- src/components/MentionSuggestions.tsx | 6 +++--- 3 files changed, 17 insertions(+), 16 deletions(-) create mode 100644 src/components/AvatarType.ts delete mode 100644 src/components/AvatarTypes.ts diff --git a/src/components/AvatarType.ts b/src/components/AvatarType.ts new file mode 100644 index 000000000000..dc07a638106d --- /dev/null +++ b/src/components/AvatarType.ts @@ -0,0 +1,14 @@ +import CONST from '@src/CONST'; + + +type AvatarFunction = () => void; + +type Avatar = { + source?: string | AvatarFunction; + type?: typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; + name?: string; + id?: number | string; + fallbackIcon?: string | AvatarFunction; +}; + +export default Avatar; diff --git a/src/components/AvatarTypes.ts b/src/components/AvatarTypes.ts deleted file mode 100644 index 2e94904b0d7c..000000000000 --- a/src/components/AvatarTypes.ts +++ /dev/null @@ -1,13 +0,0 @@ -import CONST from '@src/CONST'; - -type AvatarFunctionType = () => void; - -type AvatarType = { - source: string | AvatarFunctionType; - type: typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; - name: string; - id: number | string; - fallbackIcon: string | AvatarFunctionType; -}; - -export default AvatarType; diff --git a/src/components/MentionSuggestions.tsx b/src/components/MentionSuggestions.tsx index fe7ce2896bfc..fdff35638766 100644 --- a/src/components/MentionSuggestions.tsx +++ b/src/components/MentionSuggestions.tsx @@ -7,7 +7,7 @@ import themeColors from '@styles/themes/default'; import CONST from '@src/CONST'; import AutoCompleteSuggestions from './AutoCompleteSuggestions'; import Avatar from './Avatar'; -import AvatarTypes from './AvatarTypes'; +import AvatarType from './AvatarType'; import Text from './Text'; type Mention = { @@ -18,7 +18,7 @@ type Mention = { alternateText: string; /** Array of icons of the user. We use the first element of this array */ - icons: AvatarTypes[]; + icons: AvatarType[]; }; type MentionSuggestionsProps = { @@ -92,7 +92,7 @@ function MentionSuggestions({prefix, mentions, highlightedMentionIndex = 0, onSe {styledHandle && styledHandle.map( ({text, isColored}) => - text !== '' && ( + Boolean(text) && ( Date: Thu, 2 Nov 2023 14:18:16 +0100 Subject: [PATCH 45/97] revert to map with index key --- src/components/MentionSuggestions.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/MentionSuggestions.tsx b/src/components/MentionSuggestions.tsx index fdff35638766..74f8503881ab 100644 --- a/src/components/MentionSuggestions.tsx +++ b/src/components/MentionSuggestions.tsx @@ -76,9 +76,9 @@ function MentionSuggestions({prefix, mentions, highlightedMentionIndex = 0, onSe style={[styles.mentionSuggestionsText, styles.flexShrink1]} numberOfLines={1} > - {styledDisplayName.map(({text, isColored}) => ( + {styledDisplayName.map(({text, isColored}, i) => ( {text} @@ -91,10 +91,10 @@ function MentionSuggestions({prefix, mentions, highlightedMentionIndex = 0, onSe > {styledHandle && styledHandle.map( - ({text, isColored}) => + ({text, isColored}, i) => Boolean(text) && ( {text} From 9c256ed3f41c2b76d9568cfc9359aee6cd857a42 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Thu, 2 Nov 2023 14:27:43 +0100 Subject: [PATCH 46/97] change type name --- src/components/{AvatarType.ts => AvatarIconType.ts} | 4 ++-- src/components/MentionSuggestions.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/components/{AvatarType.ts => AvatarIconType.ts} (85%) diff --git a/src/components/AvatarType.ts b/src/components/AvatarIconType.ts similarity index 85% rename from src/components/AvatarType.ts rename to src/components/AvatarIconType.ts index dc07a638106d..117fbd2b085a 100644 --- a/src/components/AvatarType.ts +++ b/src/components/AvatarIconType.ts @@ -3,7 +3,7 @@ import CONST from '@src/CONST'; type AvatarFunction = () => void; -type Avatar = { +type AvatarIcon = { source?: string | AvatarFunction; type?: typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; name?: string; @@ -11,4 +11,4 @@ type Avatar = { fallbackIcon?: string | AvatarFunction; }; -export default Avatar; +export default AvatarIcon; diff --git a/src/components/MentionSuggestions.tsx b/src/components/MentionSuggestions.tsx index 74f8503881ab..23274fd8fa36 100644 --- a/src/components/MentionSuggestions.tsx +++ b/src/components/MentionSuggestions.tsx @@ -7,7 +7,7 @@ import themeColors from '@styles/themes/default'; import CONST from '@src/CONST'; import AutoCompleteSuggestions from './AutoCompleteSuggestions'; import Avatar from './Avatar'; -import AvatarType from './AvatarType'; +import AvatarIcon from './AvatarIconType'; import Text from './Text'; type Mention = { @@ -18,7 +18,7 @@ type Mention = { alternateText: string; /** Array of icons of the user. We use the first element of this array */ - icons: AvatarType[]; + icons: AvatarIcon[]; }; type MentionSuggestionsProps = { From 6ac09c50823b6b9de8f6f7b71b9ab1dea4b8e119 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Thu, 2 Nov 2023 14:29:25 +0100 Subject: [PATCH 47/97] remove optional for some props --- src/components/AvatarIconType.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/AvatarIconType.ts b/src/components/AvatarIconType.ts index 117fbd2b085a..5e05d357e2d3 100644 --- a/src/components/AvatarIconType.ts +++ b/src/components/AvatarIconType.ts @@ -4,10 +4,10 @@ import CONST from '@src/CONST'; type AvatarFunction = () => void; type AvatarIcon = { - source?: string | AvatarFunction; - type?: typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; - name?: string; - id?: number | string; + source: string | AvatarFunction; + type: typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; + name: string; + id: number | string; fallbackIcon?: string | AvatarFunction; }; From 4ed174463cb98a6252c241ade052d643d4c099db Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Thu, 2 Nov 2023 15:32:11 +0100 Subject: [PATCH 48/97] fix lint --- src/components/AvatarIconType.ts | 1 - src/components/MentionSuggestions.tsx | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/AvatarIconType.ts b/src/components/AvatarIconType.ts index 5e05d357e2d3..192acabb2399 100644 --- a/src/components/AvatarIconType.ts +++ b/src/components/AvatarIconType.ts @@ -1,6 +1,5 @@ import CONST from '@src/CONST'; - type AvatarFunction = () => void; type AvatarIcon = { diff --git a/src/components/MentionSuggestions.tsx b/src/components/MentionSuggestions.tsx index 23274fd8fa36..a7a67edafff0 100644 --- a/src/components/MentionSuggestions.tsx +++ b/src/components/MentionSuggestions.tsx @@ -78,6 +78,7 @@ function MentionSuggestions({prefix, mentions, highlightedMentionIndex = 0, onSe > {styledDisplayName.map(({text, isColored}, i) => ( @@ -94,6 +95,7 @@ function MentionSuggestions({prefix, mentions, highlightedMentionIndex = 0, onSe ({text, isColored}, i) => Boolean(text) && ( From 0138d943fc97c4576e41cb0fd937bb8b7d7bf2d0 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Fri, 3 Nov 2023 11:50:13 +0100 Subject: [PATCH 49/97] change types, mapping --- src/components/AvatarIconType.ts | 13 --------- src/components/MentionSuggestions.tsx | 42 +++++++++++++++------------ src/styles/StyleUtils.ts | 2 +- 3 files changed, 25 insertions(+), 32 deletions(-) delete mode 100644 src/components/AvatarIconType.ts diff --git a/src/components/AvatarIconType.ts b/src/components/AvatarIconType.ts deleted file mode 100644 index 192acabb2399..000000000000 --- a/src/components/AvatarIconType.ts +++ /dev/null @@ -1,13 +0,0 @@ -import CONST from '@src/CONST'; - -type AvatarFunction = () => void; - -type AvatarIcon = { - source: string | AvatarFunction; - type: typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; - name: string; - id: number | string; - fallbackIcon?: string | AvatarFunction; -}; - -export default AvatarIcon; diff --git a/src/components/MentionSuggestions.tsx b/src/components/MentionSuggestions.tsx index a7a67edafff0..c9f7e93d173e 100644 --- a/src/components/MentionSuggestions.tsx +++ b/src/components/MentionSuggestions.tsx @@ -7,9 +7,19 @@ import themeColors from '@styles/themes/default'; import CONST from '@src/CONST'; import AutoCompleteSuggestions from './AutoCompleteSuggestions'; import Avatar from './Avatar'; -import AvatarIcon from './AvatarIconType'; import Text from './Text'; +// TODO: remove when avatarPropTypes will be migrated to TS +type AvatarFunction = () => void; + +type AvatarIcon = { + source: string | AvatarFunction; + type: typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; + name: string; + id: number | string; + fallbackIcon?: string | AvatarFunction; +}; + type Mention = { /** Display name of the user */ text: string; @@ -45,20 +55,17 @@ type MentionSuggestionsProps = { /** * Create unique keys for each mention item - * @param item - * @param index */ const keyExtractor = (item: Mention) => item.alternateText; function MentionSuggestions({prefix, mentions, highlightedMentionIndex = 0, onSelect, isMentionPickerLarge, measureParentContainer = () => {}}: MentionSuggestionsProps) { /** * Render a suggestion menu item component. - * @param item */ const renderSuggestionMenuItem = (item: Mention) => { const isIcon = item.text === CONST.AUTO_COMPLETE_SUGGESTER.HERE_TEXT; const styledDisplayName = getStyledTextArray(item.text, prefix); - const styledHandle = item.text === item.alternateText ? '' : getStyledTextArray(item.alternateText, prefix); + const styledHandle = item.text === item.alternateText ? undefined : getStyledTextArray(item.alternateText, prefix); return ( @@ -90,19 +97,18 @@ function MentionSuggestions({prefix, mentions, highlightedMentionIndex = 0, onSe style={[styles.mentionSuggestionsText, styles.flex1]} numberOfLines={1} > - {styledHandle && - styledHandle.map( - ({text, isColored}, i) => - Boolean(text) && ( - - {text} - - ), - )} + {styledHandle?.map( + ({text, isColored}, i) => + Boolean(text) && ( + + {text} + + ), + )} ); diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index 77fe3dea9faa..393940070895 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -1012,7 +1012,7 @@ function getAutoCompleteSuggestionContainerStyle(itemsHeight: number): ViewStyle /** * Select the correct color for text. */ -function getColoredBackgroundStyle(isColored: boolean): StyleProp | undefined { +function getColoredBackgroundStyle(isColored: boolean): StyleProp { return {backgroundColor: isColored ? themeColors.link : undefined}; } From dab085cfab08cd6e8ccfe2d13448979064f8cf5e Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Fri, 3 Nov 2023 14:49:26 +0100 Subject: [PATCH 50/97] reuse existed Icon type --- src/components/MentionSuggestions.tsx | 14 ++------------ src/types/onyx/OnyxCommon.ts | 7 +++++-- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/components/MentionSuggestions.tsx b/src/components/MentionSuggestions.tsx index c9f7e93d173e..a009364c2223 100644 --- a/src/components/MentionSuggestions.tsx +++ b/src/components/MentionSuggestions.tsx @@ -5,21 +5,11 @@ import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; import themeColors from '@styles/themes/default'; import CONST from '@src/CONST'; +import {Icon} from '@src/types/onyx/OnyxCommon'; import AutoCompleteSuggestions from './AutoCompleteSuggestions'; import Avatar from './Avatar'; import Text from './Text'; -// TODO: remove when avatarPropTypes will be migrated to TS -type AvatarFunction = () => void; - -type AvatarIcon = { - source: string | AvatarFunction; - type: typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; - name: string; - id: number | string; - fallbackIcon?: string | AvatarFunction; -}; - type Mention = { /** Display name of the user */ text: string; @@ -28,7 +18,7 @@ type Mention = { alternateText: string; /** Array of icons of the user. We use the first element of this array */ - icons: AvatarIcon[]; + icons: Icon[]; }; type MentionSuggestionsProps = { diff --git a/src/types/onyx/OnyxCommon.ts b/src/types/onyx/OnyxCommon.ts index bafd5e8cbbf0..0ab6cee0fe15 100644 --- a/src/types/onyx/OnyxCommon.ts +++ b/src/types/onyx/OnyxCommon.ts @@ -1,4 +1,5 @@ import * as React from 'react'; +import {SvgProps} from 'react-native-svg'; import {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; @@ -9,9 +10,11 @@ type ErrorFields = Record | null>; type Errors = Record; type Icon = { - source: React.ReactNode | string; - type: 'avatar' | 'workspace'; + source: string | React.FC; + type: typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; name: string; + id: number | string; + fallbackIcon?: string | React.FC; }; export type {Icon, PendingAction, ErrorFields, Errors}; From fbbd172739a81f89f4dee53e0ea0a0ee26e6111f Mon Sep 17 00:00:00 2001 From: Mahesh Vagicherla Date: Sat, 4 Nov 2023 21:14:31 +0530 Subject: [PATCH 51/97] fix: remove options props --- .../Navigation/AppNavigator/Navigators/RightModalNavigator.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js index de3fb9e79659..3fd8dec90d8d 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js +++ b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js @@ -37,10 +37,6 @@ function RightModalNavigator(props) { Date: Mon, 6 Nov 2023 13:35:27 +0700 Subject: [PATCH 52/97] fix lint --- src/components/SignInButtons/AppleSignIn/index.website.js | 2 ++ src/components/SignInButtons/GoogleSignIn/index.website.js | 2 ++ src/styles/fontFamily/multiFontFamily.ts | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/SignInButtons/AppleSignIn/index.website.js b/src/components/SignInButtons/AppleSignIn/index.website.js index 587b6dbec983..f30e06a9bcab 100644 --- a/src/components/SignInButtons/AppleSignIn/index.website.js +++ b/src/components/SignInButtons/AppleSignIn/index.website.js @@ -83,6 +83,7 @@ function AppleSignInDiv({isDesktopFlow}) { }, []); return isDesktopFlow ? ( + /* eslint-disable-next-line jsx-a11y/no-static-element-interactions */
event.preventDefault()} /> ) : ( + /* eslint-disable-next-line jsx-a11y/no-static-element-interactions */
+ {/* eslint-disable-next-line jsx-a11y/interactive-supports-focus */}
) : ( + {/* eslint-disable-next-line jsx-a11y/interactive-supports-focus */}
Date: Mon, 6 Nov 2023 13:43:18 +0700 Subject: [PATCH 53/97] fix lint --- src/styles/fontFamily/multiFontFamily.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/fontFamily/multiFontFamily.ts b/src/styles/fontFamily/multiFontFamily.ts index b6b74ad68dfc..5bd89e0d4bcb 100644 --- a/src/styles/fontFamily/multiFontFamily.ts +++ b/src/styles/fontFamily/multiFontFamily.ts @@ -1,5 +1,5 @@ -import CONST from '@src/CONST'; import getOperatingSystem from '@libs/getOperatingSystem'; +import CONST from '@src/CONST'; import {multiBold} from './bold'; import FontFamilyStyles from './types'; From 08c0d4c74a85887369c86a7087f68819e86ae42e Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Mon, 6 Nov 2023 12:22:28 +0100 Subject: [PATCH 54/97] TextCommentFragment: Fix import paths --- src/pages/home/report/comment/TextCommentFragment.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/comment/TextCommentFragment.js b/src/pages/home/report/comment/TextCommentFragment.js index 10db27143012..155736e34767 100644 --- a/src/pages/home/report/comment/TextCommentFragment.js +++ b/src/pages/home/report/comment/TextCommentFragment.js @@ -14,9 +14,9 @@ import styles from '@styles/styles'; import themeColors from '@styles/themes/default'; import variables from '@styles/variables'; import CONST from '@src/CONST'; -import reportActionFragmentPropTypes from '../reportActionFragmentPropTypes'; +import reportActionFragmentPropTypes from '@pages/home/report/reportActionFragmentPropTypes'; import reportActionSourcePropType from "@pages/home/report/reportActionSourcePropType"; -import RenderCommentHTML from "@pages/home/report/comment/RenderCommentHTML"; +import RenderCommentHTML from "./RenderCommentHTML"; const propTypes = { /** The reportAction's source */ From cfaf05d106fd8c23d8e4ecc39cd7a52bb12ee69d Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Mon, 6 Nov 2023 12:26:39 +0100 Subject: [PATCH 55/97] Fix more import paths --- src/pages/home/report/ReportActionItemFragment.js | 6 +++--- src/pages/home/report/comment/AttachmentCommentFragment.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/home/report/ReportActionItemFragment.js b/src/pages/home/report/ReportActionItemFragment.js index 42294e01abf0..33f6339c2360 100644 --- a/src/pages/home/report/ReportActionItemFragment.js +++ b/src/pages/home/report/ReportActionItemFragment.js @@ -10,10 +10,10 @@ import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withW import compose from '@libs/compose'; import styles from '@styles/styles'; import CONST from '@src/CONST'; -import reportActionFragmentPropTypes from './reportActionFragmentPropTypes'; -import AttachmentCommentFragment from '@pages/home/report/comment/AttachmentCommentFragment'; -import TextCommentFragment from '@pages/home/report/comment/TextCommentFragment'; import * as ReportUtils from '@libs/ReportUtils'; +import AttachmentCommentFragment from './comment/AttachmentCommentFragment'; +import TextCommentFragment from './comment/TextCommentFragment'; +import reportActionFragmentPropTypes from './reportActionFragmentPropTypes'; const propTypes = { /** Users accountID */ diff --git a/src/pages/home/report/comment/AttachmentCommentFragment.js b/src/pages/home/report/comment/AttachmentCommentFragment.js index 9feeb3fdfc9f..db610dcb7394 100644 --- a/src/pages/home/report/comment/AttachmentCommentFragment.js +++ b/src/pages/home/report/comment/AttachmentCommentFragment.js @@ -3,7 +3,7 @@ import React from 'react'; import styles from '@styles/styles'; import reportActionSourcePropType from "@pages/home/report/reportActionSourcePropType"; import {View} from "react-native"; -import RenderCommentHTML from "@pages/home/report/comment/RenderCommentHTML"; +import RenderCommentHTML from "./RenderCommentHTML"; const propTypes = { /** The reportAction's source */ From bf943c6e4da19cd7a91f9324d157dd64e66cbf25 Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Mon, 6 Nov 2023 12:34:43 +0100 Subject: [PATCH 56/97] Re-add the convertToLTR import --- src/pages/home/report/ReportActionItemFragment.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/home/report/ReportActionItemFragment.js b/src/pages/home/report/ReportActionItemFragment.js index 33f6339c2360..2efd325ff06a 100644 --- a/src/pages/home/report/ReportActionItemFragment.js +++ b/src/pages/home/report/ReportActionItemFragment.js @@ -11,6 +11,7 @@ import compose from '@libs/compose'; import styles from '@styles/styles'; import CONST from '@src/CONST'; import * as ReportUtils from '@libs/ReportUtils'; +import convertToLTR from '@libs/convertToLTR'; import AttachmentCommentFragment from './comment/AttachmentCommentFragment'; import TextCommentFragment from './comment/TextCommentFragment'; import reportActionFragmentPropTypes from './reportActionFragmentPropTypes'; From 963b7c4d11b161d06efc4c18a456c05b454fe9d3 Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Tue, 7 Nov 2023 11:22:41 +0100 Subject: [PATCH 57/97] Run Prettier --- src/pages/home/report/ReportActionItemFragment.js | 4 ++-- src/pages/home/report/comment/AttachmentCommentFragment.js | 6 +++--- src/pages/home/report/comment/RenderCommentHTML.js | 2 +- src/pages/home/report/comment/TextCommentFragment.js | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/pages/home/report/ReportActionItemFragment.js b/src/pages/home/report/ReportActionItemFragment.js index 2efd325ff06a..c569be72658e 100644 --- a/src/pages/home/report/ReportActionItemFragment.js +++ b/src/pages/home/report/ReportActionItemFragment.js @@ -8,10 +8,10 @@ import UserDetailsTooltip from '@components/UserDetailsTooltip'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; import compose from '@libs/compose'; +import convertToLTR from '@libs/convertToLTR'; +import * as ReportUtils from '@libs/ReportUtils'; import styles from '@styles/styles'; import CONST from '@src/CONST'; -import * as ReportUtils from '@libs/ReportUtils'; -import convertToLTR from '@libs/convertToLTR'; import AttachmentCommentFragment from './comment/AttachmentCommentFragment'; import TextCommentFragment from './comment/TextCommentFragment'; import reportActionFragmentPropTypes from './reportActionFragmentPropTypes'; diff --git a/src/pages/home/report/comment/AttachmentCommentFragment.js b/src/pages/home/report/comment/AttachmentCommentFragment.js index db610dcb7394..8c04ca404f1d 100644 --- a/src/pages/home/report/comment/AttachmentCommentFragment.js +++ b/src/pages/home/report/comment/AttachmentCommentFragment.js @@ -1,9 +1,9 @@ import PropTypes from 'prop-types'; import React from 'react'; +import {View} from 'react-native'; +import reportActionSourcePropType from '@pages/home/report/reportActionSourcePropType'; import styles from '@styles/styles'; -import reportActionSourcePropType from "@pages/home/report/reportActionSourcePropType"; -import {View} from "react-native"; -import RenderCommentHTML from "./RenderCommentHTML"; +import RenderCommentHTML from './RenderCommentHTML'; const propTypes = { /** The reportAction's source */ diff --git a/src/pages/home/report/comment/RenderCommentHTML.js b/src/pages/home/report/comment/RenderCommentHTML.js index 474d87533480..dc8da2208dd5 100644 --- a/src/pages/home/report/comment/RenderCommentHTML.js +++ b/src/pages/home/report/comment/RenderCommentHTML.js @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import React from 'react'; -import reportActionSourcePropType from '@pages/home/report/reportActionSourcePropType'; import RenderHTML from '@components/RenderHTML'; +import reportActionSourcePropType from '@pages/home/report/reportActionSourcePropType'; const propTypes = { /** The reportAction's source */ diff --git a/src/pages/home/report/comment/TextCommentFragment.js b/src/pages/home/report/comment/TextCommentFragment.js index 155736e34767..b7c1ce904595 100644 --- a/src/pages/home/report/comment/TextCommentFragment.js +++ b/src/pages/home/report/comment/TextCommentFragment.js @@ -9,14 +9,14 @@ import compose from '@libs/compose'; import convertToLTR from '@libs/convertToLTR'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import * as EmojiUtils from '@libs/EmojiUtils'; +import reportActionFragmentPropTypes from '@pages/home/report/reportActionFragmentPropTypes'; +import reportActionSourcePropType from '@pages/home/report/reportActionSourcePropType'; import editedLabelStyles from '@styles/editedLabelStyles'; import styles from '@styles/styles'; import themeColors from '@styles/themes/default'; import variables from '@styles/variables'; import CONST from '@src/CONST'; -import reportActionFragmentPropTypes from '@pages/home/report/reportActionFragmentPropTypes'; -import reportActionSourcePropType from "@pages/home/report/reportActionSourcePropType"; -import RenderCommentHTML from "./RenderCommentHTML"; +import RenderCommentHTML from './RenderCommentHTML'; const propTypes = { /** The reportAction's source */ From eb58d70d9d7fdd51f5a45008d4e7542528093232 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Tue, 7 Nov 2023 16:19:59 +0100 Subject: [PATCH 58/97] remove redundant style --- src/styles/styles.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/styles/styles.ts b/src/styles/styles.ts index e591eb4176a2..46a7bde0dd7b 100644 --- a/src/styles/styles.ts +++ b/src/styles/styles.ts @@ -327,10 +327,6 @@ const styles = (theme: ThemeColors) => textAlign: 'left', }, - textUnderline: { - textDecorationLine: 'underline', - }, - verticalAlignMiddle: { verticalAlign: 'middle', }, From 4ee167df9e3cecece730c6bf89b8145126982ef8 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Wed, 8 Nov 2023 09:25:37 +0100 Subject: [PATCH 59/97] bump react-native-collapsible version --- package-lock.json | 11 +++++++---- package.json | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index b9eb50615924..5c7e14208387 100644 --- a/package-lock.json +++ b/package-lock.json @@ -78,7 +78,7 @@ "react-native": "0.72.4", "react-native-android-location-enabler": "^1.2.2", "react-native-blob-util": "^0.17.3", - "react-native-collapsible": "^1.6.0", + "react-native-collapsible": "^1.6.1", "react-native-config": "^1.4.5", "react-native-dev-menu": "^4.1.1", "react-native-device-info": "^10.3.0", @@ -44560,8 +44560,9 @@ } }, "node_modules/react-native-collapsible": { - "version": "1.6.0", - "license": "MIT", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/react-native-collapsible/-/react-native-collapsible-1.6.1.tgz", + "integrity": "sha512-orF4BeiXd2hZW7fu9YcqIJXzN6TJcFcddY807D3MAOVktLuW9oQ+RIkrTJ5DR3v9ZOFfREkOjEmS79qeUTvkBQ==", "peerDependencies": { "react": "*", "react-native": "*" @@ -85276,7 +85277,9 @@ "dev": true }, "react-native-collapsible": { - "version": "1.6.0", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/react-native-collapsible/-/react-native-collapsible-1.6.1.tgz", + "integrity": "sha512-orF4BeiXd2hZW7fu9YcqIJXzN6TJcFcddY807D3MAOVktLuW9oQ+RIkrTJ5DR3v9ZOFfREkOjEmS79qeUTvkBQ==", "requires": {} }, "react-native-config": { diff --git a/package.json b/package.json index f10ec0d81ee9..0b05e0705537 100644 --- a/package.json +++ b/package.json @@ -127,7 +127,7 @@ "react-native": "0.72.4", "react-native-android-location-enabler": "^1.2.2", "react-native-blob-util": "^0.17.3", - "react-native-collapsible": "^1.6.0", + "react-native-collapsible": "^1.6.1", "react-native-config": "^1.4.5", "react-native-dev-menu": "^4.1.1", "react-native-device-info": "^10.3.0", From 6740d923d323f7885ae09971eccdec9fd9e1297e Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Wed, 8 Nov 2023 09:26:41 +0100 Subject: [PATCH 60/97] migrate Collapsible to TypeScript --- .../Collapsible/index.native.js | 24 ------------------- .../Collapsible/index.native.tsx | 15 ++++++++++++ .../Collapsible/{index.js => index.tsx} | 0 3 files changed, 15 insertions(+), 24 deletions(-) delete mode 100644 src/components/CollapsibleSection/Collapsible/index.native.js create mode 100644 src/components/CollapsibleSection/Collapsible/index.native.tsx rename src/components/CollapsibleSection/Collapsible/{index.js => index.tsx} (100%) diff --git a/src/components/CollapsibleSection/Collapsible/index.native.js b/src/components/CollapsibleSection/Collapsible/index.native.js deleted file mode 100644 index 9b800304beeb..000000000000 --- a/src/components/CollapsibleSection/Collapsible/index.native.js +++ /dev/null @@ -1,24 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import CollapsibleRN from 'react-native-collapsible'; - -const propTypes = { - /** Whether the section should start expanded. False by default */ - isOpened: PropTypes.bool, - - /** Children to display inside the Collapsible component */ - children: PropTypes.node.isRequired, -}; - -const defaultProps = { - isOpened: false, -}; - -function Collapsible(props) { - return {props.children}; -} - -Collapsible.displayName = 'Collapsible'; -Collapsible.propTypes = propTypes; -Collapsible.defaultProps = defaultProps; -export default Collapsible; diff --git a/src/components/CollapsibleSection/Collapsible/index.native.tsx b/src/components/CollapsibleSection/Collapsible/index.native.tsx new file mode 100644 index 000000000000..48f118c7a189 --- /dev/null +++ b/src/components/CollapsibleSection/Collapsible/index.native.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import CollapsibleRN from 'react-native-collapsible'; +import ChildrenProps from '@src/types/utils/ChildrenProps'; + +type CollapsibleProps = ChildrenProps & { + /** Whether the section should start expanded. False by default */ + isOpened?: boolean; +}; + +function Collapsible({isOpened = false, children}: CollapsibleProps) { + return {children}; +} + +Collapsible.displayName = 'Collapsible'; +export default Collapsible; diff --git a/src/components/CollapsibleSection/Collapsible/index.js b/src/components/CollapsibleSection/Collapsible/index.tsx similarity index 100% rename from src/components/CollapsibleSection/Collapsible/index.js rename to src/components/CollapsibleSection/Collapsible/index.tsx From 0f9ecf598c70763153918a53f445b22471150a56 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Wed, 8 Nov 2023 09:26:56 +0100 Subject: [PATCH 61/97] start migrating CollapsibleSection to TypeScript --- src/components/CollapsibleSection/index.js | 71 --------------------- src/components/CollapsibleSection/index.tsx | 57 +++++++++++++++++ 2 files changed, 57 insertions(+), 71 deletions(-) delete mode 100644 src/components/CollapsibleSection/index.js create mode 100644 src/components/CollapsibleSection/index.tsx diff --git a/src/components/CollapsibleSection/index.js b/src/components/CollapsibleSection/index.js deleted file mode 100644 index 32d422297ca4..000000000000 --- a/src/components/CollapsibleSection/index.js +++ /dev/null @@ -1,71 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import {View} from 'react-native'; -import Icon from '@components/Icon'; -import * as Expensicons from '@components/Icon/Expensicons'; -import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; -import Text from '@components/Text'; -import styles from '@styles/styles'; -import CONST from '@src/CONST'; -import Collapsible from './Collapsible'; - -const propTypes = { - /** Title of the Collapsible section */ - title: PropTypes.string.isRequired, - - /** Children to display inside the Collapsible component */ - children: PropTypes.node.isRequired, -}; - -class CollapsibleSection extends React.Component { - constructor(props) { - super(props); - this.toggleSection = this.toggleSection.bind(this); - this.state = { - isExpanded: false, - }; - } - - /** - * Expands/collapses the section - */ - toggleSection() { - this.setState((prevState) => ({ - isExpanded: !prevState.isExpanded, - })); - } - - render() { - const src = this.state.isExpanded ? Expensicons.UpArrow : Expensicons.DownArrow; - - return ( - - - - {this.props.title} - - - - - - - {this.props.children} - - - ); - } -} - -CollapsibleSection.propTypes = propTypes; -export default CollapsibleSection; diff --git a/src/components/CollapsibleSection/index.tsx b/src/components/CollapsibleSection/index.tsx new file mode 100644 index 000000000000..1475b535cdaa --- /dev/null +++ b/src/components/CollapsibleSection/index.tsx @@ -0,0 +1,57 @@ +import React, {useState} from 'react'; +import {View} from 'react-native'; +import Icon from '@components/Icon'; +import * as Expensicons from '@components/Icon/Expensicons'; +import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; +import Text from '@components/Text'; +import styles from '@styles/styles'; +import CONST from '@src/CONST'; +import ChildrenProps from '@src/types/utils/ChildrenProps'; +import Collapsible from './Collapsible'; + +type CollapsibleSectionProps = ChildrenProps & { + /** Title of the Collapsible section */ + title: string; +}; + +function CollapsibleSection(props: CollapsibleSectionProps) { + const [isExpanded, setIsExpanded] = useState(false); + + /** + * Expands/collapses the section + */ + const toggleSection = () => { + setIsExpanded(!isExpanded); + }; + + const src = isExpanded ? Expensicons.UpArrow : Expensicons.DownArrow; + + return ( + + + + {props.title} + + + + + + + {props.children} + + + ); +} + +export default CollapsibleSection; From 280909e3327a95d775765024e4dfbf7eadee312b Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Wed, 8 Nov 2023 11:38:44 +0100 Subject: [PATCH 62/97] [TS migration] Migrate 'MultipleAvatars.js' component --- ...MultipleAvatars.js => MultipleAvatars.tsx} | 242 ++++++++++-------- src/components/Tooltip/index.js | 2 +- src/components/UserDetailsTooltip/index.js | 2 +- src/libs/UserUtils.ts | 3 + src/styles/StyleUtils.ts | 14 +- 5 files changed, 144 insertions(+), 119 deletions(-) rename src/components/{MultipleAvatars.js => MultipleAvatars.tsx} (57%) diff --git a/src/components/MultipleAvatars.js b/src/components/MultipleAvatars.tsx similarity index 57% rename from src/components/MultipleAvatars.js rename to src/components/MultipleAvatars.tsx index 209540189b69..00c591e3a580 100644 --- a/src/components/MultipleAvatars.js +++ b/src/components/MultipleAvatars.tsx @@ -1,77 +1,85 @@ -import PropTypes from 'prop-types'; import React, {memo, useMemo} from 'react'; -import {View} from 'react-native'; -import _ from 'underscore'; +import {StyleProp, View, ViewStyle} from 'react-native'; +import {ValueOf} from 'type-fest'; +import {AvatarSource} from '@libs/UserUtils'; import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; import themeColors from '@styles/themes/default'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import Avatar from './Avatar'; -import avatarPropTypes from './avatarPropTypes'; import Text from './Text'; import Tooltip from './Tooltip'; import UserDetailsTooltip from './UserDetailsTooltip'; -const propTypes = { +type AvatarType = { + /** Avatar source to display */ + source?: AvatarSource; + + /** Denotes whether it is an avatar or a workspace avatar */ + type?: typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; + + /** Owner of the avatar. If user, displayName. If workspace, policy name */ + name?: string; + + /** Avatar id */ + id?: number | string; + + /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */ + fallbackIcon?: AvatarSource; +}; + +type MultipleAvatarsProps = { /** Array of avatar URLs or icons */ - icons: PropTypes.arrayOf(avatarPropTypes), + icons: AvatarType[]; /** Set the size of avatars */ - size: PropTypes.oneOf(_.values(CONST.AVATAR_SIZE)), + size: ValueOf; /** Style for Second Avatar */ - // eslint-disable-next-line react/forbid-prop-types - secondAvatarStyle: PropTypes.arrayOf(PropTypes.object), + secondAvatarStyle: StyleProp; /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */ - fallbackIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), + fallbackIcon?: AvatarSource; /** Prop to identify if we should load avatars vertically instead of diagonally */ - shouldStackHorizontally: PropTypes.bool, + shouldStackHorizontally?: boolean; /** Prop to identify if we should display avatars in rows */ - shouldDisplayAvatarsInRows: PropTypes.bool, + shouldDisplayAvatarsInRows?: boolean; /** Whether the avatars are hovered */ - isHovered: PropTypes.bool, + isHovered?: boolean; /** Whether the avatars are in an element being pressed */ - isPressed: PropTypes.bool, + isPressed?: boolean; /** Whether #focus mode is on */ - isFocusMode: PropTypes.bool, + isFocusMode?: boolean; /** Whether avatars are displayed within a reportAction */ - isInReportAction: PropTypes.bool, + isInReportAction?: boolean; /** Whether to show the toolip text */ - shouldShowTooltip: PropTypes.bool, + shouldShowTooltip?: boolean; /** Whether avatars are displayed with the highlighted background color instead of the app background color. This is primarily the case for IOU previews. */ - shouldUseCardBackground: PropTypes.bool, + shouldUseCardBackground?: boolean; /** Prop to limit the amount of avatars displayed horizontally */ - maxAvatarsInRow: PropTypes.number, + maxAvatarsInRow?: number; }; -const defaultProps = { - icons: [], - size: CONST.AVATAR_SIZE.DEFAULT, - secondAvatarStyle: [StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG)], - fallbackIcon: undefined, - shouldStackHorizontally: false, - shouldDisplayAvatarsInRows: false, - isHovered: false, - isPressed: false, - isFocusMode: false, - isInReportAction: false, - shouldShowTooltip: true, - shouldUseCardBackground: false, - maxAvatarsInRow: CONST.AVATAR_ROW_SIZE.DEFAULT, +type AvatarStyles = { + singleAvatarStyle: ViewStyle; + secondAvatarStyles: ViewStyle; }; -const avatarSizeToStylesMap = { +type AvatarSizeToStyles = typeof CONST.AVATAR_SIZE.SMALL | typeof CONST.AVATAR_SIZE.LARGE | typeof CONST.AVATAR_SIZE.DEFAULT; + +type AvatarSizeToStylesMap = Record; + +const avatarSizeToStylesMap: AvatarSizeToStylesMap = { [CONST.AVATAR_SIZE.SMALL]: { singleAvatarStyle: styles.singleAvatarSmall, secondAvatarStyles: styles.secondAvatarSmall, @@ -80,78 +88,93 @@ const avatarSizeToStylesMap = { singleAvatarStyle: styles.singleAvatarMedium, secondAvatarStyles: styles.secondAvatarMedium, }, - default: { + [CONST.AVATAR_SIZE.DEFAULT]: { singleAvatarStyle: styles.singleAvatar, secondAvatarStyles: styles.secondAvatar, }, }; -function MultipleAvatars(props) { - let avatarContainerStyles = StyleUtils.getContainerStyles(props.size, props.isInReportAction); - const {singleAvatarStyle, secondAvatarStyles} = useMemo(() => avatarSizeToStylesMap[props.size] || avatarSizeToStylesMap.default, [props.size]); - const tooltipTexts = props.shouldShowTooltip ? _.pluck(props.icons, 'name') : ['']; +function MultipleAvatars({ + fallbackIcon, + icons = [], + size = CONST.AVATAR_SIZE.DEFAULT, + secondAvatarStyle = [StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG)], + shouldStackHorizontally = false, + shouldDisplayAvatarsInRows = false, + isHovered = false, + isPressed = false, + isFocusMode = false, + isInReportAction = false, + shouldShowTooltip = true, + shouldUseCardBackground = false, + maxAvatarsInRow = CONST.AVATAR_ROW_SIZE.DEFAULT, +}: MultipleAvatarsProps) { + let avatarContainerStyles = StyleUtils.getContainerStyles(size, isInReportAction); + const {singleAvatarStyle, secondAvatarStyles} = useMemo(() => avatarSizeToStylesMap[size as AvatarSizeToStyles] ?? avatarSizeToStylesMap.default, [size]); + + const tooltipTexts = shouldShowTooltip ? icons.map((icon) => icon.name) : ['']; const avatarSize = useMemo(() => { - if (props.isFocusMode) { + if (isFocusMode) { return CONST.AVATAR_SIZE.MID_SUBSCRIPT; } - if (props.size === CONST.AVATAR_SIZE.LARGE) { + if (size === CONST.AVATAR_SIZE.LARGE) { return CONST.AVATAR_SIZE.MEDIUM; } return CONST.AVATAR_SIZE.SMALLER; - }, [props.isFocusMode, props.size]); + }, [isFocusMode, size]); const avatarRows = useMemo(() => { // If we're not displaying avatars in rows or the number of icons is less than or equal to the max avatars in a row, return a single row - if (!props.shouldDisplayAvatarsInRows || props.icons.length <= props.maxAvatarsInRow) { - return [props.icons]; + if (!shouldDisplayAvatarsInRows || icons.length <= maxAvatarsInRow) { + return [icons]; } // Calculate the size of each row - const rowSize = Math.min(Math.ceil(props.icons.length / 2), props.maxAvatarsInRow); + const rowSize = Math.min(Math.ceil(icons.length / 2), maxAvatarsInRow); // Slice the icons array into two rows - const firstRow = props.icons.slice(rowSize); - const secondRow = props.icons.slice(0, rowSize); + const firstRow = icons.slice(rowSize); + const secondRow = icons.slice(0, rowSize); // Update the state with the two rows as an array return [firstRow, secondRow]; - }, [props.icons, props.maxAvatarsInRow, props.shouldDisplayAvatarsInRows]); + }, [icons, maxAvatarsInRow, shouldDisplayAvatarsInRows]); - if (!props.icons.length) { + if (!icons.length) { return null; } - if (props.icons.length === 1 && !props.shouldStackHorizontally) { + if (icons.length === 1 && !shouldStackHorizontally) { return ( ); } - const oneAvatarSize = StyleUtils.getAvatarStyle(props.size); - const oneAvatarBorderWidth = StyleUtils.getAvatarBorderWidth(props.size).borderWidth; + const oneAvatarSize = StyleUtils.getAvatarStyle(size); + const oneAvatarBorderWidth = StyleUtils.getAvatarBorderWidth(size).borderWidth ?? 0; const overlapSize = oneAvatarSize.width / 3; - if (props.shouldStackHorizontally) { + if (shouldStackHorizontally) { // Height of one avatar + border space const height = oneAvatarSize.height + 2 * oneAvatarBorderWidth; avatarContainerStyles = StyleUtils.combineStyles([styles.alignItemsCenter, styles.flexRow, StyleUtils.getHeight(height)]); @@ -159,36 +182,37 @@ function MultipleAvatars(props) { return ( <> - {props.shouldStackHorizontally ? ( - _.map(avatarRows, (avatars, rowIndex) => ( + {shouldStackHorizontally ? ( + avatarRows.map((avatars, rowIndex) => ( - {_.map([...avatars].splice(0, props.maxAvatarsInRow), (icon, index) => ( + {[...avatars].splice(0, maxAvatarsInRow).map((icon, index) => ( - + ))} - {avatars.length > props.maxAvatarsInRow && ( + {avatars.length > maxAvatarsInRow && ( {`+${avatars.length - props.maxAvatarsInRow}`} + >{`+${avatars.length - maxAvatarsInRow}`} @@ -238,53 +262,47 @@ function MultipleAvatars(props) { )) ) : ( - + {/* View is necessary for tooltip to show for multiple avatars in LHN */} - - {props.icons.length === 2 ? ( + + {icons.length === 2 ? ( @@ -292,10 +310,10 @@ function MultipleAvatars(props) { - {`+${props.icons.length - 1}`} + {`+${icons.length - 1}`} @@ -308,8 +326,6 @@ function MultipleAvatars(props) { ); } -MultipleAvatars.defaultProps = defaultProps; -MultipleAvatars.propTypes = propTypes; MultipleAvatars.displayName = 'MultipleAvatars'; export default memo(MultipleAvatars); diff --git a/src/components/Tooltip/index.js b/src/components/Tooltip/index.js index 19a607220e1c..c7c4428e19a3 100644 --- a/src/components/Tooltip/index.js +++ b/src/components/Tooltip/index.js @@ -15,7 +15,7 @@ const defaultProps = { shouldRender: true, }; -function Tooltip({shouldRender, children, ...props}) { +function Tooltip({shouldRender = true, children, ...props}) { if (!shouldRender) { return children; } diff --git a/src/components/UserDetailsTooltip/index.js b/src/components/UserDetailsTooltip/index.js index ea5cd4337071..6e256ac9ba9e 100644 --- a/src/components/UserDetailsTooltip/index.js +++ b/src/components/UserDetailsTooltip/index.js @@ -15,7 +15,7 @@ const defaultProps = { shouldRender: true, }; -function UserDetailsTooltip({shouldRender, children, ...props}) { +function UserDetailsTooltip({shouldRender = true, children, ...props}) { if (!shouldRender) { return children; } diff --git a/src/libs/UserUtils.ts b/src/libs/UserUtils.ts index f7883609f625..1a5ced6c9f85 100644 --- a/src/libs/UserUtils.ts +++ b/src/libs/UserUtils.ts @@ -8,6 +8,8 @@ import hashCode from './hashCode'; type AvatarRange = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24; +type AvatarSource = React.FC | string; + type LoginListIndicator = ValueOf | ''; /** @@ -202,3 +204,4 @@ export { getFullSizeAvatar, generateAccountID, }; +export type {AvatarSource}; diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index 42b7860ee263..b1116b372c2f 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -17,6 +17,12 @@ import variables from './variables'; type AllStyles = ViewStyle | TextStyle | ImageStyle; type ParsableStyle = StyleProp | ((state: PressableStateCallbackType) => StyleProp); +type AvatarStyle = { + width: number; + height: number; + borderRadius: number; + backgroundColor: string; +}; type ColorValue = ValueOf; type AvatarSizeName = ValueOf; @@ -210,7 +216,7 @@ function getAvatarWidthStyle(size: AvatarSizeName): ViewStyle { /** * Return the style from an avatar size constant */ -function getAvatarStyle(size: AvatarSizeName): ViewStyle { +function getAvatarStyle(size: AvatarSizeName): AvatarStyle { const avatarSize = getAvatarSize(size); return { height: avatarSize, @@ -241,7 +247,7 @@ function getAvatarBorderWidth(size: AvatarSizeName): ViewStyle { /** * Return the border radius for an avatar */ -function getAvatarBorderRadius(size: AvatarSizeName, type: string): ViewStyle { +function getAvatarBorderRadius(size: AvatarSizeName, type?: string): ViewStyle { if (type === CONST.ICON_TYPE_WORKSPACE) { return {borderRadius: avatarBorderSizes[size]}; } @@ -1286,8 +1292,8 @@ function getAmountFontSizeAndLineHeight(baseFontSize: number, baseLineHeight: nu /** * Returns container styles for showing the icons in MultipleAvatars/SubscriptAvatar */ -function getContainerStyles(size: string, isInReportAction = false): Array { - let containerStyles: Array; +function getContainerStyles(size: string, isInReportAction = false): ViewStyle[] { + let containerStyles: ViewStyle[]; switch (size) { case CONST.AVATAR_SIZE.SMALL: From ab92c85b94f09b58e565ac66b15528b53affbdaf Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Wed, 8 Nov 2023 12:26:04 +0100 Subject: [PATCH 63/97] Update OnyxCommon Icon type --- src/components/MultipleAvatars.tsx | 20 ++------------------ src/types/onyx/OnyxCommon.ts | 19 +++++++++++++++---- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/src/components/MultipleAvatars.tsx b/src/components/MultipleAvatars.tsx index 00c591e3a580..e3d871e0f888 100644 --- a/src/components/MultipleAvatars.tsx +++ b/src/components/MultipleAvatars.tsx @@ -7,31 +7,15 @@ import * as StyleUtils from '@styles/StyleUtils'; import themeColors from '@styles/themes/default'; import variables from '@styles/variables'; import CONST from '@src/CONST'; +import type {Icon} from '@src/types/onyx/OnyxCommon'; import Avatar from './Avatar'; import Text from './Text'; import Tooltip from './Tooltip'; import UserDetailsTooltip from './UserDetailsTooltip'; -type AvatarType = { - /** Avatar source to display */ - source?: AvatarSource; - - /** Denotes whether it is an avatar or a workspace avatar */ - type?: typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; - - /** Owner of the avatar. If user, displayName. If workspace, policy name */ - name?: string; - - /** Avatar id */ - id?: number | string; - - /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */ - fallbackIcon?: AvatarSource; -}; - type MultipleAvatarsProps = { /** Array of avatar URLs or icons */ - icons: AvatarType[]; + icons: Icon[]; /** Set the size of avatars */ size: ValueOf; diff --git a/src/types/onyx/OnyxCommon.ts b/src/types/onyx/OnyxCommon.ts index ef2944d6af82..4d376b8b7da8 100644 --- a/src/types/onyx/OnyxCommon.ts +++ b/src/types/onyx/OnyxCommon.ts @@ -1,5 +1,5 @@ -import * as React from 'react'; import {ValueOf} from 'type-fest'; +import {AvatarSource} from '@libs/UserUtils'; import CONST from '@src/CONST'; type PendingAction = ValueOf; @@ -11,9 +11,20 @@ type ErrorFields = Record; type Icon = { - source: React.ReactNode | string; - type: 'avatar' | 'workspace'; - name: string; + /** Avatar source to display */ + source?: AvatarSource; + + /** Denotes whether it is an avatar or a workspace avatar */ + type?: typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; + + /** Owner of the avatar. If user, displayName. If workspace, policy name */ + name?: string; + + /** Avatar id */ + id?: number | string; + + /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */ + fallbackIcon?: AvatarSource; }; export type {Icon, PendingAction, PendingFields, ErrorFields, Errors}; From cf6410f9128a085ef065fd3441cd352885bb799a Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Wed, 8 Nov 2023 12:39:35 +0100 Subject: [PATCH 64/97] Add useMemo for tooltipTexts --- src/components/MultipleAvatars.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MultipleAvatars.tsx b/src/components/MultipleAvatars.tsx index e3d871e0f888..a86d4946856e 100644 --- a/src/components/MultipleAvatars.tsx +++ b/src/components/MultipleAvatars.tsx @@ -96,7 +96,7 @@ function MultipleAvatars({ let avatarContainerStyles = StyleUtils.getContainerStyles(size, isInReportAction); const {singleAvatarStyle, secondAvatarStyles} = useMemo(() => avatarSizeToStylesMap[size as AvatarSizeToStyles] ?? avatarSizeToStylesMap.default, [size]); - const tooltipTexts = shouldShowTooltip ? icons.map((icon) => icon.name) : ['']; + const tooltipTexts = useMemo(() => (shouldShowTooltip ? icons.map((icon) => icon.name) : ['']), [shouldShowTooltip, icons]); const avatarSize = useMemo(() => { if (isFocusMode) { return CONST.AVATAR_SIZE.MID_SUBSCRIPT; From d04a7338598becd085b2e6f8398ca21888fbc82e Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Wed, 8 Nov 2023 13:24:09 +0100 Subject: [PATCH 65/97] fix type errors on CollapsibleSection --- src/components/CollapsibleSection/Collapsible/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/CollapsibleSection/Collapsible/index.tsx b/src/components/CollapsibleSection/Collapsible/index.tsx index 51d650ed5748..867f0bb1228b 100644 --- a/src/components/CollapsibleSection/Collapsible/index.tsx +++ b/src/components/CollapsibleSection/Collapsible/index.tsx @@ -1,3 +1,3 @@ -import Collapsible from 'react-collapse'; +import {Collapse} from 'react-collapse'; -export default Collapsible; +export default Collapse; From 5d90cc0127907cd27859f1aad899dc8baddf25c7 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Wed, 8 Nov 2023 13:45:48 +0100 Subject: [PATCH 66/97] create common types file for Collapsible component --- .../CollapsibleSection/Collapsible/index.native.tsx | 7 +------ src/components/CollapsibleSection/Collapsible/index.tsx | 7 ++++++- src/components/CollapsibleSection/Collapsible/types.ts | 8 ++++++++ 3 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 src/components/CollapsibleSection/Collapsible/types.ts diff --git a/src/components/CollapsibleSection/Collapsible/index.native.tsx b/src/components/CollapsibleSection/Collapsible/index.native.tsx index 48f118c7a189..e8d3dc9439d0 100644 --- a/src/components/CollapsibleSection/Collapsible/index.native.tsx +++ b/src/components/CollapsibleSection/Collapsible/index.native.tsx @@ -1,11 +1,6 @@ import React from 'react'; import CollapsibleRN from 'react-native-collapsible'; -import ChildrenProps from '@src/types/utils/ChildrenProps'; - -type CollapsibleProps = ChildrenProps & { - /** Whether the section should start expanded. False by default */ - isOpened?: boolean; -}; +import CollapsibleProps from './types'; function Collapsible({isOpened = false, children}: CollapsibleProps) { return {children}; diff --git a/src/components/CollapsibleSection/Collapsible/index.tsx b/src/components/CollapsibleSection/Collapsible/index.tsx index 867f0bb1228b..2585fd92f42b 100644 --- a/src/components/CollapsibleSection/Collapsible/index.tsx +++ b/src/components/CollapsibleSection/Collapsible/index.tsx @@ -1,3 +1,8 @@ +import React from 'react'; import {Collapse} from 'react-collapse'; +import CollapsibleProps from './types'; -export default Collapse; +function Collapsible({isOpened = false, children}: CollapsibleProps) { + return {children}; +} +export default Collapsible; diff --git a/src/components/CollapsibleSection/Collapsible/types.ts b/src/components/CollapsibleSection/Collapsible/types.ts new file mode 100644 index 000000000000..8b8e8aba6860 --- /dev/null +++ b/src/components/CollapsibleSection/Collapsible/types.ts @@ -0,0 +1,8 @@ +import ChildrenProps from '@src/types/utils/ChildrenProps'; + +type CollapsibleProps = ChildrenProps & { + /** Whether the section should start expanded. False by default */ + isOpened?: boolean; +}; + +export default CollapsibleProps; From 97fde1b8f93521f395595a201ad2e42a187b7e42 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Wed, 8 Nov 2023 15:00:47 +0100 Subject: [PATCH 67/97] remove unnecessary newline --- src/components/CollapsibleSection/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/CollapsibleSection/index.tsx b/src/components/CollapsibleSection/index.tsx index 9b985aea803d..3cb19a52866d 100644 --- a/src/components/CollapsibleSection/index.tsx +++ b/src/components/CollapsibleSection/index.tsx @@ -45,7 +45,6 @@ function CollapsibleSection(props: CollapsibleSectionProps) { - {props.children} From f4afdb42aae6cdd44a552454562c7be0e610836e Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Wed, 8 Nov 2023 15:53:16 +0100 Subject: [PATCH 68/97] remove redundant useNativeDriver prop --- src/components/Modal/index.android.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Modal/index.android.tsx b/src/components/Modal/index.android.tsx index 6c7f3173fd0a..3aaf2c342634 100644 --- a/src/components/Modal/index.android.tsx +++ b/src/components/Modal/index.android.tsx @@ -20,7 +20,6 @@ function Modal(props: BaseModalProps) { {props.children} From bc3181ae12a98fd29add7af9e51847c6cc18a08a Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Wed, 8 Nov 2023 18:06:05 +0100 Subject: [PATCH 69/97] Make size and secondAvatarStyle to be optional --- src/components/MultipleAvatars.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/MultipleAvatars.tsx b/src/components/MultipleAvatars.tsx index a86d4946856e..4101fd381a3f 100644 --- a/src/components/MultipleAvatars.tsx +++ b/src/components/MultipleAvatars.tsx @@ -18,10 +18,10 @@ type MultipleAvatarsProps = { icons: Icon[]; /** Set the size of avatars */ - size: ValueOf; + size?: ValueOf; /** Style for Second Avatar */ - secondAvatarStyle: StyleProp; + secondAvatarStyle?: StyleProp; /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */ fallbackIcon?: AvatarSource; From 22f237f122150cfa41698d19835c044e76460ea8 Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Wed, 8 Nov 2023 18:50:06 +0100 Subject: [PATCH 70/97] Remove unused avatar props --- src/components/MultipleAvatars.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/components/MultipleAvatars.tsx b/src/components/MultipleAvatars.tsx index 4101fd381a3f..e867de7ddb97 100644 --- a/src/components/MultipleAvatars.tsx +++ b/src/components/MultipleAvatars.tsx @@ -137,7 +137,6 @@ function MultipleAvatars({ icon={icons[0]} fallbackUserDetails={{ displayName: icons[0].name, - avatar: icons[0].source, }} > @@ -180,7 +179,6 @@ function MultipleAvatars({ icon={icon} fallbackUserDetails={{ displayName: icon.name, - avatar: icon.source, }} > @@ -252,7 +250,6 @@ function MultipleAvatars({ icon={icons[0]} fallbackUserDetails={{ displayName: icons[0].name, - avatar: icons[0].source, }} > {/* View is necessary for tooltip to show for multiple avatars in LHN */} @@ -275,7 +272,6 @@ function MultipleAvatars({ icon={icons[1]} fallbackUserDetails={{ displayName: icons[1].name, - avatar: icons[1].source, }} > From 8082b7c49407a2048faedf6cc432faa407adccab Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 9 Nov 2023 08:54:52 +0100 Subject: [PATCH 71/97] bring back default useNativeDriver prop for android --- src/components/Modal/index.android.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/Modal/index.android.tsx b/src/components/Modal/index.android.tsx index 3aaf2c342634..2343cb4c70a9 100644 --- a/src/components/Modal/index.android.tsx +++ b/src/components/Modal/index.android.tsx @@ -15,13 +15,14 @@ AppState.addEventListener('blur', () => { // Only want to use useNativeDriver on Android. It has strange flashes issue on IOS // https://github.com/react-native-modal/react-native-modal#the-modal-flashes-in-a-weird-way-when-animating -function Modal(props: BaseModalProps) { +function Modal({useNativeDriver = true, ...rest}: BaseModalProps) { return ( - {props.children} + {rest.children} ); } From a5e8a581860dd9e3a9d54f6cb95bc7371342f28d Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 9 Nov 2023 09:31:50 +0100 Subject: [PATCH 72/97] add optional chaining to onModalShow --- src/components/Modal/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx index 47da7256abf3..3d8a30ba3e2a 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -35,7 +35,7 @@ function Modal(props: BaseModalProps) { setStatusBarColor(isFullScreenModal ? themeColors.appBG : StyleUtils.getThemeBackgroundColor(statusBarColor)); } - props.onModalShow(); + props.onModalShow?.(); }; return ( From e2d0ba1e5fee0c0f4e7024149dfc3393eb506e22 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 9 Nov 2023 11:39:48 +0100 Subject: [PATCH 73/97] destructure props in CollapsibleSection --- src/components/CollapsibleSection/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/CollapsibleSection/index.tsx b/src/components/CollapsibleSection/index.tsx index 3cb19a52866d..434017f2a547 100644 --- a/src/components/CollapsibleSection/index.tsx +++ b/src/components/CollapsibleSection/index.tsx @@ -14,7 +14,7 @@ type CollapsibleSectionProps = ChildrenProps & { title: string; }; -function CollapsibleSection(props: CollapsibleSectionProps) { +function CollapsibleSection({title, children}: CollapsibleSectionProps) { const [isExpanded, setIsExpanded] = useState(false); /** @@ -32,7 +32,7 @@ function CollapsibleSection(props: CollapsibleSectionProps) { onPress={toggleSection} style={[styles.pb4, styles.flexRow]} role={CONST.ACCESSIBILITY_ROLE.BUTTON} - accessibilityLabel={props.title} + accessibilityLabel={title} hoverDimmingValue={1} pressDimmingValue={0.2} > @@ -40,13 +40,13 @@ function CollapsibleSection(props: CollapsibleSectionProps) { style={[styles.flex1, styles.textStrong, styles.userSelectNone]} dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}} > - {props.title} + {title} - {props.children} + {children} ); From c1ba54297a86b994c3fb6d117a3c7007aa82cdec Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Thu, 9 Nov 2023 12:03:36 +0100 Subject: [PATCH 74/97] Apply to the PR comments --- .../home/report/ReportActionItemFragment.js | 5 +---- .../comment/AttachmentCommentFragment.js | 18 ++++++++---------- .../home/report/comment/RenderCommentHTML.js | 6 +++--- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/pages/home/report/ReportActionItemFragment.js b/src/pages/home/report/ReportActionItemFragment.js index c569be72658e..fe2308afab27 100644 --- a/src/pages/home/report/ReportActionItemFragment.js +++ b/src/pages/home/report/ReportActionItemFragment.js @@ -107,10 +107,7 @@ function ReportActionItemFragment(props) { return ${props.translate('parentReportAction.deletedMessage')}`} />; } - // Does the fragment content represent an attachment? - const isFragmentAttachment = ReportUtils.isReportMessageAttachment(fragment); - - if (isFragmentAttachment) { + if (ReportUtils.isReportMessageAttachment(fragment)) { return ( - - - ); -} +const AttachmentCommentFragment = ({addExtraMargin, html, source}) => ( + + + +); AttachmentCommentFragment.propTypes = propTypes; AttachmentCommentFragment.displayName = 'AttachmentCommentFragment'; diff --git a/src/pages/home/report/comment/RenderCommentHTML.js b/src/pages/home/report/comment/RenderCommentHTML.js index dc8da2208dd5..cd2f464510e9 100644 --- a/src/pages/home/report/comment/RenderCommentHTML.js +++ b/src/pages/home/report/comment/RenderCommentHTML.js @@ -11,10 +11,10 @@ const propTypes = { html: PropTypes.string.isRequired, }; -function RenderCommentHTML(props) { - const html = props.html; +function RenderCommentHTML({html, source}) { + const commentHtml = source === 'email' ? `${html}` : `${html}`; - return ${html}` : `${html}`} />; + return ; } RenderCommentHTML.propTypes = propTypes; From c7187331f3b0cb3d1ada676d0bdcd2720acc3068 Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Thu, 9 Nov 2023 12:07:21 +0100 Subject: [PATCH 75/97] Run Prettier --- src/pages/home/report/comment/RenderCommentHTML.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/comment/RenderCommentHTML.js b/src/pages/home/report/comment/RenderCommentHTML.js index cd2f464510e9..14039af21189 100644 --- a/src/pages/home/report/comment/RenderCommentHTML.js +++ b/src/pages/home/report/comment/RenderCommentHTML.js @@ -14,7 +14,7 @@ const propTypes = { function RenderCommentHTML({html, source}) { const commentHtml = source === 'email' ? `${html}` : `${html}`; - return ; + return ; } RenderCommentHTML.propTypes = propTypes; From 678807569b0623efe5a3b9253b56fee96563f0d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Thu, 9 Nov 2023 16:58:07 +0100 Subject: [PATCH 76/97] useTabNavigationFocus hook refactoring --- src/hooks/useTabNavigatorFocus/index.js | 57 +++++++++++++++++ .../useTabNavigatorFocus/index.native.js | 52 +++++++++++++++ .../ReceiptSelector/NavigationAwareCamera.js | 64 +++---------------- .../NavigationAwareCamera.native.js | 60 ++--------------- src/pages/iou/ReceiptSelector/index.js | 8 +-- src/pages/iou/ReceiptSelector/index.native.js | 7 +- 6 files changed, 123 insertions(+), 125 deletions(-) create mode 100644 src/hooks/useTabNavigatorFocus/index.js create mode 100644 src/hooks/useTabNavigatorFocus/index.native.js diff --git a/src/hooks/useTabNavigatorFocus/index.js b/src/hooks/useTabNavigatorFocus/index.js new file mode 100644 index 000000000000..2a41809b7414 --- /dev/null +++ b/src/hooks/useTabNavigatorFocus/index.js @@ -0,0 +1,57 @@ +import {useTabAnimation} from '@react-navigation/material-top-tabs'; +import {useIsFocused} from '@react-navigation/native'; +import {useEffect, useState} from 'react'; + +function useTabNavigatorFocus({tabIndex}) { + let tabPositionAnimation = null; + try { + // Retrieve the animation value from the tab navigator, which ranges from 0 to the total number of pages displayed. + // Even a minimal scroll towards the camera page (e.g., a value of 0.001 at start) should activate the camera for immediate responsiveness. + // STOP!!!!!!! This is not a pattern to be followed! We are conditionally rendering this hook becase when used in the edit flow we'll never be inside a tab navigator. + // eslint-disable-next-line react-hooks/rules-of-hooks + tabPositionAnimation = useTabAnimation(); + } catch (error) { + tabPositionAnimation = null; + } + const isPageFocused = useIsFocused(); + // set to true if the hook is not used within the MaterialTopTabs context + // the hook will then return true if the screen is focused + const [isTabFocused, setIsTabFocused] = useState(!tabPositionAnimation); + + useEffect(() => { + if (!tabPositionAnimation) { + return; + } + const index = Number(tabIndex); + + const listenerId = tabPositionAnimation.addListener(({value}) => { + // Activate camera as soon the index is animating towards the `tabIndex` + requestAnimationFrame(() => { + setIsTabFocused(value > index - 1 && value < index + 1); + }); + }); + + // We need to get the position animation value on component initialization to determine + // if the tab is focused or not. Since it's an Animated.Value the only synchronous way + // to retrieve the value is to use a private method. + // eslint-disable-next-line no-underscore-dangle + const initialTabPositionValue = tabPositionAnimation.__getValue(); + + if (typeof initialTabPositionValue === 'number') { + requestAnimationFrame(() => { + setIsTabFocused(initialTabPositionValue > index - 1 && initialTabPositionValue < index + 1); + }); + } + + return () => { + if (!tabPositionAnimation) { + return; + } + tabPositionAnimation.removeListener(listenerId); + }; + }, [tabIndex, tabPositionAnimation]); + + return isTabFocused && isPageFocused; +} + +export default useTabNavigatorFocus; diff --git a/src/hooks/useTabNavigatorFocus/index.native.js b/src/hooks/useTabNavigatorFocus/index.native.js new file mode 100644 index 000000000000..a313e95ee0dd --- /dev/null +++ b/src/hooks/useTabNavigatorFocus/index.native.js @@ -0,0 +1,52 @@ +import {useTabAnimation} from '@react-navigation/material-top-tabs'; +import {useIsFocused} from '@react-navigation/native'; +import {useEffect, useState} from 'react'; +import CONST from '@src/CONST'; + +function useTabNavigatorFocus({tabIndex, selectedTab}) { + let tabPositionAnimation = null; + try { + // Retrieve the animation value from the tab navigator, which ranges from 0 to the total number of pages displayed. + // Even a minimal scroll towards the camera page (e.g., a value of 0.001 at start) should activate the camera for immediate responsiveness. + // STOP!!!!!!! This is not a pattern to be followed! We are conditionally rendering this hook becase when used in the edit flow we'll never be inside a tab navigator. + // eslint-disable-next-line react-hooks/rules-of-hooks + tabPositionAnimation = useTabAnimation(); + } catch (error) { + tabPositionAnimation = null; + } + const isPageFocused = useIsFocused(); + // set to true if the hook is not used within the MaterialTopTabs context + // the hook will then return true if the screen is focused + const [isTabFocused, setIsTabFocused] = useState(!tabPositionAnimation); + + // Retrieve the animation value from the tab navigator, which ranges from 0 to the total number of pages displayed. + // Even a minimal scroll towards the camera page (e.g., a value of 0.001 at start) should activate the camera for immediate responsiveness. + + // STOP!!!!!!! This is not a pattern to be followed! We are conditionally rendering this hook becase when used in the edit flow we'll never be inside a tab navigator. + // eslint-disable-next-line react-hooks/rules-of-hooks + + useEffect(() => { + if (!tabPositionAnimation) { + return; + } + + const listenerId = tabPositionAnimation.addListener(({value}) => { + if (selectedTab !== CONST.TAB.SCAN) { + return; + } + // Activate camera as soon the index is animating towards the `cameraTabIndex` + setIsTabFocused(value > tabIndex - 1 && value < tabIndex + 1); + }); + + return () => { + if (!tabPositionAnimation) { + return; + } + tabPositionAnimation.removeListener(listenerId); + }; + }, [tabIndex, tabPositionAnimation, selectedTab]); + + return isTabFocused && isPageFocused; +} + +export default useTabNavigatorFocus; diff --git a/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js index 783ae36f0e07..10b16da13b6e 100644 --- a/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js +++ b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js @@ -1,24 +1,20 @@ -import {useTabAnimation} from '@react-navigation/material-top-tabs'; -import {useIsFocused} from '@react-navigation/native'; import PropTypes from 'prop-types'; -import React, {useEffect, useRef, useState} from 'react'; +import React, {useEffect, useRef} from 'react'; import {View} from 'react-native'; import Webcam from 'react-webcam'; +import useTabNavigatorFocus from '@hooks/useTabNavigatorFocus'; const propTypes = { - /* Flag to turn on/off the torch/flashlight - if available */ + /** Flag to turn on/off the torch/flashlight - if available */ torchOn: PropTypes.bool, - /* The index of the tab that contains this camera */ + /** The index of the tab that contains this camera */ cameraTabIndex: PropTypes.number.isRequired, - /* Whether we're in a tab navigator */ - isInTabNavigator: PropTypes.bool.isRequired, - - /* Callback function when media stream becomes available - user granted camera permissions and camera starts to work */ + /** Callback function when media stream becomes available - user granted camera permissions and camera starts to work */ onUserMedia: PropTypes.func, - /* Callback function passing torch/flashlight capability as bool param of the browser */ + /** Callback function passing torch/flashlight capability as bool param of the browser */ onTorchAvailability: PropTypes.func, }; @@ -28,55 +24,11 @@ const defaultProps = { torchOn: false, }; -function useTabNavigatorFocus({cameraTabIndex, isInTabNavigator}) { - // Get navigation to get initial isFocused value (only needed once during init!) - const isPageFocused = useIsFocused(); - const [isTabFocused, setIsTabFocused] = useState(false); - - // Retrieve the animation value from the tab navigator, which ranges from 0 to the total number of pages displayed. - // Even a minimal scroll towards the camera page (e.g., a value of 0.001 at start) should activate the camera for immediate responsiveness. - - // STOP!!!!!!! This is not a pattern to be followed! We are conditionally rendering this hook becase when used in the edit flow we'll never be inside a tab navigator. - // eslint-disable-next-line react-hooks/rules-of-hooks - const tabPositionAnimation = isInTabNavigator ? useTabAnimation() : null; - - useEffect(() => { - if (!tabPositionAnimation) { - return; - } - const index = Number(cameraTabIndex); - - const listenerId = tabPositionAnimation.addListener(({value}) => { - // Activate camera as soon the index is animating towards the `cameraTabIndex` - requestAnimationFrame(() => { - setIsTabFocused(value > index - 1 && value < index + 1); - }); - }); - - // We need to get the position animation value on component initialization to determine - // if the tab is focused or not. Since it's an Animated.Value the only synchronous way - // to retrieve the value is to use a private method. - // eslint-disable-next-line no-underscore-dangle - const initialTabPositionValue = tabPositionAnimation.__getValue(); - - requestAnimationFrame(() => { - setIsTabFocused(initialTabPositionValue > index - 1 && initialTabPositionValue < index + 1); - }); - - return () => { - tabPositionAnimation.removeListener(listenerId); - }; - }, [cameraTabIndex, tabPositionAnimation, isInTabNavigator]); - - return isTabFocused && isPageFocused; -} - // Wraps a camera that will only be active when the tab is focused or as soon as it starts to become focused. -const NavigationAwareCamera = React.forwardRef(({torchOn, onTorchAvailability, cameraTabIndex, isInTabNavigator, ...props}, ref) => { +const NavigationAwareCamera = React.forwardRef(({torchOn, onTorchAvailability, cameraTabIndex, ...props}, ref) => { const trackRef = useRef(null); const shouldShowCamera = useTabNavigatorFocus({ - cameraTabIndex, - isInTabNavigator, + tabIndex: cameraTabIndex, }); const handleOnUserMedia = (stream) => { diff --git a/src/pages/iou/ReceiptSelector/NavigationAwareCamera.native.js b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.native.js index 9d1b1723e882..78d2abe1a3b3 100644 --- a/src/pages/iou/ReceiptSelector/NavigationAwareCamera.native.js +++ b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.native.js @@ -1,71 +1,19 @@ -import {useTabAnimation} from '@react-navigation/material-top-tabs'; -import {useNavigation} from '@react-navigation/native'; import PropTypes from 'prop-types'; -import React, {useEffect, useState} from 'react'; +import React from 'react'; import {Camera} from 'react-native-vision-camera'; -import CONST from '@src/CONST'; +import useTabNavigatorFocus from '@hooks/useTabNavigatorFocus'; const propTypes = { /* The index of the tab that contains this camera */ cameraTabIndex: PropTypes.number.isRequired, - /* Whether we're in a tab navigator */ - isInTabNavigator: PropTypes.bool.isRequired, - /** Name of the selected receipt tab */ selectedTab: PropTypes.string.isRequired, }; // Wraps a camera that will only be active when the tab is focused or as soon as it starts to become focused. -const NavigationAwareCamera = React.forwardRef(({cameraTabIndex, isInTabNavigator, selectedTab, ...props}, ref) => { - // Get navigation to get initial isFocused value (only needed once during init!) - const navigation = useNavigation(); - const [isCameraActive, setIsCameraActive] = useState(() => navigation.isFocused()); - - // Retrieve the animation value from the tab navigator, which ranges from 0 to the total number of pages displayed. - // Even a minimal scroll towards the camera page (e.g., a value of 0.001 at start) should activate the camera for immediate responsiveness. - - // STOP!!!!!!! This is not a pattern to be followed! We are conditionally rendering this hook becase when used in the edit flow we'll never be inside a tab navigator. - // eslint-disable-next-line react-hooks/rules-of-hooks - const tabPositionAnimation = isInTabNavigator ? useTabAnimation() : null; - - useEffect(() => { - if (!isInTabNavigator) { - return; - } - - const listenerId = tabPositionAnimation.addListener(({value}) => { - if (selectedTab !== CONST.TAB.SCAN) { - return; - } - // Activate camera as soon the index is animating towards the `cameraTabIndex` - setIsCameraActive(value > cameraTabIndex - 1 && value < cameraTabIndex + 1); - }); - - return () => { - tabPositionAnimation.removeListener(listenerId); - }; - }, [cameraTabIndex, tabPositionAnimation, isInTabNavigator, selectedTab]); - - // Note: The useEffect can be removed once VisionCamera V3 is used. - // Its only needed for android, because there is a native cameraX android bug. With out this flow would break the camera: - // 1. Open camera tab - // 2. Take a picture - // 3. Go back from the opened screen - // 4. The camera is not working anymore - useEffect(() => { - const removeBlurListener = navigation.addListener('blur', () => { - setIsCameraActive(false); - }); - const removeFocusListener = navigation.addListener('focus', () => { - setIsCameraActive(true); - }); - - return () => { - removeBlurListener(); - removeFocusListener(); - }; - }, [navigation]); +const NavigationAwareCamera = React.forwardRef(({cameraTabIndex, selectedTab, ...props}, ref) => { + const isCameraActive = useTabNavigatorFocus({tabIndex: cameraTabIndex, selectedTab}); return ( diff --git a/src/pages/iou/ReceiptSelector/index.native.js b/src/pages/iou/ReceiptSelector/index.native.js index d47a2c7739a2..69f1a894d3b6 100644 --- a/src/pages/iou/ReceiptSelector/index.native.js +++ b/src/pages/iou/ReceiptSelector/index.native.js @@ -51,9 +51,6 @@ const propTypes = { /** The id of the transaction we're editing */ transactionID: PropTypes.string, - /** Whether or not the receipt selector is in a tab navigator for tab animations */ - isInTabNavigator: PropTypes.bool, - /** Name of the selected receipt tab */ selectedTab: PropTypes.string, }; @@ -62,11 +59,10 @@ const defaultProps = { report: {}, iou: iouDefaultProps, transactionID: '', - isInTabNavigator: true, selectedTab: '', }; -function ReceiptSelector({route, report, iou, transactionID, isInTabNavigator, selectedTab}) { +function ReceiptSelector({route, report, iou, transactionID, selectedTab}) { const devices = useCameraDevices('wide-angle-camera'); const device = devices.back; @@ -198,7 +194,6 @@ function ReceiptSelector({route, report, iou, transactionID, isInTabNavigator, s zoom={device.neutralZoom} photo cameraTabIndex={pageIndex} - isInTabNavigator={isInTabNavigator} selectedTab={selectedTab} /> )} From 6f7692817e60fc32083d0dad903ed87ffeb6216e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Thu, 9 Nov 2023 18:19:43 +0100 Subject: [PATCH 77/97] added JSDocs for useTabNavigatorFocus hook --- src/hooks/useTabNavigatorFocus/index.js | 24 +++++++++++++++++++ .../useTabNavigatorFocus/index.native.js | 23 ++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/hooks/useTabNavigatorFocus/index.js b/src/hooks/useTabNavigatorFocus/index.js index 2a41809b7414..7af6bc5950ac 100644 --- a/src/hooks/useTabNavigatorFocus/index.js +++ b/src/hooks/useTabNavigatorFocus/index.js @@ -2,6 +2,30 @@ import {useTabAnimation} from '@react-navigation/material-top-tabs'; import {useIsFocused} from '@react-navigation/native'; import {useEffect, useState} from 'react'; +/** + * Custom React hook to determine the focus status of a tab in a Material Top Tab Navigator. + * It evaluates whether the current tab is focused based on the tab's animation position and + * the screen's focus status within a React Navigation environment. + * + * This hook is designed for use with the Material Top Tabs provided by '@react-navigation/material-top-tabs'. + * It leverages the `useTabAnimation` hook from the same package to track the animated position of tabs + * and the `useIsFocused` hook from '@react-navigation/native' to ascertain if the current screen is in focus. + * + * Note: This hook contains a conditional invocation of another hook (`useTabAnimation`), + * which is typically an anti-pattern in React. This is done to account for scenarios where the hook + * might not be used within a Material Top Tabs Navigator context. Proper usage should ensure that + * this hook is only used where appropriate. + * + * Note: This hook is almost identical to native implementation, except for the `selectedTab` parameter + * and the fact that it uses requestAnimationFrame to mitigate issues when updating the isTabFocused state. + * + * @param {Object} params - The parameters object. + * @param {number} params.tabIndex - The index of the tab for which focus status is being determined. + * @returns {boolean} Returns `true` if the tab is both animation-focused and screen-focused, otherwise `false`. + * + * @example + * const isTabFocused = useTabNavigatorFocus({ tabIndex: 1 }); + */ function useTabNavigatorFocus({tabIndex}) { let tabPositionAnimation = null; try { diff --git a/src/hooks/useTabNavigatorFocus/index.native.js b/src/hooks/useTabNavigatorFocus/index.native.js index a313e95ee0dd..7f9da7b3f841 100644 --- a/src/hooks/useTabNavigatorFocus/index.native.js +++ b/src/hooks/useTabNavigatorFocus/index.native.js @@ -3,6 +3,29 @@ import {useIsFocused} from '@react-navigation/native'; import {useEffect, useState} from 'react'; import CONST from '@src/CONST'; +/** + * Custom React hook to determine the focus status of a specific tab in a Material Top Tab Navigator, with additional + * conditions based on a selected tab state. It evaluates whether the specified tab is focused by combining the tab's + * animation position and the screen's focus status within a React Navigation environment. + * + * The hook is primarily intended for use with Material Top Tabs provided by '@react-navigation/material-top-tabs'. + * It utilizes the `useTabAnimation` hook from this package to track the animated position of the tabs and the + * `useIsFocused` hook from '@react-navigation/native' to determine if the current screen is focused. Additionally, + * it uses a `selectedTab` parameter to apply custom logic based on the currently selected tab. + * + * Note: This hook employs a conditional invocation of the `useTabAnimation` hook, which is generally against React's + * rules of hooks. This pattern is used here to handle scenarios where the hook might not be employed within a + * Material Top Tabs Navigator context. Ensure this hook is only used in appropriate scenarios to avoid potential issues. + * + * @param {Object} params - The parameters object. + * @param {number} params.tabIndex - The index of the tab for which focus status is being determined. + * @param {string} params.selectedTab - The tab identifier passed by to the component. Used only on native platform + * + * @returns {boolean} Returns `true` if the specified tab is both animation-focused and screen-focused, otherwise `false`. + * + * @example + * const isTabFocused = useTabNavigatorFocus({ tabIndex: 1, selectedTab: 'home' }); + */ function useTabNavigatorFocus({tabIndex, selectedTab}) { let tabPositionAnimation = null; try { From 2a52cfad42b4ec4b371f609d396f7199239e1d41 Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Fri, 10 Nov 2023 10:23:34 +0100 Subject: [PATCH 78/97] `ReportActionItemFragment`: Add a default for `displayAsGroup` --- src/pages/home/report/ReportActionItemFragment.js | 3 ++- src/pages/home/report/ReportActionItemSingle.js | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ReportActionItemFragment.js b/src/pages/home/report/ReportActionItemFragment.js index fe2308afab27..bdef759d0ed6 100644 --- a/src/pages/home/report/ReportActionItemFragment.js +++ b/src/pages/home/report/ReportActionItemFragment.js @@ -60,7 +60,7 @@ const propTypes = { isThreadParentMessage: PropTypes.bool, /** Should the comment have the appearance of being grouped with the previous comment? */ - displayAsGroup: PropTypes.bool.isRequired, + displayAsGroup: PropTypes.bool, /** Whether the report action type is 'APPROVED' or 'SUBMITTED'. Used to style system messages from Old Dot */ isApprovedOrSubmittedReportAction: PropTypes.bool, @@ -90,6 +90,7 @@ const defaultProps = { isThreadParentMessage: false, isApprovedOrSubmittedReportAction: false, isFragmentContainingDisplayName: false, + displayAsGroup: false, }; function ReportActionItemFragment(props) { diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index 2e1ba0664279..955e024bd7a8 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -243,7 +243,6 @@ function ReportActionItemSingle(props) { delegateAccountID={props.action.delegateAccountID} isSingleLine actorIcon={icon} - displayAsGroup={false} /> ))} From b67775ba779c039ef2efd93e45876874d4bfd968 Mon Sep 17 00:00:00 2001 From: Dylan Date: Fri, 10 Nov 2023 17:28:47 +0700 Subject: [PATCH 79/97] re-order field in address page --- src/components/AddressSearch/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js index 3e122e029969..f386c7cad96d 100644 --- a/src/components/AddressSearch/index.js +++ b/src/components/AddressSearch/index.js @@ -229,6 +229,7 @@ function AddressSearch(props) { street2: subpremise, // Make sure country is updated first, since city and state will be reset if the country changes country: '', + state: state || stateAutoCompleteFallback, // When locality is not returned, many countries return the city as postalTown (e.g. 5 New Street // Square, London), otherwise as sublocality (e.g. 384 Court Street Brooklyn). If postalTown is // returned, the sublocality will be a city subdivision so shouldn't take precedence (e.g. @@ -236,7 +237,6 @@ function AddressSearch(props) { city: locality || postalTown || sublocality || cityAutocompleteFallback, zipCode, - state: state || stateAutoCompleteFallback, lat: lodashGet(details, 'geometry.location.lat', 0), lng: lodashGet(details, 'geometry.location.lng', 0), address: lodashGet(details, 'formatted_address', ''), From ba75ff99bdef26ff2af65cbb535e4a683f871908 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 10 Nov 2023 18:46:06 +0700 Subject: [PATCH 80/97] fix: 31101 Eye icon to show password on password protected PDF file is not working --- ...ve-web+0.19.9+003+fix-pointer-events.patch | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 patches/react-native-web+0.19.9+003+fix-pointer-events.patch diff --git a/patches/react-native-web+0.19.9+003+fix-pointer-events.patch b/patches/react-native-web+0.19.9+003+fix-pointer-events.patch new file mode 100644 index 000000000000..ac8d16b866ff --- /dev/null +++ b/patches/react-native-web+0.19.9+003+fix-pointer-events.patch @@ -0,0 +1,20 @@ +diff --git a/node_modules/react-native-web/src/exports/StyleSheet/compiler/index.js b/node_modules/react-native-web/src/exports/StyleSheet/compiler/index.js +index 639f532..524c13f 100644 +--- a/node_modules/react-native-web/src/exports/StyleSheet/compiler/index.js ++++ b/node_modules/react-native-web/src/exports/StyleSheet/compiler/index.js +@@ -385,13 +385,14 @@ function createAtomicRules(identifier: string, property, value): Rules { + finalValue = 'auto!important'; + if (value === 'box-only') { + const block = createDeclarationBlock({ pointerEvents: 'none' }); +- rules.push(`${selector}>*${block}`); ++ rules.push(`${selector} *${block}`); + } + } else if (value === 'none' || value === 'box-none') { + finalValue = 'none!important'; + if (value === 'box-none') { + const block = createDeclarationBlock({ pointerEvents: 'auto' }); +- rules.push(`${selector}>*${block}`); ++ rules.push(`${selector} *${block}`); + } + } + const block = createDeclarationBlock({ pointerEvents: finalValue }); From f34c1e24730681a453245bb015db43d573dd5287 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 13 Nov 2023 10:40:39 +0700 Subject: [PATCH 81/97] rename patch file --- ...ve-web+0.19.9+003+fix-pointer-events.patch | 20 ----------------- ...ve-web+0.19.9+004+fix-pointer-events.patch | 22 +++++++++++++++++++ 2 files changed, 22 insertions(+), 20 deletions(-) delete mode 100644 patches/react-native-web+0.19.9+003+fix-pointer-events.patch create mode 100644 patches/react-native-web+0.19.9+004+fix-pointer-events.patch diff --git a/patches/react-native-web+0.19.9+003+fix-pointer-events.patch b/patches/react-native-web+0.19.9+003+fix-pointer-events.patch deleted file mode 100644 index ac8d16b866ff..000000000000 --- a/patches/react-native-web+0.19.9+003+fix-pointer-events.patch +++ /dev/null @@ -1,20 +0,0 @@ -diff --git a/node_modules/react-native-web/src/exports/StyleSheet/compiler/index.js b/node_modules/react-native-web/src/exports/StyleSheet/compiler/index.js -index 639f532..524c13f 100644 ---- a/node_modules/react-native-web/src/exports/StyleSheet/compiler/index.js -+++ b/node_modules/react-native-web/src/exports/StyleSheet/compiler/index.js -@@ -385,13 +385,14 @@ function createAtomicRules(identifier: string, property, value): Rules { - finalValue = 'auto!important'; - if (value === 'box-only') { - const block = createDeclarationBlock({ pointerEvents: 'none' }); -- rules.push(`${selector}>*${block}`); -+ rules.push(`${selector} *${block}`); - } - } else if (value === 'none' || value === 'box-none') { - finalValue = 'none!important'; - if (value === 'box-none') { - const block = createDeclarationBlock({ pointerEvents: 'auto' }); -- rules.push(`${selector}>*${block}`); -+ rules.push(`${selector} *${block}`); - } - } - const block = createDeclarationBlock({ pointerEvents: finalValue }); diff --git a/patches/react-native-web+0.19.9+004+fix-pointer-events.patch b/patches/react-native-web+0.19.9+004+fix-pointer-events.patch new file mode 100644 index 000000000000..a457fbcfe36c --- /dev/null +++ b/patches/react-native-web+0.19.9+004+fix-pointer-events.patch @@ -0,0 +1,22 @@ +diff --git a/node_modules/react-native-web/dist/exports/StyleSheet/compiler/index.js b/node_modules/react-native-web/dist/exports/StyleSheet/compiler/index.js +index bdcecc2..63f1364 100644 +--- a/node_modules/react-native-web/dist/exports/StyleSheet/compiler/index.js ++++ b/node_modules/react-native-web/dist/exports/StyleSheet/compiler/index.js +@@ -353,7 +353,7 @@ function createAtomicRules(identifier, property, value) { + var _block2 = createDeclarationBlock({ + pointerEvents: 'none' + }); +- rules.push(selector + ">*" + _block2); ++ rules.push(selector + " *" + _block2); + } + } else if (value === 'none' || value === 'box-none') { + finalValue = 'none!important'; +@@ -361,7 +361,7 @@ function createAtomicRules(identifier, property, value) { + var _block3 = createDeclarationBlock({ + pointerEvents: 'auto' + }); +- rules.push(selector + ">*" + _block3); ++ rules.push(selector + " *" + _block3); + } + } + var _block4 = createDeclarationBlock({ From 464dae23ac0900b15fdbea3aafcef6271bec17dd Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 13 Nov 2023 11:59:40 +0700 Subject: [PATCH 82/97] update onMouseDown logic --- .../AppleSignIn/index.desktop.js | 2 +- .../AppleSignIn/index.website.js | 4 -- .../GoogleSignIn/index.desktop.js | 2 +- .../GoogleSignIn/index.website.js | 4 +- src/components/SignInButtons/IconButton.js | 7 +--- src/pages/signin/LoginForm/BaseLoginForm.js | 37 +++++++++++-------- 6 files changed, 26 insertions(+), 30 deletions(-) diff --git a/src/components/SignInButtons/AppleSignIn/index.desktop.js b/src/components/SignInButtons/AppleSignIn/index.desktop.js index fac187c6fcc4..c17705110c63 100644 --- a/src/components/SignInButtons/AppleSignIn/index.desktop.js +++ b/src/components/SignInButtons/AppleSignIn/index.desktop.js @@ -19,7 +19,7 @@ function AppleSignIn() { pointerEvents="box-none" > { + onPress={() => { window.open(appleSignInWebRouteForDesktopFlow); }} provider={CONST.SIGN_IN_METHOD.APPLE} diff --git a/src/components/SignInButtons/AppleSignIn/index.website.js b/src/components/SignInButtons/AppleSignIn/index.website.js index f30e06a9bcab..adae0a691e13 100644 --- a/src/components/SignInButtons/AppleSignIn/index.website.js +++ b/src/components/SignInButtons/AppleSignIn/index.website.js @@ -83,7 +83,6 @@ function AppleSignInDiv({isDesktopFlow}) { }, []); return isDesktopFlow ? ( - /* eslint-disable-next-line jsx-a11y/no-static-element-interactions */
event.preventDefault()} /> ) : ( - /* eslint-disable-next-line jsx-a11y/no-static-element-interactions */
event.preventDefault()} /> ); } diff --git a/src/components/SignInButtons/GoogleSignIn/index.desktop.js b/src/components/SignInButtons/GoogleSignIn/index.desktop.js index b2f3cb01a980..e69905dcad05 100644 --- a/src/components/SignInButtons/GoogleSignIn/index.desktop.js +++ b/src/components/SignInButtons/GoogleSignIn/index.desktop.js @@ -19,7 +19,7 @@ function GoogleSignIn() { return ( { + onPress={() => { window.open(googleSignInWebRouteForDesktopFlow); }} provider={CONST.SIGN_IN_METHOD.GOOGLE} diff --git a/src/components/SignInButtons/GoogleSignIn/index.website.js b/src/components/SignInButtons/GoogleSignIn/index.website.js index 33f98efe4568..1ff52c949752 100644 --- a/src/components/SignInButtons/GoogleSignIn/index.website.js +++ b/src/components/SignInButtons/GoogleSignIn/index.website.js @@ -78,7 +78,7 @@ function GoogleSignIn({translate, isDesktopFlow}) { id={desktopId} role={CONST.ACCESSIBILITY_ROLE.BUTTON} aria-label={translate('common.signInWithGoogle')} - onMouseDown={(event) => event.preventDefault()} + // onMouseDown={(event) => event.preventDefault()} /> ) : ( @@ -88,7 +88,7 @@ function GoogleSignIn({translate, isDesktopFlow}) { id={mainId} role={CONST.ACCESSIBILITY_ROLE.BUTTON} aria-label={translate('common.signInWithGoogle')} - onMouseDown={(event) => event.preventDefault()} + // onMouseDown={(event) => event.preventDefault()} /> ); diff --git a/src/components/SignInButtons/IconButton.js b/src/components/SignInButtons/IconButton.js index 95ec49a74621..ce932b875542 100644 --- a/src/components/SignInButtons/IconButton.js +++ b/src/components/SignInButtons/IconButton.js @@ -11,9 +11,6 @@ const propTypes = { /** The on press method */ onPress: PropTypes.func, - /** The on press in method */ - onPressIn: PropTypes.func, - /** Which provider you are using to sign in */ provider: PropTypes.string.isRequired, @@ -22,7 +19,6 @@ const propTypes = { const defaultProps = { onPress: () => {}, - onPressIn: () => {}, }; const providerData = { @@ -36,10 +32,9 @@ const providerData = { }, }; -function IconButton({onPress, translate, provider, onPressIn}) { +function IconButton({onPress, translate, provider}) { return ( { - if (firstBlurred.current) { + if (firstBlurred.current || !Visibility.isVisible() || !Visibility.hasFocus()) { return; } firstBlurred.current = true; @@ -301,22 +302,26 @@ function LoginForm(props) { // 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 && ( - - - {props.translate('common.signInWith')} - - - - - + CONFIG.ENVIRONMENT !== CONST.ENVIRONMENT.DEV || + (true && ( + + + {props.translate('common.signInWith')} + + + e.preventDefault()} + style={props.isSmallScreenWidth ? styles.loginButtonRowSmallScreen : styles.loginButtonRow} + > + + + - - ) + )) } ) From f3d226113659ea76119cf609ceba241a6cc57d14 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 13 Nov 2023 12:01:23 +0700 Subject: [PATCH 83/97] merge main --- src/components/SignInButtons/AppleSignIn/index.desktop.js | 5 +---- src/components/SignInButtons/GoogleSignIn/index.website.js | 4 ---- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/components/SignInButtons/AppleSignIn/index.desktop.js b/src/components/SignInButtons/AppleSignIn/index.desktop.js index c17705110c63..cad37d585de4 100644 --- a/src/components/SignInButtons/AppleSignIn/index.desktop.js +++ b/src/components/SignInButtons/AppleSignIn/index.desktop.js @@ -14,10 +14,7 @@ const appleSignInWebRouteForDesktopFlow = `${CONFIG.EXPENSIFY.NEW_EXPENSIFY_URL} */ function AppleSignIn() { return ( - + { window.open(appleSignInWebRouteForDesktopFlow); diff --git a/src/components/SignInButtons/GoogleSignIn/index.website.js b/src/components/SignInButtons/GoogleSignIn/index.website.js index 1ff52c949752..d65af124bfe8 100644 --- a/src/components/SignInButtons/GoogleSignIn/index.website.js +++ b/src/components/SignInButtons/GoogleSignIn/index.website.js @@ -73,22 +73,18 @@ function GoogleSignIn({translate, isDesktopFlow}) { // ref: https://stackoverflow.com/questions/75306089/safari-when-using-border-radius-and-overflow-hidden-to-parent-and-the-child-th return isDesktopFlow ? ( - {/* eslint-disable-next-line jsx-a11y/interactive-supports-focus */}
event.preventDefault()} /> ) : ( - {/* eslint-disable-next-line jsx-a11y/interactive-supports-focus */}
event.preventDefault()} /> ); From b588fbbcc5eaf706ec2e4709cdbca0f2c8764093 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 13 Nov 2023 14:38:54 +0700 Subject: [PATCH 84/97] revert hard code --- src/pages/signin/LoginForm/BaseLoginForm.js | 37 ++++++++++----------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/pages/signin/LoginForm/BaseLoginForm.js b/src/pages/signin/LoginForm/BaseLoginForm.js index 9a862273f89e..2576401d06a7 100644 --- a/src/pages/signin/LoginForm/BaseLoginForm.js +++ b/src/pages/signin/LoginForm/BaseLoginForm.js @@ -302,26 +302,25 @@ function LoginForm(props) { // 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 || - (true && ( - - - {props.translate('common.signInWith')} - - - e.preventDefault()} - style={props.isSmallScreenWidth ? styles.loginButtonRowSmallScreen : styles.loginButtonRow} - > - - - + CONFIG.ENVIRONMENT !== CONST.ENVIRONMENT.DEV && ( + + + {props.translate('common.signInWith')} + + + e.preventDefault()} + style={props.isSmallScreenWidth ? styles.loginButtonRowSmallScreen : styles.loginButtonRow} + > + + - )) + + ) } ) From 20e80d13a434ac8bc2876394a92cf3c54f557e3a Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 13 Nov 2023 09:28:17 +0100 Subject: [PATCH 85/97] destructure props in Modal --- src/components/Modal/index.ios.tsx | 6 +++--- src/components/Modal/index.tsx | 17 +++++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/components/Modal/index.ios.tsx b/src/components/Modal/index.ios.tsx index 6952392a6326..f780775ec216 100644 --- a/src/components/Modal/index.ios.tsx +++ b/src/components/Modal/index.ios.tsx @@ -3,13 +3,13 @@ import withWindowDimensions from '@components/withWindowDimensions'; import BaseModal from './BaseModal'; import BaseModalProps from './types'; -function Modal(props: BaseModalProps) { +function Modal({children, ...rest}: BaseModalProps) { return ( - {props.children} + {children} ); } diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx index 3d8a30ba3e2a..a3d21d67b9af 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -7,11 +7,11 @@ import CONST from '@src/CONST'; import BaseModal from './BaseModal'; import BaseModalProps from './types'; -function Modal(props: BaseModalProps) { +function Modal({fullscreen, onModalHide, type, onModalShow, children, ...rest}: BaseModalProps) { const [previousStatusBarColor, setPreviousStatusBarColor] = useState(); const setStatusBarColor = (color = themeColors.appBG) => { - if (!props.fullscreen) { + if (!fullscreen) { return; } @@ -20,14 +20,13 @@ function Modal(props: BaseModalProps) { const hideModal = () => { setStatusBarColor(previousStatusBarColor); - props.onModalHide(); + onModalHide(); }; const showModal = () => { const statusBarColor = StatusBar.getBackgroundColor(); - const isFullScreenModal = - props.type === CONST.MODAL.MODAL_TYPE.CENTERED || props.type === CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE || props.type === CONST.MODAL.MODAL_TYPE.RIGHT_DOCKED; + const isFullScreenModal = type === CONST.MODAL.MODAL_TYPE.CENTERED || type === CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE || type === CONST.MODAL.MODAL_TYPE.RIGHT_DOCKED; if (statusBarColor) { setPreviousStatusBarColor(statusBarColor); @@ -35,18 +34,20 @@ function Modal(props: BaseModalProps) { setStatusBarColor(isFullScreenModal ? themeColors.appBG : StyleUtils.getThemeBackgroundColor(statusBarColor)); } - props.onModalShow?.(); + onModalShow?.(); }; return ( - {props.children} + {children} ); } From 93e29f4a04fe6efbcc361f93ca5c73afca33919f Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Mon, 13 Nov 2023 14:26:27 +0100 Subject: [PATCH 86/97] update for undefined state Co-authored-by: Monil Bhavsar --- src/components/MentionSuggestions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MentionSuggestions.tsx b/src/components/MentionSuggestions.tsx index a009364c2223..2d0f3bf32b41 100644 --- a/src/components/MentionSuggestions.tsx +++ b/src/components/MentionSuggestions.tsx @@ -73,7 +73,7 @@ function MentionSuggestions({prefix, mentions, highlightedMentionIndex = 0, onSe style={[styles.mentionSuggestionsText, styles.flexShrink1]} numberOfLines={1} > - {styledDisplayName.map(({text, isColored}, i) => ( + {styledDisplayName?.map(({text, isColored}, i) => ( Date: Mon, 13 Nov 2023 21:54:15 +0700 Subject: [PATCH 87/97] fix not blured --- src/pages/signin/LoginForm/BaseLoginForm.js | 38 +++++++++++---------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/pages/signin/LoginForm/BaseLoginForm.js b/src/pages/signin/LoginForm/BaseLoginForm.js index 2576401d06a7..bdac88d7e420 100644 --- a/src/pages/signin/LoginForm/BaseLoginForm.js +++ b/src/pages/signin/LoginForm/BaseLoginForm.js @@ -302,25 +302,27 @@ function LoginForm(props) { // 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 && ( - - - {props.translate('common.signInWith')} - - - e.preventDefault()} - style={props.isSmallScreenWidth ? styles.loginButtonRowSmallScreen : styles.loginButtonRow} - > - - + CONFIG.ENVIRONMENT !== CONST.ENVIRONMENT.DEV || + (true && ( + + + {props.translate('common.signInWith')} + + + + e.preventDefault()}> + + + e.preventDefault()}> + + + - - ) + )) } ) From 5671583543da6d0a4d62cf728c61ebf720534060 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 13 Nov 2023 21:57:26 +0700 Subject: [PATCH 88/97] revert hard code --- src/pages/signin/LoginForm/BaseLoginForm.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pages/signin/LoginForm/BaseLoginForm.js b/src/pages/signin/LoginForm/BaseLoginForm.js index bdac88d7e420..632419fd4293 100644 --- a/src/pages/signin/LoginForm/BaseLoginForm.js +++ b/src/pages/signin/LoginForm/BaseLoginForm.js @@ -302,8 +302,7 @@ function LoginForm(props) { // 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 || - (true && ( + CONFIG.ENVIRONMENT !== CONST.ENVIRONMENT.DEV && ( - )) + ) } ) From 57031a6bd243218ed08931826b811a6db85a9966 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 13 Nov 2023 22:05:36 +0700 Subject: [PATCH 89/97] fix lint --- src/pages/signin/LoginForm/BaseLoginForm.js | 34 ++++++++++----------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/pages/signin/LoginForm/BaseLoginForm.js b/src/pages/signin/LoginForm/BaseLoginForm.js index 632419fd4293..9cd620cf8f12 100644 --- a/src/pages/signin/LoginForm/BaseLoginForm.js +++ b/src/pages/signin/LoginForm/BaseLoginForm.js @@ -303,25 +303,25 @@ function LoginForm(props) { // 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 && ( - - - {props.translate('common.signInWith')} - - - - e.preventDefault()}> - - - e.preventDefault()}> - - + + + {props.translate('common.signInWith')} + + + + e.preventDefault()}> + + + e.preventDefault()}> + - ) + + ) } ) From 0b23ac2ac1930d458fa3665087112fdba07509a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Mon, 13 Nov 2023 19:18:13 +0100 Subject: [PATCH 90/97] native camera fix and useTabNavigatorFocus refactor --- src/hooks/useTabNavigatorFocus/index.js | 8 +- .../useTabNavigatorFocus/index.native.js | 75 ------------------- src/libs/DomUtils/index.native.ts | 9 +++ src/libs/DomUtils/index.ts | 1 + 4 files changed, 13 insertions(+), 80 deletions(-) delete mode 100644 src/hooks/useTabNavigatorFocus/index.native.js diff --git a/src/hooks/useTabNavigatorFocus/index.js b/src/hooks/useTabNavigatorFocus/index.js index 7af6bc5950ac..86feb282f44f 100644 --- a/src/hooks/useTabNavigatorFocus/index.js +++ b/src/hooks/useTabNavigatorFocus/index.js @@ -1,6 +1,7 @@ import {useTabAnimation} from '@react-navigation/material-top-tabs'; import {useIsFocused} from '@react-navigation/native'; import {useEffect, useState} from 'react'; +import DomUtils from '@libs/DomUtils'; /** * Custom React hook to determine the focus status of a tab in a Material Top Tab Navigator. @@ -16,9 +17,6 @@ import {useEffect, useState} from 'react'; * might not be used within a Material Top Tabs Navigator context. Proper usage should ensure that * this hook is only used where appropriate. * - * Note: This hook is almost identical to native implementation, except for the `selectedTab` parameter - * and the fact that it uses requestAnimationFrame to mitigate issues when updating the isTabFocused state. - * * @param {Object} params - The parameters object. * @param {number} params.tabIndex - The index of the tab for which focus status is being determined. * @returns {boolean} Returns `true` if the tab is both animation-focused and screen-focused, otherwise `false`. @@ -50,7 +48,7 @@ function useTabNavigatorFocus({tabIndex}) { const listenerId = tabPositionAnimation.addListener(({value}) => { // Activate camera as soon the index is animating towards the `tabIndex` - requestAnimationFrame(() => { + DomUtils.requestAnimationFrame(() => { setIsTabFocused(value > index - 1 && value < index + 1); }); }); @@ -62,7 +60,7 @@ function useTabNavigatorFocus({tabIndex}) { const initialTabPositionValue = tabPositionAnimation.__getValue(); if (typeof initialTabPositionValue === 'number') { - requestAnimationFrame(() => { + DomUtils.requestAnimationFrame(() => { setIsTabFocused(initialTabPositionValue > index - 1 && initialTabPositionValue < index + 1); }); } diff --git a/src/hooks/useTabNavigatorFocus/index.native.js b/src/hooks/useTabNavigatorFocus/index.native.js deleted file mode 100644 index 7f9da7b3f841..000000000000 --- a/src/hooks/useTabNavigatorFocus/index.native.js +++ /dev/null @@ -1,75 +0,0 @@ -import {useTabAnimation} from '@react-navigation/material-top-tabs'; -import {useIsFocused} from '@react-navigation/native'; -import {useEffect, useState} from 'react'; -import CONST from '@src/CONST'; - -/** - * Custom React hook to determine the focus status of a specific tab in a Material Top Tab Navigator, with additional - * conditions based on a selected tab state. It evaluates whether the specified tab is focused by combining the tab's - * animation position and the screen's focus status within a React Navigation environment. - * - * The hook is primarily intended for use with Material Top Tabs provided by '@react-navigation/material-top-tabs'. - * It utilizes the `useTabAnimation` hook from this package to track the animated position of the tabs and the - * `useIsFocused` hook from '@react-navigation/native' to determine if the current screen is focused. Additionally, - * it uses a `selectedTab` parameter to apply custom logic based on the currently selected tab. - * - * Note: This hook employs a conditional invocation of the `useTabAnimation` hook, which is generally against React's - * rules of hooks. This pattern is used here to handle scenarios where the hook might not be employed within a - * Material Top Tabs Navigator context. Ensure this hook is only used in appropriate scenarios to avoid potential issues. - * - * @param {Object} params - The parameters object. - * @param {number} params.tabIndex - The index of the tab for which focus status is being determined. - * @param {string} params.selectedTab - The tab identifier passed by to the component. Used only on native platform - * - * @returns {boolean} Returns `true` if the specified tab is both animation-focused and screen-focused, otherwise `false`. - * - * @example - * const isTabFocused = useTabNavigatorFocus({ tabIndex: 1, selectedTab: 'home' }); - */ -function useTabNavigatorFocus({tabIndex, selectedTab}) { - let tabPositionAnimation = null; - try { - // Retrieve the animation value from the tab navigator, which ranges from 0 to the total number of pages displayed. - // Even a minimal scroll towards the camera page (e.g., a value of 0.001 at start) should activate the camera for immediate responsiveness. - // STOP!!!!!!! This is not a pattern to be followed! We are conditionally rendering this hook becase when used in the edit flow we'll never be inside a tab navigator. - // eslint-disable-next-line react-hooks/rules-of-hooks - tabPositionAnimation = useTabAnimation(); - } catch (error) { - tabPositionAnimation = null; - } - const isPageFocused = useIsFocused(); - // set to true if the hook is not used within the MaterialTopTabs context - // the hook will then return true if the screen is focused - const [isTabFocused, setIsTabFocused] = useState(!tabPositionAnimation); - - // Retrieve the animation value from the tab navigator, which ranges from 0 to the total number of pages displayed. - // Even a minimal scroll towards the camera page (e.g., a value of 0.001 at start) should activate the camera for immediate responsiveness. - - // STOP!!!!!!! This is not a pattern to be followed! We are conditionally rendering this hook becase when used in the edit flow we'll never be inside a tab navigator. - // eslint-disable-next-line react-hooks/rules-of-hooks - - useEffect(() => { - if (!tabPositionAnimation) { - return; - } - - const listenerId = tabPositionAnimation.addListener(({value}) => { - if (selectedTab !== CONST.TAB.SCAN) { - return; - } - // Activate camera as soon the index is animating towards the `cameraTabIndex` - setIsTabFocused(value > tabIndex - 1 && value < tabIndex + 1); - }); - - return () => { - if (!tabPositionAnimation) { - return; - } - tabPositionAnimation.removeListener(listenerId); - }; - }, [tabIndex, tabPositionAnimation, selectedTab]); - - return isTabFocused && isPageFocused; -} - -export default useTabNavigatorFocus; diff --git a/src/libs/DomUtils/index.native.ts b/src/libs/DomUtils/index.native.ts index 9a9758228776..0864f1a16ac0 100644 --- a/src/libs/DomUtils/index.native.ts +++ b/src/libs/DomUtils/index.native.ts @@ -2,6 +2,15 @@ import GetActiveElement from './types'; const getActiveElement: GetActiveElement = () => null; +const requestAnimationFrame = (callback: () => void) => { + if (!callback) { + return; + } + + callback(); +}; + export default { getActiveElement, + requestAnimationFrame, }; diff --git a/src/libs/DomUtils/index.ts b/src/libs/DomUtils/index.ts index 94dd54547454..6a2eed57fbe6 100644 --- a/src/libs/DomUtils/index.ts +++ b/src/libs/DomUtils/index.ts @@ -4,4 +4,5 @@ const getActiveElement: GetActiveElement = () => document.activeElement; export default { getActiveElement, + requestAnimationFrame: window.requestAnimationFrame.bind(window), }; From 434e472ded14000ceb318e25176940523b11ed82 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Tue, 14 Nov 2023 09:28:02 +0100 Subject: [PATCH 91/97] bring back default props --- src/components/Modal/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx index a3d21d67b9af..b4cfc1f06211 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -7,7 +7,7 @@ import CONST from '@src/CONST'; import BaseModal from './BaseModal'; import BaseModalProps from './types'; -function Modal({fullscreen, onModalHide, type, onModalShow, children, ...rest}: BaseModalProps) { +function Modal({fullscreen = true, onModalHide = () => {}, type, onModalShow = () => {}, children, ...rest}: BaseModalProps) { const [previousStatusBarColor, setPreviousStatusBarColor] = useState(); const setStatusBarColor = (color = themeColors.appBG) => { From 1349a1e21f786a638120794bfdfc331bb4597a27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Tue, 14 Nov 2023 10:15:16 +0100 Subject: [PATCH 92/97] JSDocs types improvements --- src/hooks/useTabNavigatorFocus/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/useTabNavigatorFocus/index.js b/src/hooks/useTabNavigatorFocus/index.js index 86feb282f44f..f83ec5bd9270 100644 --- a/src/hooks/useTabNavigatorFocus/index.js +++ b/src/hooks/useTabNavigatorFocus/index.js @@ -18,8 +18,8 @@ import DomUtils from '@libs/DomUtils'; * this hook is only used where appropriate. * * @param {Object} params - The parameters object. - * @param {number} params.tabIndex - The index of the tab for which focus status is being determined. - * @returns {boolean} Returns `true` if the tab is both animation-focused and screen-focused, otherwise `false`. + * @param {Number} params.tabIndex - The index of the tab for which focus status is being determined. + * @returns {Boolean} Returns `true` if the tab is both animation-focused and screen-focused, otherwise `false`. * * @example * const isTabFocused = useTabNavigatorFocus({ tabIndex: 1 }); From 66092e7284a61d52835084ee3befdbcdb4616086 Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Tue, 14 Nov 2023 11:55:39 +0100 Subject: [PATCH 93/97] AttachmentCommentFragment: Convert back to a named function --- .../home/report/comment/AttachmentCommentFragment.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/home/report/comment/AttachmentCommentFragment.js b/src/pages/home/report/comment/AttachmentCommentFragment.js index ee5d2c99c783..a8a35cfb3925 100644 --- a/src/pages/home/report/comment/AttachmentCommentFragment.js +++ b/src/pages/home/report/comment/AttachmentCommentFragment.js @@ -16,14 +16,14 @@ const propTypes = { addExtraMargin: PropTypes.bool.isRequired, }; -const AttachmentCommentFragment = ({addExtraMargin, html, source}) => ( - +function AttachmentCommentFragment({addExtraMargin, html, source}) { + return - -); + ; +} AttachmentCommentFragment.propTypes = propTypes; AttachmentCommentFragment.displayName = 'AttachmentCommentFragment'; From a9eb4686f8844a27e6a75fe0d7e8770823ac98d2 Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Tue, 14 Nov 2023 11:59:46 +0100 Subject: [PATCH 94/97] Run Prettier --- .../report/comment/AttachmentCommentFragment.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/pages/home/report/comment/AttachmentCommentFragment.js b/src/pages/home/report/comment/AttachmentCommentFragment.js index a8a35cfb3925..8ee161600aee 100644 --- a/src/pages/home/report/comment/AttachmentCommentFragment.js +++ b/src/pages/home/report/comment/AttachmentCommentFragment.js @@ -17,12 +17,14 @@ const propTypes = { }; function AttachmentCommentFragment({addExtraMargin, html, source}) { - return - - ; + return ( + + + + ); } AttachmentCommentFragment.propTypes = propTypes; From 60f26d3968f16d800873adb53fefd1f46f1da8d0 Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Wed, 15 Nov 2023 11:26:16 +0100 Subject: [PATCH 95/97] Update icon type --- src/types/onyx/OnyxCommon.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/types/onyx/OnyxCommon.ts b/src/types/onyx/OnyxCommon.ts index 4d376b8b7da8..ac69baed3ef1 100644 --- a/src/types/onyx/OnyxCommon.ts +++ b/src/types/onyx/OnyxCommon.ts @@ -12,16 +12,16 @@ type Errors = Record; type Icon = { /** Avatar source to display */ - source?: AvatarSource; + source: AvatarSource; /** Denotes whether it is an avatar or a workspace avatar */ - type?: typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; + type: typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; /** Owner of the avatar. If user, displayName. If workspace, policy name */ - name?: string; + name: string; /** Avatar id */ - id?: number | string; + id: number | string; /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */ fallbackIcon?: AvatarSource; From 60807870728cd1bb56eb1f059e8af28de11133da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Wed, 15 Nov 2023 15:24:57 +0100 Subject: [PATCH 96/97] removed unnecessary HOC and unused prop --- src/components/withTabAnimation.js | 72 ------------------- .../NavigationAwareCamera/index.native.js | 20 +----- src/pages/iou/ReceiptSelector/index.native.js | 3 +- 3 files changed, 4 insertions(+), 91 deletions(-) delete mode 100644 src/components/withTabAnimation.js diff --git a/src/components/withTabAnimation.js b/src/components/withTabAnimation.js deleted file mode 100644 index 2af96f0215a3..000000000000 --- a/src/components/withTabAnimation.js +++ /dev/null @@ -1,72 +0,0 @@ -import {useTabAnimation} from '@react-navigation/material-top-tabs'; -import PropTypes from 'prop-types'; -import * as React from 'react'; -import getComponentDisplayName from '@libs/getComponentDisplayName'; -import refPropTypes from './refPropTypes'; - -const propTypes = { - /** The HOC takes an optional ref as a prop and passes it as a ref to the wrapped component. - * That way, if a ref is passed to a component wrapped in the HOC, the ref is a reference to the wrapped component, not the HOC. */ - forwardedRef: refPropTypes, - - /* Whether we're in a tab navigator */ - isInTabNavigator: PropTypes.bool.isRequired, -}; - -const defaultProps = { - forwardedRef: () => {}, -}; - -export default function (WrappedComponent) { - // The component with tab animation prop - function WrappedComponentWithTabAnimation(props) { - const animation = useTabAnimation(); - - return ( - - ); - } - - WrappedComponentWithTabAnimation.displayName = `withAnimation(${getComponentDisplayName(WrappedComponent)})`; - - // Return a component with tab animation prop if this component is in tab navigator, otherwise return itself - function WithTabAnimation({forwardedRef, ...rest}) { - if (rest.isInTabNavigator) { - return ( - - ); - } - return ( - - ); - } - - WithTabAnimation.propTypes = propTypes; - WithTabAnimation.defaultProps = defaultProps; - WithTabAnimation.displayName = `withTabAnimation(${getComponentDisplayName(WrappedComponent)})`; - - // eslint-disable-next-line rulesdir/no-negated-variables - const WithTabAnimationWithRef = React.forwardRef((props, ref) => ( - - )); - - WithTabAnimationWithRef.displayName = `withTabAnimationWithRef(${getComponentDisplayName(WrappedComponent)})`; - - return WithTabAnimationWithRef; -} diff --git a/src/pages/iou/ReceiptSelector/NavigationAwareCamera/index.native.js b/src/pages/iou/ReceiptSelector/NavigationAwareCamera/index.native.js index 3d1ffb238722..65c17d3cb7ab 100644 --- a/src/pages/iou/ReceiptSelector/NavigationAwareCamera/index.native.js +++ b/src/pages/iou/ReceiptSelector/NavigationAwareCamera/index.native.js @@ -6,24 +6,11 @@ import useTabNavigatorFocus from '@hooks/useTabNavigatorFocus'; const propTypes = { /* The index of the tab that contains this camera */ cameraTabIndex: PropTypes.number.isRequired, - - /** Name of the selected receipt tab */ - selectedTab: PropTypes.string.isRequired, - - /** The tab animation from hook */ - tabAnimation: PropTypes.shape({ - addListener: PropTypes.func, - removeListener: PropTypes.func, - }), -}; - -const defaultProps = { - tabAnimation: undefined, }; // Wraps a camera that will only be active when the tab is focused or as soon as it starts to become focused. -const NavigationAwareCamera = React.forwardRef(({cameraTabIndex, selectedTab, ...props}, ref) => { - const isCameraActive = useTabNavigatorFocus({tabIndex: cameraTabIndex, selectedTab}); +const NavigationAwareCamera = React.forwardRef(({cameraTabIndex, ...props}, ref) => { + const isCameraActive = useTabNavigatorFocus({tabIndex: cameraTabIndex}); return ( )} From 5da6f16998bc1ce0d9efa46f53d57c7f5b1fde6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Wed, 15 Nov 2023 15:40:49 +0100 Subject: [PATCH 97/97] linting --- src/pages/iou/ReceiptSelector/index.native.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/pages/iou/ReceiptSelector/index.native.js b/src/pages/iou/ReceiptSelector/index.native.js index d0e978fee15b..ef81109ffb90 100644 --- a/src/pages/iou/ReceiptSelector/index.native.js +++ b/src/pages/iou/ReceiptSelector/index.native.js @@ -50,16 +50,12 @@ const propTypes = { /** The id of the transaction we're editing */ transactionID: PropTypes.string, - - /** Name of the selected receipt tab */ - selectedTab: PropTypes.string, }; const defaultProps = { report: {}, iou: iouDefaultProps, transactionID: '', - selectedTab: '', }; function ReceiptSelector({route, report, iou, transactionID}) {