fix(mobile): do not mark as read when scrolling up (#2896)

This commit is contained in:
Stephen Zhou 2025-02-26 22:48:37 +08:00 committed by GitHub
parent f56ede452c
commit 7d092cdd2d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 50 additions and 33 deletions

View File

@ -31,7 +31,9 @@ export const EntryListContentArticle = forwardRef<
[isFetching],
)
const onViewableItemsChanged = useOnViewableItemsChanged({ isLoading: isRefetching })
const { onViewableItemsChanged, onScroll } = useOnViewableItemsChanged({
disabled: isRefetching,
})
return (
<TimelineSelectorList
@ -44,6 +46,7 @@ export const EntryListContentArticle = forwardRef<
estimatedItemSize={100}
renderItem={renderItem}
onEndReached={fetchNextPage}
onScroll={onScroll}
onViewableItemsChanged={onViewableItemsChanged}
ItemSeparatorComponent={ItemSeparator}
ListFooterComponent={ListFooterComponent}

View File

@ -18,7 +18,9 @@ export const EntryListContentGrid = forwardRef<
} & Omit<MasonryFlashListProps<string>, "data" | "renderItem">
>(({ entryIds, ...rest }, ref) => {
const { fetchNextPage, refetch, isRefetching, hasNextPage } = useFetchEntriesControls()
const onViewableItemsChanged = useOnViewableItemsChanged({ isLoading: isRefetching })
const { onViewableItemsChanged, onScroll } = useOnViewableItemsChanged({
disabled: isRefetching,
})
return (
<TimelineSelectorMasonryList
@ -30,9 +32,8 @@ export const EntryListContentGrid = forwardRef<
}, [])}
keyExtractor={defaultKeyExtractor}
onViewableItemsChanged={onViewableItemsChanged}
onEndReached={() => {
fetchNextPage()
}}
onScroll={onScroll}
onEndReached={fetchNextPage}
numColumns={2}
estimatedItemSize={100}
ListFooterComponent={

View File

@ -25,7 +25,9 @@ export const EntryListContentSocial = forwardRef<
[isFetching],
)
const onViewableItemsChanged = useOnViewableItemsChanged({ isLoading: isRefetching })
const { onViewableItemsChanged, onScroll } = useOnViewableItemsChanged({
disabled: isRefetching,
})
return (
<TimelineSelectorList
@ -38,10 +40,9 @@ export const EntryListContentSocial = forwardRef<
keyExtractor={(id) => id}
estimatedItemSize={100}
renderItem={renderItem}
onEndReached={() => {
fetchNextPage()
}}
onEndReached={fetchNextPage}
onViewableItemsChanged={onViewableItemsChanged}
onScroll={onScroll}
ItemSeparatorComponent={ItemSeparatorFullWidth}
ListFooterComponent={ListFooterComponent}
/>

View File

@ -1,5 +1,6 @@
import type ViewToken from "@shopify/flash-list/dist/viewability/ViewToken"
import { useCallback, useState } from "react"
import { useMemo, useRef, useState } from "react"
import type { NativeScrollEvent, NativeSyntheticEvent } from "react-native"
import { useGeneralSettingKey } from "@/src/atoms/settings/general"
import { debouncedFetchEntryContentByStream } from "@/src/store/entry/store"
@ -8,37 +9,48 @@ import { unreadSyncService } from "@/src/store/unread/store"
const defaultIdExtractor = (item: ViewToken) => item.key
export function useOnViewableItemsChanged({
idExtractor = defaultIdExtractor,
isLoading,
disabled,
}: {
isLoading?: boolean
disabled?: boolean
idExtractor?: (item: ViewToken) => string
} = {}): (info: { viewableItems: ViewToken[]; changed: ViewToken[] }) => void {
} = {}): {
onViewableItemsChanged: (info: { viewableItems: ViewToken[]; changed: ViewToken[] }) => void
onScroll?: (e: NativeSyntheticEvent<NativeScrollEvent>) => void
} {
const orientation = useRef<"down" | "up">("down")
const lastOffset = useRef(0)
const markAsReadWhenScrolling = useGeneralSettingKey("scrollMarkUnread")
const markAsReadWhenRendering = useGeneralSettingKey("renderMarkUnread")
const [stableIdExtractor] = useState(() => idExtractor)
return useCallback(
({ viewableItems, changed }) => {
if (isLoading) return
return useMemo(() => {
return {
onViewableItemsChanged: ({ viewableItems, changed }) => {
if (disabled) return
debouncedFetchEntryContentByStream(viewableItems.map((item) => stableIdExtractor(item)))
if (markAsReadWhenScrolling) {
changed
.filter((item) => !item.isViewable)
.forEach((item) => {
debouncedFetchEntryContentByStream(viewableItems.map((item) => stableIdExtractor(item)))
if (markAsReadWhenScrolling && orientation.current === "down") {
changed
.filter((item) => !item.isViewable)
.forEach((item) => {
unreadSyncService.markEntryAsRead(stableIdExtractor(item))
})
}
if (markAsReadWhenRendering && orientation.current === "down") {
viewableItems.forEach((item) => {
unreadSyncService.markEntryAsRead(stableIdExtractor(item))
})
}
if (markAsReadWhenRendering) {
viewableItems
.filter((item) => item.isViewable)
.forEach((item) => {
unreadSyncService.markEntryAsRead(stableIdExtractor(item))
})
}
},
[markAsReadWhenRendering, markAsReadWhenScrolling, stableIdExtractor, isLoading],
)
}
},
onScroll: (e: NativeSyntheticEvent<NativeScrollEvent>) => {
const currentOffset = e.nativeEvent.contentOffset.y
const currentOrientation = currentOffset > lastOffset.current ? "down" : "up"
orientation.current = currentOrientation
lastOffset.current = currentOffset
},
}
}, [disabled, markAsReadWhenRendering, markAsReadWhenScrolling, orientation, stableIdExtractor])
}