From 4e8cf339416183e1bc32301e7e5ab114b8116880 Mon Sep 17 00:00:00 2001 From: DIYgod Date: Fri, 5 Jun 2026 10:26:09 +0800 Subject: [PATCH] fix(mobile): preserve social timeline scroll reset --- .../entry-list/EntryListContentSocial.tsx | 12 +++++++-- .../src/modules/screen/scroll-reset.test.ts | 26 ++++++++++++++++++- .../mobile/src/modules/screen/scroll-reset.ts | 12 +++++++++ 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/apps/mobile/src/modules/entry-list/EntryListContentSocial.tsx b/apps/mobile/src/modules/entry-list/EntryListContentSocial.tsx index c3617e879..2c4080ec1 100644 --- a/apps/mobile/src/modules/entry-list/EntryListContentSocial.tsx +++ b/apps/mobile/src/modules/entry-list/EntryListContentSocial.tsx @@ -11,6 +11,7 @@ import { View } from "react-native" import { useActionLanguage, useGeneralSettingKey } from "@/src/atoms/settings/general" import { useEntries } from "../screen/atoms" +import { getResetScrollSignalForContent } from "../screen/scroll-reset" import { TimelineSelectorList } from "../screen/TimelineSelectorList" import { EntryListEndScrollSpacer } from "./EntryListEndScrollSpacer" import { EntryListFooter } from "./EntryListFooter" @@ -92,6 +93,13 @@ export const EntryListContentSocial = ({ mode: translationMode, }) + const contentResetScrollSignal = getResetScrollSignalForContent({ + entryCount: entryIds?.length ?? 0, + hasScrollableSkeleton: true, + isReady, + resetScrollSignal, + }) + // Show loading skeleton when entries are not ready and no data yet if (!isReady && (!entryIds || entryIds.length === 0)) { return ( @@ -99,7 +107,7 @@ export const EntryListContentSocial = ({ onRefresh={() => {}} isRefetching={false} onResetScrollSignalConsumed={onResetScrollSignalConsumed} - resetScrollSignal={resetScrollSignal} + resetScrollSignal={contentResetScrollSignal} data={Array.from({ length: 5 }).map((_, index) => `skeleton-${index}`)} keyExtractor={(id) => id} renderItem={EntryItemSkeleton} @@ -116,7 +124,7 @@ export const EntryListContentSocial = ({ }} isRefetching={isRefetching} onResetScrollSignalConsumed={onResetScrollSignalConsumed} - resetScrollSignal={resetScrollSignal} + resetScrollSignal={contentResetScrollSignal} data={entryIds} extraData={extraData} keyExtractor={(id) => id} diff --git a/apps/mobile/src/modules/screen/scroll-reset.test.ts b/apps/mobile/src/modules/screen/scroll-reset.test.ts index 164fef019..2f58ca8bc 100644 --- a/apps/mobile/src/modules/screen/scroll-reset.test.ts +++ b/apps/mobile/src/modules/screen/scroll-reset.test.ts @@ -1,6 +1,10 @@ import { describe, expect, test } from "vitest" -import { shouldApplyScrollResetSignal, shouldSuspendMarkReadForScrollReset } from "./scroll-reset" +import { + getResetScrollSignalForContent, + shouldApplyScrollResetSignal, + shouldSuspendMarkReadForScrollReset, +} from "./scroll-reset" describe("shouldApplyScrollResetSignal", () => { test("applies a new reset signal that has not been flushed yet", () => { @@ -34,4 +38,24 @@ describe("shouldApplyScrollResetSignal", () => { }), ).toBe(false) }) + + test("does not forward reset signal to scrollable loading skeletons", () => { + expect( + getResetScrollSignalForContent({ + entryCount: 0, + hasScrollableSkeleton: true, + isReady: false, + resetScrollSignal: 1, + }), + ).toBeUndefined() + + expect( + getResetScrollSignalForContent({ + entryCount: 1, + hasScrollableSkeleton: true, + isReady: true, + resetScrollSignal: 1, + }), + ).toBe(1) + }) }) diff --git a/apps/mobile/src/modules/screen/scroll-reset.ts b/apps/mobile/src/modules/screen/scroll-reset.ts index 92a6c32f1..4234e29eb 100644 --- a/apps/mobile/src/modules/screen/scroll-reset.ts +++ b/apps/mobile/src/modules/screen/scroll-reset.ts @@ -9,3 +9,15 @@ export const shouldApplyScrollResetSignal = ({ }: ScrollResetSignalState) => resetSignal !== undefined && resetSignal !== appliedResetSignal export const shouldSuspendMarkReadForScrollReset = shouldApplyScrollResetSignal + +export const getResetScrollSignalForContent = ({ + entryCount, + hasScrollableSkeleton, + isReady, + resetScrollSignal, +}: { + entryCount: number + hasScrollableSkeleton: boolean + isReady: boolean + resetScrollSignal?: number +}) => (!isReady && entryCount === 0 && hasScrollableSkeleton ? undefined : resetScrollSignal)