diff --git a/src/pages/inbox/report/ReportActionsList.tsx b/src/pages/inbox/report/ReportActionsList.tsx index b085862bb4dc..e1146363055a 100644 --- a/src/pages/inbox/report/ReportActionsList.tsx +++ b/src/pages/inbox/report/ReportActionsList.tsx @@ -326,7 +326,6 @@ function ReportActionsListContent({reportID, onLayout}: ReportActionsListContent reportID, actionTargetReportActionID: reportAttributes?.actionTargetReportActionID, actionBadgeTargetIndex, - actionBadge: reportAttributes?.actionBadge, renderedVisibleReportActions, scrollToActionBadgeTarget, }); diff --git a/src/pages/inbox/report/getActionBadgeScrollDelay.ts b/src/pages/inbox/report/getActionBadgeScrollDelay.ts deleted file mode 100644 index 10155aaeb1b4..000000000000 --- a/src/pages/inbox/report/getActionBadgeScrollDelay.ts +++ /dev/null @@ -1,27 +0,0 @@ -import CONST from '@src/CONST'; - -import type {ValueOf} from 'type-fest'; - -/** - * How long to wait before auto-scrolling to the next action-badge target after the current one is resolved. - * - * Submit/approve/pay badges play a success animation on the resolved preview, so we wait for it to finish before scrolling there, - * otherwise the list moves mid-animation. The submit button animates on a much longer timeline than pay/approve (a loading state, - * then a visible submitted state, then a height collapse), so it needs a longer delay than the paid/thumbs-up animation. - * - * Returns the delay in milliseconds for animated badges, or `null` for badges that don't animate (e.g. task/fix) so the caller can - * scroll on the next frame instead of forcing an unnecessary delay. - */ -function getActionBadgeScrollDelay(actionBadge: ValueOf | undefined): number | null { - switch (actionBadge) { - case CONST.REPORT.ACTION_BADGE.SUBMIT: - return CONST.ANIMATION_SUBMIT_LOADING_STATE_DURATION + CONST.ANIMATION_SUBMIT_SUBMITTED_STATE_VISIBLE_DURATION + CONST.ANIMATION_SUBMIT_DURATION; - case CONST.REPORT.ACTION_BADGE.APPROVE: - case CONST.REPORT.ACTION_BADGE.PAY: - return CONST.ANIMATION_PAID_BUTTON_HIDE_DELAY + CONST.ANIMATION_THUMBS_UP_DURATION * 2; - default: - return null; - } -} - -export default getActionBadgeScrollDelay; diff --git a/src/pages/inbox/report/useFollowActionBadgeTarget.ts b/src/pages/inbox/report/useFollowActionBadgeTarget.ts index 8c6275b91721..b15df8c3a2ff 100644 --- a/src/pages/inbox/report/useFollowActionBadgeTarget.ts +++ b/src/pages/inbox/report/useFollowActionBadgeTarget.ts @@ -2,14 +2,10 @@ import usePrevious from '@hooks/usePrevious'; import Navigation from '@libs/Navigation/Navigation'; -import type CONST from '@src/CONST'; import type * as OnyxTypes from '@src/types/onyx'; -import type {ValueOf} from 'type-fest'; - import {useEffect, useRef} from 'react'; -import getActionBadgeScrollDelay from './getActionBadgeScrollDelay'; import shouldFollowActionBadgeTarget from './shouldFollowActionBadgeTarget'; type UseFollowActionBadgeTargetParams = { @@ -25,9 +21,6 @@ type UseFollowActionBadgeTargetParams = { /** Index of the current target in the rendered (inverted) list, or -1 when it is not rendered */ actionBadgeTargetIndex: number; - /** The kind of action badge currently shown, used to decide how long to wait for its resolve animation */ - actionBadge: ValueOf | undefined; - /** The rendered (inverted) report actions the list is displaying */ renderedVisibleReportActions: OnyxTypes.ReportAction[]; @@ -36,24 +29,20 @@ type UseFollowActionBadgeTargetParams = { }; /** - * Once the current action-badge target is resolved (e.g. the user approves/pays an older report preview), the badge target - * advances to the next report preview that requires action. This hook follows it by scrolling down to the new target, waiting for - * the resolve animation to finish first when the resolved badge was an animated (submit/approve/pay) button. + * When the action-badge target is resolved (e.g. the user approves/pays/submits an older report preview), it advances to the next + * preview requiring action. This hook scrolls down to follow it immediately on action. */ function useFollowActionBadgeTarget({ isProduction, reportID, actionTargetReportActionID, actionBadgeTargetIndex, - actionBadge, renderedVisibleReportActions, scrollToActionBadgeTarget, }: UseFollowActionBadgeTargetParams) { const prevActionTargetReportActionID = usePrevious(actionTargetReportActionID); - const prevActionBadge = usePrevious(actionBadge); - // Keep the latest scroll callback in a ref so a scroll scheduled after an animation still targets the current badge index. - // The effect below only re-runs when the target id changes, so without this the delayed callback would close over a stale - // target index if the list shifts (new message, pagination, resolved preview collapsing) during the wait. + // Keep the latest scroll callback in a ref so a scroll scheduled on the next frame targets the current badge index rather than a + // stale one, in case the list shifts (new message, pagination, resolved preview collapsing) before the frame runs. const scrollToActionBadgeTargetRef = useRef(scrollToActionBadgeTarget); useEffect(() => { scrollToActionBadgeTargetRef.current = scrollToActionBadgeTarget; @@ -70,16 +59,10 @@ function useFollowActionBadgeTarget({ if (Navigation.getTopmostReportId() !== reportID || !!Navigation.getReportRHPActiveRoute()) { return; } - // Animated (submit/approve/pay) badges play a success animation on the resolved preview, so wait for it to finish before - // scrolling there so the list doesn't move mid-animation. Non-animated badges (e.g. task) return a null delay, so scroll on - // the next frame instead of forcing an unnecessary wait. - const scrollDelay = getActionBadgeScrollDelay(prevActionBadge); - if (scrollDelay === null) { - const animationFrameID = requestAnimationFrame(() => scrollToActionBadgeTargetRef.current()); - return () => cancelAnimationFrame(animationFrameID); - } - const scrollTimeoutID = setTimeout(() => scrollToActionBadgeTargetRef.current(), scrollDelay); - return () => clearTimeout(scrollTimeoutID); + // Scroll to the next target on the next frame so the forward-scroll starts as soon as the user acts. The resolved preview + // keeps animating in place while the list scrolls. + const animationFrameID = requestAnimationFrame(() => scrollToActionBadgeTargetRef.current()); + return () => cancelAnimationFrame(animationFrameID); // eslint-disable-next-line react-hooks/exhaustive-deps }, [actionTargetReportActionID]); } diff --git a/tests/unit/getActionBadgeScrollDelayTest.ts b/tests/unit/getActionBadgeScrollDelayTest.ts deleted file mode 100644 index 7ef56e9ecd1b..000000000000 --- a/tests/unit/getActionBadgeScrollDelayTest.ts +++ /dev/null @@ -1,33 +0,0 @@ -import getActionBadgeScrollDelay from '@pages/inbox/report/getActionBadgeScrollDelay'; - -import CONST from '@src/CONST'; - -const SUBMIT_DELAY = CONST.ANIMATION_SUBMIT_LOADING_STATE_DURATION + CONST.ANIMATION_SUBMIT_SUBMITTED_STATE_VISIBLE_DURATION + CONST.ANIMATION_SUBMIT_DURATION; -const PAID_DELAY = CONST.ANIMATION_PAID_BUTTON_HIDE_DELAY + CONST.ANIMATION_THUMBS_UP_DURATION * 2; - -describe('getActionBadgeScrollDelay', () => { - it('waits out the full submit animation timeline for a submit badge', () => { - expect(getActionBadgeScrollDelay(CONST.REPORT.ACTION_BADGE.SUBMIT)).toBe(SUBMIT_DELAY); - }); - - it('uses the paid/thumbs-up delay for an approve badge', () => { - expect(getActionBadgeScrollDelay(CONST.REPORT.ACTION_BADGE.APPROVE)).toBe(PAID_DELAY); - }); - - it('uses the paid/thumbs-up delay for a pay badge', () => { - expect(getActionBadgeScrollDelay(CONST.REPORT.ACTION_BADGE.PAY)).toBe(PAID_DELAY); - }); - - it('uses a longer delay for submit than for approve/pay', () => { - expect(SUBMIT_DELAY).toBeGreaterThan(PAID_DELAY); - }); - - it('returns null for non-animated badges (task/fix)', () => { - expect(getActionBadgeScrollDelay(CONST.REPORT.ACTION_BADGE.TASK)).toBeNull(); - expect(getActionBadgeScrollDelay(CONST.REPORT.ACTION_BADGE.FIX)).toBeNull(); - }); - - it('returns null when there is no badge', () => { - expect(getActionBadgeScrollDelay(undefined)).toBeNull(); - }); -});