From 5d914f18ea23782ebaf05997b3b2a19cdba1a04f Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Sat, 19 Aug 2023 14:27:10 +0200 Subject: [PATCH 001/120] 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 002/120] 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 003/120] 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 4fb1a60e6b5680c355ade566a13370e1aff381e6 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 13 Oct 2023 13:30:40 +0200 Subject: [PATCH 004/120] [TS migration] Migrate 'withLocalize.js' HOC to TypeScript --- .eslintrc.js | 2 +- .../{withLocalize.js => withLocalize.tsx} | 39 ++++++++++--------- src/libs/getComponentDisplayName.ts | 2 +- 3 files changed, 23 insertions(+), 20 deletions(-) rename src/components/{withLocalize.js => withLocalize.tsx} (57%) diff --git a/.eslintrc.js b/.eslintrc.js index 75a74ed371c4..83e9479ce0c4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -116,7 +116,7 @@ module.exports = { }, { selector: ['parameter', 'method'], - format: ['camelCase'], + format: ['camelCase', 'PascalCase'], }, ], '@typescript-eslint/ban-types': [ diff --git a/src/components/withLocalize.js b/src/components/withLocalize.tsx similarity index 57% rename from src/components/withLocalize.js rename to src/components/withLocalize.tsx index 65e98f78f312..e87a0c9984ad 100755 --- a/src/components/withLocalize.js +++ b/src/components/withLocalize.tsx @@ -1,6 +1,6 @@ -import React, {forwardRef} from 'react'; +import React, {ComponentType, ForwardedRef, RefAttributes, forwardRef} from 'react'; import PropTypes from 'prop-types'; -import {LocaleContext} from './LocaleContextProvider'; +import {LocaleContext, LocaleContextProps} from './LocaleContextProvider'; import getComponentDisplayName from '../libs/getComponentDisplayName'; const withLocalizePropTypes = { @@ -30,24 +30,27 @@ const withLocalizePropTypes = { toLocaleDigit: PropTypes.func.isRequired, }; -export default function withLocalize(WrappedComponent) { - const WithLocalize = forwardRef((props, ref) => ( - - {(translateUtils) => ( - - )} - - )); +type WithLocalizeProps = LocaleContextProps; + +export default function withLocalize(WrappedComponent: ComponentType>) { + function WithLocalize(props: Omit, ref: ForwardedRef) { + return ( + + {(translateUtils) => ( + + )} + + ); + } WithLocalize.displayName = `withLocalize(${getComponentDisplayName(WrappedComponent)})`; - - return WithLocalize; + return forwardRef(WithLocalize); } export {withLocalizePropTypes}; diff --git a/src/libs/getComponentDisplayName.ts b/src/libs/getComponentDisplayName.ts index fd1bbcaea521..0bf52d543a84 100644 --- a/src/libs/getComponentDisplayName.ts +++ b/src/libs/getComponentDisplayName.ts @@ -1,6 +1,6 @@ import {ComponentType} from 'react'; /** Returns the display name of a component */ -export default function getComponentDisplayName(component: ComponentType): string { +export default function getComponentDisplayName(component: ComponentType): string { return component.displayName ?? component.name ?? 'Component'; } From d5186d4eeedc8262c2f74037aa86d7baa5d0e6d6 Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Mon, 16 Oct 2023 10:06:21 +0200 Subject: [PATCH 005/120] 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 006/120] 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 007/120] 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 008/120] 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 009/120] 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 010/120] 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 011/120] 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 012/120] 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 013/120] 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 014/120] 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 015/120] 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 016/120] 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 017/120] 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 018/120] 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 019/120] 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 020/120] 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 021/120] 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 022/120] 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 023/120] 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 024/120] 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 025/120] 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 026/120] 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 027/120] 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 028/120] 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 029/120] 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 030/120] 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 031/120] 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 032/120] 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 033/120] 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 034/120] 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 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 035/120] 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 036/120] 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 037/120] 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 d888bd2629c78f3ebf87be47e6562c7dbb726685 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 1 Nov 2023 16:15:30 +0700 Subject: [PATCH 038/120] fix: should not be able to set welcome message for threads created from policy room --- src/libs/ReportUtils.js | 10 ++++++++++ src/pages/ReportWelcomeMessagePage.js | 3 ++- src/pages/settings/Report/ReportSettingsPage.js | 5 ++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 851dc08defaf..981da25dafca 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -4111,6 +4111,15 @@ function isReportDraft(report) { function shouldUseFullTitleToDisplay(report) { return isMoneyRequestReport(report) || isPolicyExpenseChat(report) || isChatRoom(report) || isChatThread(report) || isTaskReport(report); } +/** + * We want policy owners and admins to be able to modify the welcome message only when the report is not a thread chat + * @param {Object} report + * @param {Object} policy + * @return {Boolean} + */ +function checkShouldDisableWelcomeMessage(report, policy) { + return isMoneyRequestReport(report) || isArchivedRoom(report) || !isChatRoom(report) || _.isEmpty(policy) || policy.role !== CONST.POLICY.ROLE.ADMIN || isChatThread(report); +} export { getReportParticipantsTitle, @@ -4269,4 +4278,5 @@ export { shouldUseFullTitleToDisplay, parseReportRouteParams, getReimbursementQueuedActionMessage, + checkShouldDisableWelcomeMessage, }; diff --git a/src/pages/ReportWelcomeMessagePage.js b/src/pages/ReportWelcomeMessagePage.js index 1329817dae5b..c5721f62919a 100644 --- a/src/pages/ReportWelcomeMessagePage.js +++ b/src/pages/ReportWelcomeMessagePage.js @@ -14,6 +14,7 @@ import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; +import * as ReportUtils from '@libs/ReportUtils'; import updateMultilineInputRange from '@libs/UpdateMultilineInputRange'; import styles from '@styles/styles'; import * as Report from '@userActions/Report'; @@ -80,7 +81,7 @@ function ReportWelcomeMessagePage(props) { includeSafeAreaPaddingBottom={false} testID={ReportWelcomeMessagePage.displayName} > - + Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(props.report.reportID))} diff --git a/src/pages/settings/Report/ReportSettingsPage.js b/src/pages/settings/Report/ReportSettingsPage.js index df23e16e80cd..fb10ad1a90f4 100644 --- a/src/pages/settings/Report/ReportSettingsPage.js +++ b/src/pages/settings/Report/ReportSettingsPage.js @@ -61,9 +61,8 @@ function ReportSettingsPage(props) { const shouldDisableRename = useMemo(() => ReportUtils.shouldDisableRename(report, linkedWorkspace), [report, linkedWorkspace]); const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); - // We only want policy owners and admins to be able to modify the welcome message. - const shouldDisableWelcomeMessage = - isMoneyRequestReport || ReportUtils.isArchivedRoom(report) || !ReportUtils.isChatRoom(report) || _.isEmpty(linkedWorkspace) || linkedWorkspace.role !== CONST.POLICY.ROLE.ADMIN; + // We want policy owners and admins to be able to modify the welcome message only when the report is not a thread chat + const shouldDisableWelcomeMessage = ReportUtils.checkShouldDisableWelcomeMessage(report, linkedWorkspace); const shouldDisableSettings = _.isEmpty(report) || ReportUtils.isArchivedRoom(report); const shouldShowRoomName = !ReportUtils.isPolicyExpenseChat(report) && !ReportUtils.isChatThread(report); From 6c6a057cf574b9456fd384f126727cbf5683c270 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 6 Nov 2023 10:36:24 +0700 Subject: [PATCH 039/120] fix lint issue --- src/styles/fontFamily/multiFontFamily.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/styles/fontFamily/multiFontFamily.ts b/src/styles/fontFamily/multiFontFamily.ts index 14f64d406954..5bd89e0d4bcb 100644 --- a/src/styles/fontFamily/multiFontFamily.ts +++ b/src/styles/fontFamily/multiFontFamily.ts @@ -1,7 +1,7 @@ +import getOperatingSystem from '@libs/getOperatingSystem'; +import CONST from '@src/CONST'; import {multiBold} from './bold'; import FontFamilyStyles from './types'; -import CONST from '../../CONST'; -import getOperatingSystem from '../../libs/getOperatingSystem'; // In windows and ubuntu, we need some extra system fonts for emojis to work properly // otherwise few of them will appear as black and white From 08c0d4c74a85887369c86a7087f68819e86ae42e Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Mon, 6 Nov 2023 12:22:28 +0100 Subject: [PATCH 040/120] 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 041/120] 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 042/120] 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 043/120] 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 40f0fec7df7b48cc0304a8e797c8c0a036d3fc86 Mon Sep 17 00:00:00 2001 From: Daniel Edwards Date: Tue, 7 Nov 2023 12:16:16 -0500 Subject: [PATCH 044/120] Add SVG --- assets/images/empty-state__attach-receipt.svg | 16 ++++++++++++++++ src/components/Icon/Expensicons.js | 2 ++ 2 files changed, 18 insertions(+) create mode 100644 assets/images/empty-state__attach-receipt.svg diff --git a/assets/images/empty-state__attach-receipt.svg b/assets/images/empty-state__attach-receipt.svg new file mode 100644 index 000000000000..6b50afbdbf0b --- /dev/null +++ b/assets/images/empty-state__attach-receipt.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/src/components/Icon/Expensicons.js b/src/components/Icon/Expensicons.js index a4aa6b13cb29..e5f4d158be1a 100644 --- a/src/components/Icon/Expensicons.js +++ b/src/components/Icon/Expensicons.js @@ -46,6 +46,7 @@ import DragAndDrop from '@assets/images/drag-and-drop.svg'; import DragHandles from '@assets/images/drag-handles.svg'; import Emoji from '@assets/images/emoji.svg'; import EmptyStateRoutePending from '@assets/images/emptystate__routepending.svg'; +import EmptyStateAttachReceipt from '@assets/images/empty-state__attach-receipt.svg'; import EReceiptIcon from '@assets/images/eReceiptIcon.svg'; import Exclamation from '@assets/images/exclamation.svg'; import Exit from '@assets/images/exit.svg'; @@ -176,6 +177,7 @@ export { EReceiptIcon, Emoji, EmptyStateRoutePending, + EmptyStateAttachReceipt, Exclamation, Exit, ExpensifyCard, From 90708810d63b89f8ad1a0a1af7ef5e5cec96d459 Mon Sep 17 00:00:00 2001 From: rory Date: Tue, 7 Nov 2023 10:22:22 -0800 Subject: [PATCH 045/120] Ignore docs/vendor in eslintignore --- .eslintignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.eslintignore b/.eslintignore index d3e8a6328bc4..396bfd28c614 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,4 @@ **/node_modules/* **/dist/* .github/actions/**/index.js" +docs/vendor/** From 561c3548eab0de370a61a4403a99dcbde28ebd81 Mon Sep 17 00:00:00 2001 From: Daniel Edwards Date: Tue, 7 Nov 2023 15:31:42 -0500 Subject: [PATCH 046/120] Adds ReceiptEmptyState Component --- src/components/ReceiptEmptyState.js | 47 +++++++++++++++++++++++++++++ src/styles/styles.ts | 5 +++ 2 files changed, 52 insertions(+) create mode 100644 src/components/ReceiptEmptyState.js diff --git a/src/components/ReceiptEmptyState.js b/src/components/ReceiptEmptyState.js new file mode 100644 index 000000000000..31edb4842389 --- /dev/null +++ b/src/components/ReceiptEmptyState.js @@ -0,0 +1,47 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { View } from 'react-native'; +import styles from '@styles/styles'; +import variables from '@styles/variables'; +import Icon from './Icon'; +import * as Expensicons from './Icon/Expensicons'; + +import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; + +const propTypes = { + /** Whether or not there is a violation error */ + hasError: PropTypes.bool, + + /** Callback to be called on onPress */ + onPress: PropTypes.func, +} + +const defaultProps = { + hasError: false, + onPress: undefined, +} + + +// Create a component with the above instructions: + +function ReceiptEmptyState({hasError, onPress}) { + return ( + + + + + + + ) +} + +ReceiptEmptyState.displayName = 'ReceiptEmptyState'; +ReceiptEmptyState.propTypes = propTypes; +ReceiptEmptyState.defaultProps = defaultProps; + +export default ReceiptEmptyState; diff --git a/src/styles/styles.ts b/src/styles/styles.ts index da5cc8e726fd..588dba090767 100644 --- a/src/styles/styles.ts +++ b/src/styles/styles.ts @@ -3837,6 +3837,11 @@ const styles = (theme: ThemeDefault) => maxWidth: 400, }, + moneyRequestAttachReceipt: { + backgroundColor: theme.appBG, + borderColor: theme.textSupporting, + }, + mapViewContainer: { ...flex.flex1, minHeight: 300, From d8ededb712fc3adad9133e63d845d0df33e7acca Mon Sep 17 00:00:00 2001 From: Daniel Edwards Date: Tue, 7 Nov 2023 15:32:24 -0500 Subject: [PATCH 047/120] Adds EmptyStateReceipt to MoneyRequestView --- src/components/ReportActionItem/MoneyRequestView.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index aa1813fa6e4d..97455f621421 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -37,6 +37,7 @@ import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import ReceiptEmptyState from '@components/ReceiptEmptyState'; import ReportActionItemImage from './ReportActionItemImage'; const propTypes = { @@ -175,6 +176,11 @@ function MoneyRequestView({report, betas, parentReport, policyCategories, should )} + {!hasReceipt && Permissions.canUseViolations() && ( + + Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.RECEIPT))} /> + + )} Date: Tue, 7 Nov 2023 15:45:32 -0500 Subject: [PATCH 048/120] prettier --- src/components/Icon/Expensicons.js | 2 +- src/components/ReceiptEmptyState.js | 19 ++++++++++--------- .../ReportActionItem/MoneyRequestView.js | 7 +++++-- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/components/Icon/Expensicons.js b/src/components/Icon/Expensicons.js index e5f4d158be1a..3d4f0edb1656 100644 --- a/src/components/Icon/Expensicons.js +++ b/src/components/Icon/Expensicons.js @@ -45,8 +45,8 @@ import Download from '@assets/images/download.svg'; import DragAndDrop from '@assets/images/drag-and-drop.svg'; import DragHandles from '@assets/images/drag-handles.svg'; import Emoji from '@assets/images/emoji.svg'; -import EmptyStateRoutePending from '@assets/images/emptystate__routepending.svg'; import EmptyStateAttachReceipt from '@assets/images/empty-state__attach-receipt.svg'; +import EmptyStateRoutePending from '@assets/images/emptystate__routepending.svg'; import EReceiptIcon from '@assets/images/eReceiptIcon.svg'; import Exclamation from '@assets/images/exclamation.svg'; import Exit from '@assets/images/exit.svg'; diff --git a/src/components/ReceiptEmptyState.js b/src/components/ReceiptEmptyState.js index 31edb4842389..f32083c650d9 100644 --- a/src/components/ReceiptEmptyState.js +++ b/src/components/ReceiptEmptyState.js @@ -1,11 +1,10 @@ -import React from 'react'; import PropTypes from 'prop-types'; -import { View } from 'react-native'; +import React from 'react'; +import {View} from 'react-native'; import styles from '@styles/styles'; import variables from '@styles/variables'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; - import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; const propTypes = { @@ -14,20 +13,22 @@ const propTypes = { /** Callback to be called on onPress */ onPress: PropTypes.func, -} +}; const defaultProps = { hasError: false, onPress: undefined, -} - +}; // Create a component with the above instructions: function ReceiptEmptyState({hasError, onPress}) { return ( - - + + - ) + ); } ReceiptEmptyState.displayName = 'ReceiptEmptyState'; diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index 97455f621421..a9ae4d85e426 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -8,6 +8,7 @@ import categoryPropTypes from '@components/categoryPropTypes'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import ReceiptEmptyState from '@components/ReceiptEmptyState'; import SpacerView from '@components/SpacerView'; import Switch from '@components/Switch'; import tagPropTypes from '@components/tagPropTypes'; @@ -37,7 +38,6 @@ import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import ReceiptEmptyState from '@components/ReceiptEmptyState'; import ReportActionItemImage from './ReportActionItemImage'; const propTypes = { @@ -178,7 +178,10 @@ function MoneyRequestView({report, betas, parentReport, policyCategories, should )} {!hasReceipt && Permissions.canUseViolations() && ( - Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.RECEIPT))} /> + Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.RECEIPT))} + /> )} From a25fe7c9ef627118aeee5ae835b4bb19db77d88c Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 8 Nov 2023 15:32:25 +0700 Subject: [PATCH 049/120] app should navigate back to current chat report after closing status modal --- src/pages/settings/Profile/CustomStatus/StatusPage.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/pages/settings/Profile/CustomStatus/StatusPage.js b/src/pages/settings/Profile/CustomStatus/StatusPage.js index 5d7bb11f4537..66cb17c24161 100644 --- a/src/pages/settings/Profile/CustomStatus/StatusPage.js +++ b/src/pages/settings/Profile/CustomStatus/StatusPage.js @@ -50,7 +50,15 @@ function StatusPage({draftStatus, currentUserPersonalDetails}) { User.clearDraftCustomStatus(); }; - const navigateBackToSettingsPage = useCallback(() => Navigation.goBack(ROUTES.SETTINGS_PROFILE, false, true), []); + const topMostReportID = Navigation.getTopmostReportId(); + const navigateBackToSettingsPage = useCallback(() => { + if (topMostReportID) { + Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(topMostReportID)); + } else { + Navigation.goBack(ROUTES.SETTINGS_PROFILE, false, true); + } + }, [topMostReportID]); + const updateStatus = useCallback(() => { User.updateCustomStatus({text: defaultText, emojiCode: defaultEmoji}); From faa6cde93a30d3cd87bead35ac451f2371ddfaef Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 8 Nov 2023 16:26:18 +0700 Subject: [PATCH 050/120] fix lint issue --- src/pages/settings/Profile/CustomStatus/StatusPage.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/settings/Profile/CustomStatus/StatusPage.js b/src/pages/settings/Profile/CustomStatus/StatusPage.js index 66cb17c24161..72501c58d790 100644 --- a/src/pages/settings/Profile/CustomStatus/StatusPage.js +++ b/src/pages/settings/Profile/CustomStatus/StatusPage.js @@ -51,6 +51,7 @@ function StatusPage({draftStatus, currentUserPersonalDetails}) { }; const topMostReportID = Navigation.getTopmostReportId(); + const navigateBackToSettingsPage = useCallback(() => { if (topMostReportID) { Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(topMostReportID)); From 280909e3327a95d775765024e4dfbf7eadee312b Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Wed, 8 Nov 2023 11:38:44 +0100 Subject: [PATCH 051/120] [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 052/120] 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 053/120] 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 f4afdb42aae6cdd44a552454562c7be0e610836e Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Wed, 8 Nov 2023 15:53:16 +0100 Subject: [PATCH 054/120] 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 dfa86a01e17d73cb543f15d3eb31753c3e59a7b4 Mon Sep 17 00:00:00 2001 From: Daniel Edwards Date: Wed, 8 Nov 2023 10:48:29 -0500 Subject: [PATCH 055/120] Removed extra SVG and updated fill color --- src/components/ReceiptEmptyState.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/ReceiptEmptyState.js b/src/components/ReceiptEmptyState.js index f32083c650d9..01c1b8a484f5 100644 --- a/src/components/ReceiptEmptyState.js +++ b/src/components/ReceiptEmptyState.js @@ -33,9 +33,8 @@ function ReceiptEmptyState({hasError, onPress}) { src={Expensicons.EmptyStateAttachReceipt} width={variables.iconSizeUltraLarge} height={variables.iconSizeUltraLarge} - fill="#DC80E4" + fill="transparent" /> - ); From bc3181ae12a98fd29add7af9e51847c6cc18a08a Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Wed, 8 Nov 2023 18:06:05 +0100 Subject: [PATCH 056/120] 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 057/120] 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 058/120] 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 059/120] 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 c1ba54297a86b994c3fb6d117a3c7007aa82cdec Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Thu, 9 Nov 2023 12:03:36 +0100 Subject: [PATCH 060/120] 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 061/120] 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 062/120] 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 063/120] 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 3b7199be403bfc9ca5ea9f8fb65feeb7bc770fed Mon Sep 17 00:00:00 2001 From: Daniel Edwards Date: Thu, 9 Nov 2023 15:17:33 -0500 Subject: [PATCH 064/120] Update src/components/ReceiptEmptyState.js Co-authored-by: Carlos Alvarez --- src/components/ReceiptEmptyState.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReceiptEmptyState.js b/src/components/ReceiptEmptyState.js index 01c1b8a484f5..4275aa8739c0 100644 --- a/src/components/ReceiptEmptyState.js +++ b/src/components/ReceiptEmptyState.js @@ -8,7 +8,7 @@ import * as Expensicons from './Icon/Expensicons'; import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; const propTypes = { - /** Whether or not there is a violation error */ + /** Whether or not there is an error */ hasError: PropTypes.bool, /** Callback to be called on onPress */ From 3d11e236923525bd3887ff5e00c720cc32c51de2 Mon Sep 17 00:00:00 2001 From: Daniel Edwards Date: Thu, 9 Nov 2023 15:17:52 -0500 Subject: [PATCH 065/120] Update src/components/ReceiptEmptyState.js Co-authored-by: Carlos Alvarez --- src/components/ReceiptEmptyState.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReceiptEmptyState.js b/src/components/ReceiptEmptyState.js index 4275aa8739c0..e3e888cbf74b 100644 --- a/src/components/ReceiptEmptyState.js +++ b/src/components/ReceiptEmptyState.js @@ -17,7 +17,7 @@ const propTypes = { const defaultProps = { hasError: false, - onPress: undefined, + onPress: () => {}, }; // Create a component with the above instructions: From 162572c2f0a36b22207087b26c34c229f2f05e5b Mon Sep 17 00:00:00 2001 From: Daniel Edwards Date: Thu, 9 Nov 2023 15:25:36 -0500 Subject: [PATCH 066/120] Updates comment for component --- src/components/ReceiptEmptyState.js | 3 +-- src/components/ReportActionItem/MoneyRequestView.js | 10 ++++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/components/ReceiptEmptyState.js b/src/components/ReceiptEmptyState.js index e3e888cbf74b..205dd83dd42d 100644 --- a/src/components/ReceiptEmptyState.js +++ b/src/components/ReceiptEmptyState.js @@ -20,8 +20,7 @@ const defaultProps = { onPress: () => {}, }; -// Create a component with the above instructions: - +// Returns an SVG icon indicating that the user should attach a receipt function ReceiptEmptyState({hasError, onPress}) { return ( )} {!hasReceipt && Permissions.canUseViolations() && ( - - Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.RECEIPT))} - /> - + Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.RECEIPT))} + /> )} Date: Thu, 9 Nov 2023 20:14:56 +0100 Subject: [PATCH 067/120] Reset map boundaries on resize --- src/components/MapView/MapView.web.tsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/components/MapView/MapView.web.tsx b/src/components/MapView/MapView.web.tsx index d706168f076f..8442202b5ca8 100644 --- a/src/components/MapView/MapView.web.tsx +++ b/src/components/MapView/MapView.web.tsx @@ -19,9 +19,10 @@ import utils from './utils'; const MapView = forwardRef( ({style, styleURL, waypoints, mapPadding, accessToken, directionCoordinates, initialState = {location: CONST.MAPBOX.DEFAULT_COORDINATE, zoom: CONST.MAPBOX.DEFAULT_ZOOM}}, ref) => { const [mapRef, setMapRef] = useState(null); + const [shouldResetBoundaries, setShouldResetBoundaries] = useState(false); const setRef = useCallback((newRef: MapRef | null) => setMapRef(newRef), []); - useEffect(() => { + const resetBoundaries = useCallback(() => { if (!waypoints || waypoints.length === 0) { return; } @@ -47,6 +48,19 @@ const MapView = forwardRef( map.fitBounds([northEast, southWest], {padding: mapPadding}); }, [waypoints, mapRef, mapPadding, directionCoordinates]); + // Reset boundaries when waypoints change + useEffect(resetBoundaries, [resetBoundaries]); + + useEffect(() => { + if (!shouldResetBoundaries) { + return; + } + + resetBoundaries(); + setShouldResetBoundaries(false); + // eslint-disable-next-line react-hooks/exhaustive-deps -- this effect only needs to run when the boundaries reset is forced + }, [shouldResetBoundaries]); + useEffect(() => { if (!mapRef) { return; @@ -54,6 +68,7 @@ const MapView = forwardRef( const resizeObserver = new ResizeObserver(() => { mapRef.resize(); + setShouldResetBoundaries(true); }); resizeObserver.observe(mapRef.getContainer()); From 69368d39ebd2e91838bfcfc3eb88dee3d03f3be1 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Fri, 10 Nov 2023 15:59:02 +0700 Subject: [PATCH 068/120] update usecallback function --- src/pages/settings/Profile/CustomStatus/StatusPage.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pages/settings/Profile/CustomStatus/StatusPage.js b/src/pages/settings/Profile/CustomStatus/StatusPage.js index 72501c58d790..e00975a675c0 100644 --- a/src/pages/settings/Profile/CustomStatus/StatusPage.js +++ b/src/pages/settings/Profile/CustomStatus/StatusPage.js @@ -50,15 +50,14 @@ function StatusPage({draftStatus, currentUserPersonalDetails}) { User.clearDraftCustomStatus(); }; - const topMostReportID = Navigation.getTopmostReportId(); - const navigateBackToSettingsPage = useCallback(() => { + const topMostReportID = Navigation.getTopmostReportId(); if (topMostReportID) { Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(topMostReportID)); } else { Navigation.goBack(ROUTES.SETTINGS_PROFILE, false, true); } - }, [topMostReportID]); + }, []); const updateStatus = useCallback(() => { User.updateCustomStatus({text: defaultText, emojiCode: defaultEmoji}); From 2a52cfad42b4ec4b371f609d396f7199239e1d41 Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Fri, 10 Nov 2023 10:23:34 +0100 Subject: [PATCH 069/120] `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 ba75ff99bdef26ff2af65cbb535e4a683f871908 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 10 Nov 2023 18:46:06 +0700 Subject: [PATCH 070/120] 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 37fafc7a37d588ab09ee0b4c32d1eea021b6e052 Mon Sep 17 00:00:00 2001 From: rory Date: Fri, 10 Nov 2023 10:24:01 -0800 Subject: [PATCH 071/120] Migrate withTheme to typescript --- src/components/withTheme.js | 45 ------------------------------------ src/components/withTheme.tsx | 26 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 45 deletions(-) delete mode 100644 src/components/withTheme.js create mode 100644 src/components/withTheme.tsx diff --git a/src/components/withTheme.js b/src/components/withTheme.js deleted file mode 100644 index 1d8af53de01d..000000000000 --- a/src/components/withTheme.js +++ /dev/null @@ -1,45 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import getComponentDisplayName from '@libs/getComponentDisplayName'; -import useTheme from '@styles/themes/useTheme'; -import refPropTypes from './refPropTypes'; - -const withThemePropTypes = { - theme: PropTypes.object.isRequired, -}; - -export default function withTheme(WrappedComponent) { - function WithTheme(props) { - const theme = useTheme(); - return ( - - ); - } - - WithTheme.displayName = `withTheme(${getComponentDisplayName(WrappedComponent)})`; - WithTheme.propTypes = { - forwardedRef: refPropTypes, - }; - WithTheme.defaultProps = { - forwardedRef: () => {}, - }; - - const WithThemeWithRef = React.forwardRef((props, ref) => ( - - )); - - WithThemeWithRef.displayName = `WithThemeWithRef`; - - return WithThemeWithRef; -} - -export {withThemePropTypes}; diff --git a/src/components/withTheme.tsx b/src/components/withTheme.tsx new file mode 100644 index 000000000000..61682fd344ec --- /dev/null +++ b/src/components/withTheme.tsx @@ -0,0 +1,26 @@ +import React, {ComponentType, ForwardedRef, forwardRef, ReactElement, RefAttributes} from 'react'; +import getComponentDisplayName from '@libs/getComponentDisplayName'; +import {ThemeColors} from '@styles/themes/types'; +import useTheme from '@styles/themes/useTheme'; + +type ThemeProps = {theme: ThemeColors}; + +export default function withTheme( + WrappedComponent: ComponentType>, +): (props: Omit & React.RefAttributes) => ReactElement | null { + function WithTheme(props: Omit, ref: ForwardedRef): ReactElement { + const theme = useTheme(); + return ( + + ); + } + + WithTheme.displayName = `withTheme(${getComponentDisplayName(WrappedComponent)})`; + + return forwardRef(WithTheme); +} From a32833d5ec1157bc8bb000890661c785a9569b84 Mon Sep 17 00:00:00 2001 From: rory Date: Fri, 10 Nov 2023 10:28:50 -0800 Subject: [PATCH 072/120] Migrate withThemeStyles to typescript --- src/components/withThemeStyles.js | 45 ------------------------------ src/components/withThemeStyles.tsx | 26 +++++++++++++++++ 2 files changed, 26 insertions(+), 45 deletions(-) delete mode 100644 src/components/withThemeStyles.js create mode 100644 src/components/withThemeStyles.tsx diff --git a/src/components/withThemeStyles.js b/src/components/withThemeStyles.js deleted file mode 100644 index 533efa79a580..000000000000 --- a/src/components/withThemeStyles.js +++ /dev/null @@ -1,45 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import getComponentDisplayName from '@libs/getComponentDisplayName'; -import useThemeStyles from '@styles/useThemeStyles'; -import refPropTypes from './refPropTypes'; - -const withThemeStylesPropTypes = { - themeStyles: PropTypes.object.isRequired, -}; - -export default function withThemeStyles(WrappedComponent) { - function WithThemeStyles(props) { - const themeStyles = useThemeStyles(); - return ( - - ); - } - - WithThemeStyles.displayName = `withThemeStyles(${getComponentDisplayName(WrappedComponent)})`; - WithThemeStyles.propTypes = { - forwardedRef: refPropTypes, - }; - WithThemeStyles.defaultProps = { - forwardedRef: () => {}, - }; - - const WithThemeStylesWithRef = React.forwardRef((props, ref) => ( - - )); - - WithThemeStylesWithRef.displayName = `WithThemeStylesWithRef`; - - return WithThemeStylesWithRef; -} - -export {withThemeStylesPropTypes}; diff --git a/src/components/withThemeStyles.tsx b/src/components/withThemeStyles.tsx new file mode 100644 index 000000000000..25590667e1db --- /dev/null +++ b/src/components/withThemeStyles.tsx @@ -0,0 +1,26 @@ +import React, {ComponentType, ForwardedRef, forwardRef, ReactElement, RefAttributes} from 'react'; +import getComponentDisplayName from '@libs/getComponentDisplayName'; +import type {Styles} from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; + +type ThemeStylesProps = {themeStyles: Styles}; + +export default function withThemeStyles( + WrappedComponent: ComponentType>, +): (props: Omit & React.RefAttributes) => ReactElement | null { + function WithThemeStyles(props: Omit, ref: ForwardedRef): ReactElement { + const themeStyles = useThemeStyles(); + return ( + + ); + } + + WithThemeStyles.displayName = `withThemeStyles(${getComponentDisplayName(WrappedComponent)})`; + + return forwardRef(WithThemeStyles); +} From 2e761583260d42be68fc7293a05e1c40b16ad0ab Mon Sep 17 00:00:00 2001 From: rory Date: Fri, 10 Nov 2023 10:36:47 -0800 Subject: [PATCH 073/120] Use exact shape of styles for context type --- src/components/withThemeStyles.tsx | 4 ++-- src/styles/ThemeStylesContext.ts | 4 ++-- src/styles/ThemeStylesProvider.tsx | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/withThemeStyles.tsx b/src/components/withThemeStyles.tsx index 25590667e1db..07b707fa0ec4 100644 --- a/src/components/withThemeStyles.tsx +++ b/src/components/withThemeStyles.tsx @@ -1,9 +1,9 @@ import React, {ComponentType, ForwardedRef, forwardRef, ReactElement, RefAttributes} from 'react'; import getComponentDisplayName from '@libs/getComponentDisplayName'; -import type {Styles} from '@styles/styles'; +import styles from '@styles/styles'; import useThemeStyles from '@styles/useThemeStyles'; -type ThemeStylesProps = {themeStyles: Styles}; +type ThemeStylesProps = {themeStyles: typeof styles}; export default function withThemeStyles( WrappedComponent: ComponentType>, diff --git a/src/styles/ThemeStylesContext.ts b/src/styles/ThemeStylesContext.ts index 3df2b19b31bf..1c81ab3b39a5 100644 --- a/src/styles/ThemeStylesContext.ts +++ b/src/styles/ThemeStylesContext.ts @@ -1,6 +1,6 @@ import React from 'react'; -import styles, {type Styles} from './styles'; +import styles from './styles'; -const ThemeStylesContext = React.createContext(styles); +const ThemeStylesContext = React.createContext(styles); export default ThemeStylesContext; diff --git a/src/styles/ThemeStylesProvider.tsx b/src/styles/ThemeStylesProvider.tsx index 7f26422e98ce..af5fe815c9d9 100644 --- a/src/styles/ThemeStylesProvider.tsx +++ b/src/styles/ThemeStylesProvider.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react/jsx-props-no-spreading */ import React, {useMemo} from 'react'; import {stylesGenerator} from './styles'; import useTheme from './themes/useTheme'; From 4b76cb55d5adaefb272177d5b47b6f7123ad9faa Mon Sep 17 00:00:00 2001 From: Daniel Edwards Date: Fri, 10 Nov 2023 16:12:59 -0500 Subject: [PATCH 074/120] Moved styles to Pressable and centered SVG better --- src/components/ReceiptEmptyState.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/components/ReceiptEmptyState.js b/src/components/ReceiptEmptyState.js index 205dd83dd42d..134124017b83 100644 --- a/src/components/ReceiptEmptyState.js +++ b/src/components/ReceiptEmptyState.js @@ -1,6 +1,5 @@ import PropTypes from 'prop-types'; import React from 'react'; -import {View} from 'react-native'; import styles from '@styles/styles'; import variables from '@styles/variables'; import Icon from './Icon'; @@ -26,15 +25,14 @@ function ReceiptEmptyState({hasError, onPress}) { - - - + ); } From 876557576f3fc462700a79b26ffd21ca4861852f Mon Sep 17 00:00:00 2001 From: Daniel Edwards Date: Fri, 10 Nov 2023 16:15:52 -0500 Subject: [PATCH 075/120] Updated Accessibility prop --- src/components/ReceiptEmptyState.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReceiptEmptyState.js b/src/components/ReceiptEmptyState.js index 134124017b83..ef2f6a5b62e4 100644 --- a/src/components/ReceiptEmptyState.js +++ b/src/components/ReceiptEmptyState.js @@ -23,7 +23,7 @@ const defaultProps = { function ReceiptEmptyState({hasError, onPress}) { return ( From f34c1e24730681a453245bb015db43d573dd5287 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 13 Nov 2023 10:40:39 +0700 Subject: [PATCH 076/120] 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 20e80d13a434ac8bc2876394a92cf3c54f557e3a Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 13 Nov 2023 09:28:17 +0100 Subject: [PATCH 077/120] 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 633606e09b3b7809ae48938b8c853b145e923b95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Mon, 13 Nov 2023 06:28:22 -0300 Subject: [PATCH 078/120] Rename file to TS --- src/components/{Badge.js => Badge.tsx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/components/{Badge.js => Badge.tsx} (100%) diff --git a/src/components/Badge.js b/src/components/Badge.tsx similarity index 100% rename from src/components/Badge.js rename to src/components/Badge.tsx From b73218219e1d84e9c71feaffee7ef22a000793b6 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 13 Nov 2023 17:43:50 +0700 Subject: [PATCH 079/120] update conditions --- src/libs/ReportUtils.js | 9 +++++---- src/pages/ReportWelcomeMessagePage.js | 3 +-- src/pages/settings/Report/ReportSettingsPage.js | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index da823f3d28ec..dac27204dea7 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -19,6 +19,7 @@ import linkingConfig from './Navigation/linkingConfig'; import Navigation from './Navigation/Navigation'; import * as NumberUtils from './NumberUtils'; import Permissions from './Permissions'; +import * as PolicyUtils from './PolicyUtils'; import * as ReportActionsUtils from './ReportActionsUtils'; import * as TransactionUtils from './TransactionUtils'; import * as Url from './Url'; @@ -4179,13 +4180,13 @@ function getRoom(type, policyID) { return room; } /** - * We want policy owners and admins to be able to modify the welcome message only when the report is not a thread chat + * We only want policy owners and admins to be able to modify the welcome message, but not in thread chat. * @param {Object} report * @param {Object} policy * @return {Boolean} */ -function checkShouldDisableWelcomeMessage(report, policy) { - return isMoneyRequestReport(report) || isArchivedRoom(report) || !isChatRoom(report) || _.isEmpty(policy) || policy.role !== CONST.POLICY.ROLE.ADMIN || isChatThread(report); +function shouldDisableWelcomeMessage(report, policy) { + return isMoneyRequestReport(report) || isArchivedRoom(report) || !isChatRoom(report) || isChatThread(report) || !PolicyUtils.isPolicyAdmin(policy); } export { @@ -4349,5 +4350,5 @@ export { getReimbursementQueuedActionMessage, getPersonalDetailsForAccountID, getRoom, - checkShouldDisableWelcomeMessage, + shouldDisableWelcomeMessage, }; diff --git a/src/pages/ReportWelcomeMessagePage.js b/src/pages/ReportWelcomeMessagePage.js index 7a233a93fba4..c9bd65a11318 100644 --- a/src/pages/ReportWelcomeMessagePage.js +++ b/src/pages/ReportWelcomeMessagePage.js @@ -13,7 +13,6 @@ import TextInput from '@components/TextInput'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; -import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; import updateMultilineInputRange from '@libs/UpdateMultilineInputRange'; import styles from '@styles/styles'; @@ -81,7 +80,7 @@ function ReportWelcomeMessagePage(props) { includeSafeAreaPaddingBottom={false} testID={ReportWelcomeMessagePage.displayName} > - + Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(props.report.reportID))} diff --git a/src/pages/settings/Report/ReportSettingsPage.js b/src/pages/settings/Report/ReportSettingsPage.js index fb10ad1a90f4..0ede96ed8845 100644 --- a/src/pages/settings/Report/ReportSettingsPage.js +++ b/src/pages/settings/Report/ReportSettingsPage.js @@ -61,8 +61,8 @@ function ReportSettingsPage(props) { const shouldDisableRename = useMemo(() => ReportUtils.shouldDisableRename(report, linkedWorkspace), [report, linkedWorkspace]); const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); - // We want policy owners and admins to be able to modify the welcome message only when the report is not a thread chat - const shouldDisableWelcomeMessage = ReportUtils.checkShouldDisableWelcomeMessage(report, linkedWorkspace); + // We only want policy owners and admins to be able to modify the welcome message, but not in thread chat + const shouldDisableWelcomeMessage = ReportUtils.shouldDisableWelcomeMessage(report, linkedWorkspace); const shouldDisableSettings = _.isEmpty(report) || ReportUtils.isArchivedRoom(report); const shouldShowRoomName = !ReportUtils.isPolicyExpenseChat(report) && !ReportUtils.isChatThread(report); From fa04a3f7702418dc816397a78ba48c155cf0ac91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Mon, 13 Nov 2023 09:33:41 -0300 Subject: [PATCH 080/120] Migrate file to TS --- src/components/Badge.tsx | 62 ++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 37 deletions(-) diff --git a/src/components/Badge.tsx b/src/components/Badge.tsx index 49b330ae37b2..8a0fea75f99a 100644 --- a/src/components/Badge.tsx +++ b/src/components/Badge.tsx @@ -1,79 +1,67 @@ -import PropTypes from 'prop-types'; import React from 'react'; -import {View} from 'react-native'; +import {GestureResponderEvent, PressableStateCallbackType, StyleProp, TextStyle, View, ViewStyle} from 'react-native'; import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; import CONST from '@src/CONST'; import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; import Text from './Text'; -const propTypes = { +type BadgeProps = { /** Is Success type */ - success: PropTypes.bool, + success?: boolean; /** Is Error type */ - error: PropTypes.bool, + error?: boolean; /** Whether badge is clickable */ - pressable: PropTypes.bool, + pressable?: boolean; /** Text to display in the Badge */ - text: PropTypes.string.isRequired, + text: string; /** Text to display in the Badge */ - environment: PropTypes.string, + environment?: string; /** Styles for Badge */ - // eslint-disable-next-line react/forbid-prop-types - badgeStyles: PropTypes.arrayOf(PropTypes.object), + badgeStyles?: StyleProp; /** Styles for Badge Text */ - // eslint-disable-next-line react/forbid-prop-types - textStyles: PropTypes.arrayOf(PropTypes.object), + textStyles?: StyleProp; /** Callback to be called on onPress */ - onPress: PropTypes.func, + onPress: (event?: GestureResponderEvent | KeyboardEvent) => void; }; -const defaultProps = { - success: false, - error: false, - pressable: false, - badgeStyles: [], - textStyles: [], - onPress: undefined, - environment: CONST.ENVIRONMENT.DEV, -}; +function Badge({success = false, error = false, pressable = false, text, environment = CONST.ENVIRONMENT.DEV, badgeStyles, textStyles, onPress = () => {}}: BadgeProps) { + const textColorStyles = success || error ? styles.textWhite : undefined; + const Wrapper = pressable ? PressableWithoutFeedback : View; -function Badge(props) { - const textStyles = props.success || props.error ? styles.textWhite : undefined; - const Wrapper = props.pressable ? PressableWithoutFeedback : View; - const wrapperStyles = ({pressed}) => [ + const wrapperStyles: (state: PressableStateCallbackType) => StyleProp = ({pressed}) => [ styles.badge, styles.ml2, - StyleUtils.getBadgeColorStyle(props.success, props.error, pressed, props.environment === CONST.ENVIRONMENT.ADHOC), - ...props.badgeStyles, + StyleUtils.getBadgeColorStyle(success, error, pressed, environment === CONST.ENVIRONMENT.ADHOC), + badgeStyles, ]; return ( - {props.text} + {text} ); } Badge.displayName = 'Badge'; -Badge.propTypes = propTypes; -Badge.defaultProps = defaultProps; + export default Badge; From 95d237b608b6bd9b5f50f9b9c221615602bbfaf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Mon, 13 Nov 2023 09:51:51 -0300 Subject: [PATCH 081/120] Add useCallback --- src/components/Badge.tsx | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/components/Badge.tsx b/src/components/Badge.tsx index 8a0fea75f99a..2ccd41575073 100644 --- a/src/components/Badge.tsx +++ b/src/components/Badge.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useCallback} from 'react'; import {GestureResponderEvent, PressableStateCallbackType, StyleProp, TextStyle, View, ViewStyle} from 'react-native'; import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; @@ -36,12 +36,10 @@ function Badge({success = false, error = false, pressable = false, text, environ const textColorStyles = success || error ? styles.textWhite : undefined; const Wrapper = pressable ? PressableWithoutFeedback : View; - const wrapperStyles: (state: PressableStateCallbackType) => StyleProp = ({pressed}) => [ - styles.badge, - styles.ml2, - StyleUtils.getBadgeColorStyle(success, error, pressed, environment === CONST.ENVIRONMENT.ADHOC), - badgeStyles, - ]; + const wrapperStyles: (state: PressableStateCallbackType) => StyleProp = useCallback( + ({pressed}) => [styles.badge, styles.ml2, StyleUtils.getBadgeColorStyle(success, error, pressed, environment === CONST.ENVIRONMENT.ADHOC), badgeStyles], + [success, error, environment, badgeStyles], + ); return ( Date: Mon, 13 Nov 2023 19:18:13 +0100 Subject: [PATCH 082/120] 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 39e987519c0386814ff270222210d21e86476cb2 Mon Sep 17 00:00:00 2001 From: Christina Dobrzynski <51066321+Christinadobrzyn@users.noreply.github.com> Date: Mon, 13 Nov 2023 15:00:53 -0700 Subject: [PATCH 083/120] Update The-Expenses-Page.md adding a link to the 'support article' mention in this doc --- .../expense-and-report-features/The-Expenses-Page.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/articles/expensify-classic/expense-and-report-features/The-Expenses-Page.md b/docs/articles/expensify-classic/expense-and-report-features/The-Expenses-Page.md index 42a8a914e5bc..5431355dd790 100644 --- a/docs/articles/expensify-classic/expense-and-report-features/The-Expenses-Page.md +++ b/docs/articles/expensify-classic/expense-and-report-features/The-Expenses-Page.md @@ -66,7 +66,7 @@ A Workspace admin can see Processing, Approved, and Reimbursed expenses as long If employees submit expense reports on a workspace where you are not an admin, you will not have visibility into those expenses. Additionally, if an expense is left unreported, a workspace admin will not be able to see that expense until it’s been added to a report. A Workspace admin can edit the tags and categories on an expense, but if they want to edit the amount, date, or merchant name, the expense will need to be in a Processing state or rejected back to the submitter for changes. -We have more about company card expense reconciliation in this support article. +We have more about company card expense reconciliation in this [support article](https://help.expensify.com/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Reconciliation). ## Can I edit multiple expenses at once? Yes! Select the expenses you want to edit and click **Edit Multiple**. From 95565fec82ea06b43882c3b1e45c0691c59c0d3f Mon Sep 17 00:00:00 2001 From: rory Date: Mon, 13 Nov 2023 23:15:02 -0800 Subject: [PATCH 084/120] Add propTypes exports back to withTheme and withThemeStyles --- src/components/withTheme.tsx | 6 ++++++ src/components/withThemeStyles.tsx | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/components/withTheme.tsx b/src/components/withTheme.tsx index 61682fd344ec..d78742b7036b 100644 --- a/src/components/withTheme.tsx +++ b/src/components/withTheme.tsx @@ -1,8 +1,12 @@ +import PropTypes from 'prop-types'; import React, {ComponentType, ForwardedRef, forwardRef, ReactElement, RefAttributes} from 'react'; import getComponentDisplayName from '@libs/getComponentDisplayName'; import {ThemeColors} from '@styles/themes/types'; import useTheme from '@styles/themes/useTheme'; +const withThemePropTypes = { + theme: PropTypes.object.isRequired, +}; type ThemeProps = {theme: ThemeColors}; export default function withTheme( @@ -24,3 +28,5 @@ export default function withTheme( return forwardRef(WithTheme); } + +export {withThemePropTypes}; diff --git a/src/components/withThemeStyles.tsx b/src/components/withThemeStyles.tsx index 07b707fa0ec4..d95122c3e2ba 100644 --- a/src/components/withThemeStyles.tsx +++ b/src/components/withThemeStyles.tsx @@ -1,8 +1,12 @@ +import PropTypes from 'prop-types'; import React, {ComponentType, ForwardedRef, forwardRef, ReactElement, RefAttributes} from 'react'; import getComponentDisplayName from '@libs/getComponentDisplayName'; import styles from '@styles/styles'; import useThemeStyles from '@styles/useThemeStyles'; +const withThemeStylesPropTypes = { + themeStyles: PropTypes.object.isRequired, +}; type ThemeStylesProps = {themeStyles: typeof styles}; export default function withThemeStyles( @@ -24,3 +28,5 @@ export default function withThemeStyles( return forwardRef(WithThemeStyles); } + +export {withThemeStylesPropTypes}; From f64c2dfc8208e53eb97705130ab6c736b7ad2828 Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Tue, 14 Nov 2023 08:56:38 +0100 Subject: [PATCH 085/120] Update inaccurate comment --- src/components/DisplayNames/DisplayNamesTooltipItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DisplayNames/DisplayNamesTooltipItem.tsx b/src/components/DisplayNames/DisplayNamesTooltipItem.tsx index 8f215fefd71b..45cdb340cc98 100644 --- a/src/components/DisplayNames/DisplayNamesTooltipItem.tsx +++ b/src/components/DisplayNames/DisplayNamesTooltipItem.tsx @@ -8,7 +8,7 @@ import styles from '@styles/styles'; type DisplayNamesTooltipItemProps = { index?: number; - /** The full title of the DisplayNames component (not split up) */ + /** The function to get a distance to shift the tooltip horizontally */ getTooltipShiftX?: (index: number) => number | undefined; /** The Account ID for the tooltip */ From 434e472ded14000ceb318e25176940523b11ed82 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Tue, 14 Nov 2023 09:28:02 +0100 Subject: [PATCH 086/120] 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 087/120] 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 088/120] 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 089/120] 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 4ff98835710b8f5ad8a77319f77e11ac4d924ef3 Mon Sep 17 00:00:00 2001 From: Cheryl W <58196921+CherylWalsh@users.noreply.github.com> Date: Tue, 14 Nov 2023 12:26:11 +0000 Subject: [PATCH 090/120] Update Connect-Company-Cards.md Adding the company card settings resource to the help site --- .../company-cards/Connect-Company-Cards.md | 92 ++++++++++++++++++- 1 file changed, 89 insertions(+), 3 deletions(-) diff --git a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Connect-Company-Cards.md b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Connect-Company-Cards.md index 112c3b9617c9..f2ff837d7638 100644 --- a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Connect-Company-Cards.md +++ b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Connect-Company-Cards.md @@ -1,5 +1,91 @@ --- -title: Connect Company Cards -description: Connect Company Cards +title: Company-Card-Settings.md +description: Company card settings --- -## Resource Coming Soon! +# Overview +Once you’ve imported your company cards via commercial card feed, direct bank feed, or CSV import, the next step is to configure the cards’ settings. + +As a Domain Admin, you can access the company card settings by navigating to Settings > Domains> Domain Name > Company Card > Settings. + +If you cannot access Domains, you will need to request Domain Admin access my the Domain Admin. + +# How to configure company card settings +You can manage company cards and set and adjust the settings from the Domains page by navigating to Settings > Domains > [Domain name] > Settings + +## Reimbursable preference + +You can control how your employees' company card expenses are flagged for reimbursement: + +Force Yes: All expenses will be marked as reimbursable, and employees cannot change this setting. +Force No: All expenses will be marked as non-reimbursable, and employees cannot change this setting. +Do Not Force: Expenses will default to either reimbursable or non-reimbursable (your choice), but employees can adjust if necessary. + +## Liability type + +Choose the liability type that suits your needs: + +Corporate Liability: Users cannot delete company card expenses. +Personal Liability: Users are allowed to delete company card expenses. + +If you update the settings on an existing company card feed, the changes will apply to expenses imported after the date the setting is saved. The update will not affect previously imported expenses. + +## Preferred policy + +Setting a preferred policy for a company card feed will ensure that the imported transactions are added to a report on the policy you set. This setting is useful when members are on multiple policies and need to ensure their company card expenses are reported to a particular policy. + +# How to use Scheduled Submit with company cards +All expenses must be placed on a report if they need to be approved; with Scheduled Submit, you no longer need to worry about the arduous task of employees creating their expenses, adding them to a report, and submitting them manually. All they need to do is SmartScan their receipts and Concierge will take care of the rest, on a variety of schedules that you can set according to your preferences! + +Concierge won't automatically submit expenses on reports that have Expense Violations. Instead, these expenses will be moved to a new report, creating an additional report for the current reporting period. + +An employee can add comments in the Expense Comment field or at the bottom of the report to clarify any details. + +## Enable Scheduled Submit +Scheduled Submit is enabled in the Group Policy by navigating to Settings > Policies > Group > Policy Name > Reports > Scheduled Submit +Use the toggle to enable Scheduled Submit +Choose your desired frequency + +If Scheduled Submit is disabled on the group policy level (or set to a manual frequency), and you have noticed expense reports are still automatically submitted to the group policy, it's likely Scheduled Submit is enabled on the user’s Individual Policy settings. + +# How to connect company cards to an accounting integration + +If you're using a connected accounting system such as NetSuite, Xero, Intacct, Quickbooks Desktop, or QuickBooks Online, you can also connect the card to export to a specific credit card GL account. First, connect the card itself, and once completed, follow the steps below: +Go to Settings > Domains > Domain name > Company Cards +Click Edit Exports on the right-hand side of the card table and select the GL account you want to export expenses to +You're all done. After the account is set, exported expenses will be mapped to the specific account selected when exported by a Domain Admin. + +# How to export company card expenses to a connected accounting integration + +## Pooled GL account + +To export credit card expenses to a pooled GL account: +Go to Settings > Policies > Group > Policy Name > Connections > Accounting Integrations > Configure +Select Credit Card / Charge Card / Bank Transaction as your Non-reimbursable export option. +Please review the Export Settings page for exporting Expense Reports to NetSuite +Select the Vendor/liability account you want to export all non-reimbursable expenses to. + +## Individual GL account + +Go to Settings > Domain > Domain name > Company Cards +Click the Export Settings cog to the right-hand side of the card and select the GL account you want to export expenses to. +You're all done! After the account is set, exported expenses will be mapped to the specific account selected. + +# Deep Dive +## Identifying company card transactions +When you link your credit cards to Expensify, the transactions will appear in each user's account on the Expenses page as soon as they're posted. You can identify transactions from centrally managed cards by seeing the locked card icon next to them. That icon indicates that they’re company card expenses: +[add image here] + +## Importing historical transactions + +After a card is connected via direct connection or via Approved! banks, Expensify will import 30-90 days' worth of historical transactions to your account (the timeframe is based on your bank's discretion). Any historical expenses beyond that date range can be imported using the CSV spreadsheet import method. + +## Using eReceipts +Expensify eReceipts serve as digital substitutes for paper receipts in your purchase transactions, eliminating the necessity to retain physical receipts or utilize SmartScanning receipts. In the case of Expensify Card transactions, eReceipts are automatically generated for all amounts. For other card programs, eReceipts are specifically generated for purchases amounting to $75 or less, provided the transactions are in USD. +To ensure seamless automatic importation, it's essential to maintain your transactions in US Dollars. Additionally, eReceipts can be directly imported from your bank account. Please be aware that CSV/OFX imported files of bank transactions do not support eReceipts. +It's important to note that eReceipts are not generated for lodging expenses. Moreover, due to incomplete or inaccurate category information from certain banks, there may be instances of invalid eReceipts being generated for hotel purchases. If you choose to re-categorize expenses, a similar situation may arise. It's crucial to remember that our Expensify eReceipt Guarantee excludes coverage for hotel and motel expenses. + +# FAQ +## What plan/subscription is required in order to manage corporate cards? +Group Policy (Collect or Control plan only) +## When do my company card transactions import to Expensify? +Credit card transactions are imported to Expensify once they’re posted to the bank account. This usually takes 1-3 business days between the point of purchase and when the transactions populate in your account. From b082a9a2fc0ef64c1fb770631f69ef95f4a2fb3b Mon Sep 17 00:00:00 2001 From: rory Date: Tue, 14 Nov 2023 08:12:44 -0800 Subject: [PATCH 091/120] Remove erronious use of stylesGenerator in ListBoundaryLoader --- .../home/report/ListBoundaryLoader/ListBoundaryLoader.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ListBoundaryLoader/ListBoundaryLoader.js b/src/pages/home/report/ListBoundaryLoader/ListBoundaryLoader.js index 861f6201a53f..226f26b352c4 100644 --- a/src/pages/home/report/ListBoundaryLoader/ListBoundaryLoader.js +++ b/src/pages/home/report/ListBoundaryLoader/ListBoundaryLoader.js @@ -3,7 +3,7 @@ import React from 'react'; import {ActivityIndicator, View} from 'react-native'; import ReportActionsSkeletonView from '@components/ReportActionsSkeletonView'; import useNetwork from '@hooks/useNetwork'; -import styles, {stylesGenerator} from '@styles/styles'; +import styles from '@styles/styles'; import themeColors from '@styles/themes/default'; import CONST from '@src/CONST'; @@ -56,7 +56,7 @@ function ListBoundaryLoader({type, isLoadingOlderReportActions, isLoadingInitial // applied for a header of the list, i.e. when you scroll to the bottom of the list // the styles for android and the rest components are different that's why we use two different components return ( - + Date: Wed, 15 Nov 2023 10:27:39 +0100 Subject: [PATCH 092/120] Remove a redundant comment Co-authored-by: Santhosh Sellavel <85645967+Santhosh-Sellavel@users.noreply.github.com> --- src/components/MapView/MapView.web.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/MapView/MapView.web.tsx b/src/components/MapView/MapView.web.tsx index 8442202b5ca8..110d24f0c087 100644 --- a/src/components/MapView/MapView.web.tsx +++ b/src/components/MapView/MapView.web.tsx @@ -48,7 +48,6 @@ const MapView = forwardRef( map.fitBounds([northEast, southWest], {padding: mapPadding}); }, [waypoints, mapRef, mapPadding, directionCoordinates]); - // Reset boundaries when waypoints change useEffect(resetBoundaries, [resetBoundaries]); useEffect(() => { From 60f26d3968f16d800873adb53fefd1f46f1da8d0 Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Wed, 15 Nov 2023 11:26:16 +0100 Subject: [PATCH 093/120] 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 094/120] 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 ce0d8611abc49ea05dd6b4f1f686daacba0d6ec9 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 15 Nov 2023 15:28:29 +0100 Subject: [PATCH 095/120] Add test file --- src/styles/test.js | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/styles/test.js diff --git a/src/styles/test.js b/src/styles/test.js new file mode 100644 index 000000000000..ee12b5ed8d53 --- /dev/null +++ b/src/styles/test.js @@ -0,0 +1,3 @@ +const test = 'js'; + +export default test; From ad9d4203963a538dee37d3fdede48747bfbeb98b Mon Sep 17 00:00:00 2001 From: Daniel Edwards Date: Wed, 15 Nov 2023 09:34:42 -0500 Subject: [PATCH 096/120] Updates from review --- src/components/ReportActionItem/MoneyRequestView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index d60ea2c59e03..9ff1e0761594 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -177,7 +177,7 @@ function MoneyRequestView({report, betas, parentReport, policyCategories, should )} - {!hasReceipt && Permissions.canUseViolations() && ( + {!hasReceipt && canEdit && !isSettled && Permissions.canUseViolations() && ( Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.RECEIPT))} 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 097/120] 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}) { From b5112f41490a39293e333d996c9b77f32834be6e Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 15 Nov 2023 15:41:23 +0100 Subject: [PATCH 098/120] Update typecheck --- .github/workflows/typecheck.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index 3e54975433f6..cfeeb397ee77 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -20,3 +20,11 @@ jobs: run: npm run typecheck env: CI: true + + - name: Check for new JavaScript files + run: | + count_new_js=$(git diff --name-only --diff-filter=A main -- '*.{js,jsx}' | grep -cE 'src/libs|src/hooks|src/styles|src/languages') + if [ "$count_new_js" -gt "0" ]; then + echo "ERROR: Found new JS or JSX files in the /src/libs, /src/hooks, /src/styles, or /src/languages directories." + exit 1 + fi \ No newline at end of file From 59a08831e69bbdbb628be1b4116f530abc07aaa2 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 15 Nov 2023 15:44:40 +0100 Subject: [PATCH 099/120] Add js to typecheck to run checks also when only ts is added --- .github/workflows/typecheck.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index cfeeb397ee77..1e0373db0687 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -5,7 +5,7 @@ on: pull_request: types: [opened, synchronize] branches-ignore: [staging, production] - paths: ['**.ts', '**.tsx', 'package.json', 'package-lock.json', 'tsconfig.json'] + paths: ['**.js', '**.jsx', '**.ts', '**.tsx', 'package.json', 'package-lock.json', 'tsconfig.json'] jobs: typecheck: From a6699a1a8c3eb1f278db83d3252c8f3cf9bed4fa Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 15 Nov 2023 15:48:37 +0100 Subject: [PATCH 100/120] Fetch main before comparing --- .github/workflows/typecheck.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index 1e0373db0687..e01e87c7cb0f 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -23,6 +23,7 @@ jobs: - name: Check for new JavaScript files run: | + git fetch origin main --no-tags --depth=1 count_new_js=$(git diff --name-only --diff-filter=A main -- '*.{js,jsx}' | grep -cE 'src/libs|src/hooks|src/styles|src/languages') if [ "$count_new_js" -gt "0" ]; then echo "ERROR: Found new JS or JSX files in the /src/libs, /src/hooks, /src/styles, or /src/languages directories." From 12ce31a7c2253a28f893ccade5bd26af89154023 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 15 Nov 2023 16:02:23 +0100 Subject: [PATCH 101/120] Fix the diff filtering --- .github/workflows/typecheck.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index e01e87c7cb0f..fae744b66ba0 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -5,7 +5,7 @@ on: pull_request: types: [opened, synchronize] branches-ignore: [staging, production] - paths: ['**.js', '**.jsx', '**.ts', '**.tsx', 'package.json', 'package-lock.json', 'tsconfig.json'] + paths: ['**.js', '**.ts', '**.tsx', 'package.json', 'package-lock.json', 'tsconfig.json'] jobs: typecheck: @@ -24,8 +24,8 @@ jobs: - name: Check for new JavaScript files run: | git fetch origin main --no-tags --depth=1 - count_new_js=$(git diff --name-only --diff-filter=A main -- '*.{js,jsx}' | grep -cE 'src/libs|src/hooks|src/styles|src/languages') + count_new_js=$(git diff --name-only --diff-filter=A origin/main HEAD -- 'src/libs/*.js' 'src/hooks/*.js' 'src/styles/*.js' 'src/languages/*.js' | wc -l) if [ "$count_new_js" -gt "0" ]; then - echo "ERROR: Found new JS or JSX files in the /src/libs, /src/hooks, /src/styles, or /src/languages directories." + echo "ERROR: Found new JavaScript files in the /src/libs, /src/hooks, /src/styles, or /src/languages directories; use TypeScript instead" exit 1 - fi \ No newline at end of file + fi From e272047e8afaade3e6bf634171b98c9584b8512c Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 15 Nov 2023 16:03:56 +0100 Subject: [PATCH 102/120] Remove test.js --- src/styles/test.js | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 src/styles/test.js diff --git a/src/styles/test.js b/src/styles/test.js deleted file mode 100644 index ee12b5ed8d53..000000000000 --- a/src/styles/test.js +++ /dev/null @@ -1,3 +0,0 @@ -const test = 'js'; - -export default test; From 77b016942fb50d7c8368033abf5fcda639128ecc Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 15 Nov 2023 16:06:00 +0100 Subject: [PATCH 103/120] Add ts file to trigger typecheck --- src/styles/test.ts | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/styles/test.ts diff --git a/src/styles/test.ts b/src/styles/test.ts new file mode 100644 index 000000000000..ee12b5ed8d53 --- /dev/null +++ b/src/styles/test.ts @@ -0,0 +1,3 @@ +const test = 'js'; + +export default test; From 43f0db54b68d9058ad6aa6a4ffcb5591ecbd7c9c Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 15 Nov 2023 16:08:20 +0100 Subject: [PATCH 104/120] Revert "Add ts file to trigger typecheck" This reverts commit 77b016942fb50d7c8368033abf5fcda639128ecc. --- src/styles/test.ts | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 src/styles/test.ts diff --git a/src/styles/test.ts b/src/styles/test.ts deleted file mode 100644 index ee12b5ed8d53..000000000000 --- a/src/styles/test.ts +++ /dev/null @@ -1,3 +0,0 @@ -const test = 'js'; - -export default test; From f604d1f016190497def00c1407e8048e189f08e9 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 15 Nov 2023 16:49:13 +0100 Subject: [PATCH 105/120] Test modifying JS file --- src/libs/ComposerFocusManager.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/ComposerFocusManager.js b/src/libs/ComposerFocusManager.js index 569e165da962..8c2ba092a1c2 100644 --- a/src/libs/ComposerFocusManager.js +++ b/src/libs/ComposerFocusManager.js @@ -6,12 +6,14 @@ function resetReadyToFocus() { resolveIsReadyToFocus = resolve; }); } + function setReadyToFocus() { if (!resolveIsReadyToFocus) { return; } resolveIsReadyToFocus(); } + function isReadyToFocus() { return isReadyToFocusPromise; } From b8b60a86df4a8b8cd06e25bb2b323b4c294377e8 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 15 Nov 2023 16:56:02 +0100 Subject: [PATCH 106/120] Revert "Test modifying JS file" This reverts commit f604d1f016190497def00c1407e8048e189f08e9. --- src/libs/ComposerFocusManager.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libs/ComposerFocusManager.js b/src/libs/ComposerFocusManager.js index 8c2ba092a1c2..569e165da962 100644 --- a/src/libs/ComposerFocusManager.js +++ b/src/libs/ComposerFocusManager.js @@ -6,14 +6,12 @@ function resetReadyToFocus() { resolveIsReadyToFocus = resolve; }); } - function setReadyToFocus() { if (!resolveIsReadyToFocus) { return; } resolveIsReadyToFocus(); } - function isReadyToFocus() { return isReadyToFocusPromise; } From 1c1b7399dd46be47ab1e8492d65fb7fbabdeb1a9 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 15 Nov 2023 16:59:40 +0100 Subject: [PATCH 107/120] Test renaming JS file --- src/components/Modal/BaseModal.js | 2 +- src/components/Modal/index.android.js | 2 +- .../{ComposerFocusManager.js => ComposerFocusManagerRename.js} | 0 src/libs/focusComposerWithDelay.ts | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename src/libs/{ComposerFocusManager.js => ComposerFocusManagerRename.js} (100%) diff --git a/src/components/Modal/BaseModal.js b/src/components/Modal/BaseModal.js index bf1fdc8ee7de..e732a92380ba 100644 --- a/src/components/Modal/BaseModal.js +++ b/src/components/Modal/BaseModal.js @@ -5,7 +5,7 @@ import ReactNativeModal from 'react-native-modal'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; import usePrevious from '@hooks/usePrevious'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import ComposerFocusManager from '@libs/ComposerFocusManager'; +import ComposerFocusManager from '@libs/ComposerFocusManagerRename'; import useNativeDriver from '@libs/useNativeDriver'; import getModalStyles from '@styles/getModalStyles'; import styles from '@styles/styles'; diff --git a/src/components/Modal/index.android.js b/src/components/Modal/index.android.js index 51745ae6a20f..952aa66a5ea9 100644 --- a/src/components/Modal/index.android.js +++ b/src/components/Modal/index.android.js @@ -1,7 +1,7 @@ import React from 'react'; import {AppState} from 'react-native'; import withWindowDimensions from '@components/withWindowDimensions'; -import ComposerFocusManager from '@libs/ComposerFocusManager'; +import ComposerFocusManager from '@libs/ComposerFocusManagerRename'; import BaseModal from './BaseModal'; import {defaultProps, propTypes} from './modalPropTypes'; diff --git a/src/libs/ComposerFocusManager.js b/src/libs/ComposerFocusManagerRename.js similarity index 100% rename from src/libs/ComposerFocusManager.js rename to src/libs/ComposerFocusManagerRename.js diff --git a/src/libs/focusComposerWithDelay.ts b/src/libs/focusComposerWithDelay.ts index 19f1050d24bd..788e1025c06b 100644 --- a/src/libs/focusComposerWithDelay.ts +++ b/src/libs/focusComposerWithDelay.ts @@ -1,6 +1,6 @@ import {TextInput} from 'react-native'; import * as EmojiPickerAction from './actions/EmojiPickerAction'; -import ComposerFocusManager from './ComposerFocusManager'; +import ComposerFocusManager from './ComposerFocusManagerRename'; type FocusComposerWithDelay = (shouldDelay?: boolean) => void; /** From 550f7a8222245139f1596b60c79748ffadec3dda Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 15 Nov 2023 17:08:16 +0100 Subject: [PATCH 108/120] Rerun workflows From 590d8d7cf39f5136dcc5cbf6b5903ccd18b8493c Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 15 Nov 2023 17:08:47 +0100 Subject: [PATCH 109/120] Revert "Test renaming JS file" This reverts commit 1c1b7399dd46be47ab1e8492d65fb7fbabdeb1a9. --- src/components/Modal/BaseModal.js | 2 +- src/components/Modal/index.android.js | 2 +- .../{ComposerFocusManagerRename.js => ComposerFocusManager.js} | 0 src/libs/focusComposerWithDelay.ts | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename src/libs/{ComposerFocusManagerRename.js => ComposerFocusManager.js} (100%) diff --git a/src/components/Modal/BaseModal.js b/src/components/Modal/BaseModal.js index e732a92380ba..bf1fdc8ee7de 100644 --- a/src/components/Modal/BaseModal.js +++ b/src/components/Modal/BaseModal.js @@ -5,7 +5,7 @@ import ReactNativeModal from 'react-native-modal'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; import usePrevious from '@hooks/usePrevious'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import ComposerFocusManager from '@libs/ComposerFocusManagerRename'; +import ComposerFocusManager from '@libs/ComposerFocusManager'; import useNativeDriver from '@libs/useNativeDriver'; import getModalStyles from '@styles/getModalStyles'; import styles from '@styles/styles'; diff --git a/src/components/Modal/index.android.js b/src/components/Modal/index.android.js index 952aa66a5ea9..51745ae6a20f 100644 --- a/src/components/Modal/index.android.js +++ b/src/components/Modal/index.android.js @@ -1,7 +1,7 @@ import React from 'react'; import {AppState} from 'react-native'; import withWindowDimensions from '@components/withWindowDimensions'; -import ComposerFocusManager from '@libs/ComposerFocusManagerRename'; +import ComposerFocusManager from '@libs/ComposerFocusManager'; import BaseModal from './BaseModal'; import {defaultProps, propTypes} from './modalPropTypes'; diff --git a/src/libs/ComposerFocusManagerRename.js b/src/libs/ComposerFocusManager.js similarity index 100% rename from src/libs/ComposerFocusManagerRename.js rename to src/libs/ComposerFocusManager.js diff --git a/src/libs/focusComposerWithDelay.ts b/src/libs/focusComposerWithDelay.ts index 788e1025c06b..19f1050d24bd 100644 --- a/src/libs/focusComposerWithDelay.ts +++ b/src/libs/focusComposerWithDelay.ts @@ -1,6 +1,6 @@ import {TextInput} from 'react-native'; import * as EmojiPickerAction from './actions/EmojiPickerAction'; -import ComposerFocusManager from './ComposerFocusManagerRename'; +import ComposerFocusManager from './ComposerFocusManager'; type FocusComposerWithDelay = (shouldDelay?: boolean) => void; /** From 813c4c4ac2e2a41bacc8ff5f20fa059b92f4ca91 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 15 Nov 2023 17:09:18 +0100 Subject: [PATCH 110/120] Test renaming JS file --- src/components/Modal/BaseModal.tsx | 2 +- src/components/Modal/index.android.tsx | 2 +- .../{ComposerFocusManager.js => ComposerFocusManagerRename.js} | 0 src/libs/focusComposerWithDelay.ts | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename src/libs/{ComposerFocusManager.js => ComposerFocusManagerRename.js} (100%) diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index e428b062798f..356f7c5baa16 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -4,7 +4,7 @@ import ReactNativeModal from 'react-native-modal'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; import usePrevious from '@hooks/usePrevious'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import ComposerFocusManager from '@libs/ComposerFocusManager'; +import ComposerFocusManager from '@libs/ComposerFocusManagerRename'; import useNativeDriver from '@libs/useNativeDriver'; import getModalStyles from '@styles/getModalStyles'; import styles from '@styles/styles'; diff --git a/src/components/Modal/index.android.tsx b/src/components/Modal/index.android.tsx index 2343cb4c70a9..3486e414d8c2 100644 --- a/src/components/Modal/index.android.tsx +++ b/src/components/Modal/index.android.tsx @@ -1,7 +1,7 @@ import React from 'react'; import {AppState} from 'react-native'; import withWindowDimensions from '@components/withWindowDimensions'; -import ComposerFocusManager from '@libs/ComposerFocusManager'; +import ComposerFocusManager from '@libs/ComposerFocusManagerRename'; import BaseModal from './BaseModal'; import BaseModalProps from './types'; diff --git a/src/libs/ComposerFocusManager.js b/src/libs/ComposerFocusManagerRename.js similarity index 100% rename from src/libs/ComposerFocusManager.js rename to src/libs/ComposerFocusManagerRename.js diff --git a/src/libs/focusComposerWithDelay.ts b/src/libs/focusComposerWithDelay.ts index 19f1050d24bd..788e1025c06b 100644 --- a/src/libs/focusComposerWithDelay.ts +++ b/src/libs/focusComposerWithDelay.ts @@ -1,6 +1,6 @@ import {TextInput} from 'react-native'; import * as EmojiPickerAction from './actions/EmojiPickerAction'; -import ComposerFocusManager from './ComposerFocusManager'; +import ComposerFocusManager from './ComposerFocusManagerRename'; type FocusComposerWithDelay = (shouldDelay?: boolean) => void; /** From 602b95b9ab055b4041d09ad43f710afa0f6bd0e4 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 15 Nov 2023 17:09:30 +0100 Subject: [PATCH 111/120] Revert "Test renaming JS file" This reverts commit 813c4c4ac2e2a41bacc8ff5f20fa059b92f4ca91. --- src/components/Modal/BaseModal.tsx | 2 +- src/components/Modal/index.android.tsx | 2 +- .../{ComposerFocusManagerRename.js => ComposerFocusManager.js} | 0 src/libs/focusComposerWithDelay.ts | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename src/libs/{ComposerFocusManagerRename.js => ComposerFocusManager.js} (100%) diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index 356f7c5baa16..e428b062798f 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -4,7 +4,7 @@ import ReactNativeModal from 'react-native-modal'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; import usePrevious from '@hooks/usePrevious'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import ComposerFocusManager from '@libs/ComposerFocusManagerRename'; +import ComposerFocusManager from '@libs/ComposerFocusManager'; import useNativeDriver from '@libs/useNativeDriver'; import getModalStyles from '@styles/getModalStyles'; import styles from '@styles/styles'; diff --git a/src/components/Modal/index.android.tsx b/src/components/Modal/index.android.tsx index 3486e414d8c2..2343cb4c70a9 100644 --- a/src/components/Modal/index.android.tsx +++ b/src/components/Modal/index.android.tsx @@ -1,7 +1,7 @@ import React from 'react'; import {AppState} from 'react-native'; import withWindowDimensions from '@components/withWindowDimensions'; -import ComposerFocusManager from '@libs/ComposerFocusManagerRename'; +import ComposerFocusManager from '@libs/ComposerFocusManager'; import BaseModal from './BaseModal'; import BaseModalProps from './types'; diff --git a/src/libs/ComposerFocusManagerRename.js b/src/libs/ComposerFocusManager.js similarity index 100% rename from src/libs/ComposerFocusManagerRename.js rename to src/libs/ComposerFocusManager.js diff --git a/src/libs/focusComposerWithDelay.ts b/src/libs/focusComposerWithDelay.ts index 788e1025c06b..19f1050d24bd 100644 --- a/src/libs/focusComposerWithDelay.ts +++ b/src/libs/focusComposerWithDelay.ts @@ -1,6 +1,6 @@ import {TextInput} from 'react-native'; import * as EmojiPickerAction from './actions/EmojiPickerAction'; -import ComposerFocusManager from './ComposerFocusManagerRename'; +import ComposerFocusManager from './ComposerFocusManager'; type FocusComposerWithDelay = (shouldDelay?: boolean) => void; /** From 839c07f65f3aa1f32fb47767708b3500cf1d9597 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 15 Nov 2023 18:12:09 +0100 Subject: [PATCH 112/120] Add a dot at the end of the error message --- .github/workflows/typecheck.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index fae744b66ba0..cdb95bd66779 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -26,6 +26,6 @@ jobs: git fetch origin main --no-tags --depth=1 count_new_js=$(git diff --name-only --diff-filter=A origin/main HEAD -- 'src/libs/*.js' 'src/hooks/*.js' 'src/styles/*.js' 'src/languages/*.js' | wc -l) if [ "$count_new_js" -gt "0" ]; then - echo "ERROR: Found new JavaScript files in the /src/libs, /src/hooks, /src/styles, or /src/languages directories; use TypeScript instead" + echo "ERROR: Found new JavaScript files in the /src/libs, /src/hooks, /src/styles, or /src/languages directories; use TypeScript instead." exit 1 fi From b79391ecbac85fe89dba8470f2d19edf69d75b9a Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 15 Nov 2023 18:12:37 +0100 Subject: [PATCH 113/120] Test adding a file in a nested directory --- src/styles/addOutlineWidth/test.js | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/styles/addOutlineWidth/test.js diff --git a/src/styles/addOutlineWidth/test.js b/src/styles/addOutlineWidth/test.js new file mode 100644 index 000000000000..ef7ebe89ed2c --- /dev/null +++ b/src/styles/addOutlineWidth/test.js @@ -0,0 +1,3 @@ +const x = 'test '; + +export default x; From 73f9715308b6f173b3b51d387fcca586cdea8e23 Mon Sep 17 00:00:00 2001 From: maddylewis <38016013+maddylewis@users.noreply.github.com> Date: Wed, 15 Nov 2023 12:15:01 -0500 Subject: [PATCH 114/120] Delete docs/articles/new-expensify/getting-started/Expensify-Lounge.md Deleting the Lounge article since the Lounge closed --- .../getting-started/Expensify-Lounge.md | 67 ------------------- 1 file changed, 67 deletions(-) delete mode 100644 docs/articles/new-expensify/getting-started/Expensify-Lounge.md diff --git a/docs/articles/new-expensify/getting-started/Expensify-Lounge.md b/docs/articles/new-expensify/getting-started/Expensify-Lounge.md deleted file mode 100644 index bdccbe927769..000000000000 --- a/docs/articles/new-expensify/getting-started/Expensify-Lounge.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -title: Welcome to the Expensify Lounge! -description: How to get the most out of the Expensify Lounge. -redirect_from: articles/other/Expensify-Lounge/ ---- - - -# What is the Expensify Lounge? -The Expensify Lounge is a place where people go to Get Shit Done. It's a beautiful environment with great coffee and a group of people to collaborate with. Check out this guide on how to best utilize the Expensify Lounge! - -# The Two Rules -### Rule #1 - Get Shit Done - -The Lounge is a space for people to get work done. It is optimized to be the perfect environment for you to focus on your work, collaborate with others, and advance your most wild and creative ideas. To make this a reality, we ask our members to keep the following in mind: - -- **#focus** - Use the space for how it was designed and do not distract from others' focus. The space is beautiful, social, and collaborative, but it was created to help our members work effectively. -- **#urgency** - Working remotely is great, but there's nothing like real-time collaboration with your colleagues. Use the lounge to meet with co-workers IRL to continue the progress on whatever it is you're working on. -- **#results** - Don't mistake time for effort or effort for output. Upon arrival, visualize what you want to accomplish, and don't leave until it's done. - -## Rule #2 - Don’t Ruin it for Everyone Else - -We want this place to be incredible, innovative, and always elvoving. To achieve that, we have some general guidelines: - -- **#writeitdown** - If you can help others learn from you, do so. Write a blog post, a document, or a post in Expensify Chat to share with others. This includes making the Expensify Lounge a better space. Feel free to write down any improvements so we can make it better. -- **#showup** - If you are in the lounge, be fully present. Meet others, and collaborate in social rooms. The point is to build a community of people who are focused on getting shit done; you’ll get out what you put in. -- **#oneteam** - Providing an inclusive community is our priority, and we do not tolerate any form of discrimination. Aim to go out of your way to include people who want to be included. -- **#nocreeps** - Do not make people feel uncomfortable with your words or actions. If you are made to feel uncomfortable or notice this happening to someone else, you can use the escalation process outlined in the FAQ section. - -# How to Use the Expensify Lounge -Keeping those two rules in mind, below is a guide on how our members can get the most out of the lounge. - -### Rule #1 - Getting Shit Done -- **Order drinks from Concierge** - [Write Concierge here](https://new.expensify.com/concierge) to ask lounge questions or order beverages. Concierge will bring your order directly to you! -- **Using an office** - Offices are first come, first serve. If an office is open, feel free to use it! Please keep office use to under an hour. We currently do not allow reserving offices. -- **Lounge hours** - The lounge will be open from 8am-6pm PT, Monday through Friday and closed on some major holidays. You can review our Google Maps profile to check our holiday hours. -- **Make the lounge better** - Make any suggestions to improve the lounge experience in [#announce - Expensify Lounge](https://new.expensify.com/r/8292963527436014). - -## Rule #2 - Not Ruining it for Everyone Else -- **Offices are for calls** - Please do not occupy an office unless you have a call or collaborative meeting happening, and don't stay in an office for longer than an hour. -- **Respect other people** - Please do not be too loud or distracting while others are trying to work. While collaborating in Expensify Chat, be respectful of others’ viewpoints and keep a positive environment. -- **Stay home if you’re sick** - If you feel sick, please do not visit the lounge, or consider wearing a mask in public areas. -- **If you see something, say something** - If you are made to feel uncomfortable or witness others being made uncomfortable, let Concierge know. If this is happening in Expensify Chat, use our moderation tools (outlined below in the FAQ) to apply the applicable level of moderation. - -We’re so happy you are here to live rich, have fun, and save the world with us. Now, go enjoy the Expensify Lounge, and let's Get Shit Done! - -# FAQs - -#### What is Concierge? - -Concierge is our automated system that answers member questions in real-time. Questions regarding the local lounge will be routed directly to the lounge's Concierge. You can send Concierge a message if you have a drink request or general questions. They’ll take care of everything for you! - -#### Who is invited to the Expensify Lounge? - -Everyone is invited to the Expensify Lounge! Whether you're an existing customer, or you're someone looking for a great space to Get Shit Done, we'd love to have you. - -#### How do I escalate something that's making me or someone else uncomfortable? - -If you see something in Expensify Chat that should be escalated, you can use the escalation feature to mark a chat as: -- **Spam or Inconsiderate**: This will send a whisper to the sender of the message warning them of the violation, and the message will have a flag applied to it which will be visible to all users. Concierge will not review these flags. -- **Intimidating or Bullying**: The message will be immediately hidden, and the content will be reviewed by our team. After reviewing the message, and it's confirmed intimidation or bullying, the message will be permanently hidden and we'll communicate the violation to the sender of the message. -- **Harassment or Assault**: The message will be immediately hidden and reviewed by our team. The user will be sent a message to warning them of the violation, and Concierge can block the user if that's deemed necessary. - -If you witness something in-person, please write to Concierge referencing which lounge you are in, and they will escalate the issue appropriately. - -#### Where are other Expensify Lounge locations? - -Right now, we only have the San Francisco Lounge, but be on the lookout for more coming soon! From 4acc1df5e27c2142b567d1263b2cb8ed47944f80 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 15 Nov 2023 18:21:28 +0100 Subject: [PATCH 115/120] Revert "Test adding a file in a nested directory" This reverts commit b79391ecbac85fe89dba8470f2d19edf69d75b9a. --- src/styles/addOutlineWidth/test.js | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 src/styles/addOutlineWidth/test.js diff --git a/src/styles/addOutlineWidth/test.js b/src/styles/addOutlineWidth/test.js deleted file mode 100644 index ef7ebe89ed2c..000000000000 --- a/src/styles/addOutlineWidth/test.js +++ /dev/null @@ -1,3 +0,0 @@ -const x = 'test '; - -export default x; From 0b0518dde95d35f786fbf918a3b6e99424e0c6fa Mon Sep 17 00:00:00 2001 From: OSBotify Date: Wed, 15 Nov 2023 20:39:12 +0000 Subject: [PATCH 116/120] Update version to 1.4.0-0 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 4 ++-- ios/NewExpensifyTests/Info.plist | 4 ++-- package-lock.json | 4 ++-- package.json | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index af7f43adad0d..6827448c5053 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -91,8 +91,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001039900 - versionName "1.3.99-0" + versionCode 1001040000 + versionName "1.4.0-0" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index d40d36701731..0de3f7cb2671 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.3.99 + 1.4.0 CFBundleSignature ???? CFBundleURLTypes @@ -40,7 +40,7 @@ CFBundleVersion - 1.3.99.0 + 1.4.0.0 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 4e3ca3ebce6d..cd8b9bd630c8 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.3.99 + 1.4.0 CFBundleSignature ???? CFBundleVersion - 1.3.99.0 + 1.4.0.0 diff --git a/package-lock.json b/package-lock.json index b34cde433719..fae3dbb10ed7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.3.99-0", + "version": "1.4.0-0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.3.99-0", + "version": "1.4.0-0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index e68f10940d6a..b7e2f5ad8515 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.3.99-0", + "version": "1.4.0-0", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From d521dd6783c382297e06a429701ae1936f808041 Mon Sep 17 00:00:00 2001 From: rory Date: Wed, 15 Nov 2023 13:12:33 -0800 Subject: [PATCH 117/120] Run automated theme migration --- src/components/AddPlaidBankAccount.js | 8 ++-- .../AddressSearch/CurrentLocationButton.js | 3 +- src/components/AddressSearch/index.js | 12 +++-- src/components/AmountTextInput.js | 3 +- .../AnchorForAttachmentsOnly/index.native.js | 3 +- .../BaseAnchorForCommentsOnly.js | 3 +- src/components/AnonymousReportFooter.js | 3 +- src/components/ArchivedReportFooter.js | 3 +- src/components/AttachmentModal.js | 8 ++-- .../AttachmentPicker/index.native.js | 3 +- .../AttachmentCarouselCellRenderer.js | 3 +- .../AttachmentCarousel/CarouselButtons.js | 10 ++-- .../AttachmentCarousel/CarouselItem.js | 3 +- .../Pager/ImageTransformer.js | 3 +- .../AttachmentCarousel/Pager/ImageWrapper.js | 3 +- .../AttachmentCarousel/Pager/index.js | 3 +- .../Attachments/AttachmentCarousel/index.js | 3 +- .../AttachmentCarousel/index.native.js | 3 +- .../AttachmentViewImage/index.js | 3 +- .../AttachmentViewImage/index.native.js | 3 +- .../Attachments/AttachmentView/index.js | 8 ++-- .../BaseAutoCompleteSuggestions.js | 3 +- src/components/AutoEmailLink.js | 3 +- src/components/AutoUpdateTime.js | 3 +- src/components/Avatar.js | 14 +++--- .../AvatarCropModal/AvatarCropModal.js | 13 ++++-- .../AvatarCropModal/ImageCropView.js | 3 +- src/components/AvatarCropModal/Slider.js | 3 +- src/components/AvatarSkeleton.js | 7 +-- src/components/AvatarWithDisplayName.js | 8 ++-- src/components/AvatarWithImagePicker.js | 22 +++++---- src/components/AvatarWithIndicator.js | 3 +- src/components/Badge.tsx | 5 +- src/components/Banner.js | 3 +- src/components/BaseMiniContextMenuItem.js | 3 +- src/components/BigNumberPad.js | 3 +- src/components/BlockingViews/BlockingView.js | 10 ++-- .../BlockingViews/FullPageNotFoundView.js | 3 +- src/components/Button/index.js | 14 +++--- src/components/ButtonWithDropdownMenu.js | 8 ++-- src/components/CardPreview.js | 3 +- src/components/CategoryPicker/index.js | 3 +- src/components/Checkbox.js | 8 ++-- src/components/CheckboxWithLabel.js | 3 +- src/components/CommunicationsLink.js | 3 +- src/components/Composer/index.js | 13 ++++-- src/components/ConfirmContent.js | 3 +- src/components/ConfirmationPage.js | 3 +- src/components/ConnectBankAccountButton.js | 3 +- src/components/ContextMenuItem.js | 3 +- src/components/CountrySelector.js | 3 +- src/components/CurrencySymbolButton.js | 3 +- .../index.tsx | 17 +++---- src/components/CurrentWalletBalance.tsx | 3 +- src/components/CustomStatusBar/index.js | 11 +++-- src/components/DatePicker/index.android.js | 3 +- src/components/DatePicker/index.ios.js | 11 +++-- .../DeeplinkRedirectLoadingIndicator.js | 8 ++-- .../DisplayNames/DisplayNamesTooltipItem.tsx | 3 +- .../DisplayNames/DisplayNamesWithTooltip.tsx | 3 +- .../DisplayNamesWithoutTooltip.tsx | 3 +- src/components/DistanceEReceipt.js | 9 ++-- .../DistanceMapView/index.android.js | 3 +- .../DistanceRequest/DistanceRequestFooter.js | 8 ++-- .../DistanceRequestRenderItem.js | 3 +- src/components/DistanceRequest/index.js | 3 +- src/components/DotIndicatorMessage.js | 8 ++-- .../DragAndDrop/NoDropZone/index.tsx | 3 +- src/components/DragAndDrop/Provider/index.tsx | 3 +- src/components/DraggableList/index.native.tsx | 3 +- src/components/DraggableList/index.tsx | 3 +- src/components/EReceipt.js | 3 +- src/components/EReceiptThumbnail.js | 3 +- .../EmojiPicker/CategoryShortcutBar.js | 3 +- .../EmojiPicker/CategoryShortcutButton.js | 8 ++-- src/components/EmojiPicker/EmojiPicker.js | 3 +- .../EmojiPicker/EmojiPickerButton.js | 3 +- .../EmojiPicker/EmojiPickerButtonDropdown.js | 3 +- .../EmojiPickerMenu/index.native.js | 3 +- .../EmojiPicker/EmojiPickerMenuItem/index.js | 19 ++++---- .../EmojiPickerMenuItem/index.native.js | 21 +++++---- .../EmojiPicker/EmojiSkinToneList.js | 3 +- src/components/EmojiSuggestions.tsx | 3 +- src/components/EnvironmentBadge.js | 3 +- src/components/ExceededCommentLength.js | 3 +- src/components/ExpensifyWordmark.js | 8 ++-- src/components/FeatureList.js | 3 +- src/components/FixedFooter.tsx | 3 +- src/components/FloatingActionButton.js | 17 ++++--- src/components/Form.js | 31 +++++++------ src/components/Form/FormWrapper.js | 6 ++- src/components/FormAlertWithSubmitButton.js | 3 +- src/components/FormAlertWrapper.js | 3 +- src/components/FormHelpMessage.js | 9 ++-- src/components/FormScrollView.tsx | 3 +- src/components/FullscreenLoadingIndicator.tsx | 8 ++-- .../GrowlNotificationContainer/index.js | 3 +- .../index.native.js | 3 +- .../HTMLRenderers/AnchorRenderer.js | 3 +- .../HTMLRenderers/EditedRenderer.js | 8 ++-- .../HTMLRenderers/ImageRenderer.js | 3 +- .../HTMLRenderers/MentionUserRenderer.js | 3 +- .../PreRenderer/BasePreRenderer.js | 3 +- src/components/Header.tsx | 3 +- src/components/HeaderGap/index.desktop.js | 7 +-- src/components/HeaderPageLayout.js | 26 ++++++----- src/components/HeaderWithBackButton/index.js | 3 +- src/components/Icon/svgs/LoungeAccessIcon.tsx | 9 ++-- src/components/IllustratedHeaderPageLayout.js | 10 ++-- src/components/ImageView/index.js | 3 +- src/components/ImageView/index.native.js | 12 +++-- src/components/ImageWithSizeCalculation.tsx | 3 +- src/components/Indicator.js | 8 ++-- src/components/InlineCodeBlock/WrappedText.js | 3 +- .../InlineCodeBlock/index.native.js | 3 +- .../InvertedFlatList/index.native.js | 35 +++++++------- .../LHNOptionsList/LHNOptionsList.js | 7 +-- src/components/LHNOptionsList/OptionRowLHN.js | 25 ++++++---- src/components/LocalePicker.js | 8 ++-- .../BaseLocationErrorMessage.js | 3 +- src/components/Lottie/Lottie.tsx | 3 +- src/components/MagicCodeInput.js | 3 +- src/components/MapView/Direction.tsx | 3 +- src/components/MapView/Direction.web.tsx | 3 +- src/components/MentionSuggestions.tsx | 8 ++-- src/components/MessagesRow.js | 3 +- src/components/Modal/BaseModal.tsx | 8 ++-- src/components/Modal/index.tsx | 7 +-- src/components/MoneyReportHeader.js | 3 +- src/components/MoneyReportHeaderStatusBar.js | 3 +- .../MoneyRequestConfirmationList.js | 27 +++++++++-- src/components/MoneyRequestHeader.js | 3 +- src/components/MoneyRequestHeaderStatusBar.js | 3 +- src/components/MoneyRequestSkeletonView.js | 10 ++-- .../NewDatePicker/CalendarPicker/ArrowIcon.js | 3 +- .../CalendarPicker/YearPickerModal.js | 3 +- .../NewDatePicker/CalendarPicker/index.js | 46 ++++++++++++------- src/components/NewDatePicker/index.js | 3 +- src/components/OptionRow.js | 21 +++++---- src/components/OptionsList/BaseOptionsList.js | 3 +- src/components/OptionsListSkeletonView.js | 17 ++++--- src/components/PDFView/PDFInfoMessage.js | 3 +- src/components/PDFView/PDFPasswordForm.js | 3 +- src/components/PDFView/index.native.js | 21 +++++---- src/components/ParentNavigationSubtitle.js | 3 +- src/components/PinButton.js | 8 ++-- src/components/PopoverMenu/index.js | 3 +- src/components/PopoverWithMeasuredContent.js | 13 +++--- src/components/PopoverWithoutOverlay/index.js | 3 +- .../index.js | 3 +- src/components/QRCode.tsx | 20 +++----- src/components/QRShare/index.js | 20 ++++---- src/components/RadioButton.js | 8 ++-- src/components/RadioButtonWithLabel.js | 3 +- src/components/RadioButtons.tsx | 3 +- src/components/Reactions/AddReactionBubble.js | 7 +-- .../Reactions/EmojiReactionBubble.js | 3 +- .../Reactions/MiniQuickEmojiReactions.js | 3 +- .../BaseQuickEmojiReactions.js | 3 +- .../Reactions/ReactionTooltipContent.js | 3 +- .../ReportActionItemEmojiReactions.js | 3 +- src/components/ReceiptEmptyState.js | 3 +- .../ReimbursementAccountLoadingIndicator.js | 3 +- .../ReportActionItem/ChronosOOOListActions.js | 3 +- .../ReportActionItem/MoneyReportView.js | 10 ++-- .../ReportActionItem/MoneyRequestAction.js | 3 +- .../ReportActionItem/MoneyRequestPreview.js | 10 ++-- .../ReportActionItem/MoneyRequestView.js | 9 ++-- .../ReportActionItem/RenameAction.js | 3 +- .../ReportActionItem/ReportActionItemImage.js | 3 +- .../ReportActionItemImages.js | 6 ++- .../ReportActionItem/ReportPreview.js | 10 ++-- src/components/ReportActionItem/TaskAction.js | 3 +- .../ReportActionItem/TaskPreview.js | 3 +- src/components/ReportActionItem/TaskView.js | 3 +- .../SkeletonViewLines.tsx | 10 ++-- src/components/ReportHeaderSkeletonView.js | 10 ++-- src/components/ReportWelcomeText.js | 5 +- src/components/RoomHeaderAvatars.js | 10 ++-- src/components/SafeArea/index.ios.tsx | 3 +- src/components/Section.js | 3 +- src/components/SelectionList/BaseListItem.js | 10 ++-- .../SelectionList/BaseSelectionList.js | 8 ++-- src/components/SelectionList/RadioListItem.js | 3 +- src/components/SelectionList/UserListItem.js | 3 +- .../AppleSignIn/index.desktop.js | 3 +- .../GoogleSignIn/index.desktop.js | 3 +- .../GoogleSignIn/index.website.js | 3 +- src/components/SignInButtons/IconButton.js | 3 +- src/components/SingleOptionSelector.js | 3 +- .../SplashScreenHider/index.native.tsx | 3 +- .../StatePicker/StateSelectorModal.js | 3 +- src/components/StatePicker/index.js | 3 +- src/components/SubscriptAvatar.tsx | 19 +++----- src/components/Switch.tsx | 3 +- src/components/TabSelector/TabIcon.js | 7 +-- src/components/TabSelector/TabLabel.js | 3 +- src/components/TabSelector/TabSelectorItem.js | 3 +- src/components/TagPicker/index.js | 3 +- src/components/TaskHeaderActionButton.js | 3 +- src/components/TestToolMenu.js | 3 +- src/components/TestToolRow.tsx | 3 +- src/components/TestToolsModal.js | 3 +- src/components/Text.tsx | 10 ++-- .../TextInput/BaseTextInput/index.js | 35 +++++++------- .../TextInput/BaseTextInput/index.native.js | 26 ++++++----- .../TextInput/TextInputLabel/index.js | 3 +- .../TextInput/TextInputLabel/index.native.js | 3 +- src/components/TextInput/index.js | 3 +- src/components/TextInput/index.native.js | 3 +- src/components/TextLink.js | 3 +- src/components/TextWithEllipsis/index.tsx | 3 +- src/components/ThreeDotsMenu/index.js | 3 +- src/components/ThumbnailImage.js | 3 +- src/components/UnorderedList.js | 3 +- src/components/UnreadActionIndicator.js | 3 +- .../BaseUserDetailsTooltip.web.js | 25 +++++++++- .../ValidateCode/ExpiredValidateCodeModal.js | 8 ++-- .../ValidateCode/JustSignedInModal.js | 8 ++-- .../ValidateCode/ValidateCodeModal.js | 8 ++-- .../ValuePicker/ValueSelectorModal.js | 3 +- src/components/ValuePicker/index.js | 3 +- .../BaseVideoChatButtonAndMenu.js | 8 ++-- src/components/WalletSection.js | 3 +- src/components/WalletStatementModal/index.js | 3 +- src/components/withToggleVisibilityView.tsx | 3 +- .../Navigation/AppNavigator/AuthScreens.js | 3 +- .../AppNavigator/Navigators/Overlay.js | 9 ++-- .../Navigators/RightModalNavigator.js | 3 +- src/pages/AddPersonalBankAccountPage.js | 3 +- src/pages/DetailsPage.js | 3 +- src/pages/EditRequestCategoryPage.js | 3 +- src/pages/EditRequestCreatedPage.js | 3 +- src/pages/EditRequestDescriptionPage.js | 3 +- src/pages/EditRequestMerchantPage.js | 3 +- src/pages/EditRequestReceiptPage.js | 3 +- src/pages/EditRequestTagPage.js | 3 +- .../EnablePayments/AdditionalDetailsStep.js | 3 +- src/pages/EnablePayments/FailedKYC.js | 3 +- src/pages/EnablePayments/IdologyQuestions.js | 3 +- src/pages/EnablePayments/OnfidoPrivacy.js | 3 +- .../TermsPage/ShortTermsForm.js | 3 +- src/pages/EnablePayments/TermsStep.js | 3 +- src/pages/ErrorPage/ErrorBodyText/index.js | 3 +- src/pages/ErrorPage/GenericErrorPage.js | 10 ++-- src/pages/FlagCommentPage.js | 3 +- src/pages/GetAssistancePage.js | 3 +- src/pages/KeyboardShortcutsPage.js | 3 +- src/pages/LogInWithShortLivedAuthTokenPage.js | 8 ++-- src/pages/NewChatPage.js | 3 +- .../PrivateNotes/PrivateNotesEditPage.js | 3 +- .../PrivateNotes/PrivateNotesListPage.js | 3 +- .../PrivateNotes/PrivateNotesViewPage.js | 3 +- src/pages/ProfilePage.js | 3 +- .../ReimbursementAccount/ACHContractStep.js | 3 +- src/pages/ReimbursementAccount/AddressForm.js | 3 +- .../BankAccountManualStep.js | 3 +- .../BankAccountPlaidStep.js | 3 +- .../ReimbursementAccount/BankAccountStep.js | 11 +++-- src/pages/ReimbursementAccount/CompanyStep.js | 3 +- .../ContinueBankAccountSetup.js | 3 +- .../ReimbursementAccount/Enable2FAPrompt.js | 3 +- src/pages/ReimbursementAccount/EnableStep.js | 3 +- .../ReimbursementAccount/IdentityForm.js | 3 +- .../ReimbursementAccountPage.js | 7 ++- .../RequestorOnfidoStep.js | 3 +- .../ReimbursementAccount/ValidationStep.js | 3 +- src/pages/ReportDetailsPage.js | 3 +- src/pages/ReportParticipantsPage.js | 3 +- src/pages/ReportWelcomeMessagePage.js | 3 +- src/pages/RoomInvitePage.js | 3 +- src/pages/RoomMembersPage.js | 3 +- src/pages/SearchPage.js | 6 ++- src/pages/ShareCodePage.js | 9 ++-- .../TeachersUnite/ImTeacherUpdateEmailPage.js | 3 +- .../TeachersUnite/IntroSchoolPrincipalPage.js | 3 +- src/pages/TeachersUnite/KnowATeacherPage.js | 3 +- src/pages/TeachersUnite/SaveTheWorldPage.js | 8 ++-- src/pages/home/HeaderView.js | 8 ++-- src/pages/home/ReportScreen.js | 7 +-- .../index.android.js | 3 +- .../FloatingMessageCounterContainer/index.js | 3 +- .../report/FloatingMessageCounter/index.js | 9 ++-- src/pages/home/report/LinkPreviewer.js | 8 ++-- .../ListBoundaryLoader/ListBoundaryLoader.js | 8 ++-- src/pages/home/report/ParticipantLocalTime.js | 3 +- .../report/ReactionList/BaseReactionList.js | 3 +- .../report/ReactionList/HeaderReactionList.js | 3 +- .../AttachmentPickerWithMenuItems.js | 3 +- .../ComposerWithSuggestions.js | 8 ++-- .../ReportActionCompose.js | 3 +- .../report/ReportActionCompose/SendButton.js | 8 ++-- .../report/ReportActionItemBasicMessage.js | 3 +- .../home/report/ReportActionItemCreated.js | 3 +- src/pages/home/report/ReportActionItemDate.js | 3 +- .../home/report/ReportActionItemDraft.js | 3 +- .../home/report/ReportActionItemFragment.js | 3 +- .../home/report/ReportActionItemMessage.js | 3 +- .../report/ReportActionItemMessageEdit.js | 8 ++-- .../report/ReportActionItemParentAction.js | 3 +- .../home/report/ReportActionItemThread.js | 3 +- src/pages/home/report/ReportActionsList.js | 7 +-- src/pages/home/report/ReportDropUI.js | 3 +- src/pages/home/report/ReportFooter.js | 3 +- .../home/report/ReportTypingIndicator.js | 3 +- .../comment/AttachmentCommentFragment.js | 3 +- .../report/comment/TextCommentFragment.js | 8 ++-- .../home/sidebar/AvatarWithOptionalStatus.js | 3 +- src/pages/home/sidebar/SidebarLinks.js | 8 ++-- src/pages/home/sidebar/SidebarLinksData.js | 3 +- .../SidebarScreen/BaseSidebarScreen.js | 3 +- .../FloatingActionButtonAndPopover.js | 3 +- src/pages/home/sidebar/SignInButton.js | 3 +- src/pages/iou/MoneyRequestCategoryPage.js | 3 +- src/pages/iou/MoneyRequestDatePage.js | 3 +- src/pages/iou/MoneyRequestDescriptionPage.js | 3 +- src/pages/iou/MoneyRequestMerchantPage.js | 3 +- src/pages/iou/MoneyRequestSelectorPage.js | 3 +- src/pages/iou/MoneyRequestTagPage.js | 3 +- src/pages/iou/ReceiptDropUI.js | 3 +- src/pages/iou/ReceiptSelector/index.js | 13 ++++-- src/pages/iou/ReceiptSelector/index.native.js | 12 +++-- src/pages/iou/SplitBillDetailsPage.js | 3 +- src/pages/iou/WaypointEditor.js | 3 +- src/pages/iou/steps/MoneyRequestAmountForm.js | 3 +- .../iou/steps/MoneyRequestConfirmPage.js | 3 +- .../MoneyRequestParticipantsPage.js | 3 +- .../MoneyRequestParticipantsSelector.js | 3 +- src/pages/iou/steps/NewRequestAmountPage.js | 3 +- src/pages/settings/AboutPage/AboutPage.js | 3 +- src/pages/settings/AppDownloadLinks.js | 3 +- src/pages/settings/InitialSettingsPage.js | 10 ++-- .../settings/Preferences/PreferencesPage.js | 8 ++-- .../settings/Preferences/PriorityModePage.js | 3 +- src/pages/settings/Preferences/ThemePage.js | 3 +- .../Contacts/ContactMethodDetailsPage.js | 27 +++++++---- .../Profile/Contacts/ContactMethodsPage.js | 3 +- .../Profile/Contacts/NewContactMethodPage.js | 3 +- .../ValidateCodeForm/BaseValidateCodeForm.js | 8 ++-- .../Profile/CustomStatus/StatusPage.js | 10 ++-- .../Profile/CustomStatus/StatusSetPage.js | 3 +- src/pages/settings/Profile/DisplayNamePage.js | 3 +- .../settings/Profile/LoungeAccessPage.js | 3 +- .../Profile/PersonalDetails/AddressPage.js | 3 +- .../PersonalDetails/DateOfBirthPage.js | 3 +- .../Profile/PersonalDetails/LegalNamePage.js | 3 +- .../PersonalDetailsInitialPage.js | 3 +- src/pages/settings/Profile/ProfilePage.js | 3 +- src/pages/settings/Profile/PronounsPage.js | 3 +- .../settings/Profile/TimezoneInitialPage.js | 3 +- .../settings/Report/ReportSettingsPage.js | 3 +- src/pages/settings/Report/RoomNamePage.js | 3 +- .../settings/Security/CloseAccountPage.js | 3 +- .../settings/Security/SecuritySettingsPage.js | 8 ++-- .../TwoFactorAuth/StepWrapper/StepWrapper.js | 3 +- .../Security/TwoFactorAuth/Steps/CodesStep.js | 8 ++-- .../TwoFactorAuth/Steps/DisabledStep.js | 3 +- .../TwoFactorAuth/Steps/EnabledStep.js | 8 ++-- .../TwoFactorAuth/Steps/VerifyStep.js | 3 +- .../Wallet/ActivatePhysicalCardPage.js | 8 ++-- src/pages/settings/Wallet/AddDebitCardPage.js | 3 +- .../Wallet/ChooseTransferAccountPage.js | 3 +- .../settings/Wallet/DangerCardSection.js | 3 +- .../settings/Wallet/ExpensifyCardPage.js | 6 ++- .../settings/Wallet/PaymentMethodList.js | 9 ++-- .../settings/Wallet/ReportCardLostPage.js | 3 +- .../Wallet/ReportVirtualCardFraudPage.js | 3 +- .../settings/Wallet/TransferBalancePage.js | 3 +- src/pages/settings/Wallet/WalletEmptyState.js | 5 +- .../settings/Wallet/WalletPage/CardDetails.js | 3 +- .../settings/Wallet/WalletPage/WalletPage.js | 14 ++++-- src/pages/signin/ChangeExpensifyLoginLink.js | 3 +- src/pages/signin/ChooseSSOOrMagicCode.js | 3 +- src/pages/signin/EmailDeliveryFailurePage.js | 3 +- src/pages/signin/Licenses.js | 3 +- src/pages/signin/LoginForm/BaseLoginForm.js | 3 +- src/pages/signin/SAMLSignInPage/index.js | 8 ++-- src/pages/signin/SignInHeroCopy.js | 3 +- src/pages/signin/SignInHeroImage.js | 3 +- src/pages/signin/SignInModal.js | 3 +- src/pages/signin/SignInPage.js | 17 +++---- src/pages/signin/SignInPageHero.js | 3 +- .../BackgroundImage/index.android.js | 3 +- .../SignInPageLayout/BackgroundImage/index.js | 3 +- src/pages/signin/SignInPageLayout/Footer.js | 8 ++-- .../SignInPageLayout/SignInPageContent.js | 3 +- src/pages/signin/SignInPageLayout/index.js | 8 ++-- src/pages/signin/Socials.js | 8 ++-- src/pages/signin/ThirdPartySignInPage.js | 3 +- src/pages/signin/UnlinkLoginForm.js | 3 +- .../ValidateCodeForm/BaseValidateCodeForm.js | 10 ++-- src/pages/tasks/NewTaskDescriptionPage.js | 3 +- src/pages/tasks/NewTaskDetailsPage.js | 3 +- src/pages/tasks/NewTaskPage.js | 3 +- src/pages/tasks/NewTaskTitlePage.js | 3 +- src/pages/tasks/TaskAssigneeSelectorModal.js | 3 +- src/pages/tasks/TaskDescriptionPage.js | 3 +- .../TaskShareDestinationSelectorModal.js | 3 +- src/pages/tasks/TaskTitlePage.js | 3 +- src/pages/workspace/WorkspaceInitialPage.js | 9 ++-- .../workspace/WorkspaceInviteMessagePage.js | 27 ++++++----- src/pages/workspace/WorkspaceInvitePage.js | 5 +- src/pages/workspace/WorkspaceMembersPage.js | 3 +- src/pages/workspace/WorkspaceNewRoomPage.js | 3 +- .../workspace/WorkspacePageWithSections.js | 3 +- .../WorkspaceResetBankAccountModal.js | 3 +- src/pages/workspace/WorkspaceSettingsPage.js | 3 +- src/pages/workspace/WorkspacesListPage.js | 12 +++-- .../bills/WorkspaceBillsFirstSection.js | 3 +- .../bills/WorkspaceBillsNoVBAView.js | 3 +- .../workspace/bills/WorkspaceBillsVBAView.js | 3 +- .../workspace/card/WorkspaceCardNoVBAView.js | 3 +- .../card/WorkspaceCardVBANoECardView.js | 3 +- .../card/WorkspaceCardVBAWithECardView.js | 3 +- .../invoices/WorkspaceInvoicesFirstSection.js | 3 +- .../invoices/WorkspaceInvoicesNoVBAView.js | 3 +- .../invoices/WorkspaceInvoicesVBAView.js | 3 +- .../reimburse/WorkspaceRateAndUnitPage.js | 11 +++-- .../reimburse/WorkspaceReimburseSection.js | 8 ++-- .../reimburse/WorkspaceReimburseView.js | 3 +- .../travel/WorkspaceTravelNoVBAView.js | 3 +- .../travel/WorkspaceTravelVBAView.js | 3 +- 422 files changed, 1465 insertions(+), 881 deletions(-) diff --git a/src/components/AddPlaidBankAccount.js b/src/components/AddPlaidBankAccount.js index 566b6c709423..ec4ddd623929 100644 --- a/src/components/AddPlaidBankAccount.js +++ b/src/components/AddPlaidBankAccount.js @@ -9,8 +9,8 @@ import useNetwork from '@hooks/useNetwork'; import KeyboardShortcut from '@libs/KeyboardShortcut'; import Log from '@libs/Log'; import {plaidDataPropTypes} from '@pages/ReimbursementAccount/plaidDataPropTypes'; -import styles from '@styles/styles'; -import themeColors from '@styles/themes/default'; +import useTheme from '@styles/themes/useTheme'; +import useThemeStyles from '@styles/useThemeStyles'; import * as App from '@userActions/App'; import * as BankAccounts from '@userActions/BankAccounts'; import CONST from '@src/CONST'; @@ -83,6 +83,8 @@ function AddPlaidBankAccount({ allowDebit, isPlaidDisabled, }) { + const theme = useTheme(); + const styles = useThemeStyles(); const subscribedKeyboardShortcuts = useRef([]); const previousNetworkState = useRef(); @@ -186,7 +188,7 @@ function AddPlaidBankAccount({ {lodashGet(plaidData, 'isLoading') && ( diff --git a/src/components/AddressSearch/CurrentLocationButton.js b/src/components/AddressSearch/CurrentLocationButton.js index 326b82d31e8f..3c7feb8fb70c 100644 --- a/src/components/AddressSearch/CurrentLocationButton.js +++ b/src/components/AddressSearch/CurrentLocationButton.js @@ -7,8 +7,8 @@ import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; import useLocalize from '@hooks/useLocalize'; import getButtonState from '@libs/getButtonState'; import colors from '@styles/colors'; -import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; +import useThemeStyles from '@styles/useThemeStyles'; const propTypes = { /** Callback that runs when location button is clicked */ @@ -24,6 +24,7 @@ const defaultProps = { }; function CurrentLocationButton({onPress, isDisabled}) { + const styles = useThemeStyles(); const {translate} = useLocalize(); return ( diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js index 61460a93650e..73472beeb48d 100644 --- a/src/components/AddressSearch/index.js +++ b/src/components/AddressSearch/index.js @@ -14,9 +14,9 @@ import * as ApiUtils from '@libs/ApiUtils'; import compose from '@libs/compose'; import getCurrentPosition from '@libs/getCurrentPosition'; import * as GooglePlacesUtils from '@libs/GooglePlacesUtils'; -import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; -import themeColors from '@styles/themes/default'; +import useTheme from '@styles/themes/useTheme'; +import useThemeStyles from '@styles/useThemeStyles'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import CurrentLocationButton from './CurrentLocationButton'; @@ -144,6 +144,8 @@ const defaultProps = { // Relevant thread: https://expensify.slack.com/archives/C03TQ48KC/p1634088400387400 // Reference: https://github.com/FaridSafi/react-native-google-places-autocomplete/issues/609#issuecomment-886133839 function AddressSearch(props) { + const theme = useTheme(); + const styles = useThemeStyles(); const [displayListViewBorder, setDisplayListViewBorder] = useState(false); const [isTyping, setIsTyping] = useState(false); const [isFocused, setIsFocused] = useState(false); @@ -392,7 +394,7 @@ function AddressSearch(props) { listLoaderComponent={ @@ -489,8 +491,8 @@ function AddressSearch(props) { }} numberOfLines={2} isRowScrollable={false} - listHoverColor={themeColors.border} - listUnderlayColor={themeColors.buttonPressedBG} + listHoverColor={theme.border} + listUnderlayColor={theme.buttonPressedBG} onLayout={(event) => { // We use the height of the element to determine if we should hide the border of the listView dropdown // to prevent a lingering border when there are no address suggestions. diff --git a/src/components/AmountTextInput.js b/src/components/AmountTextInput.js index 43fd5e6a1b98..bd88712432a8 100644 --- a/src/components/AmountTextInput.js +++ b/src/components/AmountTextInput.js @@ -1,6 +1,6 @@ import PropTypes from 'prop-types'; import React from 'react'; -import styles from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import refPropTypes from './refPropTypes'; import TextInput from './TextInput'; @@ -39,6 +39,7 @@ const defaultProps = { }; function AmountTextInput(props) { + const styles = useThemeStyles(); return ( () => { ReportActionContextMenu.hideContextMenu(); diff --git a/src/components/AnonymousReportFooter.js b/src/components/AnonymousReportFooter.js index 2dc4159d1627..387e2ab01930 100644 --- a/src/components/AnonymousReportFooter.js +++ b/src/components/AnonymousReportFooter.js @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import {Text, View} from 'react-native'; import reportPropTypes from '@pages/reportPropTypes'; -import styles from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; import * as Session from '@userActions/Session'; import AvatarWithDisplayName from './AvatarWithDisplayName'; import Button from './Button'; @@ -29,6 +29,7 @@ const defaultProps = { }; function AnonymousReportFooter(props) { + const styles = useThemeStyles(); return ( diff --git a/src/components/ArchivedReportFooter.js b/src/components/ArchivedReportFooter.js index 52484355a242..b1fac827d273 100644 --- a/src/components/ArchivedReportFooter.js +++ b/src/components/ArchivedReportFooter.js @@ -9,7 +9,7 @@ import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import personalDetailsPropType from '@pages/personalDetailsPropType'; import reportPropTypes from '@pages/reportPropTypes'; -import styles from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import Banner from './Banner'; @@ -50,6 +50,7 @@ const defaultProps = { }; function ArchivedReportFooter(props) { + const styles = useThemeStyles(); const archiveReason = lodashGet(props.reportClosedAction, 'originalMessage.reason', CONST.REPORT.ARCHIVE_REASON.DEFAULT); let displayName = PersonalDetailsUtils.getDisplayNameOrDefault(props.personalDetails, [props.report.ownerAccountID, 'displayName']); diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index f82fec156f9f..a541950d063d 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -19,9 +19,9 @@ import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import useNativeDriver from '@libs/useNativeDriver'; import reportPropTypes from '@pages/reportPropTypes'; -import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; -import themeColors from '@styles/themes/default'; +import useTheme from '@styles/themes/useTheme'; +import useThemeStyles from '@styles/useThemeStyles'; import * as IOU from '@userActions/IOU'; import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; @@ -111,6 +111,8 @@ const defaultProps = { }; function AttachmentModal(props) { + const theme = useTheme(); + const styles = useThemeStyles(); const onModalHideCallbackRef = useRef(null); const [isModalOpen, setIsModalOpen] = useState(props.defaultOpen); const [shouldLoadAttachment, setShouldLoadAttachment] = useState(false); @@ -411,7 +413,7 @@ function AttachmentModal(props) { onSubmit={submitAndClose} onClose={closeModal} isVisible={isModalOpen} - backgroundColor={themeColors.componentBG} + backgroundColor={theme.componentBG} onModalShow={() => { props.onModalShow(); setShouldLoadAttachment(true); diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js index 0e723d4cf048..5b955ee69dd3 100644 --- a/src/components/AttachmentPicker/index.native.js +++ b/src/components/AttachmentPicker/index.native.js @@ -14,7 +14,7 @@ import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; import useLocalize from '@hooks/useLocalize'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as FileUtils from '@libs/fileDownload/FileUtils'; -import styles from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import {defaultProps as baseDefaultProps, propTypes as basePropTypes} from './attachmentPickerPropTypes'; import launchCamera from './launchCamera'; @@ -101,6 +101,7 @@ const getDataForUpload = (fileData) => { * @returns {JSX.Element} */ function AttachmentPicker({type, children, shouldHideCameraOption}) { + const styles = useThemeStyles(); const [isVisible, setIsVisible] = useState(false); const completeAttachmentSelection = useRef(); diff --git a/src/components/Attachments/AttachmentCarousel/AttachmentCarouselCellRenderer.js b/src/components/Attachments/AttachmentCarousel/AttachmentCarouselCellRenderer.js index 673bb7c224e2..dd2713a38b2b 100644 --- a/src/components/Attachments/AttachmentCarousel/AttachmentCarouselCellRenderer.js +++ b/src/components/Attachments/AttachmentCarousel/AttachmentCarouselCellRenderer.js @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import {PixelRatio, View} from 'react-native'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import styles from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; const propTypes = { /** Cell Container styles */ @@ -14,6 +14,7 @@ const defaultProps = { }; function AttachmentCarouselCellRenderer(props) { + const styles = useThemeStyles(); const {windowWidth, isSmallScreenWidth} = useWindowDimensions(); const modalStyles = styles.centeredModalStyles(isSmallScreenWidth, true); const style = [props.style, styles.h100, {width: PixelRatio.roundToNearestPixel(windowWidth - (modalStyles.marginHorizontal + modalStyles.borderWidth) * 2)}]; diff --git a/src/components/Attachments/AttachmentCarousel/CarouselButtons.js b/src/components/Attachments/AttachmentCarousel/CarouselButtons.js index f11bbcc9b187..14a6ea268468 100644 --- a/src/components/Attachments/AttachmentCarousel/CarouselButtons.js +++ b/src/components/Attachments/AttachmentCarousel/CarouselButtons.js @@ -8,8 +8,8 @@ import * as Expensicons from '@components/Icon/Expensicons'; import Tooltip from '@components/Tooltip'; import useLocalize from '@hooks/useLocalize'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import styles from '@styles/styles'; -import themeColors from '@styles/themes/default'; +import useTheme from '@styles/themes/useTheme'; +import useThemeStyles from '@styles/useThemeStyles'; const propTypes = { /** Where the arrows should be visible */ @@ -36,6 +36,8 @@ const defaultProps = { }; function CarouselButtons({page, attachments, shouldShowArrows, onBack, onForward, cancelAutoHideArrow, autoHideArrow}) { + const theme = useTheme(); + const styles = useThemeStyles(); const isBackDisabled = page === 0; const isForwardDisabled = page === _.size(attachments) - 1; @@ -51,7 +53,7 @@ function CarouselButtons({page, attachments, shouldShowArrows, onBack, onForward small innerStyles={[styles.arrowIcon]} icon={Expensicons.BackArrow} - iconFill={themeColors.text} + iconFill={theme.text} iconStyles={[styles.mr0]} onPress={onBack} onPressIn={cancelAutoHideArrow} @@ -67,7 +69,7 @@ function CarouselButtons({page, attachments, shouldShowArrows, onBack, onForward small innerStyles={[styles.arrowIcon]} icon={Expensicons.ArrowRight} - iconFill={themeColors.text} + iconFill={theme.text} iconStyles={[styles.mr0]} onPress={onForward} onPressIn={cancelAutoHideArrow} diff --git a/src/components/Attachments/AttachmentCarousel/CarouselItem.js b/src/components/Attachments/AttachmentCarousel/CarouselItem.js index 38f70057be61..b6cc0cbf21a4 100644 --- a/src/components/Attachments/AttachmentCarousel/CarouselItem.js +++ b/src/components/Attachments/AttachmentCarousel/CarouselItem.js @@ -9,7 +9,7 @@ import SafeAreaConsumer from '@components/SafeAreaConsumer'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import ReportAttachmentsContext from '@pages/home/report/ReportAttachmentsContext'; -import styles from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; const propTypes = { @@ -49,6 +49,7 @@ const defaultProps = { }; function CarouselItem({item, isFocused, onPress}) { + const styles = useThemeStyles(); const {translate} = useLocalize(); const {isAttachmentHidden} = useContext(ReportAttachmentsContext); // eslint-disable-next-line es/no-nullish-coalescing-operators diff --git a/src/components/Attachments/AttachmentCarousel/Pager/ImageTransformer.js b/src/components/Attachments/AttachmentCarousel/Pager/ImageTransformer.js index 0839462d4f23..cc1e20cb44e0 100644 --- a/src/components/Attachments/AttachmentCarousel/Pager/ImageTransformer.js +++ b/src/components/Attachments/AttachmentCarousel/Pager/ImageTransformer.js @@ -15,7 +15,7 @@ import Animated, { withDecay, withSpring, } from 'react-native-reanimated'; -import styles from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; import AttachmentCarouselPagerContext from './AttachmentCarouselPagerContext'; import ImageWrapper from './ImageWrapper'; @@ -60,6 +60,7 @@ const imageTransformerDefaultProps = { }; function ImageTransformer({imageWidth, imageHeight, imageScaleX, imageScaleY, scaledImageWidth, scaledImageHeight, isActive, children}) { + const styles = useThemeStyles(); const {canvasWidth, canvasHeight, onTap, onSwipe, onSwipeSuccess, pagerRef, shouldPagerScroll, isScrolling, onPinchGestureChange} = useContext(AttachmentCarouselPagerContext); const minImageScale = useMemo(() => Math.min(imageScaleX, imageScaleY), [imageScaleX, imageScaleY]); diff --git a/src/components/Attachments/AttachmentCarousel/Pager/ImageWrapper.js b/src/components/Attachments/AttachmentCarousel/Pager/ImageWrapper.js index 3a27d80c5509..b0a8b1f0d083 100644 --- a/src/components/Attachments/AttachmentCarousel/Pager/ImageWrapper.js +++ b/src/components/Attachments/AttachmentCarousel/Pager/ImageWrapper.js @@ -2,13 +2,14 @@ import PropTypes from 'prop-types'; import React from 'react'; import {StyleSheet} from 'react-native'; import Animated from 'react-native-reanimated'; -import styles from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; const imageWrapperPropTypes = { children: PropTypes.node.isRequired, }; function ImageWrapper({children}) { + const styles = useThemeStyles(); return ( diff --git a/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.js b/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.js index c024b025c80e..27790121aab0 100644 --- a/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.js +++ b/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.js @@ -3,8 +3,8 @@ import React, {useEffect, useRef} from 'react'; import {FlatList} from 'react-native-gesture-handler'; import Animated, {Easing, FadeOutDown, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated'; import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; -import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; +import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import {propTypes} from './autoCompleteSuggestionsPropTypes'; @@ -29,6 +29,7 @@ const measureHeightOfSuggestionRows = (numRows, isSuggestionPickerLarge) => { }; function BaseAutoCompleteSuggestions(props) { + const styles = useThemeStyles(); const rowHeight = useSharedValue(0); const scrollRef = useRef(null); /** diff --git a/src/components/AutoEmailLink.js b/src/components/AutoEmailLink.js index eece1a16ca5a..bffd2493aa5d 100644 --- a/src/components/AutoEmailLink.js +++ b/src/components/AutoEmailLink.js @@ -2,7 +2,7 @@ import {CONST} from 'expensify-common/lib/CONST'; import PropTypes from 'prop-types'; import React from 'react'; import _ from 'underscore'; -import styles from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; import Text from './Text'; import TextLink from './TextLink'; @@ -22,6 +22,7 @@ const defaultProps = { */ function AutoEmailLink(props) { + const styles = useThemeStyles(); return ( {_.map(props.text.split(CONST.REG_EXP.EXTRACT_EMAIL), (str, index) => { diff --git a/src/components/AutoUpdateTime.js b/src/components/AutoUpdateTime.js index c85f14ed2c29..1970839ec320 100644 --- a/src/components/AutoUpdateTime.js +++ b/src/components/AutoUpdateTime.js @@ -6,7 +6,7 @@ import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; import DateUtils from '@libs/DateUtils'; -import styles from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; import Text from './Text'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; @@ -23,6 +23,7 @@ const propTypes = { }; function AutoUpdateTime(props) { + const styles = useThemeStyles(); /** * @returns {Date} Returns the locale Date object */ diff --git a/src/components/Avatar.js b/src/components/Avatar.js index 5e414486cc70..0052400bf51a 100644 --- a/src/components/Avatar.js +++ b/src/components/Avatar.js @@ -5,9 +5,9 @@ import _ from 'underscore'; import useNetwork from '@hooks/useNetwork'; import * as ReportUtils from '@libs/ReportUtils'; import stylePropTypes from '@styles/stylePropTypes'; -import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; -import themeColors from '@styles/themes/default'; +import useTheme from '@styles/themes/useTheme'; +import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; @@ -55,13 +55,15 @@ const defaultProps = { iconAdditionalStyles: [], containerStyles: [], size: CONST.AVATAR_SIZE.DEFAULT, - fill: themeColors.icon, + fill: undefined, fallbackIcon: Expensicons.FallbackAvatar, type: CONST.ICON_TYPE_AVATAR, name: '', }; function Avatar(props) { + const theme = useTheme(); + const styles = useThemeStyles(); const [imageError, setImageError] = useState(false); useNetwork({onReconnect: () => setImageError(false)}); @@ -84,7 +86,7 @@ function Avatar(props) { const iconStyle = props.imageStyles && props.imageStyles.length ? [StyleUtils.getAvatarStyle(props.size), styles.bgTransparent, ...props.imageStyles] : undefined; - const iconFillColor = isWorkspace ? StyleUtils.getDefaultWorkspaceAvatarColor(props.name).fill : props.fill; + const iconFillColor = isWorkspace ? StyleUtils.getDefaultWorkspaceAvatarColor(props.name).fill : props.fill || theme.icon; const fallbackAvatar = isWorkspace ? ReportUtils.getDefaultWorkspaceAvatar(props.name) : props.fallbackIcon || Expensicons.FallbackAvatar; return ( @@ -95,11 +97,11 @@ function Avatar(props) { src={imageError ? fallbackAvatar : props.source} height={iconSize} width={iconSize} - fill={imageError ? themeColors.offline : iconFillColor} + fill={imageError ? theme.offline : iconFillColor} additionalStyles={[ StyleUtils.getAvatarBorderStyle(props.size, props.type), isWorkspace ? StyleUtils.getDefaultWorkspaceAvatarColor(props.name) : {}, - imageError ? StyleUtils.getBackgroundColorStyle(themeColors.fallbackIconColor) : {}, + imageError ? StyleUtils.getBackgroundColorStyle(theme.fallbackIconColor) : {}, ...props.iconAdditionalStyles, ]} /> diff --git a/src/components/AvatarCropModal/AvatarCropModal.js b/src/components/AvatarCropModal/AvatarCropModal.js index 9b2b92aa9cee..a37f228a0d0d 100644 --- a/src/components/AvatarCropModal/AvatarCropModal.js +++ b/src/components/AvatarCropModal/AvatarCropModal.js @@ -17,9 +17,9 @@ import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; import compose from '@libs/compose'; import cropOrRotateImage from '@libs/cropOrRotateImage'; -import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; -import themeColors from '@styles/themes/default'; +import useTheme from '@styles/themes/useTheme'; +import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import ImageCropView from './ImageCropView'; import Slider from './Slider'; @@ -61,6 +61,8 @@ const defaultProps = { // This component can't be written using class since reanimated API uses hooks. function AvatarCropModal(props) { + const theme = useTheme(); + const styles = useThemeStyles(); const originalImageWidth = useSharedValue(CONST.AVATAR_CROP_MODAL.INITIAL_SIZE); const originalImageHeight = useSharedValue(CONST.AVATAR_CROP_MODAL.INITIAL_SIZE); const translateY = useSharedValue(0); @@ -381,7 +383,7 @@ function AvatarCropModal(props) { {/* To avoid layout shift we should hide this component until the image container & image is initialized */} {!isImageInitialized || !isImageContainerInitialized ? ( @@ -402,8 +404,9 @@ function AvatarCropModal(props) { + diff --git a/src/components/AvatarCropModal/ImageCropView.js b/src/components/AvatarCropModal/ImageCropView.js index cb135cc76c69..a50409da64f4 100644 --- a/src/components/AvatarCropModal/ImageCropView.js +++ b/src/components/AvatarCropModal/ImageCropView.js @@ -6,8 +6,8 @@ import Animated, {interpolate, useAnimatedStyle} from 'react-native-reanimated'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import ControlSelection from '@libs/ControlSelection'; -import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; +import useThemeStyles from '@styles/useThemeStyles'; import gestureHandlerPropTypes from './gestureHandlerPropTypes'; const propTypes = { @@ -50,6 +50,7 @@ const defaultProps = { }; function ImageCropView(props) { + const styles = useThemeStyles(); const containerStyle = StyleUtils.getWidthAndHeightStyle(props.containerSize, props.containerSize); const originalImageHeight = props.originalImageHeight; diff --git a/src/components/AvatarCropModal/Slider.js b/src/components/AvatarCropModal/Slider.js index 4281da1e7b99..9df6ac3c0498 100644 --- a/src/components/AvatarCropModal/Slider.js +++ b/src/components/AvatarCropModal/Slider.js @@ -6,7 +6,7 @@ import Animated, {useAnimatedStyle} from 'react-native-reanimated'; import Tooltip from '@components/Tooltip'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import ControlSelection from '@libs/ControlSelection'; -import styles from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; import gestureHandlerPropTypes from './gestureHandlerPropTypes'; const propTypes = { @@ -26,6 +26,7 @@ const defaultProps = { // This component can't be written using class since reanimated API uses hooks. function Slider(props) { + const styles = useThemeStyles(); const sliderValue = props.sliderValue; const [tooltipIsVisible, setTooltipIsVisible] = useState(true); diff --git a/src/components/AvatarSkeleton.js b/src/components/AvatarSkeleton.js index 2a633833f228..d2706447f756 100644 --- a/src/components/AvatarSkeleton.js +++ b/src/components/AvatarSkeleton.js @@ -1,15 +1,16 @@ import React from 'react'; import {Circle} from 'react-native-svg'; -import themeColors from '@styles/themes/default'; +import useTheme from '@styles/themes/useTheme'; import SkeletonViewContentLoader from './SkeletonViewContentLoader'; function AvatarSkeleton() { + const theme = useTheme(); return ( { }; function AvatarWithDisplayName(props) { + const theme = useTheme(); + const styles = useThemeStyles(); const title = ReportUtils.getReportName(props.report); const subtitle = ReportUtils.getChatRoomSubtitle(props.report); const parentNavigationSubtitleData = ReportUtils.getParentNavigationSubtitle(props.report); @@ -99,7 +101,7 @@ function AvatarWithDisplayName(props) { const shouldShowSubscriptAvatar = ReportUtils.shouldReportShowSubscript(props.report); const isExpenseRequest = ReportUtils.isExpenseRequest(props.report); const defaultSubscriptSize = isExpenseRequest ? CONST.AVATAR_SIZE.SMALL_NORMAL : props.size; - const avatarBorderColor = props.isAnonymous ? themeColors.highlightBG : themeColors.componentBG; + const avatarBorderColor = props.isAnonymous ? theme.highlightBG : theme.componentBG; const headerView = ( diff --git a/src/components/AvatarWithImagePicker.js b/src/components/AvatarWithImagePicker.js index 87bd382e806b..893a02288e77 100644 --- a/src/components/AvatarWithImagePicker.js +++ b/src/components/AvatarWithImagePicker.js @@ -9,8 +9,6 @@ import * as FileUtils from '@libs/fileDownload/FileUtils'; import getImageResolution from '@libs/fileDownload/getImageResolution'; import SpinningIndicatorAnimation from '@styles/animation/SpinningIndicatorAnimation'; import stylePropTypes from '@styles/stylePropTypes'; -import styles from '@styles/styles'; -import themeColors from '@styles/themes/default'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import AttachmentModal from './AttachmentModal'; @@ -26,6 +24,8 @@ import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; import Tooltip from './Tooltip/PopoverAnchorTooltip'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; import withNavigationFocus from './withNavigationFocus'; +import withTheme, {withThemePropTypes} from './withTheme'; +import withThemeStyles, {withThemeStylesPropTypes} from './withThemeStyles'; const propTypes = { /** Avatar source to display */ @@ -95,6 +95,8 @@ const propTypes = { isFocused: PropTypes.bool.isRequired, ...withLocalizePropTypes, + ...withThemeStylesPropTypes, + ...withThemePropTypes, }; const defaultProps = { @@ -253,8 +255,8 @@ class AvatarWithImagePicker extends React.Component { const additionalStyles = _.isArray(this.props.style) ? this.props.style : [this.props.style]; return ( - - + + {this.props.source ? ( )} - + @@ -364,7 +366,7 @@ class AvatarWithImagePicker extends React.Component { {this.state.validationError && ( @@ -386,4 +388,4 @@ class AvatarWithImagePicker extends React.Component { AvatarWithImagePicker.propTypes = propTypes; AvatarWithImagePicker.defaultProps = defaultProps; -export default compose(withLocalize, withNavigationFocus)(AvatarWithImagePicker); +export default compose(withLocalize, withNavigationFocus, withThemeStyles, withTheme)(AvatarWithImagePicker); diff --git a/src/components/AvatarWithIndicator.js b/src/components/AvatarWithIndicator.js index 05ca65fc64da..f3607b69a73f 100644 --- a/src/components/AvatarWithIndicator.js +++ b/src/components/AvatarWithIndicator.js @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import {View} from 'react-native'; import * as UserUtils from '@libs/UserUtils'; -import styles from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; import Avatar from './Avatar'; import AvatarSkeleton from './AvatarSkeleton'; import * as Expensicons from './Icon/Expensicons'; @@ -30,6 +30,7 @@ const defaultProps = { }; function AvatarWithIndicator(props) { + const styles = useThemeStyles(); return ( diff --git a/src/components/Badge.tsx b/src/components/Badge.tsx index 2ccd41575073..22c056dfdfc4 100644 --- a/src/components/Badge.tsx +++ b/src/components/Badge.tsx @@ -1,7 +1,7 @@ import React, {useCallback} from 'react'; import {GestureResponderEvent, PressableStateCallbackType, StyleProp, TextStyle, View, ViewStyle} from 'react-native'; -import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; +import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; import Text from './Text'; @@ -33,12 +33,13 @@ type BadgeProps = { }; function Badge({success = false, error = false, pressable = false, text, environment = CONST.ENVIRONMENT.DEV, badgeStyles, textStyles, onPress = () => {}}: BadgeProps) { + const styles = useThemeStyles(); const textColorStyles = success || error ? styles.textWhite : undefined; const Wrapper = pressable ? PressableWithoutFeedback : View; const wrapperStyles: (state: PressableStateCallbackType) => StyleProp = useCallback( ({pressed}) => [styles.badge, styles.ml2, StyleUtils.getBadgeColorStyle(success, error, pressed, environment === CONST.ENVIRONMENT.ADHOC), badgeStyles], - [success, error, environment, badgeStyles], + [styles.badge, styles.ml2, success, error, environment, badgeStyles], ); return ( diff --git a/src/components/Banner.js b/src/components/Banner.js index 23226e21eb51..2fcb866334e0 100644 --- a/src/components/Banner.js +++ b/src/components/Banner.js @@ -3,8 +3,8 @@ import React, {memo} from 'react'; import {View} from 'react-native'; import compose from '@libs/compose'; import getButtonState from '@libs/getButtonState'; -import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; +import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import Hoverable from './Hoverable'; import Icon from './Icon'; @@ -56,6 +56,7 @@ const defaultProps = { }; function Banner(props) { + const styles = useThemeStyles(); return ( {(isHovered) => { diff --git a/src/components/BaseMiniContextMenuItem.js b/src/components/BaseMiniContextMenuItem.js index b8d7a4a7484b..04a569ba7f36 100644 --- a/src/components/BaseMiniContextMenuItem.js +++ b/src/components/BaseMiniContextMenuItem.js @@ -5,8 +5,8 @@ import _ from 'underscore'; import DomUtils from '@libs/DomUtils'; import getButtonState from '@libs/getButtonState'; import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; -import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; +import useThemeStyles from '@styles/useThemeStyles'; import variables from '@styles/variables'; import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; import Tooltip from './Tooltip/PopoverAnchorTooltip'; @@ -50,6 +50,7 @@ const defaultProps = { * @returns {JSX.Element} */ function BaseMiniContextMenuItem(props) { + const styles = useThemeStyles(); return ( diff --git a/src/components/BlockingViews/FullPageNotFoundView.js b/src/components/BlockingViews/FullPageNotFoundView.js index 5232b5eca8dd..b82474aa0694 100644 --- a/src/components/BlockingViews/FullPageNotFoundView.js +++ b/src/components/BlockingViews/FullPageNotFoundView.js @@ -5,7 +5,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Illustrations from '@components/Icon/Illustrations'; import useLocalize from '@hooks/useLocalize'; import Navigation from '@libs/Navigation/Navigation'; -import styles from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; import variables from '@styles/variables'; import ROUTES from '@src/ROUTES'; import BlockingView from './BlockingView'; @@ -53,6 +53,7 @@ const defaultProps = { // eslint-disable-next-line rulesdir/no-negated-variables function FullPageNotFoundView({children, shouldShow, titleKey, subtitleKey, linkKey, onBackButtonPress, shouldShowLink, shouldShowBackButton, onLinkPress}) { + const styles = useThemeStyles(); const {translate} = useLocalize(); if (shouldShow) { return ( diff --git a/src/components/Button/index.js b/src/components/Button/index.js index 5fe7dd1fe812..b9aaf8868924 100644 --- a/src/components/Button/index.js +++ b/src/components/Button/index.js @@ -10,9 +10,9 @@ import Text from '@components/Text'; import withNavigationFallback from '@components/withNavigationFallback'; import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; import HapticFeedback from '@libs/HapticFeedback'; -import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; -import themeColors from '@styles/themes/default'; +import useTheme from '@styles/themes/useTheme'; +import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import validateSubmitShortcut from './validateSubmitShortcut'; @@ -127,7 +127,7 @@ const defaultProps = { shouldShowRightIcon: false, icon: null, iconRight: Expensicons.ArrowRight, - iconFill: themeColors.textLight, + iconFill: undefined, iconStyles: [], iconRightStyles: [], isLoading: false, @@ -201,6 +201,8 @@ function Button({ accessibilityLabel, forwardedRef, }) { + const theme = useTheme(); + const styles = useThemeStyles(); const isFocused = useIsFocused(); const keyboardShortcutCallback = useCallback( @@ -254,7 +256,7 @@ function Button({ @@ -265,7 +267,7 @@ function Button({ @@ -334,7 +336,7 @@ function Button({ {renderContent()} {isLoading && ( )} diff --git a/src/components/ButtonWithDropdownMenu.js b/src/components/ButtonWithDropdownMenu.js index 7c88d9202b78..15f2e2f4d6de 100644 --- a/src/components/ButtonWithDropdownMenu.js +++ b/src/components/ButtonWithDropdownMenu.js @@ -3,9 +3,9 @@ import React, {useEffect, useRef, useState} from 'react'; import {View} from 'react-native'; import _ from 'underscore'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; -import themeColors from '@styles/themes/default'; +import useTheme from '@styles/themes/useTheme'; +import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import Button from './Button'; import Icon from './Icon'; @@ -72,6 +72,8 @@ const defaultProps = { }; function ButtonWithDropdownMenu(props) { + const theme = useTheme(); + const styles = useThemeStyles(); const [selectedItemIndex, setSelectedItemIndex] = useState(0); const [isMenuVisible, setIsMenuVisible] = useState(false); const [popoverAnchorPosition, setPopoverAnchorPosition] = useState(null); @@ -134,7 +136,7 @@ function ButtonWithDropdownMenu(props) { diff --git a/src/components/CardPreview.js b/src/components/CardPreview.js index 9f59ca140ce5..df944d930a92 100644 --- a/src/components/CardPreview.js +++ b/src/components/CardPreview.js @@ -4,7 +4,7 @@ import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import ExpensifyCardImage from '@assets/images/expensify-card.svg'; import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails'; -import styles from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; import variables from '@styles/variables'; import ONYXKEYS from '@src/ONYXKEYS'; import Text from './Text'; @@ -33,6 +33,7 @@ const defaultProps = { }; function CardPreview({privatePersonalDetails: {legalFirstName, legalLastName}, session: {email}}) { + const styles = useThemeStyles(); usePrivatePersonalDetails(); const cardHolder = legalFirstName && legalLastName ? `${legalFirstName} ${legalLastName}` : email; diff --git a/src/components/CategoryPicker/index.js b/src/components/CategoryPicker/index.js index 156007aea76e..ff7087df91dd 100644 --- a/src/components/CategoryPicker/index.js +++ b/src/components/CategoryPicker/index.js @@ -5,12 +5,13 @@ import _ from 'underscore'; import OptionsSelector from '@components/OptionsSelector'; import useLocalize from '@hooks/useLocalize'; import * as OptionsListUtils from '@libs/OptionsListUtils'; -import styles from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {defaultProps, propTypes} from './categoryPickerPropTypes'; function CategoryPicker({selectedCategory, policyCategories, policyRecentlyUsedCategories, onSubmit}) { + const styles = useThemeStyles(); const {translate} = useLocalize(); const [searchValue, setSearchValue] = useState(''); diff --git a/src/components/Checkbox.js b/src/components/Checkbox.js index 5734ad2fed26..4b9ce922aacb 100644 --- a/src/components/Checkbox.js +++ b/src/components/Checkbox.js @@ -2,9 +2,9 @@ import PropTypes from 'prop-types'; import React from 'react'; import {View} from 'react-native'; import stylePropTypes from '@styles/stylePropTypes'; -import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; -import themeColors from '@styles/themes/default'; +import useTheme from '@styles/themes/useTheme'; +import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; @@ -67,6 +67,8 @@ const defaultProps = { }; function Checkbox(props) { + const theme = useTheme(); + const styles = useThemeStyles(); const handleSpaceKey = (event) => { if (event.code !== 'Space') { return; @@ -115,7 +117,7 @@ function Checkbox(props) { {props.isChecked && ( diff --git a/src/components/CheckboxWithLabel.js b/src/components/CheckboxWithLabel.js index 86dba1d2a932..0a90a9be46e2 100644 --- a/src/components/CheckboxWithLabel.js +++ b/src/components/CheckboxWithLabel.js @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import React, {useState} from 'react'; import {View} from 'react-native'; import _ from 'underscore'; -import styles from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; import variables from '@styles/variables'; import Checkbox from './Checkbox'; import FormHelpMessage from './FormHelpMessage'; @@ -83,6 +83,7 @@ const defaultProps = { }; function CheckboxWithLabel(props) { + const styles = useThemeStyles(); // We need to pick the first value that is strictly a boolean // https://github.com/Expensify/App/issues/16885#issuecomment-1520846065 const [isChecked, setIsChecked] = useState(() => _.find([props.value, props.defaultValue, props.isChecked], (value) => _.isBoolean(value))); diff --git a/src/components/CommunicationsLink.js b/src/components/CommunicationsLink.js index f09fecea5239..dbbe5737b3aa 100644 --- a/src/components/CommunicationsLink.js +++ b/src/components/CommunicationsLink.js @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import {View} from 'react-native'; import Clipboard from '@libs/Clipboard'; -import styles from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; import ContextMenuItem from './ContextMenuItem'; import * as Expensicons from './Icon/Expensicons'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; @@ -26,6 +26,7 @@ const defaultProps = { }; function CommunicationsLink(props) { + const styles = useThemeStyles(); return ( diff --git a/src/components/Composer/index.js b/src/components/Composer/index.js index d02fdd2563b1..4c61a5b5bba5 100755 --- a/src/components/Composer/index.js +++ b/src/components/Composer/index.js @@ -16,9 +16,9 @@ import updateIsFullComposerAvailable from '@libs/ComposerUtils/updateIsFullCompo import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import isEnterWhileComposition from '@libs/KeyboardShortcut/isEnterWhileComposition'; import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; -import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; -import themeColors from '@styles/themes/default'; +import useTheme from '@styles/themes/useTheme'; +import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; const propTypes = { @@ -57,7 +57,7 @@ const propTypes = { isDisabled: PropTypes.bool, /** Set focus to this component the first time it renders. - Override this in case you need to set focus on one field out of many, or when you want to disable autoFocus */ + Override this in case you need to set focus on one field out of many, or when you want to disable autoFocus */ autoFocus: PropTypes.bool, /** Update selection position on change */ @@ -169,6 +169,8 @@ function Composer({ isComposerFullSize, ...props }) { + const theme = useTheme(); + const styles = useThemeStyles(); const {windowWidth} = useWindowDimensions(); const textRef = useRef(null); const textInput = useRef(null); @@ -448,7 +450,8 @@ function Composer({ StyleUtils.getComposeTextAreaPadding(numberOfLines, isComposerFullSize), Browser.isMobileSafari() || Browser.isSafari() ? styles.rtlTextRenderForSafari : {}, ], - [style, maxLines, numberOfLines, isComposerFullSize], + + [numberOfLines, maxLines, styles.overflowHidden, styles.rtlTextRenderForSafari, style, isComposerFullSize], ); return ( @@ -456,7 +459,7 @@ function Composer({ (textInput.current = el)} selection={selection} style={inputStyleMemo} diff --git a/src/components/ConfirmContent.js b/src/components/ConfirmContent.js index 6142322848d0..ff8ee4f861a4 100644 --- a/src/components/ConfirmContent.js +++ b/src/components/ConfirmContent.js @@ -4,7 +4,7 @@ import {View} from 'react-native'; import _ from 'underscore'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; -import styles from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; import variables from '@styles/variables'; import Button from './Button'; import Header from './Header'; @@ -87,6 +87,7 @@ const defaultProps = { }; function ConfirmContent(props) { + const styles = useThemeStyles(); const {translate} = useLocalize(); const {isOffline} = useNetwork(); diff --git a/src/components/ConfirmationPage.js b/src/components/ConfirmationPage.js index 22e29dca519d..ac56ea3d22e9 100644 --- a/src/components/ConfirmationPage.js +++ b/src/components/ConfirmationPage.js @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import {View} from 'react-native'; -import styles from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; import Button from './Button'; import FixedFooter from './FixedFooter'; import Lottie from './Lottie'; @@ -39,6 +39,7 @@ const defaultProps = { }; function ConfirmationPage(props) { + const styles = useThemeStyles(); return ( <> diff --git a/src/components/ConnectBankAccountButton.js b/src/components/ConnectBankAccountButton.js index 2c66bcc200da..6afd3d57d4e6 100644 --- a/src/components/ConnectBankAccountButton.js +++ b/src/components/ConnectBankAccountButton.js @@ -3,7 +3,7 @@ import React from 'react'; import {View} from 'react-native'; import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; -import styles from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; import * as ReimbursementAccount from '@userActions/ReimbursementAccount'; import Button from './Button'; import * as Expensicons from './Icon/Expensicons'; @@ -30,6 +30,7 @@ const defaultProps = { }; function ConnectBankAccountButton(props) { + const styles = useThemeStyles(); const activeRoute = Navigation.getActiveRouteWithoutParams(); return props.network.isOffline ? ( diff --git a/src/components/ContextMenuItem.js b/src/components/ContextMenuItem.js index 80d4855392a4..d0a43badc5e3 100644 --- a/src/components/ContextMenuItem.js +++ b/src/components/ContextMenuItem.js @@ -4,8 +4,8 @@ import useThrottledButtonState from '@hooks/useThrottledButtonState'; import useWindowDimensions from '@hooks/useWindowDimensions'; import getButtonState from '@libs/getButtonState'; import getContextMenuItemStyles from '@styles/getContextMenuItemStyles'; -import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; +import useThemeStyles from '@styles/useThemeStyles'; import BaseMiniContextMenuItem from './BaseMiniContextMenuItem'; import Icon from './Icon'; import MenuItem from './MenuItem'; @@ -53,6 +53,7 @@ const defaultProps = { }; function ContextMenuItem({onPress, successIcon, successText, icon, text, isMini, description, isAnonymousAction, isFocused, innerRef}) { + const styles = useThemeStyles(); const {windowWidth} = useWindowDimensions(); const [isThrottledButtonActive, setThrottledButtonInactive] = useThrottledButtonState(); diff --git a/src/components/CountrySelector.js b/src/components/CountrySelector.js index c2426c5b7b0b..13fc215f1d8c 100644 --- a/src/components/CountrySelector.js +++ b/src/components/CountrySelector.js @@ -3,7 +3,7 @@ import React, {useEffect} from 'react'; import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import Navigation from '@libs/Navigation/Navigation'; -import styles from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; import ROUTES from '@src/ROUTES'; import FormHelpMessage from './FormHelpMessage'; import MenuItemWithTopDescription from './MenuItemWithTopDescription'; @@ -33,6 +33,7 @@ const defaultProps = { }; function CountrySelector({errorText, value: countryCode, onInputChange, forwardedRef}) { + const styles = useThemeStyles(); const {translate} = useLocalize(); const title = countryCode ? translate(`allCountries.${countryCode}`) : ''; diff --git a/src/components/CurrencySymbolButton.js b/src/components/CurrencySymbolButton.js index ca7816a9f117..4d43ec3d93e0 100644 --- a/src/components/CurrencySymbolButton.js +++ b/src/components/CurrencySymbolButton.js @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import useLocalize from '@hooks/useLocalize'; -import styles from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; import Text from './Text'; @@ -16,6 +16,7 @@ const propTypes = { }; function CurrencySymbolButton({onCurrencyButtonPress, currencySymbol}) { + const styles = useThemeStyles(); const {translate} = useLocalize(); return ( diff --git a/src/components/CurrentUserPersonalDetailsSkeletonView/index.tsx b/src/components/CurrentUserPersonalDetailsSkeletonView/index.tsx index 3a87702b48e4..685db8031330 100644 --- a/src/components/CurrentUserPersonalDetailsSkeletonView/index.tsx +++ b/src/components/CurrentUserPersonalDetailsSkeletonView/index.tsx @@ -3,9 +3,9 @@ import {View} from 'react-native'; import {Circle, Rect} from 'react-native-svg'; import {ValueOf} from 'type-fest'; import SkeletonViewContentLoader from '@components/SkeletonViewContentLoader'; -import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; -import themeColors from '@styles/themes/default'; +import useTheme from '@styles/themes/useTheme'; +import useThemeStyles from '@styles/useThemeStyles'; import variables from '@styles/variables'; import CONST from '@src/CONST'; @@ -23,12 +23,9 @@ type CurrentUserPersonalDetailsSkeletonViewProps = { foregroundColor?: string; }; -function CurrentUserPersonalDetailsSkeletonView({ - shouldAnimate = true, - avatarSize = CONST.AVATAR_SIZE.LARGE, - backgroundColor = themeColors.highlightBG, - foregroundColor = themeColors.border, -}: CurrentUserPersonalDetailsSkeletonViewProps) { +function CurrentUserPersonalDetailsSkeletonView({shouldAnimate = true, avatarSize = CONST.AVATAR_SIZE.LARGE, backgroundColor, foregroundColor}: CurrentUserPersonalDetailsSkeletonViewProps) { + const theme = useTheme(); + const styles = useThemeStyles(); const avatarPlaceholderSize = StyleUtils.getAvatarSize(avatarSize); const avatarPlaceholderRadius = avatarPlaceholderSize / 2; const spaceBetweenAvatarAndHeadline = styles.mb3.marginBottom + styles.mt1.marginTop + (variables.lineHeightXXLarge - variables.fontSizeXLarge) / 2; @@ -39,8 +36,8 @@ function CurrentUserPersonalDetailsSkeletonView({ {formattedBalance}; } diff --git a/src/components/CustomStatusBar/index.js b/src/components/CustomStatusBar/index.js index 2ffd763bf088..a724c71059ef 100644 --- a/src/components/CustomStatusBar/index.js +++ b/src/components/CustomStatusBar/index.js @@ -1,23 +1,24 @@ import React, {useEffect} from 'react'; import Navigation, {navigationRef} from '@libs/Navigation/Navigation'; import StatusBar from '@libs/StatusBar'; -import themeColors from '@styles/themes/default'; +import useTheme from '@styles/themes/useTheme'; function CustomStatusBar() { + const theme = useTheme(); useEffect(() => { Navigation.isNavigationReady().then(() => { // Set the status bar colour depending on the current route. // If we don't have any colour defined for a route, fall back to // appBG color. const currentRoute = navigationRef.getCurrentRoute(); - let currentScreenBackgroundColor = themeColors.appBG; - if (currentRoute && 'name' in currentRoute && currentRoute.name in themeColors.PAGE_BACKGROUND_COLORS) { - currentScreenBackgroundColor = themeColors.PAGE_BACKGROUND_COLORS[currentRoute.name]; + let currentScreenBackgroundColor = theme.appBG; + if (currentRoute && 'name' in currentRoute && currentRoute.name in theme.PAGE_BACKGROUND_COLORS) { + currentScreenBackgroundColor = theme.PAGE_BACKGROUND_COLORS[currentRoute.name]; } StatusBar.setBarStyle('light-content', true); StatusBar.setBackgroundColor(currentScreenBackgroundColor); }); - }, []); + }, [theme.PAGE_BACKGROUND_COLORS, theme.appBG]); return ; } diff --git a/src/components/DatePicker/index.android.js b/src/components/DatePicker/index.android.js index 17d1e2e14e71..5e7086fb78ad 100644 --- a/src/components/DatePicker/index.android.js +++ b/src/components/DatePicker/index.android.js @@ -3,11 +3,12 @@ import {format, parseISO} from 'date-fns'; import React, {forwardRef, useCallback, useImperativeHandle, useRef, useState} from 'react'; import {Keyboard} from 'react-native'; import TextInput from '@components/TextInput'; -import styles from '@styles/styles'; +import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import {defaultProps, propTypes} from './datepickerPropTypes'; const DatePicker = forwardRef(({value, defaultValue, label, placeholder, errorText, containerStyles, disabled, onBlur, onInputChange, maxDate, minDate}, outerRef) => { + const styles = useThemeStyles(); const ref = useRef(); const [isPickerVisible, setIsPickerVisible] = useState(false); diff --git a/src/components/DatePicker/index.ios.js b/src/components/DatePicker/index.ios.js index 8b884c29b07f..44a825aa8183 100644 --- a/src/components/DatePicker/index.ios.js +++ b/src/components/DatePicker/index.ios.js @@ -7,12 +7,14 @@ import Popover from '@components/Popover'; import TextInput from '@components/TextInput'; import useKeyboardState from '@hooks/useKeyboardState'; import useLocalize from '@hooks/useLocalize'; -import styles from '@styles/styles'; -import themeColors from '@styles/themes/default'; +import useTheme from '@styles/themes/useTheme'; +import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import {defaultProps, propTypes} from './datepickerPropTypes'; function DatePicker({value, defaultValue, innerRef, onInputChange, preferredLocale, minDate, maxDate, label, disabled, onBlur, placeholder, containerStyles, errorText}) { + const theme = useTheme(); + const styles = useThemeStyles(); const dateValue = value || defaultValue; const [isPickerVisible, setIsPickerVisible] = useState(false); const [selectedDate, setSelectedDate] = useState(dateValue ? new Date(dateValue) : new Date()); @@ -104,12 +106,13 @@ function DatePicker({value, defaultValue, innerRef, onInputChange, preferredLoca