diff --git a/patches/@shopify/flash-list/@shopify+flash-list+2.3.0+008+increase-timeout.patch b/patches/@shopify/flash-list/@shopify+flash-list+2.3.0+008+increase-timeout.patch new file mode 100644 index 000000000000..a76bba73cc2d --- /dev/null +++ b/patches/@shopify/flash-list/@shopify+flash-list+2.3.0+008+increase-timeout.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/@shopify/flash-list/dist/recyclerview/hooks/useRecyclerViewController.js b/node_modules/@shopify/flash-list/dist/recyclerview/hooks/useRecyclerViewController.js +index 51b6f8c..d4ca252 100644 +--- a/node_modules/@shopify/flash-list/dist/recyclerview/hooks/useRecyclerViewController.js ++++ b/node_modules/@shopify/flash-list/dist/recyclerview/hooks/useRecyclerViewController.js +@@ -507,7 +507,7 @@ export function useRecyclerViewController(recyclerViewManager, ref, scrollViewRe + setTimeout(() => { + recyclerViewManager.isInitialScrollComplete = true; + pauseOffsetCorrection.current = false; +- }, 100); ++ }, 500); + pauseOffsetCorrection.current = true; + const additionalOffset = (_c = initialScrollIndexParams === null || initialScrollIndexParams === void 0 ? void 0 : initialScrollIndexParams.viewOffset) !== null && _c !== void 0 ? _c : 0; + const offset = horizontal diff --git a/patches/@shopify/flash-list/details.md b/patches/@shopify/flash-list/details.md index e9a2002a9426..dd145f525594 100644 --- a/patches/@shopify/flash-list/details.md +++ b/patches/@shopify/flash-list/details.md @@ -56,3 +56,11 @@ - Upstream PR/issue: TBD - E/App issue: https://github.com/Expensify/App/issues/33725 - PR introducing patch: TBD + +### [@shopify+flash-list+2.3.0+008+increase-timeout.patch](@shopify+flash-list+2.3.0+008+increase-timeout.patch) + +- Reason: Fixes an initial-render scroll jump on iOS for inverted lists using `initialScrollIndex`. The existing 100 ms `pauseOffsetCorrection` window in `applyInitialScrollIndex` wasn't long enough — MVCP resumed before the corrective `scrollToOffset` had settled, exposing the jump. Bumped to 500 ms. +- Files changed: `dist/recyclerview/hooks/useRecyclerViewController.js` only. +- Upstream PR/issue: TBD +- E/App issue: https://github.com/Expensify/App/issues/89768 +- PR introducing patch: https://github.com/Expensify/App/pull/90218 diff --git a/src/hooks/useReportActionsPagination.ts b/src/hooks/useReportActionsPagination.ts index b58e75906e56..1cf2d919a15e 100644 --- a/src/hooks/useReportActionsPagination.ts +++ b/src/hooks/useReportActionsPagination.ts @@ -25,7 +25,6 @@ type UseReportActionsPaginationResult = { transactionThreadReportID: string | undefined; transactionThreadReport: OnyxEntry; parentReportActionForTransactionThread: ReportAction | undefined; - shouldAddCreatedAction: boolean; treatAsNoPaginationAnchor: boolean; setTreatAsNoPaginationAnchor: (value: boolean) => void; }; @@ -56,14 +55,10 @@ function useReportActionsPagination(reportID: string | undefined, reportActionID const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const isConciergeSidePanel = isInSidePanel && isConciergeChatReport(report, conciergeReportID); - const [reportLoadingState] = useOnyx(`${ONYXKEYS.COLLECTION.RAM_ONLY_REPORT_LOADING_STATE}${reportID}`); - const isLoadingInitialReportActions = reportLoadingState?.isLoadingInitialReportActions; - const isReportTransactionThread = isReportTransactionThreadUtil(report); - const isInitiallyLoadingTransactionThread = isReportTransactionThread && (!!isLoadingInitialReportActions || (allReportActions ?? [])?.length <= 1); const lastAction = allReportActions?.at(-1); - const shouldAddCreatedAction = !isCreatedAction(lastAction) && (isMoneyRequestReport(report) || isInvoiceReport(report) || isInitiallyLoadingTransactionThread || isConciergeSidePanel); + const shouldAddCreatedAction = !isCreatedAction(lastAction) && (isMoneyRequestReport(report) || isInvoiceReport(report) || isReportTransactionThread || isConciergeSidePanel); const reportPreviewAction = useMemo(() => getReportPreviewAction(report?.chatReportID, report?.reportID), [report?.chatReportID, report?.reportID]); @@ -96,7 +91,6 @@ function useReportActionsPagination(reportID: string | undefined, reportActionID transactionThreadReportID: thread.transactionThreadReportID, transactionThreadReport: thread.transactionThreadReport, parentReportActionForTransactionThread: thread.parentReportActionForTransactionThread, - shouldAddCreatedAction, treatAsNoPaginationAnchor, setTreatAsNoPaginationAnchor, }; diff --git a/src/pages/inbox/report/ReportActionsList.tsx b/src/pages/inbox/report/ReportActionsList.tsx index 093564a0b8cb..0a0f6198979e 100644 --- a/src/pages/inbox/report/ReportActionsList.tsx +++ b/src/pages/inbox/report/ReportActionsList.tsx @@ -20,10 +20,8 @@ import useReportIsArchived from '@hooks/useReportIsArchived'; import useReportScrollManager from '@hooks/useReportScrollManager'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useScrollToEndOnNewMessageReceived from '@hooks/useScrollToEndOnNewMessageReceived'; -import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import {openReport, readNewestAction} from '@libs/actions/Report'; import {isSafari} from '@libs/Browser'; import {isConsecutiveChronosAutomaticTimerAction} from '@libs/ChronosUtils'; import DateUtils from '@libs/DateUtils'; @@ -63,20 +61,17 @@ import Visibility from '@libs/Visibility'; import type {ReportsSplitNavigatorParamList} from '@navigation/types'; import {useConciergeDraft, useConciergeDraftActions} from '@pages/inbox/ConciergeDraftContext'; import {ActionListContext} from '@pages/inbox/ReportScreenContext'; -import variables from '@styles/variables'; +import {openReport, readNewestAction} from '@userActions/Report'; import CONST from '@src/CONST'; 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 FloatingMessageCounter from './FloatingMessageCounter'; -import getInitialNumToRender from './getInitialNumReportActionsToRender'; -import getReportActionsListInitialNumToRender from './getReportActionsListInitialNumToRender'; import ReportActionsListHeader from './ReportActionsListHeader'; import ReportActionsListItemRenderer from './ReportActionsListItemRenderer'; import {getUnreadMarkerReportAction} from './shouldDisplayNewMarkerOnReportAction'; import ShowPreviousMessagesButton from './ShowPreviousMessagesButton'; -import StaticReportActionsPreview from './StaticReportActionsPreview'; import useReportActionsNewActionLiveTail from './useReportActionsNewActionLiveTail'; import useReportUnreadMessageScrollTracking from './useReportUnreadMessageScrollTracking'; @@ -134,9 +129,6 @@ type ReportActionsListProps = { /** Stable key to remount the list when the deep-linked action or unread anchor (or report) changes */ listID: string; - /** Whether the optimistic CREATED report action was added */ - hasCreatedActionAdded?: boolean; - /** Whether the chat history is hidden (concierge side panel fresh state) */ showHiddenHistory?: boolean; @@ -157,6 +149,11 @@ let prevReportID: string | null = null; * random enough to avoid collisions */ function keyExtractor(item: OnyxTypes.ReportAction): string { + // A report has exactly one CREATED action. Using a stable key lets FlashList recycle the same cell + // when the optimistic CREATED is swapped for the server one, avoiding a remount-induced scroll jump. + if (item.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED) { + return CONST.REPORT.ACTIONS.TYPE.CREATED; + } return item.reportActionID; } @@ -179,16 +176,13 @@ function ReportActionsList({ isComposerFullSize, listID, parentReportActionForTransactionThread, - hasCreatedActionAdded, showHiddenHistory, hasPreviousMessages, onShowPreviousMessages, }: ReportActionsListProps) { - const prevHasCreatedActionAdded = usePrevious(hasCreatedActionAdded); const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); const personalDetailsList = usePersonalDetails(); const styles = useThemeStyles(); - const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); const {windowHeight} = useWindowDimensions(); const {shouldUseNarrowLayout} = useResponsiveLayout(); @@ -232,7 +226,6 @@ function ReportActionsList({ const hasHeaderRendered = useRef(false); const lastAction = sortedVisibleReportActions.at(0); - const [shouldMaintainVisibleContentPosition, setShouldMaintainVisibleContentPosition] = useState(() => scrollOffsetRef.current > CONST.REPORT.ACTIONS.ACTION_VISIBLE_THRESHOLD); const sortedVisibleReportActionsObjects: OnyxTypes.ReportActions = useMemo( () => sortedVisibleReportActions.reduce((actions, action) => { @@ -315,13 +308,23 @@ function ReportActionsList({ ]); prevUnreadMarkerReportActionID.current = unreadMarkerReportActionID; - const initialScrollKey = useMemo(() => { - return linkedReportActionID ?? unreadMarkerReportActionID ?? undefined; - }, [linkedReportActionID, unreadMarkerReportActionID]); - const isTransactionThreadReport = useMemo(() => isTransactionThread(parentReportAction) && !isSentMoneyReportAction(parentReportAction), [parentReportAction]); const isMoneyRequestOrInvoiceReport = useMemo(() => isMoneyRequestReport(report) || isInvoiceReport(report), [report]); - const shouldFocusToTopOnMount = useMemo(() => isTransactionThreadReport || isMoneyRequestOrInvoiceReport, [isMoneyRequestOrInvoiceReport, isTransactionThreadReport]); + const shouldBeAlignedToTop = useMemo(() => isTransactionThreadReport || isMoneyRequestOrInvoiceReport, [isMoneyRequestOrInvoiceReport, isTransactionThreadReport]); + const initialScrollKey = useMemo(() => { + const actionID = linkedReportActionID ?? unreadMarkerReportActionID; + if (!actionID) { + return; + } + + // The correct scroll behavior in this case will be handled by shouldFocusToTopOnMount logic + if (shouldBeAlignedToTop && sortedVisibleReportActionsObjects[actionID]?.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED) { + return; + } + return actionID; + }, [linkedReportActionID, unreadMarkerReportActionID, shouldBeAlignedToTop, sortedVisibleReportActionsObjects]); + const shouldFocusToTopOnMount = shouldBeAlignedToTop && !initialScrollKey; + const [shouldAutoscrollToBottom, setShouldAutoscrollToBottom] = useState(shouldFocusToTopOnMount); const renderedVisibleReportActions = useMemo(() => { if (!draftReportAction) { return sortedVisibleReportActions; @@ -347,9 +350,9 @@ function ReportActionsList({ const isSyntheticDraftVisible = !!draftReportAction && renderedVisibleReportActions !== sortedVisibleReportActions; const draftAutoScrollKey = isSyntheticDraftVisible ? `${draftReportAction.reportActionID}:${draftMessageHTML ?? ''}` : ''; const previousDraftAutoScrollKey = usePrevious(draftAutoScrollKey); - const topReportAction = renderedVisibleReportActions.at(-1); - const [shouldScrollToEndAfterLayout, setShouldScrollToEndAfterLayout] = useState(shouldFocusToTopOnMount && !initialScrollKey); - const scrollEndTimerRef = useRef | undefined>(undefined); + + const [hasScrolledOverThreshold, setHasScrolledOverThreshold] = useState(() => scrollOffsetRef.current > CONST.REPORT.ACTIONS.ACTION_VISIBLE_THRESHOLD); + const shouldMaintainVisibleContentPosition = hasScrolledOverThreshold || shouldFocusToTopOnMount; /** * The timestamp for the unread marker. @@ -440,16 +443,8 @@ function ReportActionsList({ onTrackScrolling: (event: NativeSyntheticEvent) => { const offset = event.nativeEvent.contentOffset.y; scrollOffsetRef.current = offset; - setShouldMaintainVisibleContentPosition(offset > CONST.REPORT.ACTIONS.ACTION_VISIBLE_THRESHOLD); + setHasScrolledOverThreshold(offset > CONST.REPORT.ACTIONS.ACTION_VISIBLE_THRESHOLD); onScroll?.(event); - // We use a timeout to wait for the scroll to finish before resetting the flag. - // onMomentumScrollEnd would be ideal but it doesn't work on web. - if (shouldScrollToEndAfterLayout && (!hasCreatedActionAdded || isOffline) && !scrollEndTimerRef.current) { - scrollEndTimerRef.current = setTimeout(() => { - setShouldScrollToEndAfterLayout(false); - scrollEndTimerRef.current = undefined; - }, CONST.TIMING.LIST_SCROLLING_DEBOUNCE_TIME); - } }, hasOnceLoadedReportActions: !!reportLoadingState?.hasOnceLoadedReportActions, }); @@ -475,8 +470,6 @@ function ReportActionsList({ reportLoadingState, }); - useEffect(() => () => clearTimeout(scrollEndTimerRef.current), []); - useScrollToEndOnNewMessageReceived({ sizeChangeType: 'changed', scrollOffsetRef, @@ -504,14 +497,6 @@ function ReportActionsList({ }); }, [draftAutoScrollKey, hasNewestReportAction, previousDraftAutoScrollKey, reportScrollManager, scrollOffsetRef, setIsFloatingMessageCounterVisible]); - useEffect(() => { - const shouldTriggerScroll = shouldFocusToTopOnMount && prevHasCreatedActionAdded && !hasCreatedActionAdded; - if (!shouldTriggerScroll) { - return; - } - requestAnimationFrame(() => reportScrollManager.scrollToEnd()); - }, [hasCreatedActionAdded, prevHasCreatedActionAdded, shouldFocusToTopOnMount, shouldScrollToEndAfterLayout, reportScrollManager]); - useEffect(() => { userActiveSince.current = DateUtils.getDBTime(); prevReportID = report.reportID; @@ -635,7 +620,7 @@ function ReportActionsList({ } InteractionManager.runAfterInteractions(() => { - if (shouldScrollToEndAfterLayout) { + if (shouldFocusToTopOnMount) { return; } setIsFloatingMessageCounterVisible(false); @@ -700,34 +685,6 @@ function ReportActionsList({ readNewestAction(report.reportID, !!reportLoadingState?.hasOnceLoadedReportActions); }, [setIsFloatingMessageCounterVisible, hasNewestReportAction, reportScrollManager, report.reportID, backTo, introSelected, reportLoadingState?.hasOnceLoadedReportActions, betas]); - /** - * Calculates the ideal number of report actions to render in the first render, based on the screen height and on - * the height of the smallest report action possible. - */ - const initialNumToRender = useMemo((): number => { - const minimumReportActionHeight = styles.chatItem.paddingTop + styles.chatItem.paddingBottom + variables.fontSizeNormalHeight; - const availableHeight = windowHeight - (CONST.CHAT_FOOTER_MIN_HEIGHT + variables.contentHeaderHeight); - const numToRender = Math.ceil(availableHeight / minimumReportActionHeight); - return getReportActionsListInitialNumToRender({ - numToRender, - initialScrollKey, - shouldScrollToEndAfterLayout, - hasCreatedActionAdded, - sortedVisibleReportActionsLength: renderedVisibleReportActions.length, - isOffline, - getInitialNumToRender, - }); - }, [ - styles.chatItem.paddingBottom, - styles.chatItem.paddingTop, - windowHeight, - initialScrollKey, - shouldScrollToEndAfterLayout, - hasCreatedActionAdded, - renderedVisibleReportActions.length, - isOffline, - ]); - /** * Thread's divider line should hide when the first chat in the thread is marked as unread. * This is so that it will not be conflicting with header's separator line. @@ -853,22 +810,8 @@ function ReportActionsList({ setIsScrollToBottomEnabled(false); completeLiveTailPruneAfterScrollToBottom(); } - if (shouldScrollToEndAfterLayout && (!hasCreatedActionAdded || isOffline)) { - requestAnimationFrame(() => { - reportScrollManager.scrollToEnd(); - }); - } }, - [ - isOffline, - isScrollToBottomEnabled, - onLayout, - reportScrollManager, - hasCreatedActionAdded, - shouldScrollToEndAfterLayout, - completeLiveTailPruneAfterScrollToBottom, - setIsScrollToBottomEnabled, - ], + [isScrollToBottomEnabled, onLayout, reportScrollManager, completeLiveTailPruneAfterScrollToBottom, setIsScrollToBottomEnabled], ); const retryLoadNewerChatsError = useCallback(() => { @@ -901,26 +844,6 @@ function ReportActionsList({ return ; }, [shouldShowSkeleton]); - const renderTopReportActions = useCallback(() => { - const previewItems = renderedVisibleReportActions.slice(initialNumToRender ? -initialNumToRender : 0).reverse(); - - return ( - <> - {!shouldShowReportRecipientLocalTime && !hideComposer && } - - {previewItems.map((action) => ( - - {renderItem({ - item: action, - index: actionIndexMap.get(action.reportActionID) ?? 0, - } as ListRenderItemInfo)} - - ))} - - - ); - }, [actionIndexMap, hideComposer, initialNumToRender, renderItem, shouldShowReportRecipientLocalTime, renderedVisibleReportActions, styles]); - const handleStartReached = useCallback(() => { if (!isSearchTopmostFullScreenRoute()) { loadNewerChats(false); @@ -934,6 +857,31 @@ function ReportActionsList({ loadOlderChats(false); }, [loadOlderChats]); + // Data is ready at the moment FlashList finishes its first render. + // Wait one frame so the initial autoscroll-to-top can settle, then disable it. + const onLoad = () => { + if (!shouldFocusToTopOnMount) { + return; + } + if (!reportLoadingState?.hasOnceLoadedReportActions && !isOffline) { + return; + } + requestAnimationFrame(() => setShouldAutoscrollToBottom(false)); + }; + const prevHasOnceLoadedReportActions = usePrevious(reportLoadingState?.hasOnceLoadedReportActions); + + // Data finished initial loading after the list mounted. onLoad has already fired, so we need + // a separate trigger to turn off autoscroll-to-top. + useEffect(() => { + if (!shouldFocusToTopOnMount || !shouldAutoscrollToBottom) { + return; + } + if (prevHasOnceLoadedReportActions || !reportLoadingState?.hasOnceLoadedReportActions) { + return; + } + requestAnimationFrame(() => setShouldAutoscrollToBottom(false)); + }, [shouldFocusToTopOnMount, shouldAutoscrollToBottom, prevHasOnceLoadedReportActions, reportLoadingState?.hasOnceLoadedReportActions]); + return ( <> - {shouldScrollToEndAfterLayout && topReportAction ? renderTopReportActions() : undefined} item.actionName} shouldMaintainVisibleContentPosition={shouldMaintainVisibleContentPosition} + initialScrollIndex={shouldFocusToTopOnMount ? renderedVisibleReportActions.length - 1 : undefined} + initialScrollIndexParams={shouldFocusToTopOnMount ? {viewOffset: windowHeight} : undefined} + maintainVisibleContentPosition={ + shouldAutoscrollToBottom ? {autoscrollToBottomThreshold: CONST.REPORT.ACTIONS.ACTION_VISIBLE_THRESHOLD, animateAutoScrollToBottom: false} : undefined + } + onLoad={onLoad} initialScrollKey={initialScrollKey} onContentSizeChange={() => { trackVerticalScrolling(undefined); diff --git a/src/pages/inbox/report/ReportActionsView.tsx b/src/pages/inbox/report/ReportActionsView.tsx index b4ef1d4f1c14..5c6a50a56f4d 100755 --- a/src/pages/inbox/report/ReportActionsView.tsx +++ b/src/pages/inbox/report/ReportActionsView.tsx @@ -61,7 +61,6 @@ function ReportActionsView({reportID, onLayout}: ReportActionsViewProps) { transactionThreadReportID, transactionThreadReport, parentReportActionForTransactionThread, - shouldAddCreatedAction, treatAsNoPaginationAnchor, setTreatAsNoPaginationAnchor, } = useReportActionsPagination(reportID, reportActionIDFromRoute); @@ -255,7 +254,6 @@ function ReportActionsView({reportID, onLayout}: ReportActionsViewProps) { treatAsNoPaginationAnchor={treatAsNoPaginationAnchor} setTreatAsNoPaginationAnchor={setTreatAsNoPaginationAnchor} listID={listID} - hasCreatedActionAdded={shouldAddCreatedAction} showHiddenHistory={!showFullHistory} hasPreviousMessages={hasPreviousMessages} onShowPreviousMessages={handleShowPreviousMessages} diff --git a/src/pages/inbox/report/StaticReportActionsPreview/index.native.tsx b/src/pages/inbox/report/StaticReportActionsPreview/index.native.tsx deleted file mode 100644 index f5421ae3e784..000000000000 --- a/src/pages/inbox/report/StaticReportActionsPreview/index.native.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; -import {View} from 'react-native'; -import ScrollView from '@components/ScrollView'; -import useThemeStyles from '@hooks/useThemeStyles'; -import type StaticReportActionsPreviewProps from './types'; - -function StaticReportActionsPreview({children}: StaticReportActionsPreviewProps) { - const styles = useThemeStyles(); - - return ( - - {children} - - ); -} - -export default StaticReportActionsPreview; diff --git a/src/pages/inbox/report/StaticReportActionsPreview/index.tsx b/src/pages/inbox/report/StaticReportActionsPreview/index.tsx deleted file mode 100644 index 0e7a10b928d0..000000000000 --- a/src/pages/inbox/report/StaticReportActionsPreview/index.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; -import ScrollView from '@components/ScrollView'; -import useThemeStyles from '@hooks/useThemeStyles'; -import type StaticReportActionsPreviewProps from './types'; - -function StaticReportActionsPreview({children}: StaticReportActionsPreviewProps) { - const styles = useThemeStyles(); - - return {children}; -} - -export default StaticReportActionsPreview; diff --git a/src/pages/inbox/report/StaticReportActionsPreview/types.ts b/src/pages/inbox/report/StaticReportActionsPreview/types.ts deleted file mode 100644 index 3623432d0837..000000000000 --- a/src/pages/inbox/report/StaticReportActionsPreview/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type {ReactNode} from 'react'; - -type StaticReportActionsPreviewProps = { - children: ReactNode; -}; - -export default StaticReportActionsPreviewProps; diff --git a/src/pages/inbox/report/getReportActionsListInitialNumToRender.ts b/src/pages/inbox/report/getReportActionsListInitialNumToRender.ts deleted file mode 100644 index af5901a359ee..000000000000 --- a/src/pages/inbox/report/getReportActionsListInitialNumToRender.ts +++ /dev/null @@ -1,29 +0,0 @@ -type GetReportActionsListInitialNumToRenderParams = { - numToRender: number; - initialScrollKey?: string; - shouldScrollToEndAfterLayout: boolean; - hasCreatedActionAdded?: boolean; - sortedVisibleReportActionsLength: number; - isOffline: boolean; - getInitialNumToRender: (numToRender: number) => number; -}; - -export default function getReportActionsListInitialNumToRender({ - numToRender, - initialScrollKey, - shouldScrollToEndAfterLayout, - hasCreatedActionAdded, - sortedVisibleReportActionsLength, - isOffline, - getInitialNumToRender, -}: GetReportActionsListInitialNumToRenderParams): number { - if (shouldScrollToEndAfterLayout && (!hasCreatedActionAdded || isOffline)) { - return sortedVisibleReportActionsLength; - } - - if (initialScrollKey) { - return getInitialNumToRender(numToRender); - } - - return numToRender; -} diff --git a/tests/unit/getReportActionsListInitialNumToRenderTest.ts b/tests/unit/getReportActionsListInitialNumToRenderTest.ts deleted file mode 100644 index 95be72e1c8eb..000000000000 --- a/tests/unit/getReportActionsListInitialNumToRenderTest.ts +++ /dev/null @@ -1,44 +0,0 @@ -import getInitialNumToRender from '@pages/inbox/report/getInitialNumReportActionsToRender'; -import getReportActionsListInitialNumToRender from '@pages/inbox/report/getReportActionsListInitialNumToRender'; - -describe('getReportActionsListInitialNumToRender', () => { - it('returns the full list length when scroll-to-end mode is enabled before the created action is added', () => { - const result = getReportActionsListInitialNumToRender({ - numToRender: 12, - shouldScrollToEndAfterLayout: true, - hasCreatedActionAdded: false, - sortedVisibleReportActionsLength: 500, - isOffline: false, - getInitialNumToRender, - }); - - expect(result).toBe(500); - }); - - it('returns the platform-adjusted value for linked report actions', () => { - const result = getReportActionsListInitialNumToRender({ - numToRender: 10, - initialScrollKey: '123', - shouldScrollToEndAfterLayout: false, - hasCreatedActionAdded: true, - sortedVisibleReportActionsLength: 500, - isOffline: false, - getInitialNumToRender, - }); - - expect(result).toBe(getInitialNumToRender(10)); - }); - - it('returns numToRender when there is no linked report action and the scroll-to-end short-circuit does not apply', () => { - const result = getReportActionsListInitialNumToRender({ - numToRender: 10, - shouldScrollToEndAfterLayout: false, - hasCreatedActionAdded: true, - sortedVisibleReportActionsLength: 3, - isOffline: false, - getInitialNumToRender, - }); - - expect(result).toBe(10); - }); -});