about summary refs log tree commit diff
path: root/src/view/screens
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/screens')
-rw-r--r--src/view/screens/Home.tsx231
-rw-r--r--src/view/screens/LanguageSettings.tsx7
-rw-r--r--src/view/screens/Log.tsx5
-rw-r--r--src/view/screens/Moderation.tsx5
-rw-r--r--src/view/screens/PostThread.tsx143
-rw-r--r--src/view/screens/PreferencesHomeFeed.tsx7
-rw-r--r--src/view/screens/PreferencesThreads.tsx7
-rw-r--r--src/view/screens/ProfileFeed.tsx11
-rw-r--r--src/view/screens/SavedFeeds.tsx5
-rw-r--r--src/view/screens/Settings.tsx1163
10 files changed, 777 insertions, 807 deletions
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
index 8c80b0fd5..50db7e81f 100644
--- a/src/view/screens/Home.tsx
+++ b/src/view/screens/Home.tsx
@@ -1,6 +1,5 @@
 import React from 'react'
 import {useFocusEffect} from '@react-navigation/native'
-import {observer} from 'mobx-react-lite'
 import {NativeStackScreenProps, HomeTabNavigatorParams} from 'lib/routes/types'
 import {FeedDescriptor, FeedParams} from '#/state/queries/post-feed'
 import {withAuthRequired} from 'view/com/auth/withAuthRequired'
@@ -15,130 +14,126 @@ import {usePreferencesQuery} from '#/state/queries/preferences'
 import {emitSoftReset} from '#/state/events'
 
 type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'>
-export const HomeScreen = withAuthRequired(
-  observer(function HomeScreenImpl({}: Props) {
-    const setMinimalShellMode = useSetMinimalShellMode()
-    const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled()
-    const pagerRef = React.useRef<PagerRef>(null)
-    const [selectedPage, setSelectedPage] = React.useState(0)
-    const [customFeeds, setCustomFeeds] = React.useState<FeedDescriptor[]>([])
-    const {data: preferences} = usePreferencesQuery()
-
-    React.useEffect(() => {
-      if (!preferences?.feeds?.pinned) return
-
-      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}`)
-        }
+export const HomeScreen = withAuthRequired(function HomeScreenImpl({}: Props) {
+  const setMinimalShellMode = useSetMinimalShellMode()
+  const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled()
+  const pagerRef = React.useRef<PagerRef>(null)
+  const [selectedPage, setSelectedPage] = React.useState(0)
+  const [customFeeds, setCustomFeeds] = React.useState<FeedDescriptor[]>([])
+  const {data: preferences} = usePreferencesQuery()
+
+  React.useEffect(() => {
+    if (!preferences?.feeds?.pinned) return
+
+    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}`)
       }
+    }
 
-      setCustomFeeds(feeds)
+    setCustomFeeds(feeds)
 
-      pagerRef.current?.setPage(0)
-    }, [preferences?.feeds?.pinned, setCustomFeeds, pagerRef])
+    pagerRef.current?.setPage(0)
+  }, [preferences?.feeds?.pinned, setCustomFeeds, pagerRef])
 
-    const homeFeedParams = React.useMemo<FeedParams>(() => {
-      if (!preferences) return {}
+  const homeFeedParams = React.useMemo<FeedParams>(() => {
+    if (!preferences) return {}
 
-      return {
-        mergeFeedEnabled: Boolean(
-          preferences.feedViewPrefs.lab_mergeFeedEnabled,
-        ),
-        mergeFeedSources: preferences.feeds.saved,
-      }
-    }, [preferences])
+    return {
+      mergeFeedEnabled: Boolean(preferences.feedViewPrefs.lab_mergeFeedEnabled),
+      mergeFeedSources: preferences.feeds.saved,
+    }
+  }, [preferences])
 
-    useFocusEffect(
-      React.useCallback(() => {
-        setMinimalShellMode(false)
-        setDrawerSwipeDisabled(selectedPage > 0)
-        return () => {
-          setDrawerSwipeDisabled(false)
-        }
-      }, [setDrawerSwipeDisabled, selectedPage, setMinimalShellMode]),
-    )
-
-    const onPageSelected = React.useCallback(
-      (index: number) => {
+  useFocusEffect(
+    React.useCallback(() => {
+      setMinimalShellMode(false)
+      setDrawerSwipeDisabled(selectedPage > 0)
+      return () => {
+        setDrawerSwipeDisabled(false)
+      }
+    }, [setDrawerSwipeDisabled, selectedPage, setMinimalShellMode]),
+  )
+
+  const onPageSelected = React.useCallback(
+    (index: number) => {
+      setMinimalShellMode(false)
+      setSelectedPage(index)
+      setDrawerSwipeDisabled(index > 0)
+    },
+    [setDrawerSwipeDisabled, setSelectedPage, setMinimalShellMode],
+  )
+
+  const onPressSelected = React.useCallback(() => {
+    emitSoftReset()
+  }, [])
+
+  const onPageScrollStateChanged = React.useCallback(
+    (state: 'idle' | 'dragging' | 'settling') => {
+      if (state === 'dragging') {
         setMinimalShellMode(false)
-        setSelectedPage(index)
-        setDrawerSwipeDisabled(index > 0)
-      },
-      [setDrawerSwipeDisabled, setSelectedPage, setMinimalShellMode],
-    )
-
-    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) => {
+      }
+    },
+    [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 (
+    <Pager
+      ref={pagerRef}
+      testID="homeScreen"
+      onPageSelected={onPageSelected}
+      onPageScrollStateChanged={onPageScrollStateChanged}
+      renderTabBar={renderTabBar}
+      tabBarPosition="top">
+      <FeedPage
+        key="1"
+        testID="followingFeedPage"
+        isPageFocused={selectedPage === 0}
+        feed="home"
+        feedParams={homeFeedParams}
+        renderEmptyState={renderFollowingEmptyState}
+        renderEndOfFeed={FollowingEndOfFeed}
+      />
+      {customFeeds.map((f, index) => {
         return (
-          <FeedsTabBar
-            key="FEEDS_TAB_BAR"
-            selectedPage={props.selectedPage}
-            onSelect={props.onSelect}
-            testID="homeScreenFeedTabs"
-            onPressSelected={onPressSelected}
+          <FeedPage
+            key={f}
+            testID="customFeedPage"
+            isPageFocused={selectedPage === 1 + index}
+            feed={f}
+            renderEmptyState={renderCustomFeedEmptyState}
           />
         )
-      },
-      [onPressSelected],
-    )
-
-    const renderFollowingEmptyState = React.useCallback(() => {
-      return <FollowingEmptyState />
-    }, [])
-
-    const renderCustomFeedEmptyState = React.useCallback(() => {
-      return <CustomFeedEmptyState />
-    }, [])
-
-    return (
-      <Pager
-        ref={pagerRef}
-        testID="homeScreen"
-        onPageSelected={onPageSelected}
-        onPageScrollStateChanged={onPageScrollStateChanged}
-        renderTabBar={renderTabBar}
-        tabBarPosition="top">
-        <FeedPage
-          key="1"
-          testID="followingFeedPage"
-          isPageFocused={selectedPage === 0}
-          feed="home"
-          feedParams={homeFeedParams}
-          renderEmptyState={renderFollowingEmptyState}
-          renderEndOfFeed={FollowingEndOfFeed}
-        />
-        {customFeeds.map((f, index) => {
-          return (
-            <FeedPage
-              key={f}
-              testID="customFeedPage"
-              isPageFocused={selectedPage === 1 + index}
-              feed={f}
-              renderEmptyState={renderCustomFeedEmptyState}
-            />
-          )
-        })}
-      </Pager>
-    )
-  }),
-)
+      })}
+    </Pager>
+  )
+})
diff --git a/src/view/screens/LanguageSettings.tsx b/src/view/screens/LanguageSettings.tsx
index 677451526..649daea0e 100644
--- a/src/view/screens/LanguageSettings.tsx
+++ b/src/view/screens/LanguageSettings.tsx
@@ -1,6 +1,5 @@
 import React from 'react'
 import {StyleSheet, View} from 'react-native'
-import {observer} from 'mobx-react-lite'
 import {Text} from '../com/util/text/Text'
 import {s} from 'lib/styles'
 import {usePalette} from 'lib/hooks/usePalette'
@@ -23,9 +22,7 @@ import {useLanguagePrefs, useLanguagePrefsApi} from '#/state/preferences'
 
 type Props = NativeStackScreenProps<CommonNavigatorParams, 'LanguageSettings'>
 
-export const LanguageSettingsScreen = observer(function LanguageSettingsImpl(
-  _: Props,
-) {
+export function LanguageSettingsScreen(_: Props) {
   const pal = usePalette('default')
   const langPrefs = useLanguagePrefs()
   const setLangPrefs = useLanguagePrefsApi()
@@ -192,7 +189,7 @@ export const LanguageSettingsScreen = observer(function LanguageSettingsImpl(
       </View>
     </CenteredView>
   )
-})
+}
 
 const styles = StyleSheet.create({
   container: {
diff --git a/src/view/screens/Log.tsx b/src/view/screens/Log.tsx
index 69c07edae..8680b851b 100644
--- a/src/view/screens/Log.tsx
+++ b/src/view/screens/Log.tsx
@@ -1,7 +1,6 @@
 import React from 'react'
 import {StyleSheet, TouchableOpacity, View} from 'react-native'
 import {useFocusEffect} from '@react-navigation/native'
-import {observer} from 'mobx-react-lite'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
 import {ScrollView} from '../com/util/Views'
@@ -15,7 +14,7 @@ import {useLingui} from '@lingui/react'
 import {msg} from '@lingui/macro'
 import {useSetMinimalShellMode} from '#/state/shell'
 
-export const LogScreen = observer(function Log({}: NativeStackScreenProps<
+export function LogScreen({}: NativeStackScreenProps<
   CommonNavigatorParams,
   'Log'
 >) {
@@ -88,7 +87,7 @@ export const LogScreen = observer(function Log({}: NativeStackScreenProps<
       </ScrollView>
     </View>
   )
-})
+}
 
 const styles = StyleSheet.create({
   entry: {
diff --git a/src/view/screens/Moderation.tsx b/src/view/screens/Moderation.tsx
index eb952c068..37eecf22d 100644
--- a/src/view/screens/Moderation.tsx
+++ b/src/view/screens/Moderation.tsx
@@ -5,7 +5,6 @@ import {
   FontAwesomeIcon,
   FontAwesomeIconStyle,
 } from '@fortawesome/react-native-fontawesome'
-import {observer} from 'mobx-react-lite'
 import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
 import {withAuthRequired} from 'view/com/auth/withAuthRequired'
 import {s} from 'lib/styles'
@@ -21,7 +20,7 @@ import {useModalControls} from '#/state/modals'
 
 type Props = NativeStackScreenProps<CommonNavigatorParams, 'Moderation'>
 export const ModerationScreen = withAuthRequired(
-  observer(function Moderation({}: Props) {
+  function Moderation({}: Props) {
     const pal = usePalette('default')
     const setMinimalShellMode = useSetMinimalShellMode()
     const {screen, track} = useAnalytics()
@@ -111,7 +110,7 @@ export const ModerationScreen = withAuthRequired(
         </Link>
       </CenteredView>
     )
-  }),
+  },
 )
 
 const styles = StyleSheet.create({
diff --git a/src/view/screens/PostThread.tsx b/src/view/screens/PostThread.tsx
index 752f78dce..844a96d11 100644
--- a/src/view/screens/PostThread.tsx
+++ b/src/view/screens/PostThread.tsx
@@ -3,7 +3,6 @@ import {StyleSheet, View} from 'react-native'
 import Animated from 'react-native-reanimated'
 import {useFocusEffect} from '@react-navigation/native'
 import {useQueryClient} from '@tanstack/react-query'
-import {observer} from 'mobx-react-lite'
 import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
 import {makeRecordUri} from 'lib/strings/url-helpers'
 import {withAuthRequired} from 'view/com/auth/withAuthRequired'
@@ -26,83 +25,83 @@ import {CenteredView} from '../com/util/Views'
 import {useComposerControls} from '#/state/shell/composer'
 
 type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostThread'>
-export const PostThreadScreen = withAuthRequired(
-  observer(function PostThreadScreenImpl({route}: Props) {
-    const queryClient = useQueryClient()
-    const {fabMinimalShellTransform} = useMinimalShellMode()
-    const setMinimalShellMode = useSetMinimalShellMode()
-    const {openComposer} = useComposerControls()
-    const safeAreaInsets = useSafeAreaInsets()
-    const {name, rkey} = route.params
-    const {isMobile} = useWebMediaQueries()
-    const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
-    const {data: resolvedUri, error: uriError} = useResolveUriQuery(uri)
+export const PostThreadScreen = withAuthRequired(function PostThreadScreenImpl({
+  route,
+}: Props) {
+  const queryClient = useQueryClient()
+  const {fabMinimalShellTransform} = useMinimalShellMode()
+  const setMinimalShellMode = useSetMinimalShellMode()
+  const {openComposer} = useComposerControls()
+  const safeAreaInsets = useSafeAreaInsets()
+  const {name, rkey} = route.params
+  const {isMobile} = useWebMediaQueries()
+  const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
+  const {data: resolvedUri, error: uriError} = useResolveUriQuery(uri)
 
-    useFocusEffect(
-      React.useCallback(() => {
-        setMinimalShellMode(false)
-      }, [setMinimalShellMode]),
-    )
+  useFocusEffect(
+    React.useCallback(() => {
+      setMinimalShellMode(false)
+    }, [setMinimalShellMode]),
+  )
 
-    const onPressReply = React.useCallback(() => {
-      if (!resolvedUri) {
-        return
-      }
-      const thread = queryClient.getQueryData<ThreadNode>(
-        POST_THREAD_RQKEY(resolvedUri.uri),
-      )
-      if (thread?.type !== 'post') {
-        return
-      }
-      openComposer({
-        replyTo: {
-          uri: thread.post.uri,
-          cid: thread.post.cid,
-          text: thread.record.text,
-          author: {
-            handle: thread.post.author.handle,
-            displayName: thread.post.author.displayName,
-            avatar: thread.post.author.avatar,
-          },
+  const onPressReply = React.useCallback(() => {
+    if (!resolvedUri) {
+      return
+    }
+    const thread = queryClient.getQueryData<ThreadNode>(
+      POST_THREAD_RQKEY(resolvedUri.uri),
+    )
+    if (thread?.type !== 'post') {
+      return
+    }
+    openComposer({
+      replyTo: {
+        uri: thread.post.uri,
+        cid: thread.post.cid,
+        text: thread.record.text,
+        author: {
+          handle: thread.post.author.handle,
+          displayName: thread.post.author.displayName,
+          avatar: thread.post.author.avatar,
         },
-        onPost: () =>
-          queryClient.invalidateQueries({
-            queryKey: POST_THREAD_RQKEY(resolvedUri.uri || ''),
-          }),
-      })
-    }, [openComposer, queryClient, resolvedUri])
+      },
+      onPost: () =>
+        queryClient.invalidateQueries({
+          queryKey: POST_THREAD_RQKEY(resolvedUri.uri || ''),
+        }),
+    })
+  }, [openComposer, queryClient, resolvedUri])
 
-    return (
-      <View style={s.hContentRegion}>
-        {isMobile && <ViewHeader title="Post" />}
-        <View style={s.flex1}>
-          {uriError ? (
-            <CenteredView>
-              <ErrorMessage message={String(uriError)} />
-            </CenteredView>
-          ) : (
-            <PostThreadComponent
-              uri={resolvedUri?.uri}
-              onPressReply={onPressReply}
-            />
-          )}
-        </View>
-        {isMobile && (
-          <Animated.View
-            style={[
-              styles.prompt,
-              fabMinimalShellTransform,
-              {
-                bottom: clamp(safeAreaInsets.bottom, 15, 30),
-              },
-            ]}>
-            <ComposePrompt onPressCompose={onPressReply} />
-          </Animated.View>
+  return (
+    <View style={s.hContentRegion}>
+      {isMobile && <ViewHeader title="Post" />}
+      <View style={s.flex1}>
+        {uriError ? (
+          <CenteredView>
+            <ErrorMessage message={String(uriError)} />
+          </CenteredView>
+        ) : (
+          <PostThreadComponent
+            uri={resolvedUri?.uri}
+            onPressReply={onPressReply}
+          />
         )}
       </View>
-    )
-  }),
-)
+      {isMobile && (
+        <Animated.View
+          style={[
+            styles.prompt,
+            fabMinimalShellTransform,
+            {
+              bottom: clamp(safeAreaInsets.bottom, 15, 30),
+            },
+          ]}>
+          <ComposePrompt onPressCompose={onPressReply} />
+        </Animated.View>
+      )}
+    </View>
+  )
+})
 
 const styles = StyleSheet.create({
   prompt: {
diff --git a/src/view/screens/PreferencesHomeFeed.tsx b/src/view/screens/PreferencesHomeFeed.tsx
index 7b240ded0..2fd0eff37 100644
--- a/src/view/screens/PreferencesHomeFeed.tsx
+++ b/src/view/screens/PreferencesHomeFeed.tsx
@@ -1,6 +1,5 @@
 import React, {useState} from 'react'
 import {ScrollView, StyleSheet, TouchableOpacity, View} from 'react-native'
-import {observer} from 'mobx-react-lite'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {Slider} from '@miblanchard/react-native-slider'
 import {Text} from '../com/util/text/Text'
@@ -72,9 +71,7 @@ type Props = NativeStackScreenProps<
   CommonNavigatorParams,
   'PreferencesHomeFeed'
 >
-export const PreferencesHomeFeed = observer(function PreferencesHomeFeedImpl({
-  navigation,
-}: Props) {
+export function PreferencesHomeFeed({navigation}: Props) {
   const pal = usePalette('default')
   const {_} = useLingui()
   const {isTabletOrDesktop} = useWebMediaQueries()
@@ -308,7 +305,7 @@ export const PreferencesHomeFeed = observer(function PreferencesHomeFeedImpl({
       </View>
     </CenteredView>
   )
-})
+}
 
 const styles = StyleSheet.create({
   container: {
diff --git a/src/view/screens/PreferencesThreads.tsx b/src/view/screens/PreferencesThreads.tsx
index 2386f6445..7bd87b712 100644
--- a/src/view/screens/PreferencesThreads.tsx
+++ b/src/view/screens/PreferencesThreads.tsx
@@ -6,7 +6,6 @@ import {
   TouchableOpacity,
   View,
 } from 'react-native'
-import {observer} from 'mobx-react-lite'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {Text} from '../com/util/text/Text'
 import {s, colors} from 'lib/styles'
@@ -25,9 +24,7 @@ import {
 } from '#/state/queries/preferences'
 
 type Props = NativeStackScreenProps<CommonNavigatorParams, 'PreferencesThreads'>
-export const PreferencesThreads = observer(function PreferencesThreadsImpl({
-  navigation,
-}: Props) {
+export function PreferencesThreads({navigation}: Props) {
   const pal = usePalette('default')
   const {_} = useLingui()
   const {isTabletOrDesktop} = useWebMediaQueries()
@@ -162,7 +159,7 @@ export const PreferencesThreads = observer(function PreferencesThreadsImpl({
       </View>
     </CenteredView>
   )
-})
+}
 
 const styles = StyleSheet.create({
   container: {
diff --git a/src/view/screens/ProfileFeed.tsx b/src/view/screens/ProfileFeed.tsx
index f62790be6..62f5f1b36 100644
--- a/src/view/screens/ProfileFeed.tsx
+++ b/src/view/screens/ProfileFeed.tsx
@@ -15,7 +15,6 @@ import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {CommonNavigatorParams} from 'lib/routes/types'
 import {makeRecordUri} from 'lib/strings/url-helpers'
 import {colors, s} from 'lib/styles'
-import {observer} from 'mobx-react-lite'
 import {FeedDescriptor} from '#/state/queries/post-feed'
 import {withAuthRequired} from 'view/com/auth/withAuthRequired'
 import {PagerWithHeader} from 'view/com/pager/PagerWithHeader'
@@ -71,7 +70,7 @@ interface SectionRef {
 
 type Props = NativeStackScreenProps<CommonNavigatorParams, 'ProfileFeed'>
 export const ProfileFeedScreen = withAuthRequired(
-  observer(function ProfileFeedScreenImpl(props: Props) {
+  function ProfileFeedScreenImpl(props: Props) {
     const {rkey, name: handleOrDid} = props.route.params
 
     const pal = usePalette('default')
@@ -129,7 +128,7 @@ export const ProfileFeedScreen = withAuthRequired(
         </View>
       </CenteredView>
     )
-  }),
+  },
 )
 
 function ProfileFeedScreenIntermediate({feedUri}: {feedUri: string}) {
@@ -154,7 +153,7 @@ function ProfileFeedScreenIntermediate({feedUri}: {feedUri: string}) {
   )
 }
 
-export const ProfileFeedScreenInner = function ProfileFeedScreenInnerImpl({
+export function ProfileFeedScreenInner({
   preferences,
   feedInfo,
 }: {
@@ -485,7 +484,7 @@ const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
   },
 )
 
-const AboutSection = observer(function AboutPageImpl({
+function AboutSection({
   feedOwnerDid,
   feedRkey,
   feedInfo,
@@ -606,7 +605,7 @@ const AboutSection = observer(function AboutPageImpl({
       </View>
     </ScrollView>
   )
-})
+}
 
 const styles = StyleSheet.create({
   btn: {
diff --git a/src/view/screens/SavedFeeds.tsx b/src/view/screens/SavedFeeds.tsx
index c7abcf090..e3e50ca24 100644
--- a/src/view/screens/SavedFeeds.tsx
+++ b/src/view/screens/SavedFeeds.tsx
@@ -14,7 +14,6 @@ import {track} from '#/lib/analytics/analytics'
 import {useAnalytics} from 'lib/analytics/analytics'
 import {usePalette} from 'lib/hooks/usePalette'
 import {CommonNavigatorParams} from 'lib/routes/types'
-import {observer} from 'mobx-react-lite'
 import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
 import {withAuthRequired} from 'view/com/auth/withAuthRequired'
 import {ViewHeader} from 'view/com/util/ViewHeader'
@@ -146,7 +145,7 @@ export const SavedFeeds = withAuthRequired(function SavedFeedsImpl({}: Props) {
   )
 })
 
-const ListItem = observer(function ListItemImpl({
+function ListItem({
   feedUri,
   isPinned,
 }: {
@@ -269,7 +268,7 @@ const ListItem = observer(function ListItemImpl({
       </TouchableOpacity>
     </Pressable>
   )
-})
+}
 
 const styles = StyleSheet.create({
   desktopContainer: {
diff --git a/src/view/screens/Settings.tsx b/src/view/screens/Settings.tsx
index 3f7ef146a..916fd2a53 100644
--- a/src/view/screens/Settings.tsx
+++ b/src/view/screens/Settings.tsx
@@ -19,11 +19,9 @@ import {
   FontAwesomeIcon,
   FontAwesomeIconStyle,
 } from '@fortawesome/react-native-fontawesome'
-import {observer} from 'mobx-react-lite'
 import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
 import {withAuthRequired} from 'view/com/auth/withAuthRequired'
 import * as AppInfo from 'lib/app-info'
-import {useStores} from 'state/index'
 import {s, colors} from 'lib/styles'
 import {ScrollView} from '../com/util/Views'
 import {ViewHeader} from '../com/util/ViewHeader'
@@ -45,7 +43,7 @@ import {formatCount} from 'view/com/util/numeric/format'
 import Clipboard from '@react-native-clipboard/clipboard'
 import {makeProfileLink} from 'lib/routes/links'
 import {AccountDropdownBtn} from 'view/com/util/AccountDropdownBtn'
-import {logger} from '#/logger'
+import {RQKEY as RQKEY_PROFILE} from '#/state/queries/profile'
 import {useModalControls} from '#/state/modals'
 import {
   useSetMinimalShellMode,
@@ -69,6 +67,7 @@ import {useDebugHeaderSetting} from 'lib/api/debug-appview-proxy-header'
 import {STATUS_PAGE_URL} from 'lib/constants'
 import {Trans, msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
+import {useQueryClient} from '@tanstack/react-query'
 
 function SettingsAccountCard({account}: {account: SessionAccount}) {
   const pal = usePalette('default')
@@ -135,630 +134,620 @@ function SettingsAccountCard({account}: {account: SessionAccount}) {
 }
 
 type Props = NativeStackScreenProps<CommonNavigatorParams, 'Settings'>
-export const SettingsScreen = withAuthRequired(
-  observer(function Settings({}: Props) {
-    const colorMode = useColorMode()
-    const setColorMode = useSetColorMode()
-    const pal = usePalette('default')
-    const store = useStores()
-    const {_} = useLingui()
-    const setMinimalShellMode = useSetMinimalShellMode()
-    const requireAltTextEnabled = useRequireAltTextEnabled()
-    const setRequireAltTextEnabled = useSetRequireAltTextEnabled()
-    const onboardingDispatch = useOnboardingDispatch()
-    const navigation = useNavigation<NavigationProp>()
-    const {isMobile} = useWebMediaQueries()
-    const {screen, track} = useAnalytics()
-    const [debugHeaderEnabled, toggleDebugHeader] = useDebugHeaderSetting(
-      store.agent,
-    )
-    const {openModal} = useModalControls()
-    const {isSwitchingAccounts, accounts, currentAccount} = useSession()
-    const {clearCurrentAccount} = useSessionApi()
-    const {mutate: clearPreferences} = useClearPreferencesMutation()
-    const {data: invites} = useInviteCodesQuery()
-    const invitesAvailable = invites?.available?.length ?? 0
-
-    const primaryBg = useCustomPalette<ViewStyle>({
-      light: {backgroundColor: colors.blue0},
-      dark: {backgroundColor: colors.blue6},
-    })
-    const primaryText = useCustomPalette<TextStyle>({
-      light: {color: colors.blue3},
-      dark: {color: colors.blue2},
-    })
+export const SettingsScreen = withAuthRequired(function Settings({}: Props) {
+  const queryClient = useQueryClient()
+  const colorMode = useColorMode()
+  const setColorMode = useSetColorMode()
+  const pal = usePalette('default')
+  const {_} = useLingui()
+  const setMinimalShellMode = useSetMinimalShellMode()
+  const requireAltTextEnabled = useRequireAltTextEnabled()
+  const setRequireAltTextEnabled = useSetRequireAltTextEnabled()
+  const onboardingDispatch = useOnboardingDispatch()
+  const navigation = useNavigation<NavigationProp>()
+  const {isMobile} = useWebMediaQueries()
+  const {screen, track} = useAnalytics()
+  const {openModal} = useModalControls()
+  const {isSwitchingAccounts, accounts, currentAccount, agent} = useSession()
+  const {clearCurrentAccount} = useSessionApi()
+  const [debugHeaderEnabled, toggleDebugHeader] = useDebugHeaderSetting(agent)
+  const {mutate: clearPreferences} = useClearPreferencesMutation()
+  const {data: invites} = useInviteCodesQuery()
+  const invitesAvailable = invites?.available?.length ?? 0
+
+  const primaryBg = useCustomPalette<ViewStyle>({
+    light: {backgroundColor: colors.blue0},
+    dark: {backgroundColor: colors.blue6},
+  })
+  const primaryText = useCustomPalette<TextStyle>({
+    light: {color: colors.blue3},
+    dark: {color: colors.blue2},
+  })
+
+  const dangerBg = useCustomPalette<ViewStyle>({
+    light: {backgroundColor: colors.red1},
+    dark: {backgroundColor: colors.red7},
+  })
+  const dangerText = useCustomPalette<TextStyle>({
+    light: {color: colors.red4},
+    dark: {color: colors.red2},
+  })
+
+  useFocusEffect(
+    React.useCallback(() => {
+      screen('Settings')
+      setMinimalShellMode(false)
+    }, [screen, setMinimalShellMode]),
+  )
 
-    const dangerBg = useCustomPalette<ViewStyle>({
-      light: {backgroundColor: colors.red1},
-      dark: {backgroundColor: colors.red7},
-    })
-    const dangerText = useCustomPalette<TextStyle>({
-      light: {color: colors.red4},
-      dark: {color: colors.red2},
+  const onPressAddAccount = React.useCallback(() => {
+    track('Settings:AddAccountButtonClicked')
+    navigation.navigate('HomeTab')
+    navigation.dispatch(StackActions.popToTop())
+    clearCurrentAccount()
+  }, [track, navigation, clearCurrentAccount])
+
+  const onPressChangeHandle = React.useCallback(() => {
+    track('Settings:ChangeHandleButtonClicked')
+    openModal({
+      name: 'change-handle',
+      onChanged() {
+        if (currentAccount) {
+          // refresh my profile
+          queryClient.invalidateQueries({
+            queryKey: RQKEY_PROFILE(currentAccount.did),
+          })
+        }
+      },
     })
+  }, [track, queryClient, openModal, currentAccount])
 
-    useFocusEffect(
-      React.useCallback(() => {
-        screen('Settings')
-        setMinimalShellMode(false)
-      }, [screen, setMinimalShellMode]),
-    )
+  const onPressInviteCodes = React.useCallback(() => {
+    track('Settings:InvitecodesButtonClicked')
+    openModal({name: 'invite-codes'})
+  }, [track, openModal])
+
+  const onPressLanguageSettings = React.useCallback(() => {
+    navigation.navigate('LanguageSettings')
+  }, [navigation])
 
-    const onPressAddAccount = React.useCallback(() => {
-      track('Settings:AddAccountButtonClicked')
-      navigation.navigate('HomeTab')
-      navigation.dispatch(StackActions.popToTop())
-      clearCurrentAccount()
-    }, [track, navigation, clearCurrentAccount])
-
-    const onPressChangeHandle = React.useCallback(() => {
-      track('Settings:ChangeHandleButtonClicked')
-      openModal({
-        name: 'change-handle',
-        onChanged() {
-          store.session.reloadFromServer().then(
-            () => {
-              Toast.show('Your handle has been updated')
-            },
-            err => {
-              logger.error('Failed to reload from server after handle update', {
-                error: err,
-              })
-            },
-          )
-        },
-      })
-    }, [track, store, openModal])
-
-    const onPressInviteCodes = React.useCallback(() => {
-      track('Settings:InvitecodesButtonClicked')
-      openModal({name: 'invite-codes'})
-    }, [track, openModal])
-
-    const onPressLanguageSettings = React.useCallback(() => {
-      navigation.navigate('LanguageSettings')
-    }, [navigation])
-
-    const onPressDeleteAccount = React.useCallback(() => {
-      openModal({name: 'delete-account'})
-    }, [openModal])
-
-    const onPressResetPreferences = React.useCallback(async () => {
-      clearPreferences()
-    }, [clearPreferences])
-
-    const onPressResetOnboarding = React.useCallback(async () => {
-      onboardingDispatch({type: 'start'})
-      Toast.show('Onboarding reset')
-    }, [onboardingDispatch])
-
-    const onPressBuildInfo = React.useCallback(() => {
-      Clipboard.setString(
-        `Build version: ${AppInfo.appVersion}; Platform: ${Platform.OS}`,
-      )
-      Toast.show('Copied build version to clipboard')
-    }, [])
-
-    const openHomeFeedPreferences = React.useCallback(() => {
-      navigation.navigate('PreferencesHomeFeed')
-    }, [navigation])
-
-    const openThreadsPreferences = React.useCallback(() => {
-      navigation.navigate('PreferencesThreads')
-    }, [navigation])
-
-    const onPressAppPasswords = React.useCallback(() => {
-      navigation.navigate('AppPasswords')
-    }, [navigation])
-
-    const onPressSystemLog = React.useCallback(() => {
-      navigation.navigate('Log')
-    }, [navigation])
-
-    const onPressStorybook = React.useCallback(() => {
-      navigation.navigate('Debug')
-    }, [navigation])
-
-    const onPressSavedFeeds = React.useCallback(() => {
-      navigation.navigate('SavedFeeds')
-    }, [navigation])
-
-    const onPressStatusPage = React.useCallback(() => {
-      Linking.openURL(STATUS_PAGE_URL)
-    }, [])
-
-    return (
-      <View style={[s.hContentRegion]} testID="settingsScreen">
-        <ViewHeader title="Settings" />
-        <ScrollView
-          style={[s.hContentRegion]}
-          contentContainerStyle={isMobile && pal.viewLight}
-          scrollIndicatorInsets={{right: 1}}>
-          <View style={styles.spacer20} />
-          {currentAccount ? (
-            <>
-              <Text type="xl-bold" style={[pal.text, styles.heading]}>
-                <Trans>Account</Trans>
+  const onPressDeleteAccount = React.useCallback(() => {
+    openModal({name: 'delete-account'})
+  }, [openModal])
+
+  const onPressResetPreferences = React.useCallback(async () => {
+    clearPreferences()
+  }, [clearPreferences])
+
+  const onPressResetOnboarding = React.useCallback(async () => {
+    onboardingDispatch({type: 'start'})
+    Toast.show('Onboarding reset')
+  }, [onboardingDispatch])
+
+  const onPressBuildInfo = React.useCallback(() => {
+    Clipboard.setString(
+      `Build version: ${AppInfo.appVersion}; Platform: ${Platform.OS}`,
+    )
+    Toast.show('Copied build version to clipboard')
+  }, [])
+
+  const openHomeFeedPreferences = React.useCallback(() => {
+    navigation.navigate('PreferencesHomeFeed')
+  }, [navigation])
+
+  const openThreadsPreferences = React.useCallback(() => {
+    navigation.navigate('PreferencesThreads')
+  }, [navigation])
+
+  const onPressAppPasswords = React.useCallback(() => {
+    navigation.navigate('AppPasswords')
+  }, [navigation])
+
+  const onPressSystemLog = React.useCallback(() => {
+    navigation.navigate('Log')
+  }, [navigation])
+
+  const onPressStorybook = React.useCallback(() => {
+    navigation.navigate('Debug')
+  }, [navigation])
+
+  const onPressSavedFeeds = React.useCallback(() => {
+    navigation.navigate('SavedFeeds')
+  }, [navigation])
+
+  const onPressStatusPage = React.useCallback(() => {
+    Linking.openURL(STATUS_PAGE_URL)
+  }, [])
+
+  return (
+    <View style={[s.hContentRegion]} testID="settingsScreen">
+      <ViewHeader title="Settings" />
+      <ScrollView
+        style={[s.hContentRegion]}
+        contentContainerStyle={isMobile && pal.viewLight}
+        scrollIndicatorInsets={{right: 1}}>
+        <View style={styles.spacer20} />
+        {currentAccount ? (
+          <>
+            <Text type="xl-bold" style={[pal.text, styles.heading]}>
+              <Trans>Account</Trans>
+            </Text>
+            <View style={[styles.infoLine]}>
+              <Text type="lg-medium" style={pal.text}>
+                Email:{' '}
               </Text>
-              <View style={[styles.infoLine]}>
-                <Text type="lg-medium" style={pal.text}>
-                  Email:{' '}
-                </Text>
-                {currentAccount.emailConfirmed && (
-                  <>
-                    <FontAwesomeIcon
-                      icon="check"
-                      size={10}
-                      style={{color: colors.green3, marginRight: 2}}
-                    />
-                  </>
-                )}
-                <Text type="lg" style={pal.text}>
-                  {currentAccount.email}{' '}
+              {currentAccount.emailConfirmed && (
+                <>
+                  <FontAwesomeIcon
+                    icon="check"
+                    size={10}
+                    style={{color: colors.green3, marginRight: 2}}
+                  />
+                </>
+              )}
+              <Text type="lg" style={pal.text}>
+                {currentAccount.email}{' '}
+              </Text>
+              <Link onPress={() => openModal({name: 'change-email'})}>
+                <Text type="lg" style={pal.link}>
+                  <Trans>Change</Trans>
                 </Text>
-                <Link onPress={() => openModal({name: 'change-email'})}>
-                  <Text type="lg" style={pal.link}>
-                    <Trans>Change</Trans>
-                  </Text>
-                </Link>
-              </View>
-              <View style={[styles.infoLine]}>
-                <Text type="lg-medium" style={pal.text}>
-                  <Trans>Birthday:</Trans>{' '}
+              </Link>
+            </View>
+            <View style={[styles.infoLine]}>
+              <Text type="lg-medium" style={pal.text}>
+                <Trans>Birthday:</Trans>{' '}
+              </Text>
+              <Link onPress={() => openModal({name: 'birth-date-settings'})}>
+                <Text type="lg" style={pal.link}>
+                  <Trans>Show</Trans>
                 </Text>
-                <Link onPress={() => openModal({name: 'birth-date-settings'})}>
-                  <Text type="lg" style={pal.link}>
-                    <Trans>Show</Trans>
-                  </Text>
-                </Link>
-              </View>
-              <View style={styles.spacer20} />
-
-              {!currentAccount.emailConfirmed && <EmailConfirmationNotice />}
-            </>
-          ) : null}
-          <View style={[s.flexRow, styles.heading]}>
-            <Text type="xl-bold" style={pal.text}>
-              <Trans>Signed in as</Trans>
-            </Text>
-            <View style={s.flex1} />
+              </Link>
+            </View>
+            <View style={styles.spacer20} />
+
+            {!currentAccount.emailConfirmed && <EmailConfirmationNotice />}
+          </>
+        ) : null}
+        <View style={[s.flexRow, styles.heading]}>
+          <Text type="xl-bold" style={pal.text}>
+            <Trans>Signed in as</Trans>
+          </Text>
+          <View style={s.flex1} />
+        </View>
+
+        {isSwitchingAccounts ? (
+          <View style={[pal.view, styles.linkCard]}>
+            <ActivityIndicator />
           </View>
+        ) : (
+          <SettingsAccountCard account={currentAccount!} />
+        )}
 
-          {isSwitchingAccounts ? (
-            <View style={[pal.view, styles.linkCard]}>
-              <ActivityIndicator />
-            </View>
-          ) : (
-            <SettingsAccountCard account={currentAccount!} />
-          )}
+        {accounts
+          .filter(a => a.did !== currentAccount?.did)
+          .map(account => (
+            <SettingsAccountCard key={account.did} account={account} />
+          ))}
 
-          {accounts
-            .filter(a => a.did !== currentAccount?.did)
-            .map(account => (
-              <SettingsAccountCard key={account.did} account={account} />
-            ))}
+        <TouchableOpacity
+          testID="switchToNewAccountBtn"
+          style={[
+            styles.linkCard,
+            pal.view,
+            isSwitchingAccounts && styles.dimmed,
+          ]}
+          onPress={isSwitchingAccounts ? undefined : onPressAddAccount}
+          accessibilityRole="button"
+          accessibilityLabel={_(msg`Add account`)}
+          accessibilityHint="Create a new Bluesky account">
+          <View style={[styles.iconContainer, pal.btn]}>
+            <FontAwesomeIcon
+              icon="plus"
+              style={pal.text as FontAwesomeIconStyle}
+            />
+          </View>
+          <Text type="lg" style={pal.text}>
+            <Trans>Add account</Trans>
+          </Text>
+        </TouchableOpacity>
 
-          <TouchableOpacity
-            testID="switchToNewAccountBtn"
-            style={[
-              styles.linkCard,
-              pal.view,
-              isSwitchingAccounts && styles.dimmed,
-            ]}
-            onPress={isSwitchingAccounts ? undefined : onPressAddAccount}
-            accessibilityRole="button"
-            accessibilityLabel={_(msg`Add account`)}
-            accessibilityHint="Create a new Bluesky account">
-            <View style={[styles.iconContainer, pal.btn]}>
-              <FontAwesomeIcon
-                icon="plus"
-                style={pal.text as FontAwesomeIconStyle}
-              />
-            </View>
-            <Text type="lg" style={pal.text}>
-              <Trans>Add account</Trans>
-            </Text>
-          </TouchableOpacity>
+        <View style={styles.spacer20} />
 
-          <View style={styles.spacer20} />
+        <Text type="xl-bold" style={[pal.text, styles.heading]}>
+          <Trans>Invite a Friend</Trans>
+        </Text>
 
-          <Text type="xl-bold" style={[pal.text, styles.heading]}>
-            <Trans>Invite a Friend</Trans>
+        <TouchableOpacity
+          testID="inviteFriendBtn"
+          style={[
+            styles.linkCard,
+            pal.view,
+            isSwitchingAccounts && styles.dimmed,
+          ]}
+          onPress={isSwitchingAccounts ? undefined : onPressInviteCodes}
+          accessibilityRole="button"
+          accessibilityLabel={_(msg`Invite`)}
+          accessibilityHint="Opens invite code list">
+          <View
+            style={[
+              styles.iconContainer,
+              invitesAvailable > 0 ? primaryBg : pal.btn,
+            ]}>
+            <FontAwesomeIcon
+              icon="ticket"
+              style={
+                (invitesAvailable > 0
+                  ? primaryText
+                  : pal.text) as FontAwesomeIconStyle
+              }
+            />
+          </View>
+          <Text type="lg" style={invitesAvailable > 0 ? pal.link : pal.text}>
+            {formatCount(invitesAvailable)} invite{' '}
+            {pluralize(invitesAvailable, 'code')} available
           </Text>
+        </TouchableOpacity>
 
-          <TouchableOpacity
-            testID="inviteFriendBtn"
-            style={[
-              styles.linkCard,
-              pal.view,
-              isSwitchingAccounts && styles.dimmed,
-            ]}
-            onPress={isSwitchingAccounts ? undefined : onPressInviteCodes}
-            accessibilityRole="button"
-            accessibilityLabel={_(msg`Invite`)}
-            accessibilityHint="Opens invite code list">
-            <View
-              style={[
-                styles.iconContainer,
-                invitesAvailable > 0 ? primaryBg : pal.btn,
-              ]}>
-              <FontAwesomeIcon
-                icon="ticket"
-                style={
-                  (invitesAvailable > 0
-                    ? primaryText
-                    : pal.text) as FontAwesomeIconStyle
-                }
-              />
-            </View>
-            <Text type="lg" style={invitesAvailable > 0 ? pal.link : pal.text}>
-              {formatCount(invitesAvailable)} invite{' '}
-              {pluralize(invitesAvailable, 'code')} available
-            </Text>
-          </TouchableOpacity>
+        <View style={styles.spacer20} />
 
-          <View style={styles.spacer20} />
+        <Text type="xl-bold" style={[pal.text, styles.heading]}>
+          <Trans>Accessibility</Trans>
+        </Text>
+        <View style={[pal.view, styles.toggleCard]}>
+          <ToggleButton
+            type="default-light"
+            label="Require alt text before posting"
+            labelType="lg"
+            isSelected={requireAltTextEnabled}
+            onPress={() => setRequireAltTextEnabled(!requireAltTextEnabled)}
+          />
+        </View>
 
-          <Text type="xl-bold" style={[pal.text, styles.heading]}>
-            <Trans>Accessibility</Trans>
-          </Text>
-          <View style={[pal.view, styles.toggleCard]}>
-            <ToggleButton
-              type="default-light"
-              label="Require alt text before posting"
-              labelType="lg"
-              isSelected={requireAltTextEnabled}
-              onPress={() => setRequireAltTextEnabled(!requireAltTextEnabled)}
+        <View style={styles.spacer20} />
+
+        <Text type="xl-bold" style={[pal.text, styles.heading]}>
+          <Trans>Appearance</Trans>
+        </Text>
+        <View>
+          <View style={[styles.linkCard, pal.view, styles.selectableBtns]}>
+            <SelectableBtn
+              selected={colorMode === 'system'}
+              label="System"
+              left
+              onSelect={() => setColorMode('system')}
+              accessibilityHint="Set color theme to system setting"
+            />
+            <SelectableBtn
+              selected={colorMode === 'light'}
+              label="Light"
+              onSelect={() => setColorMode('light')}
+              accessibilityHint="Set color theme to light"
+            />
+            <SelectableBtn
+              selected={colorMode === 'dark'}
+              label="Dark"
+              right
+              onSelect={() => setColorMode('dark')}
+              accessibilityHint="Set color theme to dark"
             />
           </View>
+        </View>
+        <View style={styles.spacer20} />
 
-          <View style={styles.spacer20} />
-
-          <Text type="xl-bold" style={[pal.text, styles.heading]}>
-            <Trans>Appearance</Trans>
+        <Text type="xl-bold" style={[pal.text, styles.heading]}>
+          <Trans>Basics</Trans>
+        </Text>
+        <TouchableOpacity
+          testID="preferencesHomeFeedButton"
+          style={[
+            styles.linkCard,
+            pal.view,
+            isSwitchingAccounts && styles.dimmed,
+          ]}
+          onPress={openHomeFeedPreferences}
+          accessibilityRole="button"
+          accessibilityHint=""
+          accessibilityLabel={_(msg`Opens the home feed preferences`)}>
+          <View style={[styles.iconContainer, pal.btn]}>
+            <FontAwesomeIcon
+              icon="sliders"
+              style={pal.text as FontAwesomeIconStyle}
+            />
+          </View>
+          <Text type="lg" style={pal.text}>
+            <Trans>Home Feed Preferences</Trans>
           </Text>
-          <View>
-            <View style={[styles.linkCard, pal.view, styles.selectableBtns]}>
-              <SelectableBtn
-                selected={colorMode === 'system'}
-                label="System"
-                left
-                onSelect={() => setColorMode('system')}
-                accessibilityHint="Set color theme to system setting"
-              />
-              <SelectableBtn
-                selected={colorMode === 'light'}
-                label="Light"
-                onSelect={() => setColorMode('light')}
-                accessibilityHint="Set color theme to light"
-              />
-              <SelectableBtn
-                selected={colorMode === 'dark'}
-                label="Dark"
-                right
-                onSelect={() => setColorMode('dark')}
-                accessibilityHint="Set color theme to dark"
-              />
-            </View>
+        </TouchableOpacity>
+        <TouchableOpacity
+          testID="preferencesThreadsButton"
+          style={[
+            styles.linkCard,
+            pal.view,
+            isSwitchingAccounts && styles.dimmed,
+          ]}
+          onPress={openThreadsPreferences}
+          accessibilityRole="button"
+          accessibilityHint=""
+          accessibilityLabel={_(msg`Opens the threads preferences`)}>
+          <View style={[styles.iconContainer, pal.btn]}>
+            <FontAwesomeIcon
+              icon={['far', 'comments']}
+              style={pal.text as FontAwesomeIconStyle}
+              size={18}
+            />
           </View>
-          <View style={styles.spacer20} />
-
-          <Text type="xl-bold" style={[pal.text, styles.heading]}>
-            <Trans>Basics</Trans>
+          <Text type="lg" style={pal.text}>
+            <Trans>Thread Preferences</Trans>
           </Text>
-          <TouchableOpacity
-            testID="preferencesHomeFeedButton"
-            style={[
-              styles.linkCard,
-              pal.view,
-              isSwitchingAccounts && styles.dimmed,
-            ]}
-            onPress={openHomeFeedPreferences}
-            accessibilityRole="button"
-            accessibilityHint=""
-            accessibilityLabel={_(msg`Opens the home feed preferences`)}>
-            <View style={[styles.iconContainer, pal.btn]}>
-              <FontAwesomeIcon
-                icon="sliders"
-                style={pal.text as FontAwesomeIconStyle}
-              />
-            </View>
-            <Text type="lg" style={pal.text}>
-              <Trans>Home Feed Preferences</Trans>
-            </Text>
-          </TouchableOpacity>
-          <TouchableOpacity
-            testID="preferencesThreadsButton"
-            style={[
-              styles.linkCard,
-              pal.view,
-              isSwitchingAccounts && styles.dimmed,
-            ]}
-            onPress={openThreadsPreferences}
-            accessibilityRole="button"
-            accessibilityHint=""
-            accessibilityLabel={_(msg`Opens the threads preferences`)}>
-            <View style={[styles.iconContainer, pal.btn]}>
-              <FontAwesomeIcon
-                icon={['far', 'comments']}
-                style={pal.text as FontAwesomeIconStyle}
-                size={18}
-              />
-            </View>
-            <Text type="lg" style={pal.text}>
-              <Trans>Thread Preferences</Trans>
-            </Text>
-          </TouchableOpacity>
-          <TouchableOpacity
-            testID="savedFeedsBtn"
-            style={[
-              styles.linkCard,
-              pal.view,
-              isSwitchingAccounts && styles.dimmed,
-            ]}
-            accessibilityHint="My Saved Feeds"
-            accessibilityLabel={_(msg`Opens screen with all saved feeds`)}
-            onPress={onPressSavedFeeds}>
-            <View style={[styles.iconContainer, pal.btn]}>
-              <HashtagIcon style={pal.text} size={18} strokeWidth={3} />
-            </View>
-            <Text type="lg" style={pal.text}>
-              <Trans>My Saved Feeds</Trans>
-            </Text>
-          </TouchableOpacity>
-          <TouchableOpacity
-            testID="languageSettingsBtn"
-            style={[
-              styles.linkCard,
-              pal.view,
-              isSwitchingAccounts && styles.dimmed,
-            ]}
-            onPress={isSwitchingAccounts ? undefined : onPressLanguageSettings}
-            accessibilityRole="button"
-            accessibilityHint="Language settings"
-            accessibilityLabel={_(msg`Opens configurable language settings`)}>
-            <View style={[styles.iconContainer, pal.btn]}>
-              <FontAwesomeIcon
-                icon="language"
-                style={pal.text as FontAwesomeIconStyle}
-              />
-            </View>
-            <Text type="lg" style={pal.text}>
-              <Trans>Languages</Trans>
-            </Text>
-          </TouchableOpacity>
-          <TouchableOpacity
-            testID="moderationBtn"
-            style={[
-              styles.linkCard,
-              pal.view,
-              isSwitchingAccounts && styles.dimmed,
-            ]}
-            onPress={
-              isSwitchingAccounts
-                ? undefined
-                : () => navigation.navigate('Moderation')
-            }
-            accessibilityRole="button"
-            accessibilityHint=""
-            accessibilityLabel={_(msg`Opens moderation settings`)}>
-            <View style={[styles.iconContainer, pal.btn]}>
-              <HandIcon style={pal.text} size={18} strokeWidth={6} />
-            </View>
-            <Text type="lg" style={pal.text}>
-              <Trans>Moderation</Trans>
-            </Text>
-          </TouchableOpacity>
-          <View style={styles.spacer20} />
-
-          <Text type="xl-bold" style={[pal.text, styles.heading]}>
-            <Trans>Advanced</Trans>
+        </TouchableOpacity>
+        <TouchableOpacity
+          testID="savedFeedsBtn"
+          style={[
+            styles.linkCard,
+            pal.view,
+            isSwitchingAccounts && styles.dimmed,
+          ]}
+          accessibilityHint="My Saved Feeds"
+          accessibilityLabel={_(msg`Opens screen with all saved feeds`)}
+          onPress={onPressSavedFeeds}>
+          <View style={[styles.iconContainer, pal.btn]}>
+            <HashtagIcon style={pal.text} size={18} strokeWidth={3} />
+          </View>
+          <Text type="lg" style={pal.text}>
+            <Trans>My Saved Feeds</Trans>
           </Text>
-          <TouchableOpacity
-            testID="appPasswordBtn"
-            style={[
-              styles.linkCard,
-              pal.view,
-              isSwitchingAccounts && styles.dimmed,
-            ]}
-            onPress={onPressAppPasswords}
-            accessibilityRole="button"
-            accessibilityHint="Open app password settings"
-            accessibilityLabel={_(msg`Opens the app password settings page`)}>
-            <View style={[styles.iconContainer, pal.btn]}>
-              <FontAwesomeIcon
-                icon="lock"
-                style={pal.text as FontAwesomeIconStyle}
-              />
-            </View>
-            <Text type="lg" style={pal.text}>
-              <Trans>App passwords</Trans>
-            </Text>
-          </TouchableOpacity>
-          <TouchableOpacity
-            testID="changeHandleBtn"
-            style={[
-              styles.linkCard,
-              pal.view,
-              isSwitchingAccounts && styles.dimmed,
-            ]}
-            onPress={isSwitchingAccounts ? undefined : onPressChangeHandle}
-            accessibilityRole="button"
-            accessibilityLabel={_(msg`Change handle`)}
-            accessibilityHint="Choose a new Bluesky username or create">
-            <View style={[styles.iconContainer, pal.btn]}>
-              <FontAwesomeIcon
-                icon="at"
-                style={pal.text as FontAwesomeIconStyle}
-              />
-            </View>
-            <Text type="lg" style={pal.text} numberOfLines={1}>
-              <Trans>Change handle</Trans>
-            </Text>
-          </TouchableOpacity>
-          <View style={styles.spacer20} />
-          <Text type="xl-bold" style={[pal.text, styles.heading]}>
-            <Trans>Danger Zone</Trans>
+        </TouchableOpacity>
+        <TouchableOpacity
+          testID="languageSettingsBtn"
+          style={[
+            styles.linkCard,
+            pal.view,
+            isSwitchingAccounts && styles.dimmed,
+          ]}
+          onPress={isSwitchingAccounts ? undefined : onPressLanguageSettings}
+          accessibilityRole="button"
+          accessibilityHint="Language settings"
+          accessibilityLabel={_(msg`Opens configurable language settings`)}>
+          <View style={[styles.iconContainer, pal.btn]}>
+            <FontAwesomeIcon
+              icon="language"
+              style={pal.text as FontAwesomeIconStyle}
+            />
+          </View>
+          <Text type="lg" style={pal.text}>
+            <Trans>Languages</Trans>
           </Text>
-          <TouchableOpacity
-            style={[pal.view, styles.linkCard]}
-            onPress={onPressDeleteAccount}
-            accessible={true}
-            accessibilityRole="button"
-            accessibilityLabel={_(msg`Delete account`)}
-            accessibilityHint="Opens modal for account deletion confirmation. Requires email code.">
-            <View style={[styles.iconContainer, dangerBg]}>
-              <FontAwesomeIcon
-                icon={['far', 'trash-can']}
-                style={dangerText as FontAwesomeIconStyle}
-                size={18}
-              />
-            </View>
-            <Text type="lg" style={dangerText}>
-              <Trans>Delete my account…</Trans>
-            </Text>
-          </TouchableOpacity>
-          <View style={styles.spacer20} />
-          <Text type="xl-bold" style={[pal.text, styles.heading]}>
-            <Trans>Developer Tools</Trans>
+        </TouchableOpacity>
+        <TouchableOpacity
+          testID="moderationBtn"
+          style={[
+            styles.linkCard,
+            pal.view,
+            isSwitchingAccounts && styles.dimmed,
+          ]}
+          onPress={
+            isSwitchingAccounts
+              ? undefined
+              : () => navigation.navigate('Moderation')
+          }
+          accessibilityRole="button"
+          accessibilityHint=""
+          accessibilityLabel={_(msg`Opens moderation settings`)}>
+          <View style={[styles.iconContainer, pal.btn]}>
+            <HandIcon style={pal.text} size={18} strokeWidth={6} />
+          </View>
+          <Text type="lg" style={pal.text}>
+            <Trans>Moderation</Trans>
           </Text>
-          <TouchableOpacity
-            style={[pal.view, styles.linkCardNoIcon]}
-            onPress={onPressSystemLog}
-            accessibilityRole="button"
-            accessibilityHint="Open system log"
-            accessibilityLabel={_(msg`Opens the system log page`)}>
-            <Text type="lg" style={pal.text}>
-              <Trans>System log</Trans>
-            </Text>
-          </TouchableOpacity>
-          {__DEV__ ? (
-            <ToggleButton
-              type="default-light"
-              label="Experiment: Use AppView Proxy"
-              isSelected={debugHeaderEnabled}
-              onPress={toggleDebugHeader}
+        </TouchableOpacity>
+        <View style={styles.spacer20} />
+
+        <Text type="xl-bold" style={[pal.text, styles.heading]}>
+          <Trans>Advanced</Trans>
+        </Text>
+        <TouchableOpacity
+          testID="appPasswordBtn"
+          style={[
+            styles.linkCard,
+            pal.view,
+            isSwitchingAccounts && styles.dimmed,
+          ]}
+          onPress={onPressAppPasswords}
+          accessibilityRole="button"
+          accessibilityHint="Open app password settings"
+          accessibilityLabel={_(msg`Opens the app password settings page`)}>
+          <View style={[styles.iconContainer, pal.btn]}>
+            <FontAwesomeIcon
+              icon="lock"
+              style={pal.text as FontAwesomeIconStyle}
             />
-          ) : null}
-          {__DEV__ ? (
-            <>
-              <TouchableOpacity
-                style={[pal.view, styles.linkCardNoIcon]}
-                onPress={onPressStorybook}
-                accessibilityRole="button"
-                accessibilityHint="Open storybook page"
-                accessibilityLabel={_(msg`Opens the storybook page`)}>
-                <Text type="lg" style={pal.text}>
-                  <Trans>Storybook</Trans>
-                </Text>
-              </TouchableOpacity>
-              <TouchableOpacity
-                style={[pal.view, styles.linkCardNoIcon]}
-                onPress={onPressResetPreferences}
-                accessibilityRole="button"
-                accessibilityHint="Reset preferences"
-                accessibilityLabel={_(msg`Resets the preferences state`)}>
-                <Text type="lg" style={pal.text}>
-                  <Trans>Reset preferences state</Trans>
-                </Text>
-              </TouchableOpacity>
-              <TouchableOpacity
-                style={[pal.view, styles.linkCardNoIcon]}
-                onPress={onPressResetOnboarding}
-                accessibilityRole="button"
-                accessibilityHint="Reset onboarding"
-                accessibilityLabel={_(msg`Resets the onboarding state`)}>
-                <Text type="lg" style={pal.text}>
-                  <Trans>Reset onboarding state</Trans>
-                </Text>
-              </TouchableOpacity>
-            </>
-          ) : null}
-          <View style={[styles.footer]}>
+          </View>
+          <Text type="lg" style={pal.text}>
+            <Trans>App passwords</Trans>
+          </Text>
+        </TouchableOpacity>
+        <TouchableOpacity
+          testID="changeHandleBtn"
+          style={[
+            styles.linkCard,
+            pal.view,
+            isSwitchingAccounts && styles.dimmed,
+          ]}
+          onPress={isSwitchingAccounts ? undefined : onPressChangeHandle}
+          accessibilityRole="button"
+          accessibilityLabel={_(msg`Change handle`)}
+          accessibilityHint="Choose a new Bluesky username or create">
+          <View style={[styles.iconContainer, pal.btn]}>
+            <FontAwesomeIcon
+              icon="at"
+              style={pal.text as FontAwesomeIconStyle}
+            />
+          </View>
+          <Text type="lg" style={pal.text} numberOfLines={1}>
+            <Trans>Change handle</Trans>
+          </Text>
+        </TouchableOpacity>
+        <View style={styles.spacer20} />
+        <Text type="xl-bold" style={[pal.text, styles.heading]}>
+          <Trans>Danger Zone</Trans>
+        </Text>
+        <TouchableOpacity
+          style={[pal.view, styles.linkCard]}
+          onPress={onPressDeleteAccount}
+          accessible={true}
+          accessibilityRole="button"
+          accessibilityLabel={_(msg`Delete account`)}
+          accessibilityHint="Opens modal for account deletion confirmation. Requires email code.">
+          <View style={[styles.iconContainer, dangerBg]}>
+            <FontAwesomeIcon
+              icon={['far', 'trash-can']}
+              style={dangerText as FontAwesomeIconStyle}
+              size={18}
+            />
+          </View>
+          <Text type="lg" style={dangerText}>
+            <Trans>Delete my account…</Trans>
+          </Text>
+        </TouchableOpacity>
+        <View style={styles.spacer20} />
+        <Text type="xl-bold" style={[pal.text, styles.heading]}>
+          <Trans>Developer Tools</Trans>
+        </Text>
+        <TouchableOpacity
+          style={[pal.view, styles.linkCardNoIcon]}
+          onPress={onPressSystemLog}
+          accessibilityRole="button"
+          accessibilityHint="Open system log"
+          accessibilityLabel={_(msg`Opens the system log page`)}>
+          <Text type="lg" style={pal.text}>
+            <Trans>System log</Trans>
+          </Text>
+        </TouchableOpacity>
+        {__DEV__ ? (
+          <ToggleButton
+            type="default-light"
+            label="Experiment: Use AppView Proxy"
+            isSelected={debugHeaderEnabled}
+            onPress={toggleDebugHeader}
+          />
+        ) : null}
+        {__DEV__ ? (
+          <>
             <TouchableOpacity
+              style={[pal.view, styles.linkCardNoIcon]}
+              onPress={onPressStorybook}
               accessibilityRole="button"
-              onPress={onPressBuildInfo}>
-              <Text type="sm" style={[styles.buildInfo, pal.textLight]}>
-                <Trans>
-                  Build version {AppInfo.appVersion} {AppInfo.updateChannel}
-                </Trans>
+              accessibilityHint="Open storybook page"
+              accessibilityLabel={_(msg`Opens the storybook page`)}>
+              <Text type="lg" style={pal.text}>
+                <Trans>Storybook</Trans>
               </Text>
             </TouchableOpacity>
-            <Text type="sm" style={[pal.textLight]}>
-              &middot; &nbsp;
-            </Text>
             <TouchableOpacity
+              style={[pal.view, styles.linkCardNoIcon]}
+              onPress={onPressResetPreferences}
               accessibilityRole="button"
-              onPress={onPressStatusPage}>
-              <Text type="sm" style={[styles.buildInfo, pal.textLight]}>
-                <Trans>Status page</Trans>
+              accessibilityHint="Reset preferences"
+              accessibilityLabel={_(msg`Resets the preferences state`)}>
+              <Text type="lg" style={pal.text}>
+                <Trans>Reset preferences state</Trans>
               </Text>
             </TouchableOpacity>
-          </View>
-          <View style={s.footerSpacer} />
-        </ScrollView>
-      </View>
-    )
-  }),
-)
-
-const EmailConfirmationNotice = observer(
-  function EmailConfirmationNoticeImpl() {
-    const pal = usePalette('default')
-    const palInverted = usePalette('inverted')
-    const {_} = useLingui()
-    const {isMobile} = useWebMediaQueries()
-    const {openModal} = useModalControls()
-
-    return (
-      <View style={{marginBottom: 20}}>
-        <Text type="xl-bold" style={[pal.text, styles.heading]}>
-          <Trans>Verify email</Trans>
-        </Text>
-        <View
-          style={[
-            {
-              paddingVertical: isMobile ? 12 : 0,
-              paddingHorizontal: 18,
-            },
-            pal.view,
-          ]}>
-          <View style={{flexDirection: 'row', marginBottom: 8}}>
-            <Pressable
-              style={[
-                palInverted.view,
-                {
-                  flexDirection: 'row',
-                  gap: 6,
-                  borderRadius: 6,
-                  paddingHorizontal: 12,
-                  paddingVertical: 10,
-                  alignItems: 'center',
-                },
-                isMobile && {flex: 1},
-              ]}
+            <TouchableOpacity
+              style={[pal.view, styles.linkCardNoIcon]}
+              onPress={onPressResetOnboarding}
               accessibilityRole="button"
-              accessibilityLabel={_(msg`Verify my email`)}
-              accessibilityHint=""
-              onPress={() => openModal({name: 'verify-email'})}>
-              <FontAwesomeIcon
-                icon="envelope"
-                color={palInverted.colors.text}
-                size={16}
-              />
-              <Text type="button" style={palInverted.text}>
-                <Trans>Verify My Email</Trans>
+              accessibilityHint="Reset onboarding"
+              accessibilityLabel={_(msg`Resets the onboarding state`)}>
+              <Text type="lg" style={pal.text}>
+                <Trans>Reset onboarding state</Trans>
               </Text>
-            </Pressable>
-          </View>
-          <Text style={pal.textLight}>
-            <Trans>Protect your account by verifying your email.</Trans>
+            </TouchableOpacity>
+          </>
+        ) : null}
+        <View style={[styles.footer]}>
+          <TouchableOpacity
+            accessibilityRole="button"
+            onPress={onPressBuildInfo}>
+            <Text type="sm" style={[styles.buildInfo, pal.textLight]}>
+              <Trans>
+                Build version {AppInfo.appVersion} {AppInfo.updateChannel}
+              </Trans>
+            </Text>
+          </TouchableOpacity>
+          <Text type="sm" style={[pal.textLight]}>
+            &middot; &nbsp;
           </Text>
+          <TouchableOpacity
+            accessibilityRole="button"
+            onPress={onPressStatusPage}>
+            <Text type="sm" style={[styles.buildInfo, pal.textLight]}>
+              <Trans>Status page</Trans>
+            </Text>
+          </TouchableOpacity>
         </View>
+        <View style={s.footerSpacer} />
+      </ScrollView>
+    </View>
+  )
+})
+
+function EmailConfirmationNotice() {
+  const pal = usePalette('default')
+  const palInverted = usePalette('inverted')
+  const {_} = useLingui()
+  const {isMobile} = useWebMediaQueries()
+  const {openModal} = useModalControls()
+
+  return (
+    <View style={{marginBottom: 20}}>
+      <Text type="xl-bold" style={[pal.text, styles.heading]}>
+        <Trans>Verify email</Trans>
+      </Text>
+      <View
+        style={[
+          {
+            paddingVertical: isMobile ? 12 : 0,
+            paddingHorizontal: 18,
+          },
+          pal.view,
+        ]}>
+        <View style={{flexDirection: 'row', marginBottom: 8}}>
+          <Pressable
+            style={[
+              palInverted.view,
+              {
+                flexDirection: 'row',
+                gap: 6,
+                borderRadius: 6,
+                paddingHorizontal: 12,
+                paddingVertical: 10,
+                alignItems: 'center',
+              },
+              isMobile && {flex: 1},
+            ]}
+            accessibilityRole="button"
+            accessibilityLabel={_(msg`Verify my email`)}
+            accessibilityHint=""
+            onPress={() => openModal({name: 'verify-email'})}>
+            <FontAwesomeIcon
+              icon="envelope"
+              color={palInverted.colors.text}
+              size={16}
+            />
+            <Text type="button" style={palInverted.text}>
+              <Trans>Verify My Email</Trans>
+            </Text>
+          </Pressable>
+        </View>
+        <Text style={pal.textLight}>
+          <Trans>Protect your account by verifying your email.</Trans>
+        </Text>
       </View>
-    )
-  },
-)
+    </View>
+  )
+}
 
 const styles = StyleSheet.create({
   dimmed: {