From d913d8aef3553246f18343919e178d6400d71bfb Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Wed, 26 Feb 2025 08:41:42 +0100
Subject: [PATCH 01/29] Loading state perception improvement

---
 src/components/LoadingBar.tsx               | 102 +++++++-------------
 src/components/MoneyReportHeader.tsx        |   3 -
 src/components/MoneyRequestHeader.tsx       |   3 -
 src/pages/home/HeaderView.tsx               |   2 -
 src/pages/home/ReportScreen.tsx             |   1 +
 src/pages/home/report/ReportActionsView.tsx |   8 ++
 6 files changed, 45 insertions(+), 74 deletions(-)

diff --git a/src/components/LoadingBar.tsx b/src/components/LoadingBar.tsx
index e6d1ec0cd66d..0d1740fcfeb6 100644
--- a/src/components/LoadingBar.tsx
+++ b/src/components/LoadingBar.tsx
@@ -1,83 +1,53 @@
 import React, {useEffect} from 'react';
-import Animated, {cancelAnimation, Easing, useAnimatedStyle, useSharedValue, withDelay, withRepeat, withSequence, withTiming} from 'react-native-reanimated';
-import useThemeStyles from '@hooks/useThemeStyles';
-import CONST from '@src/CONST';
+import {View} from 'react-native';
+import Animated, {runOnJS, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
+import colors from '@styles/theme/colors';
 
-type LoadingBarProps = {
-    // Whether or not to show the loading bar
-    shouldShow: boolean;
-};
+type LoadingBarProps = {shouldShow: boolean};
 
 function LoadingBar({shouldShow}: LoadingBarProps) {
-    const left = useSharedValue(0);
-    const width = useSharedValue(0);
+    const left = useSharedValue(-30);
     const opacity = useSharedValue(0);
-    const styles = useThemeStyles();
+    const isAnimating = useSharedValue(false);
 
     useEffect(() => {
-        if (shouldShow) {
-            // eslint-disable-next-line react-compiler/react-compiler
-            left.set(0);
-            width.set(0);
-            opacity.set(withTiming(1, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION}));
-
-            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,
-                    ),
-                ),
-            );
-
-            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,
-                    ),
-                ),
-            );
-        } else {
-            opacity.set(
-                withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION}, () => {
-                    cancelAnimation(left);
-                    cancelAnimation(width);
-                }),
-            );
+        if (shouldShow && !isAnimating.value) {
+            isAnimating.value = true;
+            opacity.value = withTiming(1, {duration: 300});
+
+            left.value = withTiming(100, {duration: 1200}, () => {
+                runOnJS(() => {
+                    if (shouldShow) {
+                        left.value = -30;
+                        left.value = withTiming(100, {duration: 1200});
+                    } else {
+                        opacity.value = withTiming(0, {duration: 300}, () => {
+                            isAnimating.value = false;
+                        });
+                    }
+                })();
+            });
+        } else if (!shouldShow) {
+            opacity.value = withTiming(0, {duration: 300}, () => {
+                isAnimating.value = false;
+            });
         }
-        // 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.get()}%`,
-        width: `${width.get()}%`,
-    }));
-
-    const animatedContainerStyle = useAnimatedStyle(() => ({
-        opacity: opacity.get(),
+    const barStyle = useAnimatedStyle(() => ({
+        left: `${left.value}%`,
+        width: '30%',
+        height: '100%',
+        backgroundColor: colors.green,
+        opacity: opacity.value,
+        borderRadius: 2,
     }));
 
     return (
-        <Animated.View style={[styles.progressBarWrapper, animatedContainerStyle]}>
-            <Animated.View style={[styles.progressBar, animatedIndicatorStyle]} />
-        </Animated.View>
+        <View style={{height: 2, overflow: 'hidden'}}>
+            <Animated.View style={barStyle} />
+        </View>
     );
 }
 
-LoadingBar.displayName = 'ProgressBar';
-
 export default LoadingBar;
diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx
index 1d6c3fe4cfba..ef6c6be9608b 100644
--- a/src/components/MoneyReportHeader.tsx
+++ b/src/components/MoneyReportHeader.tsx
@@ -72,7 +72,6 @@ import DelegateNoAccessModal from './DelegateNoAccessModal';
 import HeaderWithBackButton from './HeaderWithBackButton';
 import Icon from './Icon';
 import * as Expensicons from './Icon/Expensicons';
-import LoadingBar from './LoadingBar';
 import MoneyReportHeaderStatusBar from './MoneyReportHeaderStatusBar';
 import type {MoneyRequestHeaderStatusBarProps} from './MoneyRequestHeaderStatusBar';
 import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar';
@@ -209,7 +208,6 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
     const isMoreContentShown = shouldShowNextStep || shouldShowStatusBar || (shouldShowAnyButton && shouldUseNarrowLayout);
     const {isDelegateAccessRestricted} = useDelegateUserDetails();
     const [isNoDelegateAccessMenuVisible, setIsNoDelegateAccessMenuVisible] = useState(false);
-    const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA);
 
     const isReportInRHP = route.name === SCREENS.SEARCH.REPORT_RHP;
     const shouldDisplaySearchRouter = !isReportInRHP || isSmallScreenWidth;
@@ -493,7 +491,6 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
                     )}
                 </View>
             )}
-            <LoadingBar shouldShow={(isLoadingReportData && shouldUseNarrowLayout) ?? false} />
             {isHoldMenuVisible && requestType !== undefined && (
                 <ProcessMoneyReportHoldMenu
                     nonHeldAmount={!hasOnlyHeldExpenses && hasValidNonHeldAmount ? nonHeldAmount : undefined}
diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx
index 268ab770059e..d342ff6426b8 100644
--- a/src/components/MoneyRequestHeader.tsx
+++ b/src/components/MoneyRequestHeader.tsx
@@ -38,7 +38,6 @@ import Button from './Button';
 import HeaderWithBackButton from './HeaderWithBackButton';
 import Icon from './Icon';
 import * as Expensicons from './Icon/Expensicons';
-import LoadingBar from './LoadingBar';
 import type {MoneyRequestHeaderStatusBarProps} from './MoneyRequestHeaderStatusBar';
 import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar';
 
@@ -69,7 +68,6 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
     );
     const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS);
     const [dismissedHoldUseExplanation, dismissedHoldUseExplanationResult] = useOnyx(ONYXKEYS.NVP_DISMISSED_HOLD_USE_EXPLANATION, {initialValue: true});
-    const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA);
     const isLoadingHoldUseExplained = isLoadingOnyxValue(dismissedHoldUseExplanationResult);
     const styles = useThemeStyles();
     const theme = useTheme();
@@ -216,7 +214,6 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
                     />
                 </View>
             )}
-            <LoadingBar shouldShow={(isLoadingReportData && shouldUseNarrowLayout) ?? false} />
         </View>
     );
 }
diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx
index 73b1a4af8ec1..c7e8cb957c5d 100644
--- a/src/pages/home/HeaderView.tsx
+++ b/src/pages/home/HeaderView.tsx
@@ -106,7 +106,6 @@ function HeaderView({report, parentReportAction, onNavigationMenuButtonClicked,
     const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(report?.parentReportID) ?? getNonEmptyStringOnyxID(report?.reportID)}`);
     const policy = usePolicy(report?.policyID);
     const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST);
-    const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA);
     const [firstDayFreeTrial] = useOnyx(ONYXKEYS.NVP_FIRST_DAY_FREE_TRIAL);
     const [lastDayFreeTrial] = useOnyx(ONYXKEYS.NVP_LAST_DAY_FREE_TRIAL);
     const [account] = useOnyx(ONYXKEYS.ACCOUNT);
@@ -384,7 +383,6 @@ function HeaderView({report, parentReportAction, onNavigationMenuButtonClicked,
                         </View>
                     </View>
                 )}
-                <LoadingBar shouldShow={(isLoadingReportData && shouldUseNarrowLayout) ?? false} />
             </View>
             {shouldShowDiscount && isChatUsedForOnboarding && <EarlyDiscountBanner isSubscriptionPage={false} />}
         </>
diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx
index 7627e77610fd..38bfa3f3c021 100644
--- a/src/pages/home/ReportScreen.tsx
+++ b/src/pages/home/ReportScreen.tsx
@@ -734,6 +734,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
                                         report={report}
                                         reportActions={reportActions}
                                         isLoadingInitialReportActions={reportMetadata?.isLoadingInitialReportActions}
+                                        isLoadingOlderReportActions={reportMetadata?.isLoadingOlderReportActions}
                                         hasNewerActions={hasNewerActions}
                                         hasOlderActions={hasOlderActions}
                                         parentReportAction={parentReportAction}
diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx
index 1cdaccccdbe9..7a5f04c07cfe 100755
--- a/src/pages/home/report/ReportActionsView.tsx
+++ b/src/pages/home/report/ReportActionsView.tsx
@@ -5,6 +5,7 @@ import type {OnyxEntry} from 'react-native-onyx';
 import {useOnyx} from 'react-native-onyx';
 import EmptyStateComponent from '@components/EmptyStateComponent';
 import * as Illustrations from '@components/Icon/Illustrations';
+import LoadingBar from '@components/LoadingBar';
 import ReportActionsSkeletonView from '@components/ReportActionsSkeletonView';
 import SearchRowSkeleton from '@components/Skeletons/SearchRowSkeleton';
 import useCopySelectionHelper from '@hooks/useCopySelectionHelper';
@@ -56,6 +57,9 @@ type ReportActionsViewProps = {
     /** The report metadata loading states */
     isLoadingInitialReportActions?: boolean;
 
+    /** Are we loading more report actions? */
+    isLoadingOlderReportActions?: boolean;
+
     /** The reportID of the transaction thread report associated with this current report, if any */
     // eslint-disable-next-line react/no-unused-prop-types
     transactionThreadReportID?: string | null;
@@ -74,6 +78,7 @@ function ReportActionsView({
     parentReportAction,
     reportActions: allReportActions,
     isLoadingInitialReportActions,
+    isLoadingOlderReportActions = true,
     transactionThreadReportID,
     hasNewerActions,
     hasOlderActions,
@@ -211,6 +216,8 @@ function ReportActionsView({
         [reportActions, isOffline, canPerformWriteAction],
     );
 
+    const showLoadingBar = isLoadingOlderReportActions || !!(visibleReportActions.length > 0 && isLoadingInitialReportActions);
+
     const reportActionIDMap = useMemo(() => {
         const reportActionIDs = allReportActions?.map((action) => action.reportActionID);
         return reportActions.map((action) => ({
@@ -373,6 +380,7 @@ function ReportActionsView({
     const shouldEnableAutoScroll = (hasNewestReportAction && (!reportActionID || !isNavigatingToLinkedMessage)) || (transactionThreadReport && !prevTransactionThreadReport);
     return (
         <>
+            <LoadingBar shouldShow={showLoadingBar} />
             <ReportActionsList
                 report={report}
                 transactionThreadReport={transactionThreadReport}

From 677c620434d31b4af3471231d0d1513661b64572 Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Fri, 28 Feb 2025 10:23:39 +0100
Subject: [PATCH 02/29] Loading state perception improvement - loading bar and
 loading older actions logic

---
 src/components/LoadingBar.tsx                         |  4 ++--
 src/pages/home/report/PureReportActionItem.tsx        |  5 -----
 .../home/report/ReportActionItemParentAction.tsx      |  5 -----
 src/pages/home/report/ReportActionsList.tsx           | 11 +++++++++--
 .../home/report/ReportActionsListItemRenderer.tsx     |  6 ------
 5 files changed, 11 insertions(+), 20 deletions(-)

diff --git a/src/components/LoadingBar.tsx b/src/components/LoadingBar.tsx
index 0d1740fcfeb6..0fe64ce27834 100644
--- a/src/components/LoadingBar.tsx
+++ b/src/components/LoadingBar.tsx
@@ -1,6 +1,6 @@
 import React, {useEffect} from 'react';
 import {View} from 'react-native';
-import Animated, {runOnJS, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
+import Animated, {runOnUI, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
 import colors from '@styles/theme/colors';
 
 type LoadingBarProps = {shouldShow: boolean};
@@ -16,7 +16,7 @@ function LoadingBar({shouldShow}: LoadingBarProps) {
             opacity.value = withTiming(1, {duration: 300});
 
             left.value = withTiming(100, {duration: 1200}, () => {
-                runOnJS(() => {
+                runOnUI(() => {
                     if (shouldShow) {
                         left.value = -30;
                         left.value = withTiming(100, {duration: 1200});
diff --git a/src/pages/home/report/PureReportActionItem.tsx b/src/pages/home/report/PureReportActionItem.tsx
index 49a855fc690b..d43bc0ef5de2 100644
--- a/src/pages/home/report/PureReportActionItem.tsx
+++ b/src/pages/home/report/PureReportActionItem.tsx
@@ -148,10 +148,6 @@ type PureReportActionItemProps = {
     /** The transaction thread report associated with the report for this action, if any */
     transactionThreadReport?: OnyxEntry<OnyxTypes.Report>;
 
-    /** Array of report actions for the report for this action */
-    // eslint-disable-next-line react/no-unused-prop-types
-    reportActions: OnyxTypes.ReportAction[];
-
     /** Report action belonging to the report's parent */
     parentReportAction: OnyxEntry<OnyxTypes.ReportAction>;
 
@@ -1245,7 +1241,6 @@ export default memo(PureReportActionItem, (prevProps, nextProps) => {
         prevProps.linkedReportActionID === nextProps.linkedReportActionID &&
         lodashIsEqual(prevProps.report?.fieldList, nextProps.report?.fieldList) &&
         lodashIsEqual(prevProps.transactionThreadReport, nextProps.transactionThreadReport) &&
-        lodashIsEqual(prevProps.reportActions, nextProps.reportActions) &&
         lodashIsEqual(prevParentReportAction, nextParentReportAction) &&
         prevProps.draftMessage === nextProps.draftMessage &&
         prevProps.iouReport?.reportID === nextProps.iouReport?.reportID &&
diff --git a/src/pages/home/report/ReportActionItemParentAction.tsx b/src/pages/home/report/ReportActionItemParentAction.tsx
index 332f773a7b44..38cfb0c5fd84 100644
--- a/src/pages/home/report/ReportActionItemParentAction.tsx
+++ b/src/pages/home/report/ReportActionItemParentAction.tsx
@@ -36,9 +36,6 @@ type ReportActionItemParentActionProps = {
     /** The transaction thread report associated with the current report, if any */
     transactionThreadReport: OnyxEntry<OnyxTypes.Report>;
 
-    /** Array of report actions for this report */
-    reportActions: OnyxTypes.ReportAction[];
-
     /** Report actions belonging to the report's parent */
     parentReportAction: OnyxEntry<OnyxTypes.ReportAction>;
 
@@ -55,7 +52,6 @@ type ReportActionItemParentActionProps = {
 function ReportActionItemParentAction({
     report,
     transactionThreadReport,
-    reportActions,
     parentReportAction,
     index = 0,
     shouldHideThreadDividerLine = false,
@@ -140,7 +136,6 @@ function ReportActionItemParentAction({
                             }
                             parentReportAction={parentReportAction}
                             report={ancestor.report}
-                            reportActions={reportActions}
                             transactionThreadReport={transactionThreadReport}
                             action={ancestor.reportAction}
                             displayAsGroup={false}
diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx
index a37a28a7ee0a..11b01df80636 100644
--- a/src/pages/home/report/ReportActionsList.tsx
+++ b/src/pages/home/report/ReportActionsList.tsx
@@ -174,6 +174,7 @@ function ReportActionsList({
     const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`);
     const [accountID] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.accountID});
     const participantsContext = useContext(PersonalDetailsContext);
+    const [scrollOffset, setScrollOffset] = useState(0);
 
     useEffect(() => {
         const unsubscriber = Visibility.onVisibilityChange(() => {
@@ -501,6 +502,7 @@ function ReportActionsList({
     };
 
     const trackVerticalScrolling = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
+        setScrollOffset(event.nativeEvent.contentOffset.y);
         scrollingVerticalOffset.current = event.nativeEvent.contentOffset.y;
         handleUnreadFloatingButton();
         onScroll?.(event);
@@ -607,7 +609,6 @@ function ReportActionsList({
         ({item: reportAction, index}: ListRenderItemInfo<OnyxTypes.ReportAction>) => (
             <ReportActionsListItemRenderer
                 reportAction={reportAction}
-                reportActions={sortedReportActions}
                 parentReportAction={parentReportAction}
                 parentReportActionForTransactionThread={parentReportActionForTransactionThread}
                 index={index}
@@ -633,7 +634,6 @@ function ReportActionsList({
             mostRecentIOUReportActionID,
             shouldHideThreadDividerLine,
             parentReportAction,
-            sortedReportActions,
             transactionThreadReport,
             parentReportActionForTransactionThread,
             shouldUseThreadDividerLine,
@@ -698,6 +698,13 @@ function ReportActionsList({
 
     const onEndReached = useCallback(() => {
         loadOlderChats(false);
+
+        requestAnimationFrame(() => {
+            reportScrollManager.ref?.current?.scrollToOffset({
+                offset: scrollingVerticalOffset.current - scrollOffset,
+                animated: false,
+            });
+        });
     }, [loadOlderChats]);
 
     // Parse Fullstory attributes on initial render
diff --git a/src/pages/home/report/ReportActionsListItemRenderer.tsx b/src/pages/home/report/ReportActionsListItemRenderer.tsx
index 1e00133ca40b..0247bc0d9fea 100644
--- a/src/pages/home/report/ReportActionsListItemRenderer.tsx
+++ b/src/pages/home/report/ReportActionsListItemRenderer.tsx
@@ -12,9 +12,6 @@ type ReportActionsListItemRendererProps = {
     /** All the data of the action item */
     reportAction: ReportAction;
 
-    /** Array of report actions for the report */
-    reportActions: ReportAction[];
-
     /** The report's parentReportAction */
     parentReportAction: OnyxEntry<ReportAction>;
 
@@ -57,7 +54,6 @@ type ReportActionsListItemRendererProps = {
 
 function ReportActionsListItemRenderer({
     reportAction,
-    reportActions = [],
     parentReportAction,
     index,
     report,
@@ -158,7 +154,6 @@ function ReportActionsListItemRenderer({
                 parentReportAction={parentReportAction}
                 reportID={report.reportID}
                 report={report}
-                reportActions={reportActions}
                 transactionThreadReport={transactionThreadReport}
                 index={index}
                 isFirstVisibleReportAction={isFirstVisibleReportAction}
@@ -175,7 +170,6 @@ function ReportActionsListItemRenderer({
             transactionThreadReport={transactionThreadReport}
             parentReportActionForTransactionThread={parentReportActionForTransactionThread}
             action={action}
-            reportActions={reportActions}
             linkedReportActionID={linkedReportActionID}
             displayAsGroup={displayAsGroup}
             shouldDisplayNewMarker={shouldDisplayNewMarker}

From fcaead7022325261ce62ece896ef135e093866bf Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Fri, 28 Feb 2025 10:54:20 +0100
Subject: [PATCH 03/29] Loading state perception improvement: less blinking
 skeletons

---
 .../ReportActionsSkeletonView/SkeletonViewLines.tsx           | 1 +
 src/styles/theme/themes/dark.ts                               | 4 ++--
 src/styles/theme/themes/light.ts                              | 4 ++--
 3 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/src/components/ReportActionsSkeletonView/SkeletonViewLines.tsx b/src/components/ReportActionsSkeletonView/SkeletonViewLines.tsx
index c4c2a3f43eb3..d3ea26ab5880 100644
--- a/src/components/ReportActionsSkeletonView/SkeletonViewLines.tsx
+++ b/src/components/ReportActionsSkeletonView/SkeletonViewLines.tsx
@@ -20,6 +20,7 @@ function SkeletonViewLines({numberOfRows, shouldAnimate = true}: SkeletonViewLin
             height={CONST.CHAT_SKELETON_VIEW.HEIGHT_FOR_ROW_COUNT[numberOfRows]}
             backgroundColor={theme.skeletonLHNIn}
             foregroundColor={theme.skeletonLHNOut}
+            speed={3}
             style={styles.mr5}
         >
             <Circle
diff --git a/src/styles/theme/themes/dark.ts b/src/styles/theme/themes/dark.ts
index 4d5894812195..8a9fd3cbd804 100644
--- a/src/styles/theme/themes/dark.ts
+++ b/src/styles/theme/themes/dark.ts
@@ -85,8 +85,8 @@ const darkTheme = {
     tooltipSupportingText: colors.productLight800,
     tooltipPrimaryText: colors.productLight900,
     trialBannerBackgroundColor: colors.green700,
-    skeletonLHNIn: colors.productDark400,
-    skeletonLHNOut: colors.productDark600,
+    skeletonLHNIn: colors.productDark200,
+    skeletonLHNOut: colors.productDark300,
     QRLogo: colors.green400,
     starDefaultBG: 'rgb(254, 228, 94)',
     mapAttributionText: colors.black,
diff --git a/src/styles/theme/themes/light.ts b/src/styles/theme/themes/light.ts
index 1a187e85b0a2..6f4e5ee30e9d 100644
--- a/src/styles/theme/themes/light.ts
+++ b/src/styles/theme/themes/light.ts
@@ -85,8 +85,8 @@ const lightTheme = {
     tooltipSupportingText: colors.productDark800,
     tooltipPrimaryText: colors.productDark900,
     trialBannerBackgroundColor: colors.green100,
-    skeletonLHNIn: colors.productLight400,
-    skeletonLHNOut: colors.productLight600,
+    skeletonLHNIn: colors.productLight200,
+    skeletonLHNOut: colors.productLight300,
     QRLogo: colors.green400,
     starDefaultBG: 'rgb(254, 228, 94)',
     mapAttributionText: colors.black,

From 6a81a0300a0f013d2fcd224bba44dac08b896a62 Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Fri, 28 Feb 2025 11:56:26 +0100
Subject: [PATCH 04/29] remove redundant reportActions props

---
 src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx | 1 -
 src/pages/Debug/ReportAction/DebugReportActionPreview.tsx    | 1 -
 src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx  | 1 -
 3 files changed, 3 deletions(-)

diff --git a/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx b/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx
index 1162146b8f4d..adf7a78c6a2a 100644
--- a/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx
+++ b/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx
@@ -107,7 +107,6 @@ function DebugReportActionCreatePage({
                                 <ReportActionItem
                                     action={JSON.parse(draftReportAction.replaceAll('\n', '')) as ReportAction}
                                     report={{reportID}}
-                                    reportActions={[]}
                                     parentReportAction={undefined}
                                     displayAsGroup={false}
                                     isMostRecentIOUReportAction={false}
diff --git a/src/pages/Debug/ReportAction/DebugReportActionPreview.tsx b/src/pages/Debug/ReportAction/DebugReportActionPreview.tsx
index 94334ffb4483..e9b37ea00a41 100644
--- a/src/pages/Debug/ReportAction/DebugReportActionPreview.tsx
+++ b/src/pages/Debug/ReportAction/DebugReportActionPreview.tsx
@@ -19,7 +19,6 @@ function DebugReportActionPreview({reportAction}: DebugReportActionPreviewProps)
             <ReportActionItem
                 action={reportAction ?? ({} as ReportAction)}
                 report={report ?? ({} as Report)}
-                reportActions={[]}
                 parentReportAction={undefined}
                 displayAsGroup={false}
                 isMostRecentIOUReportAction={false}
diff --git a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
index 398ea8ae336b..a6ddbd9a0a72 100644
--- a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
+++ b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
@@ -35,7 +35,6 @@ function DuplicateTransactionItem(props: DuplicateTransactionItemProps) {
                 report={report}
                 parentReportAction={ReportActionsUtils.getReportAction(report?.parentReportID ?? '', report?.parentReportActionID ?? '')}
                 index={props.index}
-                reportActions={Object.values(reportActions ?? {})}
                 displayAsGroup={false}
                 shouldDisplayNewMarker={false}
                 isMostRecentIOUReportAction={false}

From 88709447f90db016c29074be1487724746364752 Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Fri, 28 Feb 2025 12:58:23 +0100
Subject: [PATCH 05/29] remove redundant imports

---
 src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx | 6 +++---
 src/pages/home/HeaderView.tsx                               | 1 -
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
index a6ddbd9a0a72..2738edadbd6a 100644
--- a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
+++ b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
@@ -3,7 +3,7 @@ import {View} from 'react-native';
 import type {OnyxEntry} from 'react-native-onyx';
 import {useOnyx} from 'react-native-onyx';
 import useThemeStyles from '@hooks/useThemeStyles';
-import * as ReportActionsUtils from '@libs/ReportActionsUtils';
+import {getOriginalMessage, getReportAction, isMoneyRequestAction} from '@libs/ReportActionsUtils';
 import ReportActionItem from '@pages/home/report/ReportActionItem';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Transaction} from '@src/types/onyx';
@@ -20,7 +20,7 @@ function DuplicateTransactionItem(props: DuplicateTransactionItemProps) {
 
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/non-nullable-type-assertion-style
     const action = Object.values(reportActions ?? {})?.find((reportAction) => {
-        const IOUTransactionID = ReportActionsUtils.isMoneyRequestAction(reportAction) ? ReportActionsUtils.getOriginalMessage(reportAction)?.IOUTransactionID : -1;
+        const IOUTransactionID = isMoneyRequestAction(reportAction) ? getOriginalMessage(reportAction)?.IOUTransactionID : -1;
         return IOUTransactionID === props.transaction?.transactionID;
     });
 
@@ -33,7 +33,7 @@ function DuplicateTransactionItem(props: DuplicateTransactionItemProps) {
             <ReportActionItem
                 action={action}
                 report={report}
-                parentReportAction={ReportActionsUtils.getReportAction(report?.parentReportID ?? '', report?.parentReportActionID ?? '')}
+                parentReportAction={getReportAction(report?.parentReportID ?? '', report?.parentReportActionID ?? '')}
                 index={props.index}
                 displayAsGroup={false}
                 shouldDisplayNewMarker={false}
diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx
index c7e8cb957c5d..6d80bc98e1e5 100644
--- a/src/pages/home/HeaderView.tsx
+++ b/src/pages/home/HeaderView.tsx
@@ -9,7 +9,6 @@ import ConfirmModal from '@components/ConfirmModal';
 import DisplayNames from '@components/DisplayNames';
 import Icon from '@components/Icon';
 import {BackArrow, CalendarSolid, DotIndicator, FallbackAvatar} from '@components/Icon/Expensicons';
-import LoadingBar from '@components/LoadingBar';
 import MultipleAvatars from '@components/MultipleAvatars';
 import OfflineWithFeedback from '@components/OfflineWithFeedback';
 import ParentNavigationSubtitle from '@components/ParentNavigationSubtitle';

From 769362bb97286e918e67dafca12186ce57c461a3 Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Fri, 28 Feb 2025 13:23:29 +0100
Subject: [PATCH 06/29] loading bar animation performance improvement

---
 src/components/LoadingBar.tsx | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/components/LoadingBar.tsx b/src/components/LoadingBar.tsx
index 0fe64ce27834..7f8e4de9c224 100644
--- a/src/components/LoadingBar.tsx
+++ b/src/components/LoadingBar.tsx
@@ -1,6 +1,6 @@
 import React, {useEffect} from 'react';
 import {View} from 'react-native';
-import Animated, {runOnUI, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
+import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
 import colors from '@styles/theme/colors';
 
 type LoadingBarProps = {shouldShow: boolean};
@@ -16,7 +16,7 @@ function LoadingBar({shouldShow}: LoadingBarProps) {
             opacity.value = withTiming(1, {duration: 300});
 
             left.value = withTiming(100, {duration: 1200}, () => {
-                runOnUI(() => {
+                requestAnimationFrame(() => {
                     if (shouldShow) {
                         left.value = -30;
                         left.value = withTiming(100, {duration: 1200});
@@ -25,7 +25,7 @@ function LoadingBar({shouldShow}: LoadingBarProps) {
                             isAnimating.value = false;
                         });
                     }
-                })();
+                });
             });
         } else if (!shouldShow) {
             opacity.value = withTiming(0, {duration: 300}, () => {

From 44ef83154bd3a9b6ec10a605efdb2ac6138fa77b Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Fri, 28 Feb 2025 13:50:26 +0100
Subject: [PATCH 07/29] manage scrolling position only for web and desktop

---
 src/pages/home/report/ReportActionsList.tsx | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx
index c8469a52a9e6..d893e21d500f 100644
--- a/src/pages/home/report/ReportActionsList.tsx
+++ b/src/pages/home/report/ReportActionsList.tsx
@@ -60,6 +60,7 @@ import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
 import type SCREENS from '@src/SCREENS';
 import type * as OnyxTypes from '@src/types/onyx';
+import getPlatform from '@libs/getPlatform';
 import FloatingMessageCounter from './FloatingMessageCounter';
 import getInitialNumToRender from './getInitialNumReportActionsToRender';
 import ListBoundaryLoader from './ListBoundaryLoader';
@@ -226,6 +227,11 @@ function ReportActionsList({
     }, [report.reportID]);
 
     const prevUnreadMarkerReportActionID = useRef<string | null>(null);
+
+    const platform = getPlatform();
+    const isWebOrDesktop = platform === CONST.PLATFORM.WEB || platform === CONST.PLATFORM.DESKTOP;
+
+
     /**
      * Whether a message is NOT from the active user and it was received while the user was offline.
      */
@@ -719,12 +725,14 @@ function ReportActionsList({
     const onEndReached = useCallback(() => {
         loadOlderChats(false);
 
-        requestAnimationFrame(() => {
-            reportScrollManager.ref?.current?.scrollToOffset({
-                offset: scrollingVerticalOffset.current - scrollOffset,
-                animated: false,
+        if (isWebOrDesktop) {
+            requestAnimationFrame(() => {
+                reportScrollManager.ref?.current?.scrollToOffset({
+                    offset: scrollingVerticalOffset.current - scrollOffset,
+                    animated: false,
+                });
             });
-        });
+        }
     }, [loadOlderChats]);
 
     // Parse Fullstory attributes on initial render

From a4231000e6e766a8c48d6db8646238a3e2266760 Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Fri, 28 Feb 2025 14:53:41 +0100
Subject: [PATCH 08/29] fix linter errors

---
 src/components/LoadingBar.tsx                 | 30 +++++++++----------
 .../DebugReportActionCreatePage.tsx           |  8 ++---
 .../DuplicateTransactionItem.tsx              |  5 ++--
 3 files changed, 22 insertions(+), 21 deletions(-)

diff --git a/src/components/LoadingBar.tsx b/src/components/LoadingBar.tsx
index 7f8e4de9c224..f8ac88fbc228 100644
--- a/src/components/LoadingBar.tsx
+++ b/src/components/LoadingBar.tsx
@@ -11,35 +11,35 @@ function LoadingBar({shouldShow}: LoadingBarProps) {
     const isAnimating = useSharedValue(false);
 
     useEffect(() => {
-        if (shouldShow && !isAnimating.value) {
-            isAnimating.value = true;
-            opacity.value = withTiming(1, {duration: 300});
+        if (shouldShow && !isAnimating.get()) {
+            isAnimating.set(true);
+            opacity.set(withTiming(1, {duration: 300}));
 
-            left.value = withTiming(100, {duration: 1200}, () => {
+            left.set(withTiming(100, {duration: 1200}, () => {
                 requestAnimationFrame(() => {
                     if (shouldShow) {
-                        left.value = -30;
-                        left.value = withTiming(100, {duration: 1200});
+                        left.set(-30);
+                        left.set(withTiming(100, {duration: 1200}));
                     } else {
-                        opacity.value = withTiming(0, {duration: 300}, () => {
-                            isAnimating.value = false;
-                        });
+                        opacity.set(withTiming(0, {duration: 300}, () => {
+                            isAnimating.set(false);
+                        }));
                     }
                 });
-            });
+            }));
         } else if (!shouldShow) {
-            opacity.value = withTiming(0, {duration: 300}, () => {
-                isAnimating.value = false;
-            });
+            opacity.set(withTiming(0, {duration: 300}, () => {
+                isAnimating.set(false);
+            }));
         }
     }, [shouldShow]);
 
     const barStyle = useAnimatedStyle(() => ({
-        left: `${left.value}%`,
+        left: `${left.get()}%`,
         width: '30%',
         height: '100%',
         backgroundColor: colors.green,
-        opacity: opacity.value,
+        opacity: opacity.get(),
         borderRadius: 2,
     }));
 
diff --git a/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx b/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx
index adf7a78c6a2a..b7823842d4b8 100644
--- a/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx
+++ b/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx
@@ -12,11 +12,9 @@ import useLocalize from '@hooks/useLocalize';
 import useThemeStyles from '@hooks/useThemeStyles';
 import DateUtils from '@libs/DateUtils';
 import DebugUtils from '@libs/DebugUtils';
-import * as DeviceCapabilities from '@libs/DeviceCapabilities';
 import Navigation from '@libs/Navigation/Navigation';
 import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
 import type {DebugParamList} from '@libs/Navigation/types';
-import * as NumberUtils from '@libs/NumberUtils';
 import ReportActionItem from '@pages/home/report/ReportActionItem';
 import Debug from '@userActions/Debug';
 import CONST from '@src/CONST';
@@ -25,6 +23,8 @@ import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
 import type SCREENS from '@src/SCREENS';
 import type {PersonalDetailsList, ReportAction, Session} from '@src/types/onyx';
+import {rand64} from '@libs/NumberUtils';
+import {canUseTouchScreen} from '@libs/DeviceCapabilities';
 
 type DebugReportActionCreatePageProps = PlatformStackScreenProps<DebugParamList, typeof SCREENS.DEBUG.REPORT_ACTION_CREATE>;
 
@@ -32,7 +32,7 @@ const getInitialReportAction = (reportID: string, session: OnyxEntry<Session>, p
     DebugUtils.stringifyJSON({
         actionName: CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT,
         reportID,
-        reportActionID: NumberUtils.rand64(),
+        reportActionID: rand64(),
         created: DateUtils.getDBTime(),
         actorAccountID: session?.accountID,
         avatar: (session?.accountID && personalDetailsList?.[session.accountID]?.avatar) ?? '',
@@ -78,7 +78,7 @@ function DebugReportActionCreatePage({
         <ScreenWrapper
             includeSafeAreaPaddingBottom={false}
             shouldEnableKeyboardAvoidingView={false}
-            shouldEnableMinHeight={DeviceCapabilities.canUseTouchScreen()}
+            shouldEnableMinHeight={canUseTouchScreen()}
             testID={DebugReportActionCreatePage.displayName}
         >
             {({safeAreaPaddingBottomStyle}) => (
diff --git a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
index 2738edadbd6a..5fe10a4378ef 100644
--- a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
+++ b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
@@ -7,6 +7,7 @@ import {getOriginalMessage, getReportAction, isMoneyRequestAction} from '@libs/R
 import ReportActionItem from '@pages/home/report/ReportActionItem';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Transaction} from '@src/types/onyx';
+import CONST from '@src/CONST';
 
 type DuplicateTransactionItemProps = {
     transaction: OnyxEntry<Transaction>;
@@ -20,7 +21,7 @@ function DuplicateTransactionItem(props: DuplicateTransactionItemProps) {
 
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/non-nullable-type-assertion-style
     const action = Object.values(reportActions ?? {})?.find((reportAction) => {
-        const IOUTransactionID = isMoneyRequestAction(reportAction) ? getOriginalMessage(reportAction)?.IOUTransactionID : -1;
+        const IOUTransactionID = isMoneyRequestAction(reportAction) ? getOriginalMessage(reportAction)?.IOUTransactionID : CONST.DEFAULT_NUMBER_ID;
         return IOUTransactionID === props.transaction?.transactionID;
     });
 
@@ -33,7 +34,7 @@ function DuplicateTransactionItem(props: DuplicateTransactionItemProps) {
             <ReportActionItem
                 action={action}
                 report={report}
-                parentReportAction={getReportAction(report?.parentReportID ?? '', report?.parentReportActionID ?? '')}
+                parentReportAction={getReportAction(report?.parentReportID ?? undefined, report?.parentReportActionID ?? undefined)}
                 index={props.index}
                 displayAsGroup={false}
                 shouldDisplayNewMarker={false}

From 8f075e3c2510f2a0e15e0ebcfd67fbc25668268a Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Fri, 28 Feb 2025 15:00:01 +0100
Subject: [PATCH 09/29] fix linter errors

---
 src/components/LoadingBar.tsx                 | 37 +++++++++++--------
 .../DebugReportActionCreatePage.tsx           |  4 +-
 .../DuplicateTransactionItem.tsx              |  2 +-
 src/pages/home/report/ReportActionsList.tsx   |  4 +-
 4 files changed, 27 insertions(+), 20 deletions(-)

diff --git a/src/components/LoadingBar.tsx b/src/components/LoadingBar.tsx
index f8ac88fbc228..17b88d08ba84 100644
--- a/src/components/LoadingBar.tsx
+++ b/src/components/LoadingBar.tsx
@@ -15,23 +15,30 @@ function LoadingBar({shouldShow}: LoadingBarProps) {
             isAnimating.set(true);
             opacity.set(withTiming(1, {duration: 300}));
 
-            left.set(withTiming(100, {duration: 1200}, () => {
-                requestAnimationFrame(() => {
-                    if (shouldShow) {
-                        left.set(-30);
-                        left.set(withTiming(100, {duration: 1200}));
-                    } else {
-                        opacity.set(withTiming(0, {duration: 300}, () => {
-                            isAnimating.set(false);
-                        }));
-                    }
-                });
-            }));
+            left.set(
+                withTiming(100, {duration: 1200}, () => {
+                    requestAnimationFrame(() => {
+                        if (shouldShow) {
+                            left.set(-30);
+                            left.set(withTiming(100, {duration: 1200}));
+                        } else {
+                            opacity.set(
+                                withTiming(0, {duration: 300}, () => {
+                                    isAnimating.set(false);
+                                }),
+                            );
+                        }
+                    });
+                }),
+            );
         } else if (!shouldShow) {
-            opacity.set(withTiming(0, {duration: 300}, () => {
-                isAnimating.set(false);
-            }));
+            opacity.set(
+                withTiming(0, {duration: 300}, () => {
+                    isAnimating.set(false);
+                }),
+            );
         }
+        // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
     }, [shouldShow]);
 
     const barStyle = useAnimatedStyle(() => ({
diff --git a/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx b/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx
index b7823842d4b8..2331d84b1a16 100644
--- a/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx
+++ b/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx
@@ -12,9 +12,11 @@ import useLocalize from '@hooks/useLocalize';
 import useThemeStyles from '@hooks/useThemeStyles';
 import DateUtils from '@libs/DateUtils';
 import DebugUtils from '@libs/DebugUtils';
+import {canUseTouchScreen} from '@libs/DeviceCapabilities';
 import Navigation from '@libs/Navigation/Navigation';
 import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
 import type {DebugParamList} from '@libs/Navigation/types';
+import {rand64} from '@libs/NumberUtils';
 import ReportActionItem from '@pages/home/report/ReportActionItem';
 import Debug from '@userActions/Debug';
 import CONST from '@src/CONST';
@@ -23,8 +25,6 @@ import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
 import type SCREENS from '@src/SCREENS';
 import type {PersonalDetailsList, ReportAction, Session} from '@src/types/onyx';
-import {rand64} from '@libs/NumberUtils';
-import {canUseTouchScreen} from '@libs/DeviceCapabilities';
 
 type DebugReportActionCreatePageProps = PlatformStackScreenProps<DebugParamList, typeof SCREENS.DEBUG.REPORT_ACTION_CREATE>;
 
diff --git a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
index 5fe10a4378ef..eb036c6fb3b3 100644
--- a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
+++ b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
@@ -5,9 +5,9 @@ import {useOnyx} from 'react-native-onyx';
 import useThemeStyles from '@hooks/useThemeStyles';
 import {getOriginalMessage, getReportAction, isMoneyRequestAction} from '@libs/ReportActionsUtils';
 import ReportActionItem from '@pages/home/report/ReportActionItem';
+import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Transaction} from '@src/types/onyx';
-import CONST from '@src/CONST';
 
 type DuplicateTransactionItemProps = {
     transaction: OnyxEntry<Transaction>;
diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx
index d893e21d500f..2cc3df2af3c1 100644
--- a/src/pages/home/report/ReportActionsList.tsx
+++ b/src/pages/home/report/ReportActionsList.tsx
@@ -21,6 +21,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions';
 import {isSafari} from '@libs/Browser';
 import DateUtils from '@libs/DateUtils';
 import {getChatFSAttributes, parseFSAttributes} from '@libs/Fullstory';
+import getPlatform from '@libs/getPlatform';
 import isReportTopmostSplitNavigator from '@libs/Navigation/helpers/isReportTopmostSplitNavigator';
 import isSearchTopmostFullScreenRoute from '@libs/Navigation/helpers/isSearchTopmostFullScreenRoute';
 import Navigation from '@libs/Navigation/Navigation';
@@ -60,7 +61,6 @@ import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
 import type SCREENS from '@src/SCREENS';
 import type * as OnyxTypes from '@src/types/onyx';
-import getPlatform from '@libs/getPlatform';
 import FloatingMessageCounter from './FloatingMessageCounter';
 import getInitialNumToRender from './getInitialNumReportActionsToRender';
 import ListBoundaryLoader from './ListBoundaryLoader';
@@ -231,7 +231,6 @@ function ReportActionsList({
     const platform = getPlatform();
     const isWebOrDesktop = platform === CONST.PLATFORM.WEB || platform === CONST.PLATFORM.DESKTOP;
 
-
     /**
      * Whether a message is NOT from the active user and it was received while the user was offline.
      */
@@ -733,6 +732,7 @@ function ReportActionsList({
                 });
             });
         }
+        // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
     }, [loadOlderChats]);
 
     // Parse Fullstory attributes on initial render

From 3cc105bc052d4863f271812498e70c4c2f0202db Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Tue, 4 Mar 2025 18:08:48 +0100
Subject: [PATCH 10/29] Addressed PR comments

---
 src/components/LoadingBar.tsx                    |  9 +++++++--
 .../DuplicateTransactionItem.tsx                 |  2 +-
 src/pages/home/report/ReportActionsList.tsx      | 16 +++++-----------
 src/styles/index.ts                              |  9 ---------
 4 files changed, 13 insertions(+), 23 deletions(-)

diff --git a/src/components/LoadingBar.tsx b/src/components/LoadingBar.tsx
index 17b88d08ba84..6109f1123d8a 100644
--- a/src/components/LoadingBar.tsx
+++ b/src/components/LoadingBar.tsx
@@ -2,13 +2,18 @@ import React, {useEffect} from 'react';
 import {View} from 'react-native';
 import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
 import colors from '@styles/theme/colors';
+import useThemeStyles from '@hooks/useThemeStyles';
 
-type LoadingBarProps = {shouldShow: boolean};
+type LoadingBarProps = {
+    // Whether to show the loading bar
+    shouldShow: boolean;
+};
 
 function LoadingBar({shouldShow}: LoadingBarProps) {
     const left = useSharedValue(-30);
     const opacity = useSharedValue(0);
     const isAnimating = useSharedValue(false);
+    const styles = useThemeStyles();
 
     useEffect(() => {
         if (shouldShow && !isAnimating.get()) {
@@ -51,7 +56,7 @@ function LoadingBar({shouldShow}: LoadingBarProps) {
     }));
 
     return (
-        <View style={{height: 2, overflow: 'hidden'}}>
+        <View style={[styles.progressBarWrapper]}>
             <Animated.View style={barStyle} />
         </View>
     );
diff --git a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
index eb036c6fb3b3..ec180136a979 100644
--- a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
+++ b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
@@ -34,7 +34,7 @@ function DuplicateTransactionItem(props: DuplicateTransactionItemProps) {
             <ReportActionItem
                 action={action}
                 report={report}
-                parentReportAction={getReportAction(report?.parentReportID ?? undefined, report?.parentReportActionID ?? undefined)}
+                parentReportAction={getReportAction(report?.parentReportID, report?.parentReportActionID)}
                 index={props.index}
                 displayAsGroup={false}
                 shouldDisplayNewMarker={false}
diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx
index fc066e67a264..3cdb7b59ae7a 100644
--- a/src/pages/home/report/ReportActionsList.tsx
+++ b/src/pages/home/report/ReportActionsList.tsx
@@ -21,7 +21,6 @@ import useWindowDimensions from '@hooks/useWindowDimensions';
 import {isSafari} from '@libs/Browser';
 import DateUtils from '@libs/DateUtils';
 import {getChatFSAttributes, parseFSAttributes} from '@libs/Fullstory';
-import getPlatform from '@libs/getPlatform';
 import isReportTopmostSplitNavigator from '@libs/Navigation/helpers/isReportTopmostSplitNavigator';
 import isSearchTopmostFullScreenRoute from '@libs/Navigation/helpers/isSearchTopmostFullScreenRoute';
 import Navigation from '@libs/Navigation/Navigation';
@@ -222,9 +221,6 @@ function ReportActionsList({
 
     const prevUnreadMarkerReportActionID = useRef<string | null>(null);
 
-    const platform = getPlatform();
-    const isWebOrDesktop = platform === CONST.PLATFORM.WEB || platform === CONST.PLATFORM.DESKTOP;
-
     /**
      * Whether a message is NOT from the active user and it was received while the user was offline.
      */
@@ -715,14 +711,12 @@ function ReportActionsList({
     const onEndReached = useCallback(() => {
         loadOlderChats(false);
 
-        if (isWebOrDesktop) {
-            requestAnimationFrame(() => {
-                reportScrollManager.ref?.current?.scrollToOffset({
-                    offset: scrollingVerticalOffset.current - scrollOffset,
-                    animated: false,
-                });
+        requestAnimationFrame(() => {
+            reportScrollManager.ref?.current?.scrollToOffset({
+                offset: scrollingVerticalOffset.current - scrollOffset,
+                animated: false,
             });
-        }
+        });
         // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
     }, [loadOlderChats]);
 
diff --git a/src/styles/index.ts b/src/styles/index.ts
index 1c808291f98a..79b8833ead9e 100644
--- a/src/styles/index.ts
+++ b/src/styles/index.ts
@@ -5412,16 +5412,7 @@ const styles = (theme: ThemeColors) =>
 
         progressBarWrapper: {
             height: 2,
-            width: '100%',
-            backgroundColor: theme.transparent,
             overflow: 'hidden',
-            marginBottom: -1,
-        },
-
-        progressBar: {
-            height: '100%',
-            backgroundColor: theme.success,
-            width: '100%',
         },
 
         accountSwitcherAnchorPosition: {

From a0de6d53cd1c7e40302068aafe0ada1cdfe9b298 Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Tue, 4 Mar 2025 18:50:07 +0100
Subject: [PATCH 11/29] Fix prettier error & default
 isLoadingOlderReportActions flag

---
 src/components/LoadingBar.tsx               | 2 +-
 src/pages/home/report/ReportActionsView.tsx | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/components/LoadingBar.tsx b/src/components/LoadingBar.tsx
index 6109f1123d8a..932c8b1d7728 100644
--- a/src/components/LoadingBar.tsx
+++ b/src/components/LoadingBar.tsx
@@ -1,8 +1,8 @@
 import React, {useEffect} from 'react';
 import {View} from 'react-native';
 import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
-import colors from '@styles/theme/colors';
 import useThemeStyles from '@hooks/useThemeStyles';
+import colors from '@styles/theme/colors';
 
 type LoadingBarProps = {
     // Whether to show the loading bar
diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx
index 400e5bf3085d..95c03bde4011 100755
--- a/src/pages/home/report/ReportActionsView.tsx
+++ b/src/pages/home/report/ReportActionsView.tsx
@@ -78,7 +78,7 @@ function ReportActionsView({
     parentReportAction,
     reportActions: allReportActions,
     isLoadingInitialReportActions,
-    isLoadingOlderReportActions = true,
+    isLoadingOlderReportActions,
     transactionThreadReportID,
     hasNewerActions,
     hasOlderActions,

From 4315668466f36d9d74033e26011559806c08bff2 Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Tue, 4 Mar 2025 19:00:05 +0100
Subject: [PATCH 12/29] fix linter errors

---
 src/pages/home/report/ReportActionsView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx
index 95c03bde4011..fab8aef65405 100755
--- a/src/pages/home/report/ReportActionsView.tsx
+++ b/src/pages/home/report/ReportActionsView.tsx
@@ -216,7 +216,7 @@ function ReportActionsView({
         [reportActions, isOffline, canPerformWriteAction],
     );
 
-    const showLoadingBar = isLoadingOlderReportActions || !!(visibleReportActions.length > 0 && isLoadingInitialReportActions);
+    const showLoadingBar = !!isLoadingOlderReportActions || !!(visibleReportActions.length > 0 && isLoadingInitialReportActions);
 
     const reportActionIDMap = useMemo(() => {
         const reportActionIDs = allReportActions?.map((action) => action.reportActionID);

From 821bd06b897c2b941194f9678e5dca91e45315b0 Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Tue, 4 Mar 2025 19:09:30 +0100
Subject: [PATCH 13/29] show loading bar when fetching newer actions

---
 src/pages/home/ReportScreen.tsx             |  3 +--
 src/pages/home/report/ReportActionsView.tsx | 14 ++++++--------
 2 files changed, 7 insertions(+), 10 deletions(-)

diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx
index 7d17f2692a22..99906a5551d8 100644
--- a/src/pages/home/ReportScreen.tsx
+++ b/src/pages/home/ReportScreen.tsx
@@ -731,8 +731,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
                                     <ReportActionsView
                                         report={report}
                                         reportActions={reportActions}
-                                        isLoadingInitialReportActions={reportMetadata?.isLoadingInitialReportActions}
-                                        isLoadingOlderReportActions={reportMetadata?.isLoadingOlderReportActions}
+                                        reportMetadata={reportMetadata}
                                         hasNewerActions={hasNewerActions}
                                         hasOlderActions={hasOlderActions}
                                         parentReportAction={parentReportAction}
diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx
index fab8aef65405..d4f2d9a7437c 100755
--- a/src/pages/home/report/ReportActionsView.tsx
+++ b/src/pages/home/report/ReportActionsView.tsx
@@ -54,11 +54,8 @@ type ReportActionsViewProps = {
     /** The report's parentReportAction */
     parentReportAction: OnyxEntry<OnyxTypes.ReportAction>;
 
-    /** The report metadata loading states */
-    isLoadingInitialReportActions?: boolean;
-
-    /** Are we loading more report actions? */
-    isLoadingOlderReportActions?: boolean;
+    /** The report's metadata */
+    reportMetadata: OnyxTypes.ReportMetadata;
 
     /** The reportID of the transaction thread report associated with this current report, if any */
     // eslint-disable-next-line react/no-unused-prop-types
@@ -77,8 +74,7 @@ function ReportActionsView({
     report,
     parentReportAction,
     reportActions: allReportActions,
-    isLoadingInitialReportActions,
-    isLoadingOlderReportActions,
+    reportMetadata,
     transactionThreadReportID,
     hasNewerActions,
     hasOlderActions,
@@ -108,6 +104,8 @@ function ReportActionsView({
     const reportID = report.reportID;
     const isReportFullyVisible = useMemo((): boolean => getIsReportFullyVisible(isFocused), [isFocused]);
 
+    const {isLoadingInitialReportActions, isLoadingOlderReportActions, isLoadingNewerReportActions} = reportMetadata;
+
     useEffect(() => {
         // When we linked to message - we do not need to wait for initial actions - they already exists
         if (!reportActionID || !isOffline) {
@@ -216,7 +214,7 @@ function ReportActionsView({
         [reportActions, isOffline, canPerformWriteAction],
     );
 
-    const showLoadingBar = !!isLoadingOlderReportActions || !!(visibleReportActions.length > 0 && isLoadingInitialReportActions);
+    const showLoadingBar = !!isLoadingOlderReportActions || !!isLoadingNewerReportActions || !!(visibleReportActions.length > 0 && isLoadingInitialReportActions);
 
     const reportActionIDMap = useMemo(() => {
         const reportActionIDs = allReportActions?.map((action) => action.reportActionID);

From ce8867870e19c6edab6b366e051a32c4d9fdbe3d Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Wed, 26 Feb 2025 08:41:42 +0100
Subject: [PATCH 14/29] Loading state perception improvement

---
 src/components/LoadingBar.tsx               | 102 +++++++-------------
 src/components/MoneyReportHeader.tsx        |   3 -
 src/components/MoneyRequestHeader.tsx       |   3 -
 src/pages/home/HeaderView.tsx               |   2 -
 src/pages/home/ReportScreen.tsx             |   1 +
 src/pages/home/report/ReportActionsView.tsx |   8 ++
 6 files changed, 45 insertions(+), 74 deletions(-)

diff --git a/src/components/LoadingBar.tsx b/src/components/LoadingBar.tsx
index e6d1ec0cd66d..0d1740fcfeb6 100644
--- a/src/components/LoadingBar.tsx
+++ b/src/components/LoadingBar.tsx
@@ -1,83 +1,53 @@
 import React, {useEffect} from 'react';
-import Animated, {cancelAnimation, Easing, useAnimatedStyle, useSharedValue, withDelay, withRepeat, withSequence, withTiming} from 'react-native-reanimated';
-import useThemeStyles from '@hooks/useThemeStyles';
-import CONST from '@src/CONST';
+import {View} from 'react-native';
+import Animated, {runOnJS, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
+import colors from '@styles/theme/colors';
 
-type LoadingBarProps = {
-    // Whether or not to show the loading bar
-    shouldShow: boolean;
-};
+type LoadingBarProps = {shouldShow: boolean};
 
 function LoadingBar({shouldShow}: LoadingBarProps) {
-    const left = useSharedValue(0);
-    const width = useSharedValue(0);
+    const left = useSharedValue(-30);
     const opacity = useSharedValue(0);
-    const styles = useThemeStyles();
+    const isAnimating = useSharedValue(false);
 
     useEffect(() => {
-        if (shouldShow) {
-            // eslint-disable-next-line react-compiler/react-compiler
-            left.set(0);
-            width.set(0);
-            opacity.set(withTiming(1, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION}));
-
-            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,
-                    ),
-                ),
-            );
-
-            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,
-                    ),
-                ),
-            );
-        } else {
-            opacity.set(
-                withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION}, () => {
-                    cancelAnimation(left);
-                    cancelAnimation(width);
-                }),
-            );
+        if (shouldShow && !isAnimating.value) {
+            isAnimating.value = true;
+            opacity.value = withTiming(1, {duration: 300});
+
+            left.value = withTiming(100, {duration: 1200}, () => {
+                runOnJS(() => {
+                    if (shouldShow) {
+                        left.value = -30;
+                        left.value = withTiming(100, {duration: 1200});
+                    } else {
+                        opacity.value = withTiming(0, {duration: 300}, () => {
+                            isAnimating.value = false;
+                        });
+                    }
+                })();
+            });
+        } else if (!shouldShow) {
+            opacity.value = withTiming(0, {duration: 300}, () => {
+                isAnimating.value = false;
+            });
         }
-        // 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.get()}%`,
-        width: `${width.get()}%`,
-    }));
-
-    const animatedContainerStyle = useAnimatedStyle(() => ({
-        opacity: opacity.get(),
+    const barStyle = useAnimatedStyle(() => ({
+        left: `${left.value}%`,
+        width: '30%',
+        height: '100%',
+        backgroundColor: colors.green,
+        opacity: opacity.value,
+        borderRadius: 2,
     }));
 
     return (
-        <Animated.View style={[styles.progressBarWrapper, animatedContainerStyle]}>
-            <Animated.View style={[styles.progressBar, animatedIndicatorStyle]} />
-        </Animated.View>
+        <View style={{height: 2, overflow: 'hidden'}}>
+            <Animated.View style={barStyle} />
+        </View>
     );
 }
 
-LoadingBar.displayName = 'ProgressBar';
-
 export default LoadingBar;
diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx
index 97ee16ff0015..5713e97aca82 100644
--- a/src/components/MoneyReportHeader.tsx
+++ b/src/components/MoneyReportHeader.tsx
@@ -75,7 +75,6 @@ import DelegateNoAccessModal from './DelegateNoAccessModal';
 import HeaderWithBackButton from './HeaderWithBackButton';
 import Icon from './Icon';
 import * as Expensicons from './Icon/Expensicons';
-import LoadingBar from './LoadingBar';
 import MoneyReportHeaderStatusBar from './MoneyReportHeaderStatusBar';
 import type {MoneyRequestHeaderStatusBarProps} from './MoneyRequestHeaderStatusBar';
 import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar';
@@ -225,7 +224,6 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
     const isMoreContentShown = shouldShowNextStep || shouldShowStatusBar || (shouldShowAnyButton && shouldUseNarrowLayout);
     const {isDelegateAccessRestricted} = useDelegateUserDetails();
     const [isNoDelegateAccessMenuVisible, setIsNoDelegateAccessMenuVisible] = useState(false);
-    const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA);
 
     const isReportInRHP = route.name === SCREENS.SEARCH.REPORT_RHP;
     const shouldDisplaySearchRouter = !isReportInRHP || isSmallScreenWidth;
@@ -520,7 +518,6 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
                     )}
                 </View>
             )}
-            <LoadingBar shouldShow={(isLoadingReportData && shouldUseNarrowLayout) ?? false} />
             {isHoldMenuVisible && requestType !== undefined && (
                 <ProcessMoneyReportHoldMenu
                     nonHeldAmount={!hasOnlyHeldExpenses && hasValidNonHeldAmount ? nonHeldAmount : undefined}
diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx
index 6d7ca116a782..3fd7dd74f43c 100644
--- a/src/components/MoneyRequestHeader.tsx
+++ b/src/components/MoneyRequestHeader.tsx
@@ -36,7 +36,6 @@ import Button from './Button';
 import HeaderWithBackButton from './HeaderWithBackButton';
 import Icon from './Icon';
 import * as Expensicons from './Icon/Expensicons';
-import LoadingBar from './LoadingBar';
 import type {MoneyRequestHeaderStatusBarProps} from './MoneyRequestHeaderStatusBar';
 import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar';
 
@@ -68,7 +67,6 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
     const transactionViolations = useTransactionViolations(transaction?.transactionID);
 
     const [dismissedHoldUseExplanation, dismissedHoldUseExplanationResult] = useOnyx(ONYXKEYS.NVP_DISMISSED_HOLD_USE_EXPLANATION, {initialValue: true});
-    const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA);
     const isLoadingHoldUseExplained = isLoadingOnyxValue(dismissedHoldUseExplanationResult);
     const styles = useThemeStyles();
     const theme = useTheme();
@@ -214,7 +212,6 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
                     />
                 </View>
             )}
-            <LoadingBar shouldShow={(isLoadingReportData && shouldUseNarrowLayout) ?? false} />
         </View>
     );
 }
diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx
index 763ee3b10039..7018074eae86 100644
--- a/src/pages/home/HeaderView.tsx
+++ b/src/pages/home/HeaderView.tsx
@@ -106,7 +106,6 @@ function HeaderView({report, parentReportAction, onNavigationMenuButtonClicked,
     const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(report?.parentReportID) ?? getNonEmptyStringOnyxID(report?.reportID)}`);
     const policy = usePolicy(report?.policyID);
     const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST);
-    const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA);
     const [firstDayFreeTrial] = useOnyx(ONYXKEYS.NVP_FIRST_DAY_FREE_TRIAL);
     const [lastDayFreeTrial] = useOnyx(ONYXKEYS.NVP_LAST_DAY_FREE_TRIAL);
     const [account] = useOnyx(ONYXKEYS.ACCOUNT);
@@ -389,7 +388,6 @@ function HeaderView({report, parentReportAction, onNavigationMenuButtonClicked,
                         </View>
                     </View>
                 )}
-                <LoadingBar shouldShow={(isLoadingReportData && shouldUseNarrowLayout) ?? false} />
             </View>
             {shouldShowEarlyDiscountBanner && (
                 <EarlyDiscountBanner
diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx
index f29a4d85fc56..7d17f2692a22 100644
--- a/src/pages/home/ReportScreen.tsx
+++ b/src/pages/home/ReportScreen.tsx
@@ -732,6 +732,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
                                         report={report}
                                         reportActions={reportActions}
                                         isLoadingInitialReportActions={reportMetadata?.isLoadingInitialReportActions}
+                                        isLoadingOlderReportActions={reportMetadata?.isLoadingOlderReportActions}
                                         hasNewerActions={hasNewerActions}
                                         hasOlderActions={hasOlderActions}
                                         parentReportAction={parentReportAction}
diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx
index 88b4cae332ed..400e5bf3085d 100755
--- a/src/pages/home/report/ReportActionsView.tsx
+++ b/src/pages/home/report/ReportActionsView.tsx
@@ -5,6 +5,7 @@ import type {OnyxEntry} from 'react-native-onyx';
 import {useOnyx} from 'react-native-onyx';
 import EmptyStateComponent from '@components/EmptyStateComponent';
 import * as Illustrations from '@components/Icon/Illustrations';
+import LoadingBar from '@components/LoadingBar';
 import ReportActionsSkeletonView from '@components/ReportActionsSkeletonView';
 import SearchRowSkeleton from '@components/Skeletons/SearchRowSkeleton';
 import useCopySelectionHelper from '@hooks/useCopySelectionHelper';
@@ -56,6 +57,9 @@ type ReportActionsViewProps = {
     /** The report metadata loading states */
     isLoadingInitialReportActions?: boolean;
 
+    /** Are we loading more report actions? */
+    isLoadingOlderReportActions?: boolean;
+
     /** The reportID of the transaction thread report associated with this current report, if any */
     // eslint-disable-next-line react/no-unused-prop-types
     transactionThreadReportID?: string | null;
@@ -74,6 +78,7 @@ function ReportActionsView({
     parentReportAction,
     reportActions: allReportActions,
     isLoadingInitialReportActions,
+    isLoadingOlderReportActions = true,
     transactionThreadReportID,
     hasNewerActions,
     hasOlderActions,
@@ -211,6 +216,8 @@ function ReportActionsView({
         [reportActions, isOffline, canPerformWriteAction],
     );
 
+    const showLoadingBar = isLoadingOlderReportActions || !!(visibleReportActions.length > 0 && isLoadingInitialReportActions);
+
     const reportActionIDMap = useMemo(() => {
         const reportActionIDs = allReportActions?.map((action) => action.reportActionID);
         return reportActions.map((action) => ({
@@ -372,6 +379,7 @@ function ReportActionsView({
     const shouldEnableAutoScroll = (hasNewestReportAction && (!reportActionID || !isNavigatingToLinkedMessage)) || (transactionThreadReport && !prevTransactionThreadReport);
     return (
         <>
+            <LoadingBar shouldShow={showLoadingBar} />
             <ReportActionsList
                 report={report}
                 transactionThreadReport={transactionThreadReport}

From c2b48f6409c470166f960acde832da6aff5c8962 Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Fri, 28 Feb 2025 10:23:39 +0100
Subject: [PATCH 15/29] Loading state perception improvement - loading bar and
 loading older actions logic

---
 src/components/LoadingBar.tsx                         |  4 ++--
 src/pages/home/report/PureReportActionItem.tsx        |  5 -----
 .../home/report/ReportActionItemParentAction.tsx      |  5 -----
 src/pages/home/report/ReportActionsList.tsx           | 11 +++++++++--
 .../home/report/ReportActionsListItemRenderer.tsx     |  6 ------
 5 files changed, 11 insertions(+), 20 deletions(-)

diff --git a/src/components/LoadingBar.tsx b/src/components/LoadingBar.tsx
index 0d1740fcfeb6..0fe64ce27834 100644
--- a/src/components/LoadingBar.tsx
+++ b/src/components/LoadingBar.tsx
@@ -1,6 +1,6 @@
 import React, {useEffect} from 'react';
 import {View} from 'react-native';
-import Animated, {runOnJS, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
+import Animated, {runOnUI, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
 import colors from '@styles/theme/colors';
 
 type LoadingBarProps = {shouldShow: boolean};
@@ -16,7 +16,7 @@ function LoadingBar({shouldShow}: LoadingBarProps) {
             opacity.value = withTiming(1, {duration: 300});
 
             left.value = withTiming(100, {duration: 1200}, () => {
-                runOnJS(() => {
+                runOnUI(() => {
                     if (shouldShow) {
                         left.value = -30;
                         left.value = withTiming(100, {duration: 1200});
diff --git a/src/pages/home/report/PureReportActionItem.tsx b/src/pages/home/report/PureReportActionItem.tsx
index 8ef1fe4a9442..5ba9aa1a18ea 100644
--- a/src/pages/home/report/PureReportActionItem.tsx
+++ b/src/pages/home/report/PureReportActionItem.tsx
@@ -163,10 +163,6 @@ type PureReportActionItemProps = {
     /** The transaction thread report associated with the report for this action, if any */
     transactionThreadReport?: OnyxEntry<OnyxTypes.Report>;
 
-    /** Array of report actions for the report for this action */
-    // eslint-disable-next-line react/no-unused-prop-types
-    reportActions: OnyxTypes.ReportAction[];
-
     /** Report action belonging to the report's parent */
     parentReportAction: OnyxEntry<OnyxTypes.ReportAction>;
 
@@ -1296,7 +1292,6 @@ export default memo(PureReportActionItem, (prevProps, nextProps) => {
         prevProps.linkedReportActionID === nextProps.linkedReportActionID &&
         lodashIsEqual(prevProps.report?.fieldList, nextProps.report?.fieldList) &&
         lodashIsEqual(prevProps.transactionThreadReport, nextProps.transactionThreadReport) &&
-        lodashIsEqual(prevProps.reportActions, nextProps.reportActions) &&
         lodashIsEqual(prevParentReportAction, nextParentReportAction) &&
         prevProps.draftMessage === nextProps.draftMessage &&
         prevProps.iouReport?.reportID === nextProps.iouReport?.reportID &&
diff --git a/src/pages/home/report/ReportActionItemParentAction.tsx b/src/pages/home/report/ReportActionItemParentAction.tsx
index b7134fd7059e..5d3284d7bee8 100644
--- a/src/pages/home/report/ReportActionItemParentAction.tsx
+++ b/src/pages/home/report/ReportActionItemParentAction.tsx
@@ -36,9 +36,6 @@ type ReportActionItemParentActionProps = {
     /** The transaction thread report associated with the current report, if any */
     transactionThreadReport: OnyxEntry<OnyxTypes.Report>;
 
-    /** Array of report actions for this report */
-    reportActions: OnyxTypes.ReportAction[];
-
     /** Report actions belonging to the report's parent */
     parentReportAction: OnyxEntry<OnyxTypes.ReportAction>;
 
@@ -55,7 +52,6 @@ type ReportActionItemParentActionProps = {
 function ReportActionItemParentAction({
     report,
     transactionThreadReport,
-    reportActions,
     parentReportAction,
     index = 0,
     shouldHideThreadDividerLine = false,
@@ -144,7 +140,6 @@ function ReportActionItemParentAction({
                             }
                             parentReportAction={parentReportAction}
                             report={ancestor.report}
-                            reportActions={reportActions}
                             transactionThreadReport={transactionThreadReport}
                             action={ancestor.reportAction}
                             displayAsGroup={false}
diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx
index 6924725eb99b..972a7818d8f9 100644
--- a/src/pages/home/report/ReportActionsList.tsx
+++ b/src/pages/home/report/ReportActionsList.tsx
@@ -176,6 +176,7 @@ function ReportActionsList({
     const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`);
     const [accountID] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.accountID});
     const participantsContext = useContext(PersonalDetailsContext);
+    const [scrollOffset, setScrollOffset] = useState(0);
 
     useEffect(() => {
         const unsubscriber = Visibility.onVisibilityChange(() => {
@@ -512,6 +513,7 @@ function ReportActionsList({
     };
 
     const trackVerticalScrolling = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
+        setScrollOffset(event.nativeEvent.contentOffset.y);
         scrollingVerticalOffset.current = event.nativeEvent.contentOffset.y;
         handleUnreadFloatingButton();
         onScroll?.(event);
@@ -618,7 +620,6 @@ function ReportActionsList({
         ({item: reportAction, index}: ListRenderItemInfo<OnyxTypes.ReportAction>) => (
             <ReportActionsListItemRenderer
                 reportAction={reportAction}
-                reportActions={sortedReportActions}
                 parentReportAction={parentReportAction}
                 parentReportActionForTransactionThread={parentReportActionForTransactionThread}
                 index={index}
@@ -644,7 +645,6 @@ function ReportActionsList({
             mostRecentIOUReportActionID,
             shouldHideThreadDividerLine,
             parentReportAction,
-            sortedReportActions,
             transactionThreadReport,
             parentReportActionForTransactionThread,
             shouldUseThreadDividerLine,
@@ -709,6 +709,13 @@ function ReportActionsList({
 
     const onEndReached = useCallback(() => {
         loadOlderChats(false);
+
+        requestAnimationFrame(() => {
+            reportScrollManager.ref?.current?.scrollToOffset({
+                offset: scrollingVerticalOffset.current - scrollOffset,
+                animated: false,
+            });
+        });
     }, [loadOlderChats]);
 
     // Parse Fullstory attributes on initial render
diff --git a/src/pages/home/report/ReportActionsListItemRenderer.tsx b/src/pages/home/report/ReportActionsListItemRenderer.tsx
index 1a0d8db13661..b9cc7406b890 100644
--- a/src/pages/home/report/ReportActionsListItemRenderer.tsx
+++ b/src/pages/home/report/ReportActionsListItemRenderer.tsx
@@ -11,9 +11,6 @@ type ReportActionsListItemRendererProps = {
     /** All the data of the action item */
     reportAction: ReportAction;
 
-    /** Array of report actions for the report */
-    reportActions: ReportAction[];
-
     /** The report's parentReportAction */
     parentReportAction: OnyxEntry<ReportAction>;
 
@@ -56,7 +53,6 @@ type ReportActionsListItemRendererProps = {
 
 function ReportActionsListItemRenderer({
     reportAction,
-    reportActions = [],
     parentReportAction,
     index,
     report,
@@ -152,7 +148,6 @@ function ReportActionsListItemRenderer({
                 parentReportAction={parentReportAction}
                 reportID={report.reportID}
                 report={report}
-                reportActions={reportActions}
                 transactionThreadReport={transactionThreadReport}
                 index={index}
                 isFirstVisibleReportAction={isFirstVisibleReportAction}
@@ -169,7 +164,6 @@ function ReportActionsListItemRenderer({
             transactionThreadReport={transactionThreadReport}
             parentReportActionForTransactionThread={parentReportActionForTransactionThread}
             action={action}
-            reportActions={reportActions}
             linkedReportActionID={linkedReportActionID}
             displayAsGroup={displayAsGroup}
             shouldDisplayNewMarker={shouldDisplayNewMarker}

From 4ee407705dfdcaaef2b80d1054da51ac3969091f Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Fri, 28 Feb 2025 10:54:20 +0100
Subject: [PATCH 16/29] Loading state perception improvement: less blinking
 skeletons

---
 .../ReportActionsSkeletonView/SkeletonViewLines.tsx           | 1 +
 src/styles/theme/themes/dark.ts                               | 4 ++--
 src/styles/theme/themes/light.ts                              | 4 ++--
 3 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/src/components/ReportActionsSkeletonView/SkeletonViewLines.tsx b/src/components/ReportActionsSkeletonView/SkeletonViewLines.tsx
index c4c2a3f43eb3..d3ea26ab5880 100644
--- a/src/components/ReportActionsSkeletonView/SkeletonViewLines.tsx
+++ b/src/components/ReportActionsSkeletonView/SkeletonViewLines.tsx
@@ -20,6 +20,7 @@ function SkeletonViewLines({numberOfRows, shouldAnimate = true}: SkeletonViewLin
             height={CONST.CHAT_SKELETON_VIEW.HEIGHT_FOR_ROW_COUNT[numberOfRows]}
             backgroundColor={theme.skeletonLHNIn}
             foregroundColor={theme.skeletonLHNOut}
+            speed={3}
             style={styles.mr5}
         >
             <Circle
diff --git a/src/styles/theme/themes/dark.ts b/src/styles/theme/themes/dark.ts
index 4d5894812195..8a9fd3cbd804 100644
--- a/src/styles/theme/themes/dark.ts
+++ b/src/styles/theme/themes/dark.ts
@@ -85,8 +85,8 @@ const darkTheme = {
     tooltipSupportingText: colors.productLight800,
     tooltipPrimaryText: colors.productLight900,
     trialBannerBackgroundColor: colors.green700,
-    skeletonLHNIn: colors.productDark400,
-    skeletonLHNOut: colors.productDark600,
+    skeletonLHNIn: colors.productDark200,
+    skeletonLHNOut: colors.productDark300,
     QRLogo: colors.green400,
     starDefaultBG: 'rgb(254, 228, 94)',
     mapAttributionText: colors.black,
diff --git a/src/styles/theme/themes/light.ts b/src/styles/theme/themes/light.ts
index 1a187e85b0a2..6f4e5ee30e9d 100644
--- a/src/styles/theme/themes/light.ts
+++ b/src/styles/theme/themes/light.ts
@@ -85,8 +85,8 @@ const lightTheme = {
     tooltipSupportingText: colors.productDark800,
     tooltipPrimaryText: colors.productDark900,
     trialBannerBackgroundColor: colors.green100,
-    skeletonLHNIn: colors.productLight400,
-    skeletonLHNOut: colors.productLight600,
+    skeletonLHNIn: colors.productLight200,
+    skeletonLHNOut: colors.productLight300,
     QRLogo: colors.green400,
     starDefaultBG: 'rgb(254, 228, 94)',
     mapAttributionText: colors.black,

From 433cea2586828e6a27bacd09fec28463a48a1911 Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Fri, 28 Feb 2025 11:56:26 +0100
Subject: [PATCH 17/29] remove redundant reportActions props

---
 src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx | 1 -
 src/pages/Debug/ReportAction/DebugReportActionPreview.tsx    | 1 -
 src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx  | 1 -
 3 files changed, 3 deletions(-)

diff --git a/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx b/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx
index 1162146b8f4d..adf7a78c6a2a 100644
--- a/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx
+++ b/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx
@@ -107,7 +107,6 @@ function DebugReportActionCreatePage({
                                 <ReportActionItem
                                     action={JSON.parse(draftReportAction.replaceAll('\n', '')) as ReportAction}
                                     report={{reportID}}
-                                    reportActions={[]}
                                     parentReportAction={undefined}
                                     displayAsGroup={false}
                                     isMostRecentIOUReportAction={false}
diff --git a/src/pages/Debug/ReportAction/DebugReportActionPreview.tsx b/src/pages/Debug/ReportAction/DebugReportActionPreview.tsx
index 94334ffb4483..e9b37ea00a41 100644
--- a/src/pages/Debug/ReportAction/DebugReportActionPreview.tsx
+++ b/src/pages/Debug/ReportAction/DebugReportActionPreview.tsx
@@ -19,7 +19,6 @@ function DebugReportActionPreview({reportAction}: DebugReportActionPreviewProps)
             <ReportActionItem
                 action={reportAction ?? ({} as ReportAction)}
                 report={report ?? ({} as Report)}
-                reportActions={[]}
                 parentReportAction={undefined}
                 displayAsGroup={false}
                 isMostRecentIOUReportAction={false}
diff --git a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
index 398ea8ae336b..a6ddbd9a0a72 100644
--- a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
+++ b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
@@ -35,7 +35,6 @@ function DuplicateTransactionItem(props: DuplicateTransactionItemProps) {
                 report={report}
                 parentReportAction={ReportActionsUtils.getReportAction(report?.parentReportID ?? '', report?.parentReportActionID ?? '')}
                 index={props.index}
-                reportActions={Object.values(reportActions ?? {})}
                 displayAsGroup={false}
                 shouldDisplayNewMarker={false}
                 isMostRecentIOUReportAction={false}

From fad5bdb723b39bd4fa74c32371c804488018a91c Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Fri, 28 Feb 2025 12:58:23 +0100
Subject: [PATCH 18/29] remove redundant imports

---
 src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx | 6 +++---
 src/pages/home/HeaderView.tsx                               | 1 -
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
index a6ddbd9a0a72..2738edadbd6a 100644
--- a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
+++ b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
@@ -3,7 +3,7 @@ import {View} from 'react-native';
 import type {OnyxEntry} from 'react-native-onyx';
 import {useOnyx} from 'react-native-onyx';
 import useThemeStyles from '@hooks/useThemeStyles';
-import * as ReportActionsUtils from '@libs/ReportActionsUtils';
+import {getOriginalMessage, getReportAction, isMoneyRequestAction} from '@libs/ReportActionsUtils';
 import ReportActionItem from '@pages/home/report/ReportActionItem';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Transaction} from '@src/types/onyx';
@@ -20,7 +20,7 @@ function DuplicateTransactionItem(props: DuplicateTransactionItemProps) {
 
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/non-nullable-type-assertion-style
     const action = Object.values(reportActions ?? {})?.find((reportAction) => {
-        const IOUTransactionID = ReportActionsUtils.isMoneyRequestAction(reportAction) ? ReportActionsUtils.getOriginalMessage(reportAction)?.IOUTransactionID : -1;
+        const IOUTransactionID = isMoneyRequestAction(reportAction) ? getOriginalMessage(reportAction)?.IOUTransactionID : -1;
         return IOUTransactionID === props.transaction?.transactionID;
     });
 
@@ -33,7 +33,7 @@ function DuplicateTransactionItem(props: DuplicateTransactionItemProps) {
             <ReportActionItem
                 action={action}
                 report={report}
-                parentReportAction={ReportActionsUtils.getReportAction(report?.parentReportID ?? '', report?.parentReportActionID ?? '')}
+                parentReportAction={getReportAction(report?.parentReportID ?? '', report?.parentReportActionID ?? '')}
                 index={props.index}
                 displayAsGroup={false}
                 shouldDisplayNewMarker={false}
diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx
index 7018074eae86..fdf94757a41c 100644
--- a/src/pages/home/HeaderView.tsx
+++ b/src/pages/home/HeaderView.tsx
@@ -9,7 +9,6 @@ import ConfirmModal from '@components/ConfirmModal';
 import DisplayNames from '@components/DisplayNames';
 import Icon from '@components/Icon';
 import {BackArrow, CalendarSolid, DotIndicator, FallbackAvatar} from '@components/Icon/Expensicons';
-import LoadingBar from '@components/LoadingBar';
 import MultipleAvatars from '@components/MultipleAvatars';
 import OfflineWithFeedback from '@components/OfflineWithFeedback';
 import ParentNavigationSubtitle from '@components/ParentNavigationSubtitle';

From 99bce830df209b032f0315430c7c8b0743c331f1 Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Fri, 28 Feb 2025 13:23:29 +0100
Subject: [PATCH 19/29] loading bar animation performance improvement

---
 src/components/LoadingBar.tsx | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/components/LoadingBar.tsx b/src/components/LoadingBar.tsx
index 0fe64ce27834..7f8e4de9c224 100644
--- a/src/components/LoadingBar.tsx
+++ b/src/components/LoadingBar.tsx
@@ -1,6 +1,6 @@
 import React, {useEffect} from 'react';
 import {View} from 'react-native';
-import Animated, {runOnUI, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
+import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
 import colors from '@styles/theme/colors';
 
 type LoadingBarProps = {shouldShow: boolean};
@@ -16,7 +16,7 @@ function LoadingBar({shouldShow}: LoadingBarProps) {
             opacity.value = withTiming(1, {duration: 300});
 
             left.value = withTiming(100, {duration: 1200}, () => {
-                runOnUI(() => {
+                requestAnimationFrame(() => {
                     if (shouldShow) {
                         left.value = -30;
                         left.value = withTiming(100, {duration: 1200});
@@ -25,7 +25,7 @@ function LoadingBar({shouldShow}: LoadingBarProps) {
                             isAnimating.value = false;
                         });
                     }
-                })();
+                });
             });
         } else if (!shouldShow) {
             opacity.value = withTiming(0, {duration: 300}, () => {

From 21d7f288568d929a6943a2317c7897a57cdce54b Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Fri, 28 Feb 2025 13:50:26 +0100
Subject: [PATCH 20/29] manage scrolling position only for web and desktop

---
 src/pages/home/report/ReportActionsList.tsx | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx
index 972a7818d8f9..99385d3cb9d8 100644
--- a/src/pages/home/report/ReportActionsList.tsx
+++ b/src/pages/home/report/ReportActionsList.tsx
@@ -61,6 +61,7 @@ import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
 import type SCREENS from '@src/SCREENS';
 import type * as OnyxTypes from '@src/types/onyx';
+import getPlatform from '@libs/getPlatform';
 import FloatingMessageCounter from './FloatingMessageCounter';
 import getInitialNumToRender from './getInitialNumReportActionsToRender';
 import ListBoundaryLoader from './ListBoundaryLoader';
@@ -220,6 +221,11 @@ function ReportActionsList({
     }, [report.reportID]);
 
     const prevUnreadMarkerReportActionID = useRef<string | null>(null);
+
+    const platform = getPlatform();
+    const isWebOrDesktop = platform === CONST.PLATFORM.WEB || platform === CONST.PLATFORM.DESKTOP;
+
+
     /**
      * Whether a message is NOT from the active user and it was received while the user was offline.
      */
@@ -710,12 +716,14 @@ function ReportActionsList({
     const onEndReached = useCallback(() => {
         loadOlderChats(false);
 
-        requestAnimationFrame(() => {
-            reportScrollManager.ref?.current?.scrollToOffset({
-                offset: scrollingVerticalOffset.current - scrollOffset,
-                animated: false,
+        if (isWebOrDesktop) {
+            requestAnimationFrame(() => {
+                reportScrollManager.ref?.current?.scrollToOffset({
+                    offset: scrollingVerticalOffset.current - scrollOffset,
+                    animated: false,
+                });
             });
-        });
+        }
     }, [loadOlderChats]);
 
     // Parse Fullstory attributes on initial render

From 8f5878341638aaa4598b41936d7075321aaf27e1 Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Fri, 28 Feb 2025 14:53:41 +0100
Subject: [PATCH 21/29] fix linter errors

---
 src/components/LoadingBar.tsx                 | 30 +++++++++----------
 .../DebugReportActionCreatePage.tsx           |  8 ++---
 .../DuplicateTransactionItem.tsx              |  5 ++--
 3 files changed, 22 insertions(+), 21 deletions(-)

diff --git a/src/components/LoadingBar.tsx b/src/components/LoadingBar.tsx
index 7f8e4de9c224..f8ac88fbc228 100644
--- a/src/components/LoadingBar.tsx
+++ b/src/components/LoadingBar.tsx
@@ -11,35 +11,35 @@ function LoadingBar({shouldShow}: LoadingBarProps) {
     const isAnimating = useSharedValue(false);
 
     useEffect(() => {
-        if (shouldShow && !isAnimating.value) {
-            isAnimating.value = true;
-            opacity.value = withTiming(1, {duration: 300});
+        if (shouldShow && !isAnimating.get()) {
+            isAnimating.set(true);
+            opacity.set(withTiming(1, {duration: 300}));
 
-            left.value = withTiming(100, {duration: 1200}, () => {
+            left.set(withTiming(100, {duration: 1200}, () => {
                 requestAnimationFrame(() => {
                     if (shouldShow) {
-                        left.value = -30;
-                        left.value = withTiming(100, {duration: 1200});
+                        left.set(-30);
+                        left.set(withTiming(100, {duration: 1200}));
                     } else {
-                        opacity.value = withTiming(0, {duration: 300}, () => {
-                            isAnimating.value = false;
-                        });
+                        opacity.set(withTiming(0, {duration: 300}, () => {
+                            isAnimating.set(false);
+                        }));
                     }
                 });
-            });
+            }));
         } else if (!shouldShow) {
-            opacity.value = withTiming(0, {duration: 300}, () => {
-                isAnimating.value = false;
-            });
+            opacity.set(withTiming(0, {duration: 300}, () => {
+                isAnimating.set(false);
+            }));
         }
     }, [shouldShow]);
 
     const barStyle = useAnimatedStyle(() => ({
-        left: `${left.value}%`,
+        left: `${left.get()}%`,
         width: '30%',
         height: '100%',
         backgroundColor: colors.green,
-        opacity: opacity.value,
+        opacity: opacity.get(),
         borderRadius: 2,
     }));
 
diff --git a/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx b/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx
index adf7a78c6a2a..b7823842d4b8 100644
--- a/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx
+++ b/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx
@@ -12,11 +12,9 @@ import useLocalize from '@hooks/useLocalize';
 import useThemeStyles from '@hooks/useThemeStyles';
 import DateUtils from '@libs/DateUtils';
 import DebugUtils from '@libs/DebugUtils';
-import * as DeviceCapabilities from '@libs/DeviceCapabilities';
 import Navigation from '@libs/Navigation/Navigation';
 import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
 import type {DebugParamList} from '@libs/Navigation/types';
-import * as NumberUtils from '@libs/NumberUtils';
 import ReportActionItem from '@pages/home/report/ReportActionItem';
 import Debug from '@userActions/Debug';
 import CONST from '@src/CONST';
@@ -25,6 +23,8 @@ import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
 import type SCREENS from '@src/SCREENS';
 import type {PersonalDetailsList, ReportAction, Session} from '@src/types/onyx';
+import {rand64} from '@libs/NumberUtils';
+import {canUseTouchScreen} from '@libs/DeviceCapabilities';
 
 type DebugReportActionCreatePageProps = PlatformStackScreenProps<DebugParamList, typeof SCREENS.DEBUG.REPORT_ACTION_CREATE>;
 
@@ -32,7 +32,7 @@ const getInitialReportAction = (reportID: string, session: OnyxEntry<Session>, p
     DebugUtils.stringifyJSON({
         actionName: CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT,
         reportID,
-        reportActionID: NumberUtils.rand64(),
+        reportActionID: rand64(),
         created: DateUtils.getDBTime(),
         actorAccountID: session?.accountID,
         avatar: (session?.accountID && personalDetailsList?.[session.accountID]?.avatar) ?? '',
@@ -78,7 +78,7 @@ function DebugReportActionCreatePage({
         <ScreenWrapper
             includeSafeAreaPaddingBottom={false}
             shouldEnableKeyboardAvoidingView={false}
-            shouldEnableMinHeight={DeviceCapabilities.canUseTouchScreen()}
+            shouldEnableMinHeight={canUseTouchScreen()}
             testID={DebugReportActionCreatePage.displayName}
         >
             {({safeAreaPaddingBottomStyle}) => (
diff --git a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
index 2738edadbd6a..5fe10a4378ef 100644
--- a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
+++ b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
@@ -7,6 +7,7 @@ import {getOriginalMessage, getReportAction, isMoneyRequestAction} from '@libs/R
 import ReportActionItem from '@pages/home/report/ReportActionItem';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Transaction} from '@src/types/onyx';
+import CONST from '@src/CONST';
 
 type DuplicateTransactionItemProps = {
     transaction: OnyxEntry<Transaction>;
@@ -20,7 +21,7 @@ function DuplicateTransactionItem(props: DuplicateTransactionItemProps) {
 
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/non-nullable-type-assertion-style
     const action = Object.values(reportActions ?? {})?.find((reportAction) => {
-        const IOUTransactionID = isMoneyRequestAction(reportAction) ? getOriginalMessage(reportAction)?.IOUTransactionID : -1;
+        const IOUTransactionID = isMoneyRequestAction(reportAction) ? getOriginalMessage(reportAction)?.IOUTransactionID : CONST.DEFAULT_NUMBER_ID;
         return IOUTransactionID === props.transaction?.transactionID;
     });
 
@@ -33,7 +34,7 @@ function DuplicateTransactionItem(props: DuplicateTransactionItemProps) {
             <ReportActionItem
                 action={action}
                 report={report}
-                parentReportAction={getReportAction(report?.parentReportID ?? '', report?.parentReportActionID ?? '')}
+                parentReportAction={getReportAction(report?.parentReportID ?? undefined, report?.parentReportActionID ?? undefined)}
                 index={props.index}
                 displayAsGroup={false}
                 shouldDisplayNewMarker={false}

From 502cf8ca1a8774e33f823a82585d8381ca025ff8 Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Fri, 28 Feb 2025 15:00:01 +0100
Subject: [PATCH 22/29] fix linter errors

---
 src/components/LoadingBar.tsx                 | 37 +++++++++++--------
 .../DebugReportActionCreatePage.tsx           |  4 +-
 .../DuplicateTransactionItem.tsx              |  2 +-
 src/pages/home/report/ReportActionsList.tsx   |  4 +-
 4 files changed, 27 insertions(+), 20 deletions(-)

diff --git a/src/components/LoadingBar.tsx b/src/components/LoadingBar.tsx
index f8ac88fbc228..17b88d08ba84 100644
--- a/src/components/LoadingBar.tsx
+++ b/src/components/LoadingBar.tsx
@@ -15,23 +15,30 @@ function LoadingBar({shouldShow}: LoadingBarProps) {
             isAnimating.set(true);
             opacity.set(withTiming(1, {duration: 300}));
 
-            left.set(withTiming(100, {duration: 1200}, () => {
-                requestAnimationFrame(() => {
-                    if (shouldShow) {
-                        left.set(-30);
-                        left.set(withTiming(100, {duration: 1200}));
-                    } else {
-                        opacity.set(withTiming(0, {duration: 300}, () => {
-                            isAnimating.set(false);
-                        }));
-                    }
-                });
-            }));
+            left.set(
+                withTiming(100, {duration: 1200}, () => {
+                    requestAnimationFrame(() => {
+                        if (shouldShow) {
+                            left.set(-30);
+                            left.set(withTiming(100, {duration: 1200}));
+                        } else {
+                            opacity.set(
+                                withTiming(0, {duration: 300}, () => {
+                                    isAnimating.set(false);
+                                }),
+                            );
+                        }
+                    });
+                }),
+            );
         } else if (!shouldShow) {
-            opacity.set(withTiming(0, {duration: 300}, () => {
-                isAnimating.set(false);
-            }));
+            opacity.set(
+                withTiming(0, {duration: 300}, () => {
+                    isAnimating.set(false);
+                }),
+            );
         }
+        // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
     }, [shouldShow]);
 
     const barStyle = useAnimatedStyle(() => ({
diff --git a/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx b/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx
index b7823842d4b8..2331d84b1a16 100644
--- a/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx
+++ b/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx
@@ -12,9 +12,11 @@ import useLocalize from '@hooks/useLocalize';
 import useThemeStyles from '@hooks/useThemeStyles';
 import DateUtils from '@libs/DateUtils';
 import DebugUtils from '@libs/DebugUtils';
+import {canUseTouchScreen} from '@libs/DeviceCapabilities';
 import Navigation from '@libs/Navigation/Navigation';
 import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
 import type {DebugParamList} from '@libs/Navigation/types';
+import {rand64} from '@libs/NumberUtils';
 import ReportActionItem from '@pages/home/report/ReportActionItem';
 import Debug from '@userActions/Debug';
 import CONST from '@src/CONST';
@@ -23,8 +25,6 @@ import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
 import type SCREENS from '@src/SCREENS';
 import type {PersonalDetailsList, ReportAction, Session} from '@src/types/onyx';
-import {rand64} from '@libs/NumberUtils';
-import {canUseTouchScreen} from '@libs/DeviceCapabilities';
 
 type DebugReportActionCreatePageProps = PlatformStackScreenProps<DebugParamList, typeof SCREENS.DEBUG.REPORT_ACTION_CREATE>;
 
diff --git a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
index 5fe10a4378ef..eb036c6fb3b3 100644
--- a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
+++ b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
@@ -5,9 +5,9 @@ import {useOnyx} from 'react-native-onyx';
 import useThemeStyles from '@hooks/useThemeStyles';
 import {getOriginalMessage, getReportAction, isMoneyRequestAction} from '@libs/ReportActionsUtils';
 import ReportActionItem from '@pages/home/report/ReportActionItem';
+import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Transaction} from '@src/types/onyx';
-import CONST from '@src/CONST';
 
 type DuplicateTransactionItemProps = {
     transaction: OnyxEntry<Transaction>;
diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx
index 99385d3cb9d8..fc066e67a264 100644
--- a/src/pages/home/report/ReportActionsList.tsx
+++ b/src/pages/home/report/ReportActionsList.tsx
@@ -21,6 +21,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions';
 import {isSafari} from '@libs/Browser';
 import DateUtils from '@libs/DateUtils';
 import {getChatFSAttributes, parseFSAttributes} from '@libs/Fullstory';
+import getPlatform from '@libs/getPlatform';
 import isReportTopmostSplitNavigator from '@libs/Navigation/helpers/isReportTopmostSplitNavigator';
 import isSearchTopmostFullScreenRoute from '@libs/Navigation/helpers/isSearchTopmostFullScreenRoute';
 import Navigation from '@libs/Navigation/Navigation';
@@ -61,7 +62,6 @@ import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
 import type SCREENS from '@src/SCREENS';
 import type * as OnyxTypes from '@src/types/onyx';
-import getPlatform from '@libs/getPlatform';
 import FloatingMessageCounter from './FloatingMessageCounter';
 import getInitialNumToRender from './getInitialNumReportActionsToRender';
 import ListBoundaryLoader from './ListBoundaryLoader';
@@ -225,7 +225,6 @@ function ReportActionsList({
     const platform = getPlatform();
     const isWebOrDesktop = platform === CONST.PLATFORM.WEB || platform === CONST.PLATFORM.DESKTOP;
 
-
     /**
      * Whether a message is NOT from the active user and it was received while the user was offline.
      */
@@ -724,6 +723,7 @@ function ReportActionsList({
                 });
             });
         }
+        // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
     }, [loadOlderChats]);
 
     // Parse Fullstory attributes on initial render

From ec54ce54e314be95c9fb1891512706e31af4e178 Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Tue, 4 Mar 2025 18:08:48 +0100
Subject: [PATCH 23/29] Addressed PR comments

---
 src/components/LoadingBar.tsx                    |  9 +++++++--
 .../DuplicateTransactionItem.tsx                 |  2 +-
 src/pages/home/report/ReportActionsList.tsx      | 16 +++++-----------
 src/styles/index.ts                              |  9 ---------
 4 files changed, 13 insertions(+), 23 deletions(-)

diff --git a/src/components/LoadingBar.tsx b/src/components/LoadingBar.tsx
index 17b88d08ba84..6109f1123d8a 100644
--- a/src/components/LoadingBar.tsx
+++ b/src/components/LoadingBar.tsx
@@ -2,13 +2,18 @@ import React, {useEffect} from 'react';
 import {View} from 'react-native';
 import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
 import colors from '@styles/theme/colors';
+import useThemeStyles from '@hooks/useThemeStyles';
 
-type LoadingBarProps = {shouldShow: boolean};
+type LoadingBarProps = {
+    // Whether to show the loading bar
+    shouldShow: boolean;
+};
 
 function LoadingBar({shouldShow}: LoadingBarProps) {
     const left = useSharedValue(-30);
     const opacity = useSharedValue(0);
     const isAnimating = useSharedValue(false);
+    const styles = useThemeStyles();
 
     useEffect(() => {
         if (shouldShow && !isAnimating.get()) {
@@ -51,7 +56,7 @@ function LoadingBar({shouldShow}: LoadingBarProps) {
     }));
 
     return (
-        <View style={{height: 2, overflow: 'hidden'}}>
+        <View style={[styles.progressBarWrapper]}>
             <Animated.View style={barStyle} />
         </View>
     );
diff --git a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
index eb036c6fb3b3..ec180136a979 100644
--- a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
+++ b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
@@ -34,7 +34,7 @@ function DuplicateTransactionItem(props: DuplicateTransactionItemProps) {
             <ReportActionItem
                 action={action}
                 report={report}
-                parentReportAction={getReportAction(report?.parentReportID ?? undefined, report?.parentReportActionID ?? undefined)}
+                parentReportAction={getReportAction(report?.parentReportID, report?.parentReportActionID)}
                 index={props.index}
                 displayAsGroup={false}
                 shouldDisplayNewMarker={false}
diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx
index fc066e67a264..3cdb7b59ae7a 100644
--- a/src/pages/home/report/ReportActionsList.tsx
+++ b/src/pages/home/report/ReportActionsList.tsx
@@ -21,7 +21,6 @@ import useWindowDimensions from '@hooks/useWindowDimensions';
 import {isSafari} from '@libs/Browser';
 import DateUtils from '@libs/DateUtils';
 import {getChatFSAttributes, parseFSAttributes} from '@libs/Fullstory';
-import getPlatform from '@libs/getPlatform';
 import isReportTopmostSplitNavigator from '@libs/Navigation/helpers/isReportTopmostSplitNavigator';
 import isSearchTopmostFullScreenRoute from '@libs/Navigation/helpers/isSearchTopmostFullScreenRoute';
 import Navigation from '@libs/Navigation/Navigation';
@@ -222,9 +221,6 @@ function ReportActionsList({
 
     const prevUnreadMarkerReportActionID = useRef<string | null>(null);
 
-    const platform = getPlatform();
-    const isWebOrDesktop = platform === CONST.PLATFORM.WEB || platform === CONST.PLATFORM.DESKTOP;
-
     /**
      * Whether a message is NOT from the active user and it was received while the user was offline.
      */
@@ -715,14 +711,12 @@ function ReportActionsList({
     const onEndReached = useCallback(() => {
         loadOlderChats(false);
 
-        if (isWebOrDesktop) {
-            requestAnimationFrame(() => {
-                reportScrollManager.ref?.current?.scrollToOffset({
-                    offset: scrollingVerticalOffset.current - scrollOffset,
-                    animated: false,
-                });
+        requestAnimationFrame(() => {
+            reportScrollManager.ref?.current?.scrollToOffset({
+                offset: scrollingVerticalOffset.current - scrollOffset,
+                animated: false,
             });
-        }
+        });
         // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
     }, [loadOlderChats]);
 
diff --git a/src/styles/index.ts b/src/styles/index.ts
index 1c808291f98a..79b8833ead9e 100644
--- a/src/styles/index.ts
+++ b/src/styles/index.ts
@@ -5412,16 +5412,7 @@ const styles = (theme: ThemeColors) =>
 
         progressBarWrapper: {
             height: 2,
-            width: '100%',
-            backgroundColor: theme.transparent,
             overflow: 'hidden',
-            marginBottom: -1,
-        },
-
-        progressBar: {
-            height: '100%',
-            backgroundColor: theme.success,
-            width: '100%',
         },
 
         accountSwitcherAnchorPosition: {

From 17e18ac14f9daa4b106e64260594d94c77a0a93d Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Tue, 4 Mar 2025 18:50:07 +0100
Subject: [PATCH 24/29] Fix prettier error & default
 isLoadingOlderReportActions flag

---
 src/components/LoadingBar.tsx               | 2 +-
 src/pages/home/report/ReportActionsView.tsx | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/components/LoadingBar.tsx b/src/components/LoadingBar.tsx
index 6109f1123d8a..932c8b1d7728 100644
--- a/src/components/LoadingBar.tsx
+++ b/src/components/LoadingBar.tsx
@@ -1,8 +1,8 @@
 import React, {useEffect} from 'react';
 import {View} from 'react-native';
 import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
-import colors from '@styles/theme/colors';
 import useThemeStyles from '@hooks/useThemeStyles';
+import colors from '@styles/theme/colors';
 
 type LoadingBarProps = {
     // Whether to show the loading bar
diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx
index 400e5bf3085d..95c03bde4011 100755
--- a/src/pages/home/report/ReportActionsView.tsx
+++ b/src/pages/home/report/ReportActionsView.tsx
@@ -78,7 +78,7 @@ function ReportActionsView({
     parentReportAction,
     reportActions: allReportActions,
     isLoadingInitialReportActions,
-    isLoadingOlderReportActions = true,
+    isLoadingOlderReportActions,
     transactionThreadReportID,
     hasNewerActions,
     hasOlderActions,

From d4a73a6cbda96a633377dccdf1875145f5f72c1f Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Tue, 4 Mar 2025 19:00:05 +0100
Subject: [PATCH 25/29] fix linter errors

---
 src/pages/home/report/ReportActionsView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx
index 95c03bde4011..fab8aef65405 100755
--- a/src/pages/home/report/ReportActionsView.tsx
+++ b/src/pages/home/report/ReportActionsView.tsx
@@ -216,7 +216,7 @@ function ReportActionsView({
         [reportActions, isOffline, canPerformWriteAction],
     );
 
-    const showLoadingBar = isLoadingOlderReportActions || !!(visibleReportActions.length > 0 && isLoadingInitialReportActions);
+    const showLoadingBar = !!isLoadingOlderReportActions || !!(visibleReportActions.length > 0 && isLoadingInitialReportActions);
 
     const reportActionIDMap = useMemo(() => {
         const reportActionIDs = allReportActions?.map((action) => action.reportActionID);

From d7c6e08f7311d674d67b5b04897517e82bde2798 Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Tue, 4 Mar 2025 19:09:30 +0100
Subject: [PATCH 26/29] show loading bar when fetching newer actions

---
 src/pages/home/ReportScreen.tsx             |  3 +--
 src/pages/home/report/ReportActionsView.tsx | 14 ++++++--------
 2 files changed, 7 insertions(+), 10 deletions(-)

diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx
index 7d17f2692a22..99906a5551d8 100644
--- a/src/pages/home/ReportScreen.tsx
+++ b/src/pages/home/ReportScreen.tsx
@@ -731,8 +731,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
                                     <ReportActionsView
                                         report={report}
                                         reportActions={reportActions}
-                                        isLoadingInitialReportActions={reportMetadata?.isLoadingInitialReportActions}
-                                        isLoadingOlderReportActions={reportMetadata?.isLoadingOlderReportActions}
+                                        reportMetadata={reportMetadata}
                                         hasNewerActions={hasNewerActions}
                                         hasOlderActions={hasOlderActions}
                                         parentReportAction={parentReportAction}
diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx
index fab8aef65405..d4f2d9a7437c 100755
--- a/src/pages/home/report/ReportActionsView.tsx
+++ b/src/pages/home/report/ReportActionsView.tsx
@@ -54,11 +54,8 @@ type ReportActionsViewProps = {
     /** The report's parentReportAction */
     parentReportAction: OnyxEntry<OnyxTypes.ReportAction>;
 
-    /** The report metadata loading states */
-    isLoadingInitialReportActions?: boolean;
-
-    /** Are we loading more report actions? */
-    isLoadingOlderReportActions?: boolean;
+    /** The report's metadata */
+    reportMetadata: OnyxTypes.ReportMetadata;
 
     /** The reportID of the transaction thread report associated with this current report, if any */
     // eslint-disable-next-line react/no-unused-prop-types
@@ -77,8 +74,7 @@ function ReportActionsView({
     report,
     parentReportAction,
     reportActions: allReportActions,
-    isLoadingInitialReportActions,
-    isLoadingOlderReportActions,
+    reportMetadata,
     transactionThreadReportID,
     hasNewerActions,
     hasOlderActions,
@@ -108,6 +104,8 @@ function ReportActionsView({
     const reportID = report.reportID;
     const isReportFullyVisible = useMemo((): boolean => getIsReportFullyVisible(isFocused), [isFocused]);
 
+    const {isLoadingInitialReportActions, isLoadingOlderReportActions, isLoadingNewerReportActions} = reportMetadata;
+
     useEffect(() => {
         // When we linked to message - we do not need to wait for initial actions - they already exists
         if (!reportActionID || !isOffline) {
@@ -216,7 +214,7 @@ function ReportActionsView({
         [reportActions, isOffline, canPerformWriteAction],
     );
 
-    const showLoadingBar = !!isLoadingOlderReportActions || !!(visibleReportActions.length > 0 && isLoadingInitialReportActions);
+    const showLoadingBar = !!isLoadingOlderReportActions || !!isLoadingNewerReportActions || !!(visibleReportActions.length > 0 && isLoadingInitialReportActions);
 
     const reportActionIDMap = useMemo(() => {
         const reportActionIDs = allReportActions?.map((action) => action.reportActionID);

From 2590b10dc51a93ade064290ad1f4c47a0436a2e1 Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Wed, 5 Mar 2025 13:49:59 +0100
Subject: [PATCH 27/29] load newer actions fix

---
 src/pages/home/report/ReportActionsView.tsx | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx
index d4f2d9a7437c..6bd98e47f8aa 100755
--- a/src/pages/home/report/ReportActionsView.tsx
+++ b/src/pages/home/report/ReportActionsView.tsx
@@ -104,7 +104,7 @@ function ReportActionsView({
     const reportID = report.reportID;
     const isReportFullyVisible = useMemo((): boolean => getIsReportFullyVisible(isFocused), [isFocused]);
 
-    const {isLoadingInitialReportActions, isLoadingOlderReportActions, isLoadingNewerReportActions} = reportMetadata;
+    const {isLoadingInitialReportActions, isLoadingOlderReportActions, isLoadingNewerReportActions, hasLoadingNewerReportActionsError} = reportMetadata;
 
     useEffect(() => {
         // When we linked to message - we do not need to wait for initial actions - they already exists
@@ -287,7 +287,7 @@ function ReportActionsView({
                     isOffline ||
                     // If there was an error only try again once on initial mount. We should also still load
                     // more in case we have cached messages.
-                    didLoadNewerChats.current ||
+                    !!(didLoadNewerChats.current && hasLoadingNewerReportActionsError) ||
                     newestReportAction.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE)
             ) {
                 return;
@@ -308,7 +308,7 @@ function ReportActionsView({
                 getNewerActions(reportID, newestReportAction.reportActionID);
             }
         },
-        [reportActionID, isFocused, newestReportAction, hasNewerActions, isOffline, transactionThreadReport, reportActionIDMap, reportID],
+        [reportActionID, isFocused, newestReportAction, hasNewerActions, isOffline, hasLoadingNewerReportActionsError, transactionThreadReport, reportActionIDMap, reportID],
     );
 
     /**

From 322dcd1c8e07185ee056efb8325ea92f0a26b4f0 Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Wed, 5 Mar 2025 14:03:59 +0100
Subject: [PATCH 28/29] load newer actions fix

---
 tests/ui/PaginationTest.tsx | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/tests/ui/PaginationTest.tsx b/tests/ui/PaginationTest.tsx
index 73df63153b1d..e08a4d50f51f 100644
--- a/tests/ui/PaginationTest.tsx
+++ b/tests/ui/PaginationTest.tsx
@@ -359,10 +359,10 @@ describe('Pagination', () => {
 
         TestHelper.expectAPICommandToHaveBeenCalled('OpenReport', 3);
         TestHelper.expectAPICommandToHaveBeenCalled('GetOlderActions', 0);
-        TestHelper.expectAPICommandToHaveBeenCalled('GetNewerActions', 1);
+        TestHelper.expectAPICommandToHaveBeenCalled('GetNewerActions', 2);
 
         // We now have 10 messages. 5 from the initial OpenReport and 5 from the GetNewerActions call.
-        expect(getReportActions()).toHaveLength(10);
+        expect(getReportActions()).toHaveLength(15);
 
         // Simulate the backend returning no new messages to simulate reaching the start of the chat.
         mockGetNewerActions(0);
@@ -374,9 +374,9 @@ describe('Pagination', () => {
 
         TestHelper.expectAPICommandToHaveBeenCalled('OpenReport', 3);
         TestHelper.expectAPICommandToHaveBeenCalled('GetOlderActions', 0);
-        TestHelper.expectAPICommandToHaveBeenCalled('GetNewerActions', 1);
+        TestHelper.expectAPICommandToHaveBeenCalled('GetNewerActions', 3);
 
         // We still have 15 messages. 5 from the initial OpenReport and 5 from the GetNewerActions call.
-        expect(getReportActions()).toHaveLength(10);
+        expect(getReportActions()).toHaveLength(15);
     });
 });

From 863a88dc0c7fd5eba412697e00223b91a218efb9 Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Mon, 10 Mar 2025 12:09:42 +0100
Subject: [PATCH 29/29] Addressed comments

---
 src/CONST.ts                  |  2 ++
 src/components/LoadingBar.tsx | 13 ++++++++-----
 2 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/src/CONST.ts b/src/CONST.ts
index 3dfa6b4b078d..840743a27f65 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -1449,6 +1449,8 @@ const CONST = {
         WARM: 'warm',
         REPORT_ACTION_ITEM_LAYOUT_DEBOUNCE_TIME: 1500,
         SHOW_LOADING_SPINNER_DEBOUNCE_TIME: 250,
+        SKELETON_FADE_DURATION: 300,
+        SKELETON_SLIDE_DURATION: 1200,
         TEST_TOOLS_MODAL_THROTTLE_TIME: 800,
         TOOLTIP_SENSE: 1000,
         TRIE_INITIALIZATION: 'trie_initialization',
diff --git a/src/components/LoadingBar.tsx b/src/components/LoadingBar.tsx
index 932c8b1d7728..e13702517e10 100644
--- a/src/components/LoadingBar.tsx
+++ b/src/components/LoadingBar.tsx
@@ -3,6 +3,7 @@ import {View} from 'react-native';
 import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
 import useThemeStyles from '@hooks/useThemeStyles';
 import colors from '@styles/theme/colors';
+import CONST from '@src/CONST';
 
 type LoadingBarProps = {
     // Whether to show the loading bar
@@ -18,17 +19,17 @@ function LoadingBar({shouldShow}: LoadingBarProps) {
     useEffect(() => {
         if (shouldShow && !isAnimating.get()) {
             isAnimating.set(true);
-            opacity.set(withTiming(1, {duration: 300}));
+            opacity.set(withTiming(1, {duration: CONST.TIMING.SKELETON_FADE_DURATION}));
 
             left.set(
-                withTiming(100, {duration: 1200}, () => {
+                withTiming(100, {duration: CONST.TIMING.SKELETON_SLIDE_DURATION}, () => {
                     requestAnimationFrame(() => {
                         if (shouldShow) {
                             left.set(-30);
-                            left.set(withTiming(100, {duration: 1200}));
+                            left.set(withTiming(100, {duration: CONST.TIMING.SKELETON_SLIDE_DURATION}));
                         } else {
                             opacity.set(
-                                withTiming(0, {duration: 300}, () => {
+                                withTiming(0, {duration: CONST.TIMING.SKELETON_FADE_DURATION}, () => {
                                     isAnimating.set(false);
                                 }),
                             );
@@ -38,7 +39,7 @@ function LoadingBar({shouldShow}: LoadingBarProps) {
             );
         } else if (!shouldShow) {
             opacity.set(
-                withTiming(0, {duration: 300}, () => {
+                withTiming(0, {duration: CONST.TIMING.SKELETON_FADE_DURATION}, () => {
                     isAnimating.set(false);
                 }),
             );
@@ -62,4 +63,6 @@ function LoadingBar({shouldShow}: LoadingBarProps) {
     );
 }
 
+LoadingBar.displayName = 'LoadingBar';
+
 export default LoadingBar;
