From df8c9da065dc25cdad4df69aa3af57cadca4e7fb Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 4 Oct 2024 23:00:13 +0800 Subject: [PATCH 001/311] don't include resolved actionable whisper to the visible actions --- src/libs/OptionsListUtils.ts | 3 +- src/libs/ReportActionsUtils.ts | 77 +++++++++++----------- src/pages/home/report/ReportActionItem.tsx | 25 +------ 3 files changed, 42 insertions(+), 63 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 90320b4a9ea1..164929fa445c 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -329,8 +329,7 @@ Onyx.connect({ ReportActionUtils.shouldReportActionBeVisible(reportAction, actionKey) && !ReportActionUtils.isWhisperAction(reportAction) && reportAction.actionName !== CONST.REPORT.ACTIONS.TYPE.CREATED && - reportAction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && - !ReportActionUtils.isResolvedActionTrackExpense(reportAction), + reportAction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, ); const reportActionForDisplay = reportActionsForDisplay.at(0); if (!reportActionForDisplay) { diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 486943494854..9716f22688fd 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -618,6 +618,39 @@ function isReportActionDeprecated(reportAction: OnyxEntry, key: st return false; } +/** + * Checks if a given report action corresponds to an actionable mention whisper. + * @param reportAction + */ +function isActionableMentionWhisper(reportAction: OnyxEntry): reportAction is ReportAction { + return isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_MENTION_WHISPER); +} + +/** + * Checks if a given report action corresponds to an actionable report mention whisper. + * @param reportAction + */ +function isActionableReportMentionWhisper(reportAction: OnyxEntry): reportAction is ReportAction { + return isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_REPORT_MENTION_WHISPER); +} + +/** + * Checks whether an action is actionable track expense. + */ +function isActionableTrackExpense(reportAction: OnyxInputOrEntry): reportAction is ReportAction { + return isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_TRACK_EXPENSE_WHISPER); +} + +function isActionableWhisper( + reportAction: OnyxEntry, +): reportAction is ReportAction< + | typeof CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_MENTION_WHISPER + | typeof CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_TRACK_EXPENSE_WHISPER + | typeof CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_REPORT_MENTION_WHISPER +> { + return isActionableMentionWhisper(reportAction) || isActionableTrackExpense(reportAction) || isActionableReportMentionWhisper(reportAction); +} + const {POLICY_CHANGE_LOG: policyChangelogTypes, ROOM_CHANGE_LOG: roomChangeLogTypes, ...otherActionTypes} = CONST.REPORT.ACTIONS.TYPE; const supportedActionTypes: ReportActionName[] = [...Object.values(otherActionTypes), ...Object.values(policyChangelogTypes), ...Object.values(roomChangeLogTypes)]; @@ -662,6 +695,11 @@ function shouldReportActionBeVisible(reportAction: OnyxEntry, key: return true; } + // If action is actionable whisper and resolved by user, then we don't want to render anything + if (isActionableWhisper(reportAction) && getOriginalMessage(reportAction)?.resolution) { + return false; + } + // All other actions are displayed except thread parents, deleted, or non-pending actions const isDeleted = isDeletedAction(reportAction); const isPending = !!reportAction.pendingAction; @@ -679,24 +717,6 @@ function shouldHideNewMarker(reportAction: OnyxEntry): boolean { return !isNetworkOffline && reportAction.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; } -/** - * Checks whether an action is actionable track expense. - * - */ -function isActionableTrackExpense(reportAction: OnyxInputOrEntry): reportAction is ReportAction { - return isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_TRACK_EXPENSE_WHISPER); -} - -/** - * Checks whether an action is actionable track expense and resolved. - * - */ -function isResolvedActionTrackExpense(reportAction: OnyxEntry): boolean { - const originalMessage = getOriginalMessage(reportAction); - const resolution = originalMessage && typeof originalMessage === 'object' && 'resolution' in originalMessage ? originalMessage?.resolution : null; - return isActionableTrackExpense(reportAction) && !!resolution; -} - /** * Checks if a reportAction is fit for display as report last action, meaning that * it satisfies shouldReportActionBeVisible, it's not whisper action and not deleted. @@ -715,8 +735,7 @@ function shouldReportActionBeVisibleAsLastAction(reportAction: OnyxInputOrEntry< return ( shouldReportActionBeVisible(reportAction, reportAction.reportActionID) && !(isWhisperAction(reportAction) && !isReportPreviewAction(reportAction) && !isMoneyRequestAction(reportAction)) && - !(isDeletedAction(reportAction) && !isDeletedParentAction(reportAction)) && - !isResolvedActionTrackExpense(reportAction) + !(isDeletedAction(reportAction) && !isDeletedParentAction(reportAction)) ); } @@ -1396,22 +1415,6 @@ function hasRequestFromCurrentAccount(reportID: string, currentAccountID: number return reportActions.some((action) => action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU && action.actorAccountID === currentAccountID); } -/** - * Checks if a given report action corresponds to an actionable mention whisper. - * @param reportAction - */ -function isActionableMentionWhisper(reportAction: OnyxEntry): reportAction is ReportAction { - return isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_MENTION_WHISPER); -} - -/** - * Checks if a given report action corresponds to an actionable report mention whisper. - * @param reportAction - */ -function isActionableReportMentionWhisper(reportAction: OnyxEntry): reportAction is ReportAction { - return isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_REPORT_MENTION_WHISPER); -} - /** * Constructs a message for an actionable mention whisper report action. * @param reportAction @@ -1802,6 +1805,7 @@ export { getWhisperedTo, hasRequestFromCurrentAccount, isActionOfType, + isActionableWhisper, isActionableJoinRequest, isActionableJoinRequestPending, isActionableMentionWhisper, @@ -1834,7 +1838,6 @@ export { isReportActionAttachment, isReportActionDeprecated, isReportPreviewAction, - isResolvedActionTrackExpense, isReversedTransaction, isRoomChangeLogAction, isSentMoneyReportAction, diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index 5b826b94aa77..9e9feb2d7905 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -200,10 +200,7 @@ function ReportActionItem({ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID || -1}`); const isReportActionLinked = linkedReportActionID && action.reportActionID && linkedReportActionID === action.reportActionID; - const reportScrollManager = useReportScrollManager(); - const isActionableWhisper = - ReportActionsUtils.isActionableMentionWhisper(action) || ReportActionsUtils.isActionableTrackExpense(action) || ReportActionsUtils.isActionableReportMentionWhisper(action); - const originalMessage = ReportActionsUtils.getOriginalMessage(action); + const isActionableWhisper = ReportActionsUtils.isActionableWhisper(action); const highlightedBackgroundColorIfNeeded = useMemo( () => (isReportActionLinked ? StyleUtils.getBackgroundColorStyle(theme.messageHighlightBG) : {}), @@ -211,9 +208,6 @@ function ReportActionItem({ ); const isDeletedParentAction = ReportActionsUtils.isDeletedParentAction(action); - const isOriginalMessageAnObject = originalMessage && typeof originalMessage === 'object'; - const hasResolutionInOriginalMessage = isOriginalMessageAnObject && 'resolution' in originalMessage; - const prevActionResolution = usePrevious(isActionableWhisper && hasResolutionInOriginalMessage ? originalMessage?.resolution : null); // IOUDetails only exists when we are sending money const isSendingMoney = @@ -360,18 +354,6 @@ function ReportActionItem({ [draftMessage, action, reportID, toggleContextMenuFromActiveReportAction, originalReportID, shouldDisplayContextMenu, disabledActions, isArchivedRoom, isChronosReport], ); - // Handles manual scrolling to the bottom of the chat when the last message is an actionable whisper and it's resolved. - // This fixes an issue where InvertedFlatList fails to auto scroll down and results in an empty space at the bottom of the chat in IOS. - useEffect(() => { - if (index !== 0 || !isActionableWhisper) { - return; - } - - if (prevActionResolution !== (hasResolutionInOriginalMessage ? originalMessage.resolution : null)) { - reportScrollManager.scrollToIndex(index); - } - }, [index, originalMessage, prevActionResolution, reportScrollManager, isActionableWhisper, hasResolutionInOriginalMessage]); - const toggleReaction = useCallback( (emoji: Emoji, ignoreSkinToneOnCompare?: boolean) => { Report.toggleEmojiReaction(reportID, action, emoji, emojiReactions, undefined, ignoreSkinToneOnCompare); @@ -879,11 +861,6 @@ function ReportActionItem({ return null; } - // If action is actionable whisper and resolved by user, then we don't want to render anything - if (isActionableWhisper && (hasResolutionInOriginalMessage ? originalMessage.resolution : null)) { - return null; - } - // We currently send whispers to all report participants and hide them in the UI for users that shouldn't see them. // This is a temporary solution needed for comment-linking. // The long term solution will leverage end-to-end encryption and only targeted users will be able to decrypt. From 0058a068f4df100578b70ddf41763ac63e5dbd33 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 4 Oct 2024 23:09:27 +0800 Subject: [PATCH 002/311] remove unused import --- src/pages/home/report/ReportActionItem.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index 9e9feb2d7905..983b35128689 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -34,7 +34,6 @@ import Text from '@components/Text'; import UnreadActionIndicator from '@components/UnreadActionIndicator'; import useLocalize from '@hooks/useLocalize'; import usePrevious from '@hooks/usePrevious'; -import useReportScrollManager from '@hooks/useReportScrollManager'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; From 1c3c0351eeb13fbf113079a34746a04e8244ecab Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Sat, 9 Nov 2024 00:38:19 +0700 Subject: [PATCH 003/311] feature: Add loading indicator when ReconnectApp is running --- src/CONST.ts | 3 +++ src/pages/home/ReportScreen.tsx | 4 +++- .../sidebar/SidebarScreen/BaseSidebarScreen.tsx | 4 +++- src/styles/index.ts | 14 ++++++++++++++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index e95e3d4a5603..07e5a5085302 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -315,6 +315,9 @@ const CONST = { ANIMATED_HIGHLIGHT_END_DURATION: 2000, ANIMATED_TRANSITION: 300, ANIMATED_TRANSITION_FROM_VALUE: 100, + ANIMATED_PROGRESS_BAR_DELAY: 300, + ANIMATED_PROGRESS_BAR_OPACITY_DURATION: 300, + ANIMATED_PROGRESS_BAR_DURATION: 750, ANIMATION_IN_TIMING: 100, ANIMATION_DIRECTION: { IN: 'in', diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 4c3ed5c705a5..a5c31563f8c3 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -10,6 +10,7 @@ import {useOnyx} from 'react-native-onyx'; import Banner from '@components/Banner'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import DragAndDropProvider from '@components/DragAndDrop/Provider'; +import LoadingBar from '@components/LoadingBar'; import MoneyReportHeader from '@components/MoneyReportHeader'; import MoneyRequestHeader from '@components/MoneyRequestHeader'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -132,7 +133,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro const finishedLoadingApp = wasLoadingApp && !isLoadingApp; const isDeletedParentAction = ReportActionsUtils.isDeletedParentAction(parentReportAction); const prevIsDeletedParentAction = usePrevious(isDeletedParentAction); - + const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA, {initialValue: true}); const isLoadingReportOnyx = isLoadingOnyxValue(reportResult); const permissions = useDeepCompareRef(reportOnyx?.permissions); @@ -756,6 +757,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro needsOffscreenAlphaCompositing > {headerView} + {shouldUseNarrowLayout && !!isLoadingReportData && } {!!report && ReportUtils.isTaskReport(report) && shouldUseNarrowLayout && ReportUtils.isOpenTaskReport(report, parentReportAction) && ( diff --git a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx index e77f2000b85f..9b6b498ab078 100644 --- a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx +++ b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx @@ -1,6 +1,7 @@ import React, {useEffect} from 'react'; import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; +import LoadingBar from '@components/LoadingBar'; import ScreenWrapper from '@components/ScreenWrapper'; import useActiveWorkspaceFromNavigationState from '@hooks/useActiveWorkspaceFromNavigationState'; import useLocalize from '@hooks/useLocalize'; @@ -30,7 +31,7 @@ function BaseSidebarScreen() { const {translate} = useLocalize(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const [activeWorkspace] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activeWorkspaceID ?? -1}`); - + const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA, {initialValue: true}); useEffect(() => { Performance.markStart(CONST.TIMING.SIDEBAR_LOADED); Timing.start(CONST.TIMING.SIDEBAR_LOADED); @@ -62,6 +63,7 @@ function BaseSidebarScreen() { activeWorkspaceID={activeWorkspaceID} shouldDisplaySearch={shouldDisplaySearch} /> + width: variables.sideBarWidth - 19, }, + progressBarWrapper: { + height: 2, + width: '100%', + backgroundColor: theme.border, + borderRadius: 5, + overflow: 'hidden', + }, + + progressBar: { + height: '100%', + backgroundColor: theme.success, + width: '100%', + }, + accountSwitcherAnchorPosition: { top: 80, left: 12, From c8f048a174b6cfe847e1b8ec19609c529d15e0bf Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Sat, 9 Nov 2024 00:51:15 +0700 Subject: [PATCH 004/311] fix lint --- src/components/LoadingBar.tsx | 85 +++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/components/LoadingBar.tsx diff --git a/src/components/LoadingBar.tsx b/src/components/LoadingBar.tsx new file mode 100644 index 000000000000..163ffe2aa66b --- /dev/null +++ b/src/components/LoadingBar.tsx @@ -0,0 +1,85 @@ +import React, {useEffect} from 'react'; +import Animated, {cancelAnimation, Easing, runOnJS, useAnimatedStyle, useSharedValue, withDelay, withRepeat, withSequence, withTiming} from 'react-native-reanimated'; +import useThemeStyles from '@hooks/useThemeStyles'; +import CONST from '@src/CONST'; + +type LoadingBarProps = { + // Whether or not to show the loading bar + shouldShow: boolean; +}; + +function LoadingBar({shouldShow}: LoadingBarProps) { + const left = useSharedValue(0); + const width = useSharedValue(0); + const opacity = useSharedValue(0); + const isVisible = useSharedValue(false); + const styles = useThemeStyles(); + + useEffect(() => { + if (shouldShow) { + // eslint-disable-next-line react-compiler/react-compiler + isVisible.value = true; + left.value = 0; + width.value = 0; + opacity.value = withTiming(1, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION}); + left.value = withDelay( + CONST.ANIMATED_PROGRESS_BAR_DELAY, + withRepeat( + withSequence( + withTiming(0, {duration: 0}), + withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + withTiming(100, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + ), + -1, + false, + ), + ); + + width.value = withDelay( + CONST.ANIMATED_PROGRESS_BAR_DELAY, + withRepeat( + withSequence( + withTiming(0, {duration: 0}), + withTiming(100, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + ), + -1, + false, + ), + ); + } else if (isVisible.value) { + opacity.value = withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION}, () => { + runOnJS(() => { + isVisible.value = false; + cancelAnimation(left); + cancelAnimation(width); + }); + }); + } + // we want to update only when shouldShow changes + // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps + }, [shouldShow]); + + const animatedIndicatorStyle = useAnimatedStyle(() => { + return { + left: `${left.value}%`, + width: `${width.value}%`, + }; + }); + + const animatedContainerStyle = useAnimatedStyle(() => { + return { + opacity: opacity.value, + }; + }); + + return ( + + {isVisible.value ? : null} + + ); +} + +LoadingBar.displayName = 'ProgressBar'; + +export default LoadingBar; From a3dcad33f2cc76729d6733c6d93d8ee9701c6941 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 12 Nov 2024 16:29:48 +0700 Subject: [PATCH 005/311] remove isVisible shared value --- src/components/LoadingBar.tsx | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/src/components/LoadingBar.tsx b/src/components/LoadingBar.tsx index 163ffe2aa66b..6956887227bb 100644 --- a/src/components/LoadingBar.tsx +++ b/src/components/LoadingBar.tsx @@ -1,5 +1,5 @@ import React, {useEffect} from 'react'; -import Animated, {cancelAnimation, Easing, runOnJS, useAnimatedStyle, useSharedValue, withDelay, withRepeat, withSequence, withTiming} from 'react-native-reanimated'; +import Animated, {cancelAnimation, Easing, useAnimatedStyle, useSharedValue, withDelay, withRepeat, withSequence, withTiming} from 'react-native-reanimated'; import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; @@ -12,16 +12,15 @@ function LoadingBar({shouldShow}: LoadingBarProps) { const left = useSharedValue(0); const width = useSharedValue(0); const opacity = useSharedValue(0); - const isVisible = useSharedValue(false); const styles = useThemeStyles(); useEffect(() => { if (shouldShow) { // eslint-disable-next-line react-compiler/react-compiler - isVisible.value = true; left.value = 0; width.value = 0; opacity.value = withTiming(1, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION}); + left.value = withDelay( CONST.ANIMATED_PROGRESS_BAR_DELAY, withRepeat( @@ -47,35 +46,28 @@ function LoadingBar({shouldShow}: LoadingBarProps) { false, ), ); - } else if (isVisible.value) { + } else { opacity.value = withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION}, () => { - runOnJS(() => { - isVisible.value = false; - cancelAnimation(left); - cancelAnimation(width); - }); + cancelAnimation(left); + cancelAnimation(width); }); } // we want to update only when shouldShow changes // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [shouldShow]); - const animatedIndicatorStyle = useAnimatedStyle(() => { - return { - left: `${left.value}%`, - width: `${width.value}%`, - }; - }); + const animatedIndicatorStyle = useAnimatedStyle(() => ({ + left: `${left.value}%`, + width: `${width.value}%`, + })); - const animatedContainerStyle = useAnimatedStyle(() => { - return { - opacity: opacity.value, - }; - }); + const animatedContainerStyle = useAnimatedStyle(() => ({ + opacity: opacity.value, + })); return ( - {isVisible.value ? : null} + {opacity.value > 0 ? : null} ); } From 85753dd50570de0458ae5d67db2761ea84a0a3c5 Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Wed, 20 Nov 2024 02:43:41 -0500 Subject: [PATCH 006/311] Don't use report for isArchived check --- src/libs/ReportUtils.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 373a861f7c2e..88088560e170 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1460,18 +1460,15 @@ function isClosedExpenseReportWithNoExpenses(report: OnyxEntry): boolean /** * Whether the provided report is an archived room */ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -function isArchivedRoom(report: OnyxInputOrEntry, reportNameValuePairs?: OnyxInputOrEntry): boolean { - return !!report?.private_isArchived; +function isArchivedRoom(reportNameValuePairs?: OnyxInputOrEntry): boolean { + return !!reportNameValuePairs?.private_isArchived; } /** * Whether the report with the provided reportID is an archived room */ function isArchivedRoomWithID(reportID?: string) { - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const report = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID || -1}`]; - return isArchivedRoom(report, getReportNameValuePairs(reportID)); + return isArchivedRoom(getReportNameValuePairs(reportID)); } /** From 0c31b926e2509a67007d0313eab2161e6ea3c127 Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Wed, 20 Nov 2024 02:49:27 -0500 Subject: [PATCH 007/311] update optional --- .../AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx | 2 +- .../HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx | 2 +- .../HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx | 2 +- src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx | 2 +- src/components/ReportWelcomeText.tsx | 2 +- src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx | 2 +- src/libs/ReportUtils.ts | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx b/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx index ae74a11c7e9d..ab754a33e3d2 100644 --- a/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx +++ b/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx @@ -51,7 +51,7 @@ function BaseAnchorForAttachmentsOnly({style, source = '', displayName = '', onP if (isDisabled) { return; } - showContextMenuForReport(event, anchor, report?.reportID ?? '-1', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report, reportNameValuePairs)); + showContextMenuForReport(event, anchor, report?.reportID ?? '-1', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(reportNameValuePairs)); }} shouldUseHapticsOnLongPress accessibilityLabel={displayName} diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx index c27eef1de91e..95db3aba7a37 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx @@ -112,7 +112,7 @@ function ImageRenderer({tnode}: ImageRendererProps) { if (isDisabled) { return; } - showContextMenuForReport(event, anchor, report?.reportID ?? '-1', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report, reportNameValuePairs)); + showContextMenuForReport(event, anchor, report?.reportID ?? '-1', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(reportNameValuePairs)); }} shouldUseHapticsOnLongPress accessibilityRole={CONST.ROLE.BUTTON} diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx index 36586b09e514..e19a5b996460 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx @@ -90,7 +90,7 @@ function MentionUserRenderer({style, tnode, TDefaultRenderer, currentUserPersona if (isDisabled) { return; } - showContextMenuForReport(event, anchor, report?.reportID ?? '-1', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report, reportNameValuePairs)); + showContextMenuForReport(event, anchor, report?.reportID ?? '-1', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(reportNameValuePairs)); }} onPress={(event) => { event.preventDefault(); diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx index b1e5c21500f0..854a934e3337 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx @@ -43,7 +43,7 @@ function PreRenderer({TDefaultRenderer, onPressIn, onPressOut, onLongPress, ...d if (isDisabled) { return; } - showContextMenuForReport(event, anchor, report?.reportID ?? '-1', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report, reportNameValuePairs)); + showContextMenuForReport(event, anchor, report?.reportID ?? '-1', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(reportNameValuePairs)); }} shouldUseHapticsOnLongPress role={CONST.ROLE.PRESENTATION} diff --git a/src/components/ReportWelcomeText.tsx b/src/components/ReportWelcomeText.tsx index 3c38c9f4c4a3..8a6025c56384 100644 --- a/src/components/ReportWelcomeText.tsx +++ b/src/components/ReportWelcomeText.tsx @@ -34,7 +34,7 @@ function ReportWelcomeText({report, policy}: ReportWelcomeTextProps) { const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(report); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID || -1}`); - const isArchivedRoom = ReportUtils.isArchivedRoom(report, reportNameValuePairs); + const isArchivedRoom = ReportUtils.isArchivedRoom(reportNameValuePairs); const isChatRoom = ReportUtils.isChatRoom(report); const isSelfDM = ReportUtils.isSelfDM(report); const isInvoiceRoom = ReportUtils.isInvoiceRoom(report); diff --git a/src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx b/src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx index 832b5eef45f0..49ce60281c82 100644 --- a/src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx +++ b/src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx @@ -57,7 +57,7 @@ function VideoPlayerThumbnail({thumbnailUrl, onPress, accessibilityLabel, isDele if (isDisabled) { return; } - showContextMenuForReport(event, anchor, report?.reportID ?? '-1', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report, reportNameValuePairs)); + showContextMenuForReport(event, anchor, report?.reportID ?? '-1', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(reportNameValuePairs)); }} shouldUseHapticsOnLongPress > diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 88088560e170..39aa034c11c4 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1460,7 +1460,7 @@ function isClosedExpenseReportWithNoExpenses(report: OnyxEntry): boolean /** * Whether the provided report is an archived room */ -function isArchivedRoom(reportNameValuePairs?: OnyxInputOrEntry): boolean { +function isArchivedRoom(reportNameValuePairs: OnyxInputOrEntry): boolean { return !!reportNameValuePairs?.private_isArchived; } From d1bd7912fce2d2c4f37a6ceb3fd0e573e9bd800e Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Wed, 20 Nov 2024 02:56:12 -0500 Subject: [PATCH 008/311] ReportUtils fix --- src/libs/ReportUtils.ts | 52 ++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 39aa034c11c4..050168b7a011 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1779,7 +1779,7 @@ function getChildReportNotificationPreference(reportAction: OnyxInputOrEntry): boolean { - if (!isMoneyRequestReport(moneyRequestReport) || isArchivedRoom(moneyRequestReport)) { + if (!isMoneyRequestReport(moneyRequestReport) || isArchivedRoomWithID(moneyRequestReport?.reportID)) { return false; } @@ -2379,7 +2379,7 @@ function getIcons( }; return [domainIcon]; } - if (isAdminRoom(report) || isAnnounceRoom(report) || isChatRoom(report) || isArchivedRoom(report, getReportNameValuePairs(report?.reportID))) { + if (isAdminRoom(report) || isAnnounceRoom(report) || isChatRoom(report) || isArchivedRoom(getReportNameValuePairs(report?.reportID))) { const icons = [getWorkspaceIcon(report, policy)]; if (isInvoiceRoom(report)) { @@ -2767,8 +2767,8 @@ function getReasonAndReportActionThatRequiresAttention( } if ( - isArchivedRoom(optionOrReport, getReportNameValuePairs(optionOrReport?.reportID)) || - isArchivedRoom(getReportOrDraftReport(optionOrReport.parentReportID), getReportNameValuePairs(optionOrReport?.reportID)) + isArchivedRoom(getReportNameValuePairs(optionOrReport?.reportID)) || + isArchivedRoom(getReportNameValuePairs(optionOrReport?.reportID)) ) { return null; } @@ -2899,7 +2899,7 @@ function getPolicyExpenseChatName(report: OnyxEntry, policy?: OnyxEntry< // If this user is not admin and this policy expense chat has been archived because of account merging, this must be an old workspace chat // of the account which was merged into the current user's account. Use the name of the policy as the name of the report. - if (isArchivedRoom(report, getReportNameValuePairs(report?.reportID))) { + if (isArchivedRoom(getReportNameValuePairs(report?.reportID))) { const lastAction = ReportActionsUtils.getLastVisibleAction(report?.reportID ?? '-1'); const archiveReason = ReportActionsUtils.isClosedAction(lastAction) ? ReportActionsUtils.getOriginalMessage(lastAction)?.reason : CONST.REPORT.ARCHIVE_REASON.DEFAULT; if (archiveReason === CONST.REPORT.ARCHIVE_REASON.ACCOUNT_MERGED && policyExpenseChatRole !== CONST.POLICY.ROLE.ADMIN) { @@ -3968,7 +3968,7 @@ function getReportName( if (isChatThread(report)) { if (!isEmptyObject(parentReportAction) && ReportActionsUtils.isTransactionThread(parentReportAction)) { formattedName = getTransactionReportName(parentReportAction); - if (isArchivedRoom(report, getReportNameValuePairs(report?.reportID))) { + if (isArchivedRoom(getReportNameValuePairs(report?.reportID))) { formattedName += ` (${Localize.translateLocal('common.archived')})`; } return formatReportLastMessageText(formattedName); @@ -3997,7 +3997,7 @@ function getReportName( if (isAdminRoom(report) || isUserCreatedPolicyRoom(report)) { return getAdminRoomInvitedParticipants(parentReportAction, reportActionMessage); } - if (reportActionMessage && isArchivedRoom(report, getReportNameValuePairs(report?.reportID))) { + if (reportActionMessage && isArchivedRoom(getReportNameValuePairs(report?.reportID))) { return `${reportActionMessage} (${Localize.translateLocal('common.archived')})`; } if (!isEmptyObject(parentReportAction) && ReportActionsUtils.isModifiedExpenseAction(parentReportAction)) { @@ -4046,7 +4046,7 @@ function getReportName( formattedName = getInvoicesChatName(report, invoiceReceiverPolicy); } - if (isArchivedRoom(report, getReportNameValuePairs(report?.reportID))) { + if (isArchivedRoom(getReportNameValuePairs(report?.reportID))) { formattedName += ` (${Localize.translateLocal('common.archived')})`; } @@ -4122,7 +4122,7 @@ function getChatRoomSubtitle(report: OnyxEntry): string | undefined { if ((isPolicyExpenseChat(report) && !!report?.isOwnPolicyExpenseChat) || isExpenseReport(report)) { return Localize.translateLocal('workspace.common.workspace'); } - if (isArchivedRoom(report, getReportNameValuePairs(report?.reportID))) { + if (isArchivedRoom(getReportNameValuePairs(report?.reportID))) { return report?.oldPolicyName ?? ''; } return getPolicyName(report); @@ -4148,7 +4148,7 @@ function getParentNavigationSubtitle(report: OnyxEntry, invoiceReceiverP if (isInvoiceReport(report) || isInvoiceRoom(parentReport)) { let reportName = `${getPolicyName(parentReport)} & ${getInvoicePayerName(parentReport, invoiceReceiverPolicy)}`; - if (isArchivedRoom(parentReport, getReportNameValuePairs(parentReport?.reportID))) { + if (isArchivedRoom(getReportNameValuePairs(parentReport?.reportID))) { reportName += ` (${Localize.translateLocal('common.archived')})`; } @@ -6282,7 +6282,7 @@ function isIOUOwnedByCurrentUser(report: OnyxEntry, allReportsDict?: Ony */ function canSeeDefaultRoom(report: OnyxEntry, policies: OnyxCollection, betas: OnyxEntry): boolean { // Include archived rooms - if (isArchivedRoom(report, getReportNameValuePairs(report?.reportID))) { + if (isArchivedRoom(getReportNameValuePairs(report?.reportID))) { return true; } @@ -6422,7 +6422,7 @@ function getAllReportActionsErrorsAndReportActionThatRequiresAttention(report: O ? undefined : allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID ?? '-1'}`]?.[report.parentReportActionID ?? '-1']; - if (!isArchivedRoom(report)) { + if (!isArchivedRoomWithID(report?.reportID)) { if (ReportActionsUtils.wasActionTakenByCurrentUser(parentReportAction) && ReportActionsUtils.isTransactionThread(parentReportAction)) { const transactionID = ReportActionsUtils.isMoneyRequestAction(parentReportAction) ? ReportActionsUtils.getOriginalMessage(parentReportAction)?.IOUTransactionID : null; const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; @@ -6533,7 +6533,7 @@ function reasonForReportToBeInOptionList({ // So we allow showing rooms with no participants–in any other circumstances we should never have these reports with no participants in Onyx. !isChatRoom(report) && !isChatThread(report) && - !isArchivedRoom(report, getReportNameValuePairs(report?.reportID)) && + !isArchivedRoom(getReportNameValuePairs(report?.reportID)) && !isMoneyRequestReport(report) && !isTaskReport(report) && !isSelfDM(report) && @@ -6620,7 +6620,7 @@ function reasonForReportToBeInOptionList({ } // Archived reports should always be shown when in default (most recent) mode. This is because you should still be able to access and search for the chats to find them. - if (isInDefaultMode && isArchivedRoom(report, getReportNameValuePairs(report?.reportID))) { + if (isInDefaultMode && isArchivedRoom(getReportNameValuePairs(report?.reportID))) { return CONST.REPORT_IN_LHN_REASONS.IS_ARCHIVED; } @@ -6702,7 +6702,7 @@ function getChatByParticipants(newParticipantList: number[], reports: OnyxCollec */ function getInvoiceChatByParticipants(policyID: string, receiverID: string | number, reports: OnyxCollection = ReportConnection.getAllReports()): OnyxEntry { return Object.values(reports ?? {}).find((report) => { - if (!report || !isInvoiceRoom(report) || isArchivedRoom(report)) { + if (!report || !isInvoiceRoom(report) || isArchivedRoomWithID(report?.reportID)) { return false; } @@ -6790,7 +6790,7 @@ function canFlagReportAction(reportAction: OnyxInputOrEntry, repor function shouldShowFlagComment(reportAction: OnyxInputOrEntry, report: OnyxInputOrEntry): boolean { return ( canFlagReportAction(reportAction, report?.reportID) && - !isArchivedRoom(report, getReportNameValuePairs(report?.reportID)) && + !isArchivedRoom(getReportNameValuePairs(report?.reportID)) && !chatIncludesChronos(report) && !isConciergeChatReport(report) && reportAction?.actorAccountID !== CONST.ACCOUNT_ID.CONCIERGE @@ -7113,7 +7113,7 @@ function canLeaveInvoiceRoom(report: OnyxEntry): boolean { */ function canLeaveRoom(report: OnyxEntry, isPolicyEmployee: boolean): boolean { if (isInvoiceRoom(report)) { - if (isArchivedRoom(report, getReportNameValuePairs(report?.reportID))) { + if (isArchivedRoom(getReportNameValuePairs(report?.reportID))) { return false; } @@ -7184,7 +7184,7 @@ function getWhisperDisplayNames(participantAccountIDs?: number[]): string | unde * Show subscript on workspace chats / threads and expense requests */ function shouldReportShowSubscript(report: OnyxEntry): boolean { - if (isArchivedRoom(report, getReportNameValuePairs(report?.reportID)) && !isWorkspaceThread(report)) { + if (isArchivedRoom(getReportNameValuePairs(report?.reportID)) && !isWorkspaceThread(report)) { return false; } @@ -7265,7 +7265,7 @@ function canUserPerformWriteAction(report: OnyxEntry) { } const reportNameValuePairs = getReportNameValuePairs(report?.reportID); - return !isArchivedRoom(report, reportNameValuePairs) && isEmptyObject(reportErrors) && report && isAllowedToComment(report) && !isAnonymousUser && canWriteInReport(report); + return !isArchivedRoom(reportNameValuePairs) && isEmptyObject(reportErrors) && report && isAllowedToComment(report) && !isAnonymousUser && canWriteInReport(report); } /** @@ -7338,7 +7338,7 @@ function getAllWorkspaceReports(policyID: string): Array> { function shouldDisableRename(report: OnyxEntry): boolean { if ( isDefaultRoom(report) || - isArchivedRoom(report, getReportNameValuePairs(report?.reportID)) || + isArchivedRoom(getReportNameValuePairs(report?.reportID)) || isPublicRoom(report) || isThread(report) || isMoneyRequest(report) || @@ -7366,14 +7366,14 @@ function shouldDisableRename(report: OnyxEntry): boolean { * @param policy - the workspace the report is on, null if the user isn't a member of the workspace */ function canEditWriteCapability(report: OnyxEntry, policy: OnyxEntry): boolean { - return PolicyUtils.isPolicyAdmin(policy) && !isAdminRoom(report) && !isArchivedRoom(report, getReportNameValuePairs(report?.reportID)) && !isThread(report) && !isInvoiceRoom(report); + return PolicyUtils.isPolicyAdmin(policy) && !isAdminRoom(report) && !isArchivedRoom(getReportNameValuePairs(report?.reportID)) && !isThread(report) && !isInvoiceRoom(report); } /** * @param policy - the workspace the report is on, null if the user isn't a member of the workspace */ function canEditRoomVisibility(report: OnyxEntry, policy: OnyxEntry): boolean { - return PolicyUtils.isPolicyAdmin(policy) && !isArchivedRoom(report, getReportNameValuePairs(report?.reportID)); + return PolicyUtils.isPolicyAdmin(policy) && !isArchivedRoom(getReportNameValuePairs(report?.reportID)); } /** @@ -7582,7 +7582,7 @@ function isDeprecatedGroupDM(report: OnyxEntry): boolean { !isTaskReport(report) && !isInvoiceReport(report) && !isMoneyRequestReport(report) && - !isArchivedRoom(report, getReportNameValuePairs(report?.reportID)) && + !isArchivedRoom(getReportNameValuePairs(report?.reportID)) && !Object.values(CONST.REPORT.CHAT_TYPE).some((chatType) => chatType === getChatType(report)) && Object.keys(report.participants ?? {}) .map(Number) @@ -7646,7 +7646,7 @@ function getRoom(type: ValueOf, policyID: string) function canEditReportDescription(report: OnyxEntry, policy: OnyxEntry): boolean { return ( !isMoneyRequestReport(report) && - !isArchivedRoom(report, getReportNameValuePairs(report?.reportID)) && + !isArchivedRoom(getReportNameValuePairs(report?.reportID)) && isChatRoom(report) && !isChatThread(report) && !isEmpty(policy) && @@ -7812,7 +7812,7 @@ function shouldDisableThread(reportAction: OnyxInputOrEntry, repor const isReportPreviewAction = ReportActionsUtils.isReportPreviewAction(reportAction); const isIOUAction = ReportActionsUtils.isMoneyRequestAction(reportAction); const isWhisperAction = ReportActionsUtils.isWhisperAction(reportAction) || ReportActionsUtils.isActionableTrackExpense(reportAction); - const isArchivedReport = isArchivedRoom(getReportOrDraftReport(reportID), getReportNameValuePairs(reportID)); + const isArchivedReport = isArchivedRoom(getReportNameValuePairs(reportID)); const isActionDisabled = CONST.REPORT.ACTIONS.THREAD_DISABLED.some((action: string) => action === reportAction?.actionName); return ( @@ -7947,7 +7947,7 @@ function getQuickActionDetails( policyChatForActivePolicy: Report | undefined, reportNameValuePairs: ReportNameValuePairs, ): {quickActionAvatars: Icon[]; hideQABSubtitle: boolean} { - const isValidQuickActionReport = !(isEmptyObject(quickActionReport) || isArchivedRoom(quickActionReport, reportNameValuePairs)); + const isValidQuickActionReport = !(isEmptyObject(quickActionReport) || isArchivedRoom(reportNameValuePairs)); let hideQABSubtitle = false; let quickActionAvatars: Icon[] = []; if (isValidQuickActionReport) { From 139c91d04de4c6873f84ff98edc1c061dddf0764 Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Wed, 20 Nov 2024 03:01:43 -0500 Subject: [PATCH 009/311] parentReportNameValuePairs fix --- src/libs/SidebarUtils.ts | 2 +- src/libs/actions/IOU.ts | 4 ++-- src/libs/actions/Task.ts | 2 +- src/pages/ReportDetailsPage.tsx | 4 ++-- src/pages/ReportParticipantsPage.tsx | 2 +- src/pages/home/report/ReportActionItem.tsx | 3 +-- src/pages/home/report/ReportActionsList.tsx | 6 +++--- 7 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 806cebd9bf7f..7f5bd68c5163 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -194,7 +194,7 @@ function getOrderedReportIDs( errorReports.push(miniReport); } else if (hasValidDraftComment(report?.reportID ?? '-1')) { draftReports.push(miniReport); - } else if (ReportUtils.isArchivedRoom(report, reportNameValuePairs)) { + } else if (ReportUtils.isArchivedRoom(reportNameValuePairs)) { archivedReports.push(miniReport); } else { nonArchivedReports.push(miniReport); diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index e9eba8d57ef4..8705ec5da7b9 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -6759,7 +6759,7 @@ function canApproveIOU(iouReport: OnyxTypes.OnyxInputOrEntry, const isApproved = ReportUtils.isReportApproved(iouReport); const iouSettled = ReportUtils.isSettled(iouReport?.reportID); const reportNameValuePairs = ReportUtils.getReportNameValuePairs(iouReport?.reportID); - const isArchivedReport = ReportUtils.isArchivedRoom(iouReport, reportNameValuePairs); + const isArchivedReport = ReportUtils.isArchivedRoom(reportNameValuePairs); let isTransactionBeingScanned = false; const reportTransactions = TransactionUtils.getAllReportTransactions(iouReport?.reportID); for (const transaction of reportTransactions) { @@ -6785,7 +6785,7 @@ function canIOUBePaid( ) { const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(chatReport); const reportNameValuePairs = ReportUtils.getReportNameValuePairs(chatReport?.reportID); - const isChatReportArchived = ReportUtils.isArchivedRoom(chatReport, reportNameValuePairs); + const isChatReportArchived = ReportUtils.isArchivedRoom(reportNameValuePairs); const iouSettled = ReportUtils.isSettled(iouReport?.reportID); if (isEmptyObject(iouReport)) { diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 664bdb3779a6..b572fa80429d 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -1165,7 +1165,7 @@ function canModifyTask(taskReport: OnyxEntry, sessionAccountID const parentReport = getParentReport(taskReport); const reportNameValuePairs = ReportUtils.getReportNameValuePairs(parentReport?.reportID); - if (ReportUtils.isArchivedRoom(parentReport, reportNameValuePairs)) { + if (ReportUtils.isArchivedRoom(reportNameValuePairs)) { return false; } diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 296a2d51e283..0e62a68e9b41 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -125,7 +125,7 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { const isUserCreatedPolicyRoom = useMemo(() => ReportUtils.isUserCreatedPolicyRoom(report), [report]); const isDefaultRoom = useMemo(() => ReportUtils.isDefaultRoom(report), [report]); const isChatThread = useMemo(() => ReportUtils.isChatThread(report), [report]); - const isArchivedRoom = useMemo(() => ReportUtils.isArchivedRoom(report, reportNameValuePairs), [report, reportNameValuePairs]); + const isArchivedRoom = useMemo(() => ReportUtils.isArchivedRoom(reportNameValuePairs), [reportNameValuePairs]); const isMoneyRequestReport = useMemo(() => ReportUtils.isMoneyRequestReport(report), [report]); const isMoneyRequest = useMemo(() => ReportUtils.isMoneyRequest(report), [report]); const isInvoiceReport = useMemo(() => ReportUtils.isInvoiceReport(report), [report]); @@ -639,7 +639,7 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { const shouldShowHoldAction = caseID !== CASES.DEFAULT && (canHoldUnholdReportAction.canHoldRequest || canHoldUnholdReportAction.canUnholdRequest) && - !ReportUtils.isArchivedRoom(transactionThreadReportID ? report : parentReport, parentReportNameValuePairs); + !ReportUtils.isArchivedRoom(transactionThreadReportID ? reportNameValuePairs : parentReportNameValuePairs); const canJoin = ReportUtils.canJoinChat(report, parentReportAction, policy); diff --git a/src/pages/ReportParticipantsPage.tsx b/src/pages/ReportParticipantsPage.tsx index 1c91023f2d6f..db00d9467d39 100755 --- a/src/pages/ReportParticipantsPage.tsx +++ b/src/pages/ReportParticipantsPage.tsx @@ -363,7 +363,7 @@ function ReportParticipantsPage({report, route}: ReportParticipantsPageProps) { style={[styles.defaultModalContainer]} testID={ReportParticipantsPage.displayName} > - + { diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index 559d635f73fe..4dbfcd4d01fb 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -771,8 +771,7 @@ function ReportActionItem({ index={index} ref={textInputRef} shouldDisableEmojiPicker={ - (ReportUtils.chatIncludesConcierge(report) && User.isBlockedFromConcierge(blockedFromConcierge)) || - ReportUtils.isArchivedRoom(report, reportNameValuePairs) + (ReportUtils.chatIncludesConcierge(report) && User.isBlockedFromConcierge(blockedFromConcierge)) || ReportUtils.isArchivedRoom(reportNameValuePairs) } isGroupPolicyReport={!!report?.policyID && report.policyID !== CONST.POLICY.ID_FAKE} /> diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index 8ddea8e7e940..b4f40949f454 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -561,7 +561,7 @@ function ReportActionsList({ const newMessageTimeReference = lastMessageTime.current && report.lastReadTime && lastMessageTime.current > report.lastReadTime ? userActiveSince.current : report.lastReadTime; lastMessageTime.current = null; - const isArchivedReport = ReportUtils.isArchivedRoom(report); + const isArchivedReport = ReportUtils.isArchivedRoomWithID(report.reportID); const hasNewMessagesInView = scrollingVerticalOffset.current < MSG_VISIBLE_THRESHOLD; const hasUnreadReportAction = sortedVisibleReportActions.some( (reportAction) => @@ -626,8 +626,8 @@ function ReportActionsList({ // Native mobile does not render updates flatlist the changes even though component did update called. // To notify there something changes we can use extraData prop to flatlist const extraData = useMemo( - () => [shouldUseNarrowLayout ? unreadMarkerReportActionID : undefined, ReportUtils.isArchivedRoom(report, reportNameValuePairs)], - [unreadMarkerReportActionID, shouldUseNarrowLayout, report, reportNameValuePairs], + () => [shouldUseNarrowLayout ? unreadMarkerReportActionID : undefined, ReportUtils.isArchivedRoom(reportNameValuePairs)], + [unreadMarkerReportActionID, shouldUseNarrowLayout, reportNameValuePairs], ); const hideComposer = !ReportUtils.canUserPerformWriteAction(report); const shouldShowReportRecipientLocalTime = ReportUtils.canShowReportRecipientLocalTime(personalDetailsList, report, currentUserPersonalDetails.accountID) && !isComposerFullSize; From bd7aa227d677e5fc455fc8d82e1716a15bfcc133 Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Wed, 20 Nov 2024 03:27:10 -0500 Subject: [PATCH 010/311] child report name value pairs --- src/libs/ReportUtils.ts | 5 +---- .../home/report/ContextMenu/BaseReportActionContextMenu.tsx | 4 ++-- src/pages/home/report/ReportFooter.tsx | 2 +- .../home/report/withReportAndPrivateNotesOrNotFound.tsx | 2 +- .../sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx | 2 +- src/pages/iou/request/step/IOURequestStepAmount.tsx | 4 ++-- src/pages/iou/request/step/IOURequestStepDistance.tsx | 4 ++-- .../iou/request/step/IOURequestStepScan/index.native.tsx | 4 +--- src/pages/iou/request/step/IOURequestStepScan/index.tsx | 4 +--- src/pages/settings/Report/NotificationPreferencePage.tsx | 2 +- src/pages/settings/Report/ReportSettingsPage.tsx | 2 +- src/pages/settings/Report/VisibilityPage.tsx | 2 +- 12 files changed, 15 insertions(+), 22 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 050168b7a011..c310d0f4f815 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2766,10 +2766,7 @@ function getReasonAndReportActionThatRequiresAttention( }; } - if ( - isArchivedRoom(getReportNameValuePairs(optionOrReport?.reportID)) || - isArchivedRoom(getReportNameValuePairs(optionOrReport?.reportID)) - ) { + if (isArchivedRoom(getReportNameValuePairs(optionOrReport?.reportID)) || isArchivedRoom(getReportNameValuePairs(optionOrReport?.reportID))) { return null; } diff --git a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx index 2e92669aa8c5..f20302a6ad0f 100755 --- a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx @@ -169,8 +169,8 @@ function BaseReportActionContextMenu({ const moneyRequestAction = transactionThreadReportID ? requestParentReportAction : parentReportAction; + const [childReportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${childReport?.reportID ?? '-1'}`); const [parentReportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${childReport?.parentReportID ?? '-1'}`); - const parentReport = ReportUtils.getReport(childReport?.parentReportID ?? '-1'); const isMoneyRequest = useMemo(() => ReportUtils.isMoneyRequest(childReport), [childReport]); const isTrackExpenseReport = ReportUtils.isTrackExpenseReport(childReport); @@ -178,7 +178,7 @@ function BaseReportActionContextMenu({ const isMoneyRequestOrReport = isMoneyRequestReport || isSingleTransactionView; const areHoldRequirementsMet = - !isInvoiceReport && isMoneyRequestOrReport && !ReportUtils.isArchivedRoom(transactionThreadReportID ? childReport : parentReport, parentReportNameValuePairs); + !isInvoiceReport && isMoneyRequestOrReport && !ReportUtils.isArchivedRoom(transactionThreadReportID ? childReportNameValuePairs : parentReportNameValuePairs); const shouldEnableArrowNavigation = !isMini && (isVisible || shouldKeepOpen); let filteredContextMenuActions = ContextMenuActions.filter( diff --git a/src/pages/home/report/ReportFooter.tsx b/src/pages/home/report/ReportFooter.tsx index c087510374be..e05525df6ce1 100644 --- a/src/pages/home/report/ReportFooter.tsx +++ b/src/pages/home/report/ReportFooter.tsx @@ -106,7 +106,7 @@ function ReportFooter({ const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID ?? -1}`); const chatFooterStyles = {...styles.chatFooter, minHeight: !isOffline ? CONST.CHAT_FOOTER_MIN_HEIGHT : 0}; - const isArchivedRoom = ReportUtils.isArchivedRoom(report, reportNameValuePairs); + const isArchivedRoom = ReportUtils.isArchivedRoom(reportNameValuePairs); const isSmallSizeLayout = windowWidth - (shouldUseNarrowLayout ? 0 : variables.sideBarWidth) < variables.anonymousReportFooterBreakpoint; diff --git a/src/pages/home/report/withReportAndPrivateNotesOrNotFound.tsx b/src/pages/home/report/withReportAndPrivateNotesOrNotFound.tsx index 2fed275045bc..597c49af4d8d 100644 --- a/src/pages/home/report/withReportAndPrivateNotesOrNotFound.tsx +++ b/src/pages/home/report/withReportAndPrivateNotesOrNotFound.tsx @@ -56,7 +56,7 @@ export default function (pageTitle: TranslationPaths) { // eslint-disable-next-line rulesdir/no-negated-variables const shouldShowNotFoundPage = useMemo(() => { // Show not found view if the report is archived, or if the note is not of current user or if report is a self DM. - if (ReportUtils.isArchivedRoom(report, reportNameValuePairs) || isOtherUserNote || ReportUtils.isSelfDM(report)) { + if (ReportUtils.isArchivedRoom(reportNameValuePairs) || isOtherUserNote || ReportUtils.isSelfDM(report)) { return true; } diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index 93d582c391c5..531b5ba1db0b 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -178,7 +178,7 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu}: Fl const {canUseSpotnanaTravel, canUseCombinedTrackSubmit} = usePermissions(); const canSendInvoice = useMemo(() => PolicyUtils.canSendInvoice(allPolicies as OnyxCollection, session?.email), [allPolicies, session?.email]); - const isValidReport = !(isEmptyObject(quickActionReport) || ReportUtils.isArchivedRoom(quickActionReport, reportNameValuePairs)); + const isValidReport = !(isEmptyObject(quickActionReport) || ReportUtils.isArchivedRoom(reportNameValuePairs)); const {environment} = useEnvironment(); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const navatticURL = getNavatticURL(environment, introSelected?.choice); diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index 70a9c545dc8e..01fe6de51176 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -80,7 +80,7 @@ function IOURequestStepAmount({ return false; } - return !(ReportUtils.isArchivedRoom(report, reportNameValuePairs) || ReportUtils.isPolicyExpenseChat(report)); + return !(ReportUtils.isArchivedRoom(reportNameValuePairs) || ReportUtils.isPolicyExpenseChat(report)); }, [report, skipConfirmation, reportNameValuePairs]); useFocusEffect( @@ -169,7 +169,7 @@ function IOURequestStepAmount({ // In this case, the participants can be automatically assigned from the report and the user can skip the participants step and go straight // to the confirm step. // If the user is started this flow using the Create expense option (combined submit/track flow), they should be redirected to the participants page. - if (report?.reportID && !ReportUtils.isArchivedRoom(report, reportNameValuePairs) && iouType !== CONST.IOU.TYPE.CREATE) { + if (report?.reportID && !ReportUtils.isArchivedRoom(reportNameValuePairs) && iouType !== CONST.IOU.TYPE.CREATE) { const selectedParticipants = IOU.setMoneyRequestParticipantsFromReport(transactionID, report); const participants = selectedParticipants.map((participant) => { const participantAccountID = participant?.accountID ?? -1; diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index ae3ecff9adb2..6a2c65f70ecb 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -164,7 +164,7 @@ function IOURequestStepDistance({ return ( iouType !== CONST.IOU.TYPE.SPLIT && - !ReportUtils.isArchivedRoom(report, reportNameValuePairs) && + !ReportUtils.isArchivedRoom(reportNameValuePairs) && !(ReportUtils.isPolicyExpenseChat(report) && ((policy?.requiresCategory ?? false) || (policy?.requiresTag ?? false))) ); }, [report, skipConfirmation, policy, reportNameValuePairs, iouType]); @@ -296,7 +296,7 @@ function IOURequestStepDistance({ // In this case, the participants can be automatically assigned from the report and the user can skip the participants step and go straight // to the confirm step. // If the user started this flow using the Create expense option (combined submit/track flow), they should be redirected to the participants page. - if (report?.reportID && !ReportUtils.isArchivedRoom(report, reportNameValuePairs) && iouType !== CONST.IOU.TYPE.CREATE) { + if (report?.reportID && !ReportUtils.isArchivedRoom(reportNameValuePairs) && iouType !== CONST.IOU.TYPE.CREATE) { const selectedParticipants = IOU.setMoneyRequestParticipantsFromReport(transactionID, report); const participants = selectedParticipants.map((participant) => { const participantAccountID = participant?.accountID ?? -1; diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index 418474e117ed..ab94dcfda8b6 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -96,9 +96,7 @@ function IOURequestStepScan({ return false; } - return ( - !ReportUtils.isArchivedRoom(report, reportNameValuePairs) && !(ReportUtils.isPolicyExpenseChat(report) && ((policy?.requiresCategory ?? false) || (policy?.requiresTag ?? false))) - ); + return !ReportUtils.isArchivedRoom(reportNameValuePairs) && !(ReportUtils.isPolicyExpenseChat(report) && ((policy?.requiresCategory ?? false) || (policy?.requiresTag ?? false))); }, [report, skipConfirmation, policy, reportNameValuePairs]); const {translate} = useLocalize(); diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.tsx index 375936b05c1a..057afa009a30 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx @@ -107,9 +107,7 @@ function IOURequestStepScan({ return false; } - return ( - !ReportUtils.isArchivedRoom(report, reportNameValuePairs) && !(ReportUtils.isPolicyExpenseChat(report) && ((policy?.requiresCategory ?? false) || (policy?.requiresTag ?? false))) - ); + return !ReportUtils.isArchivedRoom(reportNameValuePairs) && !(ReportUtils.isPolicyExpenseChat(report) && ((policy?.requiresCategory ?? false) || (policy?.requiresTag ?? false))); }, [report, skipConfirmation, policy, reportNameValuePairs]); /** diff --git a/src/pages/settings/Report/NotificationPreferencePage.tsx b/src/pages/settings/Report/NotificationPreferencePage.tsx index 416d710d4966..448d2fd75866 100644 --- a/src/pages/settings/Report/NotificationPreferencePage.tsx +++ b/src/pages/settings/Report/NotificationPreferencePage.tsx @@ -28,7 +28,7 @@ function NotificationPreferencePage({report}: NotificationPreferencePageProps) { const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); const currentNotificationPreference = ReportUtils.getReportNotificationPreference(report); const shouldDisableNotificationPreferences = - ReportUtils.isArchivedRoom(report, reportNameValuePairs) || + ReportUtils.isArchivedRoom(reportNameValuePairs) || ReportUtils.isSelfDM(report) || (!isMoneyRequestReport && currentNotificationPreference === CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN); const notificationPreferenceOptions = Object.values(CONST.REPORT.NOTIFICATION_PREFERENCE) diff --git a/src/pages/settings/Report/ReportSettingsPage.tsx b/src/pages/settings/Report/ReportSettingsPage.tsx index c407788dce65..b5d56999358f 100644 --- a/src/pages/settings/Report/ReportSettingsPage.tsx +++ b/src/pages/settings/Report/ReportSettingsPage.tsx @@ -33,7 +33,7 @@ function ReportSettingsPage({report, policies, route}: ReportSettingsPageProps) const linkedWorkspace = useMemo(() => Object.values(policies ?? {}).find((policy) => policy && policy.id === report?.policyID), [policies, report?.policyID]); const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); - const shouldDisableSettings = isEmptyObject(report) || ReportUtils.isArchivedRoom(report, reportNameValuePairs) || ReportUtils.isSelfDM(report); + const shouldDisableSettings = isEmptyObject(report) || ReportUtils.isArchivedRoom(reportNameValuePairs) || ReportUtils.isSelfDM(report); const notificationPreferenceValue = ReportUtils.getReportNotificationPreference(report); const notificationPreference = notificationPreferenceValue && notificationPreferenceValue !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN diff --git a/src/pages/settings/Report/VisibilityPage.tsx b/src/pages/settings/Report/VisibilityPage.tsx index 07c05d1b8de1..ef180533d7a0 100644 --- a/src/pages/settings/Report/VisibilityPage.tsx +++ b/src/pages/settings/Report/VisibilityPage.tsx @@ -28,7 +28,7 @@ function VisibilityPage({report}: VisibilityProps) { const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID || -1}`); const shouldGoBackToDetailsPage = useRef(false); - const shouldDisableVisibility = ReportUtils.isArchivedRoom(report, reportNameValuePairs); + const shouldDisableVisibility = ReportUtils.isArchivedRoom(reportNameValuePairs); const {translate} = useLocalize(); const visibilityOptions = useMemo( From 37e4804c533dba1d8df9f7a1761900e78da1b7a3 Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Wed, 20 Nov 2024 19:28:07 -0500 Subject: [PATCH 011/311] fix tests --- tests/unit/ReportUtilsTest.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 2af8a0777c74..b9a5577a070f 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -249,7 +249,9 @@ describe('ReportUtils', () => { isOwnPolicyExpenseChat: true, }; - expect(ReportUtils.getReportName(memberArchivedPolicyExpenseChat)).toBe('Vikings Policy (archived)'); + Onyx.set(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS + memberArchivedPolicyExpenseChat.reportID, {private_isArchived: DateUtils.getDBTime()}).then(() => { + expect(ReportUtils.getReportName(memberArchivedPolicyExpenseChat)).toBe('Vikings Policy (archived)'); + }); return Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, CONST.LOCALES.ES).then(() => expect(ReportUtils.getReportName(memberArchivedPolicyExpenseChat)).toBe('Vikings Policy (archivado)'), From ecd47fa72f52e926e3765fe655955db7b75fd352 Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Fri, 22 Nov 2024 18:59:15 -0500 Subject: [PATCH 012/311] type collection --- src/types/onyx/ReportNameValuePairs.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/types/onyx/ReportNameValuePairs.ts b/src/types/onyx/ReportNameValuePairs.ts index 8b5a50d8d5c8..d8f18e5773f5 100644 --- a/src/types/onyx/ReportNameValuePairs.ts +++ b/src/types/onyx/ReportNameValuePairs.ts @@ -1,3 +1,5 @@ +import type ONYXKEYS from '@src/ONYXKEYS'; +import type CollectionDataSet from '@src/types/utils/CollectionDataSet'; import type * as OnyxCommon from './OnyxCommon'; /** Model of additional report details */ @@ -7,4 +9,9 @@ type ReportNameValuePairs = OnyxCommon.OnyxValueWithOfflineFeedback<{ private_isArchived: boolean; }>; +/** Collection of reportNameValuePairs, indexed by reportNameValuePairs_{reportID} */ +type ReportNameValuePairsCollectionDataSet = CollectionDataSet; + export default ReportNameValuePairs; + +export type {ReportNameValuePairsCollectionDataSet}; From 2d7af61b6f69afc19ae0eb5ae1126ad5ffefd1d8 Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Fri, 22 Nov 2024 20:55:31 -0500 Subject: [PATCH 013/311] optimistic and failure for deletePolicy --- src/libs/actions/Policy/Policy.ts | 19 ++++++++++++++----- src/types/onyx/ReportNameValuePairs.ts | 2 +- tests/unit/SidebarTest.ts | 11 +++++++++++ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 628e0918aa54..994961ab37d8 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -319,10 +319,15 @@ function deleteWorkspace(policyID: string, policyName: string) { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: { - stateNum: CONST.REPORT.STATE_NUM.APPROVED, - statusNum: CONST.REPORT.STATUS_NUM.CLOSED, oldPolicyName: allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.name ?? '', policyName: '', + }, + }); + + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`, + value: { // eslint-disable-next-line @typescript-eslint/naming-convention private_isArchived: currentTime, }, @@ -380,15 +385,19 @@ function deleteWorkspace(policyID: string, policyName: string) { ]; reportsToArchive.forEach((report) => { - const {reportID, stateNum, statusNum, oldPolicyName} = report ?? {}; + const {reportID, oldPolicyName} = report ?? {}; failureData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: { - stateNum, - statusNum, oldPolicyName, policyName: report?.policyName, + }, + }); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`, + value: { // eslint-disable-next-line @typescript-eslint/naming-convention private_isArchived: null, }, diff --git a/src/types/onyx/ReportNameValuePairs.ts b/src/types/onyx/ReportNameValuePairs.ts index d8f18e5773f5..925bbdf82b5e 100644 --- a/src/types/onyx/ReportNameValuePairs.ts +++ b/src/types/onyx/ReportNameValuePairs.ts @@ -6,7 +6,7 @@ import type * as OnyxCommon from './OnyxCommon'; type ReportNameValuePairs = OnyxCommon.OnyxValueWithOfflineFeedback<{ /** Whether the report is an archived room */ // eslint-disable-next-line @typescript-eslint/naming-convention - private_isArchived: boolean; + private_isArchived: string; }>; /** Collection of reportNameValuePairs, indexed by reportNameValuePairs_{reportID} */ diff --git a/tests/unit/SidebarTest.ts b/tests/unit/SidebarTest.ts index 2c8d28c537c6..15af96efc7ed 100644 --- a/tests/unit/SidebarTest.ts +++ b/tests/unit/SidebarTest.ts @@ -10,6 +10,7 @@ import * as LHNTestUtils from '../utils/LHNTestUtils'; import * as TestHelper from '../utils/TestHelper'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import wrapOnyxWithWaitForBatchedUpdates from '../utils/wrapOnyxWithWaitForBatchedUpdates'; +import { ReportNameValuePairsCollectionDataSet } from '@src/types/onyx/ReportNameValuePairs'; // Be sure to include the mocked Permissions and Expensicons libraries or else the beta tests won't work jest.mock('@src/libs/Permissions'); @@ -59,6 +60,11 @@ describe('Sidebar', () => { }, }; + const reportNameValuePairs = { + // eslint-disable-next-line @typescript-eslint/naming-convention + private_isArchived: DateUtils.getDBTime(), + }; + // Given the user is in all betas const betas = [CONST.BETAS.DEFAULT_ROOMS]; return ( @@ -74,6 +80,10 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]: {[action.reportActionID]: action}, } as ReportActionsCollectionDataSet; + const reportNameValuePairsCollection: ReportNameValuePairsCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`]: reportNameValuePairs, + }; + return Onyx.multiSet({ [ONYXKEYS.BETAS]: betas, [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, @@ -81,6 +91,7 @@ describe('Sidebar', () => { [ONYXKEYS.IS_LOADING_APP]: false, ...reportCollection, ...reportAction, + ...reportNameValuePairsCollection, }); }) .then(() => { From 32c275bf98297922e3b437efec2c71086dddc944 Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Tue, 26 Nov 2024 13:57:35 -0500 Subject: [PATCH 014/311] small fixes --- tests/unit/SidebarTest.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/unit/SidebarTest.ts b/tests/unit/SidebarTest.ts index 15af96efc7ed..50e67bc915a3 100644 --- a/tests/unit/SidebarTest.ts +++ b/tests/unit/SidebarTest.ts @@ -6,11 +6,11 @@ import * as Localize from '@src/libs/Localize'; import ONYXKEYS from '@src/ONYXKEYS'; import type {ReportCollectionDataSet} from '@src/types/onyx/Report'; import type {ReportActionsCollectionDataSet} from '@src/types/onyx/ReportAction'; +import type {ReportNameValuePairsCollectionDataSet} from '@src/types/onyx/ReportNameValuePairs'; import * as LHNTestUtils from '../utils/LHNTestUtils'; import * as TestHelper from '../utils/TestHelper'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import wrapOnyxWithWaitForBatchedUpdates from '../utils/wrapOnyxWithWaitForBatchedUpdates'; -import { ReportNameValuePairsCollectionDataSet } from '@src/types/onyx/ReportNameValuePairs'; // Be sure to include the mocked Permissions and Expensicons libraries or else the beta tests won't work jest.mock('@src/libs/Permissions'); @@ -45,10 +45,6 @@ describe('Sidebar', () => { const report = { ...LHNTestUtils.getFakeReport([1, 2], 3, true), chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM, - statusNum: CONST.REPORT.STATUS_NUM.CLOSED, - stateNum: CONST.REPORT.STATE_NUM.APPROVED, - // eslint-disable-next-line @typescript-eslint/naming-convention - private_isArchived: DateUtils.getDBTime(), lastMessageText: 'test', }; From cd37b5129065077111ef86012b5c44a84ea1e5a9 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Thu, 28 Nov 2024 14:22:17 +0700 Subject: [PATCH 015/311] fix lint --- src/components/LoadingBar.tsx | 64 +++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/src/components/LoadingBar.tsx b/src/components/LoadingBar.tsx index 6956887227bb..63b4c80a4a8b 100644 --- a/src/components/LoadingBar.tsx +++ b/src/components/LoadingBar.tsx @@ -17,57 +17,63 @@ function LoadingBar({shouldShow}: LoadingBarProps) { useEffect(() => { if (shouldShow) { // eslint-disable-next-line react-compiler/react-compiler - left.value = 0; - width.value = 0; - opacity.value = withTiming(1, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION}); + left.set(0); + width.set(0); + opacity.set(withTiming(1, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION})); - left.value = withDelay( - CONST.ANIMATED_PROGRESS_BAR_DELAY, - withRepeat( - withSequence( - withTiming(0, {duration: 0}), - withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), - withTiming(100, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + left.set( + withDelay( + CONST.ANIMATED_PROGRESS_BAR_DELAY, + withRepeat( + withSequence( + withTiming(0, {duration: 0}), + withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + withTiming(100, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + ), + -1, + false, ), - -1, - false, ), ); - width.value = withDelay( - CONST.ANIMATED_PROGRESS_BAR_DELAY, - withRepeat( - withSequence( - withTiming(0, {duration: 0}), - withTiming(100, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), - withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + width.set( + withDelay( + CONST.ANIMATED_PROGRESS_BAR_DELAY, + withRepeat( + withSequence( + withTiming(0, {duration: 0}), + withTiming(100, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + ), + -1, + false, ), - -1, - false, ), ); } else { - opacity.value = withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION}, () => { - cancelAnimation(left); - cancelAnimation(width); - }); + opacity.set( + withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION}, () => { + cancelAnimation(left); + cancelAnimation(width); + }), + ); } // we want to update only when shouldShow changes // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [shouldShow]); const animatedIndicatorStyle = useAnimatedStyle(() => ({ - left: `${left.value}%`, - width: `${width.value}%`, + left: `${left.get()}%`, + width: `${width.get()}%`, })); const animatedContainerStyle = useAnimatedStyle(() => ({ - opacity: opacity.value, + opacity: opacity.get(), })); return ( - {opacity.value > 0 ? : null} + {opacity.get() > 0 ? : null} ); } From 7e16699b8cffd0dd75c894f510b8bb7224968308 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Thu, 28 Nov 2024 14:26:35 +0700 Subject: [PATCH 016/311] fix border radius --- src/styles/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index 5cdbf4fa6ea0..111312d749c5 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -5325,7 +5325,7 @@ const styles = (theme: ThemeColors) => height: 2, width: '100%', backgroundColor: theme.border, - borderRadius: 5, + borderRadius: 2, overflow: 'hidden', }, From 4015f65a09831a14d688a9812069880954d7b1b1 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Fri, 29 Nov 2024 12:36:08 +0700 Subject: [PATCH 017/311] fix loading bar animation doesn't work --- src/components/LoadingBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/LoadingBar.tsx b/src/components/LoadingBar.tsx index 63b4c80a4a8b..e6d1ec0cd66d 100644 --- a/src/components/LoadingBar.tsx +++ b/src/components/LoadingBar.tsx @@ -73,7 +73,7 @@ function LoadingBar({shouldShow}: LoadingBarProps) { return ( - {opacity.get() > 0 ? : null} + ); } From 683e2c3636794e1cf782846c2732c15082673904 Mon Sep 17 00:00:00 2001 From: nkdengineer <161821005+nkdengineer@users.noreply.github.com> Date: Thu, 5 Dec 2024 11:55:32 +0700 Subject: [PATCH 018/311] Update src/pages/home/ReportScreen.tsx Co-authored-by: Getabalew <75031127+getusha@users.noreply.github.com> --- src/pages/home/ReportScreen.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 8e9bc187a92d..022a48db3f42 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -769,7 +769,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro needsOffscreenAlphaCompositing > {headerView} - {shouldUseNarrowLayout && !!isLoadingReportData && } + {shouldUseNarrowLayout && } {!!report && ReportUtils.isTaskReport(report) && shouldUseNarrowLayout && ReportUtils.isOpenTaskReport(report, parentReportAction) && ( From 1b1278748b6417baf61ab8943e439607c9b9cf33 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Thu, 5 Dec 2024 11:33:56 +0530 Subject: [PATCH 019/311] fix: Tweak UI for deleted message and expense. Signed-off-by: krishna2323 --- src/CONST.ts | 8 ++ .../BaseHTMLEngineProvider.tsx | 5 + .../HTMLRenderers/DeletedActionRenderer.tsx | 64 ++++++++++ .../HTMLEngineProvider/HTMLRenderers/index.ts | 2 + .../home/report/ReportActionItemFragment.tsx | 2 +- tests/ui/DemoTest.tsx | 120 ++++++++++++++++++ 6 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 src/components/HTMLEngineProvider/HTMLRenderers/DeletedActionRenderer.tsx create mode 100644 tests/ui/DemoTest.tsx diff --git a/src/CONST.ts b/src/CONST.ts index c28914541113..52ff37ca791b 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1184,6 +1184,14 @@ const CONST = { TEXT: 'TEXT', }, }, + ACTION_TYPE: { + DELETED_MESSAGE: 'deletedMessage', + DELETED_REPORT: 'deletedReport', + DELETED_EXPENSE: 'deletedExpense', + REVERSED_TRANSACTION: 'reversedTransaction', + DELETED_TASK: 'deletedTask', + HIDDEN_MESSAGE: 'hiddenMessage', + }, TYPE: { CHAT: 'chat', EXPENSE: 'expense', diff --git a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx index d211aac7fd4c..703835710e37 100755 --- a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx +++ b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx @@ -37,6 +37,11 @@ function BaseHTMLEngineProvider({textSelectable = false, children, enableExperim mixedUAStyles: {...styles.formError, ...styles.mb0}, contentModel: HTMLContentModel.block, }), + 'deleted-action': HTMLElementModel.fromCustomModel({ + tagName: 'alert-text', + mixedUAStyles: {...styles.formError, ...styles.mb0}, + contentModel: HTMLContentModel.block, + }), 'muted-text': HTMLElementModel.fromCustomModel({ tagName: 'muted-text', mixedUAStyles: {...styles.colorMuted, ...styles.mb0}, diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/DeletedActionRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/DeletedActionRenderer.tsx new file mode 100644 index 000000000000..55b628ac4cba --- /dev/null +++ b/src/components/HTMLEngineProvider/HTMLRenderers/DeletedActionRenderer.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import {View} from 'react-native'; +import type {CustomRendererProps, TPhrasing, TText} from 'react-native-render-html'; +import Icon from '@components/Icon'; +import * as Expensicons from '@components/Icon/Expensicons'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import CONST from '@src/CONST'; +import type {TranslationPaths} from '@src/languages/types'; + +function DeletedActionRenderer({tnode}: CustomRendererProps) { + const styles = useThemeStyles(); + const theme = useTheme(); + const {translate} = useLocalize(); + + const actionTypes = Object.values(CONST.REPORT.ACTION_TYPE); + const action = tnode.attributes.action; + let translation = action; + + if (actionTypes.some((e) => e === action)) { + translation = translate(`parentReportAction.${action}` as TranslationPaths); + } + + Object.values(CONST.REPORT.ACTION_TYPE); + + const getIcon = () => { + // This needs to be updated with new icons + switch (action) { + case CONST.REPORT.ACTION_TYPE.DELETED_MESSAGE: + return {icon: Expensicons.ReceiptSlash, width: 18, height: 18}; + case CONST.REPORT.ACTION_TYPE.DELETED_EXPENSE: + return {icon: Expensicons.ReceiptSlash, width: 18, height: 18}; + case CONST.REPORT.ACTION_TYPE.DELETED_REPORT: + return {icon: Expensicons.ReceiptSlash, width: 18, height: 18}; + case CONST.REPORT.ACTION_TYPE.DELETED_TASK: + return {icon: Expensicons.ReceiptSlash, width: 18, height: 18}; + case CONST.REPORT.ACTION_TYPE.HIDDEN_MESSAGE: + return {icon: Expensicons.ReceiptSlash, width: 18, height: 18}; + case CONST.REPORT.ACTION_TYPE.REVERSED_TRANSACTION: + return {icon: Expensicons.ReceiptSlash, width: 18, height: 18}; + default: + return {icon: Expensicons.ReceiptSlash, width: 18, height: 18}; + } + }; + + const icon = getIcon(); + return ( + + + {translation} + + ); +} + +DeletedActionRenderer.displayName = 'DeletedActionRenderer'; + +export default DeletedActionRenderer; diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/index.ts b/src/components/HTMLEngineProvider/HTMLRenderers/index.ts index ce24584048b0..91ed66f8b931 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/index.ts +++ b/src/components/HTMLEngineProvider/HTMLRenderers/index.ts @@ -1,6 +1,7 @@ import type {CustomTagRendererRecord} from 'react-native-render-html'; import AnchorRenderer from './AnchorRenderer'; import CodeRenderer from './CodeRenderer'; +import DeletedActionRenderer from './DeletedActionRenderer'; import EditedRenderer from './EditedRenderer'; import EmojiRenderer from './EmojiRenderer'; import ImageRenderer from './ImageRenderer'; @@ -30,6 +31,7 @@ const HTMLEngineProviderComponentList: CustomTagRendererRecord = { 'mention-here': MentionHereRenderer, emoji: EmojiRenderer, 'next-step-email': NextStepEmailRenderer, + 'deleted-action': DeletedActionRenderer, /* eslint-enable @typescript-eslint/naming-convention */ }; diff --git a/src/pages/home/report/ReportActionItemFragment.tsx b/src/pages/home/report/ReportActionItemFragment.tsx index 05cb657b1e54..b6e4a1e9f48e 100644 --- a/src/pages/home/report/ReportActionItemFragment.tsx +++ b/src/pages/home/report/ReportActionItemFragment.tsx @@ -106,7 +106,7 @@ function ReportActionItemFragment({ // immediately display "[Deleted message]" while the delete action is pending. if ((!isOffline && isThreadParentMessage && isPendingDelete) || fragment?.isDeletedParentAction) { - return ${translate('parentReportAction.deletedMessage')}`} />; + return `} />; } if (isThreadParentMessage && moderationDecision === CONST.MODERATION.MODERATOR_DECISION_PENDING_REMOVE) { diff --git a/tests/ui/DemoTest.tsx b/tests/ui/DemoTest.tsx new file mode 100644 index 000000000000..d05ca6a1524b --- /dev/null +++ b/tests/ui/DemoTest.tsx @@ -0,0 +1,120 @@ +import * as NativeNavigation from '@react-navigation/native'; +import {fireEvent, render, screen} from '@testing-library/react-native'; +import Onyx from 'react-native-onyx'; +import {act} from 'react-test-renderer'; +import * as Report from '@libs/actions/Report'; +import * as Localize from '@libs/Localize'; +import App from '@src/App'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {PolicyCollectionDataSet} from '@src/types/onyx/Policy'; +import type {ReportCollectionDataSet} from '@src/types/onyx/Report'; +import type {NativeNavigationMock} from '../../__mocks__/@react-navigation/native'; +import * as LHNTestUtils from '../utils/LHNTestUtils'; +import PusherHelper from '../utils/PusherHelper'; +import * as TestHelper from '../utils/TestHelper'; +import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; +import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct'; + +const USER_A_ACCOUNT_ID = 1; +const USER_A_EMAIL = 'user_a@test.com'; + +jest.setTimeout(60000); + +jest.mock('@react-navigation/native'); + +TestHelper.setupApp(); +TestHelper.setupGlobalFetchMock(); + +function navigateToSetting() { + const hintText = Localize.translateLocal('workspace.switcher.headerTitle'); + const mySettingButton = screen.queryByAccessibilityHint(hintText); + if (mySettingButton) { + fireEvent(mySettingButton, 'press'); + } + return waitForBatchedUpdatesWithAct(); +} + +function navigateToExpensifyClassicFlow() { + const hintText = Localize.translateLocal('exitSurvey.goToExpensifyClassic'); + const switchToExpensifyClassicBtn = screen.queryByAccessibilityHint('Workspace 1'); + expect(switchToExpensifyClassicBtn).toHaveLength(210); + if (switchToExpensifyClassicBtn) { + fireEvent(switchToExpensifyClassicBtn, 'press'); + } + return waitForBatchedUpdatesWithAct(); +} + +function signInAppAndEnterTestFlow(dismissedValue?: boolean): Promise { + render(); + return waitForBatchedUpdatesWithAct() + .then(async () => { + await waitForBatchedUpdatesWithAct(); + const hintText = Localize.translateLocal('loginForm.loginForm'); + const loginForm = screen.queryAllByLabelText(hintText); + expect(loginForm).toHaveLength(1); + + // Given three unread reports in the recently updated order of 3, 2, 1 + const report1 = LHNTestUtils.getFakeReport([1, 2], 3); + const report2 = LHNTestUtils.getFakeReport([1, 3], 2); + const report3 = LHNTestUtils.getFakeReport([1, 4], 1); + + const policy1 = LHNTestUtils.getFakePolicy('1', 'Workspace A'); + const policy2 = LHNTestUtils.getFakePolicy('2', 'B'); + const policy3 = LHNTestUtils.getFakePolicy('3', 'C'); + + // Each report has at least one ADD_COMMENT action so should be rendered in the LNH + Report.addComment(report1.reportID, 'Hi, this is a comment'); + Report.addComment(report2.reportID, 'Hi, this is a comment'); + Report.addComment(report3.reportID, 'Hi, this is a comment'); + + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, + [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, + [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + }; + + const policyCollectionDataSet: PolicyCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.POLICY}${policy1.id}`]: policy1, + [`${ONYXKEYS.COLLECTION.POLICY}${policy2.id}`]: policy2, + [`${ONYXKEYS.COLLECTION.POLICY}${policy3.id}`]: policy3, + }; + + await act(async () => { + await TestHelper.signInWithTestUser(USER_A_ACCOUNT_ID, USER_A_EMAIL, undefined, undefined, 'A'); + }); + await Onyx.multiSet({ + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.IS_LOADING_APP]: false, + ...reportCollectionDataSet, + ...policyCollectionDataSet, + }); + await waitForBatchedUpdates(); + return navigateToSetting(); + }) + .then(async () => { + await act(() => (NativeNavigation as NativeNavigationMock).triggerTransitionEnd()); + return navigateToExpensifyClassicFlow(); + }); +} + +describe('Switch to Expensify Classic flow', () => { + beforeEach(() => { + jest.clearAllMocks(); + Onyx.clear(); + + // Unsubscribe to pusher channels + PusherHelper.teardown(); + }); + + test('Should navigate to BookACall when dismissed is false', () => { + signInAppAndEnterTestFlow(false).then(() => { + expect(screen.getAllByText(Localize.translateLocal('exitSurvey.bookACallTitle')).at(0)).toBeOnTheScreen(); + }); + }); + + test('Should navigate to ConfirmPage when dismissed is true', () => { + signInAppAndEnterTestFlow(true).then(() => { + expect(screen.getAllByText(Localize.translateLocal('exitSurvey.goToExpensifyClassic')).at(0)).toBeOnTheScreen(); + }); + }); +}); From 5d71aee0a8a492210f3d71f2209e1f671f13399d Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Sun, 8 Dec 2024 23:38:24 -0500 Subject: [PATCH 020/311] fix errors --- tests/unit/DebugUtilsTest.ts | 2 +- tests/unit/ReportUtilsTest.ts | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/unit/DebugUtilsTest.ts b/tests/unit/DebugUtilsTest.ts index ac15d041bc2f..6cd9b13521c8 100644 --- a/tests/unit/DebugUtilsTest.ts +++ b/tests/unit/DebugUtilsTest.ts @@ -1062,7 +1062,7 @@ describe('DebugUtils', () => { ...MOCK_REPORTS, [`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}2` as const]: { // eslint-disable-next-line @typescript-eslint/naming-convention - private_isArchived: false, + private_isArchived: DateUtils.getDBTime(), }, [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1` as const]: MOCK_REPORT_ACTIONS, [`${ONYXKEYS.COLLECTION.POLICY}1` as const]: { diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 6f7c108be16f..b1a28aa3020f 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -250,10 +250,6 @@ describe('ReportUtils', () => { isOwnPolicyExpenseChat: true, }; - Onyx.set(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS + memberArchivedPolicyExpenseChat.reportID, {private_isArchived: DateUtils.getDBTime()}).then(() => { - expect(ReportUtils.getReportName(memberArchivedPolicyExpenseChat)).toBe('Vikings Policy (archived)'); - }); - return Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, CONST.LOCALES.ES).then(() => expect(ReportUtils.getReportName(memberArchivedPolicyExpenseChat)).toBe('Vikings Policy (archivado)'), ); @@ -988,7 +984,7 @@ describe('ReportUtils', () => { }; const reportNameValuePairs = { type: 'chat', - private_isArchived: true, + private_isArchived: DateUtils.getDBTime(), }; // Get the quick action detail From 8c93e83948c1968346b1c605b2cc016d20c82780 Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Mon, 9 Dec 2024 00:58:54 -0500 Subject: [PATCH 021/311] unsafe --- tests/unit/DebugUtilsTest.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit/DebugUtilsTest.ts b/tests/unit/DebugUtilsTest.ts index 6cd9b13521c8..3fd9bdf7735b 100644 --- a/tests/unit/DebugUtilsTest.ts +++ b/tests/unit/DebugUtilsTest.ts @@ -1,4 +1,5 @@ import Onyx from 'react-native-onyx'; +import DateUtils from '@libs/DateUtils'; import type {ObjectType} from '@libs/DebugUtils'; import DebugUtils from '@libs/DebugUtils'; import CONST from '@src/CONST'; From bf4660ceff559f426b5e3d807fe763cc5fcbb6dd Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 10 Dec 2024 15:53:42 +0700 Subject: [PATCH 022/311] remove initial value --- src/pages/home/ReportScreen.tsx | 2 +- src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index c4a7952b4b67..81079f52d045 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -137,7 +137,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro const finishedLoadingApp = wasLoadingApp && !isLoadingApp; const isDeletedParentAction = ReportActionsUtils.isDeletedParentAction(parentReportAction); const prevIsDeletedParentAction = usePrevious(isDeletedParentAction); - const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA, {initialValue: true}); + const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA); const isLoadingReportOnyx = isLoadingOnyxValue(reportResult); const permissions = useDeepCompareRef(reportOnyx?.permissions); diff --git a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx index 6ab3fd109217..8db6ba294908 100644 --- a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx +++ b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx @@ -23,7 +23,7 @@ function BaseSidebarScreen() { const {translate} = useLocalize(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const [activeWorkspace] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activeWorkspaceID ?? -1}`); - const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA, {initialValue: true}); + const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA); useEffect(() => { Performance.markStart(CONST.TIMING.SIDEBAR_LOADED); Timing.start(CONST.TIMING.SIDEBAR_LOADED); From 22f1ebd454b3e2f871f5d26250d3ec6a148733ff Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Mon, 16 Dec 2024 09:41:24 +0530 Subject: [PATCH 023/311] fix ts errors --- src/pages/home/report/PureReportActionItem.tsx | 3 +-- tests/unit/ReportUtilsTest.ts | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pages/home/report/PureReportActionItem.tsx b/src/pages/home/report/PureReportActionItem.tsx index 34dd2a9d1350..6686ce7e0003 100644 --- a/src/pages/home/report/PureReportActionItem.tsx +++ b/src/pages/home/report/PureReportActionItem.tsx @@ -903,8 +903,7 @@ function PureReportActionItem({ index={index} ref={textInputRef} shouldDisableEmojiPicker={ - (ReportUtils.chatIncludesConcierge(report) && User.isBlockedFromConcierge(blockedFromConcierge)) || - ReportUtils.isArchivedRoom(report, reportNameValuePairs) + (ReportUtils.chatIncludesConcierge(report) && User.isBlockedFromConcierge(blockedFromConcierge)) || ReportUtils.isArchivedRoom(reportNameValuePairs) } isGroupPolicyReport={!!report?.policyID && report.policyID !== CONST.POLICY.ID_FAKE} /> diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 746515916ec7..ba1d851a85dd 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -1339,7 +1339,7 @@ describe('ReportUtils', () => { }; const reportNameValuePairs = { type: 'chat', - private_isArchived: true, + private_isArchived: DateUtils.getDBTime(), }; const currentReportId = '3'; const isInFocusMode = false; @@ -1368,7 +1368,7 @@ describe('ReportUtils', () => { }; const reportNameValuePairs = { type: 'chat', - private_isArchived: true, + private_isArchived: DateUtils.getDBTime(), }; const currentReportId = '3'; const isInFocusMode = true; From 764dd81179da3c105df15a475681332bda6461b9 Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Mon, 16 Dec 2024 11:02:21 +0530 Subject: [PATCH 024/311] typecheck again --- tests/ui/LHNItemsPresence.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/LHNItemsPresence.tsx b/tests/ui/LHNItemsPresence.tsx index b7e14bb5bd82..629dbdeb0fca 100644 --- a/tests/ui/LHNItemsPresence.tsx +++ b/tests/ui/LHNItemsPresence.tsx @@ -261,7 +261,7 @@ describe('SidebarLinksData', () => { const reportNameValuePairs = { type: 'chat', // eslint-disable-next-line @typescript-eslint/naming-convention - private_isArchived: true, + private_isArchived: DateUtils.getDBTime(), }; await initializeState({ From e0d6894c6bc2e57089838ba0f17734265e15e5fb Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Mon, 16 Dec 2024 11:24:38 +0530 Subject: [PATCH 025/311] try optionData --- src/libs/OptionsListUtils.ts | 4 +++- src/libs/SidebarUtils.ts | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index a7f738790f92..ecb8c972111d 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -490,7 +490,9 @@ function getLastMessageTextForReport(report: OnyxEntry, lastActorDetails const lastOriginalReportAction = lastReportActions[reportID] ?? null; let lastMessageTextFromReport = ''; - if (report?.private_isArchived) { + const reportNameValuePairs = ReportUtils.getReportNameValuePairs(reportID); + + if (reportNameValuePairs?.private_isArchived) { const archiveReason = // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing (ReportActionUtils.isClosedAction(lastOriginalReportAction) && ReportActionUtils.getOriginalMessage(lastOriginalReportAction)?.reason) || CONST.REPORT.ARCHIVE_REASON.DEFAULT; diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index d41695705530..d7e819209239 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -348,13 +348,15 @@ function getOptionData({ const participantPersonalDetailList = Object.values(OptionsListUtils.getPersonalDetailsForAccountIDs(participantAccountIDs, personalDetails)); const personalDetail = participantPersonalDetailList.at(0) ?? ({} as PersonalDetails); + const reportNameValuePairs = ReportUtils.getReportNameValuePairs(report.reportID); + result.isThread = ReportUtils.isChatThread(report); result.isChatRoom = ReportUtils.isChatRoom(report); result.isTaskReport = ReportUtils.isTaskReport(report); result.isInvoiceReport = ReportUtils.isInvoiceReport(report); result.parentReportAction = parentReportAction; // eslint-disable-next-line @typescript-eslint/naming-convention - result.private_isArchived = report?.private_isArchived; + result.private_isArchived = reportNameValuePairs?.private_isArchived; result.isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(report); result.isExpenseRequest = ReportUtils.isExpenseRequest(report); result.isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); From 92616dd72da4567d1e88b8b6381e6ef6f3950ecf Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Mon, 16 Dec 2024 11:45:20 +0530 Subject: [PATCH 026/311] more private_isArchived fixes --- src/libs/OptionsListUtils.ts | 2 +- src/libs/ReportUtils.ts | 2 +- src/libs/SidebarUtils.ts | 2 +- src/types/onyx/Report.ts | 4 ---- src/types/onyx/ReportNameValuePairs.ts | 2 +- tests/ui/LHNItemsPresence.tsx | 1 - tests/unit/DebugUtilsTest.ts | 2 -- tests/unit/ReportUtilsTest.ts | 3 --- tests/unit/SidebarOrderTest.ts | 1 - 9 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index ecb8c972111d..29976065b562 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -653,7 +653,7 @@ function createOption( result.isChatRoom = ReportUtils.isChatRoom(report); result.isDefaultRoom = ReportUtils.isDefaultRoom(report); // eslint-disable-next-line @typescript-eslint/naming-convention - result.private_isArchived = report.private_isArchived; + result.private_isArchived = ReportUtils.getReportNameValuePairs(report.reportID)?.private_isArchived; result.isExpenseReport = ReportUtils.isExpenseReport(report); result.isInvoiceRoom = ReportUtils.isInvoiceRoom(report); result.isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 6cdbe7a6f314..d8eb0a35dca4 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -531,7 +531,7 @@ type OptionData = { icons?: Icon[]; iouReportAmount?: number; displayName?: string; -} & Report; +} & Report & ReportNameValuePairs; type OnyxDataTaskAssigneeChat = { optimisticData: OnyxUpdate[]; diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index d7e819209239..3aa936aa69f0 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -613,7 +613,7 @@ function getRoomWelcomeMessage(report: OnyxEntry): WelcomeMessage { return welcomeMessage; } - if (report?.private_isArchived) { + if (ReportUtils.isArchivedRoomWithID(report?.reportID)) { welcomeMessage.phrase1 = Localize.translateLocal('reportActionsView.beginningOfArchivedRoomPartOne'); welcomeMessage.phrase2 = Localize.translateLocal('reportActionsView.beginningOfArchivedRoomPartTwo'); } else if (ReportUtils.isDomainRoom(report)) { diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index b89b6c8e0777..34b01191adb9 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -231,10 +231,6 @@ type Report = OnyxCommon.OnyxValueWithOfflineFeedback< /** The trip ID in spotnana */ tripID: string; }; - - /** Whether the report is archived */ - // eslint-disable-next-line @typescript-eslint/naming-convention - private_isArchived?: string; }, 'addWorkspaceRoom' | 'avatar' | 'createChat' | 'partial' | 'reimbursed' | 'preview' >; diff --git a/src/types/onyx/ReportNameValuePairs.ts b/src/types/onyx/ReportNameValuePairs.ts index 925bbdf82b5e..1927d36d48e7 100644 --- a/src/types/onyx/ReportNameValuePairs.ts +++ b/src/types/onyx/ReportNameValuePairs.ts @@ -6,7 +6,7 @@ import type * as OnyxCommon from './OnyxCommon'; type ReportNameValuePairs = OnyxCommon.OnyxValueWithOfflineFeedback<{ /** Whether the report is an archived room */ // eslint-disable-next-line @typescript-eslint/naming-convention - private_isArchived: string; + private_isArchived?: string; }>; /** Collection of reportNameValuePairs, indexed by reportNameValuePairs_{reportID} */ diff --git a/tests/ui/LHNItemsPresence.tsx b/tests/ui/LHNItemsPresence.tsx index 629dbdeb0fca..fa4cb6dbf52b 100644 --- a/tests/ui/LHNItemsPresence.tsx +++ b/tests/ui/LHNItemsPresence.tsx @@ -256,7 +256,6 @@ describe('SidebarLinksData', () => { const archivedReport: Report = { ...createReport(false), // eslint-disable-next-line @typescript-eslint/naming-convention - private_isArchived: DateUtils.getDBTime(), }; const reportNameValuePairs = { type: 'chat', diff --git a/tests/unit/DebugUtilsTest.ts b/tests/unit/DebugUtilsTest.ts index 3fd9bdf7735b..dbb746b29089 100644 --- a/tests/unit/DebugUtilsTest.ts +++ b/tests/unit/DebugUtilsTest.ts @@ -788,8 +788,6 @@ describe('DebugUtils', () => { await Onyx.set(ONYXKEYS.NVP_PRIORITY_MODE, CONST.PRIORITY_MODE.DEFAULT); const reason = DebugUtils.getReasonForShowingRowInLHN({ ...baseReport, - // eslint-disable-next-line @typescript-eslint/naming-convention - private_isArchived: 'true', }); expect(reason).toBe('debug.reasonVisibleInLHN.isArchived'); }); diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index ba1d851a85dd..ce672f4a04f9 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -999,7 +999,6 @@ describe('ReportUtils', () => { const archivedReport: Report = { ...LHNTestUtils.getFakeReport(), reportID: '1', - private_isArchived: DateUtils.getDBTime(), }; const reportNameValuePairs = { type: 'chat', @@ -1335,7 +1334,6 @@ describe('ReportUtils', () => { const archivedReport: Report = { ...LHNTestUtils.getFakeReport(), reportID: '1', - private_isArchived: DateUtils.getDBTime(), }; const reportNameValuePairs = { type: 'chat', @@ -1364,7 +1362,6 @@ describe('ReportUtils', () => { const archivedReport: Report = { ...LHNTestUtils.getFakeReport(), reportID: '1', - private_isArchived: DateUtils.getDBTime(), }; const reportNameValuePairs = { type: 'chat', diff --git a/tests/unit/SidebarOrderTest.ts b/tests/unit/SidebarOrderTest.ts index 1de2654c09bf..61e6871d4151 100644 --- a/tests/unit/SidebarOrderTest.ts +++ b/tests/unit/SidebarOrderTest.ts @@ -832,7 +832,6 @@ describe('Sidebar', () => { statusNum: CONST.REPORT.STATUS_NUM.CLOSED, stateNum: CONST.REPORT.STATE_NUM.APPROVED, // eslint-disable-next-line @typescript-eslint/naming-convention - private_isArchived: DateUtils.getDBTime(), }; const report2 = LHNTestUtils.getFakeReport([1, 3]); const report3 = LHNTestUtils.getFakeReport([1, 4]); From c8140e035679806fcc660c7a6c00968c8b277efb Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Mon, 16 Dec 2024 12:11:21 +0530 Subject: [PATCH 027/311] remove private_isArchived from report type --- src/libs/DebugUtils.ts | 8 ++++---- src/pages/home/ReportScreen.tsx | 5 +++-- tests/unit/OptionsListUtilsTest.ts | 1 - 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libs/DebugUtils.ts b/src/libs/DebugUtils.ts index 9343164db1e8..6f0f85e6ee53 100644 --- a/src/libs/DebugUtils.ts +++ b/src/libs/DebugUtils.ts @@ -8,7 +8,7 @@ import type {TupleToUnion} from 'type-fest'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Beta, Policy, Report, ReportAction, ReportActions, Transaction, TransactionViolation} from '@src/types/onyx'; +import type {Beta, Policy, Report, ReportAction, ReportActions, ReportNameValuePairs, Transaction, TransactionViolation} from '@src/types/onyx'; import * as ReportActionsUtils from './ReportActionsUtils'; import * as ReportUtils from './ReportUtils'; import SidebarUtils from './SidebarUtils'; @@ -60,7 +60,7 @@ type ObjectElement = ['reportID'] satisfies Array; +const REPORT_REQUIRED_PROPERTIES: Array = ['reportID'] satisfies Array; const REPORT_ACTION_REQUIRED_PROPERTIES: Array = ['reportActionID', 'created', 'actionName'] satisfies Array; @@ -441,7 +441,7 @@ function unionValidation(firstValidation: () => void, secondValidation: () => vo * @param key - property key * @param value - value provided by the user */ -function validateReportDraftProperty(key: keyof Report, value: string) { +function validateReportDraftProperty(key: keyof Report | keyof ReportNameValuePairs, value: string) { if (REPORT_REQUIRED_PROPERTIES.includes(key) && isEmptyValue(value)) { throw SyntaxError('debug.missingValue'); } @@ -570,7 +570,7 @@ function validateReportDraftProperty(key: keyof Report, value: string) { case 'pendingAction': return validateConstantEnum(value, CONST.RED_BRICK_ROAD_PENDING_ACTION); case 'pendingFields': - return validateObject>(value, { + return validateObject>(value, { description: CONST.RED_BRICK_ROAD_PENDING_ACTION, privateNotes: CONST.RED_BRICK_ROAD_PENDING_ACTION, currency: CONST.RED_BRICK_ROAD_PENDING_ACTION, diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 47315b604f0b..1c65dfddac8d 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -121,6 +121,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro const [accountManagerReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${accountManagerReportID || '-1'}`); const [userLeavingStatus] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportIDFromRoute}`, {initialValue: false}); const [reportOnyx, reportResult] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportIDFromRoute}`, {allowStaleData: true}); + const [reportNameValuePairsOnyx, reportNameValuePairsResult] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportIDFromRoute}`, {allowStaleData: true}); const [reportMetadata = defaultReportMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportIDFromRoute}`, {initialValue: defaultReportMetadata}); const [isSidebarLoaded] = useOnyx(ONYXKEYS.IS_SIDEBAR_LOADED, {initialValue: false}); const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {allowStaleData: true, initialValue: {}}); @@ -222,7 +223,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro oldPolicyName: reportOnyx.oldPolicyName, policyName: reportOnyx.policyName, // eslint-disable-next-line @typescript-eslint/naming-convention - private_isArchived: reportOnyx.private_isArchived, + private_isArchived: reportNameValuePairsOnyx?.private_isArchived, lastMentionedTime: reportOnyx.lastMentionedTime, avatarUrl: reportOnyx.avatarUrl, permissions, @@ -230,7 +231,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro policyAvatar: reportOnyx.policyAvatar, pendingChatMembers: reportOnyx.pendingChatMembers, }, - [reportOnyx, permissions], + [reportOnyx, reportNameValuePairsOnyx, permissions], ); const reportID = report?.reportID; diff --git a/tests/unit/OptionsListUtilsTest.ts b/tests/unit/OptionsListUtilsTest.ts index 8916b7c3bac8..bf7c511c2041 100644 --- a/tests/unit/OptionsListUtilsTest.ts +++ b/tests/unit/OptionsListUtilsTest.ts @@ -153,7 +153,6 @@ describe('OptionsListUtils', () => { stateNum: 2, statusNum: 2, // eslint-disable-next-line @typescript-eslint/naming-convention - private_isArchived: DateUtils.getDBTime(), }, }; From 85ec2852bd773b348d11842c00a81859cbcaf65a Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Mon, 16 Dec 2024 12:15:45 +0530 Subject: [PATCH 028/311] fix lint --- src/libs/ReportUtils.ts | 3 ++- src/pages/home/ReportScreen.tsx | 2 +- tests/unit/OptionsListUtilsTest.ts | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index d8eb0a35dca4..a8dcac7b080f 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -531,7 +531,8 @@ type OptionData = { icons?: Icon[]; iouReportAmount?: number; displayName?: string; -} & Report & ReportNameValuePairs; +} & Report & + ReportNameValuePairs; type OnyxDataTaskAssigneeChat = { optimisticData: OnyxUpdate[]; diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 1c65dfddac8d..067ae4fc2007 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -121,7 +121,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro const [accountManagerReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${accountManagerReportID || '-1'}`); const [userLeavingStatus] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportIDFromRoute}`, {initialValue: false}); const [reportOnyx, reportResult] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportIDFromRoute}`, {allowStaleData: true}); - const [reportNameValuePairsOnyx, reportNameValuePairsResult] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportIDFromRoute}`, {allowStaleData: true}); + const [reportNameValuePairsOnyx] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportIDFromRoute}`, {allowStaleData: true}); const [reportMetadata = defaultReportMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportIDFromRoute}`, {initialValue: defaultReportMetadata}); const [isSidebarLoaded] = useOnyx(ONYXKEYS.IS_SIDEBAR_LOADED, {initialValue: false}); const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {allowStaleData: true, initialValue: {}}); diff --git a/tests/unit/OptionsListUtilsTest.ts b/tests/unit/OptionsListUtilsTest.ts index bf7c511c2041..43b0522b4d84 100644 --- a/tests/unit/OptionsListUtilsTest.ts +++ b/tests/unit/OptionsListUtilsTest.ts @@ -1,7 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ import type {OnyxCollection} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; -import DateUtils from '@libs/DateUtils'; import CONST from '@src/CONST'; import * as OptionsListUtils from '@src/libs/OptionsListUtils'; import * as ReportUtils from '@src/libs/ReportUtils'; From a1c089c76ae0ed534869a94233792a21f1919629 Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Mon, 16 Dec 2024 12:17:34 +0530 Subject: [PATCH 029/311] add parantheses for lint --- src/libs/actions/Policy/Policy.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 110067de68f1..479b32bce023 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -4144,7 +4144,7 @@ function setPolicyAutomaticApprovalLimit(policyID: string, limit: string) { const fallbackLimit = limit === '' ? '0' : limit; const parsedLimit = CurrencyUtils.convertToBackendAmount(parseFloat(fallbackLimit)); - if (parsedLimit === policy?.autoApproval?.limit ?? CONST.POLICY.AUTO_APPROVE_REPORTS_UNDER_DEFAULT_CENTS) { + if (parsedLimit === (policy?.autoApproval?.limit ?? CONST.POLICY.AUTO_APPROVE_REPORTS_UNDER_DEFAULT_CENTS)) { return; } @@ -4364,7 +4364,7 @@ function setPolicyAutoReimbursementLimit(policyID: string, limit: string) { const fallbackLimit = limit === '' ? '0' : limit; const parsedLimit = CurrencyUtils.convertToBackendAmount(parseFloat(fallbackLimit)); - if (parsedLimit === policy?.autoReimbursement?.limit ?? CONST.POLICY.AUTO_REIMBURSEMENT_DEFAULT_LIMIT_CENTS) { + if (parsedLimit === (policy?.autoReimbursement?.limit ?? CONST.POLICY.AUTO_REIMBURSEMENT_DEFAULT_LIMIT_CENTS)) { return; } From 5b43c6fe371c92e75542106551e6f3f8086e0b1d Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Mon, 16 Dec 2024 15:02:58 +0530 Subject: [PATCH 030/311] option data changes --- src/components/LHNOptionsList/LHNOptionsList.tsx | 3 +++ src/components/LHNOptionsList/OptionRowLHNData.tsx | 3 +++ src/components/LHNOptionsList/types.ts | 5 ++++- src/libs/ReportUtils.ts | 2 +- src/libs/SidebarUtils.ts | 6 +++--- 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index 9594e6ede24a..c62914c03055 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -36,6 +36,7 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio const route = useRoute(); const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); + const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS); const [reportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS); const [policy] = useOnyx(ONYXKEYS.COLLECTION.POLICY); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); @@ -118,6 +119,7 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio ({item: reportID}: RenderItemProps): ReactElement => { const itemFullReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; const itemParentReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${itemFullReport?.parentReportID ?? '-1'}`]; + const itemReportNameValuePairs = reportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`]; const itemReportActions = reportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`]; const itemParentReportActions = reportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${itemFullReport?.parentReportID}`]; const itemParentReportAction = itemParentReportActions?.[itemFullReport?.parentReportActionID ?? '-1']; @@ -171,6 +173,7 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio ; @@ -47,6 +47,9 @@ type OptionRowLHNDataProps = { /** The full data of the report */ fullReport: OnyxEntry; + /** Array of report name value pairs for this report */ + reportNameValuePairs: OnyxEntry; + /** The policy which the user has access to and which the report could be tied to */ policy?: OnyxEntry; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index a8dcac7b080f..e4929fa03c8a 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2805,7 +2805,7 @@ function getReasonAndReportActionThatRequiresAttention( }; } - if (isArchivedRoom(getReportNameValuePairs(optionOrReport?.reportID)) || isArchivedRoom(getReportNameValuePairs(optionOrReport?.reportID))) { + if (isArchivedRoom(getReportNameValuePairs(optionOrReport?.reportID)) || isArchivedRoom(getReportNameValuePairs(optionOrReport?.parentReportID))) { return null; } diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 3aa936aa69f0..0dcd579b8fab 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -5,7 +5,7 @@ import type {ValueOf} from 'type-fest'; import type {PolicySelector} from '@hooks/useReportIDs'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {PersonalDetails, PersonalDetailsList, ReportActions, TransactionViolation} from '@src/types/onyx'; +import type {PersonalDetails, PersonalDetailsList, ReportActions, ReportNameValuePairs, TransactionViolation} from '@src/types/onyx'; import type Beta from '@src/types/onyx/Beta'; import type Policy from '@src/types/onyx/Policy'; import type PriorityMode from '@src/types/onyx/PriorityMode'; @@ -280,6 +280,7 @@ function shouldShowRedBrickRoad(report: Report, reportActions: OnyxEntry; + reportNameValuePairs: OnyxEntry; reportActions: OnyxEntry; personalDetails: OnyxEntry; preferredLocale: DeepValueOf; @@ -348,8 +350,6 @@ function getOptionData({ const participantPersonalDetailList = Object.values(OptionsListUtils.getPersonalDetailsForAccountIDs(participantAccountIDs, personalDetails)); const personalDetail = participantPersonalDetailList.at(0) ?? ({} as PersonalDetails); - const reportNameValuePairs = ReportUtils.getReportNameValuePairs(report.reportID); - result.isThread = ReportUtils.isChatThread(report); result.isChatRoom = ReportUtils.isChatRoom(report); result.isTaskReport = ReportUtils.isTaskReport(report); From e30dd02a8a932b1eae23bda8f840df82543e70a9 Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Mon, 16 Dec 2024 15:19:47 +0530 Subject: [PATCH 031/311] add deps --- src/components/LHNOptionsList/LHNOptionsList.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index c62914c03055..6c5c856443c4 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -203,6 +203,7 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio preferredLocale, reportActions, reports, + reportNameValuePairs, shouldDisableFocusOptions, transactions, transactionViolations, @@ -211,8 +212,8 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio ); const extraData = useMemo( - () => [reportActions, reports, transactionViolations, policy, personalDetails, data.length, draftComments, optionMode, preferredLocale], - [reportActions, reports, transactionViolations, policy, personalDetails, data.length, draftComments, optionMode, preferredLocale], + () => [reportActions, reports, reportNameValuePairs, transactionViolations, policy, personalDetails, data.length, draftComments, optionMode, preferredLocale], + [reportActions, reports, reportNameValuePairs, transactionViolations, policy, personalDetails, data.length, draftComments, optionMode, preferredLocale], ); const previousOptionMode = usePrevious(optionMode); From a9f7912905e5845a7f9e9dbbd66a4c36105a2e31 Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Mon, 16 Dec 2024 15:25:26 +0530 Subject: [PATCH 032/311] fix perf tests --- tests/perf-test/SidebarUtils.perf-test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/perf-test/SidebarUtils.perf-test.ts b/tests/perf-test/SidebarUtils.perf-test.ts index 4ea4e1d04b50..c1b7dd337fa4 100644 --- a/tests/perf-test/SidebarUtils.perf-test.ts +++ b/tests/perf-test/SidebarUtils.perf-test.ts @@ -76,12 +76,14 @@ describe('SidebarUtils', () => { const preferredLocale = 'en'; const policy = createRandomPolicy(1); const parentReportAction = createRandomReportAction(1); + const reportNameValuePairs = {}; await waitForBatchedUpdates(); await measureFunction(() => SidebarUtils.getOptionData({ report, + reportNameValuePairs, reportActions, personalDetails, preferredLocale, From 06e9143f9ece329805f81509cb5347f959df9595 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 18 Dec 2024 13:53:05 +0800 Subject: [PATCH 033/311] clear the pending action when fails --- src/libs/actions/Policy/Policy.ts | 1 + tests/actions/PolicyTest.ts | 70 ++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index f81539d1e921..23dc7d327879 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -396,6 +396,7 @@ function deleteWorkspace(policyID: string, policyName: string) { key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { avatarURL: policy?.avatarURL, + pendingAction: null, }, }, ]; diff --git a/tests/actions/PolicyTest.ts b/tests/actions/PolicyTest.ts index 0bcd22e05bb7..25a74c19a56f 100644 --- a/tests/actions/PolicyTest.ts +++ b/tests/actions/PolicyTest.ts @@ -10,6 +10,7 @@ import createRandomPolicy from '../utils/collections/policies'; import * as TestHelper from '../utils/TestHelper'; import type {MockFetch} from '../utils/TestHelper'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; +import createRandomReport from '../utils/collections/reports'; const ESH_EMAIL = 'eshgupta1217@gmail.com'; const ESH_ACCOUNT_ID = 1; @@ -24,9 +25,11 @@ describe('actions/Policy', () => { keys: ONYXKEYS, }); }); - + + let mockFetch: MockFetch; beforeEach(() => { global.fetch = TestHelper.getGlobalFetchMock(); + mockFetch = fetch as MockFetch; return Onyx.clear().then(waitForBatchedUpdates); }); @@ -190,4 +193,69 @@ describe('actions/Policy', () => { }); }); }); + + describe('deleteWorkspace', () => { + it('should apply failure data when deleteWorkspace fails', async () => { + // Given a policy + const fakePolicy = createRandomPolicy(0); + const fakeReport = { + ...createRandomReport(0), + stateNum: CONST.REPORT.STATE_NUM.OPEN, + statusNum: CONST.REPORT.STATUS_NUM.OPEN, + policyName: fakePolicy.name, + }; + const fakeReimbursementAccount = {errors: {}}; + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`, fakePolicy); + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${fakeReport.reportID}`, fakeReport); + await Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, fakeReimbursementAccount); + + // When deleting a workspace fails + mockFetch?.fail?.(); + Policy.deleteWorkspace(fakePolicy.id, fakePolicy.name); + + await waitForBatchedUpdates(); + + // Then it should apply the correct failure data + await new Promise((resolve) => { + const connection = Onyx.connect({ + key: `${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`, + callback: (policy) => { + Onyx.disconnect(connection); + expect(policy?.pendingAction).toBeUndefined(); + expect(policy?.avatarURL).toBe(fakePolicy.avatarURL); + resolve(); + }, + }); + }); + + // Unarchive the report + await new Promise((resolve) => { + const connection = Onyx.connect({ + key: `${ONYXKEYS.COLLECTION.REPORT}${fakeReport.reportID}`, + callback: (report) => { + Onyx.disconnect(connection); + expect(report?.stateNum).toBe(fakeReport.stateNum); + expect(report?.statusNum).toBe(fakeReport.statusNum); + expect(report?.policyName).toBe(fakeReport.policyName); + expect(report?.oldPolicyName).toBeUndefined(); + expect(report?.private_isArchived).toBeUndefined(); + resolve(); + }, + }); + }); + + // Restore the reimbursement account errors + await new Promise((resolve) => { + const connection = Onyx.connect({ + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + callback: (reimbursementAccount) => { + Onyx.disconnect(connection); + console.log(reimbursementAccount) + expect(reimbursementAccount?.errors).not.toBeUndefined(); + resolve(); + }, + }); + }); + }); + }); }); From fa1c94a7a768ed1592239a1f3f5b1d2d1149a40f Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 18 Dec 2024 14:17:02 +0800 Subject: [PATCH 034/311] lint --- .../CreateWorkspaceFromIOUPaymentParams.ts | 2 +- src/libs/actions/Policy/Policy.ts | 85 ++++++++++--------- tests/actions/PolicyTest.ts | 6 +- 3 files changed, 48 insertions(+), 45 deletions(-) diff --git a/src/libs/API/parameters/CreateWorkspaceFromIOUPaymentParams.ts b/src/libs/API/parameters/CreateWorkspaceFromIOUPaymentParams.ts index a1256f5ad051..35be0440c5c4 100644 --- a/src/libs/API/parameters/CreateWorkspaceFromIOUPaymentParams.ts +++ b/src/libs/API/parameters/CreateWorkspaceFromIOUPaymentParams.ts @@ -12,7 +12,7 @@ type CreateWorkspaceFromIOUPaymentParams = { customUnitRateID: string; iouReportID: string; memberData: string; - reportActionID: string; + reportActionID: string | undefined; }; export default CreateWorkspaceFromIOUPaymentParams; diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 23dc7d327879..6b1ebc209f63 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -196,7 +196,7 @@ Onyx.connect({ key: ONYXKEYS.SESSION, callback: (val) => { sessionEmail = val?.email ?? ''; - sessionAccountID = val?.accountID ?? -1; + sessionAccountID = val?.accountID ?? CONST.DEFAULT_NUMBER_ID; }, }); @@ -258,8 +258,8 @@ function hasInvoicingDetails(policy: OnyxEntry): boolean { * Returns a primary invoice workspace for the user */ function getInvoicePrimaryWorkspace(currentUserLogin: string | undefined): Policy | undefined { - if (PolicyUtils.canSendInvoiceFromWorkspace(activePolicyID ?? '-1')) { - return allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID ?? '-1'}`]; + if (PolicyUtils.canSendInvoiceFromWorkspace(activePolicyID)) { + return allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`]; } const activeAdminWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies, currentUserLogin); return activeAdminWorkspaces.find((policy) => PolicyUtils.canSendInvoiceFromWorkspace(policy.id)); @@ -1863,7 +1863,7 @@ function buildPolicyData(policyOwnerEmail = '', makeMeAdmin = false, policyName failureData.push({ onyxMethod: Onyx.METHOD.SET, key: ONYXKEYS.NVP_ACTIVE_POLICY_ID, - value: activePolicyID ?? '', + value: activePolicyID, }); } @@ -2513,16 +2513,18 @@ function createWorkspaceFromIOUPayment(iouReport: OnyxEntry): WorkspaceF const parentReportActionID = iouReport.parentReportActionID; const reportPreview = iouReport?.parentReportID && parentReportActionID ? parentReport?.[parentReportActionID] : undefined; - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, - value: {[reportPreview?.reportActionID ?? '-1']: null}, - }); - failureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, - value: {[reportPreview?.reportActionID ?? '-1']: reportPreview}, - }); + if (reportPreview) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, + value: {[reportPreview.reportActionID]: null}, + }); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, + value: {[reportPreview.reportActionID]: reportPreview}, + }); + } // To optimistically remove the GBR from the DM we need to update the hasOutstandingChildRequest param to false optimisticData.push({ @@ -2558,36 +2560,37 @@ function createWorkspaceFromIOUPayment(iouReport: OnyxEntry): WorkspaceF }, }, }); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${memberData.workspaceChatReportID}`, + value: {[reportPreview.reportActionID]: null}, + }); } - failureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${memberData.workspaceChatReportID}`, - value: {[reportPreview?.reportActionID ?? '-1']: null}, - }); - // Create the MOVED report action and add it to the DM chat which indicates to the user where the report has been moved - const movedReportAction = ReportUtils.buildOptimisticMovedReportAction(oldPersonalPolicyID ?? '-1', policyID, memberData.workspaceChatReportID, iouReportID, workspaceName); - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, - value: {[movedReportAction.reportActionID]: movedReportAction}, - }); - successData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, - value: { - [movedReportAction.reportActionID]: { - ...movedReportAction, - pendingAction: null, + const movedReportAction = oldPersonalPolicyID ? ReportUtils.buildOptimisticMovedReportAction(oldPersonalPolicyID, policyID, memberData.workspaceChatReportID, iouReportID, workspaceName) : undefined; + if (movedReportAction) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, + value: {[movedReportAction.reportActionID]: movedReportAction}, + }); + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, + value: { + [movedReportAction.reportActionID]: { + ...movedReportAction, + pendingAction: null, + }, }, - }, - }); - failureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, - value: {[movedReportAction.reportActionID]: null}, - }); + }); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, + value: {[movedReportAction.reportActionID]: null}, + }); + } // We know that this new workspace has no BankAccount yet, so we can set // the reimbursement account to be immediately in the setup state for a new bank account: @@ -2623,7 +2626,7 @@ function createWorkspaceFromIOUPayment(iouReport: OnyxEntry): WorkspaceF customUnitRateID, iouReportID, memberData: JSON.stringify(memberData), - reportActionID: movedReportAction.reportActionID, + reportActionID: movedReportAction?.reportActionID, }; API.write(WRITE_COMMANDS.CREATE_WORKSPACE_FROM_IOU_PAYMENT, params, {optimisticData, successData, failureData}); diff --git a/tests/actions/PolicyTest.ts b/tests/actions/PolicyTest.ts index 25a74c19a56f..b46142fbfff8 100644 --- a/tests/actions/PolicyTest.ts +++ b/tests/actions/PolicyTest.ts @@ -7,10 +7,10 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy as PolicyType, Report, ReportAction, ReportActions} from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/Report'; import createRandomPolicy from '../utils/collections/policies'; +import createRandomReport from '../utils/collections/reports'; import * as TestHelper from '../utils/TestHelper'; import type {MockFetch} from '../utils/TestHelper'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; -import createRandomReport from '../utils/collections/reports'; const ESH_EMAIL = 'eshgupta1217@gmail.com'; const ESH_ACCOUNT_ID = 1; @@ -25,7 +25,7 @@ describe('actions/Policy', () => { keys: ONYXKEYS, }); }); - + let mockFetch: MockFetch; beforeEach(() => { global.fetch = TestHelper.getGlobalFetchMock(); @@ -250,7 +250,7 @@ describe('actions/Policy', () => { key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, callback: (reimbursementAccount) => { Onyx.disconnect(connection); - console.log(reimbursementAccount) + console.log(reimbursementAccount); expect(reimbursementAccount?.errors).not.toBeUndefined(); resolve(); }, From c361bbf96f9616f17f857ae34eb72f21fb9ed87a Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 18 Dec 2024 14:17:50 +0800 Subject: [PATCH 035/311] remove log --- tests/actions/PolicyTest.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/actions/PolicyTest.ts b/tests/actions/PolicyTest.ts index b46142fbfff8..157990168a6b 100644 --- a/tests/actions/PolicyTest.ts +++ b/tests/actions/PolicyTest.ts @@ -250,7 +250,6 @@ describe('actions/Policy', () => { key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, callback: (reimbursementAccount) => { Onyx.disconnect(connection); - console.log(reimbursementAccount); expect(reimbursementAccount?.errors).not.toBeUndefined(); resolve(); }, From f6fbf9752c88853bc70af44e77c289d06d641c99 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 18 Dec 2024 14:20:25 +0800 Subject: [PATCH 036/311] prettier --- src/libs/actions/Policy/Policy.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 6b1ebc209f63..c9964e08cf95 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -2568,7 +2568,9 @@ function createWorkspaceFromIOUPayment(iouReport: OnyxEntry): WorkspaceF } // Create the MOVED report action and add it to the DM chat which indicates to the user where the report has been moved - const movedReportAction = oldPersonalPolicyID ? ReportUtils.buildOptimisticMovedReportAction(oldPersonalPolicyID, policyID, memberData.workspaceChatReportID, iouReportID, workspaceName) : undefined; + const movedReportAction = oldPersonalPolicyID + ? ReportUtils.buildOptimisticMovedReportAction(oldPersonalPolicyID, policyID, memberData.workspaceChatReportID, iouReportID, workspaceName) + : undefined; if (movedReportAction) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, From 1fa5ab2049bc15fd231ac9739c82d5cf19ee1efa Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 18 Dec 2024 14:37:19 +0800 Subject: [PATCH 037/311] fix test --- tests/actions/PolicyTest.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/actions/PolicyTest.ts b/tests/actions/PolicyTest.ts index 157990168a6b..c0a4ba6c9cce 100644 --- a/tests/actions/PolicyTest.ts +++ b/tests/actions/PolicyTest.ts @@ -202,6 +202,7 @@ describe('actions/Policy', () => { ...createRandomReport(0), stateNum: CONST.REPORT.STATE_NUM.OPEN, statusNum: CONST.REPORT.STATUS_NUM.OPEN, + chatType: CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT, policyName: fakePolicy.name, }; const fakeReimbursementAccount = {errors: {}}; @@ -237,7 +238,7 @@ describe('actions/Policy', () => { expect(report?.stateNum).toBe(fakeReport.stateNum); expect(report?.statusNum).toBe(fakeReport.statusNum); expect(report?.policyName).toBe(fakeReport.policyName); - expect(report?.oldPolicyName).toBeUndefined(); + expect(report?.oldPolicyName).toBe(fakePolicy.name); expect(report?.private_isArchived).toBeUndefined(); resolve(); }, From bb2b53811f18b1b8731ccfb95f59f830992aefe0 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Wed, 18 Dec 2024 14:14:06 +0700 Subject: [PATCH 038/311] move the loading bar to TopBar and HeaderView --- src/components/LoadingBar.tsx | 1 + .../AppNavigator/createCustomBottomTabNavigator/TopBar.tsx | 3 +++ src/pages/home/HeaderView.tsx | 3 +++ src/pages/home/ReportScreen.tsx | 2 +- src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx | 2 +- 5 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/components/LoadingBar.tsx b/src/components/LoadingBar.tsx index e6d1ec0cd66d..68408cb2881f 100644 --- a/src/components/LoadingBar.tsx +++ b/src/components/LoadingBar.tsx @@ -19,6 +19,7 @@ function LoadingBar({shouldShow}: LoadingBarProps) { // eslint-disable-next-line react-compiler/react-compiler left.set(0); width.set(0); + height.set(withTiming(2, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION})); opacity.set(withTiming(1, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION})); left.set( diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar.tsx index 01caa79692f1..aad05f388971 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar.tsx @@ -16,6 +16,7 @@ import * as Session from '@userActions/Session'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import LoadingBar from '@components/LoadingBar'; type TopBarProps = { breadcrumbLabel: string; @@ -35,6 +36,7 @@ function TopBar({breadcrumbLabel, activeWorkspaceID, shouldDisplaySearch = true, const {translate} = useLocalize(); const policy = usePolicy(activeWorkspaceID); const [session] = useOnyx(ONYXKEYS.SESSION, {selector: (sessionValue) => sessionValue && {authTokenType: sessionValue.authTokenType}}); + const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA); const isAnonymousUser = Session.isAnonymousUser(session); const headerBreadcrumb = policy?.name @@ -83,6 +85,7 @@ function TopBar({breadcrumbLabel, activeWorkspaceID, shouldDisplaySearch = true, )} {displaySearch && } + ); } diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx index 03e7dcd82156..30cb51fad186 100644 --- a/src/pages/home/HeaderView.tsx +++ b/src/pages/home/HeaderView.tsx @@ -40,6 +40,7 @@ import SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; import type {Icon as IconType} from '@src/types/onyx/OnyxCommon'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import LoadingBar from '@components/LoadingBar'; type HeaderViewProps = { /** Toggles the navigationMenu open and closed */ @@ -75,6 +76,7 @@ function HeaderView({report, parentReportAction, reportID, onNavigationMenuButto const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID || report?.reportID || '-1'}`); const policy = usePolicy(report?.policyID); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); + const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA); const {translate} = useLocalize(); const theme = useTheme(); @@ -312,6 +314,7 @@ function HeaderView({report, parentReportAction, reportID, onNavigationMenuButto {!isParentReportLoading && !isLoading && canJoin && shouldUseNarrowLayout && {joinButton}} {!isLoading && isChatUsedForOnboarding && shouldUseNarrowLayout && {freeTrialButton}} + {shouldUseNarrowLayout && } ); } diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 81079f52d045..6250cc6d7b22 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -786,7 +786,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro needsOffscreenAlphaCompositing > {headerView} - {shouldUseNarrowLayout && } + {/* {shouldUseNarrowLayout && } */} {!!report && ReportUtils.isTaskReport(report) && shouldUseNarrowLayout && ReportUtils.isOpenTaskReport(report, parentReportAction) && ( diff --git a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx index 8db6ba294908..de003c462e34 100644 --- a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx +++ b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx @@ -70,7 +70,7 @@ function BaseSidebarScreen() { shouldDisplaySearch={shouldDisplaySearch} onSwitchWorkspace={() => (isSwitchingWorkspace.current = true)} /> - + {/* */} From 6da849152d798dbfed815390b478da1c86a784a1 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Thu, 19 Dec 2024 04:44:33 +0700 Subject: [PATCH 039/311] fix bug --- src/components/LoadingBar.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/LoadingBar.tsx b/src/components/LoadingBar.tsx index 68408cb2881f..e6d1ec0cd66d 100644 --- a/src/components/LoadingBar.tsx +++ b/src/components/LoadingBar.tsx @@ -19,7 +19,6 @@ function LoadingBar({shouldShow}: LoadingBarProps) { // eslint-disable-next-line react-compiler/react-compiler left.set(0); width.set(0); - height.set(withTiming(2, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION})); opacity.set(withTiming(1, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION})); left.set( From fa89d7af12df0360dcb516535a0a9dccd4613ec6 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Thu, 19 Dec 2024 17:12:03 +0300 Subject: [PATCH 040/311] updated to request search on transaction or report action list change --- src/hooks/useSearchHighlightAndScroll.ts | 25 ++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/hooks/useSearchHighlightAndScroll.ts b/src/hooks/useSearchHighlightAndScroll.ts index a5937559fd2f..6cd53dcf9bd7 100644 --- a/src/hooks/useSearchHighlightAndScroll.ts +++ b/src/hooks/useSearchHighlightAndScroll.ts @@ -1,3 +1,4 @@ +import isEqual from 'lodash/isEqual'; import {useCallback, useEffect, useRef, useState} from 'react'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import type {SearchQueryJSON} from '@components/Search/types'; @@ -34,20 +35,24 @@ function useSearchHighlightAndScroll({searchResults, transactions, previousTrans // Trigger search when a new report action is added while on chat or when a new transaction is added for the other search types. useEffect(() => { - const previousTransactionsLength = previousTransactions && Object.keys(previousTransactions).length; - const transactionsLength = transactions && Object.keys(transactions).length; + const previousTransactionIDList = Object.keys(previousTransactions ?? {}); + const transactionIDList = Object.keys(transactions ?? {}); - const reportActionsLength = reportActions && Object.values(reportActions).reduce((sum, curr) => sum + Object.keys(curr ?? {}).length, 0); - const prevReportActionsLength = previousReportActions && Object.values(previousReportActions).reduce((sum, curr) => sum + Object.keys(curr ?? {}).length, 0); - // Return early if search was already triggered or there's no change in current and previous data length - if (searchTriggeredRef.current || (!isChat && previousTransactionsLength === transactionsLength) || (isChat && reportActionsLength === prevReportActionsLength)) { + const reportActionIDList = Object.values(reportActions ?? {}) + .map((actions) => Object.keys(actions ?? {})) + .flat(); + const prevReportActionIDList = Object.values(previousReportActions ?? {}) + .map((actions) => Object.keys(actions ?? {})) + .flat(); + + if (searchTriggeredRef.current) { return; } - const newTransactionAdded = transactionsLength && typeof previousTransactionsLength === 'number' && transactionsLength > previousTransactionsLength; - const newReportActionAdded = reportActionsLength && typeof prevReportActionsLength === 'number' && reportActionsLength > prevReportActionsLength; + const hasTransactionChange = !isEqual(transactionIDList, previousTransactionIDList); + const hasReportActionChange = !isEqual(reportActionIDList, prevReportActionIDList); - // Check if a new transaction or report action was added - if ((!isChat && !!newTransactionAdded) || (isChat && !!newReportActionAdded)) { + // Check if there is a change in transaction or report action list + if ((!isChat && hasTransactionChange) || (isChat && hasReportActionChange)) { // Set the flag indicating the search is triggered by the hook triggeredByHookRef.current = true; From f2b8c3369643612a31dfc95bf0de67db3f63c1c1 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Thu, 19 Dec 2024 17:15:36 +0300 Subject: [PATCH 041/311] changed var name --- src/hooks/useSearchHighlightAndScroll.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/useSearchHighlightAndScroll.ts b/src/hooks/useSearchHighlightAndScroll.ts index 6cd53dcf9bd7..6110a5037a3a 100644 --- a/src/hooks/useSearchHighlightAndScroll.ts +++ b/src/hooks/useSearchHighlightAndScroll.ts @@ -41,7 +41,7 @@ function useSearchHighlightAndScroll({searchResults, transactions, previousTrans const reportActionIDList = Object.values(reportActions ?? {}) .map((actions) => Object.keys(actions ?? {})) .flat(); - const prevReportActionIDList = Object.values(previousReportActions ?? {}) + const previousReportActionIDList = Object.values(previousReportActions ?? {}) .map((actions) => Object.keys(actions ?? {})) .flat(); @@ -49,7 +49,7 @@ function useSearchHighlightAndScroll({searchResults, transactions, previousTrans return; } const hasTransactionChange = !isEqual(transactionIDList, previousTransactionIDList); - const hasReportActionChange = !isEqual(reportActionIDList, prevReportActionIDList); + const hasReportActionChange = !isEqual(reportActionIDList, previousReportActionIDList); // Check if there is a change in transaction or report action list if ((!isChat && hasTransactionChange) || (isChat && hasReportActionChange)) { From fce28c5f3bc715b2d2dd02dda2faedb9086de7f6 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Fri, 20 Dec 2024 22:01:35 +0700 Subject: [PATCH 042/311] update loading bar height to 1px --- src/styles/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index 602b3174d593..799a567d7d17 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -5339,7 +5339,7 @@ const styles = (theme: ThemeColors) => }, progressBarWrapper: { - height: 2, + height: 1, width: '100%', backgroundColor: theme.border, borderRadius: 2, From ad94f68b61f73510d164b7b92f0b0f43699dc500 Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Sun, 22 Dec 2024 02:13:15 +0100 Subject: [PATCH 043/311] Create discount component --- Mobile-Expensify | 2 +- src/CONST.ts | 1 + src/libs/SubscriptionUtils.ts | 49 +++ src/pages/home/HeaderView.tsx | 294 +++++++++--------- src/pages/home/ReportScreen.tsx | 4 + .../BillingBanner/EarlyDiscountBanner.tsx | 60 ++++ 6 files changed, 266 insertions(+), 144 deletions(-) create mode 100644 src/pages/settings/Subscription/CardSection/BillingBanner/EarlyDiscountBanner.tsx diff --git a/Mobile-Expensify b/Mobile-Expensify index 177c9b6cbeb1..d81135521e25 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 177c9b6cbeb11b29e8e48abd3a748891a40f9746 +Subproject commit d81135521e25add10bfebebf83fa5ec4a67cca13 diff --git a/src/CONST.ts b/src/CONST.ts index 4bfaad7b6d1b..d33f791c2e2b 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -514,6 +514,7 @@ const CONST = { MAX_DATE: '9999-12-31', MIN_DATE: '0001-01-01', ORDINAL_DAY_OF_MONTH: 'do', + SECONDS_PER_DAY: 84600, }, SMS: { DOMAIN: '@expensify.sms', diff --git a/src/libs/SubscriptionUtils.ts b/src/libs/SubscriptionUtils.ts index 87f750957abc..07ccac1f9be9 100644 --- a/src/libs/SubscriptionUtils.ts +++ b/src/libs/SubscriptionUtils.ts @@ -1,6 +1,7 @@ import {differenceInSeconds, fromUnixTime, isAfter, isBefore} from 'date-fns'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {BillingGraceEndPeriod, BillingStatus, Fund, FundList, Policy, StripeCustomerID} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; @@ -234,6 +235,52 @@ function hasCardExpiringSoon(): boolean { return isExpiringThisMonth || isExpiringNextMonth; } +function shouldShowDiscountBanner(firstDayFreeTrial: string, lastDayFreeTrial: string): boolean { + if (!isUserOnFreeTrial()) { + return false; + } + + if (doesUserHavePaymentCardAdded()) { + return false; + } + + const dateNow = Date.now() / 1000; + const firstDayTimestamp = new Date(`${firstDayFreeTrial} UTC`).getTime() / 1000; + const lastDayTimestamp = new Date(`${lastDayFreeTrial} UTC`).getTime() / 1000; + if (dateNow > lastDayTimestamp) { + return false; + } + + return dateNow <= firstDayTimestamp + 8 * CONST.DATE.SECONDS_PER_DAY * 1000; +} + +function getDiscountTimeRemaining(firstDayFreeTrial: string | undefined) { + if (!firstDayFreeTrial) { + return null; + } + const dateNow = Date.now() / 1000; + const firstDayTimestamp = new Date(`${firstDayFreeTrial} UTC`).getTime() / 1000; + + let timeLeftInSeconds; + const timeLeft24 = CONST.DATE.SECONDS_PER_DAY - (dateNow - firstDayTimestamp); + if (timeLeft24 > 0) { + timeLeftInSeconds = timeLeft24; + } else { + timeLeftInSeconds = firstDayTimestamp + 8 * CONST.DATE.SECONDS_PER_DAY - dateNow; + } + + if (timeLeftInSeconds <= 0) { + return null; + } + + return { + days: Math.floor(timeLeftInSeconds / 86400), + hours: Math.floor((timeLeftInSeconds % 86400) / 3600), + minutes: Math.floor((timeLeftInSeconds % 3600) / 60), + seconds: Math.floor(timeLeftInSeconds % 60), + }; +} + /** * @returns Whether there is a retry billing error. */ @@ -494,4 +541,6 @@ export { PAYMENT_STATUS, shouldRestrictUserBillableActions, shouldShowPreTrialBillingBanner, + shouldShowDiscountBanner, + getDiscountTimeRemaining, }; diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx index 03e7dcd82156..117573b262a8 100644 --- a/src/pages/home/HeaderView.tsx +++ b/src/pages/home/HeaderView.tsx @@ -10,6 +10,7 @@ import DisplayNames from '@components/DisplayNames'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import {FallbackAvatar} from '@components/Icon/Expensicons'; +import * as Illustrations from '@components/Icon/Illustrations'; import MultipleAvatars from '@components/MultipleAvatars'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import ParentNavigationSubtitle from '@components/ParentNavigationSubtitle'; @@ -29,12 +30,15 @@ import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import Parser from '@libs/Parser'; import * as ReportUtils from '@libs/ReportUtils'; +import {getDiscountTimeRemaining, shouldShowDiscountBanner} from '@libs/SubscriptionUtils'; +import EarlyDiscountBanner from '@pages/settings/Subscription/CardSection/BillingBanner/EarlyDiscountBanner'; import FreeTrial from '@pages/settings/Subscription/FreeTrial'; import * as Report from '@userActions/Report'; import * as Session from '@userActions/Session'; import * as Task from '@userActions/Task'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import BillingBanner from '@src/pages/settings/Subscription/CardSection/BillingBanner/BillingBanner'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; @@ -88,6 +92,7 @@ function HeaderView({report, parentReportAction, reportID, onNavigationMenuButto const participantPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(participants, personalDetails); const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(participantPersonalDetails, isMultipleParticipant, undefined, isSelfDM); + const [firstDayFreeTrial] = useOnyx(ONYXKEYS.NVP_FIRST_DAY_FREE_TRIAL); const isChatThread = ReportUtils.isChatThread(report); const isChatRoom = ReportUtils.isChatRoom(report); const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(report); @@ -155,164 +160,167 @@ function HeaderView({report, parentReportAction, reportID, onNavigationMenuButto const isChatUsedForOnboarding = ReportUtils.isChatUsedForOnboarding(report); return ( - - - {isLoading ? ( - - ) : ( - - {shouldUseNarrowLayout && ( - - + + + {isLoading ? ( + + ) : ( + + {shouldUseNarrowLayout && ( + - - - - - - )} - - ReportUtils.navigateToDetailsPage(report, Navigation.getReportRHPActiveRoute())} - style={[styles.flexRow, styles.alignItemsCenter, styles.flex1]} - disabled={shouldDisableDetailPage} - accessibilityLabel={title} - role={CONST.ROLE.BUTTON} - > - {shouldShowSubscript ? ( - - ) : ( - - - - )} - + + + + + + )} + + ReportUtils.navigateToDetailsPage(report, Navigation.getReportRHPActiveRoute())} + style={[styles.flexRow, styles.alignItemsCenter, styles.flex1]} + disabled={shouldDisableDetailPage} + accessibilityLabel={title} + role={CONST.ROLE.BUTTON} > - - - - {!isEmptyObject(parentNavigationSubtitleData) && ( - - )} - {shouldShowSubtitle() && ( - - {subtitle} - + ) : ( + + + )} - {isChatRoom && !!reportDescription && isEmptyObject(parentNavigationSubtitleData) && ( - { - const activeRoute = Navigation.getReportRHPActiveRoute(); - if (ReportUtils.canEditReportDescription(report, policy)) { - Navigation.navigate(ROUTES.REPORT_DESCRIPTION.getRoute(reportID, activeRoute)); - return; - } - Navigation.navigate(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(reportID, activeRoute)); - }} - style={[styles.alignSelfStart, styles.mw100]} - accessibilityLabel={translate('reportDescriptionPage.roomDescription')} - > - + + - {reportDescription} - - - )} - {isPolicyExpenseChat && !!policyDescription && isEmptyObject(parentNavigationSubtitleData) && ( - { - if (ReportUtils.canEditPolicyDescription(policy)) { - Navigation.navigate(ROUTES.WORKSPACE_PROFILE_DESCRIPTION.getRoute(report.policyID ?? '-1')); - return; - } - Navigation.navigate(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(reportID, Navigation.getReportRHPActiveRoute())); - }} - style={[styles.alignSelfStart, styles.mw100]} - accessibilityLabel={translate('workspace.editor.descriptionInputLabel')} - > + textStyles={[styles.headerText, styles.pre]} + shouldUseFullTitle={isChatRoom || isPolicyExpenseChat || isChatThread || isTaskReport || shouldUseGroupTitle} + renderAdditionalText={renderAdditionalText} + /> + + {!isEmptyObject(parentNavigationSubtitleData) && ( + + )} + {shouldShowSubtitle() && ( - {policyDescription} + {subtitle} - + )} + {isChatRoom && !!reportDescription && isEmptyObject(parentNavigationSubtitleData) && ( + { + const activeRoute = Navigation.getReportRHPActiveRoute(); + if (ReportUtils.canEditReportDescription(report, policy)) { + Navigation.navigate(ROUTES.REPORT_DESCRIPTION.getRoute(reportID, activeRoute)); + return; + } + Navigation.navigate(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(reportID, activeRoute)); + }} + style={[styles.alignSelfStart, styles.mw100]} + accessibilityLabel={translate('reportDescriptionPage.roomDescription')} + > + + {reportDescription} + + + )} + {isPolicyExpenseChat && !!policyDescription && isEmptyObject(parentNavigationSubtitleData) && ( + { + if (ReportUtils.canEditPolicyDescription(policy)) { + Navigation.navigate(ROUTES.WORKSPACE_PROFILE_DESCRIPTION.getRoute(report.policyID ?? '-1')); + return; + } + Navigation.navigate(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(reportID, Navigation.getReportRHPActiveRoute())); + }} + style={[styles.alignSelfStart, styles.mw100]} + accessibilityLabel={translate('workspace.editor.descriptionInputLabel')} + > + + {policyDescription} + + + )} + + {brickRoadIndicator === CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR && ( + + + )} + + + {!shouldUseNarrowLayout && isChatUsedForOnboarding && freeTrialButton} + {isTaskReport && !shouldUseNarrowLayout && ReportUtils.isOpenTaskReport(report, parentReportAction) && } + {!isParentReportLoading && canJoin && !shouldUseNarrowLayout && joinButton} - {brickRoadIndicator === CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR && ( - - - - )} - - - {!shouldUseNarrowLayout && isChatUsedForOnboarding && freeTrialButton} - {isTaskReport && !shouldUseNarrowLayout && ReportUtils.isOpenTaskReport(report, parentReportAction) && } - {!isParentReportLoading && canJoin && !shouldUseNarrowLayout && joinButton} + {shouldDisplaySearchRouter && } - {shouldDisplaySearchRouter && } + { + setIsDeleteTaskConfirmModalVisible(false); + Task.deleteTask(report); + }} + onCancel={() => setIsDeleteTaskConfirmModalVisible(false)} + title={translate('task.deleteTask')} + prompt={translate('task.deleteConfirmation')} + confirmText={translate('common.delete')} + cancelText={translate('common.cancel')} + danger + shouldEnableNewFocusManagement + /> - { - setIsDeleteTaskConfirmModalVisible(false); - Task.deleteTask(report); - }} - onCancel={() => setIsDeleteTaskConfirmModalVisible(false)} - title={translate('task.deleteTask')} - prompt={translate('task.deleteConfirmation')} - confirmText={translate('common.delete')} - cancelText={translate('common.cancel')} - danger - shouldEnableNewFocusManagement - /> - - )} + )} + + {!isParentReportLoading && !isLoading && canJoin && shouldUseNarrowLayout && {joinButton}} + {!isLoading && isChatUsedForOnboarding && shouldUseNarrowLayout && {freeTrialButton}} - {!isParentReportLoading && !isLoading && canJoin && shouldUseNarrowLayout && {joinButton}} - {!isLoading && isChatUsedForOnboarding && shouldUseNarrowLayout && {freeTrialButton}} - + {shouldShowDiscountBanner() && } + ); } diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 97582f75b7b1..5fc98373ef6c 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -339,6 +339,10 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro ); } + if (ReportUtils.isAdminRoom(report)) { + console.log('uyes'); + } + /** * When false the ReportActionsView will completely unmount and we will show a loader until it returns true. */ diff --git a/src/pages/settings/Subscription/CardSection/BillingBanner/EarlyDiscountBanner.tsx b/src/pages/settings/Subscription/CardSection/BillingBanner/EarlyDiscountBanner.tsx new file mode 100644 index 000000000000..060479cef06c --- /dev/null +++ b/src/pages/settings/Subscription/CardSection/BillingBanner/EarlyDiscountBanner.tsx @@ -0,0 +1,60 @@ +import React, {useCallback, useEffect, useMemo, useState} from 'react'; +import {useOnyx} from 'react-native-onyx'; +import * as Illustrations from '@components/Icon/Illustrations'; +import Text from '@components/Text'; +import TextLink from '@components/TextLink'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as Report from '@libs/actions/Report'; +import Navigation from '@libs/Navigation/Navigation'; +import * as ReportUtils from '@libs/ReportUtils'; +import {getDiscountTimeRemaining} from '@libs/SubscriptionUtils'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import BillingBanner from './BillingBanner'; + +function EarlyDiscountBanner({timeRemainingProp}) { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + + const [firstDayFreeTrial] = useOnyx(ONYXKEYS.NVP_FIRST_DAY_FREE_TRIAL); + const [lastDayFreeTrial] = useOnyx(ONYXKEYS.NVP_LAST_DAY_FREE_TRIAL); + const [timeRemaining, setTimeRemaining] = useState(timeRemainingProp); + const discountType = useMemo(() => (timeRemaining < CONST.DATE.SECONDS_PER_DAY ? 50 : 25), [timeRemaining]); + + useEffect(() => { + const intervalID = setInterval(() => { + setTimeRemaining(getDiscountTimeRemaining(firstDayFreeTrial)); + }, 1000); + + return () => clearInterval(intervalID); + }, [firstDayFreeTrial]); + + const title = ( + + Limited time offer: +  50% off your first year! + + ); + + const formatTimeRemaining = useCallback(() => { + if (timeRemaining.days === 0) { + return `Claim within ${timeRemaining.hours}h : ${timeRemaining.minutes}m : ${timeRemaining.seconds}s`; + } + return `Claim within ${timeRemaining.days}d : ${timeRemaining.hours}h : ${timeRemaining.minutes}m : ${timeRemaining.seconds}s`; + }, [timeRemaining]); + + return ( + + ); +} + +EarlyDiscountBanner.displayName = 'PreTrialBillingBanner'; + +export default EarlyDiscountBanner; From f99771998cc60a7ad268f57c97ab41a5a15360e2 Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Sun, 22 Dec 2024 19:22:48 +0100 Subject: [PATCH 044/311] Add banner to subscription page --- src/libs/SubscriptionUtils.ts | 4 ++-- src/pages/home/HeaderView.tsx | 3 ++- .../BillingBanner/EarlyDiscountBanner.tsx | 13 +++++++++++-- .../Subscription/CardSection/CardSection.tsx | 5 ++++- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/libs/SubscriptionUtils.ts b/src/libs/SubscriptionUtils.ts index 07ccac1f9be9..4cdd10782ff2 100644 --- a/src/libs/SubscriptionUtils.ts +++ b/src/libs/SubscriptionUtils.ts @@ -235,7 +235,7 @@ function hasCardExpiringSoon(): boolean { return isExpiringThisMonth || isExpiringNextMonth; } -function shouldShowDiscountBanner(firstDayFreeTrial: string, lastDayFreeTrial: string): boolean { +function shouldShowDiscountBanner(): boolean { if (!isUserOnFreeTrial()) { return false; } @@ -254,7 +254,7 @@ function shouldShowDiscountBanner(firstDayFreeTrial: string, lastDayFreeTrial: s return dateNow <= firstDayTimestamp + 8 * CONST.DATE.SECONDS_PER_DAY * 1000; } -function getDiscountTimeRemaining(firstDayFreeTrial: string | undefined) { +function getDiscountTimeRemaining() { if (!firstDayFreeTrial) { return null; } diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx index 117573b262a8..da44a7b57510 100644 --- a/src/pages/home/HeaderView.tsx +++ b/src/pages/home/HeaderView.tsx @@ -93,6 +93,7 @@ function HeaderView({report, parentReportAction, reportID, onNavigationMenuButto const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(participantPersonalDetails, isMultipleParticipant, undefined, isSelfDM); const [firstDayFreeTrial] = useOnyx(ONYXKEYS.NVP_FIRST_DAY_FREE_TRIAL); + const [lastDayFreeTrial] = useOnyx(ONYXKEYS.NVP_LAST_DAY_FREE_TRIAL); const isChatThread = ReportUtils.isChatThread(report); const isChatRoom = ReportUtils.isChatRoom(report); const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(report); @@ -319,7 +320,7 @@ function HeaderView({report, parentReportAction, reportID, onNavigationMenuButto {!isParentReportLoading && !isLoading && canJoin && shouldUseNarrowLayout && {joinButton}} {!isLoading && isChatUsedForOnboarding && shouldUseNarrowLayout && {freeTrialButton}} - {shouldShowDiscountBanner() && } + {shouldShowDiscountBanner() && ReportUtils.isChatUsedForOnboarding(report) && } ); } diff --git a/src/pages/settings/Subscription/CardSection/BillingBanner/EarlyDiscountBanner.tsx b/src/pages/settings/Subscription/CardSection/BillingBanner/EarlyDiscountBanner.tsx index 060479cef06c..eae090791a37 100644 --- a/src/pages/settings/Subscription/CardSection/BillingBanner/EarlyDiscountBanner.tsx +++ b/src/pages/settings/Subscription/CardSection/BillingBanner/EarlyDiscountBanner.tsx @@ -14,18 +14,20 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import BillingBanner from './BillingBanner'; -function EarlyDiscountBanner({timeRemainingProp}) { +function EarlyDiscountBanner({isSubscriptionPage}) { const {translate} = useLocalize(); const styles = useThemeStyles(); const [firstDayFreeTrial] = useOnyx(ONYXKEYS.NVP_FIRST_DAY_FREE_TRIAL); const [lastDayFreeTrial] = useOnyx(ONYXKEYS.NVP_LAST_DAY_FREE_TRIAL); - const [timeRemaining, setTimeRemaining] = useState(timeRemainingProp); + + const [timeRemaining, setTimeRemaining] = useState({}); const discountType = useMemo(() => (timeRemaining < CONST.DATE.SECONDS_PER_DAY ? 50 : 25), [timeRemaining]); useEffect(() => { const intervalID = setInterval(() => { setTimeRemaining(getDiscountTimeRemaining(firstDayFreeTrial)); + console.log('hi'); }, 1000); return () => clearInterval(intervalID); @@ -39,12 +41,19 @@ function EarlyDiscountBanner({timeRemainingProp}) { ); const formatTimeRemaining = useCallback(() => { + if (!timeRemaining) { + return; + } if (timeRemaining.days === 0) { return `Claim within ${timeRemaining.hours}h : ${timeRemaining.minutes}m : ${timeRemaining.seconds}s`; } return `Claim within ${timeRemaining.days}d : ${timeRemaining.hours}h : ${timeRemaining.minutes}m : ${timeRemaining.seconds}s`; }, [timeRemaining]); + if (!firstDayFreeTrial || !lastDayFreeTrial) { + return null; + } + return ( ; + } else if (SubscriptionUtils.shouldShowPreTrialBillingBanner()) { BillingBanner = ; } else if (SubscriptionUtils.isUserOnFreeTrial()) { BillingBanner = ; From 340c0ca692542af243ee0a9383e8d296d4057f87 Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Mon, 23 Dec 2024 14:00:01 +0530 Subject: [PATCH 045/311] listen for changes --- src/components/OptionListContextProvider.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/OptionListContextProvider.tsx b/src/components/OptionListContextProvider.tsx index 994ebfc2a1b0..e3c6cc7a1690 100644 --- a/src/components/OptionListContextProvider.tsx +++ b/src/components/OptionListContextProvider.tsx @@ -47,6 +47,7 @@ function OptionsListContextProvider({children}: OptionsListProviderProps) { personalDetails: [], }); const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); + const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS); const personalDetails = usePersonalDetails(); const prevPersonalDetails = usePrevious(personalDetails); @@ -71,7 +72,7 @@ function OptionsListContextProvider({children}: OptionsListProviderProps) { return newOptions; }); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps - }, [reports]); + }, [reports, reportNameValuePairs]); /** * This effect is used to update the options list when personal details change. From 70ffbfa0a7e4202e476cd1c3eb662db4e18b4abe Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Tue, 24 Dec 2024 15:07:21 +0100 Subject: [PATCH 046/311] Make 25% discount banner dismissable --- .../CardSection/BillingBanner/EarlyDiscountBanner.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/settings/Subscription/CardSection/BillingBanner/EarlyDiscountBanner.tsx b/src/pages/settings/Subscription/CardSection/BillingBanner/EarlyDiscountBanner.tsx index 783e063004ca..d8730f3426bb 100644 --- a/src/pages/settings/Subscription/CardSection/BillingBanner/EarlyDiscountBanner.tsx +++ b/src/pages/settings/Subscription/CardSection/BillingBanner/EarlyDiscountBanner.tsx @@ -52,7 +52,6 @@ function EarlyDiscountBanner({isSubscriptionPage}) { }, [discountInfo]); const {shouldUseNarrowLayout} = useResponsiveLayout(); - console.log(shouldUseNarrowLayout); const rightComponent = useMemo(() => { const smallScreenStyle = shouldUseNarrowLayout ? [styles.flex0, styles.flexBasis100, styles.flexRow, styles.justifyContentCenter] : []; return ( @@ -62,7 +61,7 @@ function EarlyDiscountBanner({isSubscriptionPage}) { text="Claim offer" onPress={() => Navigation.navigate(ROUTES.SETTINGS_SUBSCRIPTION)} /> - {discountInfo?.discountType === 50 && ( + {discountInfo?.discountType === 25 && (