From d642ad87921a6999b7c1386a8d6284b8eba3e511 Mon Sep 17 00:00:00 2001 From: "marta.sudol" Date: Tue, 1 Jul 2025 11:12:40 +0200 Subject: [PATCH 1/6] popover memoization --- src/components/Search/FilterDropdowns/DropdownButton.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/Search/FilterDropdowns/DropdownButton.tsx b/src/components/Search/FilterDropdowns/DropdownButton.tsx index 36f57edd94e9..1057e672be0e 100644 --- a/src/components/Search/FilterDropdowns/DropdownButton.tsx +++ b/src/components/Search/FilterDropdowns/DropdownButton.tsx @@ -99,6 +99,13 @@ function DropdownButton({label, value, viewportOffsetTop, PopoverComponent}: Dro return {width: CONST.POPOVER_DROPDOWN_WIDTH}; }, [isSmallScreenWidth, styles]); + const popoverContent = useMemo(() => { + if (!isOverlayVisible) { + return null; + } + return PopoverComponent({closeOverlay: toggleOverlay}); + }, [isOverlayVisible, PopoverComponent, toggleOverlay]); + return ( <> {/* Dropdown Trigger */} @@ -140,7 +147,7 @@ function DropdownButton({label, value, viewportOffsetTop, PopoverComponent}: Dro height: CONST.POPOVER_DROPDOWN_MIN_HEIGHT, }} > - {PopoverComponent({closeOverlay: toggleOverlay})} + {popoverContent} ); From f44307cb13a5bbc199029d6956fe45f4c0751a37 Mon Sep 17 00:00:00 2001 From: "marta.sudol" Date: Tue, 1 Jul 2025 11:26:32 +0200 Subject: [PATCH 2/6] perf --- src/components/AccountSwitcher.tsx | 4 ++-- .../Search/FilterDropdowns/UserSelectPopup.tsx | 12 ++++++------ .../Search/SearchPageHeader/SearchFiltersBar.tsx | 7 +++---- .../home/sidebar/FloatingActionButtonAndPopover.tsx | 2 +- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/components/AccountSwitcher.tsx b/src/components/AccountSwitcher.tsx index 07c446cb7de1..beb08841dd3c 100644 --- a/src/components/AccountSwitcher.tsx +++ b/src/components/AccountSwitcher.tsx @@ -45,7 +45,7 @@ function AccountSwitcher({isScreenFocused}: AccountSwitcherProps) { const {isOffline} = useNetwork(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: true}); - const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: false}); + const [accountID] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: false, selector: (onyxSession) => onyxSession?.accountID}); const buttonRef = useRef(null); const {windowHeight} = useWindowDimensions(); @@ -230,7 +230,7 @@ function AccountSwitcher({isScreenFocused}: AccountSwitcherProps) { style={[styles.textLabelSupporting, styles.mt1, styles.w100]} numberOfLines={1} > - AccountID: {session?.accountID} + AccountID: {accountID} )} diff --git a/src/components/Search/FilterDropdowns/UserSelectPopup.tsx b/src/components/Search/FilterDropdowns/UserSelectPopup.tsx index f04df0a874ad..c7cb26b4da51 100644 --- a/src/components/Search/FilterDropdowns/UserSelectPopup.tsx +++ b/src/components/Search/FilterDropdowns/UserSelectPopup.tsx @@ -41,13 +41,13 @@ function UserSelectPopup({value, closeOverlay, onChange}: UserSelectPopupProps) const personalDetails = usePersonalDetails(); const {windowHeight} = useWindowDimensions(); const {shouldUseNarrowLayout} = useResponsiveLayout(); - const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true}); + const [accountID] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true, selector: (onyxSession) => onyxSession?.accountID}); const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState(''); const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false, canBeMissing: true}); const [selectedOptions, setSelectedOptions] = useState(() => { - return value.reduce((acc, accountID) => { - const participant = personalDetails?.[accountID]; + return value.reduce((acc, id) => { + const participant = personalDetails?.[id]; if (!participant) { return acc; } @@ -90,17 +90,17 @@ function UserSelectPopup({value, closeOverlay, onChange}: UserSelectPopupProps) })) .sort((a, b) => { // Put the current user at the top of the list - if (a.accountID === session?.accountID) { + if (a.accountID === accountID) { return -1; } - if (b.accountID === session?.accountID) { + if (b.accountID === accountID) { return 1; } return 0; }); return [...(personalDetailList ?? []), ...(recentReports ?? [])]; - }, [cleanSearchTerm, options.personalDetails, options.reports, selectedOptions, session?.accountID]); + }, [cleanSearchTerm, options.personalDetails, options.reports, selectedOptions, accountID]); const {sections, headerMessage} = useMemo(() => { const newSections: Section[] = [ diff --git a/src/components/Search/SearchPageHeader/SearchFiltersBar.tsx b/src/components/Search/SearchPageHeader/SearchFiltersBar.tsx index b00b0c790dfb..20524d447969 100644 --- a/src/components/Search/SearchPageHeader/SearchFiltersBar.tsx +++ b/src/components/Search/SearchPageHeader/SearchFiltersBar.tsx @@ -61,7 +61,7 @@ function SearchFiltersBar({queryJSON, headerButtonsOptions}: SearchFiltersBarPro const {shouldUseNarrowLayout} = useResponsiveLayout(); const {selectedTransactions, setExportMode, isExportMode, shouldShowExportModeOption, shouldShowFiltersBarLoading} = useSearchContext(); - const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true}); + const [email] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true, selector: (onyxSession) => onyxSession?.email}); const [userCardList] = useOnyx(ONYXKEYS.CARD_LIST, {canBeMissing: true}); const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: false}); const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true}); @@ -70,14 +70,13 @@ function SearchFiltersBar({queryJSON, headerButtonsOptions}: SearchFiltersBarPro const [policyCategories] = useOnyx(ONYXKEYS.COLLECTION.POLICY_CATEGORIES, {canBeMissing: true}); const [workspaceCardFeeds] = useOnyx(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST, {canBeMissing: true}); const [selectionMode] = useOnyx(ONYXKEYS.MOBILE_SELECTION_MODE, {canBeMissing: true}); - const [currentSearchResults] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`, {canBeMissing: true}); + const [searchResultsErrors] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`, {canBeMissing: true, selector: (data) => data?.errors}); const taxRates = getAllTaxRates(); const allCards = useMemo(() => mergeCardListWithWorkspaceFeeds(workspaceCardFeeds ?? CONST.EMPTY_OBJECT, userCardList), [userCardList, workspaceCardFeeds]); const selectedTransactionsKeys = useMemo(() => Object.keys(selectedTransactions ?? {}), [selectedTransactions]); - const email = session?.email; - const hasErrors = Object.keys(currentSearchResults?.errors ?? {}).length > 0 && !isOffline; + const hasErrors = Object.keys(searchResultsErrors ?? {}).length > 0 && !isOffline; const shouldShowSelectedDropdown = headerButtonsOptions.length > 0 && (!shouldUseNarrowLayout || (!!selectionMode && selectionMode.isEnabled)); const [typeOptions, type] = useMemo(() => { diff --git a/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx index cfd009a63f82..925c784b20e2 100644 --- a/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx @@ -92,7 +92,7 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, isT const {translate} = useLocalize(); const [isLoading = false] = useOnyx(ONYXKEYS.IS_LOADING_APP, {canBeMissing: true}); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: true}); - const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: false}); + const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: false, selector: (onyxSession) => ({email: onyxSession?.email, accountID: onyxSession?.accountID})}); const [quickAction] = useOnyx(ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE, {canBeMissing: true}); const [quickActionReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${quickAction?.chatReportID}`, {canBeMissing: true}); const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${quickActionReport?.reportID}`, {canBeMissing: true}); From b6f4ae6932046a5c96362071ef5eac0f0487667d Mon Sep 17 00:00:00 2001 From: "marta.sudol" Date: Tue, 1 Jul 2025 11:51:25 +0200 Subject: [PATCH 3/6] perf --- .../Navigation/NavigationTabBar/index.tsx | 16 ++---- src/hooks/useChatTabBrickRoad.tsx | 52 +++++++++++++++++++ src/libs/WorkspacesSettingsUtils.ts | 25 ++++++++- 3 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 src/hooks/useChatTabBrickRoad.tsx diff --git a/src/components/Navigation/NavigationTabBar/index.tsx b/src/components/Navigation/NavigationTabBar/index.tsx index 9c2760868aff..76790dbc6c60 100644 --- a/src/components/Navigation/NavigationTabBar/index.tsx +++ b/src/components/Navigation/NavigationTabBar/index.tsx @@ -1,5 +1,5 @@ import {findFocusedRoute} from '@react-navigation/native'; -import React, {memo, useCallback, useEffect, useState} from 'react'; +import React, {memo, useCallback} from 'react'; import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; @@ -14,7 +14,7 @@ import Text from '@components/Text'; import EducationalTooltip from '@components/Tooltip/EducationalTooltip'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; -import {useSidebarOrderedReports} from '@hooks/useSidebarOrderedReports'; +import useChatTabBrickRoad from '@hooks/useChatTabBrickRoad'; import useStyleUtils from '@hooks/useStyleUtils'; import useSubscriptionPlan from '@hooks/useSubscriptionPlan'; import useTheme from '@hooks/useTheme'; @@ -23,6 +23,7 @@ import useWorkspacesTabIndicatorStatus from '@hooks/useWorkspacesTabIndicatorSta import clearSelectedText from '@libs/clearSelectedText/clearSelectedText'; import getPlatform from '@libs/getPlatform'; import interceptAnonymousUser from '@libs/interceptAnonymousUser'; +import Performance from '@libs/Performance'; import {getPreservedNavigatorState} from '@libs/Navigation/AppNavigator/createSplitNavigator/usePreserveNavigatorState'; import { getLastVisitedTabPath, @@ -32,7 +33,6 @@ import { } from '@libs/Navigation/helpers/lastVisitedTabPathUtils'; import {buildCannedSearchQuery, buildSearchQueryJSON, buildSearchQueryString} from '@libs/SearchQueryUtils'; import type {BrickRoad} from '@libs/WorkspacesSettingsUtils'; -import {getChatTabBrickRoad} from '@libs/WorkspacesSettingsUtils'; import {isFullScreenName, isWorkspacesTabScreenName} from '@navigation/helpers/isNavigatorName'; import Navigation from '@navigation/Navigation'; import navigationRef from '@navigation/navigationRef'; @@ -58,12 +58,10 @@ function NavigationTabBar({selectedTab, isTooltipAllowed = false, isTopLevelBar const styles = useThemeStyles(); const {translate, preferredLocale} = useLocalize(); const {indicatorColor: workspacesTabIndicatorColor, status: workspacesTabIndicatorStatus} = useWorkspacesTabIndicatorStatus(); - const {orderedReports} = useSidebarOrderedReports(); + const chatTabBrickRoad = useChatTabBrickRoad(); const subscriptionPlan = useSubscriptionPlan(); const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: false}); - const [reportAttributes] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {selector: (value) => value?.reports, canBeMissing: true}); const {shouldUseNarrowLayout} = useResponsiveLayout(); - const [chatTabBrickRoad, setChatTabBrickRoad] = useState(undefined); const platform = getPlatform(); const isWebOrDesktop = platform === CONST.PLATFORM.WEB || platform === CONST.PLATFORM.DESKTOP; const { @@ -77,12 +75,6 @@ function NavigationTabBar({selectedTab, isTooltipAllowed = false, isTopLevelBar // On a wide layout DebugTabView should be rendered only within the navigation tab bar displayed directly on screens. const shouldRenderDebugTabViewOnWideLayout = !!account?.isDebugModeEnabled && !isTopLevelBar; - useEffect(() => { - setChatTabBrickRoad(getChatTabBrickRoad(orderedReports)); - // We need to get a new brick road state when report attributes are updated, otherwise we'll be showing an outdated brick road. - // That's why reportAttributes is added as a dependency here - }, [orderedReports, reportAttributes]); - const navigateToChats = useCallback(() => { if (selectedTab === NAVIGATION_TABS.HOME) { return; diff --git a/src/hooks/useChatTabBrickRoad.tsx b/src/hooks/useChatTabBrickRoad.tsx new file mode 100644 index 000000000000..a27127882b19 --- /dev/null +++ b/src/hooks/useChatTabBrickRoad.tsx @@ -0,0 +1,52 @@ +import {useMemo} from 'react'; +import {useOnyx} from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {Report, ReportAttributesDerivedValue} from '@src/types/onyx'; + +type BrickRoad = ValueOf | undefined; + +/** + * Lightweight hook to determine chat tab brick road status without processing all reports. + * This avoids the expensive useSidebarOrderedReports computation during tab switching. + */ +function useChatTabBrickRoad(): BrickRoad { + const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: true}); + const [reportAttributes] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, { + selector: (value) => value?.reports, + canBeMissing: true, + }); + + return useMemo(() => { + if (!reports || !reportAttributes) { + return undefined; + } + + // Look for any report with brick road status in priority order: + // 1. ERROR status (red brick road) - highest priority + // 2. INFO status (green brick road) - lower priority + let hasInfoBrickRoad = false; + + // We only need to check reports that have brick road attributes + // This is much more efficient than processing all reports through the full sorting logic + for (const reportID of Object.keys(reportAttributes)) { + const brickRoadStatus = reportAttributes[reportID]?.brickRoadStatus; + + if (brickRoadStatus === CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR) { + // Found an error brick road - this has highest priority, return immediately + return CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR; + } + + if (brickRoadStatus === CONST.BRICK_ROAD_INDICATOR_STATUS.INFO) { + // Found an info brick road - remember it but keep looking for errors + hasInfoBrickRoad = true; + } + } + + // Return info brick road if we found one and no errors + return hasInfoBrickRoad ? CONST.BRICK_ROAD_INDICATOR_STATUS.INFO : undefined; + }, [reports, reportAttributes]); +} + +export default useChatTabBrickRoad; \ No newline at end of file diff --git a/src/libs/WorkspacesSettingsUtils.ts b/src/libs/WorkspacesSettingsUtils.ts index c328a2a18d5d..7248a9fd3809 100644 --- a/src/libs/WorkspacesSettingsUtils.ts +++ b/src/libs/WorkspacesSettingsUtils.ts @@ -104,9 +104,32 @@ function getChatTabBrickRoadReport(orderedReports: Array> = [] return undefined; } +// Memoization cache for getChatTabBrickRoad to avoid recomputing with same input +const chatTabBrickRoadCache = new Map(); + function getChatTabBrickRoad(orderedReports: Array>): BrickRoad | undefined { + // Create a cache key based on report IDs and their brick road status + const cacheKey = orderedReports + .filter(Boolean) + .map((report) => `${report?.reportID}:${reportAttributes?.[report?.reportID ?? '']?.brickRoadStatus ?? 'none'}`) + .join('|'); + + // Check cache first + if (chatTabBrickRoadCache.has(cacheKey)) { + return chatTabBrickRoadCache.get(cacheKey); + } + + // Compute the result const report = getChatTabBrickRoadReport(orderedReports); - return report ? getBrickRoadForPolicy(report) : undefined; + const result = report ? getBrickRoadForPolicy(report) : undefined; + + // Cache the result (limit cache size to prevent memory leaks) + if (chatTabBrickRoadCache.size > 100) { + chatTabBrickRoadCache.clear(); + } + chatTabBrickRoadCache.set(cacheKey, result); + + return result; } /** From 67c82056da9e3f87b7cadec2c60296eb3cfe4164 Mon Sep 17 00:00:00 2001 From: "marta.sudol" Date: Tue, 1 Jul 2025 12:10:42 +0200 Subject: [PATCH 4/6] chat bricks --- .../Navigation/NavigationTabBar/index.tsx | 15 ++++-- src/hooks/useChatTabBrickRoad.tsx | 52 ------------------- src/libs/WorkspacesSettingsUtils.ts | 25 +-------- 3 files changed, 13 insertions(+), 79 deletions(-) delete mode 100644 src/hooks/useChatTabBrickRoad.tsx diff --git a/src/components/Navigation/NavigationTabBar/index.tsx b/src/components/Navigation/NavigationTabBar/index.tsx index 76790dbc6c60..af7c576dbe5d 100644 --- a/src/components/Navigation/NavigationTabBar/index.tsx +++ b/src/components/Navigation/NavigationTabBar/index.tsx @@ -1,5 +1,5 @@ import {findFocusedRoute} from '@react-navigation/native'; -import React, {memo, useCallback} from 'react'; +import React, {memo, useCallback, useEffect, useState} from 'react'; import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; @@ -14,7 +14,7 @@ import Text from '@components/Text'; import EducationalTooltip from '@components/Tooltip/EducationalTooltip'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; -import useChatTabBrickRoad from '@hooks/useChatTabBrickRoad'; +import {useSidebarOrderedReports} from '@hooks/useSidebarOrderedReports'; import useStyleUtils from '@hooks/useStyleUtils'; import useSubscriptionPlan from '@hooks/useSubscriptionPlan'; import useTheme from '@hooks/useTheme'; @@ -33,6 +33,7 @@ import { } from '@libs/Navigation/helpers/lastVisitedTabPathUtils'; import {buildCannedSearchQuery, buildSearchQueryJSON, buildSearchQueryString} from '@libs/SearchQueryUtils'; import type {BrickRoad} from '@libs/WorkspacesSettingsUtils'; +import {getChatTabBrickRoad} from '@libs/WorkspacesSettingsUtils'; import {isFullScreenName, isWorkspacesTabScreenName} from '@navigation/helpers/isNavigatorName'; import Navigation from '@navigation/Navigation'; import navigationRef from '@navigation/navigationRef'; @@ -58,10 +59,12 @@ function NavigationTabBar({selectedTab, isTooltipAllowed = false, isTopLevelBar const styles = useThemeStyles(); const {translate, preferredLocale} = useLocalize(); const {indicatorColor: workspacesTabIndicatorColor, status: workspacesTabIndicatorStatus} = useWorkspacesTabIndicatorStatus(); - const chatTabBrickRoad = useChatTabBrickRoad(); + const {orderedReports} = useSidebarOrderedReports(); const subscriptionPlan = useSubscriptionPlan(); const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: false}); + const [reportAttributes] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {selector: (value) => value?.reports, canBeMissing: true}); const {shouldUseNarrowLayout} = useResponsiveLayout(); + const [chatTabBrickRoad, setChatTabBrickRoad] = useState(undefined); const platform = getPlatform(); const isWebOrDesktop = platform === CONST.PLATFORM.WEB || platform === CONST.PLATFORM.DESKTOP; const { @@ -75,6 +78,12 @@ function NavigationTabBar({selectedTab, isTooltipAllowed = false, isTopLevelBar // On a wide layout DebugTabView should be rendered only within the navigation tab bar displayed directly on screens. const shouldRenderDebugTabViewOnWideLayout = !!account?.isDebugModeEnabled && !isTopLevelBar; + useEffect(() => { + setChatTabBrickRoad(getChatTabBrickRoad(orderedReports)); + // We need to get a new brick road state when report attributes are updated, otherwise we'll be showing an outdated brick road. + // That's why reportAttributes is added as a dependency here + }, [orderedReports, reportAttributes]); + const navigateToChats = useCallback(() => { if (selectedTab === NAVIGATION_TABS.HOME) { return; diff --git a/src/hooks/useChatTabBrickRoad.tsx b/src/hooks/useChatTabBrickRoad.tsx deleted file mode 100644 index a27127882b19..000000000000 --- a/src/hooks/useChatTabBrickRoad.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import {useMemo} from 'react'; -import {useOnyx} from 'react-native-onyx'; -import type {ValueOf} from 'type-fest'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type {Report, ReportAttributesDerivedValue} from '@src/types/onyx'; - -type BrickRoad = ValueOf | undefined; - -/** - * Lightweight hook to determine chat tab brick road status without processing all reports. - * This avoids the expensive useSidebarOrderedReports computation during tab switching. - */ -function useChatTabBrickRoad(): BrickRoad { - const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: true}); - const [reportAttributes] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, { - selector: (value) => value?.reports, - canBeMissing: true, - }); - - return useMemo(() => { - if (!reports || !reportAttributes) { - return undefined; - } - - // Look for any report with brick road status in priority order: - // 1. ERROR status (red brick road) - highest priority - // 2. INFO status (green brick road) - lower priority - let hasInfoBrickRoad = false; - - // We only need to check reports that have brick road attributes - // This is much more efficient than processing all reports through the full sorting logic - for (const reportID of Object.keys(reportAttributes)) { - const brickRoadStatus = reportAttributes[reportID]?.brickRoadStatus; - - if (brickRoadStatus === CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR) { - // Found an error brick road - this has highest priority, return immediately - return CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR; - } - - if (brickRoadStatus === CONST.BRICK_ROAD_INDICATOR_STATUS.INFO) { - // Found an info brick road - remember it but keep looking for errors - hasInfoBrickRoad = true; - } - } - - // Return info brick road if we found one and no errors - return hasInfoBrickRoad ? CONST.BRICK_ROAD_INDICATOR_STATUS.INFO : undefined; - }, [reports, reportAttributes]); -} - -export default useChatTabBrickRoad; \ No newline at end of file diff --git a/src/libs/WorkspacesSettingsUtils.ts b/src/libs/WorkspacesSettingsUtils.ts index 7248a9fd3809..c328a2a18d5d 100644 --- a/src/libs/WorkspacesSettingsUtils.ts +++ b/src/libs/WorkspacesSettingsUtils.ts @@ -104,32 +104,9 @@ function getChatTabBrickRoadReport(orderedReports: Array> = [] return undefined; } -// Memoization cache for getChatTabBrickRoad to avoid recomputing with same input -const chatTabBrickRoadCache = new Map(); - function getChatTabBrickRoad(orderedReports: Array>): BrickRoad | undefined { - // Create a cache key based on report IDs and their brick road status - const cacheKey = orderedReports - .filter(Boolean) - .map((report) => `${report?.reportID}:${reportAttributes?.[report?.reportID ?? '']?.brickRoadStatus ?? 'none'}`) - .join('|'); - - // Check cache first - if (chatTabBrickRoadCache.has(cacheKey)) { - return chatTabBrickRoadCache.get(cacheKey); - } - - // Compute the result const report = getChatTabBrickRoadReport(orderedReports); - const result = report ? getBrickRoadForPolicy(report) : undefined; - - // Cache the result (limit cache size to prevent memory leaks) - if (chatTabBrickRoadCache.size > 100) { - chatTabBrickRoadCache.clear(); - } - chatTabBrickRoadCache.set(cacheKey, result); - - return result; + return report ? getBrickRoadForPolicy(report) : undefined; } /** From 39b504baa603b6751755b1add0b897b4e3af2506 Mon Sep 17 00:00:00 2001 From: "marta.sudol" Date: Tue, 1 Jul 2025 12:11:33 +0200 Subject: [PATCH 5/6] prettier --- src/components/Navigation/NavigationTabBar/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Navigation/NavigationTabBar/index.tsx b/src/components/Navigation/NavigationTabBar/index.tsx index af7c576dbe5d..9c2760868aff 100644 --- a/src/components/Navigation/NavigationTabBar/index.tsx +++ b/src/components/Navigation/NavigationTabBar/index.tsx @@ -23,7 +23,6 @@ import useWorkspacesTabIndicatorStatus from '@hooks/useWorkspacesTabIndicatorSta import clearSelectedText from '@libs/clearSelectedText/clearSelectedText'; import getPlatform from '@libs/getPlatform'; import interceptAnonymousUser from '@libs/interceptAnonymousUser'; -import Performance from '@libs/Performance'; import {getPreservedNavigatorState} from '@libs/Navigation/AppNavigator/createSplitNavigator/usePreserveNavigatorState'; import { getLastVisitedTabPath, From 2c03c00b935e7952ab647899f43b44d5ddb2193d Mon Sep 17 00:00:00 2001 From: "marta.sudol" Date: Tue, 1 Jul 2025 12:35:56 +0200 Subject: [PATCH 6/6] prettier --- .../Search/FilterDropdowns/DropdownButton.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/Search/FilterDropdowns/DropdownButton.tsx b/src/components/Search/FilterDropdowns/DropdownButton.tsx index 1057e672be0e..aae4f7157b92 100644 --- a/src/components/Search/FilterDropdowns/DropdownButton.tsx +++ b/src/components/Search/FilterDropdowns/DropdownButton.tsx @@ -1,5 +1,5 @@ import type {ReactNode} from 'react'; -import React, {useMemo, useRef, useState} from 'react'; +import React, {useCallback, useMemo, useRef, useState} from 'react'; import type {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import Button from '@components/Button'; @@ -69,7 +69,7 @@ function DropdownButton({label, value, viewportOffsetTop, PopoverComponent}: Dro * Toggle the overlay between open & closed, and re-calculate the * position of the trigger */ - const toggleOverlay = () => { + const toggleOverlay = useCallback(() => { setIsOverlayVisible((previousValue) => { if (!previousValue && willAlertModalBecomeVisible) { return false; @@ -77,7 +77,7 @@ function DropdownButton({label, value, viewportOffsetTop, PopoverComponent}: Dro return !previousValue; }); - }; + }, [willAlertModalBecomeVisible]); /** * When no items are selected, render the label, otherwise, render the @@ -104,7 +104,9 @@ function DropdownButton({label, value, viewportOffsetTop, PopoverComponent}: Dro return null; } return PopoverComponent({closeOverlay: toggleOverlay}); - }, [isOverlayVisible, PopoverComponent, toggleOverlay]); + // PopoverComponent is stable so we don't need it here as a dep. + // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps + }, [isOverlayVisible, toggleOverlay]); return ( <>