diff --git a/src/App.native.tsx b/src/App.native.tsx
index 50a80d9fea..1284154f3b 100644
--- a/src/App.native.tsx
+++ b/src/App.native.tsx
@@ -32,6 +32,7 @@ import {Provider as MutedThreadsProvider} from 'state/muted-threads'
import {Provider as InvitesStateProvider} from 'state/invites'
import {Provider as PrefsStateProvider} from 'state/preferences'
import {Provider as LoggedOutViewProvider} from 'state/shell/logged-out'
+import {Provider as SelectedFeedProvider} from 'state/shell/selected-feed'
import I18nProvider from './locale/i18nProvider'
import {
Provider as SessionProvider,
@@ -72,17 +73,19 @@ function InnerApp() {
// Resets the entire tree below when it changes:
key={currentAccount?.did}>
-
-
- {/* All components should be within this provider */}
-
-
-
-
-
-
-
-
+
+
+
+ {/* All components should be within this provider */}
+
+
+
+
+
+
+
+
+
diff --git a/src/App.web.tsx b/src/App.web.tsx
index 9d1bd35063..f10bb194d4 100644
--- a/src/App.web.tsx
+++ b/src/App.web.tsx
@@ -22,6 +22,7 @@ import {Provider as MutedThreadsProvider} from 'state/muted-threads'
import {Provider as InvitesStateProvider} from 'state/invites'
import {Provider as PrefsStateProvider} from 'state/preferences'
import {Provider as LoggedOutViewProvider} from 'state/shell/logged-out'
+import {Provider as SelectedFeedProvider} from 'state/shell/selected-feed'
import I18nProvider from './locale/i18nProvider'
import {
Provider as SessionProvider,
@@ -52,17 +53,19 @@ function InnerApp() {
// Resets the entire tree below when it changes:
key={currentAccount?.did}>
-
-
- {/* All components should be within this provider */}
-
-
-
-
-
-
-
-
+
+
+
+ {/* All components should be within this provider */}
+
+
+
+
+
+
+
+
+
diff --git a/src/state/shell/selected-feed.tsx b/src/state/shell/selected-feed.tsx
new file mode 100644
index 0000000000..a05d8661b4
--- /dev/null
+++ b/src/state/shell/selected-feed.tsx
@@ -0,0 +1,61 @@
+import React from 'react'
+import * as persisted from '#/state/persisted'
+import {isWeb} from '#/platform/detection'
+
+type StateContext = string
+type SetContext = (v: string) => void
+
+const stateContext = React.createContext('home')
+const setContext = React.createContext((_: string) => {})
+
+function getInitialFeed() {
+ if (isWeb) {
+ if (window.location.pathname === '/') {
+ const params = new URLSearchParams(window.location.search)
+ const feedFromUrl = params.get('feed')
+ if (feedFromUrl) {
+ // If explicitly booted from a link like /?feed=..., prefer that.
+ return feedFromUrl
+ }
+ }
+ const feedFromSession = sessionStorage.getItem('lastSelectedHomeFeed')
+ if (feedFromSession) {
+ // Fall back to a previously chosen feed for this browser tab.
+ return feedFromSession
+ }
+ }
+ const feedFromPersisted = persisted.get('lastSelectedHomeFeed')
+ if (feedFromPersisted) {
+ // Fall back to the last chosen one across all tabs.
+ return feedFromPersisted
+ }
+ return 'home'
+}
+
+export function Provider({children}: React.PropsWithChildren<{}>) {
+ const [state, setState] = React.useState(getInitialFeed)
+
+ const saveState = React.useCallback((feed: string) => {
+ setState(feed)
+ if (isWeb) {
+ try {
+ sessionStorage.setItem('lastSelectedHomeFeed', feed)
+ } catch {}
+ }
+ persisted.write('lastSelectedHomeFeed', feed)
+ }, [])
+
+ return (
+
+ {children}
+
+ )
+}
+
+export function useSelectedFeed() {
+ return React.useContext(stateContext)
+}
+
+export function useSetSelectedFeed() {
+ return React.useContext(setContext)
+}
diff --git a/src/view/com/pager/Pager.web.tsx b/src/view/com/pager/Pager.web.tsx
index d7113bb05a..42982ef7f8 100644
--- a/src/view/com/pager/Pager.web.tsx
+++ b/src/view/com/pager/Pager.web.tsx
@@ -31,7 +31,7 @@ export const Pager = React.forwardRef(function PagerImpl(
const anchorRef = React.useRef(null)
React.useImperativeHandle(ref, () => ({
- setPage: (index: number) => setSelectedPage(index),
+ setPage: (index: number) => onTabBarSelect(index),
}))
const onTabBarSelect = React.useCallback(
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
index 7228a9e1be..cb2abf1bca 100644
--- a/src/view/screens/Home.tsx
+++ b/src/view/screens/Home.tsx
@@ -7,7 +7,7 @@ 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 {Pager, RenderTabBarFnProps, PagerRef} from 'view/com/pager/Pager'
import {FeedPage} from 'view/com/feeds/FeedPage'
import {HomeLoggedOutCTA} from '../com/auth/HomeLoggedOutCTA'
import {useSetMinimalShellMode, useSetDrawerSwipeDisabled} from '#/state/shell'
@@ -16,25 +16,19 @@ import {usePinnedFeedsInfos, FeedSourceInfo} from '#/state/queries/feed'
import {UsePreferencesQueryResponse} from '#/state/queries/preferences/types'
import {emitSoftReset} from '#/state/events'
import {useSession} from '#/state/session'
-import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
-import * as persisted from '#/state/persisted'
+import {useSelectedFeed, useSetSelectedFeed} from '#/state/shell/selected-feed'
type Props = NativeStackScreenProps
export function HomeScreen(props: Props) {
const {data: preferences} = usePreferencesQuery()
const {feeds: pinnedFeedInfos, isLoading: isPinnedFeedsLoading} =
usePinnedFeedsInfos()
- const {isDesktop} = useWebMediaQueries()
- const [rawInitialFeed] = React.useState(
- () => persisted.get('lastSelectedHomeFeed') ?? 'home',
- )
if (preferences && pinnedFeedInfos && !isPinnedFeedsLoading) {
return (
)
} else {
@@ -49,11 +43,9 @@ export function HomeScreen(props: Props) {
function HomeScreenReady({
preferences,
pinnedFeedInfos,
- rawInitialFeed,
}: Props & {
preferences: UsePreferencesQueryResponse
pinnedFeedInfos: FeedSourceInfo[]
- rawInitialFeed: string
}) {
const allFeeds = React.useMemo(() => {
const feeds: FeedDescriptor[] = []
@@ -68,12 +60,24 @@ function HomeScreenReady({
return feeds
}, [pinnedFeedInfos])
- const [rawSelectedFeed, setSelectedFeed] =
- React.useState(rawInitialFeed)
+ const rawSelectedFeed = useSelectedFeed()
+ const setSelectedFeed = useSetSelectedFeed()
const maybeFoundIndex = allFeeds.indexOf(rawSelectedFeed as FeedDescriptor)
const selectedIndex = Math.max(0, maybeFoundIndex)
const selectedFeed = allFeeds[selectedIndex]
+ const pagerRef = React.useRef(null)
+ const lastPagerReportedIndexRef = React.useRef(selectedIndex)
+ React.useLayoutEffect(() => {
+ // Since the pager is not a controlled component, adjust it imperatively
+ // if the selected index gets out of sync with what it last reported.
+ // This is supposed to only happen on the web when you use the right nav.
+ if (selectedIndex !== lastPagerReportedIndexRef.current) {
+ lastPagerReportedIndexRef.current = selectedIndex
+ pagerRef.current?.setPage(selectedIndex)
+ }
+ }, [selectedIndex])
+
const {hasSession} = useSession()
const setMinimalShellMode = useSetMinimalShellMode()
const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled()
@@ -93,7 +97,7 @@ function HomeScreenReady({
setDrawerSwipeDisabled(index > 0)
const feed = allFeeds[index]
setSelectedFeed(feed)
- persisted.write('lastSelectedHomeFeed', feed)
+ lastPagerReportedIndexRef.current = index
},
[setDrawerSwipeDisabled, setSelectedFeed, setMinimalShellMode, allFeeds],
)
@@ -147,6 +151,7 @@ function HomeScreenReady({
return hasSession ? (
()
const route = useNavigationState(state => {
if (!state) {
return {name: 'Home'}
@@ -22,30 +28,34 @@ export function DesktopFeeds() {
return (
-
- {feeds
- .filter(f => f.displayName !== 'Following')
- .map(feed => {
- try {
- const params = route.params as Record
- const routeName =
- feed.type === 'feed' ? 'ProfileFeed' : 'ProfileList'
- return (
-
- )
- } catch {
- return null
- }
- })}
+ {pinnedFeedInfos.map(feedInfo => {
+ const uri = feedInfo.uri
+ let feed: FeedDescriptor
+ if (!uri) {
+ feed = 'home'
+ } else if (uri.includes('app.bsky.feed.generator')) {
+ feed = `feedgen|${uri}`
+ } else if (uri.includes('app.bsky.graph.list')) {
+ feed = `list|${uri}`
+ } else {
+ return null
+ }
+ return (
+ {
+ setSelectedFeed(feed)
+ navigation.navigate('Home')
+ if (feed === selectedFeed) {
+ emitSoftReset()
+ }
+ }}
+ />
+ )
+ })}
void
}) {
const pal = usePalette('default')
return (
@@ -74,6 +86,7 @@ function FeedItem({
type="xl"
href={href}
text={title}
+ onPress={onPress}
style={[
current ? pal.text : pal.textLight,
{letterSpacing: 0.15, fontWeight: current ? '500' : 'normal'},