import React from 'react' import {View} from 'react-native' import {AppBskyActorDefs} from '@atproto/api' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {NavigationProp, useNavigation} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' import {getRootNavigation, getTabState, TabState} from '#/lib/routes/helpers' import {logEvent} from '#/lib/statsig/statsig' import {isNative} from '#/platform/detection' import {listenSoftReset} from '#/state/events' import {FeedFeedbackProvider, useFeedFeedback} from '#/state/feed-feedback' import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed' import {FeedDescriptor, FeedParams} from '#/state/queries/post-feed' import {truncateAndInvalidate} from '#/state/queries/util' import {useSession} from '#/state/session' import {useSetMinimalShellMode} from '#/state/shell' import {useComposerControls} from '#/state/shell/composer' import {useAnalytics} from 'lib/analytics/analytics' import {ComposeIcon2} from 'lib/icons' import {AllNavigatorParams} from 'lib/routes/types' import {s} from 'lib/styles' import {useHeaderOffset} from '#/components/hooks/useHeaderOffset' import {Feed} from '../posts/Feed' import {FAB} from '../util/fab/FAB' import {ListMethods} from '../util/List' import {LoadLatestBtn} from '../util/load-latest/LoadLatestBtn' import {MainScrollProvider} from '../util/MainScrollProvider' const POLL_FREQ = 60e3 // 60sec export function FeedPage({ testID, isPageFocused, feed, feedParams, renderEmptyState, renderEndOfFeed, savedFeedConfig, }: { testID?: string feed: FeedDescriptor feedParams?: FeedParams isPageFocused: boolean renderEmptyState: () => JSX.Element renderEndOfFeed?: () => JSX.Element savedFeedConfig?: AppBskyActorDefs.SavedFeed }) { const {hasSession} = useSession() const {_} = useLingui() const navigation = useNavigation>() const queryClient = useQueryClient() const {openComposer} = useComposerControls() const [isScrolledDown, setIsScrolledDown] = React.useState(false) const setMinimalShellMode = useSetMinimalShellMode() const {screen, track} = useAnalytics() const headerOffset = useHeaderOffset() const feedFeedback = useFeedFeedback(feed, hasSession) const scrollElRef = React.useRef(null) const [hasNew, setHasNew] = React.useState(false) const scrollToTop = React.useCallback(() => { scrollElRef.current?.scrollToOffset({ animated: isNative, offset: -headerOffset, }) setMinimalShellMode(false) }, [headerOffset, setMinimalShellMode]) const onSoftReset = React.useCallback(() => { const isScreenFocused = getTabState(getRootNavigation(navigation).getState(), 'Home') === TabState.InsideAtRoot if (isScreenFocused && isPageFocused) { scrollToTop() truncateAndInvalidate(queryClient, FEED_RQKEY(feed)) setHasNew(false) logEvent('feed:refresh:sampled', { feedType: feed.split('|')[0], feedUrl: feed, reason: 'soft-reset', }) } }, [navigation, isPageFocused, scrollToTop, queryClient, feed, setHasNew]) // fires when page within screen is activated/deactivated React.useEffect(() => { if (!isPageFocused) { return } screen('Feed') return listenSoftReset(onSoftReset) }, [onSoftReset, screen, isPageFocused]) const onPressCompose = React.useCallback(() => { track('HomeScreen:PressCompose') openComposer({}) }, [openComposer, track]) const onPressLoadLatest = React.useCallback(() => { scrollToTop() truncateAndInvalidate(queryClient, FEED_RQKEY(feed)) setHasNew(false) logEvent('feed:refresh:sampled', { feedType: feed.split('|')[0], feedUrl: feed, reason: 'load-latest', }) }, [scrollToTop, feed, queryClient, setHasNew]) return ( {(isScrolledDown || hasNew) && ( )} {hasSession && ( } accessibilityRole="button" accessibilityLabel={_(msg({message: `New post`, context: 'action'}))} accessibilityHint="" /> )} ) }