Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d913d8a
Loading state perception improvement
martasudol Feb 26, 2025
48c0a57
Merge branch 'fix-infinite-loader' into loading-state-perception-impr…
martasudol Feb 27, 2025
677c620
Loading state perception improvement - loading bar and loading older …
martasudol Feb 28, 2025
fcaead7
Loading state perception improvement: less blinking skeletons
martasudol Feb 28, 2025
6a81a03
remove redundant reportActions props
martasudol Feb 28, 2025
8870944
remove redundant imports
martasudol Feb 28, 2025
769362b
loading bar animation performance improvement
martasudol Feb 28, 2025
c86d880
Merge branch 'main' into loading-state-perception-improvement
martasudol Feb 28, 2025
44ef831
manage scrolling position only for web and desktop
martasudol Feb 28, 2025
a423100
fix linter errors
martasudol Feb 28, 2025
8f075e3
fix linter errors
martasudol Feb 28, 2025
f300945
Merge branch 'main' into loading-state-perception-improvement
martasudol Mar 4, 2025
2dcbe0c
Merge branch 'main' into loading-state-perception-improvement
martasudol Mar 4, 2025
3cc105b
Addressed PR comments
martasudol Mar 4, 2025
a0de6d5
Fix prettier error & default isLoadingOlderReportActions flag
martasudol Mar 4, 2025
4315668
fix linter errors
martasudol Mar 4, 2025
821bd06
show loading bar when fetching newer actions
martasudol Mar 4, 2025
9a61799
Merge branch 'main' into loading-state-perception-improvement
martasudol Mar 5, 2025
ce88678
Loading state perception improvement
martasudol Feb 26, 2025
c2b48f6
Loading state perception improvement - loading bar and loading older …
martasudol Feb 28, 2025
4ee4077
Loading state perception improvement: less blinking skeletons
martasudol Feb 28, 2025
433cea2
remove redundant reportActions props
martasudol Feb 28, 2025
fad5bdb
remove redundant imports
martasudol Feb 28, 2025
99bce83
loading bar animation performance improvement
martasudol Feb 28, 2025
21d7f28
manage scrolling position only for web and desktop
martasudol Feb 28, 2025
8f58783
fix linter errors
martasudol Feb 28, 2025
502cf8c
fix linter errors
martasudol Feb 28, 2025
ec54ce5
Addressed PR comments
martasudol Mar 4, 2025
17e18ac
Fix prettier error & default isLoadingOlderReportActions flag
martasudol Mar 4, 2025
d4a73a6
fix linter errors
martasudol Mar 4, 2025
d7c6e08
show loading bar when fetching newer actions
martasudol Mar 4, 2025
4bdfa9c
Merge branch 'main' into loading-state-perception-improvement
martasudol Mar 5, 2025
119f91c
Merge branch 'loading-state-perception-improvement' of github.com:cal…
martasudol Mar 5, 2025
2590b10
load newer actions fix
martasudol Mar 5, 2025
322dcd1
load newer actions fix
martasudol Mar 5, 2025
863a88d
Addressed comments
martasudol Mar 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
85 changes: 35 additions & 50 deletions src/components/LoadingBar.tsx
Original file line number Diff line number Diff line change
@@ -1,83 +1,68 @@
import React, {useEffect} from 'react';
import Animated, {cancelAnimation, Easing, useAnimatedStyle, useSharedValue, withDelay, withRepeat, withSequence, withTiming} from 'react-native-reanimated';
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 or not to show the loading bar
// Whether to show the loading bar
shouldShow: boolean;
};

function LoadingBar({shouldShow}: LoadingBarProps) {
const left = useSharedValue(0);
const width = useSharedValue(0);
const left = useSharedValue(-30);
const opacity = useSharedValue(0);
const isAnimating = useSharedValue(false);
const styles = useThemeStyles();

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}));
if (shouldShow && !isAnimating.get()) {
isAnimating.set(true);
opacity.set(withTiming(1, {duration: CONST.TIMING.SKELETON_FADE_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,
),
),
withTiming(100, {duration: CONST.TIMING.SKELETON_SLIDE_DURATION}, () => {
requestAnimationFrame(() => {
if (shouldShow) {
left.set(-30);
left.set(withTiming(100, {duration: CONST.TIMING.SKELETON_SLIDE_DURATION}));
} else {
opacity.set(
withTiming(0, {duration: CONST.TIMING.SKELETON_FADE_DURATION}, () => {
isAnimating.set(false);
}),
);
}
});
}),
);
} else {
} else if (!shouldShow) {
opacity.set(
withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION}, () => {
cancelAnimation(left);
cancelAnimation(width);
withTiming(0, {duration: CONST.TIMING.SKELETON_FADE_DURATION}, () => {
isAnimating.set(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(() => ({
const barStyle = useAnimatedStyle(() => ({
left: `${left.get()}%`,
width: `${width.get()}%`,
}));

const animatedContainerStyle = useAnimatedStyle(() => ({
width: '30%',
height: '100%',
backgroundColor: colors.green,
opacity: opacity.get(),
borderRadius: 2,
}));

return (
<Animated.View style={[styles.progressBarWrapper, animatedContainerStyle]}>
<Animated.View style={[styles.progressBar, animatedIndicatorStyle]} />
</Animated.View>
<View style={[styles.progressBarWrapper]}>
<Animated.View style={barStyle} />
Comment thread
martasudol marked this conversation as resolved.
</View>
);
}

LoadingBar.displayName = 'ProgressBar';
Comment thread
martasudol marked this conversation as resolved.
LoadingBar.displayName = 'LoadingBar';

export default LoadingBar;
3 changes: 0 additions & 3 deletions src/components/MoneyReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -520,7 +518,6 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
)}
</View>
)}
<LoadingBar shouldShow={(isLoadingReportData && shouldUseNarrowLayout) ?? false} />
Comment thread
martasudol marked this conversation as resolved.
{isHoldMenuVisible && requestType !== undefined && (
<ProcessMoneyReportHoldMenu
nonHeldAmount={!hasOnlyHeldExpenses && hasValidNonHeldAmount ? nonHeldAmount : undefined}
Expand Down
3 changes: 0 additions & 3 deletions src/components/MoneyRequestHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -214,7 +212,6 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
/>
</View>
)}
<LoadingBar shouldShow={(isLoadingReportData && shouldUseNarrowLayout) ?? false} />
Comment thread
martasudol marked this conversation as resolved.
</View>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ 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 {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 * as NumberUtils from '@libs/NumberUtils';
import {rand64} from '@libs/NumberUtils';
import ReportActionItem from '@pages/home/report/ReportActionItem';
import Debug from '@userActions/Debug';
import CONST from '@src/CONST';
Expand All @@ -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) ?? '',
Expand Down Expand Up @@ -78,7 +78,7 @@ function DebugReportActionCreatePage({
<ScreenWrapper
includeSafeAreaPaddingBottom={false}
shouldEnableKeyboardAvoidingView={false}
shouldEnableMinHeight={DeviceCapabilities.canUseTouchScreen()}
shouldEnableMinHeight={canUseTouchScreen()}
testID={DebugReportActionCreatePage.displayName}
>
{({safeAreaPaddingBottomStyle}) => (
Expand Down Expand Up @@ -107,7 +107,6 @@ function DebugReportActionCreatePage({
<ReportActionItem
action={JSON.parse(draftReportAction.replaceAll('\n', '')) as ReportAction}
report={{reportID}}
reportActions={[]}
Comment thread
martasudol marked this conversation as resolved.
parentReportAction={undefined}
displayAsGroup={false}
isMostRecentIOUReportAction={false}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ function DebugReportActionPreview({reportAction}: DebugReportActionPreviewProps)
<ReportActionItem
action={reportAction ?? ({} as ReportAction)}
report={report ?? ({} as Report)}
reportActions={[]}
Comment thread
martasudol marked this conversation as resolved.
parentReportAction={undefined}
displayAsGroup={false}
isMostRecentIOUReportAction={false}
Expand Down
8 changes: 4 additions & 4 deletions src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ 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 CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Transaction} from '@src/types/onyx';

Expand All @@ -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 = ReportActionsUtils.isMoneyRequestAction(reportAction) ? ReportActionsUtils.getOriginalMessage(reportAction)?.IOUTransactionID : -1;
const IOUTransactionID = isMoneyRequestAction(reportAction) ? getOriginalMessage(reportAction)?.IOUTransactionID : CONST.DEFAULT_NUMBER_ID;
return IOUTransactionID === props.transaction?.transactionID;
});

Expand All @@ -33,9 +34,8 @@ 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}
reportActions={Object.values(reportActions ?? {})}
displayAsGroup={false}
shouldDisplayNewMarker={false}
isMostRecentIOUReportAction={false}
Expand Down
3 changes: 0 additions & 3 deletions src/pages/home/HeaderView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -107,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);
Expand Down Expand Up @@ -393,7 +391,6 @@ function HeaderView({report, parentReportAction, onNavigationMenuButtonClicked,
</View>
</View>
)}
<LoadingBar shouldShow={(isLoadingReportData && shouldUseNarrowLayout) ?? false} />
</View>
{shouldShowEarlyDiscountBanner && (
<EarlyDiscountBanner
Expand Down
2 changes: 1 addition & 1 deletion src/pages/home/ReportScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -731,7 +731,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
<ReportActionsView
report={report}
reportActions={reportActions}
isLoadingInitialReportActions={reportMetadata?.isLoadingInitialReportActions}
reportMetadata={reportMetadata}
hasNewerActions={hasNewerActions}
hasOlderActions={hasOlderActions}
parentReportAction={parentReportAction}
Expand Down
5 changes: 0 additions & 5 deletions src/pages/home/report/PureReportActionItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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>;

Expand Down Expand Up @@ -1297,7 +1293,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 &&
Expand Down
5 changes: 0 additions & 5 deletions src/pages/home/report/ReportActionItemParentAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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>;

Expand All @@ -55,7 +52,6 @@ type ReportActionItemParentActionProps = {
function ReportActionItemParentAction({
report,
transactionThreadReport,
reportActions,
parentReportAction,
index = 0,
shouldHideThreadDividerLine = false,
Expand Down Expand Up @@ -144,7 +140,6 @@ function ReportActionItemParentAction({
}
parentReportAction={parentReportAction}
report={ancestor.report}
reportActions={reportActions}
transactionThreadReport={transactionThreadReport}
action={ancestor.reportAction}
displayAsGroup={false}
Expand Down
13 changes: 11 additions & 2 deletions src/pages/home/report/ReportActionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand Down Expand Up @@ -219,6 +220,7 @@ function ReportActionsList({
}, [report.reportID]);

const prevUnreadMarkerReportActionID = useRef<string | null>(null);

/**
* Whether a message is NOT from the active user and it was received while the user was offline.
*/
Expand Down Expand Up @@ -512,6 +514,7 @@ function ReportActionsList({
};

const trackVerticalScrolling = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
setScrollOffset(event.nativeEvent.contentOffset.y);
scrollingVerticalOffset.current = event.nativeEvent.contentOffset.y;
handleUnreadFloatingButton();
onScroll?.(event);
Expand Down Expand Up @@ -618,7 +621,6 @@ function ReportActionsList({
({item: reportAction, index}: ListRenderItemInfo<OnyxTypes.ReportAction>) => (
<ReportActionsListItemRenderer
reportAction={reportAction}
reportActions={sortedReportActions}
parentReportAction={parentReportAction}
parentReportActionForTransactionThread={parentReportActionForTransactionThread}
index={index}
Expand All @@ -644,7 +646,6 @@ function ReportActionsList({
mostRecentIOUReportActionID,
shouldHideThreadDividerLine,
parentReportAction,
sortedReportActions,
transactionThreadReport,
parentReportActionForTransactionThread,
shouldUseThreadDividerLine,
Expand Down Expand Up @@ -709,6 +710,14 @@ function ReportActionsList({

const onEndReached = useCallback(() => {
loadOlderChats(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]);

// Parse Fullstory attributes on initial render
Expand Down
Loading