Refactor Home feed pager rendering logic (#2768)
* Use new persistence API for selected feed * Refactor Home feeds pager data sourcezio/stable
parent
f393dda528
commit
4583521b11
|
@ -111,6 +111,7 @@ export function transform(legacy: Partial<LegacySchema>): Schema {
|
||||||
},
|
},
|
||||||
hiddenPosts: defaults.hiddenPosts,
|
hiddenPosts: defaults.hiddenPosts,
|
||||||
externalEmbeds: defaults.externalEmbeds,
|
externalEmbeds: defaults.externalEmbeds,
|
||||||
|
lastSelectedHomeFeed: defaults.lastSelectedHomeFeed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,7 @@ export const schema = z.object({
|
||||||
}),
|
}),
|
||||||
hiddenPosts: z.array(z.string()).optional(), // should move to server
|
hiddenPosts: z.array(z.string()).optional(), // should move to server
|
||||||
useInAppBrowser: z.boolean().optional(),
|
useInAppBrowser: z.boolean().optional(),
|
||||||
|
lastSelectedHomeFeed: z.string().optional(),
|
||||||
})
|
})
|
||||||
export type Schema = z.infer<typeof schema>
|
export type Schema = z.infer<typeof schema>
|
||||||
|
|
||||||
|
@ -89,4 +90,5 @@ export const defaults: Schema = {
|
||||||
},
|
},
|
||||||
hiddenPosts: [],
|
hiddenPosts: [],
|
||||||
useInAppBrowser: undefined,
|
useInAppBrowser: undefined,
|
||||||
|
lastSelectedHomeFeed: undefined,
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,45 +16,25 @@ import {usePinnedFeedsInfos, FeedSourceInfo} from '#/state/queries/feed'
|
||||||
import {UsePreferencesQueryResponse} from '#/state/queries/preferences/types'
|
import {UsePreferencesQueryResponse} from '#/state/queries/preferences/types'
|
||||||
import {emitSoftReset} from '#/state/events'
|
import {emitSoftReset} from '#/state/events'
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {loadString, saveString} from '#/lib/storage'
|
|
||||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
||||||
import {clamp} from '#/lib/numbers'
|
import * as persisted from '#/state/persisted'
|
||||||
|
|
||||||
type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'>
|
type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'>
|
||||||
export function HomeScreen(props: Props) {
|
export function HomeScreen(props: Props) {
|
||||||
const {data: preferences} = usePreferencesQuery()
|
const {data: preferences} = usePreferencesQuery()
|
||||||
const {feeds: pinnedFeeds, isLoading: isPinnedFeedsLoading} =
|
const {feeds: pinnedFeedInfos, isLoading: isPinnedFeedsLoading} =
|
||||||
usePinnedFeedsInfos()
|
usePinnedFeedsInfos()
|
||||||
const {isDesktop} = useWebMediaQueries()
|
const {isDesktop} = useWebMediaQueries()
|
||||||
const [initialPage, setInitialPage] = React.useState<string | undefined>(
|
const [rawInitialFeed] = React.useState<string>(
|
||||||
undefined,
|
() => persisted.get('lastSelectedHomeFeed') ?? 'home',
|
||||||
)
|
)
|
||||||
|
if (preferences && pinnedFeedInfos && !isPinnedFeedsLoading) {
|
||||||
React.useEffect(() => {
|
|
||||||
const loadLastActivePage = async () => {
|
|
||||||
try {
|
|
||||||
const lastActivePage =
|
|
||||||
(await loadString('lastActivePage')) ?? 'Following'
|
|
||||||
setInitialPage(lastActivePage)
|
|
||||||
} catch {
|
|
||||||
setInitialPage('Following')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
loadLastActivePage()
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
if (
|
|
||||||
preferences &&
|
|
||||||
pinnedFeeds &&
|
|
||||||
initialPage !== undefined &&
|
|
||||||
!isPinnedFeedsLoading
|
|
||||||
) {
|
|
||||||
return (
|
return (
|
||||||
<HomeScreenReady
|
<HomeScreenReady
|
||||||
{...props}
|
{...props}
|
||||||
preferences={preferences}
|
preferences={preferences}
|
||||||
pinnedFeeds={pinnedFeeds}
|
pinnedFeedInfos={pinnedFeedInfos}
|
||||||
initialPage={isDesktop ? 'Following' : initialPage}
|
rawInitialFeed={isDesktop ? 'home' : rawInitialFeed}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -68,35 +48,17 @@ export function HomeScreen(props: Props) {
|
||||||
|
|
||||||
function HomeScreenReady({
|
function HomeScreenReady({
|
||||||
preferences,
|
preferences,
|
||||||
pinnedFeeds,
|
pinnedFeedInfos,
|
||||||
initialPage,
|
rawInitialFeed,
|
||||||
}: Props & {
|
}: Props & {
|
||||||
preferences: UsePreferencesQueryResponse
|
preferences: UsePreferencesQueryResponse
|
||||||
pinnedFeeds: FeedSourceInfo[]
|
pinnedFeedInfos: FeedSourceInfo[]
|
||||||
initialPage: string
|
rawInitialFeed: string
|
||||||
}) {
|
}) {
|
||||||
const {hasSession} = useSession()
|
const allFeeds = React.useMemo(() => {
|
||||||
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 = pinnedFeeds
|
|
||||||
const feeds: FeedDescriptor[] = []
|
const feeds: FeedDescriptor[] = []
|
||||||
for (const {uri} of pinned) {
|
feeds.push('home')
|
||||||
|
for (const {uri} of pinnedFeedInfos) {
|
||||||
if (uri.includes('app.bsky.feed.generator')) {
|
if (uri.includes('app.bsky.feed.generator')) {
|
||||||
feeds.push(`feedgen|${uri}`)
|
feeds.push(`feedgen|${uri}`)
|
||||||
} else if (uri.includes('app.bsky.graph.list')) {
|
} else if (uri.includes('app.bsky.graph.list')) {
|
||||||
|
@ -104,41 +66,36 @@ function HomeScreenReady({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return feeds
|
return feeds
|
||||||
}, [pinnedFeeds])
|
}, [pinnedFeedInfos])
|
||||||
|
|
||||||
const homeFeedParams = React.useMemo<FeedParams>(() => {
|
const [rawSelectedFeed, setSelectedFeed] =
|
||||||
return {
|
React.useState<string>(rawInitialFeed)
|
||||||
mergeFeedEnabled: Boolean(preferences.feedViewPrefs.lab_mergeFeedEnabled),
|
const maybeFoundIndex = allFeeds.indexOf(rawSelectedFeed as FeedDescriptor)
|
||||||
mergeFeedSources: preferences.feedViewPrefs.lab_mergeFeedEnabled
|
const selectedIndex = Math.max(0, maybeFoundIndex)
|
||||||
? preferences.feeds.saved
|
const selectedFeed = allFeeds[selectedIndex]
|
||||||
: [],
|
|
||||||
}
|
|
||||||
}, [preferences])
|
|
||||||
|
|
||||||
|
const {hasSession} = useSession()
|
||||||
|
const setMinimalShellMode = useSetMinimalShellMode()
|
||||||
|
const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled()
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
React.useCallback(() => {
|
React.useCallback(() => {
|
||||||
setMinimalShellMode(false)
|
setMinimalShellMode(false)
|
||||||
setDrawerSwipeDisabled(selectedPageIndex > 0)
|
setDrawerSwipeDisabled(selectedIndex > 0)
|
||||||
return () => {
|
return () => {
|
||||||
setDrawerSwipeDisabled(false)
|
setDrawerSwipeDisabled(false)
|
||||||
}
|
}
|
||||||
}, [setDrawerSwipeDisabled, selectedPageIndex, setMinimalShellMode]),
|
}, [setDrawerSwipeDisabled, selectedIndex, setMinimalShellMode]),
|
||||||
)
|
)
|
||||||
|
|
||||||
const onPageSelected = React.useCallback(
|
const onPageSelected = React.useCallback(
|
||||||
(index: number) => {
|
(index: number) => {
|
||||||
setMinimalShellMode(false)
|
setMinimalShellMode(false)
|
||||||
setDrawerSwipeDisabled(index > 0)
|
setDrawerSwipeDisabled(index > 0)
|
||||||
const page = ['Following', ...preferences.feeds.pinned][index]
|
const feed = allFeeds[index]
|
||||||
setSelectedPage(page)
|
setSelectedFeed(feed)
|
||||||
saveString('lastActivePage', page)
|
persisted.write('lastSelectedHomeFeed', feed)
|
||||||
},
|
},
|
||||||
[
|
[setDrawerSwipeDisabled, setSelectedFeed, setMinimalShellMode, allFeeds],
|
||||||
setDrawerSwipeDisabled,
|
|
||||||
setSelectedPage,
|
|
||||||
setMinimalShellMode,
|
|
||||||
preferences.feeds.pinned,
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const onPressSelected = React.useCallback(() => {
|
const onPressSelected = React.useCallback(() => {
|
||||||
|
@ -177,30 +134,40 @@ function HomeScreenReady({
|
||||||
return <CustomFeedEmptyState />
|
return <CustomFeedEmptyState />
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
const [homeFeed, ...customFeeds] = allFeeds
|
||||||
|
const homeFeedParams = React.useMemo<FeedParams>(() => {
|
||||||
|
return {
|
||||||
|
mergeFeedEnabled: Boolean(preferences.feedViewPrefs.lab_mergeFeedEnabled),
|
||||||
|
mergeFeedSources: preferences.feedViewPrefs.lab_mergeFeedEnabled
|
||||||
|
? preferences.feeds.saved
|
||||||
|
: [],
|
||||||
|
}
|
||||||
|
}, [preferences])
|
||||||
|
|
||||||
return hasSession ? (
|
return hasSession ? (
|
||||||
<Pager
|
<Pager
|
||||||
key={pinnedFeedOrderKey}
|
key={allFeeds.join(',')}
|
||||||
testID="homeScreen"
|
testID="homeScreen"
|
||||||
initialPage={clamp(selectedPageIndex, 0, customFeeds.length)}
|
initialPage={selectedIndex}
|
||||||
onPageSelected={onPageSelected}
|
onPageSelected={onPageSelected}
|
||||||
onPageScrollStateChanged={onPageScrollStateChanged}
|
onPageScrollStateChanged={onPageScrollStateChanged}
|
||||||
renderTabBar={renderTabBar}>
|
renderTabBar={renderTabBar}>
|
||||||
<FeedPage
|
<FeedPage
|
||||||
key="1"
|
key={homeFeed}
|
||||||
testID="followingFeedPage"
|
testID="followingFeedPage"
|
||||||
isPageFocused={selectedPageIndex === 0}
|
isPageFocused={selectedFeed === homeFeed}
|
||||||
feed="home"
|
feed={homeFeed}
|
||||||
feedParams={homeFeedParams}
|
feedParams={homeFeedParams}
|
||||||
renderEmptyState={renderFollowingEmptyState}
|
renderEmptyState={renderFollowingEmptyState}
|
||||||
renderEndOfFeed={FollowingEndOfFeed}
|
renderEndOfFeed={FollowingEndOfFeed}
|
||||||
/>
|
/>
|
||||||
{customFeeds.map((f, index) => {
|
{customFeeds.map(feed => {
|
||||||
return (
|
return (
|
||||||
<FeedPage
|
<FeedPage
|
||||||
key={f}
|
key={feed}
|
||||||
testID="customFeedPage"
|
testID="customFeedPage"
|
||||||
isPageFocused={selectedPageIndex === 1 + index}
|
isPageFocused={selectedFeed === feed}
|
||||||
feed={f}
|
feed={feed}
|
||||||
renderEmptyState={renderCustomFeedEmptyState}
|
renderEmptyState={renderCustomFeedEmptyState}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue