diff --git a/apps/mobile/src/modules/entry-list/EntryListContentArticle.tsx b/apps/mobile/src/modules/entry-list/EntryListContentArticle.tsx index c681bb2f0..7525706fd 100644 --- a/apps/mobile/src/modules/entry-list/EntryListContentArticle.tsx +++ b/apps/mobile/src/modules/entry-list/EntryListContentArticle.tsx @@ -31,7 +31,9 @@ export const EntryListContentArticle = forwardRef< [isFetching], ) - const onViewableItemsChanged = useOnViewableItemsChanged({ isLoading: isRefetching }) + const { onViewableItemsChanged, onScroll } = useOnViewableItemsChanged({ + disabled: isRefetching, + }) return ( , "data" | "renderItem"> >(({ entryIds, ...rest }, ref) => { const { fetchNextPage, refetch, isRefetching, hasNextPage } = useFetchEntriesControls() - const onViewableItemsChanged = useOnViewableItemsChanged({ isLoading: isRefetching }) + const { onViewableItemsChanged, onScroll } = useOnViewableItemsChanged({ + disabled: isRefetching, + }) return ( { - fetchNextPage() - }} + onScroll={onScroll} + onEndReached={fetchNextPage} numColumns={2} estimatedItemSize={100} ListFooterComponent={ diff --git a/apps/mobile/src/modules/entry-list/EntryListContentSocial.tsx b/apps/mobile/src/modules/entry-list/EntryListContentSocial.tsx index 1c2a533cb..58a1236df 100644 --- a/apps/mobile/src/modules/entry-list/EntryListContentSocial.tsx +++ b/apps/mobile/src/modules/entry-list/EntryListContentSocial.tsx @@ -25,7 +25,9 @@ export const EntryListContentSocial = forwardRef< [isFetching], ) - const onViewableItemsChanged = useOnViewableItemsChanged({ isLoading: isRefetching }) + const { onViewableItemsChanged, onScroll } = useOnViewableItemsChanged({ + disabled: isRefetching, + }) return ( id} estimatedItemSize={100} renderItem={renderItem} - onEndReached={() => { - fetchNextPage() - }} + onEndReached={fetchNextPage} onViewableItemsChanged={onViewableItemsChanged} + onScroll={onScroll} ItemSeparatorComponent={ItemSeparatorFullWidth} ListFooterComponent={ListFooterComponent} /> diff --git a/apps/mobile/src/modules/entry-list/hooks.ts b/apps/mobile/src/modules/entry-list/hooks.ts index 72896c036..9822ca481 100644 --- a/apps/mobile/src/modules/entry-list/hooks.ts +++ b/apps/mobile/src/modules/entry-list/hooks.ts @@ -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) => 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) => { + const currentOffset = e.nativeEvent.contentOffset.y + const currentOrientation = currentOffset > lastOffset.current ? "down" : "up" + orientation.current = currentOrientation + lastOffset.current = currentOffset + }, + } + }, [disabled, markAsReadWhenRendering, markAsReadWhenScrolling, orientation, stableIdExtractor]) }