Skip to content

Commit 4cb9bfc

Browse files
committed
Add shouldMaintainVisibleContentPosition logic
1 parent ec523a1 commit 4cb9bfc

3 files changed

Lines changed: 30 additions & 5 deletions

File tree

src/components/FlashList/InvertedFlashList/index.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,18 @@ type InvertedFlashListProps<T> = FlashListProps<T> & {
1717

1818
/** Ref to the underlying list instance. */
1919
ref: FlatListRefType;
20+
21+
/** Whether the list should handle `maintainVisibleContentPosition` */
22+
shouldMaintainVisibleContentPosition?: boolean;
2023
};
2124

22-
function InvertedFlashList<T>({data, keyExtractor, initialScrollKey, onStartReached: onStartReachedProp, ...restProps}: InvertedFlashListProps<T>) {
25+
function InvertedFlashList<T>({data, keyExtractor, initialScrollKey, onStartReached: onStartReachedProp, shouldMaintainVisibleContentPosition, ...restProps}: InvertedFlashListProps<T>) {
2326
const {displayedData, onStartReached, maintainVisibleContentPosition} = useFlashListScrollKey<T>({
2427
data,
2528
keyExtractor,
2629
initialScrollKey,
2730
onStartReached: onStartReachedProp,
31+
shouldMaintainVisibleContentPosition,
2832
});
2933

3034
return (

src/components/FlashList/useFlashListScrollKey.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import type {FlashListProps} from '@shopify/flash-list';
22
import {useEffect, useState} from 'react';
33

4+
// How long to keep MVCP enabled after `shouldMaintainVisibleContentPosition` drops back to false
5+
const MVCP_FALLOFF_MS = 500;
6+
47
type FlashListScrollKeyProps<T> = {
58
/** The array of items to render in the list. */
69
data: T[];
@@ -13,9 +16,12 @@ type FlashListScrollKeyProps<T> = {
1316

1417
/** Callback invoked when the user scrolls close to the start of the list. */
1518
onStartReached: FlashListProps<T>['onStartReached'];
19+
20+
/** Whether the list should handle `maintainVisibleContentPosition` */
21+
shouldMaintainVisibleContentPosition?: boolean;
1622
};
1723

18-
export default function useFlashListScrollKey<T>({data, keyExtractor, initialScrollKey, onStartReached}: FlashListScrollKeyProps<T>) {
24+
export default function useFlashListScrollKey<T>({data, keyExtractor, initialScrollKey, onStartReached, shouldMaintainVisibleContentPosition}: FlashListScrollKeyProps<T>) {
1925
const [isInitialRender, setIsInitialRender] = useState(true);
2026
const [hasLinkingSettled, setHasLinkingSettled] = useState(!initialScrollKey);
2127

@@ -33,9 +39,20 @@ export default function useFlashListScrollKey<T>({data, keyExtractor, initialScr
3339
});
3440
}, [isInitialRender, initialScrollKey]);
3541

36-
// `undefined` = leave FlashList's default (MVCP enabled) while we're still pinning the linked item.
37-
// `{disabled: true}` once that's done so MVCP can't interfere afterward.
38-
const maintainVisibleContentPosition: FlashListProps<T>['maintainVisibleContentPosition'] = hasLinkingSettled ? {disabled: true} : undefined;
42+
// FlashList captures MVCP anchors on the render BEFORE a data change. Keep MVCP on at mount
43+
// (warmup) and while the caller raises the prop, then disable after a short falloff.
44+
const [isKeepingMVCPOn, setIsKeepingMVCPOn] = useState(true);
45+
useEffect(() => {
46+
if (shouldMaintainVisibleContentPosition) {
47+
setIsKeepingMVCPOn(true);
48+
return;
49+
}
50+
const timeoutID = setTimeout(() => setIsKeepingMVCPOn(false), MVCP_FALLOFF_MS);
51+
return () => clearTimeout(timeoutID);
52+
}, [shouldMaintainVisibleContentPosition]);
53+
54+
const shouldEnableMVCP = !!shouldMaintainVisibleContentPosition || isKeepingMVCPOn || !hasLinkingSettled;
55+
const maintainVisibleContentPosition: FlashListProps<T>['maintainVisibleContentPosition'] = {disabled: !shouldEnableMVCP};
3956

4057
if (!isInitialRender || !initialScrollKey) {
4158
return {displayedData: data, onStartReached, maintainVisibleContentPosition};

src/pages/inbox/report/ReportActionsList.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,9 @@ function ReportActionsList({
233233
const hasHeaderRendered = useRef(false);
234234

235235
const lastAction = sortedVisibleReportActions.at(0);
236+
const isLastActionFromCurrentUser = (isReportPreviewAction(lastAction) ? lastAction?.childLastActorAccountID : lastAction?.actorAccountID) === currentUserAccountID;
237+
const prevLastActionID = usePrevious(lastAction?.reportActionID);
238+
const shouldMaintainVisibleContentPosition = prevLastActionID !== undefined && prevLastActionID !== lastAction?.reportActionID && !isLastActionFromCurrentUser;
236239
const sortedVisibleReportActionsObjects: OnyxTypes.ReportActions = useMemo(
237240
() =>
238241
sortedVisibleReportActions.reduce((actions, action) => {
@@ -900,6 +903,7 @@ function ReportActionsList({
900903
extraData={extraData}
901904
key={listID}
902905
getItemType={(item) => item.actionName}
906+
shouldMaintainVisibleContentPosition={shouldMaintainVisibleContentPosition}
903907
initialScrollKey={linkedReportActionID}
904908
onContentSizeChange={() => {
905909
trackVerticalScrolling(undefined);

0 commit comments

Comments
 (0)