From fab750614102206d93735f364193ae51f92ac560 Mon Sep 17 00:00:00 2001 From: Edu Date: Fri, 25 Aug 2023 10:35:56 -0300 Subject: [PATCH 001/245] Updated logic to handle the shouldDisplayNewMarker --- src/pages/home/report/ReportActionsList.js | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 7f897ee825fb..8229cd4b4fb9 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -70,10 +70,6 @@ const defaultProps = { const VERTICAL_OFFSET_THRESHOLD = 200; const MSG_VISIBLE_THRESHOLD = 250; -// Seems that there is an architecture issue that prevents us from using the reportID with useRef -// the useRef value gets reset when the reportID changes, so we use a global variable to keep track -let prevReportID = null; - /** * Create a unique key for each action in the FlatList. * We use the reportActionID that is a string representation of a random 64-bit int, which should be @@ -109,6 +105,7 @@ function ReportActionsList({ const {isOffline} = useNetwork(); const opacity = useSharedValue(0); const userActiveSince = useRef(null); + const prevReportID = useRef(null); const currentUnreadMarker = useRef(null); const scrollingVerticalOffset = useRef(0); const readActionSkipped = useRef(false); @@ -131,16 +128,16 @@ function ReportActionsList({ // If the reportID changes, we reset the userActiveSince to null, we need to do it because // the parent component is sending the previous reportID even when the user isn't active // on the report - if (userActiveSince.current && prevReportID && prevReportID !== report.reportID) { + if (userActiveSince.current && prevReportID.current && prevReportID.current !== report.reportID) { userActiveSince.current = null; } else { userActiveSince.current = DateUtils.getDBTime(); } - prevReportID = report.reportID; + prevReportID.current = report.reportID; }, [report.reportID]); useEffect(() => { - if (!userActiveSince.current || report.reportID !== prevReportID) { + if (!userActiveSince.current || report.reportID !== prevReportID.current) { return; } @@ -228,15 +225,18 @@ function ReportActionsList({ if (!currentUnreadMarker.current) { const nextMessage = sortedReportActions[index + 1]; const isCurrentMessageUnread = isMessageUnread(reportAction, report.lastReadTime); - shouldDisplayNewMarker = isCurrentMessageUnread && !isMessageUnread(nextMessage, report.lastReadTime); + let canDisplayNewMarker = isCurrentMessageUnread && !isMessageUnread(nextMessage, report.lastReadTime); if (!messageManuallyMarked.read) { - shouldDisplayNewMarker = shouldDisplayNewMarker && reportAction.actorAccountID !== Report.getCurrentUserAccountID(); + canDisplayNewMarker = canDisplayNewMarker && reportAction.actorAccountID !== Report.getCurrentUserAccountID(); } - const canDisplayMarker = scrollingVerticalOffset.current < MSG_VISIBLE_THRESHOLD ? reportAction.created < userActiveSince.current : true; - - if (!currentUnreadMarker.current && shouldDisplayNewMarker && canDisplayMarker) { + let isMessageInScope = scrollingVerticalOffset.current < MSG_VISIBLE_THRESHOLD ? reportAction.created < userActiveSince.current : true; + if (messageManuallyMarked.read) { + isMessageInScope = true; + } + if (!currentUnreadMarker.current && canDisplayNewMarker && isMessageInScope) { currentUnreadMarker.current = reportAction.reportActionID; + shouldDisplayNewMarker = true; } } else { shouldDisplayNewMarker = reportAction.reportActionID === currentUnreadMarker.current; From 47dd5cd037bbbe0949e377bc48caa6ee71741a7e Mon Sep 17 00:00:00 2001 From: Eduardo Date: Thu, 14 Sep 2023 13:33:23 +0200 Subject: [PATCH 002/245] Sending an event when the user mark as unread a message --- src/libs/actions/Report.js | 1 + .../report/ContextMenu/ContextMenuActions.js | 4 ++- src/pages/home/report/ReportActionsList.js | 35 +++++++++++-------- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 8b898a6aaaea..f3bb12cce4af 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -793,6 +793,7 @@ function markCommentAsUnread(reportID, reportActionCreated) { ], }, ); + return lastReadTime; } /** diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index a3621da3e820..4cf5fa8ab0dd 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -2,6 +2,7 @@ import React from 'react'; import _ from 'underscore'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import lodashGet from 'lodash/get'; +import {DeviceEventEmitter} from 'react-native'; import * as Expensicons from '../../../../components/Icon/Expensicons'; import * as Report from '../../../../libs/actions/Report'; import * as Download from '../../../../libs/actions/Download'; @@ -250,7 +251,8 @@ export default [ shouldShow: (type, reportAction, isArchivedRoom, betas, anchor, isChronosReport, reportID, isPinnedChat, isUnreadChat) => type === CONTEXT_MENU_TYPES.REPORT_ACTION || (type === CONTEXT_MENU_TYPES.REPORT && !isUnreadChat), onPress: (closePopover, {reportAction, reportID}) => { - Report.markCommentAsUnread(reportID, reportAction.created); + const lastReadTime = Report.markCommentAsUnread(reportID, reportAction.created); + DeviceEventEmitter.emit('unreadAction', lastReadTime); if (closePopover) { hideContextMenu(true, ReportActionComposeFocusManager.focus); } diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 8229cd4b4fb9..f2591415a18a 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -23,6 +23,8 @@ import useNetwork from '../../../hooks/useNetwork'; import DateUtils from '../../../libs/DateUtils'; import FloatingMessageCounter from './FloatingMessageCounter'; import useReportScrollManager from '../../../hooks/useReportScrollManager'; +import {DeviceEventEmitter} from 'react-native'; +import {use} from '../../../libs/Request'; const propTypes = { /** The report currently being looked at */ @@ -110,10 +112,11 @@ function ReportActionsList({ const scrollingVerticalOffset = useRef(0); const readActionSkipped = useRef(false); const reportActionSize = useRef(sortedReportActions.length); + const lastReadRef = useRef(report.lastReadTime); // Considering that renderItem is enclosed within a useCallback, marking it as "read" twice will retain the value as "true," preventing the useCallback from re-executing. // However, if we create and listen to an object, it will lead to a new useCallback execution. - const [messageManuallyMarked, setMessageManuallyMarked] = useState({read: false}); + const [messageManuallyMarked, setMessageManuallyMarked] = useState(0); const [isFloatingMessageCounterVisible, setIsFloatingMessageCounterVisible] = useState(false); const animatedStyles = useAnimatedStyle(() => ({ opacity: opacity.value, @@ -159,19 +162,23 @@ function ReportActionsList({ }, [sortedReportActions.length, report.reportID]); useEffect(() => { - const didManuallyMarkReportAsUnread = report.lastReadTime < DateUtils.getDBTime() && ReportUtils.isUnread(report); - if (!didManuallyMarkReportAsUnread) { - setMessageManuallyMarked({read: false}); + if (!userActiveSince.current || report.reportID !== prevReportID.current) { return; } - // Clearing the current unread marker so that it can be recalculated - currentUnreadMarker.current = null; - setMessageManuallyMarked({read: true}); + lastReadRef.current = report.lastReadTime; + setMessageManuallyMarked(0); + }, [report.lastReadTime, report.reportID]); - // We only care when a new lastReadTime is set in the report - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [report.lastReadTime]); + useEffect(() => { + const unreadActionSubscription = DeviceEventEmitter.addListener('unreadAction', (newLastReadTime) => { + currentUnreadMarker.current = null; + lastReadRef.current = newLastReadTime; + setMessageManuallyMarked(new Date().getTime()); + }); + + return () => unreadActionSubscription.remove(); + }, []); /** * Show/hide the new floating message counter when user is scrolling back/forth in the history of messages. @@ -224,14 +231,14 @@ function ReportActionsList({ if (!currentUnreadMarker.current) { const nextMessage = sortedReportActions[index + 1]; - const isCurrentMessageUnread = isMessageUnread(reportAction, report.lastReadTime); - let canDisplayNewMarker = isCurrentMessageUnread && !isMessageUnread(nextMessage, report.lastReadTime); + const isCurrentMessageUnread = isMessageUnread(reportAction, lastReadRef.current); + let canDisplayNewMarker = isCurrentMessageUnread && !isMessageUnread(nextMessage, lastReadRef.current); - if (!messageManuallyMarked.read) { + if (!messageManuallyMarked) { canDisplayNewMarker = canDisplayNewMarker && reportAction.actorAccountID !== Report.getCurrentUserAccountID(); } let isMessageInScope = scrollingVerticalOffset.current < MSG_VISIBLE_THRESHOLD ? reportAction.created < userActiveSince.current : true; - if (messageManuallyMarked.read) { + if (messageManuallyMarked) { isMessageInScope = true; } if (!currentUnreadMarker.current && canDisplayNewMarker && isMessageInScope) { From 922fbf301014df3a18d23aa1f23df77c1b22299a Mon Sep 17 00:00:00 2001 From: Eduardo Date: Thu, 14 Sep 2023 14:30:09 +0200 Subject: [PATCH 003/245] lint fixes --- src/libs/actions/Report.js | 2 ++ src/pages/home/report/ReportActionsList.js | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index f3bb12cce4af..a8e2291407f4 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -766,6 +766,8 @@ function readNewestAction(reportID) { * * @param {String} reportID * @param {String} reportActionCreated + * + * @returns {String} lastReadTime */ function markCommentAsUnread(reportID, reportActionCreated) { // If no action created date is provided, use the last action's diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index f2591415a18a..8a489f5dab22 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -2,6 +2,7 @@ import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useState, useRef, useMemo} from 'react'; import Animated, {useSharedValue, useAnimatedStyle, withTiming} from 'react-native-reanimated'; import _ from 'underscore'; +import {DeviceEventEmitter} from 'react-native'; import InvertedFlatList from '../../../components/InvertedFlatList'; import compose from '../../../libs/compose'; import styles from '../../../styles/styles'; @@ -23,8 +24,6 @@ import useNetwork from '../../../hooks/useNetwork'; import DateUtils from '../../../libs/DateUtils'; import FloatingMessageCounter from './FloatingMessageCounter'; import useReportScrollManager from '../../../hooks/useReportScrollManager'; -import {DeviceEventEmitter} from 'react-native'; -import {use} from '../../../libs/Request'; const propTypes = { /** The report currently being looked at */ From 364684848ee6ad0e4d4b21383320589e63f2fe40 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Wed, 27 Sep 2023 16:41:25 +0200 Subject: [PATCH 004/245] Listening to the specific reports and clearing unread marker cache --- .../report/ContextMenu/ContextMenuActions.js | 2 +- src/pages/home/report/ReportActionsList.js | 35 +++++++++++++++---- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index 2847df080f4b..d4dbda641b12 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -253,7 +253,7 @@ export default [ type === CONTEXT_MENU_TYPES.REPORT_ACTION || (type === CONTEXT_MENU_TYPES.REPORT && !isUnreadChat), onPress: (closePopover, {reportAction, reportID}) => { const lastReadTime = Report.markCommentAsUnread(reportID, reportAction.created); - DeviceEventEmitter.emit('unreadAction', lastReadTime); + DeviceEventEmitter.emit(`unreadAction_${reportID}`, lastReadTime); if (closePopover) { hideContextMenu(true, ReportActionComposeFocusManager.focus); } diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 30b712f5a705..03257b79ce7c 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -75,6 +75,9 @@ const MSG_VISIBLE_THRESHOLD = 250; // the subscriptions could otherwise be conflicting. const newActionUnsubscribeMap = {}; +// We cache the unread markers for each report, because the unread marker isn't +// kept between reports. +const cacheUnreadMarkers = new Map(); /** * Create a unique key for each action in the FlatList. * We use the reportActionID that is a string representation of a random 64-bit int, which should be @@ -111,7 +114,14 @@ function ReportActionsList({ const opacity = useSharedValue(0); const userActiveSince = useRef(null); const prevReportID = useRef(null); - const [currentUnreadMarker, setCurrentUnreadMarker] = useState(null); + const unreadActionSubscription = useRef(null); + const markerInit = () => { + if (!cacheUnreadMarkers.has(report.reportID)) { + return null; + } + return cacheUnreadMarkers.get(report.reportID); + }; + const [currentUnreadMarker, setCurrentUnreadMarker] = useState(markerInit); const scrollingVerticalOffset = useRef(0); const readActionSkipped = useRef(false); const reportActionSize = useRef(sortedReportActions.length); @@ -159,6 +169,7 @@ function ReportActionsList({ return; } + cacheUnreadMarkers.delete(report.reportID); reportActionSize.current = sortedReportActions.length; setCurrentUnreadMarker(null); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -169,19 +180,29 @@ function ReportActionsList({ return; } + if (!messageManuallyMarked && lastReadRef.current && lastReadRef.current < report.lastReadTime) { + cacheUnreadMarkers.delete(report.reportID); + } lastReadRef.current = report.lastReadTime; setMessageManuallyMarked(0); }, [report.lastReadTime, report.reportID]); useEffect(() => { - const unreadActionSubscription = DeviceEventEmitter.addListener('unreadAction', (newLastReadTime) => { - setCurrentUnreadMarker(null); + // If the reportID changes, we reset the userActiveSince to null, we need to do it because + // this component doesn't unmount when the reportID changes + if (unreadActionSubscription.current) { + unreadActionSubscription.current.remove(); + unreadActionSubscription.current = null; + } + + // Need to listen for the specific reportID, otherwise we could be listening to all the reports + unreadActionSubscription.current = DeviceEventEmitter.addListener(`unreadAction_${report.reportID}`, (newLastReadTime) => { + cacheUnreadMarkers.delete(report.reportID); lastReadRef.current = newLastReadTime; + setCurrentUnreadMarker(null); setMessageManuallyMarked(new Date().getTime()); }); - - return () => unreadActionSubscription.remove(); - }, []); + }, [report.reportID]); useEffect(() => { // Why are we doing this, when in the cleanup of the useEffect we are already calling the unsubscribe function? @@ -278,7 +299,6 @@ function ReportActionsList({ const renderItem = useCallback( ({item: reportAction, index}) => { let shouldDisplayNewMarker = false; - if (!currentUnreadMarker) { const nextMessage = sortedReportActions[index + 1]; const isCurrentMessageUnread = isMessageUnread(reportAction, lastReadRef.current); @@ -293,6 +313,7 @@ function ReportActionsList({ isMessageInScope = true; } if (!currentUnreadMarker && canDisplayNewMarker && isMessageInScope) { + cacheUnreadMarkers.set(report.reportID, reportAction.reportActionID); setCurrentUnreadMarker(reportAction.reportActionID); shouldDisplayNewMarker = true; } From c71ecae0c06e54530ab07a85c38f3612fd7263e8 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Thu, 28 Sep 2023 07:39:41 +0200 Subject: [PATCH 005/245] disable dependency hook, no need to listen to the messageManuallyMarked --- src/pages/home/report/ReportActionsList.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 03257b79ce7c..e5d76a54a0a4 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -185,6 +185,7 @@ function ReportActionsList({ } lastReadRef.current = report.lastReadTime; setMessageManuallyMarked(0); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [report.lastReadTime, report.reportID]); useEffect(() => { From de5b8fe154d5fbbf5840eaeab8fbaeb6a968731e Mon Sep 17 00:00:00 2001 From: Eduardo Date: Fri, 29 Sep 2023 14:59:39 +0200 Subject: [PATCH 006/245] removed console logs --- src/pages/home/report/ReportActionsList.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 3392799bc4d0..0a85bb0e91a9 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -185,9 +185,7 @@ function ReportActionsList({ if (!userActiveSince.current || report.reportID !== prevReportID.current) { return; } - console.log('ReportActionsList.js useEffect(): ', lastReadRef.current, ' < report.lastRead: ', report.lastReadTime); if (!messageManuallyMarkedUnread && lastReadRef.current && lastReadRef.current < report.lastReadTime) { - console.log('ReportActionsList.js useEffect(): delete reportID from cache'); cacheUnreadMarkers.delete(report.reportID); } lastReadRef.current = report.lastReadTime; From 5d914f18ea23782ebaf05997b3b2a19cdba1a04f Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Sat, 19 Aug 2023 14:27:10 +0200 Subject: [PATCH 007/245] 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 008/245] 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 009/245] 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 942cb0ae6d7691d03e2c2e05a2ed8030f8784ca9 Mon Sep 17 00:00:00 2001 From: Shaelyn Combs Date: Mon, 2 Oct 2023 15:40:11 -0700 Subject: [PATCH 010/245] Update Free-Trial.md Replacing "coming soon" text with actual article copy. --- .../billing-and-subscriptions/Free-Trial.md | 56 ++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/docs/articles/expensify-classic/billing-and-subscriptions/Free-Trial.md b/docs/articles/expensify-classic/billing-and-subscriptions/Free-Trial.md index e08aaa3d6094..9fc63e1db308 100644 --- a/docs/articles/expensify-classic/billing-and-subscriptions/Free-Trial.md +++ b/docs/articles/expensify-classic/billing-and-subscriptions/Free-Trial.md @@ -1,5 +1,57 @@ --- title: Free Trial -description: Free Trial +description: Learn more about your free trial with Expensify. --- -## Resource Coming Soon! + + +# Overview + + +# How to start a Free Trial + + +# How to unlock additional Free Trial weeks + + +# How to make the most of your Free Trial + + +# FAQ + From 89255151125cecd985f1b4078b23d67bb66c332b Mon Sep 17 00:00:00 2001 From: Eduardo Date: Tue, 3 Oct 2023 09:10:04 +0200 Subject: [PATCH 011/245] fixed lint issues --- src/pages/home/report/ReportActionsList.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 1be51973fbac..23a9fbb00c05 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -3,14 +3,14 @@ import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated'; import _ from 'underscore'; import {DeviceEventEmitter} from 'react-native'; +import {useRoute} from '@react-navigation/native'; +import lodashGet from 'lodash/get'; import compose from '../../../libs/compose'; import styles from '../../../styles/styles'; import * as ReportUtils from '../../../libs/ReportUtils'; import * as Report from '../../../libs/actions/Report'; import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes, withCurrentUserPersonalDetailsDefaultProps} from '../../../components/withCurrentUserPersonalDetails'; -import {useRoute} from '@react-navigation/native'; -import lodashGet from 'lodash/get'; import CONST from '../../../CONST'; import InvertedFlatList from '../../../components/InvertedFlatList'; import {withPersonalDetails} from '../../../components/OnyxProvider'; From f3366099b78ec65480e8f931732f83cd2ee7e8e5 Mon Sep 17 00:00:00 2001 From: Shaelyn Combs Date: Wed, 4 Oct 2023 11:34:42 -0700 Subject: [PATCH 012/245] Update Free-Trial.md Removed comments --- .../billing-and-subscriptions/Free-Trial.md | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/docs/articles/expensify-classic/billing-and-subscriptions/Free-Trial.md b/docs/articles/expensify-classic/billing-and-subscriptions/Free-Trial.md index 9fc63e1db308..1e510f623ee5 100644 --- a/docs/articles/expensify-classic/billing-and-subscriptions/Free-Trial.md +++ b/docs/articles/expensify-classic/billing-and-subscriptions/Free-Trial.md @@ -2,17 +2,12 @@ title: Free Trial description: Learn more about your free trial with Expensify. --- - # Overview - - # How to start a Free Trial - # How to unlock additional Free Trial weeks - # How to make the most of your Free Trial - - # FAQ - From ae362c2540ad8f49c63de64d00a5b7355e43a60c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Fa=C5=82at?= Date: Mon, 18 Sep 2023 11:37:06 +0200 Subject: [PATCH 013/245] Migrated CheckForPreviousReportActionID.js with Log.js dependency. --- src/libs/{Log.js => Log.ts} | 28 +++++++------ ...D.js => CheckForPreviousReportActionID.ts} | 42 +++++++++++-------- 2 files changed, 40 insertions(+), 30 deletions(-) rename src/libs/{Log.js => Log.ts} (81%) rename src/libs/migrations/{CheckForPreviousReportActionID.js => CheckForPreviousReportActionID.ts} (62%) diff --git a/src/libs/Log.js b/src/libs/Log.ts similarity index 81% rename from src/libs/Log.js rename to src/libs/Log.ts index e51fb74aedd5..e9874e0b739d 100644 --- a/src/libs/Log.js +++ b/src/libs/Log.ts @@ -7,15 +7,15 @@ import pkg from '../../package.json'; import requireParameters from './requireParameters'; import * as Network from './Network'; -let timeout = null; +let timeout: NodeJS.Timeout | null = null; /** - * @param {Object} parameters - * @param {String} parameters.expensifyCashAppVersion - * @param {Object[]} parameters.logPacket - * @returns {Promise} + * @param parameters + * @param parameters.expensifyCashAppVersion + * @param parameters.logPacket + * @returns Promise */ -function LogCommand(parameters) { +function LogCommand(parameters: Record) { const commandName = 'Log'; requireParameters(['logPacket', 'expensifyCashAppVersion'], parameters, commandName); @@ -27,13 +27,13 @@ function LogCommand(parameters) { /** * Network interface for logger. * - * @param {Logger} logger - * @param {Object} params - * @param {Object} params.parameters - * @param {String} params.message - * @return {Promise} + * @param logger + * @param params + * @param params.parameters + * @param params.message + * @return */ -function serverLoggingCallback(logger, params) { +function serverLoggingCallback(logger: Logger, params: Record) { const requestParams = params; requestParams.shouldProcessImmediately = false; requestParams.shouldRetry = false; @@ -41,7 +41,9 @@ function serverLoggingCallback(logger, params) { if (requestParams.parameters) { requestParams.parameters = JSON.stringify(params.parameters); } - clearTimeout(timeout); + if(timeout) { + clearTimeout(timeout); + } timeout = setTimeout(() => logger.info('Flushing logs older than 10 minutes', true, {}, true), 10 * 60 * 1000); return LogCommand(requestParams); } diff --git a/src/libs/migrations/CheckForPreviousReportActionID.js b/src/libs/migrations/CheckForPreviousReportActionID.ts similarity index 62% rename from src/libs/migrations/CheckForPreviousReportActionID.js rename to src/libs/migrations/CheckForPreviousReportActionID.ts index a61d9bfb08ec..ea1c2e2ad5b9 100644 --- a/src/libs/migrations/CheckForPreviousReportActionID.js +++ b/src/libs/migrations/CheckForPreviousReportActionID.ts @@ -1,17 +1,19 @@ -import _ from 'underscore'; -import Onyx from 'react-native-onyx'; +import Onyx, {OnyxCollection} from 'react-native-onyx'; +import _ from "lodash"; +import lodashHas from "lodash/has"; import Log from '../Log'; import ONYXKEYS from '../../ONYXKEYS'; +import {ReportAction} from "../../types/onyx"; /** - * @returns {Promise} + * @returns */ -function getReportActionsFromOnyx() { +function getReportActionsFromOnyx(): Promise> { return new Promise((resolve) => { const connectionID = Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, waitForCollectionCallback: true, - callback: (allReportActions) => { + callback: (allReportActions: OnyxCollection) => { Onyx.disconnect(connectionID); return resolve(allReportActions); }, @@ -23,9 +25,9 @@ function getReportActionsFromOnyx() { * This migration checks for the 'previousReportActionID' key in the first valid reportAction of a report in Onyx. * If the key is not found then all reportActions for all reports are removed from Onyx. * - * @returns {Promise} + * @returns */ -export default function () { +export default function (): Promise { return getReportActionsFromOnyx().then((allReportActions) => { if (_.isEmpty(allReportActions)) { Log.info(`[Migrate Onyx] Skipped migration CheckForPreviousReportActionID because there were no reportActions`); @@ -33,18 +35,23 @@ export default function () { } let firstValidValue; - _.some(_.values(allReportActions), (reportActions) => - _.some(_.values(reportActions), (reportActionData) => { - if (_.has(reportActionData, 'reportActionID')) { + + const records = allReportActions as Record; + + Object.values(records).some((reportAction: ReportAction) => { + Object.values(reportAction).some((reportActionData: unknown) => { + if (lodashHas(reportActionData, 'reportActionID')) { firstValidValue = reportActionData; return true; } return false; - }), - ); + }); - if (_.isUndefined(firstValidValue)) { + return true; + }); + + if (firstValidValue === undefined) { Log.info(`[Migrate Onyx] Skipped migration CheckForPreviousReportActionID because there were no valid reportActions`); return; } @@ -57,11 +64,12 @@ export default function () { // If previousReportActionID not found: Log.info(`[Migrate Onyx] CheckForPreviousReportActionID Migration: removing all reportActions because previousReportActionID not found in the first valid reportAction`); - const onyxData = {}; - _.each(allReportActions, (reportAction, onyxKey) => { - onyxData[onyxKey] = {}; + const onyxData: Record = {}; + + Object.entries(records).forEach(([onyxKey]) => { + onyxData[onyxKey] = {} as ReportAction; }); - return Onyx.multiSet(onyxData); + return Onyx.multiSet(onyxData as never); }); } From 626de5aec8be864b1723a80545548ff55a4051cd Mon Sep 17 00:00:00 2001 From: Kacper Falat Date: Thu, 5 Oct 2023 09:37:29 +0200 Subject: [PATCH 014/245] Fixes for migrations. --- .../CheckForPreviousReportActionID.ts | 41 +++++++------------ 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/src/libs/migrations/CheckForPreviousReportActionID.ts b/src/libs/migrations/CheckForPreviousReportActionID.ts index ea1c2e2ad5b9..59ce2e9689ba 100644 --- a/src/libs/migrations/CheckForPreviousReportActionID.ts +++ b/src/libs/migrations/CheckForPreviousReportActionID.ts @@ -1,13 +1,8 @@ -import Onyx, {OnyxCollection} from 'react-native-onyx'; -import _ from "lodash"; -import lodashHas from "lodash/has"; +import Onyx, {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import Log from '../Log'; import ONYXKEYS from '../../ONYXKEYS'; import {ReportAction} from "../../types/onyx"; -/** - * @returns - */ function getReportActionsFromOnyx(): Promise> { return new Promise((resolve) => { const connectionID = Onyx.connect({ @@ -24,31 +19,23 @@ function getReportActionsFromOnyx(): Promise> { /** * This migration checks for the 'previousReportActionID' key in the first valid reportAction of a report in Onyx. * If the key is not found then all reportActions for all reports are removed from Onyx. - * - * @returns */ export default function (): Promise { return getReportActionsFromOnyx().then((allReportActions) => { - if (_.isEmpty(allReportActions)) { + if (!Array.isArray(allReportActions) || allReportActions.length === 0) { Log.info(`[Migrate Onyx] Skipped migration CheckForPreviousReportActionID because there were no reportActions`); return; } - let firstValidValue; - - const records = allReportActions as Record; - - Object.values(records).some((reportAction: ReportAction) => { - Object.values(reportAction).some((reportActionData: unknown) => { - if (lodashHas(reportActionData, 'reportActionID')) { - firstValidValue = reportActionData; - return true; - } + let firstValidValue: undefined | ReportAction; - return false; - }); + Object.values(allReportActions as ReportAction[]).some((reportAction) => { + if (reportAction.reportActionID !== undefined) { + firstValidValue = reportAction; + return true; + } - return true; + return false; }); if (firstValidValue === undefined) { @@ -56,7 +43,7 @@ export default function (): Promise { return; } - if (_.has(firstValidValue, 'previousReportActionID')) { + if (Object.hasOwn(firstValidValue, 'previousReportActionID')) { Log.info(`[Migrate Onyx] CheckForPreviousReportActionID Migration: previousReportActionID found. Migration complete`); return; } @@ -64,12 +51,12 @@ export default function (): Promise { // If previousReportActionID not found: Log.info(`[Migrate Onyx] CheckForPreviousReportActionID Migration: removing all reportActions because previousReportActionID not found in the first valid reportAction`); - const onyxData: Record = {}; + const onyxData: Record>> = {}; - Object.entries(records).forEach(([onyxKey]) => { - onyxData[onyxKey] = {} as ReportAction; + Object.entries(allReportActions).forEach(([onyxKey]) => { + onyxData[onyxKey] = {}; }); - return Onyx.multiSet(onyxData as never); + return Onyx.multiSet(onyxData); }); } From 71495589834b33b3f1a957bc40bae336304c47db Mon Sep 17 00:00:00 2001 From: Eduardo Date: Mon, 9 Oct 2023 10:50:14 +0200 Subject: [PATCH 015/245] Moved the DeviceEventEmitter into the markCommentAsUnread function --- src/libs/actions/Report.js | 6 ++---- src/pages/home/report/ContextMenu/ContextMenuActions.js | 4 +--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 19b77fd2c331..37d52baa720b 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1,4 +1,4 @@ -import {InteractionManager} from 'react-native'; +import {InteractionManager, DeviceEventEmitter} from 'react-native'; import _ from 'underscore'; import lodashGet from 'lodash/get'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; @@ -842,8 +842,6 @@ function readNewestAction(reportID) { * * @param {String} reportID * @param {String} reportActionCreated - * - * @returns {String} lastReadTime */ function markCommentAsUnread(reportID, reportActionCreated) { // If no action created date is provided, use the last action's @@ -871,7 +869,7 @@ function markCommentAsUnread(reportID, reportActionCreated) { ], }, ); - return lastReadTime; + DeviceEventEmitter.emit(`unreadAction_${reportID}`, lastReadTime); } /** diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index 919dfd037703..92f6c5a454e8 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -2,7 +2,6 @@ import React from 'react'; import _ from 'underscore'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import lodashGet from 'lodash/get'; -import {DeviceEventEmitter} from 'react-native'; import * as Expensicons from '../../../../components/Icon/Expensicons'; import * as Report from '../../../../libs/actions/Report'; import * as Download from '../../../../libs/actions/Download'; @@ -252,8 +251,7 @@ export default [ shouldShow: (type, reportAction, isArchivedRoom, betas, anchor, isChronosReport, reportID, isPinnedChat, isUnreadChat) => type === CONTEXT_MENU_TYPES.REPORT_ACTION || (type === CONTEXT_MENU_TYPES.REPORT && !isUnreadChat), onPress: (closePopover, {reportAction, reportID}) => { - const lastReadTime = Report.markCommentAsUnread(reportID, reportAction.created); - DeviceEventEmitter.emit(`unreadAction_${reportID}`, lastReadTime); + Report.markCommentAsUnread(reportID, reportAction.created); if (closePopover) { hideContextMenu(true, ReportActionComposeFocusManager.focus); } From 8c5cf6dfd374dd428fa070facc60cabf32e870e1 Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Tue, 10 Oct 2023 10:48:47 +0700 Subject: [PATCH 016/245] fix task description does not remove empty quotes --- src/pages/tasks/NewTaskDescriptionPage.js | 8 ++++++-- src/pages/tasks/NewTaskDetailsPage.js | 9 +++++++-- src/pages/tasks/NewTaskPage.js | 5 ++++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/pages/tasks/NewTaskDescriptionPage.js b/src/pages/tasks/NewTaskDescriptionPage.js index 44fd4346538d..ce8a46c59151 100644 --- a/src/pages/tasks/NewTaskDescriptionPage.js +++ b/src/pages/tasks/NewTaskDescriptionPage.js @@ -3,6 +3,7 @@ import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import {useFocusEffect} from '@react-navigation/native'; import PropTypes from 'prop-types'; +import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; import compose from '../../libs/compose'; import HeaderWithBackButton from '../../components/HeaderWithBackButton'; @@ -39,6 +40,8 @@ const defaultProps = { }, }; +const parser = new ExpensiMark(); + function NewTaskDescriptionPage(props) { const inputRef = useRef(null); const focusTimeoutRef = useRef(null); @@ -62,7 +65,8 @@ function NewTaskDescriptionPage(props) { ); const onSubmit = (values) => { - Task.setDescriptionValue(values.taskDescription); + const parsedTaskDescription = parser.replace(values.taskDescription); + Task.setDescriptionValue(parsedTaskDescription); Navigation.goBack(ROUTES.NEW_TASK); }; @@ -91,7 +95,7 @@ function NewTaskDescriptionPage(props) { > { setTaskTitle(props.task.title); - setTaskDescription(props.task.description || ''); + setTaskDescription(parser.htmlToMarkdown(props.task.description || '')); }, [props.task]); /** @@ -64,7 +67,8 @@ function NewTaskDetailsPage(props) { // On submit, we want to call the assignTask function and wait to validate // the response function onSubmit(values) { - Task.setDetailsValue(values.taskTitle, values.taskDescription); + const parsedTaskDescription = parser.replace(values.taskDescription); + Task.setDetailsValue(values.taskTitle, parsedTaskDescription); Navigation.navigate(ROUTES.NEW_TASK); } @@ -114,6 +118,7 @@ function NewTaskDetailsPage(props) { submitOnEnter={!Browser.isMobile()} containerStyles={[styles.autoGrowHeightMultilineInput]} textAlignVertical="top" + defaultValue={parser.htmlToMarkdown(taskDescription)} value={taskDescription} onValueChange={(value) => setTaskDescription(value)} /> diff --git a/src/pages/tasks/NewTaskPage.js b/src/pages/tasks/NewTaskPage.js index 790bd6ceeb64..5fe1685cdef7 100644 --- a/src/pages/tasks/NewTaskPage.js +++ b/src/pages/tasks/NewTaskPage.js @@ -4,6 +4,7 @@ import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import PropTypes from 'prop-types'; import lodashGet from 'lodash/get'; +import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; import compose from '../../libs/compose'; import HeaderWithBackButton from '../../components/HeaderWithBackButton'; @@ -61,6 +62,7 @@ const defaultProps = { personalDetails: {}, reports: {}, }; +const parser = new ExpensiMark(); function NewTaskPage(props) { const [assignee, setAssignee] = useState({}); @@ -129,7 +131,7 @@ function NewTaskPage(props) { Task.createTaskAndNavigate( parentReport.reportID, props.task.title, - props.task.description, + parser.htmlToMarkdown(props.task.description || ''), props.task.assignee, props.task.assigneeAccountID, props.task.assigneeChatReport, @@ -177,6 +179,7 @@ function NewTaskPage(props) { shouldParseTitle numberOfLinesTitle={2} titleStyle={styles.flex1} + shouldRenderAsHTML /> Date: Tue, 10 Oct 2023 11:03:30 +0700 Subject: [PATCH 017/245] fix bug when save description --- src/pages/tasks/TaskDescriptionPage.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/tasks/TaskDescriptionPage.js b/src/pages/tasks/TaskDescriptionPage.js index be2cdad03fe6..0ba65c5921c4 100644 --- a/src/pages/tasks/TaskDescriptionPage.js +++ b/src/pages/tasks/TaskDescriptionPage.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import {View} from 'react-native'; import {useFocusEffect} from '@react-navigation/native'; import {withOnyx} from 'react-native-onyx'; +import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import ScreenWrapper from '../../components/ScreenWrapper'; import HeaderWithBackButton from '../../components/HeaderWithBackButton'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; @@ -40,6 +41,7 @@ const defaultProps = { report: {}, }; +const parser = new ExpensiMark(); function TaskDescriptionPage(props) { const validate = useCallback(() => ({}), []); @@ -103,7 +105,7 @@ function TaskDescriptionPage(props) { name="description" label={props.translate('newTaskPage.descriptionOptional')} accessibilityLabel={props.translate('newTaskPage.descriptionOptional')} - defaultValue={(props.report && props.report.description) || ''} + defaultValue={parser.htmlToMarkdown((props.report && parser.replace(props.report.description)) || '')} ref={(el) => { if (!el) { return; From 47e4904a46f661fb50354b217932343bc38b8197 Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Tue, 10 Oct 2023 22:14:57 +0700 Subject: [PATCH 018/245] fix update the logic --- src/pages/PrivateNotes/PrivateNotesViewPage.js | 2 +- src/pages/tasks/NewTaskDescriptionPage.js | 5 ++--- src/pages/tasks/NewTaskDetailsPage.js | 8 ++++---- src/pages/tasks/NewTaskPage.js | 6 ++---- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/pages/PrivateNotes/PrivateNotesViewPage.js b/src/pages/PrivateNotes/PrivateNotesViewPage.js index 6550c9d7d3c3..09fe3455afb7 100644 --- a/src/pages/PrivateNotes/PrivateNotesViewPage.js +++ b/src/pages/PrivateNotes/PrivateNotesViewPage.js @@ -85,7 +85,7 @@ function PrivateNotesViewPage({route, personalDetailsList, session, report}) { isCurrentUserNote && Navigation.navigate(ROUTES.PRIVATE_NOTES_EDIT.getRoute(report.reportID, route.params.accountID))} shouldShowRightIcon={isCurrentUserNote} diff --git a/src/pages/tasks/NewTaskDescriptionPage.js b/src/pages/tasks/NewTaskDescriptionPage.js index ce8a46c59151..aedb4ca284db 100644 --- a/src/pages/tasks/NewTaskDescriptionPage.js +++ b/src/pages/tasks/NewTaskDescriptionPage.js @@ -65,8 +65,7 @@ function NewTaskDescriptionPage(props) { ); const onSubmit = (values) => { - const parsedTaskDescription = parser.replace(values.taskDescription); - Task.setDescriptionValue(parsedTaskDescription); + Task.setDescriptionValue(values.taskDescription); Navigation.goBack(ROUTES.NEW_TASK); }; @@ -95,7 +94,7 @@ function NewTaskDescriptionPage(props) { > { setTaskTitle(props.task.title); - setTaskDescription(parser.htmlToMarkdown(props.task.description || '')); + setTaskDescription(parser.htmlToMarkdown(parser.replace(props.task.description || ''))); }, [props.task]); /** @@ -67,8 +68,7 @@ function NewTaskDetailsPage(props) { // On submit, we want to call the assignTask function and wait to validate // the response function onSubmit(values) { - const parsedTaskDescription = parser.replace(values.taskDescription); - Task.setDetailsValue(values.taskTitle, parsedTaskDescription); + Task.setDetailsValue(values.taskTitle, values.taskDescription); Navigation.navigate(ROUTES.NEW_TASK); } @@ -118,7 +118,7 @@ function NewTaskDetailsPage(props) { submitOnEnter={!Browser.isMobile()} containerStyles={[styles.autoGrowHeightMultilineInput]} textAlignVertical="top" - defaultValue={parser.htmlToMarkdown(taskDescription)} + defaultValue={parser.htmlToMarkdown(parser.replace(taskDescription))} value={taskDescription} onValueChange={(value) => setTaskDescription(value)} /> diff --git a/src/pages/tasks/NewTaskPage.js b/src/pages/tasks/NewTaskPage.js index 5fe1685cdef7..8635d8378811 100644 --- a/src/pages/tasks/NewTaskPage.js +++ b/src/pages/tasks/NewTaskPage.js @@ -71,7 +71,6 @@ function NewTaskPage(props) { const [description, setDescription] = useState(''); const [errorMessage, setErrorMessage] = useState(''); const [parentReport, setParentReport] = useState({}); - const isAllowedToCreateTask = useMemo(() => _.isEmpty(parentReport) || ReportUtils.isAllowedToComment(parentReport), [parentReport]); useEffect(() => { @@ -131,7 +130,7 @@ function NewTaskPage(props) { Task.createTaskAndNavigate( parentReport.reportID, props.task.title, - parser.htmlToMarkdown(props.task.description || ''), + props.task.description, props.task.assignee, props.task.assigneeAccountID, props.task.assigneeChatReport, @@ -173,13 +172,12 @@ function NewTaskPage(props) { /> Navigation.navigate(ROUTES.NEW_TASK_DESCRIPTION)} shouldShowRightIcon shouldParseTitle numberOfLinesTitle={2} titleStyle={styles.flex1} - shouldRenderAsHTML /> Date: Tue, 10 Oct 2023 22:15:42 +0700 Subject: [PATCH 019/245] fix remove redundant code --- src/pages/PrivateNotes/PrivateNotesViewPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/PrivateNotes/PrivateNotesViewPage.js b/src/pages/PrivateNotes/PrivateNotesViewPage.js index 09fe3455afb7..6550c9d7d3c3 100644 --- a/src/pages/PrivateNotes/PrivateNotesViewPage.js +++ b/src/pages/PrivateNotes/PrivateNotesViewPage.js @@ -85,7 +85,7 @@ function PrivateNotesViewPage({route, personalDetailsList, session, report}) { isCurrentUserNote && Navigation.navigate(ROUTES.PRIVATE_NOTES_EDIT.getRoute(report.reportID, route.params.accountID))} shouldShowRightIcon={isCurrentUserNote} From 9797b1f126fda336eae1b0c5df81372b4f762933 Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Tue, 10 Oct 2023 22:23:23 +0700 Subject: [PATCH 020/245] fix lint --- src/pages/tasks/NewTaskDetailsPage.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/tasks/NewTaskDetailsPage.js b/src/pages/tasks/NewTaskDetailsPage.js index 21131a36b69d..29e1c80c5062 100644 --- a/src/pages/tasks/NewTaskDetailsPage.js +++ b/src/pages/tasks/NewTaskDetailsPage.js @@ -18,7 +18,6 @@ import ROUTES from '../../ROUTES'; import * as Task from '../../libs/actions/Task'; import CONST from '../../CONST'; import * as Browser from '../../libs/Browser'; -import {useFocusEffect} from '@react-navigation/native'; const propTypes = { /** Beta features list */ From c8aeb9b571cd6140e209cb50bf136ff3efb76a51 Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Wed, 11 Oct 2023 01:12:59 +0700 Subject: [PATCH 021/245] fix android issue whenn displaying desc --- src/components/ReportActionItem/TaskView.js | 6 ++++-- src/pages/tasks/NewTaskPage.js | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/ReportActionItem/TaskView.js b/src/components/ReportActionItem/TaskView.js index 7cddc7a969dc..dd5e09478e4f 100644 --- a/src/components/ReportActionItem/TaskView.js +++ b/src/components/ReportActionItem/TaskView.js @@ -2,6 +2,7 @@ import React, {useEffect} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import lodashGet from 'lodash/get'; +import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import reportPropTypes from '../../pages/reportPropTypes'; import withLocalize, {withLocalizePropTypes} from '../withLocalize'; import withWindowDimensions from '../withWindowDimensions'; @@ -40,7 +41,7 @@ const propTypes = { ...withCurrentUserPersonalDetailsPropTypes, }; - +const parser = new ExpensiMark(); function TaskView(props) { useEffect(() => { Task.setTaskReport({...props.report}); @@ -130,7 +131,7 @@ function TaskView(props) { Navigation.navigate(ROUTES.TASK_DESCRIPTION.getRoute(props.report.reportID))} shouldShowRightIcon={isOpen} disabled={disableState} @@ -138,6 +139,7 @@ function TaskView(props) { shouldGreyOutWhenDisabled={false} numberOfLinesTitle={0} interactive={!isDisableInteractive} + shouldRenderAsHTML /> {props.report.managerID ? ( diff --git a/src/pages/tasks/NewTaskPage.js b/src/pages/tasks/NewTaskPage.js index 8635d8378811..441a0a3ec276 100644 --- a/src/pages/tasks/NewTaskPage.js +++ b/src/pages/tasks/NewTaskPage.js @@ -172,10 +172,10 @@ function NewTaskPage(props) { /> Navigation.navigate(ROUTES.NEW_TASK_DESCRIPTION)} shouldShowRightIcon - shouldParseTitle + shouldRenderAsHTML numberOfLinesTitle={2} titleStyle={styles.flex1} /> From 8224acd52880748b1eb2db138759d5c99bfb18fe Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Wed, 11 Oct 2023 14:43:49 +0700 Subject: [PATCH 022/245] fix revert logic fixing display description --- src/components/ReportActionItem/TaskView.js | 6 ++---- src/pages/tasks/NewTaskPage.js | 5 +---- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/components/ReportActionItem/TaskView.js b/src/components/ReportActionItem/TaskView.js index dd5e09478e4f..7cddc7a969dc 100644 --- a/src/components/ReportActionItem/TaskView.js +++ b/src/components/ReportActionItem/TaskView.js @@ -2,7 +2,6 @@ import React, {useEffect} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import lodashGet from 'lodash/get'; -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import reportPropTypes from '../../pages/reportPropTypes'; import withLocalize, {withLocalizePropTypes} from '../withLocalize'; import withWindowDimensions from '../withWindowDimensions'; @@ -41,7 +40,7 @@ const propTypes = { ...withCurrentUserPersonalDetailsPropTypes, }; -const parser = new ExpensiMark(); + function TaskView(props) { useEffect(() => { Task.setTaskReport({...props.report}); @@ -131,7 +130,7 @@ function TaskView(props) { Navigation.navigate(ROUTES.TASK_DESCRIPTION.getRoute(props.report.reportID))} shouldShowRightIcon={isOpen} disabled={disableState} @@ -139,7 +138,6 @@ function TaskView(props) { shouldGreyOutWhenDisabled={false} numberOfLinesTitle={0} interactive={!isDisableInteractive} - shouldRenderAsHTML /> {props.report.managerID ? ( diff --git a/src/pages/tasks/NewTaskPage.js b/src/pages/tasks/NewTaskPage.js index 441a0a3ec276..bb87e4b053d6 100644 --- a/src/pages/tasks/NewTaskPage.js +++ b/src/pages/tasks/NewTaskPage.js @@ -4,7 +4,6 @@ import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import PropTypes from 'prop-types'; import lodashGet from 'lodash/get'; -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; import compose from '../../libs/compose'; import HeaderWithBackButton from '../../components/HeaderWithBackButton'; @@ -62,7 +61,6 @@ const defaultProps = { personalDetails: {}, reports: {}, }; -const parser = new ExpensiMark(); function NewTaskPage(props) { const [assignee, setAssignee] = useState({}); @@ -172,10 +170,9 @@ function NewTaskPage(props) { /> Navigation.navigate(ROUTES.NEW_TASK_DESCRIPTION)} shouldShowRightIcon - shouldRenderAsHTML numberOfLinesTitle={2} titleStyle={styles.flex1} /> From cf07ccb63a67fc22b06fa8e446937a7269528326 Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Wed, 11 Oct 2023 14:46:32 +0700 Subject: [PATCH 023/245] fix revert all related to display description --- src/pages/tasks/NewTaskPage.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/tasks/NewTaskPage.js b/src/pages/tasks/NewTaskPage.js index bb87e4b053d6..790bd6ceeb64 100644 --- a/src/pages/tasks/NewTaskPage.js +++ b/src/pages/tasks/NewTaskPage.js @@ -69,6 +69,7 @@ function NewTaskPage(props) { const [description, setDescription] = useState(''); const [errorMessage, setErrorMessage] = useState(''); const [parentReport, setParentReport] = useState({}); + const isAllowedToCreateTask = useMemo(() => _.isEmpty(parentReport) || ReportUtils.isAllowedToComment(parentReport), [parentReport]); useEffect(() => { @@ -173,6 +174,7 @@ function NewTaskPage(props) { title={description} onPress={() => Navigation.navigate(ROUTES.NEW_TASK_DESCRIPTION)} shouldShowRightIcon + shouldParseTitle numberOfLinesTitle={2} titleStyle={styles.flex1} /> From f40773ba045de3050b22ec4d2ca3fcd49828df45 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Fri, 13 Oct 2023 10:28:58 +0200 Subject: [PATCH 024/245] Updated scrolling direction and amount --- tests/ui/UnreadIndicatorsTest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/UnreadIndicatorsTest.js b/tests/ui/UnreadIndicatorsTest.js index 361eb8f87081..94f55b1e9b86 100644 --- a/tests/ui/UnreadIndicatorsTest.js +++ b/tests/ui/UnreadIndicatorsTest.js @@ -78,7 +78,7 @@ function scrollUpToRevealNewMessagesBadge() { function isNewMessagesBadgeVisible() { const hintText = Localize.translateLocal('accessibilityHints.scrollToNewestMessages'); const badge = screen.queryByAccessibilityHint(hintText); - return Math.round(badge.props.style.transform[0].translateY) === 10; + return Math.round(badge.props.style.transform[0].translateY) === -40; } /** From 4fb1a60e6b5680c355ade566a13370e1aff381e6 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 13 Oct 2023 13:30:40 +0200 Subject: [PATCH 025/245] [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 026/245] 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 59fd5bfcc96acaef0bcb791439012ef99f4b2267 Mon Sep 17 00:00:00 2001 From: Kacper Falat Date: Mon, 16 Oct 2023 13:11:21 +0200 Subject: [PATCH 027/245] Fixed typing issues. --- src/ONYXKEYS.ts | 2 +- .../CheckForPreviousReportActionID.ts | 40 +++++++------- src/types/onyx/ReportAction.ts | 4 ++ src/types/onyx/index.ts | 3 +- tests/unit/MigrationTest.js | 52 +++++++++---------- 5 files changed, 54 insertions(+), 47 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index d9ea3488f85f..6861473694b0 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -386,7 +386,7 @@ type OnyxValues = { [ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT]: Record; [ONYXKEYS.COLLECTION.REPORT]: OnyxTypes.Report; [ONYXKEYS.COLLECTION.REPORT_METADATA]: OnyxTypes.ReportMetadata; - [ONYXKEYS.COLLECTION.REPORT_ACTIONS]: OnyxTypes.ReportAction; + [ONYXKEYS.COLLECTION.REPORT_ACTIONS]: OnyxTypes.ReportActions; [ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS]: string; [ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS]: OnyxTypes.ReportActionReactions; [ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT]: string; diff --git a/src/libs/migrations/CheckForPreviousReportActionID.ts b/src/libs/migrations/CheckForPreviousReportActionID.ts index 59ce2e9689ba..2224674a35fe 100644 --- a/src/libs/migrations/CheckForPreviousReportActionID.ts +++ b/src/libs/migrations/CheckForPreviousReportActionID.ts @@ -1,14 +1,14 @@ -import Onyx, {OnyxCollection, OnyxEntry} from 'react-native-onyx'; -import Log from '../Log'; +import Onyx, {OnyxCollection} from 'react-native-onyx'; import ONYXKEYS from '../../ONYXKEYS'; -import {ReportAction} from "../../types/onyx"; +import * as OnyxTypes from '../../types/onyx'; +import Log from '../Log'; -function getReportActionsFromOnyx(): Promise> { +function getReportActionsFromOnyx(): Promise> { return new Promise((resolve) => { const connectionID = Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, waitForCollectionCallback: true, - callback: (allReportActions: OnyxCollection) => { + callback: (allReportActions: OnyxCollection) => { Onyx.disconnect(connectionID); return resolve(allReportActions); }, @@ -22,28 +22,30 @@ function getReportActionsFromOnyx(): Promise> { */ export default function (): Promise { return getReportActionsFromOnyx().then((allReportActions) => { - if (!Array.isArray(allReportActions) || allReportActions.length === 0) { + if (Object.keys(allReportActions ?? {}).length === 0) { Log.info(`[Migrate Onyx] Skipped migration CheckForPreviousReportActionID because there were no reportActions`); return; } - let firstValidValue: undefined | ReportAction; + let firstValidValue: OnyxTypes.ReportAction | undefined; - Object.values(allReportActions as ReportAction[]).some((reportAction) => { - if (reportAction.reportActionID !== undefined) { - firstValidValue = reportAction; - return true; - } + Object.values(allReportActions ?? {}).some((reportActions) => + Object.values(reportActions ?? {}).some((reportActionData) => { + if (reportActionData?.reportActionID) { + firstValidValue = reportActionData; + return true; + } - return false; - }); + return false; + }), + ); - if (firstValidValue === undefined) { + if (!firstValidValue) { Log.info(`[Migrate Onyx] Skipped migration CheckForPreviousReportActionID because there were no valid reportActions`); return; } - if (Object.hasOwn(firstValidValue, 'previousReportActionID')) { + if (firstValidValue.previousReportActionID) { Log.info(`[Migrate Onyx] CheckForPreviousReportActionID Migration: previousReportActionID found. Migration complete`); return; } @@ -51,12 +53,12 @@ export default function (): Promise { // If previousReportActionID not found: Log.info(`[Migrate Onyx] CheckForPreviousReportActionID Migration: removing all reportActions because previousReportActionID not found in the first valid reportAction`); - const onyxData: Record>> = {}; + const onyxData: OnyxCollection = {}; - Object.entries(allReportActions).forEach(([onyxKey]) => { + Object.keys(allReportActions ?? {}).forEach((onyxKey) => { onyxData[onyxKey] = {}; }); - return Onyx.multiSet(onyxData); + return Onyx.multiSet(onyxData as Record<`${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS}`, Record>); }); } diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index 5cbb02f08722..819a9ecd7850 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -85,8 +85,12 @@ type ReportActionBase = { childVisibleActionCount?: number; pendingAction?: OnyxCommon.PendingAction; + errors?: OnyxCommon.Errors; }; type ReportAction = ReportActionBase & OriginalMessage; +type ReportActions = Record; + export default ReportAction; +export type {ReportActions}; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index e50925e7adf2..cb064869427e 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -37,7 +37,7 @@ import Policy from './Policy'; import PolicyCategory from './PolicyCategory'; import Report from './Report'; import ReportMetadata from './ReportMetadata'; -import ReportAction from './ReportAction'; +import ReportAction, {ReportActions} from './ReportAction'; import ReportActionReactions from './ReportActionReactions'; import SecurityGroup from './SecurityGroup'; import Transaction from './Transaction'; @@ -87,6 +87,7 @@ export type { Report, ReportMetadata, ReportAction, + ReportActions, ReportActionReactions, SecurityGroup, Transaction, diff --git a/tests/unit/MigrationTest.js b/tests/unit/MigrationTest.js index d0e7f19d3d3f..bc1b03084b99 100644 --- a/tests/unit/MigrationTest.js +++ b/tests/unit/MigrationTest.js @@ -463,10 +463,10 @@ describe('Migrations', () => { Onyx.multiSet({ [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]: { 1: { - reportActionID: 1, + reportActionID: '1', }, 2: { - reportActionID: 2, + reportActionID: '2', }, }, }) @@ -490,12 +490,12 @@ describe('Migrations', () => { Onyx.multiSet({ [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]: { 1: { - reportActionID: 1, - previousReportActionID: 0, + reportActionID: '1', + previousReportActionID: '0', }, 2: { - reportActionID: 2, - previousReportActionID: 1, + reportActionID: '2', + previousReportActionID: '1', }, }, }) @@ -509,12 +509,12 @@ describe('Migrations', () => { Onyx.disconnect(connectionID); const expectedReportAction = { 1: { - reportActionID: 1, - previousReportActionID: 0, + reportActionID: '1', + previousReportActionID: '0', }, 2: { - reportActionID: 2, - previousReportActionID: 1, + reportActionID: '2', + previousReportActionID: '1', }, }; expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).toMatchObject(expectedReportAction); @@ -529,10 +529,10 @@ describe('Migrations', () => { [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}3`]: null, [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}4`]: { 1: { - reportActionID: 1, + reportActionID: '1', }, 2: { - reportActionID: 2, + reportActionID: '2', }, }, }) @@ -548,8 +548,8 @@ describe('Migrations', () => { Onyx.disconnect(connectionID); const expectedReportAction = {}; expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).toMatchObject(expectedReportAction); - expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}2`]).toBeUndefined(); - expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}3`]).toBeUndefined(); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}2`]).toMatchObject(expectedReportAction); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}3`]).toMatchObject(expectedReportAction); expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}4`]).toMatchObject(expectedReportAction); }, }); @@ -562,12 +562,12 @@ describe('Migrations', () => { [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}3`]: null, [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}4`]: { 1: { - reportActionID: 1, - previousReportActionID: 10, + reportActionID: '1', + previousReportActionID: '10', }, 2: { - reportActionID: 2, - previousReportActionID: 23, + reportActionID: '2', + previousReportActionID: '23', }, }, }) @@ -582,17 +582,17 @@ describe('Migrations', () => { const expectedReportAction1 = {}; const expectedReportAction4 = { 1: { - reportActionID: 1, - previousReportActionID: 10, + reportActionID: '1', + previousReportActionID: '10', }, 2: { - reportActionID: 2, - previousReportActionID: 23, + reportActionID: '2', + previousReportActionID: '23', }, }; expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).toMatchObject(expectedReportAction1); - expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}2`]).toBeUndefined(); - expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}3`]).toBeUndefined(); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}2`]).toBeNull(); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}3`]).toBeNull(); expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}4`]).toMatchObject(expectedReportAction4); }, }); @@ -614,10 +614,10 @@ describe('Migrations', () => { callback: (allReportActions) => { Onyx.disconnect(connectionID); const expectedReportAction = {}; - expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).toBeUndefined(); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).toBeNull(); expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}2`]).toMatchObject(expectedReportAction); expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}3`]).toMatchObject(expectedReportAction); - expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}4`]).toBeUndefined(); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}4`]).toBeNull(); }, }); })); From 78a09b7b4e99e4f4521da604b334977664aea695 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Mon, 16 Oct 2023 15:09:32 +0200 Subject: [PATCH 028/245] ref: moved FormElement to TS --- ios/Podfile.lock | 10 +++++----- src/components/FormElement.js | 16 ---------------- src/components/FormElement.tsx | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 21 deletions(-) delete mode 100644 src/components/FormElement.js create mode 100644 src/components/FormElement.tsx diff --git a/ios/Podfile.lock b/ios/Podfile.lock index cb120bca2b88..db14878a397a 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -251,9 +251,9 @@ PODS: - nanopb/encode (= 2.30908.0) - nanopb/decode (2.30908.0) - nanopb/encode (2.30908.0) - - Onfido (28.3.0) - - onfido-react-native-sdk (8.3.0): - - Onfido (~> 28.3.0) + - Onfido (27.4.0) + - onfido-react-native-sdk (7.4.0): + - Onfido (= 27.4.0) - React - OpenSSL-Universal (1.1.1100) - Plaid (4.1.0) @@ -1204,8 +1204,8 @@ SPEC CHECKSUMS: MapboxMaps: af50ec61a7eb3b032c3f7962c6bd671d93d2a209 MapboxMobileEvents: de50b3a4de180dd129c326e09cd12c8adaaa46d6 nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 - Onfido: c7d010d9793790d44a07799d9be25aa8e3814ee7 - onfido-react-native-sdk: b346a620af5669f9fecb6dc3052314a35a94ad9f + Onfido: e36f284b865adcf99d9c905590a64ac09d4a576b + onfido-react-native-sdk: 4ecde1a97435dcff9f00a878e3f8d1eb14fabbdc OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c Plaid: 7d340abeadb46c7aa1a91f896c5b22395a31fcf2 PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef diff --git a/src/components/FormElement.js b/src/components/FormElement.js deleted file mode 100644 index f46b24708c4c..000000000000 --- a/src/components/FormElement.js +++ /dev/null @@ -1,16 +0,0 @@ -import React, {forwardRef} from 'react'; -import {View} from 'react-native'; -import * as ComponentUtils from '../libs/ComponentUtils'; - -const FormElement = forwardRef((props, ref) => ( - -)); - -FormElement.displayName = 'BaseForm'; -export default FormElement; diff --git a/src/components/FormElement.tsx b/src/components/FormElement.tsx new file mode 100644 index 000000000000..d37e792ca6a6 --- /dev/null +++ b/src/components/FormElement.tsx @@ -0,0 +1,19 @@ +import React, {ForwardedRef, forwardRef} from 'react'; +import {View, ViewStyle} from 'react-native'; +import * as ComponentUtils from '../libs/ComponentUtils'; + +function FormElement(props: {style: ViewStyle; children: React.ReactNode}, ref: ForwardedRef) { + console.debug('hej', JSON.stringify(ref)); + return ( + + ); +} + +FormElement.displayName = 'BaseForm'; +export default forwardRef(FormElement); From c784057d3ede873ed8ec6f94620e78867e8ea453 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Wed, 18 Oct 2023 10:35:21 +0200 Subject: [PATCH 029/245] Fix to add report into map --- ios/Podfile.lock | 2 +- src/pages/home/report/ReportActionsList.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index a4a8089c9c05..1979fdbb37ea 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1293,4 +1293,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: ff769666b7221c15936ebc5576a8c8e467dc6879 -COCOAPODS: 1.12.1 +COCOAPODS: 1.13.0 diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 5d304ee3cdca..c55b3a810d0a 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -322,7 +322,9 @@ function ReportActionsList({ const canDisplayMarker = scrollingVerticalOffset.current < MSG_VISIBLE_THRESHOLD ? reportAction.created < userActiveSince.current : true; if (!currentUnreadMarker && shouldDisplayNewMarker && canDisplayMarker) { + cacheUnreadMarkers.set(report.reportID, reportAction.reportActionID); setCurrentUnreadMarker(reportAction.reportActionID); + shouldDisplayNewMarker = true; } } else { shouldDisplayNewMarker = reportAction.reportActionID === currentUnreadMarker; From eee1e833ed7e531daaa4444d47fe5512310f087b Mon Sep 17 00:00:00 2001 From: Eduardo Date: Wed, 18 Oct 2023 15:34:14 +0200 Subject: [PATCH 030/245] Fixed green line placement --- src/pages/home/report/ReportActionsList.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index a57f8320ed0a..ded764a70b3f 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -310,10 +310,11 @@ function ReportActionsList({ const shouldDisplayNewMarker = useCallback( (reportAction, index) => { let shouldDisplay = false; + if (!currentUnreadMarker) { const nextMessage = sortedReportActions[index + 1]; - const isCurrentMessageUnread = isMessageUnread(reportAction, report.lastReadTime); - shouldDisplay = isCurrentMessageUnread && !isMessageUnread(nextMessage, report.lastReadTime); + const isCurrentMessageUnread = isMessageUnread(reportAction, lastReadRef.current); + shouldDisplay = isCurrentMessageUnread && !isMessageUnread(nextMessage, lastReadRef.current); if (!messageManuallyMarkedUnread) { shouldDisplay = shouldDisplay && reportAction.actorAccountID !== Report.getCurrentUserAccountID(); } @@ -323,6 +324,7 @@ function ReportActionsList({ } else { shouldDisplay = reportAction.reportActionID === currentUnreadMarker; } + return shouldDisplay; }, [currentUnreadMarker, sortedReportActions, report.lastReadTime, messageManuallyMarkedUnread], @@ -337,6 +339,7 @@ function ReportActionsList({ return; } if (!currentUnreadMarker && currentUnreadMarker !== reportAction.reportActionID) { + cacheUnreadMarkers.set(report.reportID, reportAction.reportActionID); setCurrentUnreadMarker(reportAction.reportActionID); } }); From f1de9240a250cfb31ea9a56b4aff8e6320dc3557 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Wed, 18 Oct 2023 16:22:00 +0200 Subject: [PATCH 031/245] fixed lint errors --- ios/Podfile.lock | 2 +- src/pages/home/report/ReportActionsList.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index b3933fe7b1a3..f41c2880563b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1293,4 +1293,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: ff769666b7221c15936ebc5576a8c8e467dc6879 -COCOAPODS: 1.13.0 +COCOAPODS: 1.12.1 \ No newline at end of file diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index ded764a70b3f..43ebe7c15271 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -327,7 +327,7 @@ function ReportActionsList({ return shouldDisplay; }, - [currentUnreadMarker, sortedReportActions, report.lastReadTime, messageManuallyMarkedUnread], + [currentUnreadMarker, sortedReportActions, report.lastReadTime, report.reportID, messageManuallyMarkedUnread], ); useEffect(() => { @@ -343,7 +343,7 @@ function ReportActionsList({ setCurrentUnreadMarker(reportAction.reportActionID); } }); - }, [sortedReportActions, report.lastReadTime, messageManuallyMarkedUnread, shouldDisplayNewMarker, currentUnreadMarker]); + }, [sortedReportActions, report.lastReadTime, report.reportID, messageManuallyMarkedUnread, shouldDisplayNewMarker, currentUnreadMarker]); const renderItem = useCallback( ({item: reportAction, index}) => ( From c8d48532458284670e45e0a585b4307e38408360 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Wed, 18 Oct 2023 16:39:06 +0200 Subject: [PATCH 032/245] fixed lint issue --- src/pages/home/report/ReportActionsList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 43ebe7c15271..08f11c16abe9 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -327,7 +327,7 @@ function ReportActionsList({ return shouldDisplay; }, - [currentUnreadMarker, sortedReportActions, report.lastReadTime, report.reportID, messageManuallyMarkedUnread], + [currentUnreadMarker, sortedReportActions, report.reportID, messageManuallyMarkedUnread], ); useEffect(() => { From c0f2af25f40f3a2bbf637a07864f9064042dd76b Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Thu, 19 Oct 2023 14:20:47 +0200 Subject: [PATCH 033/245] 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 c9722d90b4644e4fc6af35e2324c883f9554d86e Mon Sep 17 00:00:00 2001 From: Eduardo Date: Fri, 20 Oct 2023 07:10:53 +0200 Subject: [PATCH 034/245] Fixed test --- src/components/EmojiPicker/EmojiPicker.js | 4 +++- tests/ui/UnreadIndicatorsTest.js | 16 ++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/components/EmojiPicker/EmojiPicker.js b/src/components/EmojiPicker/EmojiPicker.js index a12b089ddf97..95a852f6489f 100644 --- a/src/components/EmojiPicker/EmojiPicker.js +++ b/src/components/EmojiPicker/EmojiPicker.js @@ -134,7 +134,9 @@ const EmojiPicker = forwardRef((props, ref) => { }); }); return () => { - emojiPopoverDimensionListener.remove(); + if (emojiPopoverDimensionListener) { + emojiPopoverDimensionListener.remove(); + } }; }, [isEmojiPickerVisible, props.isSmallScreenWidth, emojiPopoverAnchorOrigin]); diff --git a/tests/ui/UnreadIndicatorsTest.js b/tests/ui/UnreadIndicatorsTest.js index 9bbe893877ed..a3370a54d7e2 100644 --- a/tests/ui/UnreadIndicatorsTest.js +++ b/tests/ui/UnreadIndicatorsTest.js @@ -1,6 +1,6 @@ import React from 'react'; import Onyx from 'react-native-onyx'; -import {Linking, AppState} from 'react-native'; +import {Linking, AppState, DeviceEventEmitter} from 'react-native'; import {fireEvent, render, screen, waitFor} from '@testing-library/react-native'; import lodashGet from 'lodash/get'; import {subMinutes, format, addSeconds, subSeconds} from 'date-fns'; @@ -251,8 +251,12 @@ describe('Unread Indicators', () => { signInAndGetAppWithUnreadChat() // Navigate to the unread chat from the sidebar .then(() => navigateToSidebarOption(0)) - // Navigate to the unread chat from the sidebar - .then(() => navigateToSidebarOption(0)) + .then(() => { + // Verify the unread indicator is present + const newMessageLineIndicatorHintText = Localize.translateLocal('accessibilityHints.newMessageLineIndicator'); + const unreadIndicator = screen.queryAllByLabelText(newMessageLineIndicatorHintText); + expect(unreadIndicator).toHaveLength(1); + }) .then(() => { expect(areYouOnChatListScreen()).toBe(false); @@ -263,9 +267,13 @@ describe('Unread Indicators', () => { // Verify the LHN is now open expect(areYouOnChatListScreen()).toBe(true); - // Tap on the chat again return navigateToSidebarOption(0); }) + .then(() => { + // Sending event to clear the unread indicator cache, given that the test doesn't behave as the app + DeviceEventEmitter.emit(`unreadAction_${REPORT_ID}`, format(new Date(), CONST.DATE.FNS_DB_FORMAT_STRING)); + return waitForBatchedUpdatesWithAct(); + }) .then(() => { // Verify the unread indicator is not present const newMessageLineIndicatorHintText = Localize.translateLocal('accessibilityHints.newMessageLineIndicator'); From 8782bf7c0abd3f3bc938737299117be1373e1db9 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Fri, 20 Oct 2023 07:17:24 +0200 Subject: [PATCH 035/245] fixed lint error --- src/components/EmojiPicker/EmojiPicker.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/EmojiPicker/EmojiPicker.js b/src/components/EmojiPicker/EmojiPicker.js index 95a852f6489f..633fd5f6b899 100644 --- a/src/components/EmojiPicker/EmojiPicker.js +++ b/src/components/EmojiPicker/EmojiPicker.js @@ -134,9 +134,10 @@ const EmojiPicker = forwardRef((props, ref) => { }); }); return () => { - if (emojiPopoverDimensionListener) { - emojiPopoverDimensionListener.remove(); + if (!emojiPopoverDimensionListener) { + return; } + emojiPopoverDimensionListener.remove(); }; }, [isEmojiPickerVisible, props.isSmallScreenWidth, emojiPopoverAnchorOrigin]); From 2877065e334fcb75e1baf2bf9f7334cebb8671e1 Mon Sep 17 00:00:00 2001 From: Kacper Falat Date: Fri, 20 Oct 2023 11:20:15 +0200 Subject: [PATCH 036/245] After review changes. --- src/libs/migrations/CheckForPreviousReportActionID.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/migrations/CheckForPreviousReportActionID.ts b/src/libs/migrations/CheckForPreviousReportActionID.ts index 2224674a35fe..40fd95216558 100644 --- a/src/libs/migrations/CheckForPreviousReportActionID.ts +++ b/src/libs/migrations/CheckForPreviousReportActionID.ts @@ -8,7 +8,7 @@ function getReportActionsFromOnyx(): Promise) => { + callback: (allReportActions) => { Onyx.disconnect(connectionID); return resolve(allReportActions); }, @@ -31,7 +31,7 @@ export default function (): Promise { Object.values(allReportActions ?? {}).some((reportActions) => Object.values(reportActions ?? {}).some((reportActionData) => { - if (reportActionData?.reportActionID) { + if ('reportActionID' in reportActionData) { firstValidValue = reportActionData; return true; } From b0b64cb9b79e9b55a6ddaab7f5c0b37313fb913d Mon Sep 17 00:00:00 2001 From: Kacper Falat Date: Fri, 20 Oct 2023 11:25:53 +0200 Subject: [PATCH 037/245] Reverted own test changes. --- tests/unit/MigrationTest.js | 52 ++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/tests/unit/MigrationTest.js b/tests/unit/MigrationTest.js index bc1b03084b99..d0e7f19d3d3f 100644 --- a/tests/unit/MigrationTest.js +++ b/tests/unit/MigrationTest.js @@ -463,10 +463,10 @@ describe('Migrations', () => { Onyx.multiSet({ [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]: { 1: { - reportActionID: '1', + reportActionID: 1, }, 2: { - reportActionID: '2', + reportActionID: 2, }, }, }) @@ -490,12 +490,12 @@ describe('Migrations', () => { Onyx.multiSet({ [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]: { 1: { - reportActionID: '1', - previousReportActionID: '0', + reportActionID: 1, + previousReportActionID: 0, }, 2: { - reportActionID: '2', - previousReportActionID: '1', + reportActionID: 2, + previousReportActionID: 1, }, }, }) @@ -509,12 +509,12 @@ describe('Migrations', () => { Onyx.disconnect(connectionID); const expectedReportAction = { 1: { - reportActionID: '1', - previousReportActionID: '0', + reportActionID: 1, + previousReportActionID: 0, }, 2: { - reportActionID: '2', - previousReportActionID: '1', + reportActionID: 2, + previousReportActionID: 1, }, }; expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).toMatchObject(expectedReportAction); @@ -529,10 +529,10 @@ describe('Migrations', () => { [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}3`]: null, [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}4`]: { 1: { - reportActionID: '1', + reportActionID: 1, }, 2: { - reportActionID: '2', + reportActionID: 2, }, }, }) @@ -548,8 +548,8 @@ describe('Migrations', () => { Onyx.disconnect(connectionID); const expectedReportAction = {}; expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).toMatchObject(expectedReportAction); - expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}2`]).toMatchObject(expectedReportAction); - expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}3`]).toMatchObject(expectedReportAction); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}2`]).toBeUndefined(); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}3`]).toBeUndefined(); expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}4`]).toMatchObject(expectedReportAction); }, }); @@ -562,12 +562,12 @@ describe('Migrations', () => { [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}3`]: null, [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}4`]: { 1: { - reportActionID: '1', - previousReportActionID: '10', + reportActionID: 1, + previousReportActionID: 10, }, 2: { - reportActionID: '2', - previousReportActionID: '23', + reportActionID: 2, + previousReportActionID: 23, }, }, }) @@ -582,17 +582,17 @@ describe('Migrations', () => { const expectedReportAction1 = {}; const expectedReportAction4 = { 1: { - reportActionID: '1', - previousReportActionID: '10', + reportActionID: 1, + previousReportActionID: 10, }, 2: { - reportActionID: '2', - previousReportActionID: '23', + reportActionID: 2, + previousReportActionID: 23, }, }; expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).toMatchObject(expectedReportAction1); - expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}2`]).toBeNull(); - expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}3`]).toBeNull(); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}2`]).toBeUndefined(); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}3`]).toBeUndefined(); expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}4`]).toMatchObject(expectedReportAction4); }, }); @@ -614,10 +614,10 @@ describe('Migrations', () => { callback: (allReportActions) => { Onyx.disconnect(connectionID); const expectedReportAction = {}; - expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).toBeNull(); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).toBeUndefined(); expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}2`]).toMatchObject(expectedReportAction); expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}3`]).toMatchObject(expectedReportAction); - expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}4`]).toBeNull(); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}4`]).toBeUndefined(); }, }); })); From 00db0cb7b112c76873e5e42af83393026499aaf4 Mon Sep 17 00:00:00 2001 From: Kacper Falat Date: Fri, 20 Oct 2023 12:10:37 +0200 Subject: [PATCH 038/245] Fixed tests. --- .../CheckForPreviousReportActionID.ts | 2 +- tests/unit/MigrationTest.js | 40 +++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/libs/migrations/CheckForPreviousReportActionID.ts b/src/libs/migrations/CheckForPreviousReportActionID.ts index 40fd95216558..5f0e8e8d1c96 100644 --- a/src/libs/migrations/CheckForPreviousReportActionID.ts +++ b/src/libs/migrations/CheckForPreviousReportActionID.ts @@ -31,7 +31,7 @@ export default function (): Promise { Object.values(allReportActions ?? {}).some((reportActions) => Object.values(reportActions ?? {}).some((reportActionData) => { - if ('reportActionID' in reportActionData) { + if (reportActionData.reportActionID?.length) { firstValidValue = reportActionData; return true; } diff --git a/tests/unit/MigrationTest.js b/tests/unit/MigrationTest.js index d0e7f19d3d3f..66aa8646296e 100644 --- a/tests/unit/MigrationTest.js +++ b/tests/unit/MigrationTest.js @@ -463,10 +463,10 @@ describe('Migrations', () => { Onyx.multiSet({ [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]: { 1: { - reportActionID: 1, + reportActionID: '1', }, 2: { - reportActionID: 2, + reportActionID: '2', }, }, }) @@ -490,12 +490,12 @@ describe('Migrations', () => { Onyx.multiSet({ [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]: { 1: { - reportActionID: 1, - previousReportActionID: 0, + reportActionID: '1', + previousReportActionID: '0', }, 2: { - reportActionID: 2, - previousReportActionID: 1, + reportActionID: '2', + previousReportActionID: '1', }, }, }) @@ -509,12 +509,12 @@ describe('Migrations', () => { Onyx.disconnect(connectionID); const expectedReportAction = { 1: { - reportActionID: 1, - previousReportActionID: 0, + reportActionID: '1', + previousReportActionID: '0', }, 2: { - reportActionID: 2, - previousReportActionID: 1, + reportActionID: '2', + previousReportActionID: '1', }, }; expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).toMatchObject(expectedReportAction); @@ -529,10 +529,10 @@ describe('Migrations', () => { [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}3`]: null, [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}4`]: { 1: { - reportActionID: 1, + reportActionID: '1', }, 2: { - reportActionID: 2, + reportActionID: '2', }, }, }) @@ -562,12 +562,12 @@ describe('Migrations', () => { [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}3`]: null, [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}4`]: { 1: { - reportActionID: 1, - previousReportActionID: 10, + reportActionID: '1', + previousReportActionID: '10', }, 2: { - reportActionID: 2, - previousReportActionID: 23, + reportActionID: '2', + previousReportActionID: '23', }, }, }) @@ -582,12 +582,12 @@ describe('Migrations', () => { const expectedReportAction1 = {}; const expectedReportAction4 = { 1: { - reportActionID: 1, - previousReportActionID: 10, + reportActionID: '1', + previousReportActionID: '10', }, 2: { - reportActionID: 2, - previousReportActionID: 23, + reportActionID: '2', + previousReportActionID: '23', }, }; expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).toMatchObject(expectedReportAction1); From eebfbf1a08cf5d2f3bf18964d5986a3404c8303a Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Fri, 20 Oct 2023 13:41:21 +0200 Subject: [PATCH 039/245] 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 040/245] 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 fce1f22c04437b8b8c609948124a46c4e9ecdbbd Mon Sep 17 00:00:00 2001 From: Kacper Falat Date: Fri, 20 Oct 2023 14:10:28 +0200 Subject: [PATCH 041/245] Applied change in condition check. --- src/libs/migrations/CheckForPreviousReportActionID.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/migrations/CheckForPreviousReportActionID.ts b/src/libs/migrations/CheckForPreviousReportActionID.ts index 5f0e8e8d1c96..40fd95216558 100644 --- a/src/libs/migrations/CheckForPreviousReportActionID.ts +++ b/src/libs/migrations/CheckForPreviousReportActionID.ts @@ -31,7 +31,7 @@ export default function (): Promise { Object.values(allReportActions ?? {}).some((reportActions) => Object.values(reportActions ?? {}).some((reportActionData) => { - if (reportActionData.reportActionID?.length) { + if ('reportActionID' in reportActionData) { firstValidValue = reportActionData; return true; } 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 042/245] 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 043/245] 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 044/245] 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 045/245] 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 046/245] 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 047/245] 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 048/245] 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 049/245] 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 050/245] 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 051/245] 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 0f2e79102189682e6eb7ae44dc8060f0e5189799 Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 24 Oct 2023 16:14:43 +0700 Subject: [PATCH 052/245] fix: #admin notification message is inconsistent with error message --- src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.js b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.js index 026441c665a2..6cf5c1434851 100644 --- a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.js +++ b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.js @@ -21,6 +21,7 @@ import Navigation from '../../../libs/Navigation/Navigation'; import ROUTES from '../../../ROUTES'; import getPermittedDecimalSeparator from '../../../libs/getPermittedDecimalSeparator'; import * as BankAccounts from '../../../libs/actions/BankAccounts'; +import * as CurrencyUtils from '../../../libs/CurrencyUtils'; import * as ReimbursementAccountProps from '../../ReimbursementAccount/reimbursementAccountPropTypes'; import * as NumberUtils from '../../../libs/NumberUtils'; @@ -135,7 +136,8 @@ class WorkspaceRateAndUnitPage extends React.Component { validate(values) { const errors = {}; const decimalSeparator = this.props.toLocaleDigit('.'); - const rateValueRegex = RegExp(String.raw`^-?\d{0,8}([${getPermittedDecimalSeparator(decimalSeparator)}]\d{1,3})?$`, 'i'); + const outputCurrency = lodashGet(this.props, 'policy.outputCurrency', CONST.CURRENCY.USD); + const rateValueRegex = RegExp(String.raw`^-?\d{0,8}([${getPermittedDecimalSeparator(decimalSeparator)}]\d{1,${CurrencyUtils.getCurrencyDecimals(outputCurrency) + 1}})?$`, 'i'); if (!rateValueRegex.test(values.rate) || values.rate === '') { errors.rate = 'workspace.reimburse.invalidRateError'; } else if (NumberUtils.parseFloatAnyLocale(values.rate) <= 0) { From 408ef25d1139445bc451b3f0f809173f95ec2379 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Tue, 24 Oct 2023 13:53:15 +0200 Subject: [PATCH 053/245] 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 054/245] 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 055/245] 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 056/245] 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 057/245] 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 058/245] 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 059/245] 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 060/245] 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 061/245] 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 062/245] 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 063/245] 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 064/245] 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 065/245] 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 066/245] 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 067/245] 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 068/245] 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 069/245] 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 04a0fc07c6c0e7059154aa179a4e7a08be7aa786 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Mon, 30 Oct 2023 11:31:51 +0100 Subject: [PATCH 070/245] Add FormProvider in AdditionalDetailsStepPage --- .../EnablePayments/AdditionalDetailsStep.js | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/pages/EnablePayments/AdditionalDetailsStep.js b/src/pages/EnablePayments/AdditionalDetailsStep.js index f1813062d0d7..a17baf38dd56 100644 --- a/src/pages/EnablePayments/AdditionalDetailsStep.js +++ b/src/pages/EnablePayments/AdditionalDetailsStep.js @@ -5,8 +5,6 @@ import React from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; -import DatePicker from '@components/DatePicker'; -import Form from '@components/Form'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; @@ -23,6 +21,9 @@ import * as Wallet from '@userActions/Wallet'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import IdologyQuestions from './IdologyQuestions'; +import FormProvider from '@components/Form/FormProvider'; +import InputWrapper from '@components/Form/InputWrapper'; +import NewDatePicker from '@components/NewDatePicker'; const propTypes = { ...withLocalizePropTypes, @@ -177,7 +178,7 @@ function AdditionalDetailsStep({walletAdditionalDetails, translate, currentUserP {translate('additionalDetailsStep.helpLink')} -
- - - - - - +
); From b2d51c8865ed21f73be1cb30febff291c101e6e4 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Mon, 30 Oct 2023 11:37:43 +0100 Subject: [PATCH 071/245] Add FormProvider in AddressForm --- src/pages/ReimbursementAccount/AddressForm.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/pages/ReimbursementAccount/AddressForm.js b/src/pages/ReimbursementAccount/AddressForm.js index 36fbc6e4c59d..cbcbf716c2fe 100644 --- a/src/pages/ReimbursementAccount/AddressForm.js +++ b/src/pages/ReimbursementAccount/AddressForm.js @@ -6,6 +6,7 @@ import StatePicker from '@components/StatePicker'; import TextInput from '@components/TextInput'; import styles from '@styles/styles'; import CONST from '@src/CONST'; +import InputWrapper from '@components/Form/InputWrapper'; const propTypes = { /** Translate key for Street name */ @@ -109,7 +110,8 @@ function AddressForm(props) { maxInputLength={CONST.FORM_CHARACTER_LIMIT} />
- - - Date: Mon, 30 Oct 2023 11:38:30 +0100 Subject: [PATCH 072/245] fix prettier and lint errors --- src/pages/EnablePayments/AdditionalDetailsStep.js | 6 +++--- src/pages/ReimbursementAccount/AddressForm.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/EnablePayments/AdditionalDetailsStep.js b/src/pages/EnablePayments/AdditionalDetailsStep.js index a17baf38dd56..77b8cfc14bf5 100644 --- a/src/pages/EnablePayments/AdditionalDetailsStep.js +++ b/src/pages/EnablePayments/AdditionalDetailsStep.js @@ -5,7 +5,10 @@ import React from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; +import FormProvider from '@components/Form/FormProvider'; +import InputWrapper from '@components/Form/InputWrapper'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import NewDatePicker from '@components/NewDatePicker'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; @@ -21,9 +24,6 @@ import * as Wallet from '@userActions/Wallet'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import IdologyQuestions from './IdologyQuestions'; -import FormProvider from '@components/Form/FormProvider'; -import InputWrapper from '@components/Form/InputWrapper'; -import NewDatePicker from '@components/NewDatePicker'; const propTypes = { ...withLocalizePropTypes, diff --git a/src/pages/ReimbursementAccount/AddressForm.js b/src/pages/ReimbursementAccount/AddressForm.js index cbcbf716c2fe..732a1d228253 100644 --- a/src/pages/ReimbursementAccount/AddressForm.js +++ b/src/pages/ReimbursementAccount/AddressForm.js @@ -2,11 +2,11 @@ import PropTypes from 'prop-types'; import React from 'react'; import {View} from 'react-native'; import AddressSearch from '@components/AddressSearch'; +import InputWrapper from '@components/Form/InputWrapper'; import StatePicker from '@components/StatePicker'; import TextInput from '@components/TextInput'; import styles from '@styles/styles'; import CONST from '@src/CONST'; -import InputWrapper from '@components/Form/InputWrapper'; const propTypes = { /** Translate key for Street name */ From 57978b63e4573a9b91c4c3c3a3c79b63b013c4da Mon Sep 17 00:00:00 2001 From: Eduardo Date: Mon, 30 Oct 2023 16:01:02 +0100 Subject: [PATCH 073/245] updated dependency array --- src/pages/home/report/ReportActionsList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 6fea4e60911c..f2b4a4b5b6cf 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -365,7 +365,7 @@ function ReportActionsList({ if (!markerFound) { setCurrentUnreadMarker(null); } - }, [sortedReportActions, report.lastReadTime, , report.reportID, messageManuallyMarkedUnread, shouldDisplayNewMarker, currentUnreadMarker]); + }, [sortedReportActions, report.lastReadTime, report.reportID, messageManuallyMarkedUnread, shouldDisplayNewMarker, currentUnreadMarker]); const renderItem = useCallback( ({item: reportAction, index}) => ( From da8b083bd138ff6468447e173b2b559cfadddcc4 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 30 Oct 2023 16:19:29 +0100 Subject: [PATCH 074/245] 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 6546ba50f47c8e89b9c12a655bc5c661251f99bb Mon Sep 17 00:00:00 2001 From: Eduardo Date: Mon, 30 Oct 2023 16:22:24 +0100 Subject: [PATCH 075/245] Prettier fixes --- src/libs/actions/Report.js | 4 ++-- src/pages/home/report/ReportActionsList.js | 4 ++-- tests/ui/UnreadIndicatorsTest.js | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 776372392ac6..97b56a0d1327 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1,9 +1,9 @@ -import {InteractionManager, DeviceEventEmitter} from 'react-native'; -import lodashDebounce from 'lodash/debounce'; import {format as timezoneFormat, utcToZonedTime} from 'date-fns-tz'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import Str from 'expensify-common/lib/str'; +import lodashDebounce from 'lodash/debounce'; import lodashGet from 'lodash/get'; +import {DeviceEventEmitter, InteractionManager} from 'react-native'; import Onyx from 'react-native-onyx'; import _ from 'underscore'; import * as ActiveClientManager from '@libs/ActiveClientManager'; diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index f2b4a4b5b6cf..58c6fe01e007 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -1,10 +1,10 @@ import {useRoute} from '@react-navigation/native'; +import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import {DeviceEventEmitter} from 'react-native'; import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated'; import _ from 'underscore'; -import {DeviceEventEmitter} from 'react-native'; -import lodashGet from 'lodash/get'; import InvertedFlatList from '@components/InvertedFlatList'; import {withPersonalDetails} from '@components/OnyxProvider'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails'; diff --git a/tests/ui/UnreadIndicatorsTest.js b/tests/ui/UnreadIndicatorsTest.js index a7b1c2df7b21..788aed6233af 100644 --- a/tests/ui/UnreadIndicatorsTest.js +++ b/tests/ui/UnreadIndicatorsTest.js @@ -1,10 +1,10 @@ -import React from 'react'; -import Onyx from 'react-native-onyx'; -import {Linking, AppState, DeviceEventEmitter} from 'react-native'; import {fireEvent, render, screen, waitFor} from '@testing-library/react-native'; import {addSeconds, format, subMinutes, subSeconds} from 'date-fns'; import {utcToZonedTime} from 'date-fns-tz'; import lodashGet from 'lodash/get'; +import React from 'react'; +import {AppState, DeviceEventEmitter, Linking} from 'react-native'; +import Onyx from 'react-native-onyx'; import App from '../../src/App'; import CONFIG from '../../src/CONFIG'; import CONST from '../../src/CONST'; From a6287edbcb47c65f6aff9cfe680f659475266dd5 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 30 Oct 2023 16:49:36 +0100 Subject: [PATCH 076/245] 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 077/245] 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 2e68ecb919878b7c451cbf52559ff246aa143c12 Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Fri, 3 Nov 2023 23:31:44 +0700 Subject: [PATCH 078/245] fix sending html to BE --- src/pages/tasks/NewTaskDescriptionPage.js | 6 +++--- src/pages/tasks/NewTaskDetailsPage.js | 12 ++++++------ src/pages/tasks/NewTaskPage.js | 5 +++-- src/pages/tasks/TaskDescriptionPage.js | 4 ++-- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/pages/tasks/NewTaskDescriptionPage.js b/src/pages/tasks/NewTaskDescriptionPage.js index 7047a7b9d823..e8998a30ad8c 100644 --- a/src/pages/tasks/NewTaskDescriptionPage.js +++ b/src/pages/tasks/NewTaskDescriptionPage.js @@ -1,8 +1,8 @@ +import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import PropTypes from 'prop-types'; import React from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import FormProvider from '@components/Form/FormProvider'; import InputWrapperWithRef from '@components/Form/InputWrapper'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -47,7 +47,7 @@ function NewTaskDescriptionPage(props) { const {inputCallbackRef} = useAutoFocusInput(); const onSubmit = (values) => { - Task.setDescriptionValue(values.taskDescription); + Task.setDescriptionValue(parser.htmlToMarkdown(parser.replace(values.taskDescription))); Navigation.goBack(ROUTES.NEW_TASK); }; @@ -77,7 +77,7 @@ function NewTaskDescriptionPage(props) { { setTaskTitle(props.task.title); - setTaskDescription(parser.htmlToMarkdown(parser.replace(props.task.description || ''))); - }, [props.task]); + setTaskDescription(props.task.description || ''); + }, [props.task, isFocused]); /** * @param {Object} values - form input values passed by the Form component @@ -70,7 +71,7 @@ function NewTaskDetailsPage(props) { // On submit, we want to call the assignTask function and wait to validate // the response function onSubmit(values) { - Task.setDetailsValue(values.taskTitle, values.taskDescription); + Task.setDetailsValue(values.taskTitle, parser.htmlToMarkdown(parser.replace(values.taskDescription))); Navigation.navigate(ROUTES.NEW_TASK); } @@ -121,7 +122,6 @@ function NewTaskDetailsPage(props) { submitOnEnter={!Browser.isMobile()} containerStyles={[styles.autoGrowHeightMultilineInput]} textAlignVertical="top" - defaultValue={parser.htmlToMarkdown(parser.replace(taskDescription))} value={taskDescription} onValueChange={(value) => setTaskDescription(value)} /> diff --git a/src/pages/tasks/NewTaskPage.js b/src/pages/tasks/NewTaskPage.js index 41cda42ccbd8..3979fba86eb8 100644 --- a/src/pages/tasks/NewTaskPage.js +++ b/src/pages/tasks/NewTaskPage.js @@ -1,3 +1,4 @@ +import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useEffect, useMemo, useState} from 'react'; @@ -127,11 +128,11 @@ function NewTaskPage(props) { setErrorMessage(props.translate('newTaskPage.pleaseEnterTaskDestination')); return; } - + const parser = new ExpensiMark(); Task.createTaskAndNavigate( parentReport.reportID, props.task.title, - props.task.description, + parser.replace(props.task.description), props.task.assignee, props.task.assigneeAccountID, props.task.assigneeChatReport, diff --git a/src/pages/tasks/TaskDescriptionPage.js b/src/pages/tasks/TaskDescriptionPage.js index 9a1b9e4ef6f1..0e4e791a8999 100644 --- a/src/pages/tasks/TaskDescriptionPage.js +++ b/src/pages/tasks/TaskDescriptionPage.js @@ -1,8 +1,8 @@ import {useFocusEffect} from '@react-navigation/native'; +import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import React, {useCallback, useRef} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; @@ -43,7 +43,7 @@ function TaskDescriptionPage(props) { (values) => { // Set the description of the report in the store and then call Task.editTaskReport // to update the description of the report on the server - Task.editTaskAndNavigate(props.report, {description: values.description}); + Task.editTaskAndNavigate(props.report, {description: parser.replace(values.description)}); }, [props], ); From 2ab9dbf5191af194785cdaee055f91224e6e7f73 Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Fri, 3 Nov 2023 23:51:14 +0700 Subject: [PATCH 079/245] fix display task view issue --- src/components/ReportActionItem/TaskView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/TaskView.js b/src/components/ReportActionItem/TaskView.js index 9aa85392dde7..478e1c6b3607 100644 --- a/src/components/ReportActionItem/TaskView.js +++ b/src/components/ReportActionItem/TaskView.js @@ -130,9 +130,9 @@ function TaskView(props) { Navigation.navigate(ROUTES.TASK_DESCRIPTION.getRoute(props.report.reportID))} shouldShowRightIcon={isOpen} disabled={disableState} From 6c6a057cf574b9456fd384f126727cbf5683c270 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 6 Nov 2023 10:36:24 +0700 Subject: [PATCH 080/245] 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 9fbb88397ea253d3df1ff2e257eae59c91da7c1a Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 6 Nov 2023 11:34:42 +0700 Subject: [PATCH 081/245] Fix copy invite room message --- src/libs/ReportActionsUtils.ts | 5 ++++ src/libs/ReportUtils.js | 27 +++++++++++++++++++ .../report/ContextMenu/ContextMenuActions.js | 3 +++ src/styles/fontFamily/multiFontFamily.ts | 4 +-- 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 11e11f549682..1320cba4a9f9 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -95,6 +95,10 @@ function isReimbursementQueuedAction(reportAction: OnyxEntry) { return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTQUEUED; } +function isRoomChannelLogMember(reportAction: OnyxEntry) { + return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.INVITE_TO_ROOM || reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.REMOVE_FROM_ROOM; +} + /** * Returns whether the comment is a thread parent message/the first message in a thread */ @@ -657,4 +661,5 @@ export { shouldReportActionBeVisible, shouldReportActionBeVisibleAsLastAction, getFirstVisibleReportActionID, + isRoomChannelLogMember, }; diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 1e3fc5297193..0360eaead493 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -14,6 +14,7 @@ import ROUTES from '@src/ROUTES'; import * as CurrencyUtils from './CurrencyUtils'; import DateUtils from './DateUtils'; import isReportMessageAttachment from './isReportMessageAttachment'; +import * as LocalePhoneNumber from './LocalePhoneNumber'; import * as Localize from './Localize'; import linkingConfig from './Navigation/linkingConfig'; import Navigation from './Navigation/Navigation'; @@ -4043,6 +4044,21 @@ function getTaskAssigneeChatOnyxData(accountID, assigneeAccountID, taskReportID, }; } +/** + * Return the mention message of a list accounntID + * @param {Array} accountIDs + * @returns {String} + */ +function getMentionMessage(accountIDs) { + const listMention = _.map(accountIDs, (accountID) => { + const personalDetail = lodashGet(allPersonalDetails, accountID); + const displayNameOrLogin = + LocalePhoneNumber.formatPhoneNumber(lodashGet(personalDetail, 'login', '')) || lodashGet(personalDetail, 'displayName', '') || Localize.translateLocal('common.hidden'); + return `@${displayNameOrLogin}`; + }); + return listMention.join(' and '); +} + /** * Returns an array of the participants Ids of a report * @@ -4114,6 +4130,16 @@ function getIOUReportActionDisplayMessage(reportAction) { return displayMessage; } +/** + * Return room channel log display message + * @param {Object} reportAction + * @returns {String} + */ +function getRoomChannelLogMemberMessage(reportAction) { + const title = reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.INVITE_TO_ROOM ? 'invited' : 'removed'; + return `${title} ${getMentionMessage(reportAction.originalMessage.targetAccountIDs)}`; +} + /** * Checks if a report is a group chat. * @@ -4315,4 +4341,5 @@ export { parseReportRouteParams, getReimbursementQueuedActionMessage, getPersonalDetailsForAccountID, + getRoomChannelLogMemberMessage, }; diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index 5a1266d15a42..0246c514645f 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -281,6 +281,9 @@ export default [ } else if (ReportActionsUtils.isMoneyRequestAction(reportAction)) { const displayMessage = ReportUtils.getIOUReportActionDisplayMessage(reportAction); Clipboard.setString(displayMessage); + } else if (ReportActionsUtils.isRoomChannelLogMember(reportAction)) { + const logMessage = ReportUtils.getRoomChannelLogMemberMessage(reportAction); + Clipboard.setString(logMessage); } else if (content) { const parser = new ExpensiMark(); if (!Clipboard.canSetHtml()) { 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 082/245] 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 083/245] 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 084/245] 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 172f1c7a00425e018d11f097f4182d9fa6b14bbe Mon Sep 17 00:00:00 2001 From: Kacper Falat Date: Mon, 6 Nov 2023 14:16:31 +0100 Subject: [PATCH 085/245] Conflicts resolved. --- src/libs/migrations/CheckForPreviousReportActionID.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/migrations/CheckForPreviousReportActionID.ts b/src/libs/migrations/CheckForPreviousReportActionID.ts index 474c0f560558..1f31a3586b0a 100644 --- a/src/libs/migrations/CheckForPreviousReportActionID.ts +++ b/src/libs/migrations/CheckForPreviousReportActionID.ts @@ -1,7 +1,7 @@ import Onyx, {OnyxCollection} from 'react-native-onyx'; import Log from '@libs/Log'; import ONYXKEYS from '@src/ONYXKEYS'; -import * as OnyxTypes from "@src/types/onyx"; +import * as OnyxTypes from '@src/types/onyx'; function getReportActionsFromOnyx(): Promise> { return new Promise((resolve) => { From 14755ebf2720da96c2ff61c6c021c25bab353bf7 Mon Sep 17 00:00:00 2001 From: Carlos Barros <765936+barros001@users.noreply.github.com> Date: Mon, 6 Nov 2023 16:31:35 -0500 Subject: [PATCH 086/245] ensure push events are processed in the same order as they are received --- src/libs/actions/OnyxUpdates.ts | 14 ++++++++++++-- src/libs/actions/User.js | 34 +++++++++++++++++++-------------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/libs/actions/OnyxUpdates.ts b/src/libs/actions/OnyxUpdates.ts index b44b485ac60f..16212bfae106 100644 --- a/src/libs/actions/OnyxUpdates.ts +++ b/src/libs/actions/OnyxUpdates.ts @@ -15,6 +15,8 @@ Onyx.connect({ callback: (val) => (lastUpdateIDAppliedToClient = val), }); +let pusherEventsQueuePromise = Promise.resolve(); + function applyHTTPSOnyxUpdates(request: Request, response: Response) { console.debug('[OnyxUpdateManager] Applying https update'); // For most requests we can immediately update Onyx. For write requests we queue the updates and apply them after the sequential queue has flushed to prevent a replay effect in @@ -45,10 +47,18 @@ function applyHTTPSOnyxUpdates(request: Request, response: Response) { function applyPusherOnyxUpdates(updates: OnyxUpdateEvent[]) { console.debug('[OnyxUpdateManager] Applying pusher update'); - const pusherEventPromises = updates.map((update) => PusherUtils.triggerMultiEventHandler(update.eventType, update.data)); - return Promise.all(pusherEventPromises).then(() => { + + for (const update of updates) { + pusherEventsQueuePromise = pusherEventsQueuePromise.then(() => { + return PusherUtils.triggerMultiEventHandler(update.eventType, update.data); + }); + } + + pusherEventsQueuePromise = pusherEventsQueuePromise.then(() => { console.debug('[OnyxUpdateManager] Done applying Pusher update'); }); + + return pusherEventsQueuePromise; } /** diff --git a/src/libs/actions/User.js b/src/libs/actions/User.js index f7375a5583a6..9849c54dffd7 100644 --- a/src/libs/actions/User.js +++ b/src/libs/actions/User.js @@ -522,20 +522,26 @@ function subscribeToUserEvents() { }); // Handles Onyx updates coming from Pusher through the mega multipleEvents. - PusherUtils.subscribeToMultiEvent(Pusher.TYPE.MULTIPLE_EVENT_TYPE.ONYX_API_UPDATE, (pushJSON) => - SequentialQueue.getCurrentRequest().then(() => { - // If we don't have the currentUserAccountID (user is logged out) we don't want to update Onyx with data from Pusher - if (!currentUserAccountID) { - return; - } - - const onyxUpdatePromise = Onyx.update(pushJSON); - triggerNotifications(pushJSON); - - // Return a promise when Onyx is done updating so that the OnyxUpdatesManager can properly apply all - // the onyx updates in order - return onyxUpdatePromise; - }), + PusherUtils.subscribeToMultiEvent( + Pusher.TYPE.MULTIPLE_EVENT_TYPE.ONYX_API_UPDATE, + (pushJSON) => + new Promise((resolve) => + SequentialQueue.getCurrentRequest().then(() => { + // If we don't have the currentUserAccountID (user is logged out) we don't want to update Onyx with data from Pusher + if (!currentUserAccountID) { + return resolve(); + } + + const onyxUpdatePromise = Onyx.update(pushJSON); + triggerNotifications(pushJSON); + + // Return a promise when Onyx is done updating so that the OnyxUpdatesManager can properly apply all + // the onyx updates in order + return onyxUpdatePromise.then(() => { + resolve(onyxUpdatePromise); + }); + }), + ), ); } From 49557b37cbd7ff0893c08864af627845dae24f51 Mon Sep 17 00:00:00 2001 From: Carlos Barros <765936+barros001@users.noreply.github.com> Date: Mon, 6 Nov 2023 21:11:48 -0500 Subject: [PATCH 087/245] removed redundant promise --- src/libs/actions/User.js | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/src/libs/actions/User.js b/src/libs/actions/User.js index 9849c54dffd7..536c68f2cf01 100644 --- a/src/libs/actions/User.js +++ b/src/libs/actions/User.js @@ -522,26 +522,20 @@ function subscribeToUserEvents() { }); // Handles Onyx updates coming from Pusher through the mega multipleEvents. - PusherUtils.subscribeToMultiEvent( - Pusher.TYPE.MULTIPLE_EVENT_TYPE.ONYX_API_UPDATE, - (pushJSON) => - new Promise((resolve) => - SequentialQueue.getCurrentRequest().then(() => { - // If we don't have the currentUserAccountID (user is logged out) we don't want to update Onyx with data from Pusher - if (!currentUserAccountID) { - return resolve(); - } - - const onyxUpdatePromise = Onyx.update(pushJSON); - triggerNotifications(pushJSON); - - // Return a promise when Onyx is done updating so that the OnyxUpdatesManager can properly apply all - // the onyx updates in order - return onyxUpdatePromise.then(() => { - resolve(onyxUpdatePromise); - }); - }), - ), + PusherUtils.subscribeToMultiEvent(Pusher.TYPE.MULTIPLE_EVENT_TYPE.ONYX_API_UPDATE, (pushJSON) => + SequentialQueue.getCurrentRequest().then(() => { + // If we don't have the currentUserAccountID (user is logged out) we don't want to update Onyx with data from Pusher + if (!currentUserAccountID) { + return + } + + const onyxUpdatePromise = Onyx.update(pushJSON); + triggerNotifications(pushJSON); + + // Return a promise when Onyx is done updating so that the OnyxUpdatesManager can properly apply all + // the onyx updates in order + return onyxUpdatePromise; + }), ); } From f756a64754adc7dd60513ec789a21820ae30528a Mon Sep 17 00:00:00 2001 From: Carlos Barros <765936+barros001@users.noreply.github.com> Date: Mon, 6 Nov 2023 21:13:38 -0500 Subject: [PATCH 088/245] removed unnecessary return --- src/libs/actions/OnyxUpdates.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libs/actions/OnyxUpdates.ts b/src/libs/actions/OnyxUpdates.ts index 16212bfae106..8d07755be357 100644 --- a/src/libs/actions/OnyxUpdates.ts +++ b/src/libs/actions/OnyxUpdates.ts @@ -49,9 +49,7 @@ function applyPusherOnyxUpdates(updates: OnyxUpdateEvent[]) { console.debug('[OnyxUpdateManager] Applying pusher update'); for (const update of updates) { - pusherEventsQueuePromise = pusherEventsQueuePromise.then(() => { - return PusherUtils.triggerMultiEventHandler(update.eventType, update.data); - }); + pusherEventsQueuePromise = pusherEventsQueuePromise.then(() => PusherUtils.triggerMultiEventHandler(update.eventType, update.data)); } pusherEventsQueuePromise = pusherEventsQueuePromise.then(() => { From 0244379fb3ff868a5079ed0be96d2c9474f756de Mon Sep 17 00:00:00 2001 From: Carlos Barros <765936+barros001@users.noreply.github.com> Date: Mon, 6 Nov 2023 21:14:28 -0500 Subject: [PATCH 089/245] added missing ; --- src/libs/actions/User.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/User.js b/src/libs/actions/User.js index 536c68f2cf01..f7375a5583a6 100644 --- a/src/libs/actions/User.js +++ b/src/libs/actions/User.js @@ -526,7 +526,7 @@ function subscribeToUserEvents() { SequentialQueue.getCurrentRequest().then(() => { // If we don't have the currentUserAccountID (user is logged out) we don't want to update Onyx with data from Pusher if (!currentUserAccountID) { - return + return; } const onyxUpdatePromise = Onyx.update(pushJSON); From 427be617ddd28816dbae71209cfd80e373ae7ed2 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 7 Nov 2023 16:46:56 +0700 Subject: [PATCH 090/245] refactor function --- src/libs/ReportUtils.js | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 49a3f77648d0..0fbb1486156c 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -4045,21 +4045,6 @@ function getTaskAssigneeChatOnyxData(accountID, assigneeAccountID, taskReportID, }; } -/** - * Return the mention message of a list accounntID - * @param {Array} accountIDs - * @returns {String} - */ -function getMentionMessage(accountIDs) { - const listMention = _.map(accountIDs, (accountID) => { - const personalDetail = lodashGet(allPersonalDetails, accountID); - const displayNameOrLogin = - LocalePhoneNumber.formatPhoneNumber(lodashGet(personalDetail, 'login', '')) || lodashGet(personalDetail, 'displayName', '') || Localize.translateLocal('common.hidden'); - return `@${displayNameOrLogin}`; - }); - return listMention.join(' and '); -} - /** * Returns an array of the participants Ids of a report * @@ -4137,8 +4122,22 @@ function getIOUReportActionDisplayMessage(reportAction) { * @returns {String} */ function getRoomChannelLogMemberMessage(reportAction) { - const title = reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.INVITE_TO_ROOM ? 'invited' : 'removed'; - return `${title} ${getMentionMessage(reportAction.originalMessage.targetAccountIDs)}`; + const actionPerformed = reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.INVITE_TO_ROOM ? 'invited' : 'removed'; + const listMention = _.map(reportAction.originalMessage.targetAccountIDs, (accountID) => { + const personalDetail = lodashGet(allPersonalDetails, accountID); + const displayNameOrLogin = + LocalePhoneNumber.formatPhoneNumber(lodashGet(personalDetail, 'login', '')) || lodashGet(personalDetail, 'displayName', '') || Localize.translateLocal('common.hidden'); + return `@${displayNameOrLogin}`; + }); + const lastMention = listMention.pop(); + let lastPrefix = ', and '; + if (listMention.length === 0) { + lastPrefix = ''; + } + if (listMention.length === 1) { + lastPrefix = ' and '; + } + return `${actionPerformed} ${listMention.join(', ')}${lastPrefix}${lastMention}`; } /** From 963b7c4d11b161d06efc4c18a456c05b454fe9d3 Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Tue, 7 Nov 2023 11:22:41 +0100 Subject: [PATCH 091/245] 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 4df098eed0f4044dc7a21429abf8b603fa15404b Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 7 Nov 2023 19:01:24 +0700 Subject: [PATCH 092/245] simplicity function --- src/libs/ReportUtils.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 0fbb1486156c..c2c573960fd3 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -4118,26 +4118,31 @@ function getIOUReportActionDisplayMessage(reportAction) { /** * Return room channel log display message + * * @param {Object} reportAction * @returns {String} */ function getRoomChannelLogMemberMessage(reportAction) { const actionPerformed = reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.INVITE_TO_ROOM ? 'invited' : 'removed'; - const listMention = _.map(reportAction.originalMessage.targetAccountIDs, (accountID) => { + + const mentions = _.map(reportAction.originalMessage.targetAccountIDs, (accountID) => { const personalDetail = lodashGet(allPersonalDetails, accountID); const displayNameOrLogin = LocalePhoneNumber.formatPhoneNumber(lodashGet(personalDetail, 'login', '')) || lodashGet(personalDetail, 'displayName', '') || Localize.translateLocal('common.hidden'); return `@${displayNameOrLogin}`; }); - const lastMention = listMention.pop(); - let lastPrefix = ', and '; - if (listMention.length === 0) { - lastPrefix = ''; + + const lastMention = mentions.pop(); + + if (mentions.length === 0) { + return `${actionPerformed} ${lastMention}`; } - if (listMention.length === 1) { - lastPrefix = ' and '; + + if (mentions.length === 1) { + return `${actionPerformed} ${mentions[0]} and ${lastMention}`; } - return `${actionPerformed} ${listMention.join(', ')}${lastPrefix}${lastMention}`; + + return `${actionPerformed} ${mentions.join(', ')}, and ${lastMention}`; } /** From 2442dc6b8959908dc80e63d06918d80eb841a610 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Tue, 7 Nov 2023 16:34:49 +0100 Subject: [PATCH 093/245] add input wrapper in AddressForm --- src/pages/ReimbursementAccount/AddressForm.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/ReimbursementAccount/AddressForm.js b/src/pages/ReimbursementAccount/AddressForm.js index c0202c9b6dbd..d48295b4786d 100644 --- a/src/pages/ReimbursementAccount/AddressForm.js +++ b/src/pages/ReimbursementAccount/AddressForm.js @@ -96,7 +96,8 @@ function AddressForm(props) { return ( <> - Date: Tue, 7 Nov 2023 17:23:53 +0100 Subject: [PATCH 094/245] fix: typing --- src/components/FormElement.tsx | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/components/FormElement.tsx b/src/components/FormElement.tsx index d37e792ca6a6..02fab79f1d62 100644 --- a/src/components/FormElement.tsx +++ b/src/components/FormElement.tsx @@ -1,14 +1,13 @@ -import React, {ForwardedRef, forwardRef} from 'react'; -import {View, ViewStyle} from 'react-native'; -import * as ComponentUtils from '../libs/ComponentUtils'; +import React, {ForwardedRef, forwardRef, LegacyRef} from 'react'; +import {View, ViewProps} from 'react-native'; +import * as ComponentUtils from '@libs/ComponentUtils'; -function FormElement(props: {style: ViewStyle; children: React.ReactNode}, ref: ForwardedRef) { - console.debug('hej', JSON.stringify(ref)); +type FormElementProps = ViewProps; +function FormElement(props: FormElementProps, ref: ForwardedRef) { return ( } // eslint-disable-next-line react/jsx-props-no-spreading {...props} /> From 40f0fec7df7b48cc0304a8e797c8c0a036d3fc86 Mon Sep 17 00:00:00 2001 From: Daniel Edwards Date: Tue, 7 Nov 2023 12:16:16 -0500 Subject: [PATCH 095/245] 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 b2dc291390a475dff54d4d63654f786bb5b0cfb1 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 7 Nov 2023 18:51:03 +0100 Subject: [PATCH 096/245] fix: changed a type --- src/components/FormElement.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/FormElement.tsx b/src/components/FormElement.tsx index 02fab79f1d62..555f2ec59c19 100644 --- a/src/components/FormElement.tsx +++ b/src/components/FormElement.tsx @@ -1,4 +1,4 @@ -import React, {ForwardedRef, forwardRef, LegacyRef} from 'react'; +import React, {ForwardedRef, forwardRef} from 'react'; import {View, ViewProps} from 'react-native'; import * as ComponentUtils from '@libs/ComponentUtils'; @@ -7,7 +7,7 @@ function FormElement(props: FormElementProps, ref: ForwardedRef} + ref={ref as ForwardedRef} // eslint-disable-next-line react/jsx-props-no-spreading {...props} /> From 90708810d63b89f8ad1a0a1af7ef5e5cec96d459 Mon Sep 17 00:00:00 2001 From: rory Date: Tue, 7 Nov 2023 10:22:22 -0800 Subject: [PATCH 097/245] 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 7bbc9ccf4ad0069036633e5d773ccfff057d591e Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Wed, 8 Nov 2023 02:37:20 +0700 Subject: [PATCH 098/245] fix admin cannot replace receipt --- src/components/AttachmentModal.js | 7 +------ src/libs/ReportUtils.js | 9 ++++++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 8231dd7c4fe2..8fba8402a2f7 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -23,7 +23,6 @@ import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; import themeColors from '@styles/themes/default'; import * as IOU from '@userActions/IOU'; -import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -359,12 +358,8 @@ function AttachmentModal(props) { } const menuItems = []; const parentReportAction = props.parentReportActions[props.report.parentReportActionID]; - const isDeleted = ReportActionsUtils.isDeletedAction(parentReportAction); - const isSettled = ReportUtils.isSettled(props.parentReport.reportID); - const isAdmin = Policy.isAdminOfFreePolicy([props.policy]) && ReportUtils.isExpenseReport(props.parentReport); - const isRequestor = ReportUtils.isMoneyRequestReport(props.parentReport) && lodashGet(props.session, 'accountID', null) === parentReportAction.actorAccountID; - const canEdit = !isSettled && !isDeleted && (isAdmin || isRequestor); + const canEdit = ReportUtils.canEditFieldOfMoneyRequest(parentReportAction, props.parentReport.reportID, CONST.EDIT_REQUEST_FIELD.RECEIPT); if (canEdit) { menuItems.push({ icon: Expensicons.Camera, diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 8d24d98b19e8..f8579a4e011c 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1617,9 +1617,10 @@ function getTransactionDetails(transaction, createdDateFormat = CONST.DATE.FNS_F * - or the user is an admin on the policy the expense report is tied to * * @param {Object} reportAction + * @param {String} fieldToEdit * @returns {Boolean} */ -function canEditMoneyRequest(reportAction) { +function canEditMoneyRequest(reportAction, fieldToEdit) { const isDeleted = ReportActionsUtils.isDeletedAction(reportAction); if (isDeleted) { @@ -1641,7 +1642,9 @@ function canEditMoneyRequest(reportAction) { const isReportSettled = isSettled(moneyRequestReport.reportID); const isAdmin = isExpenseReport(moneyRequestReport) && lodashGet(getPolicy(moneyRequestReport.policyID), 'role', '') === CONST.POLICY.ROLE.ADMIN; const isRequestor = currentUserAccountID === reportAction.actorAccountID; - + if (isAdmin && !isRequestor && fieldToEdit === CONST.EDIT_REQUEST_FIELD.RECEIPT) { + return false; + } if (isAdmin) { return true; } @@ -1668,7 +1671,7 @@ function canEditFieldOfMoneyRequest(reportAction, reportID, fieldToEdit) { ]; // Checks if this user has permissions to edit this money request - if (!canEditMoneyRequest(reportAction)) { + if (!canEditMoneyRequest(reportAction, fieldToEdit)) { return false; // User doesn't have permission to edit } From 561c3548eab0de370a61a4403a99dcbde28ebd81 Mon Sep 17 00:00:00 2001 From: Daniel Edwards Date: Tue, 7 Nov 2023 15:31:42 -0500 Subject: [PATCH 099/245] 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 100/245] 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 101/245] 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 102/245] 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 65c5cc962efdc6d27af52b8ac654e82581336b18 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 8 Nov 2023 15:53:38 +0700 Subject: [PATCH 103/245] fix copy message for policy member log --- src/libs/ReportActionsUtils.ts | 11 +++++-- src/libs/ReportUtils.js | 29 ++++++++++++++----- .../report/ContextMenu/ContextMenuActions.js | 4 +-- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 3cc83b5f9db9..79d899c9d3a1 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -95,8 +95,13 @@ function isReimbursementQueuedAction(reportAction: OnyxEntry) { return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTQUEUED; } -function isRoomChannelLogMember(reportAction: OnyxEntry) { - return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.INVITE_TO_ROOM || reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.REMOVE_FROM_ROOM; +function isChannelLogMemberAction(reportAction: OnyxEntry) { + return ( + reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.INVITE_TO_ROOM || + reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.REMOVE_FROM_ROOM || + reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.INVITE_TO_ROOM || + reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.REMOVE_FROM_ROOM + ); } /** @@ -666,5 +671,5 @@ export { shouldReportActionBeVisible, shouldReportActionBeVisibleAsLastAction, getFirstVisibleReportActionID, - isRoomChannelLogMember, + isChannelLogMemberAction, }; diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index d899bf7dec8e..409f8387057a 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -4127,12 +4127,15 @@ function getIOUReportActionDisplayMessage(reportAction) { /** * Return room channel log display message - * + * * @param {Object} reportAction * @returns {String} */ -function getRoomChannelLogMemberMessage(reportAction) { - const actionPerformed = reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.INVITE_TO_ROOM ? 'invited' : 'removed'; +function getChannelLogMemberMessage(reportAction) { + const verb = + reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.INVITE_TO_ROOM || reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.INVITE_TO_ROOM + ? 'invited' + : 'removed'; const mentions = _.map(reportAction.originalMessage.targetAccountIDs, (accountID) => { const personalDetail = lodashGet(allPersonalDetails, accountID); @@ -4142,16 +4145,26 @@ function getRoomChannelLogMemberMessage(reportAction) { }); const lastMention = mentions.pop(); + let message = ''; if (mentions.length === 0) { - return `${actionPerformed} ${lastMention}`; + message = `${verb} ${lastMention}`; + } else if (mentions.length === 1) { + message = `${verb} ${mentions[0]} and ${lastMention}`; + } else { + message = `${verb} ${mentions.join(', ')}, and ${lastMention}`; } - if (mentions.length === 1) { - return `${actionPerformed} ${mentions[0]} and ${lastMention}`; + const roomName = lodashGet(reportAction, 'originalMessage.roomName', ''); + if (roomName) { + const preposition = + reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.INVITE_TO_ROOM || reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.INVITE_TO_ROOM + ? ' to' + : ' from'; + message += `${preposition} ${roomName}`; } - return `${actionPerformed} ${mentions.join(', ')}, and ${lastMention}`; + return message; } /** @@ -4367,6 +4380,6 @@ export { parseReportRouteParams, getReimbursementQueuedActionMessage, getPersonalDetailsForAccountID, - getRoomChannelLogMemberMessage, + getChannelLogMemberMessage, getRoom, }; diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index 0246c514645f..4f35926c5957 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -281,8 +281,8 @@ export default [ } else if (ReportActionsUtils.isMoneyRequestAction(reportAction)) { const displayMessage = ReportUtils.getIOUReportActionDisplayMessage(reportAction); Clipboard.setString(displayMessage); - } else if (ReportActionsUtils.isRoomChannelLogMember(reportAction)) { - const logMessage = ReportUtils.getRoomChannelLogMemberMessage(reportAction); + } else if (ReportActionsUtils.isChannelLogMemberAction(reportAction)) { + const logMessage = ReportUtils.getChannelLogMemberMessage(reportAction); Clipboard.setString(logMessage); } else if (content) { const parser = new ExpensiMark(); From faa6cde93a30d3cd87bead35ac451f2371ddfaef Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 8 Nov 2023 16:26:18 +0700 Subject: [PATCH 104/245] 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 7e7602f5882d2ab7d3375713669e21f676dd74f3 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 8 Nov 2023 10:55:45 +0100 Subject: [PATCH 105/245] Revert "ref: moved FormElement to TS" This reverts commit 78a09b7b4e99e4f4521da604b334977664aea695. --- src/components/FormElement.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/FormElement.tsx b/src/components/FormElement.tsx index 555f2ec59c19..42f5452b99db 100644 --- a/src/components/FormElement.tsx +++ b/src/components/FormElement.tsx @@ -3,6 +3,7 @@ import {View, ViewProps} from 'react-native'; import * as ComponentUtils from '@libs/ComponentUtils'; type FormElementProps = ViewProps; + function FormElement(props: FormElementProps, ref: ForwardedRef) { return ( Date: Wed, 8 Nov 2023 11:03:03 +0100 Subject: [PATCH 106/245] Revert "ref: moved FormElement to TS" This reverts commit 78a09b7b4e99e4f4521da604b334977664aea695. --- ios/Podfile.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 7e80f5c90c7d..97143f53b867 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -251,9 +251,9 @@ PODS: - nanopb/encode (= 2.30908.0) - nanopb/decode (2.30908.0) - nanopb/encode (2.30908.0) - - Onfido (27.4.0) - - onfido-react-native-sdk (7.4.0): - - Onfido (= 27.4.0) + - Onfido (28.3.0) + - onfido-react-native-sdk (8.3.0): + - Onfido (~> 28.3.0) - React - OpenSSL-Universal (1.1.1100) - Plaid (4.1.0) @@ -1209,8 +1209,8 @@ SPEC CHECKSUMS: MapboxMaps: af50ec61a7eb3b032c3f7962c6bd671d93d2a209 MapboxMobileEvents: de50b3a4de180dd129c326e09cd12c8adaaa46d6 nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 - Onfido: e36f284b865adcf99d9c905590a64ac09d4a576b - onfido-react-native-sdk: 4ecde1a97435dcff9f00a878e3f8d1eb14fabbdc + Onfido: c7d010d9793790d44a07799d9be25aa8e3814ee7 + onfido-react-native-sdk: b346a620af5669f9fecb6dc3052314a35a94ad9f OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c Plaid: 7d340abeadb46c7aa1a91f896c5b22395a31fcf2 PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef From 280909e3327a95d775765024e4dfbf7eadee312b Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Wed, 8 Nov 2023 11:38:44 +0100 Subject: [PATCH 107/245] [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 f3133cb7b6b801a5e88ddfe2c45797e816223b0a Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 8 Nov 2023 11:59:00 +0100 Subject: [PATCH 108/245] fix: removed unnecessary type --- src/components/FormElement.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/FormElement.tsx b/src/components/FormElement.tsx index 42f5452b99db..2af2f6af4615 100644 --- a/src/components/FormElement.tsx +++ b/src/components/FormElement.tsx @@ -2,9 +2,7 @@ import React, {ForwardedRef, forwardRef} from 'react'; import {View, ViewProps} from 'react-native'; import * as ComponentUtils from '@libs/ComponentUtils'; -type FormElementProps = ViewProps; - -function FormElement(props: FormElementProps, ref: ForwardedRef) { +function FormElement(props: ViewProps, ref: ForwardedRef) { return ( Date: Wed, 8 Nov 2023 12:26:04 +0100 Subject: [PATCH 109/245] 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 110/245] 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 111/245] 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 112/245] 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 113/245] 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 114/245] 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 115/245] 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 116/245] 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 30a304300871a7ad045a83c07f1ee3e024e504a0 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Thu, 9 Nov 2023 11:20:55 +0100 Subject: [PATCH 117/245] fix: changed dispalyName of component --- src/components/FormElement.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/FormElement.tsx b/src/components/FormElement.tsx index 2af2f6af4615..dcf17af4362f 100644 --- a/src/components/FormElement.tsx +++ b/src/components/FormElement.tsx @@ -13,6 +13,6 @@ function FormElement(props: ViewProps, ref: ForwardedRef Date: Thu, 9 Nov 2023 12:03:36 +0100 Subject: [PATCH 118/245] 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 119/245] 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 89267cbf93308384233fa3e64b31178031d09172 Mon Sep 17 00:00:00 2001 From: Carlos Barros <765936+barros001@users.noreply.github.com> Date: Thu, 9 Nov 2023 10:46:36 -0500 Subject: [PATCH 120/245] use reduce instead of for --- src/libs/actions/OnyxUpdates.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libs/actions/OnyxUpdates.ts b/src/libs/actions/OnyxUpdates.ts index 8d07755be357..8a855698a5b7 100644 --- a/src/libs/actions/OnyxUpdates.ts +++ b/src/libs/actions/OnyxUpdates.ts @@ -15,7 +15,7 @@ Onyx.connect({ callback: (val) => (lastUpdateIDAppliedToClient = val), }); -let pusherEventsQueuePromise = Promise.resolve(); +let pusherEventsPromise = Promise.resolve(); function applyHTTPSOnyxUpdates(request: Request, response: Response) { console.debug('[OnyxUpdateManager] Applying https update'); @@ -48,15 +48,15 @@ function applyHTTPSOnyxUpdates(request: Request, response: Response) { function applyPusherOnyxUpdates(updates: OnyxUpdateEvent[]) { console.debug('[OnyxUpdateManager] Applying pusher update'); - for (const update of updates) { - pusherEventsQueuePromise = pusherEventsQueuePromise.then(() => PusherUtils.triggerMultiEventHandler(update.eventType, update.data)); - } - - pusherEventsQueuePromise = pusherEventsQueuePromise.then(() => { - console.debug('[OnyxUpdateManager] Done applying Pusher update'); - }); + pusherEventsPromise = updates + .reduce((promise, update) => { + return promise.then(() => PusherUtils.triggerMultiEventHandler(update.eventType, update.data)); + }, pusherEventsPromise) + .then(() => { + console.debug('[OnyxUpdateManager2] Done applying Pusher update'); + }); - return pusherEventsQueuePromise; + return pusherEventsPromise; } /** From b79db02d7fd1e0815d3f81f49f561469a6b8e376 Mon Sep 17 00:00:00 2001 From: Carlos Barros <765936+barros001@users.noreply.github.com> Date: Thu, 9 Nov 2023 10:48:49 -0500 Subject: [PATCH 121/245] add comment --- src/libs/actions/OnyxUpdates.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/OnyxUpdates.ts b/src/libs/actions/OnyxUpdates.ts index 8a855698a5b7..213a47b64b17 100644 --- a/src/libs/actions/OnyxUpdates.ts +++ b/src/libs/actions/OnyxUpdates.ts @@ -15,6 +15,8 @@ Onyx.connect({ callback: (val) => (lastUpdateIDAppliedToClient = val), }); +// This promise is used to ensure pusher events are always processed in the order they are received, +// even when such events are received over multiple separate pusher updates. let pusherEventsPromise = Promise.resolve(); function applyHTTPSOnyxUpdates(request: Request, response: Response) { @@ -53,7 +55,7 @@ function applyPusherOnyxUpdates(updates: OnyxUpdateEvent[]) { return promise.then(() => PusherUtils.triggerMultiEventHandler(update.eventType, update.data)); }, pusherEventsPromise) .then(() => { - console.debug('[OnyxUpdateManager2] Done applying Pusher update'); + console.debug('[OnyxUpdateManager] Done applying Pusher update'); }); return pusherEventsPromise; 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 122/245] 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 39508d3072c517e6095feb5e1572d4cfd874073c Mon Sep 17 00:00:00 2001 From: Carlos Barros <765936+barros001@users.noreply.github.com> Date: Thu, 9 Nov 2023 11:27:35 -0500 Subject: [PATCH 123/245] fixed lint issues --- src/libs/actions/OnyxUpdates.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/OnyxUpdates.ts b/src/libs/actions/OnyxUpdates.ts index 213a47b64b17..ce673fa6aaaf 100644 --- a/src/libs/actions/OnyxUpdates.ts +++ b/src/libs/actions/OnyxUpdates.ts @@ -48,12 +48,12 @@ function applyHTTPSOnyxUpdates(request: Request, response: Response) { } function applyPusherOnyxUpdates(updates: OnyxUpdateEvent[]) { - console.debug('[OnyxUpdateManager] Applying pusher update'); + pusherEventsPromise = pusherEventsPromise.then(() => { + console.debug('[OnyxUpdateManager] Applying pusher update'); + }); pusherEventsPromise = updates - .reduce((promise, update) => { - return promise.then(() => PusherUtils.triggerMultiEventHandler(update.eventType, update.data)); - }, pusherEventsPromise) + .reduce((promise, update) => promise.then(() => PusherUtils.triggerMultiEventHandler(update.eventType, update.data)), pusherEventsPromise) .then(() => { console.debug('[OnyxUpdateManager] Done applying Pusher update'); }); 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 124/245] 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 8b21ba995ddc9bfaaffa3d2f03e1a3fb76ef5098 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Thu, 9 Nov 2023 18:24:24 +0100 Subject: [PATCH 125/245] fix other pages using AddressForm --- src/components/CheckboxWithLabel.js | 3 +- .../ReimbursementAccount/ACHContractStep.js | 19 +++++++---- src/pages/ReimbursementAccount/CompanyStep.js | 32 ++++++++++++------- .../ReimbursementAccount/IdentityForm.js | 14 +++++--- .../ReimbursementAccount/RequestorStep.js | 10 +++--- 5 files changed, 49 insertions(+), 29 deletions(-) diff --git a/src/components/CheckboxWithLabel.js b/src/components/CheckboxWithLabel.js index 86dba1d2a932..148ac5f9822b 100644 --- a/src/components/CheckboxWithLabel.js +++ b/src/components/CheckboxWithLabel.js @@ -8,6 +8,7 @@ import Checkbox from './Checkbox'; import FormHelpMessage from './FormHelpMessage'; import PressableWithFeedback from './Pressable/PressableWithFeedback'; import Text from './Text'; +import refPropTypes from './refPropTypes'; /** * Returns an error if the required props are not provided @@ -54,7 +55,7 @@ const propTypes = { defaultValue: PropTypes.bool, /** React ref being forwarded to the Checkbox input */ - forwardedRef: PropTypes.func, + forwardedRef: refPropTypes, /** The ID used to uniquely identify the input in a Form */ /* eslint-disable-next-line react/no-unused-prop-types */ diff --git a/src/pages/ReimbursementAccount/ACHContractStep.js b/src/pages/ReimbursementAccount/ACHContractStep.js index 8f2d2e07998e..f35acd5ea16e 100644 --- a/src/pages/ReimbursementAccount/ACHContractStep.js +++ b/src/pages/ReimbursementAccount/ACHContractStep.js @@ -5,7 +5,6 @@ import React, {useState} from 'react'; import {View} from 'react-native'; import _ from 'underscore'; import CheckboxWithLabel from '@components/CheckboxWithLabel'; -import Form from '@components/Form'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; @@ -19,6 +18,8 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import IdentityForm from './IdentityForm'; import StepPropTypes from './StepPropTypes'; +import FormProvider from '@components/Form/FormProvider'; +import InputWrapper from '@components/Form/InputWrapper'; const propTypes = { ...StepPropTypes, @@ -156,7 +157,7 @@ function ACHContractStep(props) { shouldShowGetAssistanceButton guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_BANK_ACCOUNT} /> -
{props.translate('beneficialOwnersStep.checkAllThatApply')} - - )} {props.translate('beneficialOwnersStep.agreement')} - - )} - + ); } diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js index 41f73d1ebf8e..ace059ae46ea 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.js @@ -7,8 +7,7 @@ import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import CheckboxWithLabel from '@components/CheckboxWithLabel'; -import DatePicker from '@components/DatePicker'; -import Form from '@components/Form'; +import NewDatePicker from '@components/NewDatePicker'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import Picker from '@components/Picker'; import ScreenWrapper from '@components/ScreenWrapper'; @@ -25,6 +24,8 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import AddressForm from './AddressForm'; import StepPropTypes from './StepPropTypes'; +import FormProvider from '@components/Form/FormProvider'; +import InputWrapper from '@components/Form/InputWrapper'; const propTypes = { ...StepPropTypes, @@ -151,7 +152,7 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_BANK_ACCOUNT} onBackButtonPress={onBackButtonPress} /> -
{translate('companyStep.subtitle')} - - - - - ({value: key, label: translate(`companyStep.incorporationTypes.${key}`)}))} @@ -234,7 +240,7 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul /> - - - - + ); } diff --git a/src/pages/ReimbursementAccount/IdentityForm.js b/src/pages/ReimbursementAccount/IdentityForm.js index f584ca3f665a..cf6d894ca7b9 100644 --- a/src/pages/ReimbursementAccount/IdentityForm.js +++ b/src/pages/ReimbursementAccount/IdentityForm.js @@ -3,11 +3,12 @@ import PropTypes from 'prop-types'; import React from 'react'; import {View} from 'react-native'; import _ from 'underscore'; -import DatePicker from '@components/DatePicker'; +import NewDatePicker from '@components/NewDatePicker'; import TextInput from '@components/TextInput'; import styles from '@styles/styles'; import CONST from '@src/CONST'; import AddressForm from './AddressForm'; +import InputWrapper from '@components/Form/InputWrapper'; const propTypes = { /** Style for wrapping View */ @@ -141,7 +142,8 @@ function IdentityForm(props) { - - - - -
- - + ); } From 5c462730b56dbd42597252fdc1c81765a7e40dd6 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Thu, 9 Nov 2023 18:25:02 +0100 Subject: [PATCH 126/245] fix prettier --- src/components/CheckboxWithLabel.js | 2 +- src/pages/ReimbursementAccount/ACHContractStep.js | 4 ++-- src/pages/ReimbursementAccount/CompanyStep.js | 6 +++--- src/pages/ReimbursementAccount/IdentityForm.js | 2 +- src/pages/ReimbursementAccount/RequestorStep.js | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/CheckboxWithLabel.js b/src/components/CheckboxWithLabel.js index 148ac5f9822b..3d467b6372d2 100644 --- a/src/components/CheckboxWithLabel.js +++ b/src/components/CheckboxWithLabel.js @@ -7,8 +7,8 @@ import variables from '@styles/variables'; import Checkbox from './Checkbox'; import FormHelpMessage from './FormHelpMessage'; import PressableWithFeedback from './Pressable/PressableWithFeedback'; -import Text from './Text'; import refPropTypes from './refPropTypes'; +import Text from './Text'; /** * Returns an error if the required props are not provided diff --git a/src/pages/ReimbursementAccount/ACHContractStep.js b/src/pages/ReimbursementAccount/ACHContractStep.js index f35acd5ea16e..8543dfdba5b3 100644 --- a/src/pages/ReimbursementAccount/ACHContractStep.js +++ b/src/pages/ReimbursementAccount/ACHContractStep.js @@ -5,6 +5,8 @@ import React, {useState} from 'react'; import {View} from 'react-native'; import _ from 'underscore'; import CheckboxWithLabel from '@components/CheckboxWithLabel'; +import FormProvider from '@components/Form/FormProvider'; +import InputWrapper from '@components/Form/InputWrapper'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; @@ -18,8 +20,6 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import IdentityForm from './IdentityForm'; import StepPropTypes from './StepPropTypes'; -import FormProvider from '@components/Form/FormProvider'; -import InputWrapper from '@components/Form/InputWrapper'; const propTypes = { ...StepPropTypes, diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js index ace059ae46ea..c8c8c555e78f 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.js @@ -7,8 +7,10 @@ import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import CheckboxWithLabel from '@components/CheckboxWithLabel'; -import NewDatePicker from '@components/NewDatePicker'; +import FormProvider from '@components/Form/FormProvider'; +import InputWrapper from '@components/Form/InputWrapper'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import NewDatePicker from '@components/NewDatePicker'; import Picker from '@components/Picker'; import ScreenWrapper from '@components/ScreenWrapper'; import StatePicker from '@components/StatePicker'; @@ -24,8 +26,6 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import AddressForm from './AddressForm'; import StepPropTypes from './StepPropTypes'; -import FormProvider from '@components/Form/FormProvider'; -import InputWrapper from '@components/Form/InputWrapper'; const propTypes = { ...StepPropTypes, diff --git a/src/pages/ReimbursementAccount/IdentityForm.js b/src/pages/ReimbursementAccount/IdentityForm.js index cf6d894ca7b9..4f1e5e6413c5 100644 --- a/src/pages/ReimbursementAccount/IdentityForm.js +++ b/src/pages/ReimbursementAccount/IdentityForm.js @@ -3,12 +3,12 @@ import PropTypes from 'prop-types'; import React from 'react'; import {View} from 'react-native'; import _ from 'underscore'; +import InputWrapper from '@components/Form/InputWrapper'; import NewDatePicker from '@components/NewDatePicker'; import TextInput from '@components/TextInput'; import styles from '@styles/styles'; import CONST from '@src/CONST'; import AddressForm from './AddressForm'; -import InputWrapper from '@components/Form/InputWrapper'; const propTypes = { /** Style for wrapping View */ diff --git a/src/pages/ReimbursementAccount/RequestorStep.js b/src/pages/ReimbursementAccount/RequestorStep.js index 2431abd98380..2b78eeee84ae 100644 --- a/src/pages/ReimbursementAccount/RequestorStep.js +++ b/src/pages/ReimbursementAccount/RequestorStep.js @@ -3,6 +3,8 @@ import PropTypes from 'prop-types'; import React, {useCallback, useMemo} from 'react'; import {View} from 'react-native'; import CheckboxWithLabel from '@components/CheckboxWithLabel'; +import FormProvider from '@components/Form/FormProvider'; +import InputWrapper from '@components/Form/InputWrapper'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; @@ -16,8 +18,6 @@ import ONYXKEYS from '@src/ONYXKEYS'; import IdentityForm from './IdentityForm'; import {reimbursementAccountPropTypes} from './reimbursementAccountPropTypes'; import RequestorOnfidoStep from './RequestorOnfidoStep'; -import FormProvider from '@components/Form/FormProvider'; -import InputWrapper from '@components/Form/InputWrapper'; const propTypes = { onBackButtonPress: PropTypes.func.isRequired, From 3b7199be403bfc9ca5ea9f8fb65feeb7bc770fed Mon Sep 17 00:00:00 2001 From: Daniel Edwards Date: Thu, 9 Nov 2023 15:17:33 -0500 Subject: [PATCH 127/245] 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 128/245] 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 129/245] 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 130/245] 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 131/245] 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 132/245] `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 3ff80939f3aa87512e8ed5966163f1550e9392dd Mon Sep 17 00:00:00 2001 From: Dylan Date: Fri, 10 Nov 2023 16:43:53 +0700 Subject: [PATCH 133/245] update correct pendingFields --- src/libs/actions/IOU.js | 9 +++++++++ src/libs/actions/Transaction.ts | 2 +- src/types/onyx/Transaction.ts | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 86b933a6897d..7facdeac431e 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -301,6 +301,7 @@ function buildOnyxDataForMoneyRequest( hasOutstandingIOU: chatReport.hasOutstandingIOU, iouReportID: chatReport.iouReportID, lastReadTime: chatReport.lastReadTime, + pendingFields: null, ...(isNewChatReport ? { errorFields: { @@ -316,6 +317,7 @@ function buildOnyxDataForMoneyRequest( onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, value: { + pendingFields: null, errorFields: { createChat: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), }, @@ -328,6 +330,7 @@ function buildOnyxDataForMoneyRequest( key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, value: { errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + pendingAction: null, }, }, { @@ -337,14 +340,17 @@ function buildOnyxDataForMoneyRequest( ...(isNewChatReport ? { [chatCreatedAction.reportActionID]: { + pendingAction: null, errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), }, [reportPreviewAction.reportActionID]: { + pendingAction: null, errors: ErrorUtils.getMicroSecondOnyxError(null), }, } : { [reportPreviewAction.reportActionID]: { + pendingAction: null, created: reportPreviewAction.created, errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), }, @@ -358,14 +364,17 @@ function buildOnyxDataForMoneyRequest( ...(isNewIOUReport ? { [iouCreatedAction.reportActionID]: { + pendingAction: null, errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), }, [iouAction.reportActionID]: { + pendingAction: null, errors: ErrorUtils.getMicroSecondOnyxError(null), }, } : { [iouAction.reportActionID]: { + pendingAction: null, errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), }, }), diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index 285fd5b251df..65795e660df0 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -59,7 +59,7 @@ function addStop(transactionID: string) { function saveWaypoint(transactionID: string, index: string, waypoint: RecentWaypoint | null, isEditingWaypoint = false) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, { pendingFields: { - comment: isEditingWaypoint ? CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE : CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + waypoints: isEditingWaypoint ? CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE : CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, }, comment: { waypoints: { diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index e52389a72b46..295f5a1639e5 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -71,7 +71,7 @@ type Transaction = { routes?: Routes; transactionID: string; tag: string; - pendingFields?: Partial<{[K in keyof Transaction]: ValueOf}>; + pendingFields?: Partial<{[K in keyof Transaction | keyof Comment]: ValueOf}>; /** Card Transactions */ From 1f75c957989c3f86cd7513d465fd9ff973f70c24 Mon Sep 17 00:00:00 2001 From: Dylan Date: Fri, 10 Nov 2023 17:06:26 +0700 Subject: [PATCH 134/245] update correct pendingFields --- src/libs/actions/IOU.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 7facdeac431e..274ef07ea8d6 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -340,17 +340,14 @@ function buildOnyxDataForMoneyRequest( ...(isNewChatReport ? { [chatCreatedAction.reportActionID]: { - pendingAction: null, errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), }, [reportPreviewAction.reportActionID]: { - pendingAction: null, errors: ErrorUtils.getMicroSecondOnyxError(null), }, } : { [reportPreviewAction.reportActionID]: { - pendingAction: null, created: reportPreviewAction.created, errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), }, @@ -364,17 +361,14 @@ function buildOnyxDataForMoneyRequest( ...(isNewIOUReport ? { [iouCreatedAction.reportActionID]: { - pendingAction: null, errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), }, [iouAction.reportActionID]: { - pendingAction: null, errors: ErrorUtils.getMicroSecondOnyxError(null), }, } : { [iouAction.reportActionID]: { - pendingAction: null, errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), }, }), From ba75ff99bdef26ff2af65cbb535e4a683f871908 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 10 Nov 2023 18:46:06 +0700 Subject: [PATCH 135/245] 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 136/245] 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 137/245] 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 138/245] 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 139/245] 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 140/245] 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 d8b9866ce701ac04351296fa4b93bc7aa610fe09 Mon Sep 17 00:00:00 2001 From: BhuvaneshPatil Date: Sat, 11 Nov 2023 16:39:56 +0530 Subject: [PATCH 141/245] fix: double tap on currency selects AFN --- src/pages/iou/IOUCurrencySelection.js | 37 +++++++++++++++++---------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index 20344a08a2c8..eab9ab5a7510 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -157,20 +157,29 @@ function IOUCurrencySelection(props) { onEntryTransitionEnd={() => optionsSelectorRef.current && optionsSelectorRef.current.focus()} testID={IOUCurrencySelection.displayName} > - Navigation.goBack(ROUTES.MONEY_REQUEST.getRoute(iouType, reportID))} - /> - + {({didScreenTransitionEnd}) => ( + <> + Navigation.goBack(ROUTES.MONEY_REQUEST.getRoute(iouType, reportID))} + /> + { + if (!didScreenTransitionEnd) { + return; + } + confirmCurrencySelection(option); + }} + headerMessage={headerMessage} + initiallyFocusedOptionKey={initiallyFocusedOptionKey} + showScrollIndicator + /> + + )} ); } From f34c1e24730681a453245bb015db43d573dd5287 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 13 Nov 2023 10:40:39 +0700 Subject: [PATCH 142/245] 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 4043d4f60a89db07d75fe745b62d22a864b64112 Mon Sep 17 00:00:00 2001 From: Dylan Date: Mon, 13 Nov 2023 14:44:13 +0700 Subject: [PATCH 143/245] fix test --- src/libs/actions/IOU.js | 6 ++++++ tests/actions/IOUTest.js | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 3e1385e6af6b..73eb1aafac0e 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -341,15 +341,18 @@ function buildOnyxDataForMoneyRequest( ? { [chatCreatedAction.reportActionID]: { errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + pendingAction: null, }, [reportPreviewAction.reportActionID]: { errors: ErrorUtils.getMicroSecondOnyxError(null), + pendingAction: null, }, } : { [reportPreviewAction.reportActionID]: { created: reportPreviewAction.created, errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + pendingAction: null, }, }), }, @@ -362,14 +365,17 @@ function buildOnyxDataForMoneyRequest( ? { [iouCreatedAction.reportActionID]: { errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + pendingAction: null, }, [iouAction.reportActionID]: { errors: ErrorUtils.getMicroSecondOnyxError(null), + pendingAction: null, }, } : { [iouAction.reportActionID]: { errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + pendingAction: null, }, }), }, diff --git a/tests/actions/IOUTest.js b/tests/actions/IOUTest.js index 76098d72f52e..bd069b5efd74 100644 --- a/tests/actions/IOUTest.js +++ b/tests/actions/IOUTest.js @@ -672,7 +672,7 @@ describe('actions/IOU', () => { Onyx.disconnect(connectionID); expect(_.size(reportActionsForIOUReport)).toBe(2); iouAction = _.find(reportActionsForIOUReport, (reportAction) => reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU); - expect(iouAction.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); + expect(iouAction.pendingAction).toBeFalsy(); resolve(); }, }); @@ -686,7 +686,7 @@ describe('actions/IOU', () => { waitForCollectionCallback: true, callback: (transaction) => { Onyx.disconnect(connectionID); - expect(transaction.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); + expect(transaction.pendingAction).toBeFalsy(); expect(transaction.errors).toBeTruthy(); expect(_.values(transaction.errors)[0]).toBe('iou.error.genericCreateFailureMessage'); resolve(); From 20e80d13a434ac8bc2876394a92cf3c54f557e3a Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 13 Nov 2023 09:28:17 +0100 Subject: [PATCH 144/245] 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 145/245] 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 11478622faf3d0ea20ce3a21931e530431c03aa9 Mon Sep 17 00:00:00 2001 From: Dylan Date: Mon, 13 Nov 2023 16:32:07 +0700 Subject: [PATCH 146/245] fix test --- src/libs/actions/IOU.js | 6 ------ tests/actions/IOUTest.js | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 73eb1aafac0e..3e1385e6af6b 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -341,18 +341,15 @@ function buildOnyxDataForMoneyRequest( ? { [chatCreatedAction.reportActionID]: { errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), - pendingAction: null, }, [reportPreviewAction.reportActionID]: { errors: ErrorUtils.getMicroSecondOnyxError(null), - pendingAction: null, }, } : { [reportPreviewAction.reportActionID]: { created: reportPreviewAction.created, errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), - pendingAction: null, }, }), }, @@ -365,17 +362,14 @@ function buildOnyxDataForMoneyRequest( ? { [iouCreatedAction.reportActionID]: { errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), - pendingAction: null, }, [iouAction.reportActionID]: { errors: ErrorUtils.getMicroSecondOnyxError(null), - pendingAction: null, }, } : { [iouAction.reportActionID]: { errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), - pendingAction: null, }, }), }, diff --git a/tests/actions/IOUTest.js b/tests/actions/IOUTest.js index bd069b5efd74..d3be1dc619d4 100644 --- a/tests/actions/IOUTest.js +++ b/tests/actions/IOUTest.js @@ -672,7 +672,7 @@ describe('actions/IOU', () => { Onyx.disconnect(connectionID); expect(_.size(reportActionsForIOUReport)).toBe(2); iouAction = _.find(reportActionsForIOUReport, (reportAction) => reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU); - expect(iouAction.pendingAction).toBeFalsy(); + expect(iouAction.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); resolve(); }, }); From ba8a7400a80af873f3775cc57eba73104e068c94 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Mon, 13 Nov 2023 10:45:02 +0100 Subject: [PATCH 147/245] pass inputValues through children in FormProvider --- src/components/Form/FormProvider.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Form/FormProvider.js b/src/components/Form/FormProvider.js index 92baa9727832..e06965062262 100644 --- a/src/components/Form/FormProvider.js +++ b/src/components/Form/FormProvider.js @@ -320,7 +320,7 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC errors={errors} enabledWhenOffline={enabledWhenOffline} > - {children} + {_.isFunction(children) ? children({inputValues}) : children} ); From b73218219e1d84e9c71feaffee7ef22a000793b6 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 13 Nov 2023 17:43:50 +0700 Subject: [PATCH 148/245] 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 149/245] 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 150/245] 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 151/245] 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 a196af833adc06f9e3c98ad41166304a508d99c0 Mon Sep 17 00:00:00 2001 From: Puneet Lath Date: Mon, 13 Nov 2023 15:18:50 -0500 Subject: [PATCH 152/245] Fallback to login if we have it --- src/libs/ReportUtils.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 9f8e138afccd..8c5c264010f7 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -11,6 +11,7 @@ import * as defaultWorkspaceAvatars from '@components/Icon/WorkspaceDefaultAvata import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import * as LocalePhoneNumber from './LocalePhoneNumber'; import * as CurrencyUtils from './CurrencyUtils'; import DateUtils from './DateUtils'; import isReportMessageAttachment from './isReportMessageAttachment'; @@ -1258,15 +1259,18 @@ function getDisplayNameForParticipant(accountID, shouldUseShortForm = false, sho return ''; } const personalDetails = getPersonalDetailsForAccountID(accountID); - // this is to check if account is an invite/optimistically created one + + // This is to check if account is an invite/optimistically created one // and prevent from falling back to 'Hidden', so a correct value is shown // when searching for a new user if (lodashGet(personalDetails, 'isOptimisticPersonalDetail') === true) { return personalDetails.login || ''; } - const longName = personalDetails.displayName; + + const login = LocalePhoneNumber.formatPhoneNumber(personalDetails.login || ''); + const longName = personalDetails.displayName || login; const shortName = personalDetails.firstName || longName; - if (!longName && !personalDetails.login && shouldFallbackToHidden) { + if (!longName && shouldFallbackToHidden) { return Localize.translateLocal('common.hidden'); } return shouldUseShortForm ? shortName : longName; From 040e2a258ba693819b48de5b476ae392ef68910d Mon Sep 17 00:00:00 2001 From: Puneet Lath Date: Mon, 13 Nov 2023 15:25:29 -0500 Subject: [PATCH 153/245] Remove unneeded isOptimisticPersonalDetail logic --- src/libs/ReportUtils.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 8c5c264010f7..fc4648d31daf 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1259,16 +1259,8 @@ function getDisplayNameForParticipant(accountID, shouldUseShortForm = false, sho return ''; } const personalDetails = getPersonalDetailsForAccountID(accountID); - - // This is to check if account is an invite/optimistically created one - // and prevent from falling back to 'Hidden', so a correct value is shown - // when searching for a new user - if (lodashGet(personalDetails, 'isOptimisticPersonalDetail') === true) { - return personalDetails.login || ''; - } - - const login = LocalePhoneNumber.formatPhoneNumber(personalDetails.login || ''); - const longName = personalDetails.displayName || login; + const formattedLogin = LocalePhoneNumber.formatPhoneNumber(personalDetails.login || ''); + const longName = personalDetails.displayName || formattedLogin; const shortName = personalDetails.firstName || longName; if (!longName && shouldFallbackToHidden) { return Localize.translateLocal('common.hidden'); 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 154/245] 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 155/245] 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 156/245] 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 157/245] 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 aadf005473d4d177394b5d302a576f14a6c73466 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Tue, 14 Nov 2023 09:48:26 +0100 Subject: [PATCH 158/245] bump onyx --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4772399c3a3a..2aa88a47df85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -94,7 +94,7 @@ "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", "react-native-modal": "^13.0.0", - "react-native-onyx": "1.0.115", + "react-native-onyx": "1.0.117", "react-native-pager-view": "^6.2.0", "react-native-pdf": "^6.7.1", "react-native-performance": "^5.1.0", @@ -44345,9 +44345,9 @@ } }, "node_modules/react-native-onyx": { - "version": "1.0.115", - "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-1.0.115.tgz", - "integrity": "sha512-uPrJcw3Ta/EFL3Mh3iUggZ7EeEwLTSSSc5iUkKAA+a9Y8kBo8+6MWup9VCM/4wgysZbf3VHUGJCWQ8H3vWKgUg==", + "version": "1.0.117", + "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-1.0.117.tgz", + "integrity": "sha512-zX2bCclBd+QPkRpPlFKbh/yfpx0RXcJ8qpqpI1Mml7unN9R0DqhAAZj3hA7rAnBWGgoss8smMg8DbyGghpIi5Q==", "dependencies": { "ascii-table": "0.0.9", "fast-equals": "^4.0.3", @@ -84651,9 +84651,9 @@ } }, "react-native-onyx": { - "version": "1.0.115", - "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-1.0.115.tgz", - "integrity": "sha512-uPrJcw3Ta/EFL3Mh3iUggZ7EeEwLTSSSc5iUkKAA+a9Y8kBo8+6MWup9VCM/4wgysZbf3VHUGJCWQ8H3vWKgUg==", + "version": "1.0.117", + "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-1.0.117.tgz", + "integrity": "sha512-zX2bCclBd+QPkRpPlFKbh/yfpx0RXcJ8qpqpI1Mml7unN9R0DqhAAZj3hA7rAnBWGgoss8smMg8DbyGghpIi5Q==", "requires": { "ascii-table": "0.0.9", "fast-equals": "^4.0.3", diff --git a/package.json b/package.json index 77bd4c0281d5..1c4d005ddd4d 100644 --- a/package.json +++ b/package.json @@ -141,7 +141,7 @@ "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", "react-native-modal": "^13.0.0", - "react-native-onyx": "1.0.115", + "react-native-onyx": "1.0.117", "react-native-pager-view": "^6.2.0", "react-native-pdf": "^6.7.1", "react-native-performance": "^5.1.0", 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 159/245] 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 4156a51bcea75eef859ccb06eed8a0fff0a702b1 Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 14 Nov 2023 16:27:27 +0700 Subject: [PATCH 160/245] add comment --- src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.js b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.js index 3239343accd5..1f4accbe8d8a 100644 --- a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.js +++ b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.js @@ -137,6 +137,7 @@ class WorkspaceRateAndUnitPage extends React.Component { const errors = {}; const decimalSeparator = this.props.toLocaleDigit('.'); const outputCurrency = lodashGet(this.props, 'policy.outputCurrency', CONST.CURRENCY.USD); + // Allow one more decimal place for accuracy const rateValueRegex = RegExp(String.raw`^-?\d{0,8}([${getPermittedDecimalSeparator(decimalSeparator)}]\d{1,${CurrencyUtils.getCurrencyDecimals(outputCurrency) + 1}})?$`, 'i'); if (!rateValueRegex.test(values.rate) || values.rate === '') { errors.rate = 'workspace.reimburse.invalidRateError'; From 8240651e1f5230626f099388fe558efa1244caea Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 14 Nov 2023 17:17:17 +0700 Subject: [PATCH 161/245] fix lint --- src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.js b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.js index 1f4accbe8d8a..534b3b1a821f 100644 --- a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.js +++ b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.js @@ -10,9 +10,9 @@ import Picker from '@components/Picker'; import TextInput from '@components/TextInput'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import compose from '@libs/compose'; +import * as CurrencyUtils from '@libs/CurrencyUtils'; import getPermittedDecimalSeparator from '@libs/getPermittedDecimalSeparator'; import Navigation from '@libs/Navigation/Navigation'; -import * as CurrencyUtils from '@libs/CurrencyUtils'; import * as NumberUtils from '@libs/NumberUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; From 66092e7284a61d52835084ee3befdbcdb4616086 Mon Sep 17 00:00:00 2001 From: Jakub Trzebiatowski Date: Tue, 14 Nov 2023 11:55:39 +0100 Subject: [PATCH 162/245] 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 163/245] 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 164/245] 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 12bed4ff951665a2d99329329797f056ee6450c4 Mon Sep 17 00:00:00 2001 From: Situ Chandra Shil <108292595+situchan@users.noreply.github.com> Date: Tue, 14 Nov 2023 18:26:38 +0600 Subject: [PATCH 165/245] Revert "fixes the issue #29379" --- .../EmojiPicker/EmojiPickerButton.js | 7 +-- .../AttachmentPickerWithMenuItems.js | 55 +++---------------- 2 files changed, 10 insertions(+), 52 deletions(-) diff --git a/src/components/EmojiPicker/EmojiPickerButton.js b/src/components/EmojiPicker/EmojiPickerButton.js index ddfa6b89c899..ab9daf3f0cb1 100644 --- a/src/components/EmojiPicker/EmojiPickerButton.js +++ b/src/components/EmojiPicker/EmojiPickerButton.js @@ -5,8 +5,6 @@ import * as Expensicons from '@components/Icon/Expensicons'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import Tooltip from '@components/Tooltip/PopoverAnchorTooltip'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; -import withNavigationFocus from '@components/withNavigationFocus'; -import compose from '@libs/compose'; import getButtonState from '@libs/getButtonState'; import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; @@ -43,9 +41,6 @@ function EmojiPickerButton(props) { style={({hovered, pressed}) => [styles.chatItemEmojiButton, StyleUtils.getButtonBackgroundColorStyle(getButtonState(hovered, pressed))]} disabled={props.isDisabled} onPress={() => { - if (!props.isFocused) { - return; - } if (!EmojiPickerAction.emojiPickerRef.current.isEmojiPickerVisible) { EmojiPickerAction.showEmojiPicker(props.onModalHide, props.onEmojiSelected, emojiPopoverAnchor.current, undefined, () => {}, props.emojiPickerID); } else { @@ -69,4 +64,4 @@ function EmojiPickerButton(props) { EmojiPickerButton.propTypes = propTypes; EmojiPickerButton.defaultProps = defaultProps; EmojiPickerButton.displayName = 'EmojiPickerButton'; -export default compose(withLocalize, withNavigationFocus)(EmojiPickerButton); +export default withLocalize(EmojiPickerButton); diff --git a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.js b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.js index 6ab09a5a1bd4..a8ecff7c8d82 100644 --- a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.js +++ b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.js @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import React, {useCallback, useEffect, useMemo} from 'react'; +import React, {useMemo} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; @@ -9,12 +9,9 @@ import * as Expensicons from '@components/Icon/Expensicons'; import PopoverMenu from '@components/PopoverMenu'; import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; import Tooltip from '@components/Tooltip/PopoverAnchorTooltip'; -import withNavigationFocus from '@components/withNavigationFocus'; import useLocalize from '@hooks/useLocalize'; -import usePrevious from '@hooks/usePrevious'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as Browser from '@libs/Browser'; -import compose from '@libs/compose'; import Permissions from '@libs/Permissions'; import * as ReportUtils from '@libs/ReportUtils'; import styles from '@styles/styles'; @@ -87,9 +84,6 @@ const propTypes = { // eslint-disable-next-line react/forbid-prop-types current: PropTypes.object, }).isRequired, - - /** Whether navigation is focused */ - isFocused: PropTypes.bool.isRequired, }; const defaultProps = { @@ -122,7 +116,6 @@ function AttachmentPickerWithMenuItems({ onAddActionPressed, onItemSelected, actionButtonRef, - isFocused, }) { const {translate} = useLocalize(); const {windowHeight} = useWindowDimensions(); @@ -171,33 +164,10 @@ function AttachmentPickerWithMenuItems({ ]; }, [betas, report, reportID, translate]); - const onPopoverMenuClose = useCallback(() => { + const onPopoverMenuClose = () => { setMenuVisibility(false); onMenuClosed(); - }, [onMenuClosed, setMenuVisibility]); - - const prevIsFocused = usePrevious(isFocused); - - /** - * Check if current screen is inactive and previous screen is active. - * Used to close already opened popover menu when any other page is opened over current page. - * - * @return {Boolean} - */ - const didScreenBecomeInactive = useCallback( - () => - // When any other page is opened over LHN - !isFocused && prevIsFocused, - [isFocused, prevIsFocused], - ); - - // When the navigation is focused, we want to close the popover menu. - useEffect(() => { - if (!didScreenBecomeInactive()) { - return; - } - onPopoverMenuClose(); - }, [didScreenBecomeInactive, onPopoverMenuClose]); + }; return ( @@ -269,10 +239,6 @@ function AttachmentPickerWithMenuItems({ ref={actionButtonRef} onPress={(e) => { e.preventDefault(); - if (!isFocused) { - return; - } - onAddActionPressed(); // Drop focus to avoid blue focus ring. @@ -290,7 +256,7 @@ function AttachmentPickerWithMenuItems({
{ setMenuVisibility(false); @@ -320,11 +286,8 @@ AttachmentPickerWithMenuItems.propTypes = propTypes; AttachmentPickerWithMenuItems.defaultProps = defaultProps; AttachmentPickerWithMenuItems.displayName = 'AttachmentPickerWithMenuItems'; -export default compose( - withNavigationFocus, - withOnyx({ - betas: { - key: ONYXKEYS.BETAS, - }, - }), -)(AttachmentPickerWithMenuItems); +export default withOnyx({ + betas: { + key: ONYXKEYS.BETAS, + }, +})(AttachmentPickerWithMenuItems); From cdb1b804c19eef54c0841d0ba8d426ab7a79a9d2 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Tue, 14 Nov 2023 15:37:48 +0100 Subject: [PATCH 166/245] reverting changes --- ios/Podfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 5b7886c8ebcd..952451ff88bf 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -234,8 +234,8 @@ PODS: - libwebp/demux - libwebp/webp (1.2.4) - lottie-ios (4.3.3) - - lottie-react-native (6.4.0): - - lottie-ios (~> 4.3.3) + - lottie-react-native (6.3.1): + - lottie-ios (~> 4.3.0) - React-Core - MapboxCommon (23.6.0) - MapboxCoreMaps (10.14.0): @@ -1203,7 +1203,7 @@ SPEC CHECKSUMS: libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef lottie-ios: 25e7b2675dad5c3ddad369ac9baab03560c5bfdd - lottie-react-native: 3a3084faddd3891c276f23fd6e797b83f2021bbc + lottie-react-native: c9f1db4f4124dcce9f8159e65d8dc6e8bcb11fb4 MapboxCommon: 4a0251dd470ee37e7fadda8e285c01921a5e1eb0 MapboxCoreMaps: eb07203bbb0b1509395db5ab89cd3ad6c2e3c04c MapboxMaps: af50ec61a7eb3b032c3f7962c6bd671d93d2a209 From 5dde6f20f053480024bc298bba130ba45269fed9 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Tue, 14 Nov 2023 16:07:38 +0100 Subject: [PATCH 167/245] Fixed comments --- ios/Podfile.lock | 6 +++--- src/pages/home/report/ReportActionsList.js | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 952451ff88bf..5b7886c8ebcd 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -234,8 +234,8 @@ PODS: - libwebp/demux - libwebp/webp (1.2.4) - lottie-ios (4.3.3) - - lottie-react-native (6.3.1): - - lottie-ios (~> 4.3.0) + - lottie-react-native (6.4.0): + - lottie-ios (~> 4.3.3) - React-Core - MapboxCommon (23.6.0) - MapboxCoreMaps (10.14.0): @@ -1203,7 +1203,7 @@ SPEC CHECKSUMS: libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef lottie-ios: 25e7b2675dad5c3ddad369ac9baab03560c5bfdd - lottie-react-native: c9f1db4f4124dcce9f8159e65d8dc6e8bcb11fb4 + lottie-react-native: 3a3084faddd3891c276f23fd6e797b83f2021bbc MapboxCommon: 4a0251dd470ee37e7fadda8e285c01921a5e1eb0 MapboxCoreMaps: eb07203bbb0b1509395db5ab89cd3ad6c2e3c04c MapboxMaps: af50ec61a7eb3b032c3f7962c6bd671d93d2a209 diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index c808dfbbb8be..c3e4371cdfbf 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -222,7 +222,7 @@ function ReportActionsList({ unreadActionSubscription.current = null; } - // Need to listen for the specific reportID, otherwise we could be listening to all the reports + // Listen to specific reportID for unread event and set the marker to new message unreadActionSubscription.current = DeviceEventEmitter.addListener(`unreadAction_${report.reportID}`, (newLastReadTime) => { cacheUnreadMarkers.delete(report.reportID); lastReadRef.current = newLastReadTime; @@ -326,12 +326,10 @@ function ReportActionsList({ const shouldDisplayNewMarker = useCallback( (reportAction, index) => { let shouldDisplay = false; - if (!currentUnreadMarker) { const nextMessage = sortedReportActions[index + 1]; const isCurrentMessageUnread = isMessageUnread(reportAction, lastReadRef.current); shouldDisplay = isCurrentMessageUnread && (!nextMessage || !isMessageUnread(nextMessage, lastReadRef.current)); - if (!messageManuallyMarkedUnread) { shouldDisplay = shouldDisplay && reportAction.actorAccountID !== Report.getCurrentUserAccountID(); } From b082a9a2fc0ef64c1fb770631f69ef95f4a2fb3b Mon Sep 17 00:00:00 2001 From: rory Date: Tue, 14 Nov 2023 08:12:44 -0800 Subject: [PATCH 168/245] 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: Tue, 14 Nov 2023 14:04:34 -0500 Subject: [PATCH 169/245] serialize execution of entire apply method instead of only applyPusherOnyxUpdates --- src/libs/actions/OnyxUpdates.ts | 48 ++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/src/libs/actions/OnyxUpdates.ts b/src/libs/actions/OnyxUpdates.ts index ce673fa6aaaf..852fc907a920 100644 --- a/src/libs/actions/OnyxUpdates.ts +++ b/src/libs/actions/OnyxUpdates.ts @@ -15,10 +15,6 @@ Onyx.connect({ callback: (val) => (lastUpdateIDAppliedToClient = val), }); -// This promise is used to ensure pusher events are always processed in the order they are received, -// even when such events are received over multiple separate pusher updates. -let pusherEventsPromise = Promise.resolve(); - function applyHTTPSOnyxUpdates(request: Request, response: Response) { console.debug('[OnyxUpdateManager] Applying https update'); // For most requests we can immediately update Onyx. For write requests we queue the updates and apply them after the sequential queue has flushed to prevent a replay effect in @@ -48,19 +44,22 @@ function applyHTTPSOnyxUpdates(request: Request, response: Response) { } function applyPusherOnyxUpdates(updates: OnyxUpdateEvent[]) { - pusherEventsPromise = pusherEventsPromise.then(() => { - console.debug('[OnyxUpdateManager] Applying pusher update'); - }); - - pusherEventsPromise = updates - .reduce((promise, update) => promise.then(() => PusherUtils.triggerMultiEventHandler(update.eventType, update.data)), pusherEventsPromise) + return updates + .reduce( + (promise, update) => promise.then(() => PusherUtils.triggerMultiEventHandler(update.eventType, update.data)), + Promise.resolve().then(() => { + console.debug('[OnyxUpdateManager] Applying pusher update'); + }), + ) .then(() => { console.debug('[OnyxUpdateManager] Done applying Pusher update'); }); - - return pusherEventsPromise; } +// This promise is used to ensure pusher events are always processed in the order they are received, +// even when such events are received over multiple separate pusher updates. +let applyPromise: Promise = Promise.resolve(); + /** * @param [updateParams.request] Exists if updateParams.type === 'https' * @param [updateParams.response] Exists if updateParams.type === 'https' @@ -69,21 +68,28 @@ function applyPusherOnyxUpdates(updates: OnyxUpdateEvent[]) { function apply({lastUpdateID, type, request, response, updates}: Merge): Promise; function apply({lastUpdateID, type, request, response, updates}: Merge): Promise; function apply({lastUpdateID, type, request, response, updates}: OnyxUpdatesFromServer): Promise | undefined { - console.debug(`[OnyxUpdateManager] Applying update type: ${type} with lastUpdateID: ${lastUpdateID}`, {request, response, updates}); + applyPromise = applyPromise.then(() => { + console.debug(`[OnyxUpdateManager] Applying update type: ${type} with lastUpdateID: ${lastUpdateID}`, {request, response, updates}); + + if (lastUpdateID && lastUpdateIDAppliedToClient && Number(lastUpdateID) < lastUpdateIDAppliedToClient) { + console.debug('[OnyxUpdateManager] Update received was older than current state, returning without applying the updates'); + return Promise.resolve(); + } + if (lastUpdateID && (lastUpdateIDAppliedToClient === null || Number(lastUpdateID) > lastUpdateIDAppliedToClient)) { + return Onyx.merge(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT, Number(lastUpdateID)); + } - if (lastUpdateID && lastUpdateIDAppliedToClient && Number(lastUpdateID) < lastUpdateIDAppliedToClient) { - console.debug('[OnyxUpdateManager] Update received was older than current state, returning without applying the updates'); return Promise.resolve(); - } - if (lastUpdateID && (lastUpdateIDAppliedToClient === null || Number(lastUpdateID) > lastUpdateIDAppliedToClient)) { - Onyx.merge(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT, Number(lastUpdateID)); - } + }); + if (type === CONST.ONYX_UPDATE_TYPES.HTTPS && request && response) { - return applyHTTPSOnyxUpdates(request, response); + applyPromise = applyPromise.then(() => applyHTTPSOnyxUpdates(request, response)); } if (type === CONST.ONYX_UPDATE_TYPES.PUSHER && updates) { - return applyPusherOnyxUpdates(updates); + applyPromise = applyPromise.then(() => applyPusherOnyxUpdates(updates)); } + + return applyPromise; } /** From cb7356e22acf5e399b18a95fe179c7fb4da47b23 Mon Sep 17 00:00:00 2001 From: Carlos Barros <765936+barros001@users.noreply.github.com> Date: Tue, 14 Nov 2023 14:14:28 -0500 Subject: [PATCH 170/245] Revert "serialize execution of entire apply method instead of only applyPusherOnyxUpdates" This reverts commit 82fb44bdfd00d84ddc4abcb1e19ba686c7ed9fed. --- src/libs/actions/OnyxUpdates.ts | 48 +++++++++++++++------------------ 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/src/libs/actions/OnyxUpdates.ts b/src/libs/actions/OnyxUpdates.ts index 852fc907a920..ce673fa6aaaf 100644 --- a/src/libs/actions/OnyxUpdates.ts +++ b/src/libs/actions/OnyxUpdates.ts @@ -15,6 +15,10 @@ Onyx.connect({ callback: (val) => (lastUpdateIDAppliedToClient = val), }); +// This promise is used to ensure pusher events are always processed in the order they are received, +// even when such events are received over multiple separate pusher updates. +let pusherEventsPromise = Promise.resolve(); + function applyHTTPSOnyxUpdates(request: Request, response: Response) { console.debug('[OnyxUpdateManager] Applying https update'); // For most requests we can immediately update Onyx. For write requests we queue the updates and apply them after the sequential queue has flushed to prevent a replay effect in @@ -44,21 +48,18 @@ function applyHTTPSOnyxUpdates(request: Request, response: Response) { } function applyPusherOnyxUpdates(updates: OnyxUpdateEvent[]) { - return updates - .reduce( - (promise, update) => promise.then(() => PusherUtils.triggerMultiEventHandler(update.eventType, update.data)), - Promise.resolve().then(() => { - console.debug('[OnyxUpdateManager] Applying pusher update'); - }), - ) + pusherEventsPromise = pusherEventsPromise.then(() => { + console.debug('[OnyxUpdateManager] Applying pusher update'); + }); + + pusherEventsPromise = updates + .reduce((promise, update) => promise.then(() => PusherUtils.triggerMultiEventHandler(update.eventType, update.data)), pusherEventsPromise) .then(() => { console.debug('[OnyxUpdateManager] Done applying Pusher update'); }); -} -// This promise is used to ensure pusher events are always processed in the order they are received, -// even when such events are received over multiple separate pusher updates. -let applyPromise: Promise = Promise.resolve(); + return pusherEventsPromise; +} /** * @param [updateParams.request] Exists if updateParams.type === 'https' @@ -68,28 +69,21 @@ let applyPromise: Promise = Promise.resolve(); function apply({lastUpdateID, type, request, response, updates}: Merge): Promise; function apply({lastUpdateID, type, request, response, updates}: Merge): Promise; function apply({lastUpdateID, type, request, response, updates}: OnyxUpdatesFromServer): Promise | undefined { - applyPromise = applyPromise.then(() => { - console.debug(`[OnyxUpdateManager] Applying update type: ${type} with lastUpdateID: ${lastUpdateID}`, {request, response, updates}); - - if (lastUpdateID && lastUpdateIDAppliedToClient && Number(lastUpdateID) < lastUpdateIDAppliedToClient) { - console.debug('[OnyxUpdateManager] Update received was older than current state, returning without applying the updates'); - return Promise.resolve(); - } - if (lastUpdateID && (lastUpdateIDAppliedToClient === null || Number(lastUpdateID) > lastUpdateIDAppliedToClient)) { - return Onyx.merge(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT, Number(lastUpdateID)); - } + console.debug(`[OnyxUpdateManager] Applying update type: ${type} with lastUpdateID: ${lastUpdateID}`, {request, response, updates}); + if (lastUpdateID && lastUpdateIDAppliedToClient && Number(lastUpdateID) < lastUpdateIDAppliedToClient) { + console.debug('[OnyxUpdateManager] Update received was older than current state, returning without applying the updates'); return Promise.resolve(); - }); - + } + if (lastUpdateID && (lastUpdateIDAppliedToClient === null || Number(lastUpdateID) > lastUpdateIDAppliedToClient)) { + Onyx.merge(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT, Number(lastUpdateID)); + } if (type === CONST.ONYX_UPDATE_TYPES.HTTPS && request && response) { - applyPromise = applyPromise.then(() => applyHTTPSOnyxUpdates(request, response)); + return applyHTTPSOnyxUpdates(request, response); } if (type === CONST.ONYX_UPDATE_TYPES.PUSHER && updates) { - applyPromise = applyPromise.then(() => applyPusherOnyxUpdates(updates)); + return applyPusherOnyxUpdates(updates); } - - return applyPromise; } /** From 67afd635b6cf4029829c5b911ac906336a7e2955 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 15 Nov 2023 11:16:09 +0700 Subject: [PATCH 171/245] hide mention of new user in offline mode --- src/pages/home/report/ReportActionCompose/SuggestionMention.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.js b/src/pages/home/report/ReportActionCompose/SuggestionMention.js index 1b93b2d56af0..0a9ed2c11293 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.js +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.js @@ -146,7 +146,7 @@ function SuggestionMention({ const filteredPersonalDetails = _.filter(_.values(personalDetailsParam), (detail) => { // If we don't have user's primary login, that member is not known to the current user and hence we do not allow them to be mentioned - if (!detail.login) { + if (!detail.login || detail.isOptimisticPersonalDetail) { return false; } if (searchValue && !`${detail.displayName} ${detail.login}`.toLowerCase().includes(searchValue.toLowerCase())) { From 2b83ba6ff04e606f33d7acaca510c81076e29a2f Mon Sep 17 00:00:00 2001 From: Eduardo Date: Wed, 15 Nov 2023 08:22:52 +0100 Subject: [PATCH 172/245] fixed more comments --- ios/Podfile.lock | 2 -- src/pages/home/report/ReportActionsList.js | 12 ++++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 5b7886c8ebcd..c49543eb8217 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1298,5 +1298,3 @@ SPEC CHECKSUMS: YogaKit: f782866e155069a2cca2517aafea43200b01fd5a PODFILE CHECKSUM: ff769666b7221c15936ebc5576a8c8e467dc6879 - -COCOAPODS: 1.12.1 \ No newline at end of file diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index c3e4371cdfbf..1ee68ab165d8 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -150,7 +150,7 @@ function ReportActionsList({ const hasHeaderRendered = useRef(false); const hasFooterRendered = useRef(false); const reportActionSize = useRef(sortedReportActions.length); - const lastReadRef = useRef(report.lastReadTime); + const lastReadTimeRef = useRef(report.lastReadTime); const linkedReportActionID = lodashGet(route, 'params.reportActionID', ''); @@ -205,10 +205,10 @@ function ReportActionsList({ if (!userActiveSince.current || report.reportID !== prevReportID.current) { return; } - if (!messageManuallyMarkedUnread && lastReadRef.current && lastReadRef.current < report.lastReadTime) { + if (!messageManuallyMarkedUnread && lastReadTimeRef.current && lastReadTimeRef.current < report.lastReadTime) { cacheUnreadMarkers.delete(report.reportID); } - lastReadRef.current = report.lastReadTime; + lastReadTimeRef.current = report.lastReadTime; setMessageManuallyMarkedUnread(0); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -225,7 +225,7 @@ function ReportActionsList({ // Listen to specific reportID for unread event and set the marker to new message unreadActionSubscription.current = DeviceEventEmitter.addListener(`unreadAction_${report.reportID}`, (newLastReadTime) => { cacheUnreadMarkers.delete(report.reportID); - lastReadRef.current = newLastReadTime; + lastReadTimeRef.current = newLastReadTime; setCurrentUnreadMarker(null); setMessageManuallyMarkedUnread(new Date().getTime()); }); @@ -328,8 +328,8 @@ function ReportActionsList({ let shouldDisplay = false; if (!currentUnreadMarker) { const nextMessage = sortedReportActions[index + 1]; - const isCurrentMessageUnread = isMessageUnread(reportAction, lastReadRef.current); - shouldDisplay = isCurrentMessageUnread && (!nextMessage || !isMessageUnread(nextMessage, lastReadRef.current)); + const isCurrentMessageUnread = isMessageUnread(reportAction, lastReadTimeRef.current); + shouldDisplay = isCurrentMessageUnread && (!nextMessage || !isMessageUnread(nextMessage, lastReadTimeRef.current)); if (!messageManuallyMarkedUnread) { shouldDisplay = shouldDisplay && reportAction.actorAccountID !== Report.getCurrentUserAccountID(); } From 68233f4b7b7e2fae3f8031c0e3727e5f7c63ae94 Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Wed, 15 Nov 2023 10:27:39 +0100 Subject: [PATCH 173/245] 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 8be10f2fc3d0f7fe28aed1203b5c82d80d698c2a Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Wed, 15 Nov 2023 11:13:26 +0100 Subject: [PATCH 174/245] fix: package deps out of sync --- package-lock.json | 51 +++++++++-------------------------------------- package.json | 6 ++---- 2 files changed, 11 insertions(+), 46 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2aa88a47df85..8b277572e1f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.3.98-5", + "version": "1.3.99-0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.3.98-5", + "version": "1.3.99-0", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -59,8 +59,6 @@ "lodash": "4.17.21", "lottie-react-native": "^6.4.0", "mapbox-gl": "^2.15.0", - "moment": "^2.29.4", - "moment-timezone": "^0.5.31", "onfido-sdk-ui": "13.1.0", "patch-package": "^8.0.0", "process": "^0.11.10", @@ -76,7 +74,7 @@ "react-native": "0.72.4", "react-native-android-location-enabler": "^1.2.2", "react-native-blob-util": "^0.17.3", - "react-native-collapsible": "^1.6.0", + "react-native-collapsible": "^1.6.1", "react-native-config": "^1.4.5", "react-native-dev-menu": "^4.1.1", "react-native-device-info": "^10.3.0", @@ -40896,27 +40894,6 @@ "dev": true, "license": "MIT" }, - "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/moment-timezone": { - "version": "0.5.43", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", - "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", - "license": "MIT", - "dependencies": { - "moment": "^2.29.4" - }, - "engines": { - "node": "*" - } - }, "node_modules/move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -44103,8 +44080,9 @@ } }, "node_modules/react-native-collapsible": { - "version": "1.6.0", - "license": "MIT", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/react-native-collapsible/-/react-native-collapsible-1.6.1.tgz", + "integrity": "sha512-orF4BeiXd2hZW7fu9YcqIJXzN6TJcFcddY807D3MAOVktLuW9oQ+RIkrTJ5DR3v9ZOFfREkOjEmS79qeUTvkBQ==", "peerDependencies": { "react": "*", "react-native": "*" @@ -82114,19 +82092,6 @@ "integrity": "sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw==", "dev": true }, - "moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" - }, - "moment-timezone": { - "version": "0.5.43", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", - "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", - "requires": { - "moment": "^2.29.4" - } - }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -84511,7 +84476,9 @@ "dev": true }, "react-native-collapsible": { - "version": "1.6.0", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/react-native-collapsible/-/react-native-collapsible-1.6.1.tgz", + "integrity": "sha512-orF4BeiXd2hZW7fu9YcqIJXzN6TJcFcddY807D3MAOVktLuW9oQ+RIkrTJ5DR3v9ZOFfREkOjEmS79qeUTvkBQ==", "requires": {} }, "react-native-config": { diff --git a/package.json b/package.json index 1c4d005ddd4d..083289aa8fe5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.3.98-5", + "version": "1.3.99-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.", @@ -106,8 +106,6 @@ "lodash": "4.17.21", "lottie-react-native": "^6.4.0", "mapbox-gl": "^2.15.0", - "moment": "^2.29.4", - "moment-timezone": "^0.5.31", "onfido-sdk-ui": "13.1.0", "patch-package": "^8.0.0", "process": "^0.11.10", @@ -123,7 +121,7 @@ "react-native": "0.72.4", "react-native-android-location-enabler": "^1.2.2", "react-native-blob-util": "^0.17.3", - "react-native-collapsible": "^1.6.0", + "react-native-collapsible": "^1.6.1", "react-native-config": "^1.4.5", "react-native-dev-menu": "^4.1.1", "react-native-device-info": "^10.3.0", From 60f26d3968f16d800873adb53fefd1f46f1da8d0 Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Wed, 15 Nov 2023 11:26:16 +0100 Subject: [PATCH 175/245] 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 c4541fb3ceaadb243f973726bad07e296bca0e06 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Wed, 15 Nov 2023 12:11:01 +0100 Subject: [PATCH 176/245] improved the comments --- ios/Podfile.lock | 2 ++ src/pages/home/report/ReportActionsList.js | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index c49543eb8217..5b7886c8ebcd 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1298,3 +1298,5 @@ SPEC CHECKSUMS: YogaKit: f782866e155069a2cca2517aafea43200b01fd5a PODFILE CHECKSUM: ff769666b7221c15936ebc5576a8c8e467dc6879 + +COCOAPODS: 1.12.1 \ No newline at end of file diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 1ee68ab165d8..31d418c37d4a 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -89,8 +89,9 @@ const MSG_VISIBLE_THRESHOLD = 250; // the subscriptions could otherwise be conflicting. const newActionUnsubscribeMap = {}; -// We cache the unread markers for each report, because the unread marker isn't -// kept between reports. +// Caching the reportID and reportActionID for unread markers ensures persistent tracking +// across multiple reports, preserving the green line placement and allowing retrieval +// of the relevant reportActionID for displaying the green line. const cacheUnreadMarkers = new Map(); /** * Create a unique key for each action in the FlatList. From b1cf0c1db6bfa2ffb1d1459ced9f79dc72c7e4f0 Mon Sep 17 00:00:00 2001 From: Matt Moore <46995600+muttmuure@users.noreply.github.com> Date: Wed, 15 Nov 2023 11:38:35 +0000 Subject: [PATCH 177/245] Update redirects Matt Mo --- docs/redirects.csv | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/redirects.csv b/docs/redirects.csv index 3bcfb594bedb..82add37c330c 100644 --- a/docs/redirects.csv +++ b/docs/redirects.csv @@ -17,4 +17,10 @@ https://community.expensify.com/discussion/5802/deep-dive-understanding-math-and https://community.expensify.com/discussion/5796/deep-dive-user-level-formula,https://help.expensify.com/articles/expensify-classic/insights-and-custom-reporting/Custom-Templates https://community.expensify.com/discussion/4750/how-to-create-a-custom-export,https://help.expensify.com/articles/expensify-classic/insights-and-custom-reporting/Custom-Templates https://community.expensify.com/discussion/4642/how-to-export-reports-to-a-custom-template,https://help.expensify.com/articles/expensify-classic/insights-and-custom-reporting/Custom-Templates - +https://community.expensify.com/discussion/5648/deep-dive-policy-users-and-roles,https://help.expensify.com/articles/expensify-classic/manage-employees-and-report-approvals/User-Roles +https://community.expensify.com/discussion/5740/deep-dive-what-expense-information-is-available-based-on-role,https://help.expensify.com/articles/expensify-classic/manage-employees-and-report-approvals/User-Roles +https://community.expensify.com/discussion/4472/how-to-set-or-edit-a-user-role,https://help.expensify.com/articles/expensify-classic/manage-employees-and-report-approvals/User-Roles +https://community.expensify.com/discussion/5655/deep-dive-what-is-a-vacation-delegate,https://help.expensify.com/articles/expensify-classic/manage-employees-and-report-approvals/Vacation-Delegate +https://community.expensify.com/discussion/5194/how-to-assign-a-vacation-delegate-for-an-employee-through-domains,https://help.expensify.com/articles/expensify-classic/manage-employees-and-report-approvals/Vacation-Delegate +https://community.expensify.com/discussion/5190/how-to-individually-assign-a-vacation-delegate-from-account-settings,https://help.expensify.com/articles/expensify-classic/manage-employees-and-report-approvals/Vacation-Delegate +https://community.expensify.com/discussion/5274/how-to-set-up-an-adp-indirect-integration-with-expensify,https://help.expensify.com/articles/expensify-classic/integrations/HR-integrations/ADP From 58e2f9c2bb1a80236581250f8c8476713c92b0b3 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Wed, 15 Nov 2023 13:36:21 +0100 Subject: [PATCH 178/245] added new line after pods --- ios/Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 5b7886c8ebcd..d94e36b0b3c9 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1299,4 +1299,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: ff769666b7221c15936ebc5576a8c8e467dc6879 -COCOAPODS: 1.12.1 \ No newline at end of file +COCOAPODS: 1.12.1 From 12a92a79ebb223a1b36ed0036fd72b16deb3a6a8 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Wed, 15 Nov 2023 14:29:35 +0100 Subject: [PATCH 179/245] explaining why we cache --- src/pages/home/report/ReportActionsList.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 31d418c37d4a..728d3b0cd6a2 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -92,6 +92,8 @@ const newActionUnsubscribeMap = {}; // Caching the reportID and reportActionID for unread markers ensures persistent tracking // across multiple reports, preserving the green line placement and allowing retrieval // of the relevant reportActionID for displaying the green line. +// Is not persisted across Reports because the are at least 3 ReportScreen components created so the +// internal states are resetted or recreated. const cacheUnreadMarkers = new Map(); /** * Create a unique key for each action in the FlatList. 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 180/245] 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 181/245] 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 182/245] 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 183/245] 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 184/245] 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 185/245] 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 186/245] 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 823f3b7b86ed7f7ae642e6b738243995b9dcd928 Mon Sep 17 00:00:00 2001 From: edug Date: Wed, 15 Nov 2023 15:52:38 +0100 Subject: [PATCH 187/245] Update src/pages/home/report/ReportActionsList.js Co-authored-by: Monil Bhavsar --- src/pages/home/report/ReportActionsList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 728d3b0cd6a2..d2c401593d40 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -92,7 +92,7 @@ const newActionUnsubscribeMap = {}; // Caching the reportID and reportActionID for unread markers ensures persistent tracking // across multiple reports, preserving the green line placement and allowing retrieval // of the relevant reportActionID for displaying the green line. -// Is not persisted across Reports because the are at least 3 ReportScreen components created so the +// We need to persist it across reports because there are at least 3 ReportScreen components created so the // internal states are resetted or recreated. const cacheUnreadMarkers = new Map(); /** From 12ce31a7c2253a28f893ccade5bd26af89154023 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 15 Nov 2023 16:02:23 +0100 Subject: [PATCH 188/245] 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 189/245] 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 190/245] 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 191/245] 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 192/245] 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 193/245] 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 194/245] 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 195/245] 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 196/245] 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 197/245] 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 198/245] 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 ce598bf02f08539edfc914aebe3f08afe518f891 Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Tue, 31 Oct 2023 15:55:06 +0000 Subject: [PATCH 199/245] chore(node): bump node version to 20.9.0 --- .../composite/setupGitForOSBotifyApp/action.yml | 2 +- .github/actions/composite/setupNode/action.yml | 2 +- .github/workflows/README.md | 4 ++-- .github/workflows/cherryPick.yml | 2 +- .github/workflows/createNewVersion.yml | 2 +- .github/workflows/deploy.yml | 2 +- .github/workflows/deployBlocker.yml | 2 +- .github/workflows/e2ePerformanceTests.yml | 6 +++--- .github/workflows/finishReleaseCycle.yml | 6 +++--- .github/workflows/lint.yml | 2 +- .github/workflows/lockDeploys.yml | 2 +- .github/workflows/platformDeploy.yml | 14 +++++++------- .github/workflows/preDeploy.yml | 2 +- .github/workflows/reassurePerformanceTests.yml | 2 +- .github/workflows/shellCheck.yml | 2 +- .github/workflows/test.yml | 6 +++--- .github/workflows/testBuild.yml | 4 ++-- .github/workflows/typecheck.yml | 2 +- .github/workflows/validateDocsRoutes.yml | 2 +- .github/workflows/validateGithubActions.yml | 2 +- .github/workflows/verifyPodfile.yml | 2 +- .github/workflows/welcome.yml | 2 +- .nvmrc | 2 +- package.json | 4 ++-- 24 files changed, 39 insertions(+), 39 deletions(-) diff --git a/.github/actions/composite/setupGitForOSBotifyApp/action.yml b/.github/actions/composite/setupGitForOSBotifyApp/action.yml index 52fb097d254e..94ea94d27505 100644 --- a/.github/actions/composite/setupGitForOSBotifyApp/action.yml +++ b/.github/actions/composite/setupGitForOSBotifyApp/action.yml @@ -33,7 +33,7 @@ runs: fi - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 if: steps.key_check.outputs.key_exists != 'true' with: sparse-checkout: | diff --git a/.github/actions/composite/setupNode/action.yml b/.github/actions/composite/setupNode/action.yml index 57c7e4d91379..868d973ccefd 100644 --- a/.github/actions/composite/setupNode/action.yml +++ b/.github/actions/composite/setupNode/action.yml @@ -4,7 +4,7 @@ description: Set up Node runs: using: composite steps: - - uses: actions/setup-node@8c91899e586c5b171469028077307d293428b516 + - uses: actions/setup-node@4 with: node-version-file: '.nvmrc' cache: npm diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 68b98ab625be..c904a459d1c0 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -42,12 +42,12 @@ Due to the large, ever-growing history of this repo, do not do any full-fetches ```yaml # Bad -- uses: actions/checkout@v3 +- uses: actions/checkout@v4 with: fetch-depth: 0 # Good -- uses: actions/checkout@v3 +- uses: actions/checkout@v4 ``` ```sh diff --git a/.github/workflows/cherryPick.yml b/.github/workflows/cherryPick.yml index 43f3c64554bc..92480a94ba53 100644 --- a/.github/workflows/cherryPick.yml +++ b/.github/workflows/cherryPick.yml @@ -35,7 +35,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout staging branch - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: staging token: ${{ secrets.OS_BOTIFY_TOKEN }} diff --git a/.github/workflows/createNewVersion.yml b/.github/workflows/createNewVersion.yml index c9c97d5355fb..812ec200bd88 100644 --- a/.github/workflows/createNewVersion.yml +++ b/.github/workflows/createNewVersion.yml @@ -68,7 +68,7 @@ jobs: GITHUB_TOKEN: ${{ github.token }} - name: Check out - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: main # The OS_BOTIFY_COMMIT_TOKEN is a personal access token tied to osbotify diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 78040f237689..4aa1a6a27d1a 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -32,7 +32,7 @@ jobs: runs-on: ubuntu-latest if: github.ref == 'refs/heads/production' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 name: Checkout with: ref: production diff --git a/.github/workflows/deployBlocker.yml b/.github/workflows/deployBlocker.yml index f42d19ca8241..6356d7f65a4d 100644 --- a/.github/workflows/deployBlocker.yml +++ b/.github/workflows/deployBlocker.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/e2ePerformanceTests.yml b/.github/workflows/e2ePerformanceTests.yml index 9d94bf900615..318198981097 100644 --- a/.github/workflows/e2ePerformanceTests.yml +++ b/.github/workflows/e2ePerformanceTests.yml @@ -22,7 +22,7 @@ jobs: outputs: VERSION: ${{ steps.getMostRecentRelease.outputs.VERSION }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Get most recent release version id: getMostRecentRelease @@ -78,7 +78,7 @@ jobs: outputs: DELTA_REF: ${{ steps.getDeltaRef.outputs.DELTA_REF }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Get pull request details id: getPullRequestDetails @@ -154,7 +154,7 @@ jobs: needs: [buildBaseline, buildDelta] name: Run E2E tests in AWS device farm steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Node uses: Expensify/App/.github/actions/composite/setupNode@main diff --git a/.github/workflows/finishReleaseCycle.yml b/.github/workflows/finishReleaseCycle.yml index f8b68786aaab..6c3f3dfd7603 100644 --- a/.github/workflows/finishReleaseCycle.yml +++ b/.github/workflows/finishReleaseCycle.yml @@ -13,7 +13,7 @@ jobs: isValid: ${{ fromJSON(steps.isDeployer.outputs.IS_DEPLOYER) && !fromJSON(steps.checkDeployBlockers.outputs.HAS_DEPLOY_BLOCKERS) }} steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: main token: ${{ secrets.OS_BOTIFY_TOKEN }} @@ -77,7 +77,7 @@ jobs: if: ${{ fromJSON(needs.validate.outputs.isValid) }} steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: staging token: ${{ secrets.OS_BOTIFY_TOKEN }} @@ -119,7 +119,7 @@ jobs: needs: [updateProduction, createNewPatchVersion] steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: main token: ${{ secrets.OS_BOTIFY_TOKEN }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 3072b3354a84..22a60992e7c7 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Node uses: Expensify/App/.github/actions/composite/setupNode@main diff --git a/.github/workflows/lockDeploys.yml b/.github/workflows/lockDeploys.yml index 6ca025bb2a25..6a2812a4f92a 100644 --- a/.github/workflows/lockDeploys.yml +++ b/.github/workflows/lockDeploys.yml @@ -10,7 +10,7 @@ jobs: runs-on: macos-12 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: main token: ${{ secrets.OS_BOTIFY_TOKEN }} diff --git a/.github/workflows/platformDeploy.yml b/.github/workflows/platformDeploy.yml index d494ea0d008b..19c5cf9c90ef 100644 --- a/.github/workflows/platformDeploy.yml +++ b/.github/workflows/platformDeploy.yml @@ -41,7 +41,7 @@ jobs: needs: validateActor steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Node uses: Expensify/App/.github/actions/composite/setupNode@main @@ -62,7 +62,7 @@ jobs: runs-on: ubuntu-latest-xl steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Configure MapBox SDK run: ./scripts/setup-mapbox-sdk.sh ${{ secrets.MAPBOX_SDK_DOWNLOAD_TOKEN }} @@ -146,7 +146,7 @@ jobs: runs-on: macos-12-xl steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Node uses: Expensify/App/.github/actions/composite/setupNode@main @@ -185,7 +185,7 @@ jobs: runs-on: macos-13-xlarge steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Configure MapBox SDK run: ./scripts/setup-mapbox-sdk.sh ${{ secrets.MAPBOX_SDK_DOWNLOAD_TOKEN }} @@ -297,7 +297,7 @@ jobs: runs-on: ubuntu-latest-xl steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Node uses: Expensify/App/.github/actions/composite/setupNode@main @@ -367,7 +367,7 @@ jobs: needs: [android, desktop, iOS, web] steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set version run: echo "VERSION=$(npm run print-version --silent)" >> "$GITHUB_ENV" @@ -428,7 +428,7 @@ jobs: needs: [android, desktop, iOS, web] steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Node uses: Expensify/App/.github/actions/composite/setupNode@main diff --git a/.github/workflows/preDeploy.yml b/.github/workflows/preDeploy.yml index bae843e74709..54fd1a830b8b 100644 --- a/.github/workflows/preDeploy.yml +++ b/.github/workflows/preDeploy.yml @@ -86,7 +86,7 @@ jobs: GITHUB_TOKEN: ${{ github.token }} - name: Checkout main - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: main token: ${{ secrets.OS_BOTIFY_TOKEN }} diff --git a/.github/workflows/reassurePerformanceTests.yml b/.github/workflows/reassurePerformanceTests.yml index b259ff9052b6..4aaa6fb2ce8c 100644 --- a/.github/workflows/reassurePerformanceTests.yml +++ b/.github/workflows/reassurePerformanceTests.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup NodeJS uses: Expensify/App/.github/actions/composite/setupNode@main diff --git a/.github/workflows/shellCheck.yml b/.github/workflows/shellCheck.yml index 609541e9a660..366caa8a0d19 100644 --- a/.github/workflows/shellCheck.yml +++ b/.github/workflows/shellCheck.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Lint shell scripts with ShellCheck run: npm run shellcheck diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fa47a2f61d4a..9c2e9486150b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,7 +20,7 @@ jobs: name: test (job ${{ fromJSON(matrix.chunk) }}) steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Node uses: Expensify/App/.github/actions/composite/setupNode@main @@ -44,7 +44,7 @@ jobs: runs-on: ubuntu-latest name: Storybook tests steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: Expensify/App/.github/actions/composite/setupNode@main @@ -57,7 +57,7 @@ jobs: name: Shell tests steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Node uses: Expensify/App/.github/actions/composite/setupNode@main diff --git a/.github/workflows/testBuild.yml b/.github/workflows/testBuild.yml index b79b687e638e..1f266c59d0d1 100644 --- a/.github/workflows/testBuild.yml +++ b/.github/workflows/testBuild.yml @@ -221,7 +221,7 @@ jobs: runs-on: macos-12-xl steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || needs.getBranchRef.outputs.REF }} @@ -264,7 +264,7 @@ jobs: runs-on: ubuntu-latest-xl steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || needs.getBranchRef.outputs.REF }} diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index 3e54975433f6..8737acf4865e 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -12,7 +12,7 @@ jobs: if: ${{ github.actor != 'OSBotify' || github.event_name == 'workflow_call' }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: Expensify/App/.github/actions/composite/setupNode@main diff --git a/.github/workflows/validateDocsRoutes.yml b/.github/workflows/validateDocsRoutes.yml index 14c08e087565..702c48fbc068 100644 --- a/.github/workflows/validateDocsRoutes.yml +++ b/.github/workflows/validateDocsRoutes.yml @@ -11,7 +11,7 @@ jobs: if: github.actor != 'OSBotify' && github.actor != 'imgbot[bot]' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: Expensify/App/.github/actions/composite/setupNode@main diff --git a/.github/workflows/validateGithubActions.yml b/.github/workflows/validateGithubActions.yml index 5cfc4670620f..c493e26bc514 100644 --- a/.github/workflows/validateGithubActions.yml +++ b/.github/workflows/validateGithubActions.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Node uses: Expensify/App/.github/actions/composite/setupNode@main diff --git a/.github/workflows/verifyPodfile.yml b/.github/workflows/verifyPodfile.yml index d98780e3e829..08f9c3a5223b 100644 --- a/.github/workflows/verifyPodfile.yml +++ b/.github/workflows/verifyPodfile.yml @@ -15,7 +15,7 @@ jobs: runs-on: macos-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Node uses: Expensify/App/.github/actions/composite/setupNode@main - name: Verify podfile diff --git a/.github/workflows/welcome.yml b/.github/workflows/welcome.yml index 43e0e1650381..1ea81129fc15 100644 --- a/.github/workflows/welcome.yml +++ b/.github/workflows/welcome.yml @@ -10,7 +10,7 @@ jobs: if: ${{ github.actor != 'OSBotify' && github.actor != 'imgbot[bot]' }} steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Get merged pull request id: getMergedPullRequest diff --git a/.nvmrc b/.nvmrc index d9289897d305..43bff1f8cf98 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -16.15.1 +20.9.0 \ No newline at end of file diff --git a/package.json b/package.json index e68f10940d6a..8112d9989755 100644 --- a/package.json +++ b/package.json @@ -302,7 +302,7 @@ ] }, "engines": { - "node": "16.15.1", - "npm": "8.11.0" + "node": "20.9.0", + "npm": "10.1.0" } } From ee0fecc79187b8563eab9649e3d40e65b51a58b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Tue, 14 Nov 2023 10:29:01 -0300 Subject: [PATCH 200/245] Fix typo --- .github/actions/composite/setupNode/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/composite/setupNode/action.yml b/.github/actions/composite/setupNode/action.yml index 868d973ccefd..7e1b5fbbae90 100644 --- a/.github/actions/composite/setupNode/action.yml +++ b/.github/actions/composite/setupNode/action.yml @@ -4,7 +4,7 @@ description: Set up Node runs: using: composite steps: - - uses: actions/setup-node@4 + - uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' cache: npm From 2541e2f072e24dbfcaced05372e58b153f627a88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Tue, 14 Nov 2023 11:10:22 -0300 Subject: [PATCH 201/245] Fix build-desktop script to use npx --- scripts/build-desktop.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/build-desktop.sh b/scripts/build-desktop.sh index c67c37a527a2..88ab17e7a2bd 100755 --- a/scripts/build-desktop.sh +++ b/scripts/build-desktop.sh @@ -14,16 +14,15 @@ else fi SCRIPTS_DIR=$(dirname "${BASH_SOURCE[0]}") -LOCAL_PACKAGES=$(npm bin) source "$SCRIPTS_DIR/shellUtils.sh"; title "Bundling Desktop js Bundle Using Webpack" info " • ELECTRON_ENV: $ELECTRON_ENV" info " • ENV file: $ENV_FILE" info "" -"$LOCAL_PACKAGES/webpack" --config config/webpack/webpack.desktop.js --env envFile=$ENV_FILE +npx webpack --config config/webpack/webpack.desktop.js --env envFile=$ENV_FILE title "Building Desktop App Archive Using Electron" info "" shift 1 -"$LOCAL_PACKAGES/electron-builder" --config config/electronBuilder.config.js "$@" +npx electron-builder --config config/electronBuilder.config.js "$@" From 2e206913cdc3111050a42c14dc48d48d90352b1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Tue, 14 Nov 2023 11:43:48 -0300 Subject: [PATCH 202/245] Change e2e spec files to use Node 20 --- tests/e2e/TestSpec.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/TestSpec.yml b/tests/e2e/TestSpec.yml index 4a5be0a5fcdd..c53010ec02fd 100644 --- a/tests/e2e/TestSpec.yml +++ b/tests/e2e/TestSpec.yml @@ -6,8 +6,8 @@ phases: # Install correct version of node - export NVM_DIR=$HOME/.nvm - . $NVM_DIR/nvm.sh - - nvm install 16.15.1 - - nvm use 16.15.1 + - nvm install 20.9.0 + - nvm use 20.9.0 # Reverse ports using AWS magic - PORT=4723 From b80a22aedfbcbb58f4774d14d61376d8a2ee0559 Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Wed, 15 Nov 2023 17:08:48 +0000 Subject: [PATCH 203/245] chore(onyx): bump version to 1.0.118 --- package-lock.json | 22 +++++++++++----------- package.json | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index b34cde433719..91c54739491e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -92,7 +92,7 @@ "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", "react-native-modal": "^13.0.0", - "react-native-onyx": "1.0.115", + "react-native-onyx": "1.0.118", "react-native-pager-view": "^6.2.0", "react-native-pdf": "^6.7.1", "react-native-performance": "^5.1.0", @@ -241,8 +241,8 @@ "yaml": "^2.2.1" }, "engines": { - "node": "16.15.1", - "npm": "8.11.0" + "node": "20.9.0", + "npm": "10.1.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -44323,17 +44323,17 @@ } }, "node_modules/react-native-onyx": { - "version": "1.0.115", - "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-1.0.115.tgz", - "integrity": "sha512-uPrJcw3Ta/EFL3Mh3iUggZ7EeEwLTSSSc5iUkKAA+a9Y8kBo8+6MWup9VCM/4wgysZbf3VHUGJCWQ8H3vWKgUg==", + "version": "1.0.118", + "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-1.0.118.tgz", + "integrity": "sha512-w54jO+Bpu1ElHsrxZXIIpcBqNkrUvuVCQmwWdfOW5LvO4UwsPSwmMxzExbUZ4ip+7CROmm10IgXFaAoyfeYSVQ==", "dependencies": { "ascii-table": "0.0.9", "fast-equals": "^4.0.3", "underscore": "^1.13.6" }, "engines": { - "node": ">=16.15.1 <=18.17.1", - "npm": ">=8.11.0 <=9.6.7" + "node": ">=16.15.1 <=20.9.0", + "npm": ">=8.11.0 <=10.1.0" }, "peerDependencies": { "idb-keyval": "^6.2.1", @@ -84618,9 +84618,9 @@ } }, "react-native-onyx": { - "version": "1.0.115", - "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-1.0.115.tgz", - "integrity": "sha512-uPrJcw3Ta/EFL3Mh3iUggZ7EeEwLTSSSc5iUkKAA+a9Y8kBo8+6MWup9VCM/4wgysZbf3VHUGJCWQ8H3vWKgUg==", + "version": "1.0.118", + "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-1.0.118.tgz", + "integrity": "sha512-w54jO+Bpu1ElHsrxZXIIpcBqNkrUvuVCQmwWdfOW5LvO4UwsPSwmMxzExbUZ4ip+7CROmm10IgXFaAoyfeYSVQ==", "requires": { "ascii-table": "0.0.9", "fast-equals": "^4.0.3", diff --git a/package.json b/package.json index 8112d9989755..357ea2e8e16f 100644 --- a/package.json +++ b/package.json @@ -139,7 +139,7 @@ "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", "react-native-modal": "^13.0.0", - "react-native-onyx": "1.0.115", + "react-native-onyx": "1.0.118", "react-native-pager-view": "^6.2.0", "react-native-pdf": "^6.7.1", "react-native-performance": "^5.1.0", From 839c07f65f3aa1f32fb47767708b3500cf1d9597 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 15 Nov 2023 18:12:09 +0100 Subject: [PATCH 204/245] 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 205/245] 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 206/245] 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 207/245] 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 627b85a92a1426f17f06a472d35d7bdb560aca8b Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 15 Nov 2023 18:29:44 +0100 Subject: [PATCH 208/245] fix: resolve comments --- src/components/FormElement.tsx | 4 ++-- src/components/SignInPageForm/index.tsx | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/FormElement.tsx b/src/components/FormElement.tsx index dcf17af4362f..c61a09b9d1ec 100644 --- a/src/components/FormElement.tsx +++ b/src/components/FormElement.tsx @@ -2,11 +2,11 @@ import React, {ForwardedRef, forwardRef} from 'react'; import {View, ViewProps} from 'react-native'; import * as ComponentUtils from '@libs/ComponentUtils'; -function FormElement(props: ViewProps, ref: ForwardedRef) { +function FormElement(props: ViewProps, ref: ForwardedRef) { return ( } + ref={ref} // eslint-disable-next-line react/jsx-props-no-spreading {...props} /> diff --git a/src/components/SignInPageForm/index.tsx b/src/components/SignInPageForm/index.tsx index 1cdc31b33fd9..20b93e6db1b5 100644 --- a/src/components/SignInPageForm/index.tsx +++ b/src/components/SignInPageForm/index.tsx @@ -1,4 +1,5 @@ import React, {useEffect, useRef} from 'react'; +import {View} from 'react-native'; import FormElement from '@components/FormElement'; import SignInPageFormProps from './types'; @@ -9,7 +10,7 @@ const preventFormDefault = (event: SubmitEvent) => { }; function SignInPageForm(props: SignInPageFormProps) { - const form = useRef(null); + const form = useRef(null); useEffect(() => { const formCurrent = form.current; From 0b0518dde95d35f786fbf918a3b6e99424e0c6fa Mon Sep 17 00:00:00 2001 From: OSBotify Date: Wed, 15 Nov 2023 20:39:12 +0000 Subject: [PATCH 209/245] 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 210/245] 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