From fab750614102206d93735f364193ae51f92ac560 Mon Sep 17 00:00:00 2001 From: Edu Date: Fri, 25 Aug 2023 10:35:56 -0300 Subject: [PATCH 01/44] 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 02/44] 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 03/44] 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 04/44] 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 05/44] 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 06/44] 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 89255151125cecd985f1b4078b23d67bb66c332b Mon Sep 17 00:00:00 2001 From: Eduardo Date: Tue, 3 Oct 2023 09:10:04 +0200 Subject: [PATCH 07/44] 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 71495589834b33b3f1a957bc40bae336304c47db Mon Sep 17 00:00:00 2001 From: Eduardo Date: Mon, 9 Oct 2023 10:50:14 +0200 Subject: [PATCH 08/44] 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 f40773ba045de3050b22ec4d2ca3fcd49828df45 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Fri, 13 Oct 2023 10:28:58 +0200 Subject: [PATCH 09/44] 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 c784057d3ede873ed8ec6f94620e78867e8ea453 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Wed, 18 Oct 2023 10:35:21 +0200 Subject: [PATCH 10/44] 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 11/44] 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 12/44] 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 13/44] 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 c9722d90b4644e4fc6af35e2324c883f9554d86e Mon Sep 17 00:00:00 2001 From: Eduardo Date: Fri, 20 Oct 2023 07:10:53 +0200 Subject: [PATCH 14/44] 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 15/44] 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 0f2e79102189682e6eb7ae44dc8060f0e5189799 Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 24 Oct 2023 16:14:43 +0700 Subject: [PATCH 16/44] 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 57978b63e4573a9b91c4c3c3a3c79b63b013c4da Mon Sep 17 00:00:00 2001 From: Eduardo Date: Mon, 30 Oct 2023 16:01:02 +0100 Subject: [PATCH 17/44] 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 6546ba50f47c8e89b9c12a655bc5c661251f99bb Mon Sep 17 00:00:00 2001 From: Eduardo Date: Mon, 30 Oct 2023 16:22:24 +0100 Subject: [PATCH 18/44] 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 9fbb88397ea253d3df1ff2e257eae59c91da7c1a Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 6 Nov 2023 11:34:42 +0700 Subject: [PATCH 19/44] 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 427be617ddd28816dbae71209cfd80e373ae7ed2 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 7 Nov 2023 16:46:56 +0700 Subject: [PATCH 20/44] 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 4df098eed0f4044dc7a21429abf8b603fa15404b Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 7 Nov 2023 19:01:24 +0700 Subject: [PATCH 21/44] 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 90708810d63b89f8ad1a0a1af7ef5e5cec96d459 Mon Sep 17 00:00:00 2001 From: rory Date: Tue, 7 Nov 2023 10:22:22 -0800 Subject: [PATCH 22/44] 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 65c5cc962efdc6d27af52b8ac654e82581336b18 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 8 Nov 2023 15:53:38 +0700 Subject: [PATCH 23/44] 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 37fafc7a37d588ab09ee0b4c32d1eea021b6e052 Mon Sep 17 00:00:00 2001 From: rory Date: Fri, 10 Nov 2023 10:24:01 -0800 Subject: [PATCH 24/44] 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 25/44] 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 26/44] 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 d8b9866ce701ac04351296fa4b93bc7aa610fe09 Mon Sep 17 00:00:00 2001 From: BhuvaneshPatil Date: Sat, 11 Nov 2023 16:39:56 +0530 Subject: [PATCH 27/44] 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 95565fec82ea06b43882c3b1e45c0691c59c0d3f Mon Sep 17 00:00:00 2001 From: rory Date: Mon, 13 Nov 2023 23:15:02 -0800 Subject: [PATCH 28/44] 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 4156a51bcea75eef859ccb06eed8a0fff0a702b1 Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 14 Nov 2023 16:27:27 +0700 Subject: [PATCH 29/44] 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 30/44] 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 cdb1b804c19eef54c0841d0ba8d426ab7a79a9d2 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Tue, 14 Nov 2023 15:37:48 +0100 Subject: [PATCH 31/44] 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 32/44] 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 33/44] Remove erronious use of stylesGenerator in ListBoundaryLoader --- .../home/report/ListBoundaryLoader/ListBoundaryLoader.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ListBoundaryLoader/ListBoundaryLoader.js b/src/pages/home/report/ListBoundaryLoader/ListBoundaryLoader.js index 861f6201a53f..226f26b352c4 100644 --- a/src/pages/home/report/ListBoundaryLoader/ListBoundaryLoader.js +++ b/src/pages/home/report/ListBoundaryLoader/ListBoundaryLoader.js @@ -3,7 +3,7 @@ import React from 'react'; import {ActivityIndicator, View} from 'react-native'; import ReportActionsSkeletonView from '@components/ReportActionsSkeletonView'; import useNetwork from '@hooks/useNetwork'; -import styles, {stylesGenerator} from '@styles/styles'; +import styles from '@styles/styles'; import themeColors from '@styles/themes/default'; import CONST from '@src/CONST'; @@ -56,7 +56,7 @@ function ListBoundaryLoader({type, isLoadingOlderReportActions, isLoadingInitial // applied for a header of the list, i.e. when you scroll to the bottom of the list // the styles for android and the rest components are different that's why we use two different components return ( - + Date: Wed, 15 Nov 2023 08:22:52 +0100 Subject: [PATCH 34/44] 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 c4541fb3ceaadb243f973726bad07e296bca0e06 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Wed, 15 Nov 2023 12:11:01 +0100 Subject: [PATCH 35/44] 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 58e2f9c2bb1a80236581250f8c8476713c92b0b3 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Wed, 15 Nov 2023 13:36:21 +0100 Subject: [PATCH 36/44] 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 37/44] 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 823f3b7b86ed7f7ae642e6b738243995b9dcd928 Mon Sep 17 00:00:00 2001 From: edug Date: Wed, 15 Nov 2023 15:52:38 +0100 Subject: [PATCH 38/44] 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 d521dd6783c382297e06a429701ae1936f808041 Mon Sep 17 00:00:00 2001 From: rory Date: Wed, 15 Nov 2023 13:12:33 -0800 Subject: [PATCH 39/44] 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