This commit is contained in:
Oleksii Bulenok
2026-06-17 13:29:52 +02:00
parent 49ca9a2e7e
commit baee7d98fc
32 changed files with 584 additions and 503 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
import {MMKV} from '@bsky.app/react-native-mmkv'
import {MMKV} from 'react-native-mmkv'
import {setPolyfills} from '@growthbook/growthbook'
import {GrowthBook} from '@growthbook/growthbook-react'
@@ -1,6 +1,6 @@
import {beforeEach, describe, expect, it, jest} from '@jest/globals'
jest.mock('@bsky.app/react-native-mmkv', () => ({
jest.mock('react-native-mmkv', () => ({
MMKV: class MMKVMock {
_store = new Map<string, string>()
+4 -1
View File
@@ -11,7 +11,7 @@ import {
RQKEY as FEED_RQKEY,
} from '#/state/queries/post-feed'
import {truncateAndInvalidate} from '#/state/queries/util'
import {PostFeed} from '#/view/com/posts/PostFeed'
import {PostFeed, type PostFeedRef} from '#/view/com/posts/PostFeed'
import {
EmptyState,
type EmptyStateButtonProps,
@@ -35,6 +35,7 @@ interface FeedSectionProps {
emptyStateMessage?: string
emptyStateButton?: EmptyStateButtonProps
emptyStateIcon?: React.ComponentType<any> | React.ReactElement
postFeedRef?: React.Ref<PostFeedRef>
}
export function ProfileFeedSection({
@@ -48,6 +49,7 @@ export function ProfileFeedSection({
emptyStateMessage,
emptyStateButton,
emptyStateIcon,
postFeedRef,
}: FeedSectionProps) {
const {_} = useLingui()
const queryClient = useQueryClient()
@@ -110,6 +112,7 @@ export function ProfileFeedSection({
shouldUseAdjustedNumToRender ? adjustedInitialNumToRender : undefined
}
isVideoFeed={isVideoFeed}
ref={postFeedRef}
/>
{(isScrolledDown || hasNew) && (
<LoadLatestBtn
+1 -1
View File
@@ -2,7 +2,7 @@ import {beforeEach, expect, jest, test} from '@jest/globals'
import {Storage} from '#/storage'
jest.mock('@bsky.app/react-native-mmkv', () => ({
jest.mock('react-native-mmkv', () => ({
MMKV: class MMKVMock {
_store = new Map()
+1 -1
View File
@@ -1,4 +1,4 @@
import {MMKV} from '@bsky.app/react-native-mmkv'
import {MMKV} from 'react-native-mmkv'
import {type DB} from '#/storage/archive/db/types'
+1 -1
View File
@@ -1,5 +1,5 @@
import {useCallback, useEffect, useState} from 'react'
import {MMKV} from '@bsky.app/react-native-mmkv'
import {MMKV} from 'react-native-mmkv'
import {type Account, type Device} from '#/storage/schema'
+27 -13
View File
@@ -1,4 +1,12 @@
import {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'
import {
memo,
useCallback,
useEffect,
useImperativeHandle,
useMemo,
useRef,
useState,
} from 'react'
import {
ActivityIndicator,
AppState,
@@ -182,6 +190,10 @@ export function getItemsForFeedback(feedRow: FeedRow): {
}
}
export type PostFeedRef = {
refreshFeed: () => Promise<void>
}
// DISABLED need to check if this is causing random feed refreshes -prf
// const REFRESH_AFTER = STALE.HOURS.ONE
const CHECK_LATEST_AFTER = STALE.SECONDS.THIRTY
@@ -208,6 +220,7 @@ let PostFeed = ({
savedFeedConfig,
initialNumToRender: initialNumToRenderOverride,
isVideoFeed = false,
ref,
}: {
feed: FeedDescriptor
feedParams?: FeedParams
@@ -231,6 +244,7 @@ let PostFeed = ({
initialNumToRender?: number
isVideoFeed?: boolean
lastFetchDate?: () => number
ref?: React.Ref<PostFeedRef>
}): React.ReactNode => {
const ax = useAnalytics()
const {t: l} = useLingui()
@@ -694,8 +708,9 @@ let PostFeed = ({
// events
// =
//
const onRefresh = useCallback(async () => {
const refreshFeed = async () => {
if (!enabled) return
ax.metric('feed:refresh', {
@@ -703,24 +718,23 @@ let PostFeed = ({
feedUrl: feed,
reason: 'pull-to-refresh',
})
setIsPTRing(true)
try {
await truncateAndInvalidate(queryClient, RQKEY(feed, feedParams))
onHasNew?.(false)
} catch (err) {
logger.error('Failed to refresh posts feed', {message: err})
}
}
const onRefresh = async () => {
setIsPTRing(true)
await refreshFeed()
setIsPTRing(false)
}, [
ax,
queryClient,
setIsPTRing,
onHasNew,
feed,
feedParams,
feedType,
enabled,
])
}
useImperativeHandle(ref, () => ({
refreshFeed,
}))
const onEndReached = useCallback(async () => {
if (isFetching || !hasNextPage || isError) return
+17 -3
View File
@@ -1,6 +1,7 @@
import {useCallback, useEffect, useMemo, useRef, useState} from 'react'
import {StyleSheet} from 'react-native'
import {SafeAreaView} from 'react-native-safe-area-context'
import {ScrollForwarderView} from 'react-native-scroll-forwarder/src'
import {
type AppBskyActorDefs,
moderateProfile,
@@ -36,6 +37,7 @@ import {useAgent, useSession} from '#/state/session'
import {ProfileFeedgens} from '#/view/com/feeds/ProfileFeedgens'
import {ProfileLists} from '#/view/com/lists/ProfileLists'
import {PagerWithHeader} from '#/view/com/pager/PagerWithHeader'
import {type PostFeedRef} from '#/view/com/posts/PostFeed'
import {ErrorScreen} from '#/view/com/util/error/ErrorScreen'
import {FAB} from '#/view/com/util/fab/FAB'
import {type ListRef} from '#/view/com/util/List'
@@ -53,7 +55,6 @@ import * as Layout from '#/components/Layout'
import {ScreenHider} from '#/components/moderation/ScreenHider'
import {ProfileStarterPacks} from '#/components/StarterPack/ProfileStarterPacks'
import {navigate} from '#/Navigation'
import {ExpoScrollForwarderView} from '../../../modules/expo-scroll-forwarder'
interface SectionRef {
scrollToTop: () => void
@@ -187,6 +188,7 @@ function ProfileScreenLoaded({
enabled: !!profile.associated?.labeler,
})
const [currentPage, setCurrentPage] = useState(0)
const [isRefreshing, setIsRefreshing] = useState(false)
const {_} = useLingui()
const [scrollViewTag, setScrollViewTag] = useState<number | null>(null)
@@ -353,6 +355,14 @@ function ProfileScreenLoaded({
],
})
const postFeedRef = useRef<PostFeedRef>(null)
const onRefresh = async () => {
setIsRefreshing(true)
await postFeedRef.current?.refreshFeed()
setIsRefreshing(false)
}
// rendering
// =
@@ -362,7 +372,10 @@ function ProfileScreenLoaded({
setMinimumHeight: (height: number) => void
}) => {
return (
<ExpoScrollForwarderView scrollViewTag={scrollViewTag}>
<ScrollForwarderView
scrollViewTag={scrollViewTag}
refreshing={isRefreshing}
onRefresh={onRefresh}>
<ProfileHeader
profile={profile}
labeler={labelerInfo}
@@ -372,7 +385,7 @@ function ProfileScreenLoaded({
isPlaceholderProfile={showPlaceholder}
setMinimumHeight={setMinimumHeight}
/>
</ExpoScrollForwarderView>
</ScrollForwarderView>
)
}
@@ -440,6 +453,7 @@ function ProfileScreenLoaded({
}
: undefined
}
postFeedRef={postFeedRef}
/>
)
: null}