From 55845ca42f7965fe2524ab27358b3f6f8d6bfb3e Mon Sep 17 00:00:00 2001 From: Edu Date: Wed, 23 Aug 2023 07:12:33 -0300 Subject: [PATCH 001/244] checking the last message changes --- src/pages/home/report/ReportActionsList.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 7f897ee825fb..84be0cf8a8e8 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -112,8 +112,7 @@ function ReportActionsList({ const currentUnreadMarker = useRef(null); const scrollingVerticalOffset = useRef(0); const readActionSkipped = useRef(false); - const reportActionSize = useRef(sortedReportActions.length); - + const lastMessage = sortedReportActions.length > 0 ? sortedReportActions[0] : null; // 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}); @@ -143,8 +142,9 @@ function ReportActionsList({ if (!userActiveSince.current || report.reportID !== prevReportID) { return; } + const isUnreadMessage = ReportUtils.isUnread(report); - if (ReportUtils.isUnread(report)) { + if (isUnreadMessage) { if (scrollingVerticalOffset.current < MSG_VISIBLE_THRESHOLD) { Report.readNewestAction(report.reportID); } else { @@ -152,14 +152,18 @@ function ReportActionsList({ } } - if (currentUnreadMarker.current || reportActionSize.current === sortedReportActions.length) { + // Deleted message is marked as 'deleted', if the last message is deleted, we have to remove the unread marker + if (!(isUnreadMessage && lastMessage && lastMessage.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && !isOffline)) { + return; + } + + if (currentUnreadMarker.current) { return; } - reportActionSize.current = sortedReportActions.length; currentUnreadMarker.current = null; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [sortedReportActions.length, report.reportID]); + }, [lastMessage, report.reportID, isOffline]); useEffect(() => { const didManuallyMarkReportAsUnread = report.lastReadTime < DateUtils.getDBTime() && ReportUtils.isUnread(report); From 5263c234be7fd78e9022566296182b3832fbe111 Mon Sep 17 00:00:00 2001 From: Edu Date: Wed, 23 Aug 2023 16:22:43 -0300 Subject: [PATCH 002/244] moving currentMarker out --- src/pages/home/report/ReportActionsList.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 84be0cf8a8e8..04f317952304 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -74,6 +74,8 @@ const MSG_VISIBLE_THRESHOLD = 250; // the useRef value gets reset when the reportID changes, so we use a global variable to keep track let prevReportID = null; +const currentUnreadMarker = {current: 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,7 +111,6 @@ function ReportActionsList({ const {isOffline} = useNetwork(); const opacity = useSharedValue(0); const userActiveSince = useRef(null); - const currentUnreadMarker = useRef(null); const scrollingVerticalOffset = useRef(0); const readActionSkipped = useRef(false); const lastMessage = sortedReportActions.length > 0 ? sortedReportActions[0] : null; @@ -135,6 +136,10 @@ function ReportActionsList({ } else { userActiveSince.current = DateUtils.getDBTime(); } + + if (prevReportID !== report.reportID) { + currentUnreadMarker.current = null; + } prevReportID = report.reportID; }, [report.reportID]); @@ -143,7 +148,6 @@ function ReportActionsList({ return; } const isUnreadMessage = ReportUtils.isUnread(report); - if (isUnreadMessage) { if (scrollingVerticalOffset.current < MSG_VISIBLE_THRESHOLD) { Report.readNewestAction(report.reportID); @@ -157,28 +161,27 @@ function ReportActionsList({ return; } - if (currentUnreadMarker.current) { - return; - } - currentUnreadMarker.current = null; // eslint-disable-next-line react-hooks/exhaustive-deps }, [lastMessage, report.reportID, isOffline]); useEffect(() => { + if (!userActiveSince.current || report.reportID !== prevReportID) { + return; + } const didManuallyMarkReportAsUnread = report.lastReadTime < DateUtils.getDBTime() && ReportUtils.isUnread(report); + if (!didManuallyMarkReportAsUnread) { setMessageManuallyMarked({read: false}); return; } - // Clearing the current unread marker so that it can be recalculated currentUnreadMarker.current = null; setMessageManuallyMarked({read: true}); // We only care when a new lastReadTime is set in the report // eslint-disable-next-line react-hooks/exhaustive-deps - }, [report.lastReadTime]); + }, [report.lastReadTime, report.reportID]); /** * Show/hide the new floating message counter when user is scrolling back/forth in the history of messages. From a64a152892698c676084f86e5c70958b9575c1e5 Mon Sep 17 00:00:00 2001 From: Edu Date: Fri, 25 Aug 2023 09:55:04 -0300 Subject: [PATCH 003/244] updated logic to handle the shouldDisplayMarker --- src/pages/home/report/ReportActionsList.js | 37 +++++++++++----------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 04f317952304..b59403cf21fb 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -70,12 +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; - -const currentUnreadMarker = {current: 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 @@ -113,6 +107,8 @@ function ReportActionsList({ const userActiveSince = useRef(null); const scrollingVerticalOffset = useRef(0); const readActionSkipped = useRef(false); + const prevReportID = useRef(null); + const currentUnreadMarker = useRef(null); const lastMessage = sortedReportActions.length > 0 ? sortedReportActions[0] : null; // 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. @@ -131,20 +127,20 @@ 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(); } - if (prevReportID !== report.reportID) { - currentUnreadMarker.current = null; - } - prevReportID = report.reportID; + // if (prevReportID.current !== report.reportID) { + // currentUnreadMarker.current = null; + // } + prevReportID.current = report.reportID; }, [report.reportID]); useEffect(() => { - if (!userActiveSince.current || report.reportID !== prevReportID) { + if (!userActiveSince.current || report.reportID !== prevReportID.current) { return; } const isUnreadMessage = ReportUtils.isUnread(report); @@ -166,15 +162,15 @@ function ReportActionsList({ }, [lastMessage, report.reportID, isOffline]); useEffect(() => { - if (!userActiveSince.current || report.reportID !== prevReportID) { + if (!userActiveSince.current || report.reportID !== prevReportID.current) { return; } const didManuallyMarkReportAsUnread = report.lastReadTime < DateUtils.getDBTime() && ReportUtils.isUnread(report); - if (!didManuallyMarkReportAsUnread) { setMessageManuallyMarked({read: false}); return; } + // Clearing the current unread marker so that it can be recalculated currentUnreadMarker.current = null; setMessageManuallyMarked({read: true}); @@ -235,15 +231,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 = shouldDisplayNewMarker && 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 8b49c6f15873ea47b473ceb3fd64d1624edd018f Mon Sep 17 00:00:00 2001 From: Edu Date: Fri, 25 Aug 2023 10:24:37 -0300 Subject: [PATCH 004/244] updated logic --- 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 b59403cf21fb..be91ad809d73 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -234,7 +234,7 @@ function ReportActionsList({ let canDisplayNewMarker = isCurrentMessageUnread && !isMessageUnread(nextMessage, report.lastReadTime); if (!messageManuallyMarked.read) { - canDisplayNewMarker = shouldDisplayNewMarker && reportAction.actorAccountID !== Report.getCurrentUserAccountID(); + canDisplayNewMarker = canDisplayNewMarker && reportAction.actorAccountID !== Report.getCurrentUserAccountID(); } let isMessageInScope = scrollingVerticalOffset.current < MSG_VISIBLE_THRESHOLD ? reportAction.created < userActiveSince.current : true; if (messageManuallyMarked.read) { From 2fb7f33e11b278954bb32d6d44dafa84bfa9552c Mon Sep 17 00:00:00 2001 From: Eduardo Date: Wed, 13 Sep 2023 11:03:16 +0200 Subject: [PATCH 005/244] clean up --- src/pages/home/report/ReportActionsList.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index be91ad809d73..99464b9041e3 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -133,9 +133,6 @@ function ReportActionsList({ userActiveSince.current = DateUtils.getDBTime(); } - // if (prevReportID.current !== report.reportID) { - // currentUnreadMarker.current = null; - // } prevReportID.current = report.reportID; }, [report.reportID]); From f5575a773678586ea7cc49766ffe606b76f7fb4d Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Tue, 12 Dec 2023 19:41:26 +0100 Subject: [PATCH 006/244] migrate GrowlNotification and related components to TypeScript --- .../growlNotificationContainerPropTypes.js | 12 ------- .../GrowlNotificationContainer/index.js | 31 ------------------- .../index.native.js | 27 ---------------- .../index.native.tsx | 18 +++++++++++ .../GrowlNotificationContainer/index.tsx | 22 +++++++++++++ .../GrowlNotificationContainer/types.ts | 9 ++++++ .../GrowlNotification/{index.js => index.tsx} | 16 +++++----- src/components/GrowlNotification/types.ts | 9 ++++++ src/libs/Growl.ts | 7 ++--- 9 files changed, 69 insertions(+), 82 deletions(-) delete mode 100644 src/components/GrowlNotification/GrowlNotificationContainer/growlNotificationContainerPropTypes.js delete mode 100644 src/components/GrowlNotification/GrowlNotificationContainer/index.js delete mode 100644 src/components/GrowlNotification/GrowlNotificationContainer/index.native.js create mode 100644 src/components/GrowlNotification/GrowlNotificationContainer/index.native.tsx create mode 100644 src/components/GrowlNotification/GrowlNotificationContainer/index.tsx create mode 100644 src/components/GrowlNotification/GrowlNotificationContainer/types.ts rename src/components/GrowlNotification/{index.js => index.tsx} (88%) create mode 100644 src/components/GrowlNotification/types.ts diff --git a/src/components/GrowlNotification/GrowlNotificationContainer/growlNotificationContainerPropTypes.js b/src/components/GrowlNotification/GrowlNotificationContainer/growlNotificationContainerPropTypes.js deleted file mode 100644 index 2432d1b1748c..000000000000 --- a/src/components/GrowlNotification/GrowlNotificationContainer/growlNotificationContainerPropTypes.js +++ /dev/null @@ -1,12 +0,0 @@ -import PropTypes from 'prop-types'; -import {Animated} from 'react-native'; - -const propTypes = { - /** GrowlNotification content */ - children: PropTypes.node.isRequired, - - /** GrowlNotification Y postion, required to show or hide with fling animation */ - translateY: PropTypes.instanceOf(Animated.Value).isRequired, -}; - -export default propTypes; diff --git a/src/components/GrowlNotification/GrowlNotificationContainer/index.js b/src/components/GrowlNotification/GrowlNotificationContainer/index.js deleted file mode 100644 index 82672edb14c2..000000000000 --- a/src/components/GrowlNotification/GrowlNotificationContainer/index.js +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react'; -import {Animated} from 'react-native'; -import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; -import useThemeStyles from '@styles/useThemeStyles'; -import growlNotificationContainerPropTypes from './growlNotificationContainerPropTypes'; - -const propTypes = { - ...growlNotificationContainerPropTypes, - ...windowDimensionsPropTypes, -}; - -function GrowlNotificationContainer(props) { - const styles = useThemeStyles(); - return ( - - {props.children} - - ); -} - -GrowlNotificationContainer.propTypes = propTypes; -GrowlNotificationContainer.displayName = 'GrowlNotificationContainer'; - -export default withWindowDimensions(GrowlNotificationContainer); diff --git a/src/components/GrowlNotification/GrowlNotificationContainer/index.native.js b/src/components/GrowlNotification/GrowlNotificationContainer/index.native.js deleted file mode 100644 index 457a9dce66d9..000000000000 --- a/src/components/GrowlNotification/GrowlNotificationContainer/index.native.js +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react'; -import {Animated} from 'react-native'; -import useSafeAreaInsets from '@hooks/useSafeAreaInsets'; -import useStyleUtils from '@styles/useStyleUtils'; -import useThemeStyles from '@styles/useThemeStyles'; -import growlNotificationContainerPropTypes from './growlNotificationContainerPropTypes'; - -const propTypes = { - ...growlNotificationContainerPropTypes, -}; - -function GrowlNotificationContainer(props) { - const styles = useThemeStyles(); - const StyleUtils = useStyleUtils(); - const insets = useSafeAreaInsets; - - return ( - - {props.children} - - ); -} - -GrowlNotificationContainer.propTypes = propTypes; -GrowlNotificationContainer.displayName = 'GrowlNotificationContainer'; - -export default GrowlNotificationContainer; diff --git a/src/components/GrowlNotification/GrowlNotificationContainer/index.native.tsx b/src/components/GrowlNotification/GrowlNotificationContainer/index.native.tsx new file mode 100644 index 000000000000..0fd7167ba492 --- /dev/null +++ b/src/components/GrowlNotification/GrowlNotificationContainer/index.native.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import {Animated} from 'react-native'; +import useSafeAreaInsets from '@hooks/useSafeAreaInsets'; +import useStyleUtils from '@styles/useStyleUtils'; +import useThemeStyles from '@styles/useThemeStyles'; +import GrowlNotificationContainerProps from './types'; + +function GrowlNotificationContainer({children, translateY}: GrowlNotificationContainerProps) { + const styles = useThemeStyles(); + const StyleUtils = useStyleUtils(); + const insets = useSafeAreaInsets(); + + return {children}; +} + +GrowlNotificationContainer.displayName = 'GrowlNotificationContainer'; + +export default GrowlNotificationContainer; diff --git a/src/components/GrowlNotification/GrowlNotificationContainer/index.tsx b/src/components/GrowlNotification/GrowlNotificationContainer/index.tsx new file mode 100644 index 000000000000..9f65e269ee96 --- /dev/null +++ b/src/components/GrowlNotification/GrowlNotificationContainer/index.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import {Animated} from 'react-native'; +import withWindowDimensions from '@components/withWindowDimensions'; +import {WindowDimensionsProps} from '@components/withWindowDimensions/types'; +import useThemeStyles from '@styles/useThemeStyles'; +import GrowlNotificationContainerProps from './types'; + +function GrowlNotificationContainer({children, translateY, isSmallScreenWidth}: GrowlNotificationContainerProps & WindowDimensionsProps) { + const styles = useThemeStyles(); + + return ( + + {children} + + ); +} + +GrowlNotificationContainer.displayName = 'GrowlNotificationContainer'; + +export default withWindowDimensions(GrowlNotificationContainer); diff --git a/src/components/GrowlNotification/GrowlNotificationContainer/types.ts b/src/components/GrowlNotification/GrowlNotificationContainer/types.ts new file mode 100644 index 000000000000..9a8ea4c76958 --- /dev/null +++ b/src/components/GrowlNotification/GrowlNotificationContainer/types.ts @@ -0,0 +1,9 @@ +import {Animated} from 'react-native'; + +type GrowlNotificationContainerProps = { + children: React.ReactNode; + + translateY: Animated.Value; +}; + +export default GrowlNotificationContainerProps; diff --git a/src/components/GrowlNotification/index.js b/src/components/GrowlNotification/index.tsx similarity index 88% rename from src/components/GrowlNotification/index.js rename to src/components/GrowlNotification/index.tsx index faf1ec9cfa16..84a56594943e 100644 --- a/src/components/GrowlNotification/index.js +++ b/src/components/GrowlNotification/index.tsx @@ -1,6 +1,7 @@ import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; import {Animated, View} from 'react-native'; import {Directions, FlingGestureHandler, State} from 'react-native-gesture-handler'; +import {SvgProps} from 'react-native-svg'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import * as Pressables from '@components/Pressable'; @@ -11,20 +12,21 @@ import useTheme from '@styles/themes/useTheme'; import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import GrowlNotificationContainer from './GrowlNotificationContainer'; +import {GrowlNotificationProps} from './types'; const INACTIVE_POSITION_Y = -255; const PressableWithoutFeedback = Pressables.PressableWithoutFeedback; -function GrowlNotification(_, ref) { +function GrowlNotification({ref}: GrowlNotificationProps) { const translateY = useRef(new Animated.Value(INACTIVE_POSITION_Y)).current; const [bodyText, setBodyText] = useState(''); const [type, setType] = useState('success'); - const [duration, setDuration] = useState(); + const [duration, setDuration] = useState(); const theme = useTheme(); const styles = useThemeStyles(); - const types = { + const types: Record; iconColor: string}> = { [CONST.GROWL.SUCCESS]: { icon: Expensicons.Checkmark, iconColor: theme.success, @@ -46,7 +48,7 @@ function GrowlNotification(_, ref) { * @param {String} type * @param {Number} duration */ - const show = useCallback((text, growlType, growlDuration) => { + const show = useCallback((text: string, growlType: string, growlDuration: number) => { setBodyText(text); setType(growlType); setDuration(growlDuration); @@ -58,13 +60,11 @@ function GrowlNotification(_, ref) { * @param {Number} val */ const fling = useCallback( - (val = INACTIVE_POSITION_Y) => { + (val = INACTIVE_POSITION_Y) => Animated.spring(translateY, { toValue: val, - duration: 80, useNativeDriver, - }).start(); - }, + }).start(), [translateY], ); diff --git a/src/components/GrowlNotification/types.ts b/src/components/GrowlNotification/types.ts new file mode 100644 index 000000000000..edee6ce6b37a --- /dev/null +++ b/src/components/GrowlNotification/types.ts @@ -0,0 +1,9 @@ +type GrowlRef = { + show?: (bodyText: string, type: string, duration: number) => void; +}; + +type GrowlNotificationProps = { + ref: React.RefObject; +}; + +export type {GrowlRef, GrowlNotificationProps}; diff --git a/src/libs/Growl.ts b/src/libs/Growl.ts index 55bcf88206e9..b55ae5512c69 100644 --- a/src/libs/Growl.ts +++ b/src/libs/Growl.ts @@ -1,10 +1,7 @@ import React from 'react'; +import {GrowlRef} from '@components/GrowlNotification/types'; import CONST from '@src/CONST'; -type GrowlRef = { - show?: (bodyText: string, type: string, duration: number) => void; -}; - const growlRef = React.createRef(); let resolveIsReadyPromise: undefined | ((value?: unknown) => void); const isReadyPromise = new Promise((resolve) => { @@ -50,4 +47,6 @@ export default { success, }; +export type {GrowlRef}; + export {growlRef, setIsReady}; From 236b050889c9a3535bd65ff603d8071c05f88687 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Tue, 12 Dec 2023 19:44:40 +0100 Subject: [PATCH 007/244] remove omitted type export --- src/libs/Growl.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libs/Growl.ts b/src/libs/Growl.ts index b55ae5512c69..59cadc7073f3 100644 --- a/src/libs/Growl.ts +++ b/src/libs/Growl.ts @@ -47,6 +47,4 @@ export default { success, }; -export type {GrowlRef}; - export {growlRef, setIsReady}; From f6828ab5c83a59082c9fc658540265c8dba54244 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Thu, 14 Dec 2023 00:58:41 +0100 Subject: [PATCH 008/244] add review nitpicks and changes --- .../GrowlNotificationContainer/index.tsx | 8 ++++---- src/components/GrowlNotification/index.tsx | 3 ++- src/components/GrowlNotification/types.ts | 7 ++++++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/components/GrowlNotification/GrowlNotificationContainer/index.tsx b/src/components/GrowlNotification/GrowlNotificationContainer/index.tsx index 9f65e269ee96..07530c117f60 100644 --- a/src/components/GrowlNotification/GrowlNotificationContainer/index.tsx +++ b/src/components/GrowlNotification/GrowlNotificationContainer/index.tsx @@ -1,12 +1,12 @@ import React from 'react'; import {Animated} from 'react-native'; -import withWindowDimensions from '@components/withWindowDimensions'; -import {WindowDimensionsProps} from '@components/withWindowDimensions/types'; +import useWindowDimensions from '@hooks/useWindowDimensions'; import useThemeStyles from '@styles/useThemeStyles'; import GrowlNotificationContainerProps from './types'; -function GrowlNotificationContainer({children, translateY, isSmallScreenWidth}: GrowlNotificationContainerProps & WindowDimensionsProps) { +function GrowlNotificationContainer({children, translateY}: GrowlNotificationContainerProps) { const styles = useThemeStyles(); + const {isSmallScreenWidth} = useWindowDimensions(); return ( void; }; type GrowlNotificationProps = { - ref: React.RefObject; + // eslint-disable-next-line @typescript-eslint/naming-convention + _: unknown; + + ref: ForwardedRef; }; export type {GrowlRef, GrowlNotificationProps}; From b11c5c838c7db58e91f9038e9b02c7385bf056d8 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Thu, 14 Dec 2023 13:03:27 +0100 Subject: [PATCH 009/244] change up ts errors --- src/components/GrowlNotification/index.tsx | 8 ++++---- src/components/GrowlNotification/types.ts | 14 -------------- src/libs/Growl.ts | 7 ++++++- 3 files changed, 10 insertions(+), 19 deletions(-) delete mode 100644 src/components/GrowlNotification/types.ts diff --git a/src/components/GrowlNotification/index.tsx b/src/components/GrowlNotification/index.tsx index 780a97924354..f52a6083f4e6 100644 --- a/src/components/GrowlNotification/index.tsx +++ b/src/components/GrowlNotification/index.tsx @@ -1,4 +1,4 @@ -import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; +import React, {ForwardedRef, forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; import {Animated, View} from 'react-native'; import {Directions, FlingGestureHandler, State} from 'react-native-gesture-handler'; import {SvgProps} from 'react-native-svg'; @@ -9,17 +9,17 @@ import Text from '@components/Text'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import * as Growl from '@libs/Growl'; +import type {GrowlRef} from '@libs/Growl'; import useNativeDriver from '@libs/useNativeDriver'; import CONST from '@src/CONST'; import GrowlNotificationContainer from './GrowlNotificationContainer'; -import {GrowlNotificationProps} from './types'; const INACTIVE_POSITION_Y = -255; const PressableWithoutFeedback = Pressables.PressableWithoutFeedback; -// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-unused-vars -function GrowlNotification({_, ref}: GrowlNotificationProps) { +// eslint-disable-next-line @typescript-eslint/naming-convention +function GrowlNotification(_: unknown, ref: ForwardedRef) { const translateY = useRef(new Animated.Value(INACTIVE_POSITION_Y)).current; const [bodyText, setBodyText] = useState(''); const [type, setType] = useState('success'); diff --git a/src/components/GrowlNotification/types.ts b/src/components/GrowlNotification/types.ts deleted file mode 100644 index a984008978ed..000000000000 --- a/src/components/GrowlNotification/types.ts +++ /dev/null @@ -1,14 +0,0 @@ -import {ForwardedRef} from 'react'; - -type GrowlRef = { - show?: (bodyText: string, type: string, duration: number) => void; -}; - -type GrowlNotificationProps = { - // eslint-disable-next-line @typescript-eslint/naming-convention - _: unknown; - - ref: ForwardedRef; -}; - -export type {GrowlRef, GrowlNotificationProps}; diff --git a/src/libs/Growl.ts b/src/libs/Growl.ts index 59cadc7073f3..3812a155ba1f 100644 --- a/src/libs/Growl.ts +++ b/src/libs/Growl.ts @@ -1,7 +1,10 @@ import React from 'react'; -import {GrowlRef} from '@components/GrowlNotification/types'; import CONST from '@src/CONST'; +type GrowlRef = { + show?: (bodyText: string, type: string, duration: number) => void; +}; + const growlRef = React.createRef(); let resolveIsReadyPromise: undefined | ((value?: unknown) => void); const isReadyPromise = new Promise((resolve) => { @@ -47,4 +50,6 @@ export default { success, }; +export type {GrowlRef}; + export {growlRef, setIsReady}; From ac6b737f32def157b2efdaff3ca6fac045c24f3a Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Thu, 14 Dec 2023 13:12:20 +0100 Subject: [PATCH 010/244] add ChildrenProps to types --- .../GrowlNotification/GrowlNotificationContainer/types.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/GrowlNotification/GrowlNotificationContainer/types.ts b/src/components/GrowlNotification/GrowlNotificationContainer/types.ts index 9a8ea4c76958..69e5d11745c6 100644 --- a/src/components/GrowlNotification/GrowlNotificationContainer/types.ts +++ b/src/components/GrowlNotification/GrowlNotificationContainer/types.ts @@ -1,8 +1,7 @@ import {Animated} from 'react-native'; +import ChildrenProps from '@src/types/utils/ChildrenProps'; -type GrowlNotificationContainerProps = { - children: React.ReactNode; - +type GrowlNotificationContainerProps = ChildrenProps & { translateY: Animated.Value; }; From 5d024d763bde660513aaad091ccce1d39fe06662 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Tue, 19 Dec 2023 22:28:51 +0100 Subject: [PATCH 011/244] separate icon types --- src/components/GrowlNotification/index.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/components/GrowlNotification/index.tsx b/src/components/GrowlNotification/index.tsx index f52a6083f4e6..0ab515bf3032 100644 --- a/src/components/GrowlNotification/index.tsx +++ b/src/components/GrowlNotification/index.tsx @@ -27,7 +27,18 @@ function GrowlNotification(_: unknown, ref: ForwardedRef) { const theme = useTheme(); const styles = useThemeStyles(); - const types: Record; iconColor: string}> = { + type GrowlIconTypes = Record< + string, + { + /** Expensicon for the page */ + icon: React.FC; + + /** Color for the icon (should be from theme) */ + iconColor: string; + } + >; + + const types: GrowlIconTypes = { [CONST.GROWL.SUCCESS]: { icon: Expensicons.Checkmark, iconColor: theme.success, From d5c50b659fb3201c8ddf78aac1df7ab76590b549 Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Tue, 23 Jan 2024 11:30:01 -0800 Subject: [PATCH 012/244] allow accessing the members page in a thread --- src/pages/ReportDetailsPage.js | 3 ++- src/pages/RoomMembersPage.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index ff9ed62c6a65..7cb5b12f250e 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -106,6 +106,7 @@ function ReportDetailsPage(props) { } // The Members page is only shown when: + // - The report is a thread in a chat report // - The report is not a user created room with participants to show i.e. DM, Group Chat, etc // - The report is a user created room and the room and the current user is a workspace member i.e. non-workspace members should not see this option. if ((!isUserCreatedPolicyRoom && participants.length) || (isUserCreatedPolicyRoom && isPolicyMember)) { @@ -116,7 +117,7 @@ function ReportDetailsPage(props) { subtitle: participants.length, isAnonymousAction: false, action: () => { - if (isUserCreatedPolicyRoom && !props.report.parentReportID) { + if (props.report.parentReportID || isUserCreatedPolicyRoom) { Navigation.navigate(ROUTES.ROOM_MEMBERS.getRoute(props.report.reportID)); } else { Navigation.navigate(ROUTES.REPORT_PARTICIPANTS.getRoute(props.report.reportID)); diff --git a/src/pages/RoomMembersPage.js b/src/pages/RoomMembersPage.js index 30ffd60aa4ac..486945b24e03 100644 --- a/src/pages/RoomMembersPage.js +++ b/src/pages/RoomMembersPage.js @@ -238,7 +238,7 @@ function RoomMembersPage(props) { testID={RoomMembersPage.displayName} > Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(props.report.reportID))} > From 545e3687617d4cdad7ceb3d0543564611b448302 Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Tue, 23 Jan 2024 13:25:51 -0800 Subject: [PATCH 013/244] restrict to chat reports --- src/pages/ReportDetailsPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index 7cb5b12f250e..65456b17616b 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -117,7 +117,7 @@ function ReportDetailsPage(props) { subtitle: participants.length, isAnonymousAction: false, action: () => { - if (props.report.parentReportID || isUserCreatedPolicyRoom) { + if ((props.report.type === CONST.REPORT.TYPE.CHAT && props.report.parentReportID) || isUserCreatedPolicyRoom) { Navigation.navigate(ROUTES.ROOM_MEMBERS.getRoute(props.report.reportID)); } else { Navigation.navigate(ROUTES.REPORT_PARTICIPANTS.getRoute(props.report.reportID)); From c644fd04aa8ac28707e64003ed3049a9e9f957e9 Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Tue, 23 Jan 2024 13:31:40 -0800 Subject: [PATCH 014/244] update condition, add comment --- src/pages/RoomMembersPage.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pages/RoomMembersPage.js b/src/pages/RoomMembersPage.js index 486945b24e03..8d273c7d2fee 100644 --- a/src/pages/RoomMembersPage.js +++ b/src/pages/RoomMembersPage.js @@ -238,7 +238,12 @@ function RoomMembersPage(props) { testID={RoomMembersPage.displayName} > Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(props.report.reportID))} > From a7bfccbb7fe6c9bd5eca48fe88fddfd5d5eb80a7 Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Tue, 23 Jan 2024 14:17:11 -0800 Subject: [PATCH 015/244] style --- src/pages/RoomMembersPage.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pages/RoomMembersPage.js b/src/pages/RoomMembersPage.js index 8d273c7d2fee..df2634a1199e 100644 --- a/src/pages/RoomMembersPage.js +++ b/src/pages/RoomMembersPage.js @@ -243,7 +243,11 @@ function RoomMembersPage(props) { // - this report is a user-created policy room and the user is not a member of the policy // - this report is a default room (threads in default rooms are fine) // - this report is a policy expense chat (threads in policy expense chats are fine) - shouldShow={_.isEmpty(props.report) || (_.isEmpty(ReportUtils.getParentReport(props.report)) && ReportUtils.isUserCreatedPolicyRoom(props.report) && !isPolicyMember) || (_.isEmpty(ReportUtils.getParentReport(props.report)) && (ReportUtils.isDefaultRoom(props.report) || ReportUtils.isPolicyExpenseChat(props.report)))} + shouldShow={ + _.isEmpty(props.report) || + (_.isEmpty(ReportUtils.getParentReport(props.report)) && ReportUtils.isUserCreatedPolicyRoom(props.report) && !isPolicyMember) || + (_.isEmpty(ReportUtils.getParentReport(props.report)) && (ReportUtils.isDefaultRoom(props.report) || ReportUtils.isPolicyExpenseChat(props.report))) + } subtitleKey={_.isEmpty(props.report) ? undefined : 'roomMembersPage.notAuthorized'} onBackButtonPress={() => Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(props.report.reportID))} > From 1057dd6c48edbc4083d467fa9c0bb44e592bd129 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 31 Jan 2024 22:56:21 +0700 Subject: [PATCH 016/244] fix: Approved expense preview does not show GBR when submitter needs to add a bank account --- .../ReportActionItem/ReportPreview.tsx | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index b2fece085f57..beef49c0c015 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -25,12 +25,13 @@ import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import type {ContextMenuAnchor} from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import * as IOU from '@userActions/IOU'; +import * as store from '@userActions/ReimbursementAccount/store'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Policy, Report, ReportAction, Session, Transaction, TransactionViolations} from '@src/types/onyx'; -import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; +import type {IOUMessage, PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import ReportActionItemImages from './ReportActionItemImages'; type ReportPreviewOnyxProps = { @@ -228,6 +229,16 @@ function ReportPreview({ return isCurrentUserManager && !isDraftExpenseReport && !isApproved && !iouSettled; }, [isPaidGroupPolicy, isCurrentUserManager, isDraftExpenseReport, isApproved, iouSettled]); const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; + const paymentType = (action.originalMessage as IOUMessage).paymentType; + const isSubmitterOfUnsettledReport = ReportUtils.isCurrentUserSubmitter(iouReportID) && !ReportUtils.isSettled(iouReportID); + + const shouldPromptUserToAddBankAccount = + action.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTQUEUED && + isSubmitterOfUnsettledReport && + !store.hasCreditBankAccount() && + paymentType !== CONST.IOU.PAYMENT_TYPE.EXPENSIFY; + const shouldShowRBR = !iouSettled && hasErrors; + return ( @@ -256,12 +267,18 @@ function ReportPreview({ {getPreviewMessage()} - {!iouSettled && hasErrors && ( + {shouldShowRBR && ( )} + {!shouldShowRBR && shouldPromptUserToAddBankAccount && ( + + )} From 9ffd082c9f8027534012db6d2ef28d635c1801bc Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 1 Feb 2024 16:02:50 +0700 Subject: [PATCH 017/244] create util function --- .../ReportActionItem/ReportPreview.tsx | 17 +++++------------ src/libs/ReportUtils.ts | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index beef49c0c015..ae7edc3c6d71 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -1,8 +1,8 @@ import React, {useMemo} from 'react'; -import {View} from 'react-native'; import type {StyleProp, ViewStyle} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; +import {View} from 'react-native'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -25,13 +25,12 @@ import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import type {ContextMenuAnchor} from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import * as IOU from '@userActions/IOU'; -import * as store from '@userActions/ReimbursementAccount/store'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Policy, Report, ReportAction, Session, Transaction, TransactionViolations} from '@src/types/onyx'; -import type {IOUMessage, PaymentMethodType} from '@src/types/onyx/OriginalMessage'; +import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import ReportActionItemImages from './ReportActionItemImages'; type ReportPreviewOnyxProps = { @@ -229,14 +228,8 @@ function ReportPreview({ return isCurrentUserManager && !isDraftExpenseReport && !isApproved && !iouSettled; }, [isPaidGroupPolicy, isCurrentUserManager, isDraftExpenseReport, isApproved, iouSettled]); const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; - const paymentType = (action.originalMessage as IOUMessage).paymentType; - const isSubmitterOfUnsettledReport = ReportUtils.isCurrentUserSubmitter(iouReportID) && !ReportUtils.isSettled(iouReportID); - - const shouldPromptUserToAddBankAccount = - action.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTQUEUED && - isSubmitterOfUnsettledReport && - !store.hasCreditBankAccount() && - paymentType !== CONST.IOU.PAYMENT_TYPE.EXPENSIFY; + + const shouldPromptUserToAddBankAccount = ReportUtils.hasAddBankAccountAction(iouReportID); const shouldShowRBR = !iouSettled && hasErrors; return ( diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 228db29aea6c..29219e7df037 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -25,6 +25,7 @@ import type {Receipt, WaypointCollection} from '@src/types/onyx/Transaction'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; +import * as store from './actions/ReimbursementAccount/store'; import * as CollectionUtils from './CollectionUtils'; import * as CurrencyUtils from './CurrencyUtils'; import DateUtils from './DateUtils'; @@ -4613,6 +4614,22 @@ function canBeAutoReimbursed(report: OnyxEntry, policy: OnyxEntry + action.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTQUEUED && + isSubmitterOfUnsettledReport && + !hasCreditBankAccount && + (action.originalMessage as IOUMessage)?.paymentType !== CONST.IOU.PAYMENT_TYPE.EXPENSIFY, + ); +} + export { getReportParticipantsTitle, isReportMessageAttachment, @@ -4798,6 +4815,7 @@ export { isReportParticipant, isValidReport, isReportFieldOfTypeTitle, + hasAddBankAccountAction, }; export type { From 3b9eb00bbd396ea6e7b5dbd5e03adb96dfe8289f Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 5 Feb 2024 10:48:19 +0700 Subject: [PATCH 018/244] refactor code --- src/libs/ReportUtils.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 1760635a8352..1a5883909009 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4769,11 +4769,14 @@ function hasAddBankAccountAction(iouReportID: string): boolean { const reportActions = ReportActionsUtils.getAllReportActions(iouReportID); const isSubmitterOfUnsettledReport = isCurrentUserSubmitter(iouReportID) && !isSettled(iouReportID); const hasCreditBankAccount = store.hasCreditBankAccount(); + + if(!isSubmitterOfUnsettledReport || hasCreditBankAccount){ + return false; + } + return !!Object.values(reportActions).find( (action) => action.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTQUEUED && - isSubmitterOfUnsettledReport && - !hasCreditBankAccount && (action.originalMessage as IOUMessage)?.paymentType !== CONST.IOU.PAYMENT_TYPE.EXPENSIFY, ); } From 652298dd7c4c8712934d600ff80ea4e3b571506b Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 5 Feb 2024 10:53:06 +0700 Subject: [PATCH 019/244] lint fix --- src/libs/ReportUtils.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 1a5883909009..92e24694a56b 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4770,14 +4770,12 @@ function hasAddBankAccountAction(iouReportID: string): boolean { const isSubmitterOfUnsettledReport = isCurrentUserSubmitter(iouReportID) && !isSettled(iouReportID); const hasCreditBankAccount = store.hasCreditBankAccount(); - if(!isSubmitterOfUnsettledReport || hasCreditBankAccount){ + if (!isSubmitterOfUnsettledReport || hasCreditBankAccount) { return false; } - + return !!Object.values(reportActions).find( - (action) => - action.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTQUEUED && - (action.originalMessage as IOUMessage)?.paymentType !== CONST.IOU.PAYMENT_TYPE.EXPENSIFY, + (action) => action.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTQUEUED && (action.originalMessage as IOUMessage)?.paymentType !== CONST.IOU.PAYMENT_TYPE.EXPENSIFY, ); } From 67089268d0000f500bd4f70166c320addc50aca2 Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Mon, 12 Feb 2024 19:56:36 -0800 Subject: [PATCH 020/244] simplify condition --- src/pages/ReportDetailsPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 3c2ad805ea23..1a263ad537a4 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -119,7 +119,7 @@ function ReportDetailsPage({policies, report, session, personalDetails}: ReportD subtitle: participants.length, isAnonymousAction: false, action: () => { - if ((report?.type === CONST.REPORT.TYPE.CHAT && report?.parentReportID) ?? isUserCreatedPolicyRoom) { + if (isUserCreatedPolicyRoom || ReportUtils.isChatThread(report)) { Navigation.navigate(ROUTES.ROOM_MEMBERS.getRoute(report?.reportID ?? '')); } else { Navigation.navigate(ROUTES.REPORT_PARTICIPANTS.getRoute(report?.reportID ?? '')); From 58bb076836e979123feb3471f7b88ce087cc3db4 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 14 Feb 2024 21:35:48 +0700 Subject: [PATCH 021/244] lint fix --- src/components/ReportActionItem/ReportPreview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 1726e0f5879d..043c73dab90d 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -233,7 +233,7 @@ function ReportPreview({ const shouldPromptUserToAddBankAccount = ReportUtils.hasAddBankAccountAction(iouReportID); const shouldShowRBR = !iouSettled && hasErrors; - + /* Show subtitle if at least one of the money requests is not being smart scanned, and either: - There is more than one money request – in this case, the "X requests, Y scanning" subtitle is shown; From f8080a2a165945814a07599e50989574c0d937e0 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Fri, 16 Feb 2024 21:57:11 +0530 Subject: [PATCH 022/244] add bottom margin to info box --- docs/_sass/_main.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/_sass/_main.scss b/docs/_sass/_main.scss index ea18acef7c23..ec0f76801bc7 100644 --- a/docs/_sass/_main.scss +++ b/docs/_sass/_main.scss @@ -508,6 +508,7 @@ button { .info { padding: 12px; + margin-bottom: 20px; border-radius: 8px; background-color: $color-highlightBG; color: $color-text; From ee483b5dcb3c9bf592a9f0abb7157a2c6319cc22 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 19 Feb 2024 18:24:37 +0700 Subject: [PATCH 023/244] fix: refactor --- .../ReportActionItem/ReportPreview.tsx | 11 ++++-- src/libs/ReportUtils.ts | 35 ++++++++++++------- src/pages/home/report/ReportActionItem.js | 13 ++----- 3 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index dd7018c50b9b..6e1dfccd52ab 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -30,7 +30,7 @@ import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {Policy, Report, ReportAction, Session, Transaction, TransactionViolations} from '@src/types/onyx'; +import type {Policy, Report, ReportAction, Session, Transaction, TransactionViolations, UserWallet} from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import ReportActionItemImages from './ReportActionItemImages'; @@ -52,6 +52,9 @@ type ReportPreviewOnyxProps = { /** All of the transaction violations */ transactionViolations: OnyxCollection; + + /** The user's wallet account */ + userWallet: OnyxEntry; }; type ReportPreviewProps = ReportPreviewOnyxProps & { @@ -99,6 +102,7 @@ function ReportPreview({ isHovered = false, isWhisper = false, checkIfContextMenuActive = () => {}, + userWallet, }: ReportPreviewProps) { const theme = useTheme(); const styles = useThemeStyles(); @@ -231,7 +235,7 @@ function ReportPreview({ }, [isPaidGroupPolicy, isCurrentUserManager, isDraftExpenseReport, isApproved, isOnInstantSubmitPolicy, isOnSubmitAndClosePolicy, iouSettled]); const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; - const shouldPromptUserToAddBankAccount = ReportUtils.hasAddBankAccountAction(iouReportID); + const shouldPromptUserToAddBankAccount = ReportUtils.hasMissingPaymentMethod(userWallet, iouReportID); const shouldShowRBR = !iouSettled && hasErrors; /* @@ -371,4 +375,7 @@ export default withOnyx({ transactionViolations: { key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, }, + userWallet: { + key: ONYXKEYS.USER_WALLET, + }, })(ReportPreview); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 91dbee035516..ae7edf773938 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -28,6 +28,7 @@ import type { Session, Transaction, TransactionViolation, + UserWallet, } from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; import type {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; @@ -421,6 +422,8 @@ type AncestorIDs = { reportActionsIDs: string[]; }; +type MissingPaymentMethod = 'bankAccount' | 'wallet'; + let currentUserEmail: string | undefined; let currentUserAccountID: number | undefined; let isAnonymousUser = false; @@ -5009,20 +5012,27 @@ function canBeAutoReimbursed(report: OnyxEntry, policy: OnyxEntry, reportId: string, reportAction: ReportAction): MissingPaymentMethod | undefined { + const isSubmitterOfUnsettledReport = isCurrentUserSubmitter(reportId) && !isSettled(reportId); + if (!isSubmitterOfUnsettledReport || reportAction.actionName !== CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTQUEUED) { + return undefined; + } + const paymentType = reportAction.originalMessage?.paymentType; + if (paymentType === CONST.IOU.PAYMENT_TYPE.EXPENSIFY) { + return isEmpty(userWallet) || userWallet.tierName === CONST.WALLET.TIER_NAME.SILVER ? 'wallet' : undefined; } - return !!Object.values(reportActions).find( - (action) => action.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTQUEUED && action.originalMessage?.paymentType !== CONST.IOU.PAYMENT_TYPE.EXPENSIFY, - ); + return !store.hasCreditBankAccount() ? 'bankAccount' : undefined; +} + +/** + * Checks if report chat contains add bank account action + */ +function hasMissingPaymentMethod(userWallet: OnyxEntry, iouReportID: string): boolean { + const reportActions = ReportActionsUtils.getAllReportActions(iouReportID); + return Object.values(reportActions).some((action) => getIndicatedMissingPaymentMethod(userWallet, iouReportID, action) !== undefined); } export { @@ -5216,7 +5226,7 @@ export { isValidReport, getReportDescriptionText, isReportFieldOfTypeTitle, - hasAddBankAccountAction, + hasMissingPaymentMethod, isIOUReportUsingReport, hasUpdatedTotal, isReportFieldDisabled, @@ -5225,6 +5235,7 @@ export { getAllAncestorReportActionIDs, canEditPolicyDescription, getPolicyDescriptionText, + getIndicatedMissingPaymentMethod, }; export type { diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 39a5fcaa4ee0..d8232150b6a1 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -54,7 +54,6 @@ import {ReactionListContext} from '@pages/home/ReportScreenContext'; import reportPropTypes from '@pages/reportPropTypes'; import * as BankAccounts from '@userActions/BankAccounts'; import * as EmojiPickerAction from '@userActions/EmojiPickerAction'; -import * as store from '@userActions/ReimbursementAccount/store'; import * as Report from '@userActions/Report'; import * as ReportActions from '@userActions/ReportActions'; import * as Session from '@userActions/Session'; @@ -416,19 +415,13 @@ function ReportActionItem(props) { const submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(lodashGet(personalDetails, props.report.ownerAccountID)); const paymentType = lodashGet(props.action, 'originalMessage.paymentType', ''); - const isSubmitterOfUnsettledReport = ReportUtils.isCurrentUserSubmitter(props.report.reportID) && !ReportUtils.isSettled(props.report.reportID); - const shouldShowAddCreditBankAccountButton = isSubmitterOfUnsettledReport && !store.hasCreditBankAccount() && paymentType !== CONST.IOU.PAYMENT_TYPE.EXPENSIFY; - const shouldShowEnableWalletButton = - isSubmitterOfUnsettledReport && - (_.isEmpty(props.userWallet) || props.userWallet.tierName === CONST.WALLET.TIER_NAME.SILVER) && - paymentType === CONST.IOU.PAYMENT_TYPE.EXPENSIFY; - + const missingPaymentMethod = ReportUtils.getIndicatedMissingPaymentMethod(props.userWallet, props.report.reportID, props.action); children = ( <> - {shouldShowAddCreditBankAccountButton && ( + {missingPaymentMethod === 'bankAccount' && (