From ad07324da996ba98e73b72a4a5fc5baaeb712099 Mon Sep 17 00:00:00 2001 From: "marta.sudol" Date: Thu, 29 May 2025 11:37:46 +0200 Subject: [PATCH 01/22] LoadingBar rendering improvements --- src/components/MoneyReportHeader.tsx | 3 +- src/components/MoneyRequestHeader.tsx | 3 +- src/components/Navigation/TopBar.tsx | 3 +- src/hooks/useLoadingBarVisibility.ts | 47 +++++++++++++++++++++++++++ src/libs/Network/SequentialQueue.ts | 17 ++++++++++ src/libs/Permissions.ts | 3 +- src/libs/actions/App.ts | 16 ++------- src/pages/home/HeaderView.tsx | 3 +- 8 files changed, 76 insertions(+), 19 deletions(-) create mode 100644 src/hooks/useLoadingBarVisibility.ts diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 7c994bd9f05a..251f0002bc10 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -5,6 +5,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import {useOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import useActiveRoute from '@hooks/useActiveRoute'; +import useLoadingBarVisibility from '@hooks/useLoadingBarVisibility'; import useLocalize from '@hooks/useLocalize'; import useMobileSelectionMode from '@hooks/useMobileSelectionMode'; import useNetwork from '@hooks/useNetwork'; @@ -308,7 +309,7 @@ function MoneyReportHeader({ const isAnyTransactionOnHold = hasHeldExpensesReportUtils(moneyRequestReport?.reportID); const {isDelegateAccessRestricted} = useDelegateUserDetails(); const [isNoDelegateAccessMenuVisible, setIsNoDelegateAccessMenuVisible] = useState(false); - const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA, {canBeMissing: true}); + const isLoadingReportData = useLoadingBarVisibility(); const isReportInRHP = route.name === SCREENS.SEARCH.REPORT_RHP; const shouldDisplaySearchRouter = !isReportInRHP || isSmallScreenWidth; diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index 493352ad6ee2..b8173a5ce173 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -5,6 +5,7 @@ import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {useOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; +import useLoadingBarVisibility from '@hooks/useLoadingBarVisibility'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; @@ -82,7 +83,7 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false); const [downloadErrorModalVisible, setDownloadErrorModalVisible] = useState(false); const [dismissedHoldUseExplanation, dismissedHoldUseExplanationResult] = useOnyx(ONYXKEYS.NVP_DISMISSED_HOLD_USE_EXPLANATION, {initialValue: true, canBeMissing: false}); - const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA, {canBeMissing: true}); + const isLoadingReportData = useLoadingBarVisibility(); const isLoadingHoldUseExplained = isLoadingOnyxValue(dismissedHoldUseExplanationResult); const styles = useThemeStyles(); const theme = useTheme(); diff --git a/src/components/Navigation/TopBar.tsx b/src/components/Navigation/TopBar.tsx index dc7299f84536..0a64cfbb8946 100644 --- a/src/components/Navigation/TopBar.tsx +++ b/src/components/Navigation/TopBar.tsx @@ -6,6 +6,7 @@ import {PressableWithoutFeedback} from '@components/Pressable'; import SearchButton from '@components/Search/SearchRouter/SearchButton'; import HelpButton from '@components/SidePanel/HelpComponents/HelpButton'; import Text from '@components/Text'; +import useLoadingBarVisibility from '@hooks/useLoadingBarVisibility'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import SignInButton from '@pages/home/sidebar/SignInButton'; @@ -24,7 +25,7 @@ function TopBar({breadcrumbLabel, shouldDisplaySearch = true, shouldDisplayHelpB const styles = useThemeStyles(); const {translate} = useLocalize(); const [session] = useOnyx(ONYXKEYS.SESSION, {selector: (sessionValue) => sessionValue && {authTokenType: sessionValue.authTokenType}, canBeMissing: true}); - const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA, {canBeMissing: true}); + const isLoadingReportData = useLoadingBarVisibility(); const isAnonymousUser = isAnonymousUserUtil(session); const displaySignIn = isAnonymousUser; diff --git a/src/hooks/useLoadingBarVisibility.ts b/src/hooks/useLoadingBarVisibility.ts new file mode 100644 index 000000000000..9c52b1987207 --- /dev/null +++ b/src/hooks/useLoadingBarVisibility.ts @@ -0,0 +1,47 @@ +import {useCallback, useEffect, useState} from 'react'; +import * as SequentialQueue from '@libs/Network/SequentialQueue'; +import {WRITE_COMMANDS} from '@libs/API/types'; + +const RELEVANT_COMMANDS = [ + WRITE_COMMANDS.OPEN_APP, + WRITE_COMMANDS.RECONNECT_APP, + WRITE_COMMANDS.OPEN_REPORT, +] as const; + +/** + * Hook that determines whether LoadingBar should be visible based on active queue requests + * Replaces the Onyx-based IS_LOADING_REPORT_DATA approach + */ +export default function useLoadingBarVisibility(): boolean { + const [shouldShow, setShouldShow] = useState(false); + + const checkQueueStatus = useCallback(() => { + // Check if queue is running + const isQueueRunning = SequentialQueue.isRunning(); + + if (!isQueueRunning) { + setShouldShow(false); + return; + } + + // Get current request and check if it's a relevant command + const currentRequestCommand: string | null = SequentialQueue.getCurrentRequestCommand(); + const hasRelevantRequest = currentRequestCommand !== null && + (RELEVANT_COMMANDS as readonly string[]).includes(currentRequestCommand); + + setShouldShow(hasRelevantRequest); + }, []); + + useEffect(() => { + // Initial check + checkQueueStatus(); + + // Poll queue status - using a reasonable interval to avoid performance issues + // In the future, this could be replaced with an event-driven approach + const interval = setInterval(checkQueueStatus, 50); + + return () => clearInterval(interval); + }, [checkQueueStatus]); + + return shouldShow; +} diff --git a/src/libs/Network/SequentialQueue.ts b/src/libs/Network/SequentialQueue.ts index e89d1a5e4c5f..0632d1d6788e 100644 --- a/src/libs/Network/SequentialQueue.ts +++ b/src/libs/Network/SequentialQueue.ts @@ -47,6 +47,7 @@ resolveIsReadyPromise?.(); let isSequentialQueueRunning = false; let currentRequestPromise: Promise | null = null; +let currentRequestCommand: string | null = null; let isQueuePaused = false; const sequentialQueueRequestThrottle = new RequestThrottle('SequentialQueue'); @@ -135,9 +136,13 @@ function process(): Promise { const requestToProcess = processNextPersistedRequest(); if (!requestToProcess) { Log.info('[SequentialQueue] Unable to process. No next request to handle.'); + currentRequestCommand = null; return Promise.resolve(); } + // Track the current request command + currentRequestCommand = requestToProcess.command; + // Set the current request to a promise awaiting its processing so that getCurrentRequest can be used to take some action after the current request has processed. currentRequestPromise = processWithMiddleware(requestToProcess, true) .then((response) => { @@ -157,6 +162,7 @@ function process(): Promise { } sequentialQueueRequestThrottle.clear(); + currentRequestCommand = null; return process(); }) .catch((error: RequestError) => { @@ -169,6 +175,7 @@ function process(): Promise { Log.info("[SequentialQueue] Removing persisted request because it failed and doesn't need to be retried.", false, {error, request: requestToProcess}); endPersistedRequestAndRemoveFromQueue(requestToProcess); sequentialQueueRequestThrottle.clear(); + currentRequestCommand = null; return process(); } rollbackOngoingPersistedRequest(); @@ -180,6 +187,7 @@ function process(): Promise { Log.info('[SequentialQueue] Removing persisted request because it failed too many times.', false, {error, request: requestToProcess}); endPersistedRequestAndRemoveFromQueue(requestToProcess); sequentialQueueRequestThrottle.clear(); + currentRequestCommand = null; return process(); }); }); @@ -348,6 +356,13 @@ function getCurrentRequest(): Promise { return currentRequestPromise; } +/** + * Returns the command of the currently processing request + */ +function getCurrentRequestCommand(): string | null { + return currentRequestCommand; +} + /** * Returns a promise that resolves when the sequential queue is done processing all persisted write requests. */ @@ -362,6 +377,7 @@ function waitForIdle(): Promise { function resetQueue(): void { isSequentialQueueRunning = false; currentRequestPromise = null; + currentRequestCommand = null; isQueuePaused = false; isReadyPromise = new Promise((resolve) => { resolveIsReadyPromise = resolve; @@ -372,6 +388,7 @@ function resetQueue(): void { export { flush, getCurrentRequest, + getCurrentRequestCommand, isPaused, isRunning, pause, diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index 24eefbc3de8c..1c9b7ef4380f 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -51,7 +51,8 @@ function canUseCustomRules(betas: OnyxEntry): boolean { } function canUseTableReportView(betas: OnyxEntry): boolean { - return !!betas?.includes(CONST.BETAS.TABLE_REPORT_VIEW) || canUseAllBetas(betas); + // return !!betas?.includes(CONST.BETAS.TABLE_REPORT_VIEW) || canUseAllBetas(betas); + return true; } function canUseTalkToAISales(betas: OnyxEntry): boolean { diff --git a/src/libs/actions/App.ts b/src/libs/actions/App.ts index 8b95c70fc9a6..4bedfbad47e9 100644 --- a/src/libs/actions/App.ts +++ b/src/libs/actions/App.ts @@ -253,21 +253,9 @@ function getPolicyParamsForOpenOrReconnect(): Promise Date: Thu, 29 May 2025 12:05:17 +0200 Subject: [PATCH 02/22] prettier & eslint --- src/hooks/useLoadingBarVisibility.ts | 15 +++++---------- src/libs/Permissions.ts | 3 +-- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/hooks/useLoadingBarVisibility.ts b/src/hooks/useLoadingBarVisibility.ts index 9c52b1987207..72570922ee2d 100644 --- a/src/hooks/useLoadingBarVisibility.ts +++ b/src/hooks/useLoadingBarVisibility.ts @@ -1,12 +1,8 @@ import {useCallback, useEffect, useState} from 'react'; -import * as SequentialQueue from '@libs/Network/SequentialQueue'; import {WRITE_COMMANDS} from '@libs/API/types'; +import * as SequentialQueue from '@libs/Network/SequentialQueue'; -const RELEVANT_COMMANDS = [ - WRITE_COMMANDS.OPEN_APP, - WRITE_COMMANDS.RECONNECT_APP, - WRITE_COMMANDS.OPEN_REPORT, -] as const; +const RELEVANT_COMMANDS = [WRITE_COMMANDS.OPEN_APP, WRITE_COMMANDS.RECONNECT_APP, WRITE_COMMANDS.OPEN_REPORT] as const; /** * Hook that determines whether LoadingBar should be visible based on active queue requests @@ -18,7 +14,7 @@ export default function useLoadingBarVisibility(): boolean { const checkQueueStatus = useCallback(() => { // Check if queue is running const isQueueRunning = SequentialQueue.isRunning(); - + if (!isQueueRunning) { setShouldShow(false); return; @@ -26,9 +22,8 @@ export default function useLoadingBarVisibility(): boolean { // Get current request and check if it's a relevant command const currentRequestCommand: string | null = SequentialQueue.getCurrentRequestCommand(); - const hasRelevantRequest = currentRequestCommand !== null && - (RELEVANT_COMMANDS as readonly string[]).includes(currentRequestCommand); - + const hasRelevantRequest = currentRequestCommand !== null && (RELEVANT_COMMANDS as readonly string[]).includes(currentRequestCommand); + setShouldShow(hasRelevantRequest); }, []); diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index 1c9b7ef4380f..24eefbc3de8c 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -51,8 +51,7 @@ function canUseCustomRules(betas: OnyxEntry): boolean { } function canUseTableReportView(betas: OnyxEntry): boolean { - // return !!betas?.includes(CONST.BETAS.TABLE_REPORT_VIEW) || canUseAllBetas(betas); - return true; + return !!betas?.includes(CONST.BETAS.TABLE_REPORT_VIEW) || canUseAllBetas(betas); } function canUseTalkToAISales(betas: OnyxEntry): boolean { From c0d830acdb3bba00f615f8eda938b1869cfdc164 Mon Sep 17 00:00:00 2001 From: "marta.sudol" Date: Thu, 29 May 2025 12:08:26 +0200 Subject: [PATCH 03/22] prettier & eslint --- src/hooks/useLoadingBarVisibility.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hooks/useLoadingBarVisibility.ts b/src/hooks/useLoadingBarVisibility.ts index 72570922ee2d..9640c4462337 100644 --- a/src/hooks/useLoadingBarVisibility.ts +++ b/src/hooks/useLoadingBarVisibility.ts @@ -1,6 +1,6 @@ import {useCallback, useEffect, useState} from 'react'; import {WRITE_COMMANDS} from '@libs/API/types'; -import * as SequentialQueue from '@libs/Network/SequentialQueue'; +import {getCurrentRequestCommand, isRunning} from '@libs/Network/SequentialQueue'; const RELEVANT_COMMANDS = [WRITE_COMMANDS.OPEN_APP, WRITE_COMMANDS.RECONNECT_APP, WRITE_COMMANDS.OPEN_REPORT] as const; @@ -13,7 +13,7 @@ export default function useLoadingBarVisibility(): boolean { const checkQueueStatus = useCallback(() => { // Check if queue is running - const isQueueRunning = SequentialQueue.isRunning(); + const isQueueRunning = isRunning(); if (!isQueueRunning) { setShouldShow(false); @@ -21,7 +21,7 @@ export default function useLoadingBarVisibility(): boolean { } // Get current request and check if it's a relevant command - const currentRequestCommand: string | null = SequentialQueue.getCurrentRequestCommand(); + const currentRequestCommand: string | null = getCurrentRequestCommand(); const hasRelevantRequest = currentRequestCommand !== null && (RELEVANT_COMMANDS as readonly string[]).includes(currentRequestCommand); setShouldShow(hasRelevantRequest); From 6dc863796432cc6419238554f91ed794df6166dc Mon Sep 17 00:00:00 2001 From: "marta.sudol" Date: Thu, 29 May 2025 15:12:18 +0200 Subject: [PATCH 04/22] prettier & eslint --- src/hooks/useLoadingBarVisibility.ts | 9 ++--- src/libs/Network/SequentialQueue.ts | 56 ++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/src/hooks/useLoadingBarVisibility.ts b/src/hooks/useLoadingBarVisibility.ts index 9640c4462337..c83d10538d34 100644 --- a/src/hooks/useLoadingBarVisibility.ts +++ b/src/hooks/useLoadingBarVisibility.ts @@ -1,6 +1,6 @@ import {useCallback, useEffect, useState} from 'react'; import {WRITE_COMMANDS} from '@libs/API/types'; -import {getCurrentRequestCommand, isRunning} from '@libs/Network/SequentialQueue'; +import {getCurrentRequestCommand, isRunning, subscribeToQueueState} from '@libs/Network/SequentialQueue'; const RELEVANT_COMMANDS = [WRITE_COMMANDS.OPEN_APP, WRITE_COMMANDS.RECONNECT_APP, WRITE_COMMANDS.OPEN_REPORT] as const; @@ -31,11 +31,8 @@ export default function useLoadingBarVisibility(): boolean { // Initial check checkQueueStatus(); - // Poll queue status - using a reasonable interval to avoid performance issues - // In the future, this could be replaced with an event-driven approach - const interval = setInterval(checkQueueStatus, 50); - - return () => clearInterval(interval); + // Subscribe to queue state changes instead of polling + return subscribeToQueueState(checkQueueStatus); }, [checkQueueStatus]); return shouldShow; diff --git a/src/libs/Network/SequentialQueue.ts b/src/libs/Network/SequentialQueue.ts index 0632d1d6788e..71c018c9f773 100644 --- a/src/libs/Network/SequentialQueue.ts +++ b/src/libs/Network/SequentialQueue.ts @@ -51,6 +51,9 @@ let currentRequestCommand: string | null = null; let isQueuePaused = false; const sequentialQueueRequestThrottle = new RequestThrottle('SequentialQueue'); +// Queue state change listeners +const queueStateListeners = new Set<() => void>(); + /** * Puts the queue into a paused state so that no requests will be processed */ @@ -136,13 +139,23 @@ function process(): Promise { const requestToProcess = processNextPersistedRequest(); if (!requestToProcess) { Log.info('[SequentialQueue] Unable to process. No next request to handle.'); + const wasProcessingRelevantRequest = currentRequestCommand !== null; currentRequestCommand = null; + if (wasProcessingRelevantRequest) { + notifyQueueStateChange(); + } return Promise.resolve(); } // Track the current request command + const previousCommand = currentRequestCommand; currentRequestCommand = requestToProcess.command; + // Notify if we started processing a relevant request + if (previousCommand !== currentRequestCommand) { + notifyQueueStateChange(); + } + // Set the current request to a promise awaiting its processing so that getCurrentRequest can be used to take some action after the current request has processed. currentRequestPromise = processWithMiddleware(requestToProcess, true) .then((response) => { @@ -162,7 +175,11 @@ function process(): Promise { } sequentialQueueRequestThrottle.clear(); + const wasProcessingRelevantRequest = currentRequestCommand !== null; currentRequestCommand = null; + if (wasProcessingRelevantRequest) { + notifyQueueStateChange(); + } return process(); }) .catch((error: RequestError) => { @@ -175,7 +192,11 @@ function process(): Promise { Log.info("[SequentialQueue] Removing persisted request because it failed and doesn't need to be retried.", false, {error, request: requestToProcess}); endPersistedRequestAndRemoveFromQueue(requestToProcess); sequentialQueueRequestThrottle.clear(); + const wasProcessingRelevantRequest = currentRequestCommand !== null; currentRequestCommand = null; + if (wasProcessingRelevantRequest) { + notifyQueueStateChange(); + } return process(); } rollbackOngoingPersistedRequest(); @@ -187,7 +208,11 @@ function process(): Promise { Log.info('[SequentialQueue] Removing persisted request because it failed too many times.', false, {error, request: requestToProcess}); endPersistedRequestAndRemoveFromQueue(requestToProcess); sequentialQueueRequestThrottle.clear(); + const wasProcessingRelevantRequest = currentRequestCommand !== null; currentRequestCommand = null; + if (wasProcessingRelevantRequest) { + notifyQueueStateChange(); + } return process(); }); }); @@ -356,6 +381,29 @@ function getCurrentRequest(): Promise { return currentRequestPromise; } +/** + * Notify all listeners that queue state has changed + */ +function notifyQueueStateChange() { + queueStateListeners.forEach((callback) => { + try { + callback(); + } catch (error) { + Log.warn('[SequentialQueue] Error in queue state listener', {error}); + } + }); +} + +/** + * Subscribe to queue state changes + * @param callback Function to call when queue state changes + * @returns Unsubscribe function + */ +function subscribeToQueueState(callback: () => void): () => void { + queueStateListeners.add(callback); + return () => queueStateListeners.delete(callback); +} + /** * Returns the command of the currently processing request */ @@ -375,6 +423,7 @@ function waitForIdle(): Promise { * This is to prevent previous requests interfering with other tests */ function resetQueue(): void { + const wasProcessingRelevantRequest = currentRequestCommand !== null; isSequentialQueueRunning = false; currentRequestPromise = null; currentRequestCommand = null; @@ -383,6 +432,12 @@ function resetQueue(): void { resolveIsReadyPromise = resolve; }); resolveIsReadyPromise?.(); + + // Clear all listeners and notify of state change if needed + queueStateListeners.clear(); + if (wasProcessingRelevantRequest) { + notifyQueueStateChange(); + } } export { @@ -396,6 +451,7 @@ export { push, resetQueue, sequentialQueueRequestThrottle, + subscribeToQueueState, unpause, waitForIdle, getQueueFlushedData, From 5b409509339c598d6e7c2e1feae6b71b86710190 Mon Sep 17 00:00:00 2001 From: "marta.sudol" Date: Fri, 30 May 2025 12:47:26 +0200 Subject: [PATCH 05/22] use queueFlushedData --- src/hooks/useLoadingBarVisibility.ts | 32 +++++++++++++++++----------- src/libs/actions/App.ts | 5 +++++ src/libs/actions/Report.ts | 12 +++++++++-- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/hooks/useLoadingBarVisibility.ts b/src/hooks/useLoadingBarVisibility.ts index c83d10538d34..3546b7c04f66 100644 --- a/src/hooks/useLoadingBarVisibility.ts +++ b/src/hooks/useLoadingBarVisibility.ts @@ -2,38 +2,44 @@ import {useCallback, useEffect, useState} from 'react'; import {WRITE_COMMANDS} from '@libs/API/types'; import {getCurrentRequestCommand, isRunning, subscribeToQueueState} from '@libs/Network/SequentialQueue'; -const RELEVANT_COMMANDS = [WRITE_COMMANDS.OPEN_APP, WRITE_COMMANDS.RECONNECT_APP, WRITE_COMMANDS.OPEN_REPORT] as const; +// Commands that should trigger the LoadingBar to show +const RELEVANT_COMMANDS = [ + WRITE_COMMANDS.OPEN_APP, + WRITE_COMMANDS.RECONNECT_APP, + WRITE_COMMANDS.OPEN_REPORT +] as const; /** * Hook that determines whether LoadingBar should be visible based on active queue requests - * Replaces the Onyx-based IS_LOADING_REPORT_DATA approach + * Shows LoadingBar when OpenReport/OpenApp/ReconnectApp requests are being processed */ export default function useLoadingBarVisibility(): boolean { - const [shouldShow, setShouldShow] = useState(false); + const [isRelevantQueueActive, setIsRelevantQueueActive] = useState(false); - const checkQueueStatus = useCallback(() => { + const checkRelevantQueue = useCallback(() => { // Check if queue is running const isQueueRunning = isRunning(); if (!isQueueRunning) { - setShouldShow(false); + setIsRelevantQueueActive(false); return; } // Get current request and check if it's a relevant command - const currentRequestCommand: string | null = getCurrentRequestCommand(); - const hasRelevantRequest = currentRequestCommand !== null && (RELEVANT_COMMANDS as readonly string[]).includes(currentRequestCommand); + const currentCommand = getCurrentRequestCommand(); + const hasRelevantCommand = currentCommand && + (RELEVANT_COMMANDS as readonly string[]).includes(currentCommand); - setShouldShow(hasRelevantRequest); + setIsRelevantQueueActive(!!hasRelevantCommand); }, []); useEffect(() => { // Initial check - checkQueueStatus(); + checkRelevantQueue(); - // Subscribe to queue state changes instead of polling - return subscribeToQueueState(checkQueueStatus); - }, [checkQueueStatus]); + // Subscribe to queue state changes for real-time updates + return subscribeToQueueState(checkRelevantQueue); + }, [checkRelevantQueue]); - return shouldShow; + return isRelevantQueueActive; } diff --git a/src/libs/actions/App.ts b/src/libs/actions/App.ts index 4bedfbad47e9..eea65c6e7d73 100644 --- a/src/libs/actions/App.ts +++ b/src/libs/actions/App.ts @@ -262,6 +262,11 @@ function getOnyxDataForOpenOrReconnect(isOpenApp = false, isFullReconnect = fals key: ONYXKEYS.HAS_LOADED_APP, value: true, }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.IS_LOADING_REPORT_DATA, + value: false, + }, ], }; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 2de7dda4a5b0..850b0811647f 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1044,6 +1044,14 @@ function openReport( const finallyData: OnyxUpdate[] = []; + const queueFlushedData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.IS_LOADING_REPORT_DATA, + value: false, + }, + ]; + const parameters: OpenReportParams = { reportID, reportActionID, @@ -1309,10 +1317,10 @@ function openReport( value: false, }); - API.paginate(CONST.API_REQUEST_TYPE.WRITE, WRITE_COMMANDS.OPEN_REPORT, parameters, {optimisticData, successData, failureData, finallyData}, paginationConfig); + API.paginate(CONST.API_REQUEST_TYPE.WRITE, WRITE_COMMANDS.OPEN_REPORT, parameters, {optimisticData, successData, failureData, finallyData, queueFlushedData}, paginationConfig); } else { // eslint-disable-next-line rulesdir/no-multiple-api-calls - API.paginate(CONST.API_REQUEST_TYPE.WRITE, WRITE_COMMANDS.OPEN_REPORT, parameters, {optimisticData, successData, failureData, finallyData}, paginationConfig, { + API.paginate(CONST.API_REQUEST_TYPE.WRITE, WRITE_COMMANDS.OPEN_REPORT, parameters, {optimisticData, successData, failureData, finallyData, queueFlushedData}, paginationConfig, { checkAndFixConflictingRequest: (persistedRequests) => resolveOpenReportDuplicationConflictAction(persistedRequests, parameters), }); } From 2058dca2437f9da22c6d21ad0977962299ca07dc Mon Sep 17 00:00:00 2001 From: "marta.sudol" Date: Fri, 30 May 2025 12:54:38 +0200 Subject: [PATCH 06/22] use ongoingRequest instead of currentRequestCommand --- src/hooks/useLoadingBarVisibility.ts | 17 +++------ src/libs/Network/SequentialQueue.ts | 54 ++++++---------------------- 2 files changed, 15 insertions(+), 56 deletions(-) diff --git a/src/hooks/useLoadingBarVisibility.ts b/src/hooks/useLoadingBarVisibility.ts index 3546b7c04f66..112efbc0dece 100644 --- a/src/hooks/useLoadingBarVisibility.ts +++ b/src/hooks/useLoadingBarVisibility.ts @@ -1,6 +1,6 @@ import {useCallback, useEffect, useState} from 'react'; import {WRITE_COMMANDS} from '@libs/API/types'; -import {getCurrentRequestCommand, isRunning, subscribeToQueueState} from '@libs/Network/SequentialQueue'; +import {getOngoingRequest, subscribeToQueueState} from '@libs/Network/SequentialQueue'; // Commands that should trigger the LoadingBar to show const RELEVANT_COMMANDS = [ @@ -17,18 +17,9 @@ export default function useLoadingBarVisibility(): boolean { const [isRelevantQueueActive, setIsRelevantQueueActive] = useState(false); const checkRelevantQueue = useCallback(() => { - // Check if queue is running - const isQueueRunning = isRunning(); - - if (!isQueueRunning) { - setIsRelevantQueueActive(false); - return; - } - - // Get current request and check if it's a relevant command - const currentCommand = getCurrentRequestCommand(); - const hasRelevantCommand = currentCommand && - (RELEVANT_COMMANDS as readonly string[]).includes(currentCommand); + const ongoingRequest = getOngoingRequest(); + const hasRelevantCommand = ongoingRequest && + (RELEVANT_COMMANDS as readonly string[]).includes(ongoingRequest.command); setIsRelevantQueueActive(!!hasRelevantCommand); }, []); diff --git a/src/libs/Network/SequentialQueue.ts b/src/libs/Network/SequentialQueue.ts index 71c018c9f773..b384a4890927 100644 --- a/src/libs/Network/SequentialQueue.ts +++ b/src/libs/Network/SequentialQueue.ts @@ -4,6 +4,7 @@ import { deleteRequestsByIndices as deletePersistedRequestsByIndices, endRequestAndRemoveFromQueue as endPersistedRequestAndRemoveFromQueue, getAll as getAllPersistedRequests, + getOngoingRequest, processNextRequest as processNextPersistedRequest, rollbackOngoingRequest as rollbackOngoingPersistedRequest, save as savePersistedRequest, @@ -47,7 +48,6 @@ resolveIsReadyPromise?.(); let isSequentialQueueRunning = false; let currentRequestPromise: Promise | null = null; -let currentRequestCommand: string | null = null; let isQueuePaused = false; const sequentialQueueRequestThrottle = new RequestThrottle('SequentialQueue'); @@ -136,25 +136,16 @@ function process(): Promise { return Promise.resolve(); } + // Track the current request command const requestToProcess = processNextPersistedRequest(); if (!requestToProcess) { Log.info('[SequentialQueue] Unable to process. No next request to handle.'); - const wasProcessingRelevantRequest = currentRequestCommand !== null; - currentRequestCommand = null; - if (wasProcessingRelevantRequest) { - notifyQueueStateChange(); - } + notifyQueueStateChange(); return Promise.resolve(); } - // Track the current request command - const previousCommand = currentRequestCommand; - currentRequestCommand = requestToProcess.command; - - // Notify if we started processing a relevant request - if (previousCommand !== currentRequestCommand) { - notifyQueueStateChange(); - } + // Notify queue state change when processing starts + notifyQueueStateChange(); // Set the current request to a promise awaiting its processing so that getCurrentRequest can be used to take some action after the current request has processed. currentRequestPromise = processWithMiddleware(requestToProcess, true) @@ -175,11 +166,7 @@ function process(): Promise { } sequentialQueueRequestThrottle.clear(); - const wasProcessingRelevantRequest = currentRequestCommand !== null; - currentRequestCommand = null; - if (wasProcessingRelevantRequest) { - notifyQueueStateChange(); - } + notifyQueueStateChange(); return process(); }) .catch((error: RequestError) => { @@ -192,11 +179,7 @@ function process(): Promise { Log.info("[SequentialQueue] Removing persisted request because it failed and doesn't need to be retried.", false, {error, request: requestToProcess}); endPersistedRequestAndRemoveFromQueue(requestToProcess); sequentialQueueRequestThrottle.clear(); - const wasProcessingRelevantRequest = currentRequestCommand !== null; - currentRequestCommand = null; - if (wasProcessingRelevantRequest) { - notifyQueueStateChange(); - } + notifyQueueStateChange(); return process(); } rollbackOngoingPersistedRequest(); @@ -208,11 +191,7 @@ function process(): Promise { Log.info('[SequentialQueue] Removing persisted request because it failed too many times.', false, {error, request: requestToProcess}); endPersistedRequestAndRemoveFromQueue(requestToProcess); sequentialQueueRequestThrottle.clear(); - const wasProcessingRelevantRequest = currentRequestCommand !== null; - currentRequestCommand = null; - if (wasProcessingRelevantRequest) { - notifyQueueStateChange(); - } + notifyQueueStateChange(); return process(); }); }); @@ -404,13 +383,6 @@ function subscribeToQueueState(callback: () => void): () => void { return () => queueStateListeners.delete(callback); } -/** - * Returns the command of the currently processing request - */ -function getCurrentRequestCommand(): string | null { - return currentRequestCommand; -} - /** * Returns a promise that resolves when the sequential queue is done processing all persisted write requests. */ @@ -423,27 +395,23 @@ function waitForIdle(): Promise { * This is to prevent previous requests interfering with other tests */ function resetQueue(): void { - const wasProcessingRelevantRequest = currentRequestCommand !== null; isSequentialQueueRunning = false; currentRequestPromise = null; - currentRequestCommand = null; isQueuePaused = false; isReadyPromise = new Promise((resolve) => { resolveIsReadyPromise = resolve; }); resolveIsReadyPromise?.(); - // Clear all listeners and notify of state change if needed + // Clear all listeners queueStateListeners.clear(); - if (wasProcessingRelevantRequest) { - notifyQueueStateChange(); - } + notifyQueueStateChange(); } export { flush, getCurrentRequest, - getCurrentRequestCommand, + getOngoingRequest, isPaused, isRunning, pause, From c7a5e40f3697ba89705f188e3c9a74f22cc905e9 Mon Sep 17 00:00:00 2001 From: "marta.sudol" Date: Fri, 30 May 2025 12:57:50 +0200 Subject: [PATCH 07/22] set for relevant commands --- src/hooks/useLoadingBarVisibility.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/hooks/useLoadingBarVisibility.ts b/src/hooks/useLoadingBarVisibility.ts index 112efbc0dece..2354a84e6ec7 100644 --- a/src/hooks/useLoadingBarVisibility.ts +++ b/src/hooks/useLoadingBarVisibility.ts @@ -3,11 +3,7 @@ import {WRITE_COMMANDS} from '@libs/API/types'; import {getOngoingRequest, subscribeToQueueState} from '@libs/Network/SequentialQueue'; // Commands that should trigger the LoadingBar to show -const RELEVANT_COMMANDS = [ - WRITE_COMMANDS.OPEN_APP, - WRITE_COMMANDS.RECONNECT_APP, - WRITE_COMMANDS.OPEN_REPORT -] as const; +const RELEVANT_COMMANDS = new Set([WRITE_COMMANDS.OPEN_APP, WRITE_COMMANDS.RECONNECT_APP, WRITE_COMMANDS.OPEN_REPORT]); /** * Hook that determines whether LoadingBar should be visible based on active queue requests @@ -18,8 +14,7 @@ export default function useLoadingBarVisibility(): boolean { const checkRelevantQueue = useCallback(() => { const ongoingRequest = getOngoingRequest(); - const hasRelevantCommand = ongoingRequest && - (RELEVANT_COMMANDS as readonly string[]).includes(ongoingRequest.command); + const hasRelevantCommand = ongoingRequest && RELEVANT_COMMANDS.has(ongoingRequest.command); setIsRelevantQueueActive(!!hasRelevantCommand); }, []); From 8da76696a864baa599de3af2937ce8aa00a71593 Mon Sep 17 00:00:00 2001 From: "marta.sudol" Date: Fri, 30 May 2025 13:00:57 +0200 Subject: [PATCH 08/22] removed notifyQueueStateChange call after clearing --- src/libs/Network/SequentialQueue.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/Network/SequentialQueue.ts b/src/libs/Network/SequentialQueue.ts index b384a4890927..4c3964cca4ee 100644 --- a/src/libs/Network/SequentialQueue.ts +++ b/src/libs/Network/SequentialQueue.ts @@ -405,7 +405,6 @@ function resetQueue(): void { // Clear all listeners queueStateListeners.clear(); - notifyQueueStateChange(); } export { From 6aa3601ab2429fa9a95745190b0d40000af3808c Mon Sep 17 00:00:00 2001 From: "marta.sudol" Date: Fri, 30 May 2025 13:10:24 +0200 Subject: [PATCH 09/22] avoiding rerenders --- src/hooks/useLoadingBarVisibility.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/hooks/useLoadingBarVisibility.ts b/src/hooks/useLoadingBarVisibility.ts index 2354a84e6ec7..6f375e21f302 100644 --- a/src/hooks/useLoadingBarVisibility.ts +++ b/src/hooks/useLoadingBarVisibility.ts @@ -16,7 +16,14 @@ export default function useLoadingBarVisibility(): boolean { const ongoingRequest = getOngoingRequest(); const hasRelevantCommand = ongoingRequest && RELEVANT_COMMANDS.has(ongoingRequest.command); - setIsRelevantQueueActive(!!hasRelevantCommand); + const shouldShowLoadingBar = !!hasRelevantCommand; + + setIsRelevantQueueActive(previousValue => { + if (previousValue === shouldShowLoadingBar) { + return previousValue; // No change, avoid re-render + } + return shouldShowLoadingBar; + }); }, []); useEffect(() => { From d65371b7ffcb4e5857f819c04b6355fb5c00d3fe Mon Sep 17 00:00:00 2001 From: "marta.sudol" Date: Fri, 30 May 2025 13:16:22 +0200 Subject: [PATCH 10/22] prettier --- src/hooks/useLoadingBarVisibility.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useLoadingBarVisibility.ts b/src/hooks/useLoadingBarVisibility.ts index 6f375e21f302..473adb18fdbf 100644 --- a/src/hooks/useLoadingBarVisibility.ts +++ b/src/hooks/useLoadingBarVisibility.ts @@ -18,7 +18,7 @@ export default function useLoadingBarVisibility(): boolean { const shouldShowLoadingBar = !!hasRelevantCommand; - setIsRelevantQueueActive(previousValue => { + setIsRelevantQueueActive((previousValue) => { if (previousValue === shouldShowLoadingBar) { return previousValue; // No change, avoid re-render } From ebb3b1e6d744c5d9b05a6de3b838ab2fd5fe0b6b Mon Sep 17 00:00:00 2001 From: "marta.sudol" Date: Mon, 2 Jun 2025 13:23:54 +0200 Subject: [PATCH 11/22] reverted `IS_LOADING_REPORT_DATA` changes --- src/libs/actions/App.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/App.ts b/src/libs/actions/App.ts index eea65c6e7d73..ab3652538594 100644 --- a/src/libs/actions/App.ts +++ b/src/libs/actions/App.ts @@ -253,9 +253,21 @@ function getPolicyParamsForOpenOrReconnect(): Promise Date: Tue, 3 Jun 2025 19:25:33 +0200 Subject: [PATCH 12/22] pr comments --- src/hooks/useLoadingBarVisibility.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/hooks/useLoadingBarVisibility.ts b/src/hooks/useLoadingBarVisibility.ts index 473adb18fdbf..2f6701999c6d 100644 --- a/src/hooks/useLoadingBarVisibility.ts +++ b/src/hooks/useLoadingBarVisibility.ts @@ -18,12 +18,7 @@ export default function useLoadingBarVisibility(): boolean { const shouldShowLoadingBar = !!hasRelevantCommand; - setIsRelevantQueueActive((previousValue) => { - if (previousValue === shouldShowLoadingBar) { - return previousValue; // No change, avoid re-render - } - return shouldShowLoadingBar; - }); + setIsRelevantQueueActive(shouldShowLoadingBar); }, []); useEffect(() => { From 4130fb072df58a7cdcf7a2ecc27f1bf5400fb38f Mon Sep 17 00:00:00 2001 From: "marta.sudol" Date: Mon, 9 Jun 2025 08:55:07 +0200 Subject: [PATCH 13/22] listens for persisted requests instead of directly for sequential queue --- src/hooks/useLoadingBarVisibility.ts | 29 +++++++------------- src/libs/Network/SequentialQueue.ts | 40 ---------------------------- src/libs/actions/App.ts | 5 ---- src/libs/actions/Report.ts | 12 ++------- 4 files changed, 11 insertions(+), 75 deletions(-) diff --git a/src/hooks/useLoadingBarVisibility.ts b/src/hooks/useLoadingBarVisibility.ts index 2f6701999c6d..1fc3ae444ba8 100644 --- a/src/hooks/useLoadingBarVisibility.ts +++ b/src/hooks/useLoadingBarVisibility.ts @@ -1,6 +1,6 @@ -import {useCallback, useEffect, useState} from 'react'; import {WRITE_COMMANDS} from '@libs/API/types'; -import {getOngoingRequest, subscribeToQueueState} from '@libs/Network/SequentialQueue'; +import ONYXKEYS from '@src/ONYXKEYS'; +import useOnyx from './useOnyx'; // Commands that should trigger the LoadingBar to show const RELEVANT_COMMANDS = new Set([WRITE_COMMANDS.OPEN_APP, WRITE_COMMANDS.RECONNECT_APP, WRITE_COMMANDS.OPEN_REPORT]); @@ -10,24 +10,13 @@ const RELEVANT_COMMANDS = new Set([WRITE_COMMANDS.OPEN_APP, WRITE_COMMAN * Shows LoadingBar when OpenReport/OpenApp/ReconnectApp requests are being processed */ export default function useLoadingBarVisibility(): boolean { - const [isRelevantQueueActive, setIsRelevantQueueActive] = useState(false); + const [req] = useOnyx(ONYXKEYS.PERSISTED_REQUESTS, {canBeMissing: false}); + const [network] = useOnyx(ONYXKEYS.NETWORK); - const checkRelevantQueue = useCallback(() => { - const ongoingRequest = getOngoingRequest(); - const hasRelevantCommand = ongoingRequest && RELEVANT_COMMANDS.has(ongoingRequest.command); + // Don't show loading bar if currently offline + if (network?.isOffline) { + return false; + } - const shouldShowLoadingBar = !!hasRelevantCommand; - - setIsRelevantQueueActive(shouldShowLoadingBar); - }, []); - - useEffect(() => { - // Initial check - checkRelevantQueue(); - - // Subscribe to queue state changes for real-time updates - return subscribeToQueueState(checkRelevantQueue); - }, [checkRelevantQueue]); - - return isRelevantQueueActive; + return req?.some((request) => RELEVANT_COMMANDS.has(request.command) && !request.initiatedOffline) ?? false; } diff --git a/src/libs/Network/SequentialQueue.ts b/src/libs/Network/SequentialQueue.ts index 4c3964cca4ee..e89d1a5e4c5f 100644 --- a/src/libs/Network/SequentialQueue.ts +++ b/src/libs/Network/SequentialQueue.ts @@ -4,7 +4,6 @@ import { deleteRequestsByIndices as deletePersistedRequestsByIndices, endRequestAndRemoveFromQueue as endPersistedRequestAndRemoveFromQueue, getAll as getAllPersistedRequests, - getOngoingRequest, processNextRequest as processNextPersistedRequest, rollbackOngoingRequest as rollbackOngoingPersistedRequest, save as savePersistedRequest, @@ -51,9 +50,6 @@ let currentRequestPromise: Promise | null = null; let isQueuePaused = false; const sequentialQueueRequestThrottle = new RequestThrottle('SequentialQueue'); -// Queue state change listeners -const queueStateListeners = new Set<() => void>(); - /** * Puts the queue into a paused state so that no requests will be processed */ @@ -136,17 +132,12 @@ function process(): Promise { return Promise.resolve(); } - // Track the current request command const requestToProcess = processNextPersistedRequest(); if (!requestToProcess) { Log.info('[SequentialQueue] Unable to process. No next request to handle.'); - notifyQueueStateChange(); return Promise.resolve(); } - // Notify queue state change when processing starts - notifyQueueStateChange(); - // Set the current request to a promise awaiting its processing so that getCurrentRequest can be used to take some action after the current request has processed. currentRequestPromise = processWithMiddleware(requestToProcess, true) .then((response) => { @@ -166,7 +157,6 @@ function process(): Promise { } sequentialQueueRequestThrottle.clear(); - notifyQueueStateChange(); return process(); }) .catch((error: RequestError) => { @@ -179,7 +169,6 @@ function process(): Promise { Log.info("[SequentialQueue] Removing persisted request because it failed and doesn't need to be retried.", false, {error, request: requestToProcess}); endPersistedRequestAndRemoveFromQueue(requestToProcess); sequentialQueueRequestThrottle.clear(); - notifyQueueStateChange(); return process(); } rollbackOngoingPersistedRequest(); @@ -191,7 +180,6 @@ function process(): Promise { Log.info('[SequentialQueue] Removing persisted request because it failed too many times.', false, {error, request: requestToProcess}); endPersistedRequestAndRemoveFromQueue(requestToProcess); sequentialQueueRequestThrottle.clear(); - notifyQueueStateChange(); return process(); }); }); @@ -360,29 +348,6 @@ function getCurrentRequest(): Promise { return currentRequestPromise; } -/** - * Notify all listeners that queue state has changed - */ -function notifyQueueStateChange() { - queueStateListeners.forEach((callback) => { - try { - callback(); - } catch (error) { - Log.warn('[SequentialQueue] Error in queue state listener', {error}); - } - }); -} - -/** - * Subscribe to queue state changes - * @param callback Function to call when queue state changes - * @returns Unsubscribe function - */ -function subscribeToQueueState(callback: () => void): () => void { - queueStateListeners.add(callback); - return () => queueStateListeners.delete(callback); -} - /** * Returns a promise that resolves when the sequential queue is done processing all persisted write requests. */ @@ -402,15 +367,11 @@ function resetQueue(): void { resolveIsReadyPromise = resolve; }); resolveIsReadyPromise?.(); - - // Clear all listeners - queueStateListeners.clear(); } export { flush, getCurrentRequest, - getOngoingRequest, isPaused, isRunning, pause, @@ -418,7 +379,6 @@ export { push, resetQueue, sequentialQueueRequestThrottle, - subscribeToQueueState, unpause, waitForIdle, getQueueFlushedData, diff --git a/src/libs/actions/App.ts b/src/libs/actions/App.ts index b89f8ec4cadc..8426468d9e84 100644 --- a/src/libs/actions/App.ts +++ b/src/libs/actions/App.ts @@ -274,11 +274,6 @@ function getOnyxDataForOpenOrReconnect(isOpenApp = false, isFullReconnect = fals key: ONYXKEYS.HAS_LOADED_APP, value: true, }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.IS_LOADING_REPORT_DATA, - value: false, - }, ], }; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index c86cd7270bc6..2c002e960395 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1046,14 +1046,6 @@ function openReport( const finallyData: OnyxUpdate[] = []; - const queueFlushedData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.IS_LOADING_REPORT_DATA, - value: false, - }, - ]; - const parameters: OpenReportParams = { reportID, reportActionID, @@ -1319,10 +1311,10 @@ function openReport( value: false, }); - API.paginate(CONST.API_REQUEST_TYPE.WRITE, WRITE_COMMANDS.OPEN_REPORT, parameters, {optimisticData, successData, failureData, finallyData, queueFlushedData}, paginationConfig); + API.paginate(CONST.API_REQUEST_TYPE.WRITE, WRITE_COMMANDS.OPEN_REPORT, parameters, {optimisticData, successData, failureData, finallyData}, paginationConfig); } else { // eslint-disable-next-line rulesdir/no-multiple-api-calls - API.paginate(CONST.API_REQUEST_TYPE.WRITE, WRITE_COMMANDS.OPEN_REPORT, parameters, {optimisticData, successData, failureData, finallyData, queueFlushedData}, paginationConfig, { + API.paginate(CONST.API_REQUEST_TYPE.WRITE, WRITE_COMMANDS.OPEN_REPORT, parameters, {optimisticData, successData, failureData, finallyData}, paginationConfig, { checkAndFixConflictingRequest: (persistedRequests) => resolveOpenReportDuplicationConflictAction(persistedRequests, parameters), }); } From 8b75c819b12a4c1e37c0f8ad60924f61c0730a32 Mon Sep 17 00:00:00 2001 From: "marta.sudol" Date: Mon, 9 Jun 2025 08:59:02 +0200 Subject: [PATCH 14/22] eslint --- src/hooks/useLoadingBarVisibility.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useLoadingBarVisibility.ts b/src/hooks/useLoadingBarVisibility.ts index 1fc3ae444ba8..851de6afbd03 100644 --- a/src/hooks/useLoadingBarVisibility.ts +++ b/src/hooks/useLoadingBarVisibility.ts @@ -11,7 +11,7 @@ const RELEVANT_COMMANDS = new Set([WRITE_COMMANDS.OPEN_APP, WRITE_COMMAN */ export default function useLoadingBarVisibility(): boolean { const [req] = useOnyx(ONYXKEYS.PERSISTED_REQUESTS, {canBeMissing: false}); - const [network] = useOnyx(ONYXKEYS.NETWORK); + const [network] = useOnyx(ONYXKEYS.NETWORK, {canBeMissing: false}); // Don't show loading bar if currently offline if (network?.isOffline) { From 0173685d3b8f9d8cda194b37d9bbd817c44caf92 Mon Sep 17 00:00:00 2001 From: "marta.sudol" Date: Thu, 12 Jun 2025 09:01:27 +0200 Subject: [PATCH 15/22] rename isLoadingReportData to shouldShowLoadingBar --- src/components/MoneyReportHeader.tsx | 4 ++-- src/components/MoneyRequestHeader.tsx | 4 ++-- src/components/Navigation/TopBar.tsx | 4 ++-- src/pages/home/HeaderView.tsx | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 43f258db9425..995308151f96 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -298,7 +298,7 @@ function MoneyReportHeader({ const isAnyTransactionOnHold = hasHeldExpensesReportUtils(moneyRequestReport?.reportID); const {isDelegateAccessRestricted} = useDelegateUserDetails(); const [isNoDelegateAccessMenuVisible, setIsNoDelegateAccessMenuVisible] = useState(false); - const isLoadingReportData = useLoadingBarVisibility(); + const shouldShowLoadingBar = useLoadingBarVisibility(); const isReportInRHP = route.name === SCREENS.SEARCH.REPORT_RHP; const shouldDisplaySearchRouter = !isReportInRHP || isSmallScreenWidth; @@ -998,7 +998,7 @@ function MoneyReportHeader({ )} )} - + {isHoldMenuVisible && requestType !== undefined && ( )} - + sessionValue && {authTokenType: sessionValue.authTokenType}, canBeMissing: true}); - const isLoadingReportData = useLoadingBarVisibility(); + const shouldShowLoadingBarForReports = useLoadingBarVisibility(); const isAnonymousUser = isAnonymousUserUtil(session); const displaySignIn = isAnonymousUser; @@ -64,7 +64,7 @@ function TopBar({breadcrumbLabel, shouldDisplaySearch = true, shouldDisplayHelpB {shouldDisplayHelpButton && } {displaySearch && } - + ); } diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx index d9e360583880..b808fc314844 100644 --- a/src/pages/home/HeaderView.tsx +++ b/src/pages/home/HeaderView.tsx @@ -112,7 +112,7 @@ function HeaderView({report, parentReportAction, onNavigationMenuButtonClicked, const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(report?.parentReportID) ?? getNonEmptyStringOnyxID(report?.reportID)}`, {canBeMissing: true}); const policy = usePolicy(report?.policyID); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: true}); - const isLoadingReportData = useLoadingBarVisibility(); + const shouldShowLoadingBar = useLoadingBarVisibility(); const [firstDayFreeTrial] = useOnyx(ONYXKEYS.NVP_FIRST_DAY_FREE_TRIAL, {canBeMissing: true}); const [lastDayFreeTrial] = useOnyx(ONYXKEYS.NVP_LAST_DAY_FREE_TRIAL, {canBeMissing: true}); const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: true}); @@ -398,7 +398,7 @@ function HeaderView({report, parentReportAction, onNavigationMenuButtonClicked, )} - + {shouldShowEarlyDiscountBanner && ( Date: Fri, 13 Jun 2025 18:43:17 +0200 Subject: [PATCH 16/22] prettier & eslint --- src/components/MoneyReportHeader.tsx | 2 +- src/components/MoneyRequestHeader.tsx | 2 +- src/components/Navigation/TopBar.tsx | 2 +- src/pages/home/HeaderView.tsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index d3a3345527d9..8e382185c27d 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -1000,7 +1000,7 @@ function MoneyReportHeader({ )} )} - + {isHoldMenuVisible && requestType !== undefined && ( )} - + } {displaySearch && } - + ); } diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx index b808fc314844..316ae0d5e539 100644 --- a/src/pages/home/HeaderView.tsx +++ b/src/pages/home/HeaderView.tsx @@ -398,7 +398,7 @@ function HeaderView({report, parentReportAction, onNavigationMenuButtonClicked, )} - + {shouldShowEarlyDiscountBanner && ( Date: Wed, 18 Jun 2025 08:47:46 +0200 Subject: [PATCH 17/22] Onyx.set usage instead of in-memory let --- src/libs/actions/PersistedRequests.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/PersistedRequests.ts b/src/libs/actions/PersistedRequests.ts index ad2e3285ba7c..633e12c776d1 100644 --- a/src/libs/actions/PersistedRequests.ts +++ b/src/libs/actions/PersistedRequests.ts @@ -18,8 +18,8 @@ Onyx.connect({ // We try to remove the next request from the persistedRequests if it is the same as ongoingRequest // so we don't process it twice. - if (isEqual(nextRequestToProcess, ongoingRequest)) { - persistedRequests = persistedRequests.slice(1); + if (isEqual(nextRequestToProcess, ongoingRequest) && val?.length) { + Onyx.set(ONYXKEYS.PERSISTED_REQUESTS, val.slice(1)); } } }, From a5c5d576af1d163cb30d8c720dfb348a2537c9b6 Mon Sep 17 00:00:00 2001 From: "marta.sudol" Date: Wed, 18 Jun 2025 09:06:15 +0200 Subject: [PATCH 18/22] onyx.set --- src/libs/actions/PersistedRequests.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/PersistedRequests.ts b/src/libs/actions/PersistedRequests.ts index 2ed61cf7318a..0046030400a9 100644 --- a/src/libs/actions/PersistedRequests.ts +++ b/src/libs/actions/PersistedRequests.ts @@ -18,8 +18,8 @@ Onyx.connect({ // We try to remove the next request from the persistedRequests if it is the same as ongoingRequest // so we don't process it twice. - if (deepEqual(nextRequestToProcess, ongoingRequest)) { - persistedRequests = persistedRequests.slice(1); + if (deepEqual(nextRequestToProcess, ongoingRequest) && val?.length) { + Onyx.set(ONYXKEYS.PERSISTED_REQUESTS, val.slice(1)); } } }, From 0b9b84385861a8501262f2527f31d5aeb61840e8 Mon Sep 17 00:00:00 2001 From: "marta.sudol" Date: Fri, 20 Jun 2025 11:14:00 +0200 Subject: [PATCH 19/22] use ongoing requests Onyx key to handle requests from Pusher --- src/hooks/useLoadingBarVisibility.ts | 8 ++++++-- src/libs/actions/PersistedRequests.ts | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/hooks/useLoadingBarVisibility.ts b/src/hooks/useLoadingBarVisibility.ts index 851de6afbd03..ffb7e435d573 100644 --- a/src/hooks/useLoadingBarVisibility.ts +++ b/src/hooks/useLoadingBarVisibility.ts @@ -10,7 +10,8 @@ const RELEVANT_COMMANDS = new Set([WRITE_COMMANDS.OPEN_APP, WRITE_COMMAN * Shows LoadingBar when OpenReport/OpenApp/ReconnectApp requests are being processed */ export default function useLoadingBarVisibility(): boolean { - const [req] = useOnyx(ONYXKEYS.PERSISTED_REQUESTS, {canBeMissing: false}); + const [persistedRequests] = useOnyx(ONYXKEYS.PERSISTED_REQUESTS, {canBeMissing: false}); + const [ongoingRequests] = useOnyx(ONYXKEYS.PERSISTED_ONGOING_REQUESTS, {canBeMissing: false}); const [network] = useOnyx(ONYXKEYS.NETWORK, {canBeMissing: false}); // Don't show loading bar if currently offline @@ -18,5 +19,8 @@ export default function useLoadingBarVisibility(): boolean { return false; } - return req?.some((request) => RELEVANT_COMMANDS.has(request.command) && !request.initiatedOffline) ?? false; + const hasPersistedRequests = persistedRequests?.some((request) => RELEVANT_COMMANDS.has(request.command) && !request.initiatedOffline) ?? false; + const hasOngoingRequests = !!ongoingRequests && RELEVANT_COMMANDS.has(ongoingRequests?.command); + + return hasPersistedRequests || hasOngoingRequests; } diff --git a/src/libs/actions/PersistedRequests.ts b/src/libs/actions/PersistedRequests.ts index 0046030400a9..2ed61cf7318a 100644 --- a/src/libs/actions/PersistedRequests.ts +++ b/src/libs/actions/PersistedRequests.ts @@ -18,8 +18,8 @@ Onyx.connect({ // We try to remove the next request from the persistedRequests if it is the same as ongoingRequest // so we don't process it twice. - if (deepEqual(nextRequestToProcess, ongoingRequest) && val?.length) { - Onyx.set(ONYXKEYS.PERSISTED_REQUESTS, val.slice(1)); + if (deepEqual(nextRequestToProcess, ongoingRequest)) { + persistedRequests = persistedRequests.slice(1); } } }, From 5774d7abade52917e4c4431c9c44080da5387f8b Mon Sep 17 00:00:00 2001 From: "marta.sudol" Date: Mon, 23 Jun 2025 12:34:48 +0200 Subject: [PATCH 20/22] add ReadNewestAction to relevant commands --- src/hooks/useLoadingBarVisibility.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/useLoadingBarVisibility.ts b/src/hooks/useLoadingBarVisibility.ts index ffb7e435d573..88a18e2914fd 100644 --- a/src/hooks/useLoadingBarVisibility.ts +++ b/src/hooks/useLoadingBarVisibility.ts @@ -3,11 +3,11 @@ import ONYXKEYS from '@src/ONYXKEYS'; import useOnyx from './useOnyx'; // Commands that should trigger the LoadingBar to show -const RELEVANT_COMMANDS = new Set([WRITE_COMMANDS.OPEN_APP, WRITE_COMMANDS.RECONNECT_APP, WRITE_COMMANDS.OPEN_REPORT]); +const RELEVANT_COMMANDS = new Set([WRITE_COMMANDS.OPEN_APP, WRITE_COMMANDS.RECONNECT_APP, WRITE_COMMANDS.OPEN_REPORT, WRITE_COMMANDS.READ_NEWEST_ACTION]); /** * Hook that determines whether LoadingBar should be visible based on active queue requests - * Shows LoadingBar when OpenReport/OpenApp/ReconnectApp requests are being processed + * Shows LoadingBar when OpenReport/OpenApp/ReconnectApp/ReadNewestAction requests are being processed */ export default function useLoadingBarVisibility(): boolean { const [persistedRequests] = useOnyx(ONYXKEYS.PERSISTED_REQUESTS, {canBeMissing: false}); From 9bed9c71318cbd6216c2e39781a8e68f3b4bb312 Mon Sep 17 00:00:00 2001 From: "marta.sudol" Date: Tue, 24 Jun 2025 09:18:00 +0200 Subject: [PATCH 21/22] replace useOnyx with network key with useNetwork hook --- src/hooks/useLoadingBarVisibility.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/hooks/useLoadingBarVisibility.ts b/src/hooks/useLoadingBarVisibility.ts index 88a18e2914fd..ae3cffb646f3 100644 --- a/src/hooks/useLoadingBarVisibility.ts +++ b/src/hooks/useLoadingBarVisibility.ts @@ -1,5 +1,6 @@ import {WRITE_COMMANDS} from '@libs/API/types'; import ONYXKEYS from '@src/ONYXKEYS'; +import useNetwork from './useNetwork'; import useOnyx from './useOnyx'; // Commands that should trigger the LoadingBar to show @@ -12,10 +13,10 @@ const RELEVANT_COMMANDS = new Set([WRITE_COMMANDS.OPEN_APP, WRITE_COMMAN export default function useLoadingBarVisibility(): boolean { const [persistedRequests] = useOnyx(ONYXKEYS.PERSISTED_REQUESTS, {canBeMissing: false}); const [ongoingRequests] = useOnyx(ONYXKEYS.PERSISTED_ONGOING_REQUESTS, {canBeMissing: false}); - const [network] = useOnyx(ONYXKEYS.NETWORK, {canBeMissing: false}); + const {isOffline} = useNetwork(); // Don't show loading bar if currently offline - if (network?.isOffline) { + if (isOffline) { return false; } From bff9461aea6ab2d3c357aba1b049cbae3a65ef28 Mon Sep 17 00:00:00 2001 From: martasudol Date: Wed, 25 Jun 2025 09:06:18 +0200 Subject: [PATCH 22/22] Update src/hooks/useLoadingBarVisibility.ts Co-authored-by: Adam Horodyski <42047036+adhorodyski@users.noreply.github.com> --- src/hooks/useLoadingBarVisibility.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useLoadingBarVisibility.ts b/src/hooks/useLoadingBarVisibility.ts index ae3cffb646f3..da07cae1c942 100644 --- a/src/hooks/useLoadingBarVisibility.ts +++ b/src/hooks/useLoadingBarVisibility.ts @@ -8,7 +8,7 @@ const RELEVANT_COMMANDS = new Set([WRITE_COMMANDS.OPEN_APP, WRITE_COMMAN /** * Hook that determines whether LoadingBar should be visible based on active queue requests - * Shows LoadingBar when OpenReport/OpenApp/ReconnectApp/ReadNewestAction requests are being processed + * Shows LoadingBar when any of the RELEVANT_COMMANDS are being processed */ export default function useLoadingBarVisibility(): boolean { const [persistedRequests] = useOnyx(ONYXKEYS.PERSISTED_REQUESTS, {canBeMissing: false});