wip
This commit is contained in:
@@ -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'
|
||||
import {type I18n} from '@lingui/core'
|
||||
|
||||
@@ -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>()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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,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'
|
||||
|
||||
|
||||
@@ -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,
|
||||
@@ -183,6 +191,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
|
||||
@@ -209,6 +221,7 @@ let PostFeed = ({
|
||||
savedFeedConfig,
|
||||
initialNumToRender: initialNumToRenderOverride,
|
||||
isVideoFeed = false,
|
||||
ref,
|
||||
}: {
|
||||
feed: FeedDescriptor
|
||||
feedParams?: FeedParams
|
||||
@@ -232,6 +245,7 @@ let PostFeed = ({
|
||||
initialNumToRender?: number
|
||||
isVideoFeed?: boolean
|
||||
lastFetchDate?: () => number
|
||||
ref?: React.Ref<PostFeedRef>
|
||||
}): React.ReactNode => {
|
||||
const ax = useAnalytics()
|
||||
const {t: l} = useLingui()
|
||||
@@ -695,8 +709,9 @@ let PostFeed = ({
|
||||
|
||||
// events
|
||||
// =
|
||||
//
|
||||
|
||||
const onRefresh = useCallback(async () => {
|
||||
const refreshFeed = async () => {
|
||||
if (!enabled) return
|
||||
|
||||
ax.metric('feed:refresh', {
|
||||
@@ -704,24 +719,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
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user