* Sanity check against cases where empty pages may occur * Use the mergefeed as an emergency fallback to an empty feed * Check for new posts on focus
219 lines
6.4 KiB
TypeScript
219 lines
6.4 KiB
TypeScript
import React from 'react'
|
|
import {View, ActivityIndicator, StyleSheet} from 'react-native'
|
|
import {useFocusEffect} from '@react-navigation/native'
|
|
import {NativeStackScreenProps, HomeTabNavigatorParams} from 'lib/routes/types'
|
|
import {FeedDescriptor, FeedParams} from '#/state/queries/post-feed'
|
|
import {FollowingEmptyState} from 'view/com/posts/FollowingEmptyState'
|
|
import {FollowingEndOfFeed} from 'view/com/posts/FollowingEndOfFeed'
|
|
import {CustomFeedEmptyState} from 'view/com/posts/CustomFeedEmptyState'
|
|
import {FeedsTabBar} from '../com/pager/FeedsTabBar'
|
|
import {Pager, RenderTabBarFnProps} from 'view/com/pager/Pager'
|
|
import {FeedPage} from 'view/com/feeds/FeedPage'
|
|
import {useSetMinimalShellMode, useSetDrawerSwipeDisabled} from '#/state/shell'
|
|
import {usePreferencesQuery} from '#/state/queries/preferences'
|
|
import {UsePreferencesQueryResponse} from '#/state/queries/preferences/types'
|
|
import {emitSoftReset} from '#/state/events'
|
|
import {useSession} from '#/state/session'
|
|
import {loadString, saveString} from '#/lib/storage'
|
|
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
|
|
|
type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'>
|
|
export function HomeScreen(props: Props) {
|
|
const {data: preferences} = usePreferencesQuery()
|
|
const {isDesktop} = useWebMediaQueries()
|
|
const [initialPage, setInitialPage] = React.useState<string | undefined>(
|
|
undefined,
|
|
)
|
|
|
|
React.useEffect(() => {
|
|
const loadLastActivePage = async () => {
|
|
try {
|
|
const lastActivePage =
|
|
(await loadString('lastActivePage')) ?? 'Following'
|
|
setInitialPage(lastActivePage)
|
|
} catch {
|
|
setInitialPage('Following')
|
|
}
|
|
}
|
|
loadLastActivePage()
|
|
}, [])
|
|
|
|
if (preferences && initialPage !== undefined) {
|
|
return (
|
|
<HomeScreenReady
|
|
{...props}
|
|
preferences={preferences}
|
|
initialPage={isDesktop ? 'Following' : initialPage}
|
|
/>
|
|
)
|
|
} else {
|
|
return (
|
|
<View style={styles.loading}>
|
|
<ActivityIndicator size="large" />
|
|
</View>
|
|
)
|
|
}
|
|
}
|
|
|
|
function HomeScreenReady({
|
|
preferences,
|
|
initialPage,
|
|
}: Props & {
|
|
preferences: UsePreferencesQueryResponse
|
|
initialPage: string
|
|
}) {
|
|
const {hasSession} = useSession()
|
|
const setMinimalShellMode = useSetMinimalShellMode()
|
|
const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled()
|
|
const [selectedPage, setSelectedPage] = React.useState<string>(initialPage)
|
|
|
|
/**
|
|
* Used to ensure that we re-compute `customFeeds` AND force a re-render of
|
|
* the pager with the new order of feeds.
|
|
*/
|
|
const pinnedFeedOrderKey = JSON.stringify(preferences.feeds.pinned)
|
|
|
|
const selectedPageIndex = React.useMemo(() => {
|
|
const index = ['Following', ...preferences.feeds.pinned].indexOf(
|
|
selectedPage,
|
|
)
|
|
return Math.max(index, 0)
|
|
}, [preferences.feeds.pinned, selectedPage])
|
|
|
|
const customFeeds = React.useMemo(() => {
|
|
const pinned = preferences.feeds.pinned
|
|
const feeds: FeedDescriptor[] = []
|
|
for (const uri of pinned) {
|
|
if (uri.includes('app.bsky.feed.generator')) {
|
|
feeds.push(`feedgen|${uri}`)
|
|
} else if (uri.includes('app.bsky.graph.list')) {
|
|
feeds.push(`list|${uri}`)
|
|
}
|
|
}
|
|
return feeds
|
|
}, [preferences.feeds.pinned])
|
|
|
|
const homeFeedParams = React.useMemo<FeedParams>(() => {
|
|
return {
|
|
mergeFeedEnabled: Boolean(preferences.feedViewPrefs.lab_mergeFeedEnabled),
|
|
mergeFeedSources: preferences.feeds.saved,
|
|
}
|
|
}, [preferences])
|
|
|
|
useFocusEffect(
|
|
React.useCallback(() => {
|
|
setMinimalShellMode(false)
|
|
setDrawerSwipeDisabled(selectedPageIndex > 0)
|
|
return () => {
|
|
setDrawerSwipeDisabled(false)
|
|
}
|
|
}, [setDrawerSwipeDisabled, selectedPageIndex, setMinimalShellMode]),
|
|
)
|
|
|
|
const onPageSelected = React.useCallback(
|
|
(index: number) => {
|
|
setMinimalShellMode(false)
|
|
setDrawerSwipeDisabled(index > 0)
|
|
const page = ['Following', ...preferences.feeds.pinned][index]
|
|
setSelectedPage(page)
|
|
saveString('lastActivePage', page)
|
|
},
|
|
[
|
|
setDrawerSwipeDisabled,
|
|
setSelectedPage,
|
|
setMinimalShellMode,
|
|
preferences.feeds.pinned,
|
|
],
|
|
)
|
|
|
|
const onPressSelected = React.useCallback(() => {
|
|
emitSoftReset()
|
|
}, [])
|
|
|
|
const onPageScrollStateChanged = React.useCallback(
|
|
(state: 'idle' | 'dragging' | 'settling') => {
|
|
if (state === 'dragging') {
|
|
setMinimalShellMode(false)
|
|
}
|
|
},
|
|
[setMinimalShellMode],
|
|
)
|
|
|
|
const renderTabBar = React.useCallback(
|
|
(props: RenderTabBarFnProps) => {
|
|
return (
|
|
<FeedsTabBar
|
|
key="FEEDS_TAB_BAR"
|
|
selectedPage={props.selectedPage}
|
|
onSelect={props.onSelect}
|
|
testID="homeScreenFeedTabs"
|
|
onPressSelected={onPressSelected}
|
|
/>
|
|
)
|
|
},
|
|
[onPressSelected],
|
|
)
|
|
|
|
const renderFollowingEmptyState = React.useCallback(() => {
|
|
return <FollowingEmptyState />
|
|
}, [])
|
|
|
|
const renderCustomFeedEmptyState = React.useCallback(() => {
|
|
return <CustomFeedEmptyState />
|
|
}, [])
|
|
|
|
return hasSession ? (
|
|
<Pager
|
|
key={pinnedFeedOrderKey}
|
|
testID="homeScreen"
|
|
initialPage={selectedPageIndex}
|
|
onPageSelected={onPageSelected}
|
|
onPageScrollStateChanged={onPageScrollStateChanged}
|
|
renderTabBar={renderTabBar}
|
|
tabBarPosition="top">
|
|
<FeedPage
|
|
key="1"
|
|
testID="followingFeedPage"
|
|
isPageFocused={selectedPageIndex === 0}
|
|
feed="home"
|
|
feedParams={homeFeedParams}
|
|
renderEmptyState={renderFollowingEmptyState}
|
|
renderEndOfFeed={FollowingEndOfFeed}
|
|
/>
|
|
{customFeeds.map((f, index) => {
|
|
return (
|
|
<FeedPage
|
|
key={f}
|
|
testID="customFeedPage"
|
|
isPageFocused={selectedPageIndex === 1 + index}
|
|
feed={f}
|
|
renderEmptyState={renderCustomFeedEmptyState}
|
|
/>
|
|
)
|
|
})}
|
|
</Pager>
|
|
) : (
|
|
<Pager
|
|
testID="homeScreen"
|
|
onPageSelected={onPageSelected}
|
|
onPageScrollStateChanged={onPageScrollStateChanged}
|
|
renderTabBar={renderTabBar}
|
|
tabBarPosition="top">
|
|
<FeedPage
|
|
testID="customFeedPage"
|
|
isPageFocused={true}
|
|
feed={`feedgen|at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot`}
|
|
renderEmptyState={renderCustomFeedEmptyState}
|
|
/>
|
|
</Pager>
|
|
)
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
loading: {
|
|
height: '100%',
|
|
alignContent: 'center',
|
|
justifyContent: 'center',
|
|
paddingBottom: 100,
|
|
},
|
|
})
|