about summary refs log tree commit diff
path: root/src/view/com
diff options
context:
space:
mode:
authorPaul Frazee <pfrazee@gmail.com>2023-11-16 12:53:43 -0800
committerGitHub <noreply@github.com>2023-11-16 12:53:43 -0800
commit54faa7e176ed2f8644ef4941c8a65522107a84c1 (patch)
tree336d69d37809041dcc38a932975fcf438ac60dfd /src/view/com
parente637798e05ba3bfc1c78be1b0f70e8b0ac22554d (diff)
downloadvoidsky-54faa7e176ed2f8644ef4941c8a65522107a84c1.tar.zst
Remove deprecated models and mobx usage (react-query refactor) (#1934)
* Update login page to use service query

* Update modal to use session instead of store

* Move image sizes cache off store

* Update settings to no longer use store

* Update link-meta fetch to use agent instead of rootstore

* Remove deprecated resolveName()

* Delete deprecated link-metas cache

* Delete deprecated posts cache

* Delete all remaining mobx models, including the root store

* Strip out unused mobx observer wrappers
Diffstat (limited to 'src/view/com')
-rw-r--r--src/view/com/auth/LoggedOut.tsx5
-rw-r--r--src/view/com/auth/Onboarding.tsx5
-rw-r--r--src/view/com/auth/create/Policies.tsx4
-rw-r--r--src/view/com/auth/create/Step1.tsx2
-rw-r--r--src/view/com/auth/login/ForgotPasswordForm.tsx4
-rw-r--r--src/view/com/auth/login/Login.tsx54
-rw-r--r--src/view/com/auth/login/LoginForm.tsx4
-rw-r--r--src/view/com/auth/onboarding/RecommendedFeeds.tsx7
-rw-r--r--src/view/com/auth/onboarding/RecommendedFeedsItem.tsx5
-rw-r--r--src/view/com/auth/onboarding/RecommendedFollows.tsx7
-rw-r--r--src/view/com/auth/onboarding/WelcomeDesktop.tsx7
-rw-r--r--src/view/com/auth/onboarding/WelcomeMobile.tsx8
-rw-r--r--src/view/com/auth/withAuthRequired.tsx5
-rw-r--r--src/view/com/composer/labels/LabelsBtn.tsx5
-rw-r--r--src/view/com/composer/select-language/SelectLangBtn.tsx5
-rw-r--r--src/view/com/composer/text-input/mobile/Autocomplete.tsx5
-rw-r--r--src/view/com/composer/useExternalLinkFetch.ts12
-rw-r--r--src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx1
-rw-r--r--src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx1
-rw-r--r--src/view/com/modals/BirthDateSettings.tsx5
-rw-r--r--src/view/com/modals/ChangeEmail.tsx5
-rw-r--r--src/view/com/modals/ContentFilteringSettings.tsx145
-rw-r--r--src/view/com/modals/InviteCodes.tsx5
-rw-r--r--src/view/com/modals/LinkWarning.tsx11
-rw-r--r--src/view/com/modals/Modal.tsx5
-rw-r--r--src/view/com/modals/Modal.web.tsx5
-rw-r--r--src/view/com/modals/SelfLabel.tsx5
-rw-r--r--src/view/com/modals/ServerInput.tsx2
-rw-r--r--src/view/com/modals/VerifyEmail.tsx9
-rw-r--r--src/view/com/modals/lang-settings/LanguageToggle.tsx5
-rw-r--r--src/view/com/modals/lang-settings/PostLanguagesSettings.tsx5
-rw-r--r--src/view/com/modals/report/Modal.tsx6
-rw-r--r--src/view/com/pager/FeedsTabBar.web.tsx9
-rw-r--r--src/view/com/pager/FeedsTabBarMobile.tsx5
-rw-r--r--src/view/com/posts/FeedSlice.tsx5
-rw-r--r--src/view/com/profile/ProfileCard.tsx5
-rw-r--r--src/view/com/profile/ProfileSubpageHeader.tsx7
-rw-r--r--src/view/com/util/PostMeta.tsx5
-rw-r--r--src/view/com/util/SimpleViewHeader.tsx5
-rw-r--r--src/view/com/util/ViewHeader.tsx9
-rw-r--r--src/view/com/util/fab/FABInner.tsx9
-rw-r--r--src/view/com/util/images/AutoSizedImage.tsx9
-rw-r--r--src/view/com/util/load-latest/LoadLatestBtn.tsx5
-rw-r--r--src/view/com/util/post-embeds/ListEmbed.tsx5
44 files changed, 183 insertions, 254 deletions
diff --git a/src/view/com/auth/LoggedOut.tsx b/src/view/com/auth/LoggedOut.tsx
index 0d8172964..3505e86af 100644
--- a/src/view/com/auth/LoggedOut.tsx
+++ b/src/view/com/auth/LoggedOut.tsx
@@ -1,6 +1,5 @@
 import React from 'react'
 import {SafeAreaView} from 'react-native'
-import {observer} from 'mobx-react-lite'
 import {Login} from 'view/com/auth/login/Login'
 import {CreateAccount} from 'view/com/auth/create/CreateAccount'
 import {ErrorBoundary} from 'view/com/util/ErrorBoundary'
@@ -16,7 +15,7 @@ enum ScreenState {
   S_CreateAccount,
 }
 
-export const LoggedOut = observer(function LoggedOutImpl() {
+export function LoggedOut() {
   const pal = usePalette('default')
   const setMinimalShellMode = useSetMinimalShellMode()
   const {screen} = useAnalytics()
@@ -58,4 +57,4 @@ export const LoggedOut = observer(function LoggedOutImpl() {
       </ErrorBoundary>
     </SafeAreaView>
   )
-})
+}
diff --git a/src/view/com/auth/Onboarding.tsx b/src/view/com/auth/Onboarding.tsx
index 994f4c149..3a17eb4f8 100644
--- a/src/view/com/auth/Onboarding.tsx
+++ b/src/view/com/auth/Onboarding.tsx
@@ -1,6 +1,5 @@
 import React from 'react'
 import {SafeAreaView} from 'react-native'
-import {observer} from 'mobx-react-lite'
 import {ErrorBoundary} from 'view/com/util/ErrorBoundary'
 import {s} from 'lib/styles'
 import {usePalette} from 'lib/hooks/usePalette'
@@ -10,7 +9,7 @@ import {RecommendedFollows} from './onboarding/RecommendedFollows'
 import {useSetMinimalShellMode} from '#/state/shell/minimal-mode'
 import {useOnboardingState, useOnboardingDispatch} from '#/state/shell'
 
-export const Onboarding = observer(function OnboardingImpl() {
+export function Onboarding() {
   const pal = usePalette('default')
   const setMinimalShellMode = useSetMinimalShellMode()
   const onboardingState = useOnboardingState()
@@ -38,4 +37,4 @@ export const Onboarding = observer(function OnboardingImpl() {
       </ErrorBoundary>
     </SafeAreaView>
   )
-})
+}
diff --git a/src/view/com/auth/create/Policies.tsx b/src/view/com/auth/create/Policies.tsx
index 7d10f32fc..a52f07531 100644
--- a/src/view/com/auth/create/Policies.tsx
+++ b/src/view/com/auth/create/Policies.tsx
@@ -4,12 +4,14 @@ import {
   FontAwesomeIcon,
   FontAwesomeIconStyle,
 } from '@fortawesome/react-native-fontawesome'
+import {ComAtprotoServerDescribeServer} from '@atproto/api'
 import {TextLink} from '../../util/Link'
 import {Text} from '../../util/text/Text'
 import {s, colors} from 'lib/styles'
-import {ServiceDescription} from 'state/models/session'
 import {usePalette} from 'lib/hooks/usePalette'
 
+type ServiceDescription = ComAtprotoServerDescribeServer.OutputSchema
+
 export const Policies = ({
   serviceDescription,
   needsGuardian,
diff --git a/src/view/com/auth/create/Step1.tsx b/src/view/com/auth/create/Step1.tsx
index ab47b411f..c9d19e868 100644
--- a/src/view/com/auth/create/Step1.tsx
+++ b/src/view/com/auth/create/Step1.tsx
@@ -13,7 +13,7 @@ import {ErrorMessage} from 'view/com/util/error/ErrorMessage'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 
-import {LOCAL_DEV_SERVICE, STAGING_SERVICE, PROD_SERVICE} from 'state/index'
+import {LOCAL_DEV_SERVICE, STAGING_SERVICE, PROD_SERVICE} from 'lib/constants'
 import {LOGIN_INCLUDE_DEV_SERVERS} from 'lib/build-flags'
 
 /** STEP 1: Your hosting provider
diff --git a/src/view/com/auth/login/ForgotPasswordForm.tsx b/src/view/com/auth/login/ForgotPasswordForm.tsx
index a794665c9..215c393d9 100644
--- a/src/view/com/auth/login/ForgotPasswordForm.tsx
+++ b/src/view/com/auth/login/ForgotPasswordForm.tsx
@@ -9,13 +9,13 @@ import {
   FontAwesomeIcon,
   FontAwesomeIconStyle,
 } from '@fortawesome/react-native-fontawesome'
+import {ComAtprotoServerDescribeServer} from '@atproto/api'
 import * as EmailValidator from 'email-validator'
 import {BskyAgent} from '@atproto/api'
 import {useAnalytics} from 'lib/analytics/analytics'
 import {Text} from '../../util/text/Text'
 import {s} from 'lib/styles'
 import {toNiceDomain} from 'lib/strings/url-helpers'
-import {ServiceDescription} from 'state/models/session'
 import {isNetworkError} from 'lib/strings/errors'
 import {usePalette} from 'lib/hooks/usePalette'
 import {useTheme} from 'lib/ThemeContext'
@@ -26,6 +26,8 @@ import {useLingui} from '@lingui/react'
 import {styles} from './styles'
 import {useModalControls} from '#/state/modals'
 
+type ServiceDescription = ComAtprotoServerDescribeServer.OutputSchema
+
 export const ForgotPasswordForm = ({
   error,
   serviceUrl,
diff --git a/src/view/com/auth/login/Login.tsx b/src/view/com/auth/login/Login.tsx
index 27d08812c..a2f924f95 100644
--- a/src/view/com/auth/login/Login.tsx
+++ b/src/view/com/auth/login/Login.tsx
@@ -2,8 +2,7 @@ import React, {useState, useEffect} from 'react'
 import {KeyboardAvoidingView} from 'react-native'
 import {useAnalytics} from 'lib/analytics/analytics'
 import {LoggedOutLayout} from 'view/com/util/layouts/LoggedOutLayout'
-import {useStores, DEFAULT_SERVICE} from 'state/index'
-import {ServiceDescription} from 'state/models/session'
+import {DEFAULT_SERVICE} from '#/lib/constants'
 import {usePalette} from 'lib/hooks/usePalette'
 import {logger} from '#/logger'
 import {ChooseAccountForm} from './ChooseAccountForm'
@@ -14,6 +13,7 @@ import {PasswordUpdatedForm} from './PasswordUpdatedForm'
 import {useLingui} from '@lingui/react'
 import {msg} from '@lingui/macro'
 import {useSession, SessionAccount} from '#/state/session'
+import {useServiceQuery} from '#/state/queries/service'
 
 enum Forms {
   Login,
@@ -25,20 +25,20 @@ enum Forms {
 
 export const Login = ({onPressBack}: {onPressBack: () => void}) => {
   const pal = usePalette('default')
-  const store = useStores()
   const {accounts} = useSession()
   const {track} = useAnalytics()
   const {_} = useLingui()
   const [error, setError] = useState<string>('')
-  const [retryDescribeTrigger, setRetryDescribeTrigger] = useState<any>({})
   const [serviceUrl, setServiceUrl] = useState<string>(DEFAULT_SERVICE)
-  const [serviceDescription, setServiceDescription] = useState<
-    ServiceDescription | undefined
-  >(undefined)
   const [initialHandle, setInitialHandle] = useState<string>('')
   const [currentForm, setCurrentForm] = useState<Forms>(
     accounts.length ? Forms.ChooseAccount : Forms.Login,
   )
+  const {
+    data: serviceDescription,
+    error: serviceError,
+    refetch: refetchService,
+  } = useServiceQuery(serviceUrl)
 
   const onSelectAccount = (account?: SessionAccount) => {
     if (account?.service) {
@@ -54,35 +54,21 @@ export const Login = ({onPressBack}: {onPressBack: () => void}) => {
   }
 
   useEffect(() => {
-    let aborted = false
-    setError('')
-    store.session.describeService(serviceUrl).then(
-      desc => {
-        if (aborted) {
-          return
-        }
-        setServiceDescription(desc)
-      },
-      err => {
-        if (aborted) {
-          return
-        }
-        logger.warn(`Failed to fetch service description for ${serviceUrl}`, {
-          error: err,
-        })
-        setError(
-          _(
-            msg`Unable to contact your service. Please check your Internet connection.`,
-          ),
-        )
-      },
-    )
-    return () => {
-      aborted = true
+    if (serviceError) {
+      setError(
+        _(
+          msg`Unable to contact your service. Please check your Internet connection.`,
+        ),
+      )
+      logger.warn(`Failed to fetch service description for ${serviceUrl}`, {
+        error: String(serviceError),
+      })
+    } else {
+      setError('')
     }
-  }, [store.session, serviceUrl, retryDescribeTrigger, _])
+  }, [serviceError, serviceUrl, _])
 
-  const onPressRetryConnect = () => setRetryDescribeTrigger({})
+  const onPressRetryConnect = () => refetchService()
   const onPressForgotPassword = () => {
     track('Signin:PressedForgotPassword')
     setCurrentForm(Forms.ForgotPassword)
diff --git a/src/view/com/auth/login/LoginForm.tsx b/src/view/com/auth/login/LoginForm.tsx
index be3a95131..365f2e253 100644
--- a/src/view/com/auth/login/LoginForm.tsx
+++ b/src/view/com/auth/login/LoginForm.tsx
@@ -10,12 +10,12 @@ import {
   FontAwesomeIcon,
   FontAwesomeIconStyle,
 } from '@fortawesome/react-native-fontawesome'
+import {ComAtprotoServerDescribeServer} from '@atproto/api'
 import {useAnalytics} from 'lib/analytics/analytics'
 import {Text} from '../../util/text/Text'
 import {s} from 'lib/styles'
 import {createFullHandle} from 'lib/strings/handles'
 import {toNiceDomain} from 'lib/strings/url-helpers'
-import {ServiceDescription} from 'state/models/session'
 import {isNetworkError} from 'lib/strings/errors'
 import {usePalette} from 'lib/hooks/usePalette'
 import {useTheme} from 'lib/ThemeContext'
@@ -27,6 +27,8 @@ import {styles} from './styles'
 import {useLingui} from '@lingui/react'
 import {useModalControls} from '#/state/modals'
 
+type ServiceDescription = ComAtprotoServerDescribeServer.OutputSchema
+
 export const LoginForm = ({
   error,
   serviceUrl,
diff --git a/src/view/com/auth/onboarding/RecommendedFeeds.tsx b/src/view/com/auth/onboarding/RecommendedFeeds.tsx
index d134dae9f..941671bf8 100644
--- a/src/view/com/auth/onboarding/RecommendedFeeds.tsx
+++ b/src/view/com/auth/onboarding/RecommendedFeeds.tsx
@@ -1,6 +1,5 @@
 import React from 'react'
 import {ActivityIndicator, FlatList, StyleSheet, View} from 'react-native'
-import {observer} from 'mobx-react-lite'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {TabletOrDesktop, Mobile} from 'view/com/util/layouts/Breakpoints'
 import {Text} from 'view/com/util/text/Text'
@@ -16,9 +15,7 @@ import {useSuggestedFeedsQuery} from '#/state/queries/suggested-feeds'
 type Props = {
   next: () => void
 }
-export const RecommendedFeeds = observer(function RecommendedFeedsImpl({
-  next,
-}: Props) {
+export function RecommendedFeeds({next}: Props) {
   const pal = usePalette('default')
   const {isTabletOrMobile} = useWebMediaQueries()
   const {isLoading, data} = useSuggestedFeedsQuery()
@@ -146,7 +143,7 @@ export const RecommendedFeeds = observer(function RecommendedFeedsImpl({
       </Mobile>
     </>
   )
-})
+}
 
 const tdStyles = StyleSheet.create({
   container: {
diff --git a/src/view/com/auth/onboarding/RecommendedFeedsItem.tsx b/src/view/com/auth/onboarding/RecommendedFeedsItem.tsx
index 2eaf3cf2d..7417e5b06 100644
--- a/src/view/com/auth/onboarding/RecommendedFeedsItem.tsx
+++ b/src/view/com/auth/onboarding/RecommendedFeedsItem.tsx
@@ -1,6 +1,5 @@
 import React from 'react'
 import {View} from 'react-native'
-import {observer} from 'mobx-react-lite'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {AppBskyFeedDefs, RichText as BskRichText} from '@atproto/api'
 import {Text} from 'view/com/util/text/Text'
@@ -19,7 +18,7 @@ import {
 } from '#/state/queries/preferences'
 import {logger} from '#/logger'
 
-export const RecommendedFeedsItem = observer(function RecommendedFeedsItemImpl({
+export function RecommendedFeedsItem({
   item,
 }: {
   item: AppBskyFeedDefs.GeneratorView
@@ -164,4 +163,4 @@ export const RecommendedFeedsItem = observer(function RecommendedFeedsItemImpl({
       </View>
     </View>
   )
-})
+}
diff --git a/src/view/com/auth/onboarding/RecommendedFollows.tsx b/src/view/com/auth/onboarding/RecommendedFollows.tsx
index aa528cbd8..c327a7168 100644
--- a/src/view/com/auth/onboarding/RecommendedFollows.tsx
+++ b/src/view/com/auth/onboarding/RecommendedFollows.tsx
@@ -1,6 +1,5 @@
 import React from 'react'
 import {ActivityIndicator, FlatList, StyleSheet, View} from 'react-native'
-import {observer} from 'mobx-react-lite'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {AppBskyActorDefs, moderateProfile} from '@atproto/api'
 import {TabletOrDesktop, Mobile} from 'view/com/util/layouts/Breakpoints'
@@ -19,9 +18,7 @@ import {logger} from '#/logger'
 type Props = {
   next: () => void
 }
-export const RecommendedFollows = observer(function RecommendedFollowsImpl({
-  next,
-}: Props) {
+export function RecommendedFollows({next}: Props) {
   const pal = usePalette('default')
   const {isTabletOrMobile} = useWebMediaQueries()
   const {data: suggestedFollows, dataUpdatedAt} = useSuggestedFollowsQuery()
@@ -211,7 +208,7 @@ export const RecommendedFollows = observer(function RecommendedFollowsImpl({
       </Mobile>
     </>
   )
-})
+}
 
 const tdStyles = StyleSheet.create({
   container: {
diff --git a/src/view/com/auth/onboarding/WelcomeDesktop.tsx b/src/view/com/auth/onboarding/WelcomeDesktop.tsx
index c066e9bd5..1a30c17f9 100644
--- a/src/view/com/auth/onboarding/WelcomeDesktop.tsx
+++ b/src/view/com/auth/onboarding/WelcomeDesktop.tsx
@@ -7,16 +7,13 @@ import {usePalette} from 'lib/hooks/usePalette'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {TitleColumnLayout} from 'view/com/util/layouts/TitleColumnLayout'
 import {Button} from 'view/com/util/forms/Button'
-import {observer} from 'mobx-react-lite'
 
 type Props = {
   next: () => void
   skip: () => void
 }
 
-export const WelcomeDesktop = observer(function WelcomeDesktopImpl({
-  next,
-}: Props) {
+export function WelcomeDesktop({next}: Props) {
   const pal = usePalette('default')
   const horizontal = useMediaQuery({minWidth: 1300})
   const title = (
@@ -105,7 +102,7 @@ export const WelcomeDesktop = observer(function WelcomeDesktopImpl({
       </View>
     </TitleColumnLayout>
   )
-})
+}
 
 const styles = StyleSheet.create({
   row: {
diff --git a/src/view/com/auth/onboarding/WelcomeMobile.tsx b/src/view/com/auth/onboarding/WelcomeMobile.tsx
index ef70a1fe3..2717bff3f 100644
--- a/src/view/com/auth/onboarding/WelcomeMobile.tsx
+++ b/src/view/com/auth/onboarding/WelcomeMobile.tsx
@@ -5,7 +5,6 @@ import {s} from 'lib/styles'
 import {usePalette} from 'lib/hooks/usePalette'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {Button} from 'view/com/util/forms/Button'
-import {observer} from 'mobx-react-lite'
 import {ViewHeader} from 'view/com/util/ViewHeader'
 import {Trans} from '@lingui/macro'
 
@@ -14,10 +13,7 @@ type Props = {
   skip: () => void
 }
 
-export const WelcomeMobile = observer(function WelcomeMobileImpl({
-  next,
-  skip,
-}: Props) {
+export function WelcomeMobile({next, skip}: Props) {
   const pal = usePalette('default')
 
   return (
@@ -102,7 +98,7 @@ export const WelcomeMobile = observer(function WelcomeMobileImpl({
       />
     </View>
   )
-})
+}
 
 const styles = StyleSheet.create({
   container: {
diff --git a/src/view/com/auth/withAuthRequired.tsx b/src/view/com/auth/withAuthRequired.tsx
index 4b8b31d6c..5fd89a8bd 100644
--- a/src/view/com/auth/withAuthRequired.tsx
+++ b/src/view/com/auth/withAuthRequired.tsx
@@ -5,7 +5,6 @@ import {
   StyleSheet,
   TouchableOpacity,
 } from 'react-native'
-import {observer} from 'mobx-react-lite'
 import {CenteredView} from '../util/Views'
 import {LoggedOut} from './LoggedOut'
 import {Onboarding} from './Onboarding'
@@ -18,7 +17,7 @@ import {useSession} from '#/state/session'
 export const withAuthRequired = <P extends object>(
   Component: React.ComponentType<P>,
 ): React.FC<P> =>
-  observer(function AuthRequired(props: P) {
+  function AuthRequired(props: P) {
     const {isInitialLoad, hasSession} = useSession()
     const onboardingState = useOnboardingState()
     if (isInitialLoad) {
@@ -31,7 +30,7 @@ export const withAuthRequired = <P extends object>(
       return <Onboarding />
     }
     return <Component {...props} />
-  })
+  }
 
 function Loading() {
   const pal = usePalette('default')
diff --git a/src/view/com/composer/labels/LabelsBtn.tsx b/src/view/com/composer/labels/LabelsBtn.tsx
index 7d90774f2..a10684691 100644
--- a/src/view/com/composer/labels/LabelsBtn.tsx
+++ b/src/view/com/composer/labels/LabelsBtn.tsx
@@ -1,6 +1,5 @@
 import React from 'react'
 import {Keyboard, StyleSheet} from 'react-native'
-import {observer} from 'mobx-react-lite'
 import {Button} from 'view/com/util/forms/Button'
 import {usePalette} from 'lib/hooks/usePalette'
 import {ShieldExclamation} from 'lib/icons'
@@ -11,7 +10,7 @@ import {useLingui} from '@lingui/react'
 import {msg} from '@lingui/macro'
 import {useModalControls} from '#/state/modals'
 
-export const LabelsBtn = observer(function LabelsBtn({
+export function LabelsBtn({
   labels,
   hasMedia,
   onChange,
@@ -49,7 +48,7 @@ export const LabelsBtn = observer(function LabelsBtn({
       ) : null}
     </Button>
   )
-})
+}
 
 const styles = StyleSheet.create({
   button: {
diff --git a/src/view/com/composer/select-language/SelectLangBtn.tsx b/src/view/com/composer/select-language/SelectLangBtn.tsx
index 7ea63ed62..78b1e9ba2 100644
--- a/src/view/com/composer/select-language/SelectLangBtn.tsx
+++ b/src/view/com/composer/select-language/SelectLangBtn.tsx
@@ -1,6 +1,5 @@
 import React, {useCallback, useMemo} from 'react'
 import {StyleSheet, Keyboard} from 'react-native'
-import {observer} from 'mobx-react-lite'
 import {
   FontAwesomeIcon,
   FontAwesomeIconStyle,
@@ -24,7 +23,7 @@ import {
 import {t, msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 
-export const SelectLangBtn = observer(function SelectLangBtn() {
+export function SelectLangBtn() {
   const pal = usePalette('default')
   const {_} = useLingui()
   const {openModal} = useModalControls()
@@ -117,7 +116,7 @@ export const SelectLangBtn = observer(function SelectLangBtn() {
       )}
     </DropdownButton>
   )
-})
+}
 
 const styles = StyleSheet.create({
   button: {
diff --git a/src/view/com/composer/text-input/mobile/Autocomplete.tsx b/src/view/com/composer/text-input/mobile/Autocomplete.tsx
index bb54a2042..c400aa48d 100644
--- a/src/view/com/composer/text-input/mobile/Autocomplete.tsx
+++ b/src/view/com/composer/text-input/mobile/Autocomplete.tsx
@@ -1,6 +1,5 @@
 import React, {useEffect, useRef} from 'react'
 import {Animated, TouchableOpacity, StyleSheet, View} from 'react-native'
-import {observer} from 'mobx-react-lite'
 import {useAnimatedValue} from 'lib/hooks/useAnimatedValue'
 import {usePalette} from 'lib/hooks/usePalette'
 import {Text} from 'view/com/util/text/Text'
@@ -10,7 +9,7 @@ import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete'
 import {Trans} from '@lingui/macro'
 import {AppBskyActorDefs} from '@atproto/api'
 
-export const Autocomplete = observer(function AutocompleteImpl({
+export function Autocomplete({
   prefix,
   onSelect,
 }: {
@@ -103,7 +102,7 @@ export const Autocomplete = observer(function AutocompleteImpl({
       ) : null}
     </Animated.View>
   )
-})
+}
 
 const styles = StyleSheet.create({
   container: {
diff --git a/src/view/com/composer/useExternalLinkFetch.ts b/src/view/com/composer/useExternalLinkFetch.ts
index 0c96e4727..af6042306 100644
--- a/src/view/com/composer/useExternalLinkFetch.ts
+++ b/src/view/com/composer/useExternalLinkFetch.ts
@@ -1,5 +1,4 @@
 import {useState, useEffect} from 'react'
-import {useStores} from 'state/index'
 import {ImageModel} from 'state/models/media/image'
 import * as apilib from 'lib/api/index'
 import {getLinkMeta} from 'lib/link-meta/link-meta'
@@ -17,6 +16,7 @@ import {
 import {ComposerOpts} from 'state/shell/composer'
 import {POST_IMG_MAX} from 'lib/constants'
 import {logger} from '#/logger'
+import {useSession} from '#/state/session'
 import {useGetPost} from '#/state/queries/post'
 
 export function useExternalLinkFetch({
@@ -24,7 +24,7 @@ export function useExternalLinkFetch({
 }: {
   setQuote: (opts: ComposerOpts['quote']) => void
 }) {
-  const store = useStores()
+  const {agent} = useSession()
   const [extLink, setExtLink] = useState<apilib.ExternalEmbedDraft | undefined>(
     undefined,
   )
@@ -56,7 +56,7 @@ export function useExternalLinkFetch({
           },
         )
       } else if (isBskyCustomFeedUrl(extLink.uri)) {
-        getFeedAsEmbed(store, extLink.uri).then(
+        getFeedAsEmbed(agent, extLink.uri).then(
           ({embed, meta}) => {
             if (aborted) {
               return
@@ -74,7 +74,7 @@ export function useExternalLinkFetch({
           },
         )
       } else if (isBskyListUrl(extLink.uri)) {
-        getListAsEmbed(store, extLink.uri).then(
+        getListAsEmbed(agent, extLink.uri).then(
           ({embed, meta}) => {
             if (aborted) {
               return
@@ -92,7 +92,7 @@ export function useExternalLinkFetch({
           },
         )
       } else {
-        getLinkMeta(store, extLink.uri).then(meta => {
+        getLinkMeta(agent, extLink.uri).then(meta => {
           if (aborted) {
             return
           }
@@ -134,7 +134,7 @@ export function useExternalLinkFetch({
       })
     }
     return cleanup
-  }, [store, extLink, setQuote, getPost])
+  }, [agent, extLink, setQuote, getPost])
 
   return {extLink, setExtLink}
 }
diff --git a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx
index 7c7ad0616..ea740ec91 100644
--- a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx
+++ b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx
@@ -315,7 +315,6 @@ const ImageItem = ({
       <GestureDetector gesture={composedGesture}>
         <AnimatedImage
           contentFit="contain"
-          // NOTE: Don't pass imageSrc={imageSrc} or MobX will break.
           source={{uri: imageSrc.uri}}
           style={[styles.image, animatedStyle]}
           accessibilityLabel={imageSrc.alt}
diff --git a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx
index f73f355ac..2b0b0b149 100644
--- a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx
+++ b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx
@@ -139,7 +139,6 @@ const ImageItem = ({imageSrc, onTap, onZoom, onRequestClose}: Props) => {
         {(!loaded || !imageDimensions) && <ImageLoading />}
         <AnimatedImage
           contentFit="contain"
-          // NOTE: Don't pass imageSrc={imageSrc} or MobX will break.
           source={{uri: imageSrc.uri}}
           style={[styles.image, animatedStyle]}
           accessibilityLabel={imageSrc.alt}
diff --git a/src/view/com/modals/BirthDateSettings.tsx b/src/view/com/modals/BirthDateSettings.tsx
index 9996c5641..c78f06ed4 100644
--- a/src/view/com/modals/BirthDateSettings.tsx
+++ b/src/view/com/modals/BirthDateSettings.tsx
@@ -5,7 +5,6 @@ import {
   TouchableOpacity,
   View,
 } from 'react-native'
-import {observer} from 'mobx-react-lite'
 import {Text} from '../util/text/Text'
 import {DateInput} from '../util/forms/DateInput'
 import {ErrorMessage} from '../util/error/ErrorMessage'
@@ -103,7 +102,7 @@ function Inner({preferences}: {preferences: UsePreferencesQueryResponse}) {
   )
 }
 
-export const Component = observer(function Component({}: {}) {
+export function Component({}: {}) {
   const {data: preferences} = usePreferencesQuery()
 
   return !preferences ? (
@@ -111,7 +110,7 @@ export const Component = observer(function Component({}: {}) {
   ) : (
     <Inner preferences={preferences} />
   )
-})
+}
 
 const styles = StyleSheet.create({
   container: {
diff --git a/src/view/com/modals/ChangeEmail.tsx b/src/view/com/modals/ChangeEmail.tsx
index 6f7a92102..829188b51 100644
--- a/src/view/com/modals/ChangeEmail.tsx
+++ b/src/view/com/modals/ChangeEmail.tsx
@@ -1,7 +1,6 @@
 import React, {useState} from 'react'
 import {ActivityIndicator, SafeAreaView, StyleSheet, View} from 'react-native'
 import {ScrollView, TextInput} from './util'
-import {observer} from 'mobx-react-lite'
 import {Text} from '../util/text/Text'
 import {Button} from '../util/forms/Button'
 import {ErrorMessage} from '../util/error/ErrorMessage'
@@ -24,7 +23,7 @@ enum Stages {
 
 export const snapPoints = ['90%']
 
-export const Component = observer(function Component({}: {}) {
+export function Component() {
   const pal = usePalette('default')
   const {agent, currentAccount} = useSession()
   const {updateCurrentAccount} = useSessionApi()
@@ -226,7 +225,7 @@ export const Component = observer(function Component({}: {}) {
       </ScrollView>
     </SafeAreaView>
   )
-})
+}
 
 const styles = StyleSheet.create({
   titleSection: {
diff --git a/src/view/com/modals/ContentFilteringSettings.tsx b/src/view/com/modals/ContentFilteringSettings.tsx
index 8dc3311fc..8b42e1b1d 100644
--- a/src/view/com/modals/ContentFilteringSettings.tsx
+++ b/src/view/com/modals/ContentFilteringSettings.tsx
@@ -2,7 +2,6 @@ import React from 'react'
 import {LabelPreference} from '@atproto/api'
 import {StyleSheet, Pressable, View} from 'react-native'
 import LinearGradient from 'react-native-linear-gradient'
-import {observer} from 'mobx-react-lite'
 import {ScrollView} from './util'
 import {s, colors, gradients} from 'lib/styles'
 import {Text} from '../util/text/Text'
@@ -28,82 +27,80 @@ import {
 
 export const snapPoints = ['90%']
 
-export const Component = observer(
-  function ContentFilteringSettingsImpl({}: {}) {
-    const {isMobile} = useWebMediaQueries()
-    const pal = usePalette('default')
-    const {_} = useLingui()
-    const {closeModal} = useModalControls()
-    const {data: preferences} = usePreferencesQuery()
+export function Component({}: {}) {
+  const {isMobile} = useWebMediaQueries()
+  const pal = usePalette('default')
+  const {_} = useLingui()
+  const {closeModal} = useModalControls()
+  const {data: preferences} = usePreferencesQuery()
 
-    const onPressDone = React.useCallback(() => {
-      closeModal()
-    }, [closeModal])
+  const onPressDone = React.useCallback(() => {
+    closeModal()
+  }, [closeModal])
 
-    return (
-      <View testID="contentFilteringModal" style={[pal.view, styles.container]}>
-        <Text style={[pal.text, styles.title]}>
-          <Trans>Content Filtering</Trans>
-        </Text>
+  return (
+    <View testID="contentFilteringModal" style={[pal.view, styles.container]}>
+      <Text style={[pal.text, styles.title]}>
+        <Trans>Content Filtering</Trans>
+      </Text>
 
-        <ScrollView style={styles.scrollContainer}>
-          <AdultContentEnabledPref />
-          <ContentLabelPref
-            preferences={preferences}
-            labelGroup="nsfw"
-            disabled={!preferences?.adultContentEnabled}
-          />
-          <ContentLabelPref
-            preferences={preferences}
-            labelGroup="nudity"
-            disabled={!preferences?.adultContentEnabled}
-          />
-          <ContentLabelPref
-            preferences={preferences}
-            labelGroup="suggestive"
-            disabled={!preferences?.adultContentEnabled}
-          />
-          <ContentLabelPref
-            preferences={preferences}
-            labelGroup="gore"
-            disabled={!preferences?.adultContentEnabled}
-          />
-          <ContentLabelPref preferences={preferences} labelGroup="hate" />
-          <ContentLabelPref preferences={preferences} labelGroup="spam" />
-          <ContentLabelPref
-            preferences={preferences}
-            labelGroup="impersonation"
-          />
-          <View style={{height: isMobile ? 60 : 0}} />
-        </ScrollView>
+      <ScrollView style={styles.scrollContainer}>
+        <AdultContentEnabledPref />
+        <ContentLabelPref
+          preferences={preferences}
+          labelGroup="nsfw"
+          disabled={!preferences?.adultContentEnabled}
+        />
+        <ContentLabelPref
+          preferences={preferences}
+          labelGroup="nudity"
+          disabled={!preferences?.adultContentEnabled}
+        />
+        <ContentLabelPref
+          preferences={preferences}
+          labelGroup="suggestive"
+          disabled={!preferences?.adultContentEnabled}
+        />
+        <ContentLabelPref
+          preferences={preferences}
+          labelGroup="gore"
+          disabled={!preferences?.adultContentEnabled}
+        />
+        <ContentLabelPref preferences={preferences} labelGroup="hate" />
+        <ContentLabelPref preferences={preferences} labelGroup="spam" />
+        <ContentLabelPref
+          preferences={preferences}
+          labelGroup="impersonation"
+        />
+        <View style={{height: isMobile ? 60 : 0}} />
+      </ScrollView>
 
-        <View
-          style={[
-            styles.btnContainer,
-            isMobile && styles.btnContainerMobile,
-            pal.borderDark,
-          ]}>
-          <Pressable
-            testID="sendReportBtn"
-            onPress={onPressDone}
-            accessibilityRole="button"
-            accessibilityLabel={_(msg`Done`)}
-            accessibilityHint="">
-            <LinearGradient
-              colors={[gradients.blueLight.start, gradients.blueLight.end]}
-              start={{x: 0, y: 0}}
-              end={{x: 1, y: 1}}
-              style={[styles.btn]}>
-              <Text style={[s.white, s.bold, s.f18]}>
-                <Trans>Done</Trans>
-              </Text>
-            </LinearGradient>
-          </Pressable>
-        </View>
+      <View
+        style={[
+          styles.btnContainer,
+          isMobile && styles.btnContainerMobile,
+          pal.borderDark,
+        ]}>
+        <Pressable
+          testID="sendReportBtn"
+          onPress={onPressDone}
+          accessibilityRole="button"
+          accessibilityLabel={_(msg`Done`)}
+          accessibilityHint="">
+          <LinearGradient
+            colors={[gradients.blueLight.start, gradients.blueLight.end]}
+            start={{x: 0, y: 0}}
+            end={{x: 1, y: 1}}
+            style={[styles.btn]}>
+            <Text style={[s.white, s.bold, s.f18]}>
+              <Trans>Done</Trans>
+            </Text>
+          </LinearGradient>
+        </Pressable>
       </View>
-    )
-  },
-)
+    </View>
+  )
+}
 
 function AdultContentEnabledPref() {
   const pal = usePalette('default')
@@ -171,7 +168,7 @@ function AdultContentEnabledPref() {
 }
 
 // TODO: Refactor this component to pass labels down to each tab
-const ContentLabelPref = observer(function ContentLabelPrefImpl({
+function ContentLabelPref({
   preferences,
   labelGroup,
   disabled,
@@ -217,7 +214,7 @@ const ContentLabelPref = observer(function ContentLabelPrefImpl({
       )}
     </View>
   )
-})
+}
 
 interface SelectGroupProps {
   current: LabelPreference
diff --git a/src/view/com/modals/InviteCodes.tsx b/src/view/com/modals/InviteCodes.tsx
index 973c7c3a7..82a826aca 100644
--- a/src/view/com/modals/InviteCodes.tsx
+++ b/src/view/com/modals/InviteCodes.tsx
@@ -5,7 +5,6 @@ import {
   View,
   ActivityIndicator,
 } from 'react-native'
-import {observer} from 'mobx-react-lite'
 import {ComAtprotoServerDefs} from '@atproto/api'
 import {
   FontAwesomeIcon,
@@ -129,7 +128,7 @@ export function Inner({invites}: {invites: InviteCodesQueryResponse}) {
   )
 }
 
-const InviteCode = observer(function InviteCodeImpl({
+function InviteCode({
   testID,
   invite,
   used,
@@ -211,7 +210,7 @@ const InviteCode = observer(function InviteCodeImpl({
       ) : null}
     </View>
   )
-})
+}
 
 const styles = StyleSheet.create({
   container: {
diff --git a/src/view/com/modals/LinkWarning.tsx b/src/view/com/modals/LinkWarning.tsx
index cb86387a0..c63bc2a87 100644
--- a/src/view/com/modals/LinkWarning.tsx
+++ b/src/view/com/modals/LinkWarning.tsx
@@ -1,7 +1,6 @@
 import React from 'react'
 import {Linking, SafeAreaView, StyleSheet, View} from 'react-native'
 import {ScrollView} from './util'
-import {observer} from 'mobx-react-lite'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {Text} from '../util/text/Text'
 import {Button} from '../util/forms/Button'
@@ -16,13 +15,7 @@ import {useModalControls} from '#/state/modals'
 
 export const snapPoints = ['50%']
 
-export const Component = observer(function Component({
-  text,
-  href,
-}: {
-  text: string
-  href: string
-}) {
+export function Component({text, href}: {text: string; href: string}) {
   const pal = usePalette('default')
   const {closeModal} = useModalControls()
   const {isMobile} = useWebMediaQueries()
@@ -97,7 +90,7 @@ export const Component = observer(function Component({
       </ScrollView>
     </SafeAreaView>
   )
-})
+}
 
 function LinkBox({href}: {href: string}) {
   const pal = usePalette('default')
diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx
index 38c8bc7ba..d834db40b 100644
--- a/src/view/com/modals/Modal.tsx
+++ b/src/view/com/modals/Modal.tsx
@@ -1,7 +1,6 @@
 import React, {useRef, useEffect} from 'react'
 import {StyleSheet} from 'react-native'
 import {SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context'
-import {observer} from 'mobx-react-lite'
 import BottomSheet from '@gorhom/bottom-sheet'
 import {createCustomBackdrop} from '../util/BottomSheetCustomBackdrop'
 import {usePalette} from 'lib/hooks/usePalette'
@@ -40,7 +39,7 @@ import * as LinkWarningModal from './LinkWarning'
 const DEFAULT_SNAPPOINTS = ['90%']
 const HANDLE_HEIGHT = 24
 
-export const ModalsContainer = observer(function ModalsContainer() {
+export function ModalsContainer() {
   const {isModalActive, activeModals} = useModals()
   const {closeModal} = useModalControls()
   const bottomSheetRef = useRef<BottomSheet>(null)
@@ -198,7 +197,7 @@ export const ModalsContainer = observer(function ModalsContainer() {
       {element}
     </BottomSheet>
   )
-})
+}
 
 const styles = StyleSheet.create({
   handle: {
diff --git a/src/view/com/modals/Modal.web.tsx b/src/view/com/modals/Modal.web.tsx
index 28f6c36c9..74aa7b1a9 100644
--- a/src/view/com/modals/Modal.web.tsx
+++ b/src/view/com/modals/Modal.web.tsx
@@ -1,6 +1,5 @@
 import React from 'react'
 import {TouchableWithoutFeedback, StyleSheet, View} from 'react-native'
-import {observer} from 'mobx-react-lite'
 import {usePalette} from 'lib/hooks/usePalette'
 import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
 import type {Modal as ModalIface} from '#/state/modals'
@@ -33,7 +32,7 @@ import * as VerifyEmailModal from './VerifyEmail'
 import * as ChangeEmailModal from './ChangeEmail'
 import * as LinkWarningModal from './LinkWarning'
 
-export const ModalsContainer = observer(function ModalsContainer() {
+export function ModalsContainer() {
   const {isModalActive, activeModals} = useModals()
 
   if (!isModalActive) {
@@ -47,7 +46,7 @@ export const ModalsContainer = observer(function ModalsContainer() {
       ))}
     </>
   )
-})
+}
 
 function Modal({modal}: {modal: ModalIface}) {
   const {isModalActive} = useModals()
diff --git a/src/view/com/modals/SelfLabel.tsx b/src/view/com/modals/SelfLabel.tsx
index f8b64085d..092dd2d32 100644
--- a/src/view/com/modals/SelfLabel.tsx
+++ b/src/view/com/modals/SelfLabel.tsx
@@ -1,6 +1,5 @@
 import React, {useState} from 'react'
 import {StyleSheet, TouchableOpacity, View} from 'react-native'
-import {observer} from 'mobx-react-lite'
 import {Text} from '../util/text/Text'
 import {s, colors} from 'lib/styles'
 import {usePalette} from 'lib/hooks/usePalette'
@@ -17,7 +16,7 @@ const ADULT_CONTENT_LABELS = ['sexual', 'nudity', 'porn']
 
 export const snapPoints = ['50%']
 
-export const Component = observer(function Component({
+export function Component({
   labels,
   hasMedia,
   onChange,
@@ -161,7 +160,7 @@ export const Component = observer(function Component({
       </View>
     </View>
   )
-})
+}
 
 const styles = StyleSheet.create({
   container: {
diff --git a/src/view/com/modals/ServerInput.tsx b/src/view/com/modals/ServerInput.tsx
index 09a460315..b30293859 100644
--- a/src/view/com/modals/ServerInput.tsx
+++ b/src/view/com/modals/ServerInput.tsx
@@ -9,7 +9,7 @@ import {Text} from '../util/text/Text'
 import {s, colors} from 'lib/styles'
 import {usePalette} from 'lib/hooks/usePalette'
 import {useTheme} from 'lib/ThemeContext'
-import {LOCAL_DEV_SERVICE, STAGING_SERVICE, PROD_SERVICE} from 'state/index'
+import {LOCAL_DEV_SERVICE, STAGING_SERVICE, PROD_SERVICE} from 'lib/constants'
 import {LOGIN_INCLUDE_DEV_SERVERS} from 'lib/build-flags'
 import {Trans, msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
diff --git a/src/view/com/modals/VerifyEmail.tsx b/src/view/com/modals/VerifyEmail.tsx
index 106e05b87..e7a4537c5 100644
--- a/src/view/com/modals/VerifyEmail.tsx
+++ b/src/view/com/modals/VerifyEmail.tsx
@@ -8,7 +8,6 @@ import {
 } from 'react-native'
 import {Svg, Circle, Path} from 'react-native-svg'
 import {ScrollView, TextInput} from './util'
-import {observer} from 'mobx-react-lite'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {Text} from '../util/text/Text'
 import {Button} from '../util/forms/Button'
@@ -32,11 +31,7 @@ enum Stages {
   ConfirmCode,
 }
 
-export const Component = observer(function Component({
-  showReminder,
-}: {
-  showReminder?: boolean
-}) {
+export function Component({showReminder}: {showReminder?: boolean}) {
   const pal = usePalette('default')
   const {agent, currentAccount} = useSession()
   const {updateCurrentAccount} = useSessionApi()
@@ -244,7 +239,7 @@ export const Component = observer(function Component({
       </ScrollView>
     </SafeAreaView>
   )
-})
+}
 
 function ReminderIllustration() {
   const pal = usePalette('default')
diff --git a/src/view/com/modals/lang-settings/LanguageToggle.tsx b/src/view/com/modals/lang-settings/LanguageToggle.tsx
index 86e38a4d2..45b100f20 100644
--- a/src/view/com/modals/lang-settings/LanguageToggle.tsx
+++ b/src/view/com/modals/lang-settings/LanguageToggle.tsx
@@ -1,11 +1,10 @@
 import React from 'react'
 import {StyleSheet} from 'react-native'
 import {usePalette} from 'lib/hooks/usePalette'
-import {observer} from 'mobx-react-lite'
 import {ToggleButton} from 'view/com/util/forms/ToggleButton'
 import {useLanguagePrefs, toPostLanguages} from '#/state/preferences/languages'
 
-export const LanguageToggle = observer(function LanguageToggleImpl({
+export function LanguageToggle({
   code2,
   name,
   onPress,
@@ -39,7 +38,7 @@ export const LanguageToggle = observer(function LanguageToggleImpl({
       style={[pal.border, styles.languageToggle, isDisabled && styles.dimmed]}
     />
   )
-})
+}
 
 const styles = StyleSheet.create({
   languageToggle: {
diff --git a/src/view/com/modals/lang-settings/PostLanguagesSettings.tsx b/src/view/com/modals/lang-settings/PostLanguagesSettings.tsx
index 7fcb24d58..05cfb8115 100644
--- a/src/view/com/modals/lang-settings/PostLanguagesSettings.tsx
+++ b/src/view/com/modals/lang-settings/PostLanguagesSettings.tsx
@@ -1,6 +1,5 @@
 import React from 'react'
 import {StyleSheet, View} from 'react-native'
-import {observer} from 'mobx-react-lite'
 import {ScrollView} from '../util'
 import {Text} from '../../util/text/Text'
 import {usePalette} from 'lib/hooks/usePalette'
@@ -19,7 +18,7 @@ import {
 
 export const snapPoints = ['100%']
 
-export const Component = observer(function PostLanguagesSettingsImpl() {
+export function Component() {
   const {closeModal} = useModalControls()
   const langPrefs = useLanguagePrefs()
   const setLangPrefs = useLanguagePrefsApi()
@@ -111,7 +110,7 @@ export const Component = observer(function PostLanguagesSettingsImpl() {
       <ConfirmLanguagesButton onPress={onPressDone} />
     </View>
   )
-})
+}
 
 const styles = StyleSheet.create({
   container: {
diff --git a/src/view/com/modals/report/Modal.tsx b/src/view/com/modals/report/Modal.tsx
index 5183bdc10..06075547f 100644
--- a/src/view/com/modals/report/Modal.tsx
+++ b/src/view/com/modals/report/Modal.tsx
@@ -2,7 +2,6 @@ import React, {useState, useMemo} from 'react'
 import {Linking, StyleSheet, TouchableOpacity, View} from 'react-native'
 import {ScrollView} from 'react-native-gesture-handler'
 import {AtUri} from '@atproto/api'
-import {useStores} from 'state/index'
 import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
 import {s} from 'lib/styles'
 import {Text} from '../../util/text/Text'
@@ -17,6 +16,7 @@ import {CollectionId} from './types'
 import {Trans, msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import {useModalControls} from '#/state/modals'
+import {useSession} from '#/state/session'
 
 const DMCA_LINK = 'https://blueskyweb.xyz/support/copyright'
 
@@ -39,7 +39,7 @@ type ReportComponentProps =
     }
 
 export function Component(content: ReportComponentProps) {
-  const store = useStores()
+  const {agent} = useSession()
   const {closeModal} = useModalControls()
   const pal = usePalette('default')
   const {isMobile} = useWebMediaQueries()
@@ -70,7 +70,7 @@ export function Component(content: ReportComponentProps) {
       const $type = !isAccountReport
         ? 'com.atproto.repo.strongRef'
         : 'com.atproto.admin.defs#repoRef'
-      await store.agent.createModerationReport({
+      await agent.createModerationReport({
         reasonType: issue,
         subject: {
           $type,
diff --git a/src/view/com/pager/FeedsTabBar.web.tsx b/src/view/com/pager/FeedsTabBar.web.tsx
index b48690a66..298372893 100644
--- a/src/view/com/pager/FeedsTabBar.web.tsx
+++ b/src/view/com/pager/FeedsTabBar.web.tsx
@@ -1,7 +1,6 @@
 import React from 'react'
 import {StyleSheet} from 'react-native'
 import Animated from 'react-native-reanimated'
-import {observer} from 'mobx-react-lite'
 import {TabBar} from 'view/com/pager/TabBar'
 import {RenderTabBarFnProps} from 'view/com/pager/Pager'
 import {usePalette} from 'lib/hooks/usePalette'
@@ -11,7 +10,7 @@ import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
 import {useShellLayout} from '#/state/shell/shell-layout'
 import {usePinnedFeedsInfos} from '#/state/queries/feed'
 
-export const FeedsTabBar = observer(function FeedsTabBarImpl(
+export function FeedsTabBar(
   props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
 ) {
   const {isMobile, isTablet} = useWebMediaQueries()
@@ -22,9 +21,9 @@ export const FeedsTabBar = observer(function FeedsTabBarImpl(
   } else {
     return null
   }
-})
+}
 
-const FeedsTabBarTablet = observer(function FeedsTabBarTabletImpl(
+function FeedsTabBarTablet(
   props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
 ) {
   const feeds = usePinnedFeedsInfos()
@@ -48,7 +47,7 @@ const FeedsTabBarTablet = observer(function FeedsTabBarTabletImpl(
       />
     </Animated.View>
   )
-})
+}
 
 const styles = StyleSheet.create({
   tabBar: {
diff --git a/src/view/com/pager/FeedsTabBarMobile.tsx b/src/view/com/pager/FeedsTabBarMobile.tsx
index de985fb7c..e0c0be1ef 100644
--- a/src/view/com/pager/FeedsTabBarMobile.tsx
+++ b/src/view/com/pager/FeedsTabBarMobile.tsx
@@ -1,6 +1,5 @@
 import React from 'react'
 import {StyleSheet, TouchableOpacity, View} from 'react-native'
-import {observer} from 'mobx-react-lite'
 import {TabBar} from 'view/com/pager/TabBar'
 import {RenderTabBarFnProps} from 'view/com/pager/Pager'
 import {usePalette} from 'lib/hooks/usePalette'
@@ -20,7 +19,7 @@ import {useShellLayout} from '#/state/shell/shell-layout'
 import {useSession} from '#/state/session'
 import {usePinnedFeedsInfos} from '#/state/queries/feed'
 
-export const FeedsTabBar = observer(function FeedsTabBarImpl(
+export function FeedsTabBar(
   props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
 ) {
   const pal = usePalette('default')
@@ -88,7 +87,7 @@ export const FeedsTabBar = observer(function FeedsTabBarImpl(
       />
     </Animated.View>
   )
-})
+}
 
 const styles = StyleSheet.create({
   tabBar: {
diff --git a/src/view/com/posts/FeedSlice.tsx b/src/view/com/posts/FeedSlice.tsx
index fad9f9b4e..99ee38536 100644
--- a/src/view/com/posts/FeedSlice.tsx
+++ b/src/view/com/posts/FeedSlice.tsx
@@ -1,6 +1,5 @@
 import React from 'react'
 import {StyleSheet, View} from 'react-native'
-import {observer} from 'mobx-react-lite'
 import {FeedPostSlice} from '#/state/queries/post-feed'
 import {AtUri, moderatePost, ModerationOpts} from '@atproto/api'
 import {Link} from '../util/Link'
@@ -10,7 +9,7 @@ import {FeedItem} from './FeedItem'
 import {usePalette} from 'lib/hooks/usePalette'
 import {makeProfileLink} from 'lib/routes/links'
 
-export const FeedSlice = observer(function FeedSliceImpl({
+export function FeedSlice({
   slice,
   dataUpdatedAt,
   ignoreFilterFor,
@@ -94,7 +93,7 @@ export const FeedSlice = observer(function FeedSliceImpl({
       ))}
     </>
   )
-})
+}
 
 function ViewFullThread({slice}: {slice: FeedPostSlice}) {
   const pal = usePalette('default')
diff --git a/src/view/com/profile/ProfileCard.tsx b/src/view/com/profile/ProfileCard.tsx
index 2f359018f..cd9855456 100644
--- a/src/view/com/profile/ProfileCard.tsx
+++ b/src/view/com/profile/ProfileCard.tsx
@@ -1,6 +1,5 @@
 import * as React from 'react'
 import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
-import {observer} from 'mobx-react-lite'
 import {
   AppBskyActorDefs,
   moderateProfile,
@@ -152,7 +151,7 @@ function ProfileCardPills({
   )
 }
 
-const FollowersList = observer(function FollowersListImpl({
+function FollowersList({
   followers,
 }: {
   followers?: AppBskyActorDefs.ProfileView[] | undefined
@@ -196,7 +195,7 @@ const FollowersList = observer(function FollowersListImpl({
       ))}
     </View>
   )
-})
+}
 
 export function ProfileCardWithFollowBtn({
   profile,
diff --git a/src/view/com/profile/ProfileSubpageHeader.tsx b/src/view/com/profile/ProfileSubpageHeader.tsx
index ef128e877..0e245f0f4 100644
--- a/src/view/com/profile/ProfileSubpageHeader.tsx
+++ b/src/view/com/profile/ProfileSubpageHeader.tsx
@@ -1,6 +1,5 @@
 import React from 'react'
 import {Pressable, StyleSheet, View} from 'react-native'
-import {observer} from 'mobx-react-lite'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {useNavigation} from '@react-navigation/native'
 import {usePalette} from 'lib/hooks/usePalette'
@@ -12,7 +11,6 @@ import {LoadingPlaceholder} from '../util/LoadingPlaceholder'
 import {CenteredView} from '../util/Views'
 import {sanitizeHandle} from 'lib/strings/handles'
 import {makeProfileLink} from 'lib/routes/links'
-import {useStores} from 'state/index'
 import {NavigationProp} from 'lib/routes/types'
 import {BACK_HITSLOP} from 'lib/constants'
 import {isNative} from 'platform/detection'
@@ -22,7 +20,7 @@ import {msg} from '@lingui/macro'
 import {useSetDrawerOpen} from '#/state/shell'
 import {emitSoftReset} from '#/state/events'
 
-export const ProfileSubpageHeader = observer(function HeaderImpl({
+export function ProfileSubpageHeader({
   isLoading,
   href,
   title,
@@ -45,7 +43,6 @@ export const ProfileSubpageHeader = observer(function HeaderImpl({
     | undefined
   avatarType: UserAvatarType
 }>) {
-  const store = useStores()
   const setDrawerOpen = useSetDrawerOpen()
   const navigation = useNavigation<NavigationProp>()
   const {_} = useLingui()
@@ -183,7 +180,7 @@ export const ProfileSubpageHeader = observer(function HeaderImpl({
       </View>
     </CenteredView>
   )
-})
+}
 
 const styles = StyleSheet.create({
   backBtn: {
diff --git a/src/view/com/util/PostMeta.tsx b/src/view/com/util/PostMeta.tsx
index c5e438f8d..fa5f12f6b 100644
--- a/src/view/com/util/PostMeta.tsx
+++ b/src/view/com/util/PostMeta.tsx
@@ -6,7 +6,6 @@ import {niceDate} from 'lib/strings/time'
 import {usePalette} from 'lib/hooks/usePalette'
 import {TypographyVariant} from 'lib/ThemeContext'
 import {UserAvatar} from './UserAvatar'
-import {observer} from 'mobx-react-lite'
 import {sanitizeDisplayName} from 'lib/strings/display-names'
 import {sanitizeHandle} from 'lib/strings/handles'
 import {isAndroid} from 'platform/detection'
@@ -30,7 +29,7 @@ interface PostMetaOpts {
   style?: StyleProp<ViewStyle>
 }
 
-export const PostMeta = observer(function PostMetaImpl(opts: PostMetaOpts) {
+export function PostMeta(opts: PostMetaOpts) {
   const pal = usePalette('default')
   const displayName = opts.author.displayName || opts.author.handle
   const handle = opts.author.handle
@@ -92,7 +91,7 @@ export const PostMeta = observer(function PostMetaImpl(opts: PostMetaOpts) {
       </TimeElapsed>
     </View>
   )
-})
+}
 
 const styles = StyleSheet.create({
   container: {
diff --git a/src/view/com/util/SimpleViewHeader.tsx b/src/view/com/util/SimpleViewHeader.tsx
index c871d9404..e86e37565 100644
--- a/src/view/com/util/SimpleViewHeader.tsx
+++ b/src/view/com/util/SimpleViewHeader.tsx
@@ -1,5 +1,4 @@
 import React from 'react'
-import {observer} from 'mobx-react-lite'
 import {
   StyleProp,
   StyleSheet,
@@ -18,7 +17,7 @@ import {useSetDrawerOpen} from '#/state/shell'
 
 const BACK_HITSLOP = {left: 20, top: 20, right: 50, bottom: 20}
 
-export const SimpleViewHeader = observer(function SimpleViewHeaderImpl({
+export function SimpleViewHeader({
   showBackButton = true,
   style,
   children,
@@ -76,7 +75,7 @@ export const SimpleViewHeader = observer(function SimpleViewHeaderImpl({
       {children}
     </Container>
   )
-})
+}
 
 const styles = StyleSheet.create({
   header: {
diff --git a/src/view/com/util/ViewHeader.tsx b/src/view/com/util/ViewHeader.tsx
index adf2e4f08..082cae59c 100644
--- a/src/view/com/util/ViewHeader.tsx
+++ b/src/view/com/util/ViewHeader.tsx
@@ -1,5 +1,4 @@
 import React from 'react'
-import {observer} from 'mobx-react-lite'
 import {StyleSheet, TouchableOpacity, View} from 'react-native'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {useNavigation} from '@react-navigation/native'
@@ -15,7 +14,7 @@ import {useSetDrawerOpen} from '#/state/shell'
 
 const BACK_HITSLOP = {left: 20, top: 20, right: 50, bottom: 20}
 
-export const ViewHeader = observer(function ViewHeaderImpl({
+export function ViewHeader({
   title,
   canGoBack,
   showBackButton = true,
@@ -108,7 +107,7 @@ export const ViewHeader = observer(function ViewHeaderImpl({
       </Container>
     )
   }
-})
+}
 
 function DesktopWebHeader({
   title,
@@ -140,7 +139,7 @@ function DesktopWebHeader({
   )
 }
 
-const Container = observer(function ContainerImpl({
+function Container({
   children,
   hideOnScroll,
   showBorder,
@@ -178,7 +177,7 @@ const Container = observer(function ContainerImpl({
       {children}
     </Animated.View>
   )
-})
+}
 
 const styles = StyleSheet.create({
   header: {
diff --git a/src/view/com/util/fab/FABInner.tsx b/src/view/com/util/fab/FABInner.tsx
index 5b1d5d888..9787d92fb 100644
--- a/src/view/com/util/fab/FABInner.tsx
+++ b/src/view/com/util/fab/FABInner.tsx
@@ -1,5 +1,4 @@
 import React, {ComponentProps} from 'react'
-import {observer} from 'mobx-react-lite'
 import {StyleSheet, TouchableWithoutFeedback} from 'react-native'
 import LinearGradient from 'react-native-linear-gradient'
 import {gradients} from 'lib/styles'
@@ -15,11 +14,7 @@ export interface FABProps
   icon: JSX.Element
 }
 
-export const FABInner = observer(function FABInnerImpl({
-  testID,
-  icon,
-  ...props
-}: FABProps) {
+export function FABInner({testID, icon, ...props}: FABProps) {
   const insets = useSafeAreaInsets()
   const {isMobile, isTablet} = useWebMediaQueries()
   const {fabMinimalShellTransform} = useMinimalShellMode()
@@ -55,7 +50,7 @@ export const FABInner = observer(function FABInnerImpl({
       </Animated.View>
     </TouchableWithoutFeedback>
   )
-})
+}
 
 const styles = StyleSheet.create({
   sizeRegular: {
diff --git a/src/view/com/util/images/AutoSizedImage.tsx b/src/view/com/util/images/AutoSizedImage.tsx
index 6cbcddc32..b5b6c1b52 100644
--- a/src/view/com/util/images/AutoSizedImage.tsx
+++ b/src/view/com/util/images/AutoSizedImage.tsx
@@ -2,8 +2,8 @@ import React from 'react'
 import {StyleProp, StyleSheet, Pressable, View, ViewStyle} from 'react-native'
 import {Image} from 'expo-image'
 import {clamp} from 'lib/numbers'
-import {useStores} from 'state/index'
 import {Dimensions} from 'lib/media/types'
+import * as imageSizes from 'lib/media/image-sizes'
 
 const MIN_ASPECT_RATIO = 0.33 // 1/3
 const MAX_ASPECT_RATIO = 5 // 5/1
@@ -29,9 +29,8 @@ export function AutoSizedImage({
   style,
   children = null,
 }: Props) {
-  const store = useStores()
   const [dim, setDim] = React.useState<Dimensions | undefined>(
-    dimensionsHint || store.imageSizes.get(uri),
+    dimensionsHint || imageSizes.get(uri),
   )
   const [aspectRatio, setAspectRatio] = React.useState<number>(
     dim ? calc(dim) : 1,
@@ -41,14 +40,14 @@ export function AutoSizedImage({
     if (dim) {
       return
     }
-    store.imageSizes.fetch(uri).then(newDim => {
+    imageSizes.fetch(uri).then(newDim => {
       if (aborted) {
         return
       }
       setDim(newDim)
       setAspectRatio(calc(newDim))
     })
-  }, [dim, setDim, setAspectRatio, store, uri])
+  }, [dim, setDim, setAspectRatio, uri])
 
   if (onPress || onLongPress || onPressIn) {
     return (
diff --git a/src/view/com/util/load-latest/LoadLatestBtn.tsx b/src/view/com/util/load-latest/LoadLatestBtn.tsx
index f9a9387bb..970d3a73a 100644
--- a/src/view/com/util/load-latest/LoadLatestBtn.tsx
+++ b/src/view/com/util/load-latest/LoadLatestBtn.tsx
@@ -1,6 +1,5 @@
 import React from 'react'
 import {StyleSheet, TouchableOpacity, View} from 'react-native'
-import {observer} from 'mobx-react-lite'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {usePalette} from 'lib/hooks/usePalette'
 import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
@@ -12,7 +11,7 @@ const AnimatedTouchableOpacity =
   Animated.createAnimatedComponent(TouchableOpacity)
 import {isWeb} from 'platform/detection'
 
-export const LoadLatestBtn = observer(function LoadLatestBtnImpl({
+export function LoadLatestBtn({
   onPress,
   label,
   showIndicator,
@@ -44,7 +43,7 @@ export const LoadLatestBtn = observer(function LoadLatestBtnImpl({
       {showIndicator && <View style={[styles.indicator, pal.borderDark]} />}
     </AnimatedTouchableOpacity>
   )
-})
+}
 
 const styles = StyleSheet.create({
   loadLatest: {
diff --git a/src/view/com/util/post-embeds/ListEmbed.tsx b/src/view/com/util/post-embeds/ListEmbed.tsx
index dbf350039..fc5ad270f 100644
--- a/src/view/com/util/post-embeds/ListEmbed.tsx
+++ b/src/view/com/util/post-embeds/ListEmbed.tsx
@@ -1,12 +1,11 @@
 import React from 'react'
 import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
 import {usePalette} from 'lib/hooks/usePalette'
-import {observer} from 'mobx-react-lite'
 import {ListCard} from 'view/com/lists/ListCard'
 import {AppBskyGraphDefs} from '@atproto/api'
 import {s} from 'lib/styles'
 
-export const ListEmbed = observer(function ListEmbedImpl({
+export function ListEmbed({
   item,
   style,
 }: {
@@ -20,7 +19,7 @@ export const ListEmbed = observer(function ListEmbedImpl({
       <ListCard list={item} style={[style, styles.card]} />
     </View>
   )
-})
+}
 
 const styles = StyleSheet.create({
   container: {