about summary refs log tree commit diff
diff options
context:
space:
mode:
authordan <dan.abramov@gmail.com>2023-09-08 01:36:08 +0100
committerGitHub <noreply@github.com>2023-09-07 17:36:08 -0700
commit8a93321fb1bd4991cbb3bd1c1f09ed2196182f93 (patch)
tree2cd7cbfa0eb98a808517c8485af3ec43c0a7ea2e
parent69209c988fc412a10a5028ca915f99b1d059f5ec (diff)
downloadvoidsky-8a93321fb1bd4991cbb3bd1c1f09ed2196182f93.tar.zst
Give explicit names to MobX observer components (#1413)
* Consider observer(...) as components

* Add display names to MobX observers

* Temporarily suppress nested components

* Suppress new false positives for react/prop-types
-rw-r--r--.eslintrc.js3
-rw-r--r--src/App.native.tsx2
-rw-r--r--src/App.web.tsx2
-rw-r--r--src/Navigation.tsx4
-rw-r--r--src/view/com/auth/LoggedOut.tsx2
-rw-r--r--src/view/com/auth/Onboarding.tsx2
-rw-r--r--src/view/com/auth/create/CreateAccount.tsx196
-rw-r--r--src/view/com/auth/create/Step1.tsx6
-rw-r--r--src/view/com/auth/create/Step2.tsx6
-rw-r--r--src/view/com/auth/create/Step3.tsx6
-rw-r--r--src/view/com/auth/onboarding/RecommendedFeeds.tsx4
-rw-r--r--src/view/com/auth/onboarding/RecommendedFeedsItem.tsx236
-rw-r--r--src/view/com/auth/onboarding/WelcomeDesktop.tsx4
-rw-r--r--src/view/com/auth/onboarding/WelcomeMobile.tsx5
-rw-r--r--src/view/com/auth/withAuthRequired.tsx2
-rw-r--r--src/view/com/composer/photos/Gallery.tsx2
-rw-r--r--src/view/com/composer/text-input/mobile/Autocomplete.tsx152
-rw-r--r--src/view/com/feeds/CustomFeed.tsx214
-rw-r--r--src/view/com/lists/ListItems.tsx577
-rw-r--r--src/view/com/lists/ListsList.tsx306
-rw-r--r--src/view/com/modals/ContentFilteringSettings.tsx282
-rw-r--r--src/view/com/modals/EditImage.tsx5
-rw-r--r--src/view/com/modals/InviteCodes.tsx90
-rw-r--r--src/view/com/modals/ListAddRemoveUser.tsx367
-rw-r--r--src/view/com/modals/ProfilePreview.tsx6
-rw-r--r--src/view/com/modals/lang-settings/LanguageToggle.tsx66
-rw-r--r--src/view/com/modals/lang-settings/PostLanguagesSettings.tsx2
-rw-r--r--src/view/com/notifications/FeedItem.tsx2
-rw-r--r--src/view/com/notifications/InvitedUsers.tsx2
-rw-r--r--src/view/com/pager/FeedsTabBar.web.tsx96
-rw-r--r--src/view/com/pager/FeedsTabBarMobile.tsx132
-rw-r--r--src/view/com/post-thread/PostLikedBy.tsx8
-rw-r--r--src/view/com/post-thread/PostRepostedBy.tsx4
-rw-r--r--src/view/com/post/Post.tsx382
-rw-r--r--src/view/com/posts/FeedItem.tsx2
-rw-r--r--src/view/com/posts/FeedSlice.tsx102
-rw-r--r--src/view/com/profile/FollowButton.tsx94
-rw-r--r--src/view/com/profile/ProfileCard.tsx223
-rw-r--r--src/view/com/profile/ProfileFollowers.tsx2
-rw-r--r--src/view/com/profile/ProfileFollows.tsx2
-rw-r--r--src/view/com/profile/ProfileHeader.tsx898
-rw-r--r--src/view/com/search/SearchResults.tsx18
-rw-r--r--src/view/com/search/Suggestions.tsx3
-rw-r--r--src/view/com/util/PostMeta.tsx2
-rw-r--r--src/view/com/util/TimeElapsed.tsx3
-rw-r--r--src/view/com/util/ViewHeader.tsx104
-rw-r--r--src/view/com/util/fab/FABInner.tsx6
-rw-r--r--src/view/com/util/load-latest/LoadLatestBtnMobile.tsx68
-rw-r--r--src/view/com/util/post-embeds/ListEmbed.tsx30
-rw-r--r--src/view/screens/AppPasswords.tsx2
-rw-r--r--src/view/screens/CustomFeed.tsx7
-rw-r--r--src/view/screens/DiscoverFeeds.tsx2
-rw-r--r--src/view/screens/Feeds.tsx2
-rw-r--r--src/view/screens/Home.tsx261
-rw-r--r--src/view/screens/ModerationBlockedAccounts.tsx4
-rw-r--r--src/view/screens/ModerationMutedAccounts.tsx4
-rw-r--r--src/view/screens/Notifications.tsx2
-rw-r--r--src/view/screens/PreferencesHomeFeed.tsx4
-rw-r--r--src/view/screens/Profile.tsx2
-rw-r--r--src/view/screens/ProfileList.tsx2
-rw-r--r--src/view/screens/SavedFeeds.tsx174
-rw-r--r--src/view/screens/Search.web.tsx2
-rw-r--r--src/view/screens/SearchMobile.tsx2
-rw-r--r--src/view/shell/Composer.tsx122
-rw-r--r--src/view/shell/Composer.web.tsx86
-rw-r--r--src/view/shell/Drawer.tsx4
-rw-r--r--src/view/shell/bottom-bar/BottomBar.tsx4
-rw-r--r--src/view/shell/bottom-bar/BottomBarWeb.tsx2
-rw-r--r--src/view/shell/desktop/LeftNav.tsx144
-rw-r--r--src/view/shell/desktop/RightNav.tsx4
-rw-r--r--src/view/shell/index.tsx4
-rw-r--r--src/view/shell/index.web.tsx4
72 files changed, 2804 insertions, 2772 deletions
diff --git a/.eslintrc.js b/.eslintrc.js
index c7c987751..bc4a2a394 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -26,4 +26,7 @@ module.exports = {
     '*.html',
     'bskyweb',
   ],
+  settings: {
+    componentWrapperFunctions: ['observer'],
+  },
 }
diff --git a/src/App.native.tsx b/src/App.native.tsx
index ad37aa099..09782a875 100644
--- a/src/App.native.tsx
+++ b/src/App.native.tsx
@@ -19,7 +19,7 @@ import {handleLink} from './Navigation'
 
 SplashScreen.preventAutoHideAsync()
 
-const App = observer(() => {
+const App = observer(function AppImpl() {
   const [rootStore, setRootStore] = useState<RootStoreModel | undefined>(
     undefined,
   )
diff --git a/src/App.web.tsx b/src/App.web.tsx
index b0f949b8b..41a7189d3 100644
--- a/src/App.web.tsx
+++ b/src/App.web.tsx
@@ -10,7 +10,7 @@ import {ToastContainer} from './view/com/util/Toast.web'
 import {ThemeProvider} from 'lib/ThemeContext'
 import {observer} from 'mobx-react-lite'
 
-const App = observer(() => {
+const App = observer(function AppImpl() {
   const [rootStore, setRootStore] = useState<RootStoreModel | undefined>(
     undefined,
   )
diff --git a/src/Navigation.tsx b/src/Navigation.tsx
index 2422491e2..dac70dfc7 100644
--- a/src/Navigation.tsx
+++ b/src/Navigation.tsx
@@ -330,7 +330,7 @@ function NotificationsTabNavigator() {
   )
 }
 
-const MyProfileTabNavigator = observer(() => {
+const MyProfileTabNavigator = observer(function MyProfileTabNavigatorImpl() {
   const contentStyle = useColorSchemeStyle(styles.bgLight, styles.bgDark)
   const store = useStores()
   return (
@@ -360,7 +360,7 @@ const MyProfileTabNavigator = observer(() => {
  * The FlatNavigator is used by Web to represent the routes
  * in a single ("flat") stack.
  */
-const FlatNavigator = observer(() => {
+const FlatNavigator = observer(function FlatNavigatorImpl() {
   const pal = usePalette('default')
   const unreadCountLabel = useStores().me.notifications.unreadCountLabel
   const title = (page: string) => bskyTitle(page, unreadCountLabel)
diff --git a/src/view/com/auth/LoggedOut.tsx b/src/view/com/auth/LoggedOut.tsx
index 6d3b87dd3..c74c2aa33 100644
--- a/src/view/com/auth/LoggedOut.tsx
+++ b/src/view/com/auth/LoggedOut.tsx
@@ -16,7 +16,7 @@ enum ScreenState {
   S_CreateAccount,
 }
 
-export const LoggedOut = observer(() => {
+export const LoggedOut = observer(function LoggedOutImpl() {
   const pal = usePalette('default')
   const store = useStores()
   const {screen} = useAnalytics()
diff --git a/src/view/com/auth/Onboarding.tsx b/src/view/com/auth/Onboarding.tsx
index 065d4d244..6ea8cd79e 100644
--- a/src/view/com/auth/Onboarding.tsx
+++ b/src/view/com/auth/Onboarding.tsx
@@ -8,7 +8,7 @@ import {useStores} from 'state/index'
 import {Welcome} from './onboarding/Welcome'
 import {RecommendedFeeds} from './onboarding/RecommendedFeeds'
 
-export const Onboarding = observer(() => {
+export const Onboarding = observer(function OnboardingImpl() {
   const pal = usePalette('default')
   const store = useStores()
 
diff --git a/src/view/com/auth/create/CreateAccount.tsx b/src/view/com/auth/create/CreateAccount.tsx
index 8cf1cfaf5..1d64cc067 100644
--- a/src/view/com/auth/create/CreateAccount.tsx
+++ b/src/view/com/auth/create/CreateAccount.tsx
@@ -20,114 +20,116 @@ import {Step1} from './Step1'
 import {Step2} from './Step2'
 import {Step3} from './Step3'
 
-export const CreateAccount = observer(
-  ({onPressBack}: {onPressBack: () => void}) => {
-    const {track, screen} = useAnalytics()
-    const pal = usePalette('default')
-    const store = useStores()
-    const model = React.useMemo(() => new CreateAccountModel(store), [store])
+export const CreateAccount = observer(function CreateAccountImpl({
+  onPressBack,
+}: {
+  onPressBack: () => void
+}) {
+  const {track, screen} = useAnalytics()
+  const pal = usePalette('default')
+  const store = useStores()
+  const model = React.useMemo(() => new CreateAccountModel(store), [store])
 
-    React.useEffect(() => {
-      screen('CreateAccount')
-    }, [screen])
+  React.useEffect(() => {
+    screen('CreateAccount')
+  }, [screen])
 
-    React.useEffect(() => {
-      model.fetchServiceDescription()
-    }, [model])
+  React.useEffect(() => {
+    model.fetchServiceDescription()
+  }, [model])
 
-    const onPressRetryConnect = React.useCallback(
-      () => model.fetchServiceDescription(),
-      [model],
-    )
+  const onPressRetryConnect = React.useCallback(
+    () => model.fetchServiceDescription(),
+    [model],
+  )
 
-    const onPressBackInner = React.useCallback(() => {
-      if (model.canBack) {
-        model.back()
-      } else {
-        onPressBack()
-      }
-    }, [model, onPressBack])
+  const onPressBackInner = React.useCallback(() => {
+    if (model.canBack) {
+      model.back()
+    } else {
+      onPressBack()
+    }
+  }, [model, onPressBack])
 
-    const onPressNext = React.useCallback(async () => {
-      if (!model.canNext) {
-        return
-      }
-      if (model.step < 3) {
-        model.next()
-      } else {
-        try {
-          await model.submit()
-        } catch {
-          // dont need to handle here
-        } finally {
-          track('Try Create Account')
-        }
+  const onPressNext = React.useCallback(async () => {
+    if (!model.canNext) {
+      return
+    }
+    if (model.step < 3) {
+      model.next()
+    } else {
+      try {
+        await model.submit()
+      } catch {
+        // dont need to handle here
+      } finally {
+        track('Try Create Account')
       }
-    }, [model, track])
+    }
+  }, [model, track])
 
-    return (
-      <LoggedOutLayout
-        leadin={`Step ${model.step}`}
-        title="Create Account"
-        description="We're so excited to have you join us!">
-        <ScrollView testID="createAccount" style={pal.view}>
-          <KeyboardAvoidingView behavior="padding">
-            <View style={styles.stepContainer}>
-              {model.step === 1 && <Step1 model={model} />}
-              {model.step === 2 && <Step2 model={model} />}
-              {model.step === 3 && <Step3 model={model} />}
-            </View>
-            <View style={[s.flexRow, s.pl20, s.pr20]}>
+  return (
+    <LoggedOutLayout
+      leadin={`Step ${model.step}`}
+      title="Create Account"
+      description="We're so excited to have you join us!">
+      <ScrollView testID="createAccount" style={pal.view}>
+        <KeyboardAvoidingView behavior="padding">
+          <View style={styles.stepContainer}>
+            {model.step === 1 && <Step1 model={model} />}
+            {model.step === 2 && <Step2 model={model} />}
+            {model.step === 3 && <Step3 model={model} />}
+          </View>
+          <View style={[s.flexRow, s.pl20, s.pr20]}>
+            <TouchableOpacity
+              onPress={onPressBackInner}
+              testID="backBtn"
+              accessibilityRole="button">
+              <Text type="xl" style={pal.link}>
+                Back
+              </Text>
+            </TouchableOpacity>
+            <View style={s.flex1} />
+            {model.canNext ? (
               <TouchableOpacity
-                onPress={onPressBackInner}
-                testID="backBtn"
+                testID="nextBtn"
+                onPress={onPressNext}
                 accessibilityRole="button">
-                <Text type="xl" style={pal.link}>
-                  Back
-                </Text>
-              </TouchableOpacity>
-              <View style={s.flex1} />
-              {model.canNext ? (
-                <TouchableOpacity
-                  testID="nextBtn"
-                  onPress={onPressNext}
-                  accessibilityRole="button">
-                  {model.isProcessing ? (
-                    <ActivityIndicator />
-                  ) : (
-                    <Text type="xl-bold" style={[pal.link, s.pr5]}>
-                      Next
-                    </Text>
-                  )}
-                </TouchableOpacity>
-              ) : model.didServiceDescriptionFetchFail ? (
-                <TouchableOpacity
-                  testID="retryConnectBtn"
-                  onPress={onPressRetryConnect}
-                  accessibilityRole="button"
-                  accessibilityLabel="Retry"
-                  accessibilityHint="Retries account creation"
-                  accessibilityLiveRegion="polite">
+                {model.isProcessing ? (
+                  <ActivityIndicator />
+                ) : (
                   <Text type="xl-bold" style={[pal.link, s.pr5]}>
-                    Retry
-                  </Text>
-                </TouchableOpacity>
-              ) : model.isFetchingServiceDescription ? (
-                <>
-                  <ActivityIndicator color="#fff" />
-                  <Text type="xl" style={[pal.text, s.pr5]}>
-                    Connecting...
+                    Next
                   </Text>
-                </>
-              ) : undefined}
-            </View>
-            <View style={s.footerSpacer} />
-          </KeyboardAvoidingView>
-        </ScrollView>
-      </LoggedOutLayout>
-    )
-  },
-)
+                )}
+              </TouchableOpacity>
+            ) : model.didServiceDescriptionFetchFail ? (
+              <TouchableOpacity
+                testID="retryConnectBtn"
+                onPress={onPressRetryConnect}
+                accessibilityRole="button"
+                accessibilityLabel="Retry"
+                accessibilityHint="Retries account creation"
+                accessibilityLiveRegion="polite">
+                <Text type="xl-bold" style={[pal.link, s.pr5]}>
+                  Retry
+                </Text>
+              </TouchableOpacity>
+            ) : model.isFetchingServiceDescription ? (
+              <>
+                <ActivityIndicator color="#fff" />
+                <Text type="xl" style={[pal.text, s.pr5]}>
+                  Connecting...
+                </Text>
+              </>
+            ) : undefined}
+          </View>
+          <View style={s.footerSpacer} />
+        </KeyboardAvoidingView>
+      </ScrollView>
+    </LoggedOutLayout>
+  )
+})
 
 const styles = StyleSheet.create({
   stepContainer: {
diff --git a/src/view/com/auth/create/Step1.tsx b/src/view/com/auth/create/Step1.tsx
index 5d3dec430..cdd5cb21d 100644
--- a/src/view/com/auth/create/Step1.tsx
+++ b/src/view/com/auth/create/Step1.tsx
@@ -20,7 +20,11 @@ import {LOGIN_INCLUDE_DEV_SERVERS} from 'lib/build-flags'
  * @field Bluesky (default)
  * @field Other (staging, local dev, your own PDS, etc.)
  */
-export const Step1 = observer(({model}: {model: CreateAccountModel}) => {
+export const Step1 = observer(function Step1Impl({
+  model,
+}: {
+  model: CreateAccountModel
+}) {
   const pal = usePalette('default')
   const [isDefaultSelected, setIsDefaultSelected] = React.useState(true)
 
diff --git a/src/view/com/auth/create/Step2.tsx b/src/view/com/auth/create/Step2.tsx
index 5f71469f0..83b0aee40 100644
--- a/src/view/com/auth/create/Step2.tsx
+++ b/src/view/com/auth/create/Step2.tsx
@@ -21,7 +21,11 @@ import {useStores} from 'state/index'
  * @field Birth date
  * @readonly Terms of service & privacy policy
  */
-export const Step2 = observer(({model}: {model: CreateAccountModel}) => {
+export const Step2 = observer(function Step2Impl({
+  model,
+}: {
+  model: CreateAccountModel
+}) {
   const pal = usePalette('default')
   const store = useStores()
 
diff --git a/src/view/com/auth/create/Step3.tsx b/src/view/com/auth/create/Step3.tsx
index f35777d27..beb756ac1 100644
--- a/src/view/com/auth/create/Step3.tsx
+++ b/src/view/com/auth/create/Step3.tsx
@@ -13,7 +13,11 @@ import {ErrorMessage} from 'view/com/util/error/ErrorMessage'
 /** STEP 3: Your user handle
  * @field User handle
  */
-export const Step3 = observer(({model}: {model: CreateAccountModel}) => {
+export const Step3 = observer(function Step3Impl({
+  model,
+}: {
+  model: CreateAccountModel
+}) {
   const pal = usePalette('default')
   return (
     <View>
diff --git a/src/view/com/auth/onboarding/RecommendedFeeds.tsx b/src/view/com/auth/onboarding/RecommendedFeeds.tsx
index 92d12f60b..99cdcafd0 100644
--- a/src/view/com/auth/onboarding/RecommendedFeeds.tsx
+++ b/src/view/com/auth/onboarding/RecommendedFeeds.tsx
@@ -15,7 +15,9 @@ import {RECOMMENDED_FEEDS} from 'lib/constants'
 type Props = {
   next: () => void
 }
-export const RecommendedFeeds = observer(({next}: Props) => {
+export const RecommendedFeeds = observer(function RecommendedFeedsImpl({
+  next,
+}: Props) {
   const pal = usePalette('default')
   const {isTabletOrMobile} = useWebMediaQueries()
 
diff --git a/src/view/com/auth/onboarding/RecommendedFeedsItem.tsx b/src/view/com/auth/onboarding/RecommendedFeedsItem.tsx
index d16b3213e..e5d12273a 100644
--- a/src/view/com/auth/onboarding/RecommendedFeedsItem.tsx
+++ b/src/view/com/auth/onboarding/RecommendedFeedsItem.tsx
@@ -13,130 +13,134 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
 import {makeRecordUri} from 'lib/strings/url-helpers'
 import {sanitizeHandle} from 'lib/strings/handles'
 
-export const RecommendedFeedsItem = observer(
-  ({did, rkey}: {did: string; rkey: string}) => {
-    const {isMobile} = useWebMediaQueries()
-    const pal = usePalette('default')
-    const uri = makeRecordUri(did, 'app.bsky.feed.generator', rkey)
-    const item = useCustomFeed(uri)
-    if (!item) return null
-    const onToggle = async () => {
-      if (item.isSaved) {
-        try {
-          await item.unsave()
-        } catch (e) {
-          Toast.show('There was an issue contacting your server')
-          console.error('Failed to unsave feed', {e})
-        }
-      } else {
-        try {
-          await item.save()
-          await item.pin()
-        } catch (e) {
-          Toast.show('There was an issue contacting your server')
-          console.error('Failed to pin feed', {e})
-        }
+export const RecommendedFeedsItem = observer(function RecommendedFeedsItemImpl({
+  did,
+  rkey,
+}: {
+  did: string
+  rkey: string
+}) {
+  const {isMobile} = useWebMediaQueries()
+  const pal = usePalette('default')
+  const uri = makeRecordUri(did, 'app.bsky.feed.generator', rkey)
+  const item = useCustomFeed(uri)
+  if (!item) return null
+  const onToggle = async () => {
+    if (item.isSaved) {
+      try {
+        await item.unsave()
+      } catch (e) {
+        Toast.show('There was an issue contacting your server')
+        console.error('Failed to unsave feed', {e})
+      }
+    } else {
+      try {
+        await item.save()
+        await item.pin()
+      } catch (e) {
+        Toast.show('There was an issue contacting your server')
+        console.error('Failed to pin feed', {e})
       }
     }
-    return (
-      <View testID={`feed-${item.displayName}`}>
-        <View
-          style={[
-            pal.border,
-            {
-              flex: isMobile ? 1 : undefined,
-              flexDirection: 'row',
-              gap: 18,
-              maxWidth: isMobile ? undefined : 670,
-              borderRightWidth: isMobile ? undefined : 1,
-              paddingHorizontal: 24,
-              paddingVertical: isMobile ? 12 : 24,
-              borderTopWidth: 1,
-            },
-          ]}>
-          <View style={{marginTop: 2}}>
-            <UserAvatar type="algo" size={42} avatar={item.data.avatar} />
-          </View>
-          <View style={{flex: isMobile ? 1 : undefined}}>
+  }
+  return (
+    <View testID={`feed-${item.displayName}`}>
+      <View
+        style={[
+          pal.border,
+          {
+            flex: isMobile ? 1 : undefined,
+            flexDirection: 'row',
+            gap: 18,
+            maxWidth: isMobile ? undefined : 670,
+            borderRightWidth: isMobile ? undefined : 1,
+            paddingHorizontal: 24,
+            paddingVertical: isMobile ? 12 : 24,
+            borderTopWidth: 1,
+          },
+        ]}>
+        <View style={{marginTop: 2}}>
+          <UserAvatar type="algo" size={42} avatar={item.data.avatar} />
+        </View>
+        <View style={{flex: isMobile ? 1 : undefined}}>
+          <Text
+            type="2xl-bold"
+            numberOfLines={1}
+            style={[pal.text, {fontSize: 19}]}>
+            {item.displayName}
+          </Text>
+
+          <Text style={[pal.textLight, {marginBottom: 8}]} numberOfLines={1}>
+            by {sanitizeHandle(item.data.creator.handle, '@')}
+          </Text>
+
+          {item.data.description ? (
             <Text
-              type="2xl-bold"
-              numberOfLines={1}
-              style={[pal.text, {fontSize: 19}]}>
-              {item.displayName}
+              type="xl"
+              style={[
+                pal.text,
+                {
+                  flex: isMobile ? 1 : undefined,
+                  maxWidth: 550,
+                  marginBottom: 18,
+                },
+              ]}
+              numberOfLines={6}>
+              {item.data.description}
             </Text>
+          ) : null}
 
-            <Text style={[pal.textLight, {marginBottom: 8}]} numberOfLines={1}>
-              by {sanitizeHandle(item.data.creator.handle, '@')}
-            </Text>
+          <View style={{flexDirection: 'row', alignItems: 'center', gap: 12}}>
+            <Button
+              type="inverted"
+              style={{paddingVertical: 6}}
+              onPress={onToggle}>
+              <View
+                style={{
+                  flexDirection: 'row',
+                  alignItems: 'center',
+                  paddingRight: 2,
+                  gap: 6,
+                }}>
+                {item.isSaved ? (
+                  <>
+                    <FontAwesomeIcon
+                      icon="check"
+                      size={16}
+                      color={pal.colors.textInverted}
+                    />
+                    <Text type="lg-medium" style={pal.textInverted}>
+                      Added
+                    </Text>
+                  </>
+                ) : (
+                  <>
+                    <FontAwesomeIcon
+                      icon="plus"
+                      size={16}
+                      color={pal.colors.textInverted}
+                    />
+                    <Text type="lg-medium" style={pal.textInverted}>
+                      Add
+                    </Text>
+                  </>
+                )}
+              </View>
+            </Button>
 
-            {item.data.description ? (
-              <Text
-                type="xl"
-                style={[
-                  pal.text,
-                  {
-                    flex: isMobile ? 1 : undefined,
-                    maxWidth: 550,
-                    marginBottom: 18,
-                  },
-                ]}
-                numberOfLines={6}>
-                {item.data.description}
+            <View style={{flexDirection: 'row', gap: 4}}>
+              <HeartIcon
+                size={16}
+                strokeWidth={2.5}
+                style={[pal.textLight, {position: 'relative', top: 2}]}
+              />
+              <Text type="lg-medium" style={[pal.text, pal.textLight]}>
+                {item.data.likeCount || 0}
               </Text>
-            ) : null}
-
-            <View style={{flexDirection: 'row', alignItems: 'center', gap: 12}}>
-              <Button
-                type="inverted"
-                style={{paddingVertical: 6}}
-                onPress={onToggle}>
-                <View
-                  style={{
-                    flexDirection: 'row',
-                    alignItems: 'center',
-                    paddingRight: 2,
-                    gap: 6,
-                  }}>
-                  {item.isSaved ? (
-                    <>
-                      <FontAwesomeIcon
-                        icon="check"
-                        size={16}
-                        color={pal.colors.textInverted}
-                      />
-                      <Text type="lg-medium" style={pal.textInverted}>
-                        Added
-                      </Text>
-                    </>
-                  ) : (
-                    <>
-                      <FontAwesomeIcon
-                        icon="plus"
-                        size={16}
-                        color={pal.colors.textInverted}
-                      />
-                      <Text type="lg-medium" style={pal.textInverted}>
-                        Add
-                      </Text>
-                    </>
-                  )}
-                </View>
-              </Button>
-
-              <View style={{flexDirection: 'row', gap: 4}}>
-                <HeartIcon
-                  size={16}
-                  strokeWidth={2.5}
-                  style={[pal.textLight, {position: 'relative', top: 2}]}
-                />
-                <Text type="lg-medium" style={[pal.text, pal.textLight]}>
-                  {item.data.likeCount || 0}
-                </Text>
-              </View>
             </View>
           </View>
         </View>
       </View>
-    )
-  },
-)
+    </View>
+  )
+})
diff --git a/src/view/com/auth/onboarding/WelcomeDesktop.tsx b/src/view/com/auth/onboarding/WelcomeDesktop.tsx
index 7b7555ace..c066e9bd5 100644
--- a/src/view/com/auth/onboarding/WelcomeDesktop.tsx
+++ b/src/view/com/auth/onboarding/WelcomeDesktop.tsx
@@ -14,7 +14,9 @@ type Props = {
   skip: () => void
 }
 
-export const WelcomeDesktop = observer(({next}: Props) => {
+export const WelcomeDesktop = observer(function WelcomeDesktopImpl({
+  next,
+}: Props) {
   const pal = usePalette('default')
   const horizontal = useMediaQuery({minWidth: 1300})
   const title = (
diff --git a/src/view/com/auth/onboarding/WelcomeMobile.tsx b/src/view/com/auth/onboarding/WelcomeMobile.tsx
index 0f627ad0b..19c8d52d0 100644
--- a/src/view/com/auth/onboarding/WelcomeMobile.tsx
+++ b/src/view/com/auth/onboarding/WelcomeMobile.tsx
@@ -13,7 +13,10 @@ type Props = {
   skip: () => void
 }
 
-export const WelcomeMobile = observer(({next, skip}: Props) => {
+export const WelcomeMobile = observer(function WelcomeMobileImpl({
+  next,
+  skip,
+}: Props) {
   const pal = usePalette('default')
 
   return (
diff --git a/src/view/com/auth/withAuthRequired.tsx b/src/view/com/auth/withAuthRequired.tsx
index c81c2d5df..25d12165f 100644
--- a/src/view/com/auth/withAuthRequired.tsx
+++ b/src/view/com/auth/withAuthRequired.tsx
@@ -17,7 +17,7 @@ import {STATUS_PAGE_URL} from 'lib/constants'
 export const withAuthRequired = <P extends object>(
   Component: React.ComponentType<P>,
 ): React.FC<P> =>
-  observer((props: P) => {
+  observer(function AuthRequired(props: P) {
     const store = useStores()
     if (store.session.isResumingSession) {
       return <Loading />
diff --git a/src/view/com/composer/photos/Gallery.tsx b/src/view/com/composer/photos/Gallery.tsx
index d5465f79a..fa3f29cf2 100644
--- a/src/view/com/composer/photos/Gallery.tsx
+++ b/src/view/com/composer/photos/Gallery.tsx
@@ -16,7 +16,7 @@ interface Props {
   gallery: GalleryModel
 }
 
-export const Gallery = observer(function ({gallery}: Props) {
+export const Gallery = observer(function GalleryImpl({gallery}: Props) {
   const store = useStores()
   const pal = usePalette('default')
   const {isMobile} = useWebMediaQueries()
diff --git a/src/view/com/composer/text-input/mobile/Autocomplete.tsx b/src/view/com/composer/text-input/mobile/Autocomplete.tsx
index c9b8b84b1..d808d896f 100644
--- a/src/view/com/composer/text-input/mobile/Autocomplete.tsx
+++ b/src/view/com/composer/text-input/mobile/Autocomplete.tsx
@@ -8,90 +8,88 @@ import {Text} from 'view/com/util/text/Text'
 import {UserAvatar} from 'view/com/util/UserAvatar'
 import {useGrapheme} from '../hooks/useGrapheme'
 
-export const Autocomplete = observer(
-  ({
-    view,
-    onSelect,
-  }: {
-    view: UserAutocompleteModel
-    onSelect: (item: string) => void
-  }) => {
-    const pal = usePalette('default')
-    const positionInterp = useAnimatedValue(0)
-    const {getGraphemeString} = useGrapheme()
+export const Autocomplete = observer(function AutocompleteImpl({
+  view,
+  onSelect,
+}: {
+  view: UserAutocompleteModel
+  onSelect: (item: string) => void
+}) {
+  const pal = usePalette('default')
+  const positionInterp = useAnimatedValue(0)
+  const {getGraphemeString} = useGrapheme()
 
-    useEffect(() => {
-      Animated.timing(positionInterp, {
-        toValue: view.isActive ? 1 : 0,
-        duration: 200,
-        useNativeDriver: true,
-      }).start()
-    }, [positionInterp, view.isActive])
+  useEffect(() => {
+    Animated.timing(positionInterp, {
+      toValue: view.isActive ? 1 : 0,
+      duration: 200,
+      useNativeDriver: true,
+    }).start()
+  }, [positionInterp, view.isActive])
 
-    const topAnimStyle = {
-      transform: [
-        {
-          translateY: positionInterp.interpolate({
-            inputRange: [0, 1],
-            outputRange: [200, 0],
-          }),
-        },
-      ],
-    }
+  const topAnimStyle = {
+    transform: [
+      {
+        translateY: positionInterp.interpolate({
+          inputRange: [0, 1],
+          outputRange: [200, 0],
+        }),
+      },
+    ],
+  }
 
-    return (
-      <Animated.View style={topAnimStyle}>
-        {view.isActive ? (
-          <View style={[pal.view, styles.container, pal.border]}>
-            {view.suggestions.length > 0 ? (
-              view.suggestions.slice(0, 5).map(item => {
-                // Eventually use an average length
-                const MAX_CHARS = 40
-                const MAX_HANDLE_CHARS = 20
+  return (
+    <Animated.View style={topAnimStyle}>
+      {view.isActive ? (
+        <View style={[pal.view, styles.container, pal.border]}>
+          {view.suggestions.length > 0 ? (
+            view.suggestions.slice(0, 5).map(item => {
+              // Eventually use an average length
+              const MAX_CHARS = 40
+              const MAX_HANDLE_CHARS = 20
 
-                // Using this approach because styling is not respecting
-                // bounding box wrapping (before converting to ellipsis)
-                const {name: displayHandle, remainingCharacters} =
-                  getGraphemeString(item.handle, MAX_HANDLE_CHARS)
+              // Using this approach because styling is not respecting
+              // bounding box wrapping (before converting to ellipsis)
+              const {name: displayHandle, remainingCharacters} =
+                getGraphemeString(item.handle, MAX_HANDLE_CHARS)
 
-                const {name: displayName} = getGraphemeString(
-                  item.displayName ?? item.handle,
-                  MAX_CHARS -
-                    MAX_HANDLE_CHARS +
-                    (remainingCharacters > 0 ? remainingCharacters : 0),
-                )
+              const {name: displayName} = getGraphemeString(
+                item.displayName ?? item.handle,
+                MAX_CHARS -
+                  MAX_HANDLE_CHARS +
+                  (remainingCharacters > 0 ? remainingCharacters : 0),
+              )
 
-                return (
-                  <TouchableOpacity
-                    testID="autocompleteButton"
-                    key={item.handle}
-                    style={[pal.border, styles.item]}
-                    onPress={() => onSelect(item.handle)}
-                    accessibilityLabel={`Select ${item.handle}`}
-                    accessibilityHint="">
-                    <View style={styles.avatarAndHandle}>
-                      <UserAvatar avatar={item.avatar ?? null} size={24} />
-                      <Text type="md-medium" style={pal.text}>
-                        {displayName}
-                      </Text>
-                    </View>
-                    <Text type="sm" style={pal.textLight} numberOfLines={1}>
-                      @{displayHandle}
+              return (
+                <TouchableOpacity
+                  testID="autocompleteButton"
+                  key={item.handle}
+                  style={[pal.border, styles.item]}
+                  onPress={() => onSelect(item.handle)}
+                  accessibilityLabel={`Select ${item.handle}`}
+                  accessibilityHint="">
+                  <View style={styles.avatarAndHandle}>
+                    <UserAvatar avatar={item.avatar ?? null} size={24} />
+                    <Text type="md-medium" style={pal.text}>
+                      {displayName}
                     </Text>
-                  </TouchableOpacity>
-                )
-              })
-            ) : (
-              <Text type="sm" style={[pal.text, pal.border, styles.noResults]}>
-                No result
-              </Text>
-            )}
-          </View>
-        ) : null}
-      </Animated.View>
-    )
-  },
-)
+                  </View>
+                  <Text type="sm" style={pal.textLight} numberOfLines={1}>
+                    @{displayHandle}
+                  </Text>
+                </TouchableOpacity>
+              )
+            })
+          ) : (
+            <Text type="sm" style={[pal.text, pal.border, styles.noResults]}>
+              No result
+            </Text>
+          )}
+        </View>
+      ) : null}
+    </Animated.View>
+  )
+})
 
 const styles = StyleSheet.create({
   container: {
diff --git a/src/view/com/feeds/CustomFeed.tsx b/src/view/com/feeds/CustomFeed.tsx
index 1635d17fc..e6df15a15 100644
--- a/src/view/com/feeds/CustomFeed.tsx
+++ b/src/view/com/feeds/CustomFeed.tsx
@@ -15,120 +15,118 @@ import {AtUri} from '@atproto/api'
 import * as Toast from 'view/com/util/Toast'
 import {sanitizeHandle} from 'lib/strings/handles'
 
-export const CustomFeed = observer(
-  ({
-    item,
-    style,
-    showSaveBtn = false,
-    showDescription = false,
-    showLikes = false,
-  }: {
-    item: CustomFeedModel
-    style?: StyleProp<ViewStyle>
-    showSaveBtn?: boolean
-    showDescription?: boolean
-    showLikes?: boolean
-  }) => {
-    const store = useStores()
-    const pal = usePalette('default')
-    const navigation = useNavigation<NavigationProp>()
+export const CustomFeed = observer(function CustomFeedImpl({
+  item,
+  style,
+  showSaveBtn = false,
+  showDescription = false,
+  showLikes = false,
+}: {
+  item: CustomFeedModel
+  style?: StyleProp<ViewStyle>
+  showSaveBtn?: boolean
+  showDescription?: boolean
+  showLikes?: boolean
+}) {
+  const store = useStores()
+  const pal = usePalette('default')
+  const navigation = useNavigation<NavigationProp>()
 
-    const onToggleSaved = React.useCallback(async () => {
-      if (item.isSaved) {
-        store.shell.openModal({
-          name: 'confirm',
-          title: 'Remove from my feeds',
-          message: `Remove ${item.displayName} from my feeds?`,
-          onPressConfirm: async () => {
-            try {
-              await store.me.savedFeeds.unsave(item)
-              Toast.show('Removed from my feeds')
-            } catch (e) {
-              Toast.show('There was an issue contacting your server')
-              store.log.error('Failed to unsave feed', {e})
-            }
-          },
-        })
-      } else {
-        try {
-          await store.me.savedFeeds.save(item)
-          Toast.show('Added to my feeds')
-        } catch (e) {
-          Toast.show('There was an issue contacting your server')
-          store.log.error('Failed to save feed', {e})
-        }
+  const onToggleSaved = React.useCallback(async () => {
+    if (item.isSaved) {
+      store.shell.openModal({
+        name: 'confirm',
+        title: 'Remove from my feeds',
+        message: `Remove ${item.displayName} from my feeds?`,
+        onPressConfirm: async () => {
+          try {
+            await store.me.savedFeeds.unsave(item)
+            Toast.show('Removed from my feeds')
+          } catch (e) {
+            Toast.show('There was an issue contacting your server')
+            store.log.error('Failed to unsave feed', {e})
+          }
+        },
+      })
+    } else {
+      try {
+        await store.me.savedFeeds.save(item)
+        Toast.show('Added to my feeds')
+      } catch (e) {
+        Toast.show('There was an issue contacting your server')
+        store.log.error('Failed to save feed', {e})
       }
-    }, [store, item])
+    }
+  }, [store, item])
 
-    return (
-      <Pressable
-        testID={`feed-${item.displayName}`}
-        accessibilityRole="button"
-        style={[styles.container, pal.border, style]}
-        onPress={() => {
-          navigation.push('CustomFeed', {
-            name: item.data.creator.did,
-            rkey: new AtUri(item.data.uri).rkey,
-          })
-        }}
-        key={item.data.uri}>
-        <View style={[styles.headerContainer]}>
-          <View style={[s.mr10]}>
-            <UserAvatar type="algo" size={36} avatar={item.data.avatar} />
-          </View>
-          <View style={[styles.headerTextContainer]}>
-            <Text style={[pal.text, s.bold]} numberOfLines={3}>
-              {item.displayName}
-            </Text>
-            <Text style={[pal.textLight]} numberOfLines={3}>
-              by {sanitizeHandle(item.data.creator.handle, '@')}
-            </Text>
-          </View>
-          {showSaveBtn && (
-            <View>
-              <Pressable
-                accessibilityRole="button"
-                accessibilityLabel={
-                  item.isSaved ? 'Remove from my feeds' : 'Add to my feeds'
-                }
-                accessibilityHint=""
-                onPress={onToggleSaved}
-                hitSlop={15}
-                style={styles.btn}>
-                {item.isSaved ? (
-                  <FontAwesomeIcon
-                    icon={['far', 'trash-can']}
-                    size={19}
-                    color={pal.colors.icon}
-                  />
-                ) : (
-                  <FontAwesomeIcon
-                    icon="plus"
-                    size={18}
-                    color={pal.colors.link}
-                  />
-                )}
-              </Pressable>
-            </View>
-          )}
+  return (
+    <Pressable
+      testID={`feed-${item.displayName}`}
+      accessibilityRole="button"
+      style={[styles.container, pal.border, style]}
+      onPress={() => {
+        navigation.push('CustomFeed', {
+          name: item.data.creator.did,
+          rkey: new AtUri(item.data.uri).rkey,
+        })
+      }}
+      key={item.data.uri}>
+      <View style={[styles.headerContainer]}>
+        <View style={[s.mr10]}>
+          <UserAvatar type="algo" size={36} avatar={item.data.avatar} />
         </View>
-
-        {showDescription && item.data.description ? (
-          <Text style={[pal.textLight, styles.description]} numberOfLines={3}>
-            {item.data.description}
+        <View style={[styles.headerTextContainer]}>
+          <Text style={[pal.text, s.bold]} numberOfLines={3}>
+            {item.displayName}
           </Text>
-        ) : null}
-
-        {showLikes ? (
-          <Text type="sm-medium" style={[pal.text, pal.textLight]}>
-            Liked by {item.data.likeCount || 0}{' '}
-            {pluralize(item.data.likeCount || 0, 'user')}
+          <Text style={[pal.textLight]} numberOfLines={3}>
+            by {sanitizeHandle(item.data.creator.handle, '@')}
           </Text>
-        ) : null}
-      </Pressable>
-    )
-  },
-)
+        </View>
+        {showSaveBtn && (
+          <View>
+            <Pressable
+              accessibilityRole="button"
+              accessibilityLabel={
+                item.isSaved ? 'Remove from my feeds' : 'Add to my feeds'
+              }
+              accessibilityHint=""
+              onPress={onToggleSaved}
+              hitSlop={15}
+              style={styles.btn}>
+              {item.isSaved ? (
+                <FontAwesomeIcon
+                  icon={['far', 'trash-can']}
+                  size={19}
+                  color={pal.colors.icon}
+                />
+              ) : (
+                <FontAwesomeIcon
+                  icon="plus"
+                  size={18}
+                  color={pal.colors.link}
+                />
+              )}
+            </Pressable>
+          </View>
+        )}
+      </View>
+
+      {showDescription && item.data.description ? (
+        <Text style={[pal.textLight, styles.description]} numberOfLines={3}>
+          {item.data.description}
+        </Text>
+      ) : null}
+
+      {showLikes ? (
+        <Text type="sm-medium" style={[pal.text, pal.textLight]}>
+          Liked by {item.data.likeCount || 0}{' '}
+          {pluralize(item.data.likeCount || 0, 'user')}
+        </Text>
+      ) : null}
+    </Pressable>
+  )
+})
 
 const styles = StyleSheet.create({
   container: {
diff --git a/src/view/com/lists/ListItems.tsx b/src/view/com/lists/ListItems.tsx
index d611bc504..b78cf83cf 100644
--- a/src/view/com/lists/ListItems.tsx
+++ b/src/view/com/lists/ListItems.tsx
@@ -35,319 +35,314 @@ const EMPTY_ITEM = {_reactKey: '__empty__'}
 const ERROR_ITEM = {_reactKey: '__error__'}
 const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'}
 
-export const ListItems = observer(
-  ({
-    list,
-    style,
-    scrollElRef,
-    onPressTryAgain,
-    onToggleSubscribed,
-    onPressEditList,
-    onPressDeleteList,
-    onPressShareList,
-    onPressReportList,
-    renderEmptyState,
-    testID,
-    headerOffset = 0,
-  }: {
-    list: ListModel
-    style?: StyleProp<ViewStyle>
-    scrollElRef?: MutableRefObject<FlatList<any> | null>
-    onPressTryAgain?: () => void
-    onToggleSubscribed: () => void
-    onPressEditList: () => void
-    onPressDeleteList: () => void
-    onPressShareList: () => void
-    onPressReportList: () => void
-    renderEmptyState?: () => JSX.Element
-    testID?: string
-    headerOffset?: number
-  }) => {
-    const pal = usePalette('default')
-    const store = useStores()
-    const {track} = useAnalytics()
-    const [isRefreshing, setIsRefreshing] = React.useState(false)
+export const ListItems = observer(function ListItemsImpl({
+  list,
+  style,
+  scrollElRef,
+  onPressTryAgain,
+  onToggleSubscribed,
+  onPressEditList,
+  onPressDeleteList,
+  onPressShareList,
+  onPressReportList,
+  renderEmptyState,
+  testID,
+  headerOffset = 0,
+}: {
+  list: ListModel
+  style?: StyleProp<ViewStyle>
+  scrollElRef?: MutableRefObject<FlatList<any> | null>
+  onPressTryAgain?: () => void
+  onToggleSubscribed: () => void
+  onPressEditList: () => void
+  onPressDeleteList: () => void
+  onPressShareList: () => void
+  onPressReportList: () => void
+  renderEmptyState?: () => JSX.Element
+  testID?: string
+  headerOffset?: number
+}) {
+  const pal = usePalette('default')
+  const store = useStores()
+  const {track} = useAnalytics()
+  const [isRefreshing, setIsRefreshing] = React.useState(false)
 
-    const data = React.useMemo(() => {
-      let items: any[] = [HEADER_ITEM]
-      if (list.hasLoaded) {
-        if (list.hasError) {
-          items = items.concat([ERROR_ITEM])
-        }
-        if (list.isEmpty) {
-          items = items.concat([EMPTY_ITEM])
-        } else {
-          items = items.concat(list.items)
-        }
-        if (list.loadMoreError) {
-          items = items.concat([LOAD_MORE_ERROR_ITEM])
-        }
-      } else if (list.isLoading) {
-        items = items.concat([LOADING_ITEM])
+  const data = React.useMemo(() => {
+    let items: any[] = [HEADER_ITEM]
+    if (list.hasLoaded) {
+      if (list.hasError) {
+        items = items.concat([ERROR_ITEM])
       }
-      return items
-    }, [
-      list.hasError,
-      list.hasLoaded,
-      list.isLoading,
-      list.isEmpty,
-      list.items,
-      list.loadMoreError,
-    ])
+      if (list.isEmpty) {
+        items = items.concat([EMPTY_ITEM])
+      } else {
+        items = items.concat(list.items)
+      }
+      if (list.loadMoreError) {
+        items = items.concat([LOAD_MORE_ERROR_ITEM])
+      }
+    } else if (list.isLoading) {
+      items = items.concat([LOADING_ITEM])
+    }
+    return items
+  }, [
+    list.hasError,
+    list.hasLoaded,
+    list.isLoading,
+    list.isEmpty,
+    list.items,
+    list.loadMoreError,
+  ])
 
-    // events
-    // =
+  // events
+  // =
 
-    const onRefresh = React.useCallback(async () => {
-      track('Lists:onRefresh')
-      setIsRefreshing(true)
-      try {
-        await list.refresh()
-      } catch (err) {
-        list.rootStore.log.error('Failed to refresh lists', err)
-      }
-      setIsRefreshing(false)
-    }, [list, track, setIsRefreshing])
+  const onRefresh = React.useCallback(async () => {
+    track('Lists:onRefresh')
+    setIsRefreshing(true)
+    try {
+      await list.refresh()
+    } catch (err) {
+      list.rootStore.log.error('Failed to refresh lists', err)
+    }
+    setIsRefreshing(false)
+  }, [list, track, setIsRefreshing])
 
-    const onEndReached = React.useCallback(async () => {
-      track('Lists:onEndReached')
-      try {
-        await list.loadMore()
-      } catch (err) {
-        list.rootStore.log.error('Failed to load more lists', err)
-      }
-    }, [list, track])
+  const onEndReached = React.useCallback(async () => {
+    track('Lists:onEndReached')
+    try {
+      await list.loadMore()
+    } catch (err) {
+      list.rootStore.log.error('Failed to load more lists', err)
+    }
+  }, [list, track])
+
+  const onPressRetryLoadMore = React.useCallback(() => {
+    list.retryLoadMore()
+  }, [list])
 
-    const onPressRetryLoadMore = React.useCallback(() => {
-      list.retryLoadMore()
-    }, [list])
+  const onPressEditMembership = React.useCallback(
+    (profile: AppBskyActorDefs.ProfileViewBasic) => {
+      store.shell.openModal({
+        name: 'list-add-remove-user',
+        subject: profile.did,
+        displayName: profile.displayName || profile.handle,
+        onUpdate() {
+          list.refresh()
+        },
+      })
+    },
+    [store, list],
+  )
 
-    const onPressEditMembership = React.useCallback(
-      (profile: AppBskyActorDefs.ProfileViewBasic) => {
-        store.shell.openModal({
-          name: 'list-add-remove-user',
-          subject: profile.did,
-          displayName: profile.displayName || profile.handle,
-          onUpdate() {
-            list.refresh()
-          },
-        })
-      },
-      [store, list],
-    )
+  // rendering
+  // =
 
-    // rendering
-    // =
+  const renderMemberButton = React.useCallback(
+    (profile: AppBskyActorDefs.ProfileViewBasic) => {
+      if (!list.isOwner) {
+        return null
+      }
+      return (
+        <Button
+          type="default"
+          label="Edit"
+          onPress={() => onPressEditMembership(profile)}
+        />
+      )
+    },
+    [list, onPressEditMembership],
+  )
 
-    const renderMemberButton = React.useCallback(
-      (profile: AppBskyActorDefs.ProfileViewBasic) => {
-        if (!list.isOwner) {
-          return null
+  const renderItem = React.useCallback(
+    ({item}: {item: any}) => {
+      if (item === EMPTY_ITEM) {
+        if (renderEmptyState) {
+          return renderEmptyState()
         }
+        return <View />
+      } else if (item === HEADER_ITEM) {
+        return list.list ? (
+          <ListHeader
+            list={list.list}
+            isOwner={list.isOwner}
+            onToggleSubscribed={onToggleSubscribed}
+            onPressEditList={onPressEditList}
+            onPressDeleteList={onPressDeleteList}
+            onPressShareList={onPressShareList}
+            onPressReportList={onPressReportList}
+          />
+        ) : null
+      } else if (item === ERROR_ITEM) {
         return (
-          <Button
-            type="default"
-            label="Edit"
-            onPress={() => onPressEditMembership(profile)}
+          <ErrorMessage
+            message={list.error}
+            onPressTryAgain={onPressTryAgain}
           />
         )
-      },
-      [list, onPressEditMembership],
-    )
-
-    const renderItem = React.useCallback(
-      ({item}: {item: any}) => {
-        if (item === EMPTY_ITEM) {
-          if (renderEmptyState) {
-            return renderEmptyState()
-          }
-          return <View />
-        } else if (item === HEADER_ITEM) {
-          return list.list ? (
-            <ListHeader
-              list={list.list}
-              isOwner={list.isOwner}
-              onToggleSubscribed={onToggleSubscribed}
-              onPressEditList={onPressEditList}
-              onPressDeleteList={onPressDeleteList}
-              onPressShareList={onPressShareList}
-              onPressReportList={onPressReportList}
-            />
-          ) : null
-        } else if (item === ERROR_ITEM) {
-          return (
-            <ErrorMessage
-              message={list.error}
-              onPressTryAgain={onPressTryAgain}
-            />
-          )
-        } else if (item === LOAD_MORE_ERROR_ITEM) {
-          return (
-            <LoadMoreRetryBtn
-              label="There was an issue fetching the list. Tap here to try again."
-              onPress={onPressRetryLoadMore}
-            />
-          )
-        } else if (item === LOADING_ITEM) {
-          return <ProfileCardFeedLoadingPlaceholder />
-        }
+      } else if (item === LOAD_MORE_ERROR_ITEM) {
         return (
-          <ProfileCard
-            testID={`user-${
-              (item as AppBskyGraphDefs.ListItemView).subject.handle
-            }`}
-            profile={(item as AppBskyGraphDefs.ListItemView).subject}
-            renderButton={renderMemberButton}
+          <LoadMoreRetryBtn
+            label="There was an issue fetching the list. Tap here to try again."
+            onPress={onPressRetryLoadMore}
           />
         )
-      },
-      [
-        renderMemberButton,
-        renderEmptyState,
-        list.list,
-        list.isOwner,
-        list.error,
-        onToggleSubscribed,
-        onPressEditList,
-        onPressDeleteList,
-        onPressShareList,
-        onPressReportList,
-        onPressTryAgain,
-        onPressRetryLoadMore,
-      ],
-    )
+      } else if (item === LOADING_ITEM) {
+        return <ProfileCardFeedLoadingPlaceholder />
+      }
+      return (
+        <ProfileCard
+          testID={`user-${
+            (item as AppBskyGraphDefs.ListItemView).subject.handle
+          }`}
+          profile={(item as AppBskyGraphDefs.ListItemView).subject}
+          renderButton={renderMemberButton}
+        />
+      )
+    },
+    [
+      renderMemberButton,
+      renderEmptyState,
+      list.list,
+      list.isOwner,
+      list.error,
+      onToggleSubscribed,
+      onPressEditList,
+      onPressDeleteList,
+      onPressShareList,
+      onPressReportList,
+      onPressTryAgain,
+      onPressRetryLoadMore,
+    ],
+  )
 
-    const Footer = React.useCallback(
-      () =>
-        list.isLoading ? (
-          <View style={styles.feedFooter}>
-            <ActivityIndicator />
-          </View>
-        ) : (
-          <View />
-        ),
-      [list],
-    )
+  const Footer = React.useCallback(
+    () =>
+      list.isLoading ? (
+        <View style={styles.feedFooter}>
+          <ActivityIndicator />
+        </View>
+      ) : (
+        <View />
+      ),
+    [list],
+  )
 
-    return (
-      <View testID={testID} style={style}>
-        {data.length > 0 && (
-          <FlatList
-            testID={testID ? `${testID}-flatlist` : undefined}
-            ref={scrollElRef}
-            data={data}
-            keyExtractor={item => item._reactKey}
-            renderItem={renderItem}
-            ListFooterComponent={Footer}
-            refreshControl={
-              <RefreshControl
-                refreshing={isRefreshing}
-                onRefresh={onRefresh}
-                tintColor={pal.colors.text}
-                titleColor={pal.colors.text}
-                progressViewOffset={headerOffset}
-              />
-            }
-            contentContainerStyle={s.contentContainer}
-            style={{paddingTop: headerOffset}}
-            onEndReached={onEndReached}
-            onEndReachedThreshold={0.6}
-            removeClippedSubviews={true}
-            contentOffset={{x: 0, y: headerOffset * -1}}
-            // @ts-ignore our .web version only -prf
-            desktopFixedHeight
-          />
-        )}
-      </View>
-    )
-  },
-)
+  return (
+    <View testID={testID} style={style}>
+      {data.length > 0 && (
+        <FlatList
+          testID={testID ? `${testID}-flatlist` : undefined}
+          ref={scrollElRef}
+          data={data}
+          keyExtractor={item => item._reactKey}
+          renderItem={renderItem}
+          ListFooterComponent={Footer}
+          refreshControl={
+            <RefreshControl
+              refreshing={isRefreshing}
+              onRefresh={onRefresh}
+              tintColor={pal.colors.text}
+              titleColor={pal.colors.text}
+              progressViewOffset={headerOffset}
+            />
+          }
+          contentContainerStyle={s.contentContainer}
+          style={{paddingTop: headerOffset}}
+          onEndReached={onEndReached}
+          onEndReachedThreshold={0.6}
+          removeClippedSubviews={true}
+          contentOffset={{x: 0, y: headerOffset * -1}}
+          // @ts-ignore our .web version only -prf
+          desktopFixedHeight
+        />
+      )}
+    </View>
+  )
+})
 
-const ListHeader = observer(
-  ({
-    list,
-    isOwner,
-    onToggleSubscribed,
-    onPressEditList,
-    onPressDeleteList,
-    onPressShareList,
-    onPressReportList,
-  }: {
-    list: AppBskyGraphDefs.ListView
-    isOwner: boolean
-    onToggleSubscribed: () => void
-    onPressEditList: () => void
-    onPressDeleteList: () => void
-    onPressShareList: () => void
-    onPressReportList: () => void
-  }) => {
-    const pal = usePalette('default')
-    const store = useStores()
-    const {isDesktop} = useWebMediaQueries()
-    const descriptionRT = React.useMemo(
-      () =>
-        list?.description &&
-        new RichText({text: list.description, facets: list.descriptionFacets}),
-      [list],
-    )
-    return (
-      <>
-        <View style={[styles.header, pal.border]}>
-          <View style={s.flex1}>
-            <Text testID="listName" type="title-xl" style={[pal.text, s.bold]}>
-              {list.name}
+const ListHeader = observer(function ListHeaderImpl({
+  list,
+  isOwner,
+  onToggleSubscribed,
+  onPressEditList,
+  onPressDeleteList,
+  onPressShareList,
+  onPressReportList,
+}: {
+  list: AppBskyGraphDefs.ListView
+  isOwner: boolean
+  onToggleSubscribed: () => void
+  onPressEditList: () => void
+  onPressDeleteList: () => void
+  onPressShareList: () => void
+  onPressReportList: () => void
+}) {
+  const pal = usePalette('default')
+  const store = useStores()
+  const {isDesktop} = useWebMediaQueries()
+  const descriptionRT = React.useMemo(
+    () =>
+      list?.description &&
+      new RichText({text: list.description, facets: list.descriptionFacets}),
+    [list],
+  )
+  return (
+    <>
+      <View style={[styles.header, pal.border]}>
+        <View style={s.flex1}>
+          <Text testID="listName" type="title-xl" style={[pal.text, s.bold]}>
+            {list.name}
+          </Text>
+          {list && (
+            <Text type="md" style={[pal.textLight]} numberOfLines={1}>
+              {list.purpose === 'app.bsky.graph.defs#modlist' && 'Mute list '}
+              by{' '}
+              {list.creator.did === store.me.did ? (
+                'you'
+              ) : (
+                <TextLink
+                  text={sanitizeHandle(list.creator.handle, '@')}
+                  href={makeProfileLink(list.creator)}
+                  style={pal.textLight}
+                />
+              )}
             </Text>
-            {list && (
-              <Text type="md" style={[pal.textLight]} numberOfLines={1}>
-                {list.purpose === 'app.bsky.graph.defs#modlist' && 'Mute list '}
-                by{' '}
-                {list.creator.did === store.me.did ? (
-                  'you'
-                ) : (
-                  <TextLink
-                    text={sanitizeHandle(list.creator.handle, '@')}
-                    href={makeProfileLink(list.creator)}
-                    style={pal.textLight}
-                  />
-                )}
-              </Text>
-            )}
-            {descriptionRT && (
-              <RichTextCom
-                testID="listDescription"
-                style={[pal.text, styles.headerDescription]}
-                richText={descriptionRT}
-              />
-            )}
-            {isDesktop && (
-              <ListActions
-                isOwner={isOwner}
-                muted={list.viewer?.muted}
-                onPressDeleteList={onPressDeleteList}
-                onPressEditList={onPressEditList}
-                onToggleSubscribed={onToggleSubscribed}
-                onPressShareList={onPressShareList}
-                onPressReportList={onPressReportList}
-              />
-            )}
-          </View>
-          <View>
-            <UserAvatar type="list" avatar={list.avatar} size={64} />
-          </View>
+          )}
+          {descriptionRT && (
+            <RichTextCom
+              testID="listDescription"
+              style={[pal.text, styles.headerDescription]}
+              richText={descriptionRT}
+            />
+          )}
+          {isDesktop && (
+            <ListActions
+              isOwner={isOwner}
+              muted={list.viewer?.muted}
+              onPressDeleteList={onPressDeleteList}
+              onPressEditList={onPressEditList}
+              onToggleSubscribed={onToggleSubscribed}
+              onPressShareList={onPressShareList}
+              onPressReportList={onPressReportList}
+            />
+          )}
         </View>
-        <View
-          style={{flexDirection: 'row', paddingHorizontal: isDesktop ? 16 : 6}}>
-          <View
-            style={[styles.fakeSelectorItem, {borderColor: pal.colors.link}]}>
-            <Text type="md-medium" style={[pal.text]}>
-              Muted users
-            </Text>
-          </View>
+        <View>
+          <UserAvatar type="list" avatar={list.avatar} size={64} />
         </View>
-      </>
-    )
-  },
-)
+      </View>
+      <View
+        style={{flexDirection: 'row', paddingHorizontal: isDesktop ? 16 : 6}}>
+        <View style={[styles.fakeSelectorItem, {borderColor: pal.colors.link}]}>
+          <Text type="md-medium" style={[pal.text]}>
+            Muted users
+          </Text>
+        </View>
+      </View>
+    </>
+  )
+})
 
 const styles = StyleSheet.create({
   header: {
diff --git a/src/view/com/lists/ListsList.tsx b/src/view/com/lists/ListsList.tsx
index fb07ee0b8..4c8befa1f 100644
--- a/src/view/com/lists/ListsList.tsx
+++ b/src/view/com/lists/ListsList.tsx
@@ -30,173 +30,171 @@ const EMPTY_ITEM = {_reactKey: '__empty__'}
 const ERROR_ITEM = {_reactKey: '__error__'}
 const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'}
 
-export const ListsList = observer(
-  ({
-    listsList,
-    showAddBtns,
-    style,
-    scrollElRef,
-    onPressTryAgain,
-    onPressCreateNew,
-    renderItem,
-    renderEmptyState,
-    testID,
-    headerOffset = 0,
-  }: {
-    listsList: ListsListModel
-    showAddBtns?: boolean
-    style?: StyleProp<ViewStyle>
-    scrollElRef?: MutableRefObject<FlatList<any> | null>
-    onPressCreateNew: () => void
-    onPressTryAgain?: () => void
-    renderItem?: (list: GraphDefs.ListView) => JSX.Element
-    renderEmptyState?: () => JSX.Element
-    testID?: string
-    headerOffset?: number
-  }) => {
-    const pal = usePalette('default')
-    const {track} = useAnalytics()
-    const [isRefreshing, setIsRefreshing] = React.useState(false)
+export const ListsList = observer(function ListsListImpl({
+  listsList,
+  showAddBtns,
+  style,
+  scrollElRef,
+  onPressTryAgain,
+  onPressCreateNew,
+  renderItem,
+  renderEmptyState,
+  testID,
+  headerOffset = 0,
+}: {
+  listsList: ListsListModel
+  showAddBtns?: boolean
+  style?: StyleProp<ViewStyle>
+  scrollElRef?: MutableRefObject<FlatList<any> | null>
+  onPressCreateNew: () => void
+  onPressTryAgain?: () => void
+  renderItem?: (list: GraphDefs.ListView) => JSX.Element
+  renderEmptyState?: () => JSX.Element
+  testID?: string
+  headerOffset?: number
+}) {
+  const pal = usePalette('default')
+  const {track} = useAnalytics()
+  const [isRefreshing, setIsRefreshing] = React.useState(false)
 
-    const data = React.useMemo(() => {
-      let items: any[] = []
-      if (listsList.hasLoaded) {
-        if (listsList.hasError) {
-          items = items.concat([ERROR_ITEM])
-        }
-        if (listsList.isEmpty) {
-          items = items.concat([EMPTY_ITEM])
-        } else {
-          if (showAddBtns) {
-            items = items.concat([CREATENEW_ITEM])
-          }
-          items = items.concat(listsList.lists)
-        }
-        if (listsList.loadMoreError) {
-          items = items.concat([LOAD_MORE_ERROR_ITEM])
+  const data = React.useMemo(() => {
+    let items: any[] = []
+    if (listsList.hasLoaded) {
+      if (listsList.hasError) {
+        items = items.concat([ERROR_ITEM])
+      }
+      if (listsList.isEmpty) {
+        items = items.concat([EMPTY_ITEM])
+      } else {
+        if (showAddBtns) {
+          items = items.concat([CREATENEW_ITEM])
         }
-      } else if (listsList.isLoading) {
-        items = items.concat([LOADING_ITEM])
+        items = items.concat(listsList.lists)
+      }
+      if (listsList.loadMoreError) {
+        items = items.concat([LOAD_MORE_ERROR_ITEM])
       }
-      return items
-    }, [
-      listsList.hasError,
-      listsList.hasLoaded,
-      listsList.isLoading,
-      listsList.isEmpty,
-      listsList.lists,
-      listsList.loadMoreError,
-      showAddBtns,
-    ])
+    } else if (listsList.isLoading) {
+      items = items.concat([LOADING_ITEM])
+    }
+    return items
+  }, [
+    listsList.hasError,
+    listsList.hasLoaded,
+    listsList.isLoading,
+    listsList.isEmpty,
+    listsList.lists,
+    listsList.loadMoreError,
+    showAddBtns,
+  ])
 
-    // events
-    // =
+  // events
+  // =
 
-    const onRefresh = React.useCallback(async () => {
-      track('Lists:onRefresh')
-      setIsRefreshing(true)
-      try {
-        await listsList.refresh()
-      } catch (err) {
-        listsList.rootStore.log.error('Failed to refresh lists', err)
-      }
-      setIsRefreshing(false)
-    }, [listsList, track, setIsRefreshing])
+  const onRefresh = React.useCallback(async () => {
+    track('Lists:onRefresh')
+    setIsRefreshing(true)
+    try {
+      await listsList.refresh()
+    } catch (err) {
+      listsList.rootStore.log.error('Failed to refresh lists', err)
+    }
+    setIsRefreshing(false)
+  }, [listsList, track, setIsRefreshing])
 
-    const onEndReached = React.useCallback(async () => {
-      track('Lists:onEndReached')
-      try {
-        await listsList.loadMore()
-      } catch (err) {
-        listsList.rootStore.log.error('Failed to load more lists', err)
-      }
-    }, [listsList, track])
+  const onEndReached = React.useCallback(async () => {
+    track('Lists:onEndReached')
+    try {
+      await listsList.loadMore()
+    } catch (err) {
+      listsList.rootStore.log.error('Failed to load more lists', err)
+    }
+  }, [listsList, track])
 
-    const onPressRetryLoadMore = React.useCallback(() => {
-      listsList.retryLoadMore()
-    }, [listsList])
+  const onPressRetryLoadMore = React.useCallback(() => {
+    listsList.retryLoadMore()
+  }, [listsList])
 
-    // rendering
-    // =
+  // rendering
+  // =
 
-    const renderItemInner = React.useCallback(
-      ({item}: {item: any}) => {
-        if (item === EMPTY_ITEM) {
-          if (renderEmptyState) {
-            return renderEmptyState()
-          }
-          return <View />
-        } else if (item === CREATENEW_ITEM) {
-          return <CreateNewItem onPress={onPressCreateNew} />
-        } else if (item === ERROR_ITEM) {
-          return (
-            <ErrorMessage
-              message={listsList.error}
-              onPressTryAgain={onPressTryAgain}
-            />
-          )
-        } else if (item === LOAD_MORE_ERROR_ITEM) {
-          return (
-            <LoadMoreRetryBtn
-              label="There was an issue fetching your lists. Tap here to try again."
-              onPress={onPressRetryLoadMore}
-            />
-          )
-        } else if (item === LOADING_ITEM) {
-          return <ProfileCardFeedLoadingPlaceholder />
+  const renderItemInner = React.useCallback(
+    ({item}: {item: any}) => {
+      if (item === EMPTY_ITEM) {
+        if (renderEmptyState) {
+          return renderEmptyState()
         }
-        return renderItem ? (
-          renderItem(item)
-        ) : (
-          <ListCard
-            list={item}
-            testID={`list-${item.name}`}
-            style={styles.item}
+        return <View />
+      } else if (item === CREATENEW_ITEM) {
+        return <CreateNewItem onPress={onPressCreateNew} />
+      } else if (item === ERROR_ITEM) {
+        return (
+          <ErrorMessage
+            message={listsList.error}
+            onPressTryAgain={onPressTryAgain}
           />
         )
-      },
-      [
-        listsList,
-        onPressTryAgain,
-        onPressRetryLoadMore,
-        onPressCreateNew,
-        renderItem,
-        renderEmptyState,
-      ],
-    )
-
-    return (
-      <View testID={testID} style={style}>
-        {data.length > 0 && (
-          <FlatList
-            testID={testID ? `${testID}-flatlist` : undefined}
-            ref={scrollElRef}
-            data={data}
-            keyExtractor={item => item._reactKey}
-            renderItem={renderItemInner}
-            refreshControl={
-              <RefreshControl
-                refreshing={isRefreshing}
-                onRefresh={onRefresh}
-                tintColor={pal.colors.text}
-                titleColor={pal.colors.text}
-                progressViewOffset={headerOffset}
-              />
-            }
-            contentContainerStyle={[s.contentContainer]}
-            style={{paddingTop: headerOffset}}
-            onEndReached={onEndReached}
-            onEndReachedThreshold={0.6}
-            removeClippedSubviews={true}
-            contentOffset={{x: 0, y: headerOffset * -1}}
-            // @ts-ignore our .web version only -prf
-            desktopFixedHeight
+      } else if (item === LOAD_MORE_ERROR_ITEM) {
+        return (
+          <LoadMoreRetryBtn
+            label="There was an issue fetching your lists. Tap here to try again."
+            onPress={onPressRetryLoadMore}
           />
-        )}
-      </View>
-    )
-  },
-)
+        )
+      } else if (item === LOADING_ITEM) {
+        return <ProfileCardFeedLoadingPlaceholder />
+      }
+      return renderItem ? (
+        renderItem(item)
+      ) : (
+        <ListCard
+          list={item}
+          testID={`list-${item.name}`}
+          style={styles.item}
+        />
+      )
+    },
+    [
+      listsList,
+      onPressTryAgain,
+      onPressRetryLoadMore,
+      onPressCreateNew,
+      renderItem,
+      renderEmptyState,
+    ],
+  )
+
+  return (
+    <View testID={testID} style={style}>
+      {data.length > 0 && (
+        <FlatList
+          testID={testID ? `${testID}-flatlist` : undefined}
+          ref={scrollElRef}
+          data={data}
+          keyExtractor={item => item._reactKey}
+          renderItem={renderItemInner}
+          refreshControl={
+            <RefreshControl
+              refreshing={isRefreshing}
+              onRefresh={onRefresh}
+              tintColor={pal.colors.text}
+              titleColor={pal.colors.text}
+              progressViewOffset={headerOffset}
+            />
+          }
+          contentContainerStyle={[s.contentContainer]}
+          style={{paddingTop: headerOffset}}
+          onEndReached={onEndReached}
+          onEndReachedThreshold={0.6}
+          removeClippedSubviews={true}
+          contentOffset={{x: 0, y: headerOffset * -1}}
+          // @ts-ignore our .web version only -prf
+          desktopFixedHeight
+        />
+      )}
+    </View>
+  )
+})
 
 function CreateNewItem({onPress}: {onPress: () => void}) {
   const pal = usePalette('default')
diff --git a/src/view/com/modals/ContentFilteringSettings.tsx b/src/view/com/modals/ContentFilteringSettings.tsx
index 588b21353..d2bf278f5 100644
--- a/src/view/com/modals/ContentFilteringSettings.tsx
+++ b/src/view/com/modals/ContentFilteringSettings.tsx
@@ -17,159 +17,161 @@ import * as Toast from '../util/Toast'
 
 export const snapPoints = ['90%']
 
-export const Component = observer(({}: {}) => {
-  const store = useStores()
-  const {isMobile} = useWebMediaQueries()
-  const pal = usePalette('default')
+export const Component = observer(
+  function ContentFilteringSettingsImpl({}: {}) {
+    const store = useStores()
+    const {isMobile} = useWebMediaQueries()
+    const pal = usePalette('default')
 
-  React.useEffect(() => {
-    store.preferences.sync()
-  }, [store])
+    React.useEffect(() => {
+      store.preferences.sync()
+    }, [store])
 
-  const onToggleAdultContent = React.useCallback(async () => {
-    if (isIOS) {
-      return
-    }
-    try {
-      await store.preferences.setAdultContentEnabled(
-        !store.preferences.adultContentEnabled,
-      )
-    } catch (e) {
-      Toast.show('There was an issue syncing your preferences with the server')
-      store.log.error('Failed to update preferences with server', {e})
-    }
-  }, [store])
+    const onToggleAdultContent = React.useCallback(async () => {
+      if (isIOS) {
+        return
+      }
+      try {
+        await store.preferences.setAdultContentEnabled(
+          !store.preferences.adultContentEnabled,
+        )
+      } catch (e) {
+        Toast.show(
+          'There was an issue syncing your preferences with the server',
+        )
+        store.log.error('Failed to update preferences with server', {e})
+      }
+    }, [store])
 
-  const onPressDone = React.useCallback(() => {
-    store.shell.closeModal()
-  }, [store])
+    const onPressDone = React.useCallback(() => {
+      store.shell.closeModal()
+    }, [store])
 
-  return (
-    <View testID="contentFilteringModal" style={[pal.view, styles.container]}>
-      <Text style={[pal.text, styles.title]}>Content Filtering</Text>
-      <ScrollView style={styles.scrollContainer}>
-        <View style={s.mb10}>
-          {isIOS ? (
-            store.preferences.adultContentEnabled ? null : (
-              <Text type="md" style={pal.textLight}>
-                Adult content can only be enabled via the Web at{' '}
-                <TextLink
-                  style={pal.link}
-                  href="https://bsky.app"
-                  text="bsky.app"
-                />
-                .
-              </Text>
-            )
-          ) : (
-            <ToggleButton
-              type="default-light"
-              label="Enable Adult Content"
-              isSelected={store.preferences.adultContentEnabled}
-              onPress={onToggleAdultContent}
-              style={styles.toggleBtn}
-            />
-          )}
+    return (
+      <View testID="contentFilteringModal" style={[pal.view, styles.container]}>
+        <Text style={[pal.text, styles.title]}>Content Filtering</Text>
+        <ScrollView style={styles.scrollContainer}>
+          <View style={s.mb10}>
+            {isIOS ? (
+              store.preferences.adultContentEnabled ? null : (
+                <Text type="md" style={pal.textLight}>
+                  Adult content can only be enabled via the Web at{' '}
+                  <TextLink
+                    style={pal.link}
+                    href="https://bsky.app"
+                    text="bsky.app"
+                  />
+                  .
+                </Text>
+              )
+            ) : (
+              <ToggleButton
+                type="default-light"
+                label="Enable Adult Content"
+                isSelected={store.preferences.adultContentEnabled}
+                onPress={onToggleAdultContent}
+                style={styles.toggleBtn}
+              />
+            )}
+          </View>
+          <ContentLabelPref
+            group="nsfw"
+            disabled={!store.preferences.adultContentEnabled}
+          />
+          <ContentLabelPref
+            group="nudity"
+            disabled={!store.preferences.adultContentEnabled}
+          />
+          <ContentLabelPref
+            group="suggestive"
+            disabled={!store.preferences.adultContentEnabled}
+          />
+          <ContentLabelPref
+            group="gore"
+            disabled={!store.preferences.adultContentEnabled}
+          />
+          <ContentLabelPref group="hate" />
+          <ContentLabelPref group="spam" />
+          <ContentLabelPref group="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="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]}>Done</Text>
+            </LinearGradient>
+          </Pressable>
         </View>
-        <ContentLabelPref
-          group="nsfw"
-          disabled={!store.preferences.adultContentEnabled}
-        />
-        <ContentLabelPref
-          group="nudity"
-          disabled={!store.preferences.adultContentEnabled}
-        />
-        <ContentLabelPref
-          group="suggestive"
-          disabled={!store.preferences.adultContentEnabled}
-        />
-        <ContentLabelPref
-          group="gore"
-          disabled={!store.preferences.adultContentEnabled}
-        />
-        <ContentLabelPref group="hate" />
-        <ContentLabelPref group="spam" />
-        <ContentLabelPref group="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="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]}>Done</Text>
-          </LinearGradient>
-        </Pressable>
       </View>
-    </View>
-  )
-})
+    )
+  },
+)
 
 // TODO: Refactor this component to pass labels down to each tab
-const ContentLabelPref = observer(
-  ({
-    group,
-    disabled,
-  }: {
-    group: keyof typeof CONFIGURABLE_LABEL_GROUPS
-    disabled?: boolean
-  }) => {
-    const store = useStores()
-    const pal = usePalette('default')
+const ContentLabelPref = observer(function ContentLabelPrefImpl({
+  group,
+  disabled,
+}: {
+  group: keyof typeof CONFIGURABLE_LABEL_GROUPS
+  disabled?: boolean
+}) {
+  const store = useStores()
+  const pal = usePalette('default')
 
-    const onChange = React.useCallback(
-      async (v: LabelPreference) => {
-        try {
-          await store.preferences.setContentLabelPref(group, v)
-        } catch (e) {
-          Toast.show(
-            'There was an issue syncing your preferences with the server',
-          )
-          store.log.error('Failed to update preferences with server', {e})
-        }
-      },
-      [store, group],
-    )
+  const onChange = React.useCallback(
+    async (v: LabelPreference) => {
+      try {
+        await store.preferences.setContentLabelPref(group, v)
+      } catch (e) {
+        Toast.show(
+          'There was an issue syncing your preferences with the server',
+        )
+        store.log.error('Failed to update preferences with server', {e})
+      }
+    },
+    [store, group],
+  )
 
-    return (
-      <View style={[styles.contentLabelPref, pal.border]}>
-        <View style={s.flex1}>
-          <Text type="md-medium" style={[pal.text]}>
-            {CONFIGURABLE_LABEL_GROUPS[group].title}
-          </Text>
-          {typeof CONFIGURABLE_LABEL_GROUPS[group].subtitle === 'string' && (
-            <Text type="sm" style={[pal.textLight]}>
-              {CONFIGURABLE_LABEL_GROUPS[group].subtitle}
-            </Text>
-          )}
-        </View>
-        {disabled ? (
-          <Text type="sm-bold" style={pal.textLight}>
-            Hide
+  return (
+    <View style={[styles.contentLabelPref, pal.border]}>
+      <View style={s.flex1}>
+        <Text type="md-medium" style={[pal.text]}>
+          {CONFIGURABLE_LABEL_GROUPS[group].title}
+        </Text>
+        {typeof CONFIGURABLE_LABEL_GROUPS[group].subtitle === 'string' && (
+          <Text type="sm" style={[pal.textLight]}>
+            {CONFIGURABLE_LABEL_GROUPS[group].subtitle}
           </Text>
-        ) : (
-          <SelectGroup
-            current={store.preferences.contentLabels[group]}
-            onChange={onChange}
-            group={group}
-          />
         )}
       </View>
-    )
-  },
-)
+      {disabled ? (
+        <Text type="sm-bold" style={pal.textLight}>
+          Hide
+        </Text>
+      ) : (
+        <SelectGroup
+          current={store.preferences.contentLabels[group]}
+          onChange={onChange}
+          group={group}
+        />
+      )}
+    </View>
+  )
+})
 
 interface SelectGroupProps {
   current: LabelPreference
diff --git a/src/view/com/modals/EditImage.tsx b/src/view/com/modals/EditImage.tsx
index e4cfbac35..dcb6668c7 100644
--- a/src/view/com/modals/EditImage.tsx
+++ b/src/view/com/modals/EditImage.tsx
@@ -46,7 +46,10 @@ interface Props {
   gallery: GalleryModel
 }
 
-export const Component = observer(function ({image, gallery}: Props) {
+export const Component = observer(function EditImageImpl({
+  image,
+  gallery,
+}: Props) {
   const pal = usePalette('default')
   const theme = useTheme()
   const store = useStores()
diff --git a/src/view/com/modals/InviteCodes.tsx b/src/view/com/modals/InviteCodes.tsx
index ba3cc382b..33ffc86a2 100644
--- a/src/view/com/modals/InviteCodes.tsx
+++ b/src/view/com/modals/InviteCodes.tsx
@@ -79,50 +79,56 @@ export function Component({}: {}) {
   )
 }
 
-const InviteCode = observer(
-  ({testID, code, used}: {testID: string; code: string; used?: boolean}) => {
-    const pal = usePalette('default')
-    const store = useStores()
-    const {invitesAvailable} = store.me
+const InviteCode = observer(function InviteCodeImpl({
+  testID,
+  code,
+  used,
+}: {
+  testID: string
+  code: string
+  used?: boolean
+}) {
+  const pal = usePalette('default')
+  const store = useStores()
+  const {invitesAvailable} = store.me
 
-    const onPress = React.useCallback(() => {
-      Clipboard.setString(code)
-      Toast.show('Copied to clipboard')
-      store.invitedUsers.setInviteCopied(code)
-    }, [store, code])
+  const onPress = React.useCallback(() => {
+    Clipboard.setString(code)
+    Toast.show('Copied to clipboard')
+    store.invitedUsers.setInviteCopied(code)
+  }, [store, code])
 
-    return (
-      <TouchableOpacity
-        testID={testID}
-        style={[styles.inviteCode, pal.border]}
-        onPress={onPress}
-        accessibilityRole="button"
-        accessibilityLabel={
-          invitesAvailable === 1
-            ? 'Invite codes: 1 available'
-            : `Invite codes: ${invitesAvailable} available`
-        }
-        accessibilityHint="Opens list of invite codes">
-        <Text
-          testID={`${testID}-code`}
-          type={used ? 'md' : 'md-bold'}
-          style={used ? [pal.textLight, styles.strikeThrough] : pal.text}>
-          {code}
-        </Text>
-        <View style={styles.flex1} />
-        {!used && store.invitedUsers.isInviteCopied(code) && (
-          <Text style={[pal.textLight, styles.codeCopied]}>Copied</Text>
-        )}
-        {!used && (
-          <FontAwesomeIcon
-            icon={['far', 'clone']}
-            style={pal.text as FontAwesomeIconStyle}
-          />
-        )}
-      </TouchableOpacity>
-    )
-  },
-)
+  return (
+    <TouchableOpacity
+      testID={testID}
+      style={[styles.inviteCode, pal.border]}
+      onPress={onPress}
+      accessibilityRole="button"
+      accessibilityLabel={
+        invitesAvailable === 1
+          ? 'Invite codes: 1 available'
+          : `Invite codes: ${invitesAvailable} available`
+      }
+      accessibilityHint="Opens list of invite codes">
+      <Text
+        testID={`${testID}-code`}
+        type={used ? 'md' : 'md-bold'}
+        style={used ? [pal.textLight, styles.strikeThrough] : pal.text}>
+        {code}
+      </Text>
+      <View style={styles.flex1} />
+      {!used && store.invitedUsers.isInviteCopied(code) && (
+        <Text style={[pal.textLight, styles.codeCopied]}>Copied</Text>
+      )}
+      {!used && (
+        <FontAwesomeIcon
+          icon={['far', 'clone']}
+          style={pal.text as FontAwesomeIconStyle}
+        />
+      )}
+    </TouchableOpacity>
+  )
+})
 
 const styles = StyleSheet.create({
   container: {
diff --git a/src/view/com/modals/ListAddRemoveUser.tsx b/src/view/com/modals/ListAddRemoveUser.tsx
index e00509285..58d6a529c 100644
--- a/src/view/com/modals/ListAddRemoveUser.tsx
+++ b/src/view/com/modals/ListAddRemoveUser.tsx
@@ -24,210 +24,207 @@ import isEqual from 'lodash.isequal'
 
 export const snapPoints = ['fullscreen']
 
-export const Component = observer(
-  ({
-    subject,
-    displayName,
-    onUpdate,
-  }: {
-    subject: string
-    displayName: string
-    onUpdate?: () => void
-  }) => {
-    const store = useStores()
-    const pal = usePalette('default')
-    const palPrimary = usePalette('primary')
-    const palInverted = usePalette('inverted')
-    const [originalSelections, setOriginalSelections] = React.useState<
-      string[]
-    >([])
-    const [selected, setSelected] = React.useState<string[]>([])
-    const [membershipsLoaded, setMembershipsLoaded] = React.useState(false)
+export const Component = observer(function ListAddRemoveUserImpl({
+  subject,
+  displayName,
+  onUpdate,
+}: {
+  subject: string
+  displayName: string
+  onUpdate?: () => void
+}) {
+  const store = useStores()
+  const pal = usePalette('default')
+  const palPrimary = usePalette('primary')
+  const palInverted = usePalette('inverted')
+  const [originalSelections, setOriginalSelections] = React.useState<string[]>(
+    [],
+  )
+  const [selected, setSelected] = React.useState<string[]>([])
+  const [membershipsLoaded, setMembershipsLoaded] = React.useState(false)
 
-    const listsList: ListsListModel = React.useMemo(
-      () => new ListsListModel(store, store.me.did),
-      [store],
-    )
-    const memberships: ListMembershipModel = React.useMemo(
-      () => new ListMembershipModel(store, subject),
-      [store, subject],
+  const listsList: ListsListModel = React.useMemo(
+    () => new ListsListModel(store, store.me.did),
+    [store],
+  )
+  const memberships: ListMembershipModel = React.useMemo(
+    () => new ListMembershipModel(store, subject),
+    [store, subject],
+  )
+  React.useEffect(() => {
+    listsList.refresh()
+    memberships.fetch().then(
+      () => {
+        const ids = memberships.memberships.map(m => m.value.list)
+        setOriginalSelections(ids)
+        setSelected(ids)
+        setMembershipsLoaded(true)
+      },
+      err => {
+        store.log.error('Failed to fetch memberships', {err})
+      },
     )
-    React.useEffect(() => {
-      listsList.refresh()
-      memberships.fetch().then(
-        () => {
-          const ids = memberships.memberships.map(m => m.value.list)
-          setOriginalSelections(ids)
-          setSelected(ids)
-          setMembershipsLoaded(true)
-        },
-        err => {
-          store.log.error('Failed to fetch memberships', {err})
-        },
-      )
-    }, [memberships, listsList, store, setSelected, setMembershipsLoaded])
+  }, [memberships, listsList, store, setSelected, setMembershipsLoaded])
 
-    const onPressCancel = useCallback(() => {
-      store.shell.closeModal()
-    }, [store])
+  const onPressCancel = useCallback(() => {
+    store.shell.closeModal()
+  }, [store])
 
-    const onPressSave = useCallback(async () => {
-      try {
-        await memberships.updateTo(selected)
-      } catch (err) {
-        store.log.error('Failed to update memberships', {err})
-        return
-      }
-      Toast.show('Lists updated')
-      onUpdate?.()
-      store.shell.closeModal()
-    }, [store, selected, memberships, onUpdate])
-
-    const onPressNewMuteList = useCallback(() => {
-      store.shell.openModal({
-        name: 'create-or-edit-mute-list',
-        onSave: (_uri: string) => {
-          listsList.refresh()
-        },
-      })
-    }, [store, listsList])
+  const onPressSave = useCallback(async () => {
+    try {
+      await memberships.updateTo(selected)
+    } catch (err) {
+      store.log.error('Failed to update memberships', {err})
+      return
+    }
+    Toast.show('Lists updated')
+    onUpdate?.()
+    store.shell.closeModal()
+  }, [store, selected, memberships, onUpdate])
 
-    const onToggleSelected = useCallback(
-      (uri: string) => {
-        if (selected.includes(uri)) {
-          setSelected(selected.filter(uri2 => uri2 !== uri))
-        } else {
-          setSelected([...selected, uri])
-        }
+  const onPressNewMuteList = useCallback(() => {
+    store.shell.openModal({
+      name: 'create-or-edit-mute-list',
+      onSave: (_uri: string) => {
+        listsList.refresh()
       },
-      [selected, setSelected],
-    )
+    })
+  }, [store, listsList])
 
-    const renderItem = useCallback(
-      (list: GraphDefs.ListView) => {
-        const isSelected = selected.includes(list.uri)
-        return (
-          <Pressable
-            testID={`toggleBtn-${list.name}`}
-            style={[
-              styles.listItem,
-              pal.border,
-              {opacity: membershipsLoaded ? 1 : 0.5},
-            ]}
-            accessibilityLabel={`${isSelected ? 'Remove from' : 'Add to'} ${
-              list.name
-            }`}
-            accessibilityHint=""
-            disabled={!membershipsLoaded}
-            onPress={() => onToggleSelected(list.uri)}>
-            <View style={styles.listItemAvi}>
-              <UserAvatar size={40} avatar={list.avatar} />
-            </View>
-            <View style={styles.listItemContent}>
-              <Text
-                type="lg"
-                style={[s.bold, pal.text]}
-                numberOfLines={1}
-                lineHeight={1.2}>
-                {sanitizeDisplayName(list.name)}
-              </Text>
-              <Text type="md" style={[pal.textLight]} numberOfLines={1}>
-                {list.purpose === 'app.bsky.graph.defs#modlist' && 'Mute list'}{' '}
-                by{' '}
-                {list.creator.did === store.me.did
-                  ? 'you'
-                  : sanitizeHandle(list.creator.handle, '@')}
-              </Text>
-            </View>
-            {membershipsLoaded && (
-              <View
-                style={
-                  isSelected
-                    ? [styles.checkbox, palPrimary.border, palPrimary.view]
-                    : [styles.checkbox, pal.borderDark]
-                }>
-                {isSelected && (
-                  <FontAwesomeIcon
-                    icon="check"
-                    style={palInverted.text as FontAwesomeIconStyle}
-                  />
-                )}
-              </View>
-            )}
-          </Pressable>
-        )
-      },
-      [
-        pal,
-        palPrimary,
-        palInverted,
-        onToggleSelected,
-        selected,
-        store.me.did,
-        membershipsLoaded,
-      ],
-    )
+  const onToggleSelected = useCallback(
+    (uri: string) => {
+      if (selected.includes(uri)) {
+        setSelected(selected.filter(uri2 => uri2 !== uri))
+      } else {
+        setSelected([...selected, uri])
+      }
+    },
+    [selected, setSelected],
+  )
 
-    const renderEmptyState = React.useCallback(() => {
+  const renderItem = useCallback(
+    (list: GraphDefs.ListView) => {
+      const isSelected = selected.includes(list.uri)
       return (
-        <EmptyStateWithButton
-          icon="users-slash"
-          message="You can subscribe to mute lists to automatically mute all of the users they include. Mute lists are public but your subscription to a mute list is private."
-          buttonLabel="New Mute List"
-          onPress={onPressNewMuteList}
-        />
+        <Pressable
+          testID={`toggleBtn-${list.name}`}
+          style={[
+            styles.listItem,
+            pal.border,
+            {opacity: membershipsLoaded ? 1 : 0.5},
+          ]}
+          accessibilityLabel={`${isSelected ? 'Remove from' : 'Add to'} ${
+            list.name
+          }`}
+          accessibilityHint=""
+          disabled={!membershipsLoaded}
+          onPress={() => onToggleSelected(list.uri)}>
+          <View style={styles.listItemAvi}>
+            <UserAvatar size={40} avatar={list.avatar} />
+          </View>
+          <View style={styles.listItemContent}>
+            <Text
+              type="lg"
+              style={[s.bold, pal.text]}
+              numberOfLines={1}
+              lineHeight={1.2}>
+              {sanitizeDisplayName(list.name)}
+            </Text>
+            <Text type="md" style={[pal.textLight]} numberOfLines={1}>
+              {list.purpose === 'app.bsky.graph.defs#modlist' && 'Mute list'} by{' '}
+              {list.creator.did === store.me.did
+                ? 'you'
+                : sanitizeHandle(list.creator.handle, '@')}
+            </Text>
+          </View>
+          {membershipsLoaded && (
+            <View
+              style={
+                isSelected
+                  ? [styles.checkbox, palPrimary.border, palPrimary.view]
+                  : [styles.checkbox, pal.borderDark]
+              }>
+              {isSelected && (
+                <FontAwesomeIcon
+                  icon="check"
+                  style={palInverted.text as FontAwesomeIconStyle}
+                />
+              )}
+            </View>
+          )}
+        </Pressable>
       )
-    }, [onPressNewMuteList])
-
-    // Only show changes button if there are some items on the list to choose from AND user has made changes in selection
-    const canSaveChanges =
-      !listsList.isEmpty && !isEqual(selected, originalSelections)
+    },
+    [
+      pal,
+      palPrimary,
+      palInverted,
+      onToggleSelected,
+      selected,
+      store.me.did,
+      membershipsLoaded,
+    ],
+  )
 
+  const renderEmptyState = React.useCallback(() => {
     return (
-      <View testID="listAddRemoveUserModal" style={s.hContentRegion}>
-        <Text style={[styles.title, pal.text]}>Add {displayName} to Lists</Text>
-        <ListsList
-          listsList={listsList}
-          showAddBtns
-          onPressCreateNew={onPressNewMuteList}
-          renderItem={renderItem}
-          renderEmptyState={renderEmptyState}
-          style={[styles.list, pal.border]}
+      <EmptyStateWithButton
+        icon="users-slash"
+        message="You can subscribe to mute lists to automatically mute all of the users they include. Mute lists are public but your subscription to a mute list is private."
+        buttonLabel="New Mute List"
+        onPress={onPressNewMuteList}
+      />
+    )
+  }, [onPressNewMuteList])
+
+  // Only show changes button if there are some items on the list to choose from AND user has made changes in selection
+  const canSaveChanges =
+    !listsList.isEmpty && !isEqual(selected, originalSelections)
+
+  return (
+    <View testID="listAddRemoveUserModal" style={s.hContentRegion}>
+      <Text style={[styles.title, pal.text]}>Add {displayName} to Lists</Text>
+      <ListsList
+        listsList={listsList}
+        showAddBtns
+        onPressCreateNew={onPressNewMuteList}
+        renderItem={renderItem}
+        renderEmptyState={renderEmptyState}
+        style={[styles.list, pal.border]}
+      />
+      <View style={[styles.btns, pal.border]}>
+        <Button
+          testID="cancelBtn"
+          type="default"
+          onPress={onPressCancel}
+          style={styles.footerBtn}
+          accessibilityLabel="Cancel"
+          accessibilityHint=""
+          onAccessibilityEscape={onPressCancel}
+          label="Cancel"
         />
-        <View style={[styles.btns, pal.border]}>
+        {canSaveChanges && (
           <Button
-            testID="cancelBtn"
-            type="default"
-            onPress={onPressCancel}
+            testID="saveBtn"
+            type="primary"
+            onPress={onPressSave}
             style={styles.footerBtn}
-            accessibilityLabel="Cancel"
+            accessibilityLabel="Save changes"
             accessibilityHint=""
-            onAccessibilityEscape={onPressCancel}
-            label="Cancel"
+            onAccessibilityEscape={onPressSave}
+            label="Save Changes"
           />
-          {canSaveChanges && (
-            <Button
-              testID="saveBtn"
-              type="primary"
-              onPress={onPressSave}
-              style={styles.footerBtn}
-              accessibilityLabel="Save changes"
-              accessibilityHint=""
-              onAccessibilityEscape={onPressSave}
-              label="Save Changes"
-            />
-          )}
+        )}
 
-          {(listsList.isLoading || !membershipsLoaded) && (
-            <View style={styles.loadingContainer}>
-              <ActivityIndicator />
-            </View>
-          )}
-        </View>
+        {(listsList.isLoading || !membershipsLoaded) && (
+          <View style={styles.loadingContainer}>
+            <ActivityIndicator />
+          </View>
+        )}
       </View>
-    )
-  },
-)
+    </View>
+  )
+})
 
 const styles = StyleSheet.create({
   container: {
diff --git a/src/view/com/modals/ProfilePreview.tsx b/src/view/com/modals/ProfilePreview.tsx
index 65b584866..6f189cf1a 100644
--- a/src/view/com/modals/ProfilePreview.tsx
+++ b/src/view/com/modals/ProfilePreview.tsx
@@ -14,7 +14,11 @@ import {s} from 'lib/styles'
 
 export const snapPoints = [520, '100%']
 
-export const Component = observer(({did}: {did: string}) => {
+export const Component = observer(function ProfilePreviewImpl({
+  did,
+}: {
+  did: string
+}) {
   const store = useStores()
   const pal = usePalette('default')
   const [model] = useState(new ProfileModel(store, {actor: did}))
diff --git a/src/view/com/modals/lang-settings/LanguageToggle.tsx b/src/view/com/modals/lang-settings/LanguageToggle.tsx
index df1b405ca..187b46e8c 100644
--- a/src/view/com/modals/lang-settings/LanguageToggle.tsx
+++ b/src/view/com/modals/lang-settings/LanguageToggle.tsx
@@ -5,43 +5,41 @@ import {observer} from 'mobx-react-lite'
 import {ToggleButton} from 'view/com/util/forms/ToggleButton'
 import {useStores} from 'state/index'
 
-export const LanguageToggle = observer(
-  ({
-    code2,
-    name,
-    onPress,
-    langType,
-  }: {
-    code2: string
-    name: string
-    onPress: () => void
-    langType: 'contentLanguages' | 'postLanguages'
-  }) => {
-    const pal = usePalette('default')
-    const store = useStores()
+export const LanguageToggle = observer(function LanguageToggleImpl({
+  code2,
+  name,
+  onPress,
+  langType,
+}: {
+  code2: string
+  name: string
+  onPress: () => void
+  langType: 'contentLanguages' | 'postLanguages'
+}) {
+  const pal = usePalette('default')
+  const store = useStores()
 
-    const isSelected = store.preferences[langType].includes(code2)
+  const isSelected = store.preferences[langType].includes(code2)
 
-    // enforce a max of 3 selections for post languages
-    let isDisabled = false
-    if (
-      langType === 'postLanguages' &&
-      store.preferences[langType].length >= 3 &&
-      !isSelected
-    ) {
-      isDisabled = true
-    }
+  // enforce a max of 3 selections for post languages
+  let isDisabled = false
+  if (
+    langType === 'postLanguages' &&
+    store.preferences[langType].length >= 3 &&
+    !isSelected
+  ) {
+    isDisabled = true
+  }
 
-    return (
-      <ToggleButton
-        label={name}
-        isSelected={isSelected}
-        onPress={isDisabled ? undefined : onPress}
-        style={[pal.border, styles.languageToggle, isDisabled && styles.dimmed]}
-      />
-    )
-  },
-)
+  return (
+    <ToggleButton
+      label={name}
+      isSelected={isSelected}
+      onPress={isDisabled ? undefined : onPress}
+      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 1ee5c9d1f..d74d884cc 100644
--- a/src/view/com/modals/lang-settings/PostLanguagesSettings.tsx
+++ b/src/view/com/modals/lang-settings/PostLanguagesSettings.tsx
@@ -13,7 +13,7 @@ import {ToggleButton} from 'view/com/util/forms/ToggleButton'
 
 export const snapPoints = ['100%']
 
-export const Component = observer(() => {
+export const Component = observer(function PostLanguagesSettingsImpl() {
   const store = useStores()
   const pal = usePalette('default')
   const {isMobile} = useWebMediaQueries()
diff --git a/src/view/com/notifications/FeedItem.tsx b/src/view/com/notifications/FeedItem.tsx
index ef191a1d2..00e56e1cc 100644
--- a/src/view/com/notifications/FeedItem.tsx
+++ b/src/view/com/notifications/FeedItem.tsx
@@ -52,7 +52,7 @@ interface Author {
   moderation: ProfileModeration
 }
 
-export const FeedItem = observer(function ({
+export const FeedItem = observer(function FeedItemImpl({
   item,
 }: {
   item: NotificationsFeedItemModel
diff --git a/src/view/com/notifications/InvitedUsers.tsx b/src/view/com/notifications/InvitedUsers.tsx
index 1bdb42a9c..89a0da47f 100644
--- a/src/view/com/notifications/InvitedUsers.tsx
+++ b/src/view/com/notifications/InvitedUsers.tsx
@@ -18,7 +18,7 @@ import {s} from 'lib/styles'
 import {sanitizeDisplayName} from 'lib/strings/display-names'
 import {makeProfileLink} from 'lib/routes/links'
 
-export const InvitedUsers = observer(() => {
+export const InvitedUsers = observer(function InvitedUsersImpl() {
   const store = useStores()
   return (
     <CenteredView>
diff --git a/src/view/com/pager/FeedsTabBar.web.tsx b/src/view/com/pager/FeedsTabBar.web.tsx
index 48a6ed3a9..0083e953b 100644
--- a/src/view/com/pager/FeedsTabBar.web.tsx
+++ b/src/view/com/pager/FeedsTabBar.web.tsx
@@ -9,59 +9,55 @@ import {useAnimatedValue} from 'lib/hooks/useAnimatedValue'
 import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
 import {FeedsTabBar as FeedsTabBarMobile} from './FeedsTabBarMobile'
 
-export const FeedsTabBar = observer(
-  (
-    props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
-  ) => {
-    const {isMobile} = useWebMediaQueries()
-    if (isMobile) {
-      return <FeedsTabBarMobile {...props} />
-    } else {
-      return <FeedsTabBarDesktop {...props} />
-    }
-  },
-)
+export const FeedsTabBar = observer(function FeedsTabBarImpl(
+  props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
+) {
+  const {isMobile} = useWebMediaQueries()
+  if (isMobile) {
+    return <FeedsTabBarMobile {...props} />
+  } else {
+    return <FeedsTabBarDesktop {...props} />
+  }
+})
 
-const FeedsTabBarDesktop = observer(
-  (
-    props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
-  ) => {
-    const store = useStores()
-    const items = useMemo(
-      () => ['Following', ...store.me.savedFeeds.pinnedFeedNames],
-      [store.me.savedFeeds.pinnedFeedNames],
-    )
-    const pal = usePalette('default')
-    const interp = useAnimatedValue(0)
+const FeedsTabBarDesktop = observer(function FeedsTabBarDesktopImpl(
+  props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
+) {
+  const store = useStores()
+  const items = useMemo(
+    () => ['Following', ...store.me.savedFeeds.pinnedFeedNames],
+    [store.me.savedFeeds.pinnedFeedNames],
+  )
+  const pal = usePalette('default')
+  const interp = useAnimatedValue(0)
 
-    React.useEffect(() => {
-      Animated.timing(interp, {
-        toValue: store.shell.minimalShellMode ? 1 : 0,
-        duration: 100,
-        useNativeDriver: true,
-        isInteraction: false,
-      }).start()
-    }, [interp, store.shell.minimalShellMode])
-    const transform = {
-      transform: [
-        {translateX: '-50%'},
-        {translateY: Animated.multiply(interp, -100)},
-      ],
-    }
+  React.useEffect(() => {
+    Animated.timing(interp, {
+      toValue: store.shell.minimalShellMode ? 1 : 0,
+      duration: 100,
+      useNativeDriver: true,
+      isInteraction: false,
+    }).start()
+  }, [interp, store.shell.minimalShellMode])
+  const transform = {
+    transform: [
+      {translateX: '-50%'},
+      {translateY: Animated.multiply(interp, -100)},
+    ],
+  }
 
-    return (
-      // @ts-ignore the type signature for transform wrong here, translateX and translateY need to be in separate objects -prf
-      <Animated.View style={[pal.view, styles.tabBar, transform]}>
-        <TabBar
-          key={items.join(',')}
-          {...props}
-          items={items}
-          indicatorColor={pal.colors.link}
-        />
-      </Animated.View>
-    )
-  },
-)
+  return (
+    // @ts-ignore the type signature for transform wrong here, translateX and translateY need to be in separate objects -prf
+    <Animated.View style={[pal.view, styles.tabBar, transform]}>
+      <TabBar
+        key={items.join(',')}
+        {...props}
+        items={items}
+        indicatorColor={pal.colors.link}
+      />
+    </Animated.View>
+  )
+})
 
 const styles = StyleSheet.create({
   tabBar: {
diff --git a/src/view/com/pager/FeedsTabBarMobile.tsx b/src/view/com/pager/FeedsTabBarMobile.tsx
index 55a38803f..5ce2906b3 100644
--- a/src/view/com/pager/FeedsTabBarMobile.tsx
+++ b/src/view/com/pager/FeedsTabBarMobile.tsx
@@ -14,79 +14,77 @@ import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {s} from 'lib/styles'
 import {HITSLOP_10} from 'lib/constants'
 
-export const FeedsTabBar = observer(
-  (
-    props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
-  ) => {
-    const store = useStores()
-    const pal = usePalette('default')
-    const interp = useAnimatedValue(0)
+export const FeedsTabBar = observer(function FeedsTabBarImpl(
+  props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
+) {
+  const store = useStores()
+  const pal = usePalette('default')
+  const interp = useAnimatedValue(0)
 
-    React.useEffect(() => {
-      Animated.timing(interp, {
-        toValue: store.shell.minimalShellMode ? 1 : 0,
-        duration: 100,
-        useNativeDriver: true,
-        isInteraction: false,
-      }).start()
-    }, [interp, store.shell.minimalShellMode])
-    const transform = {
-      transform: [{translateY: Animated.multiply(interp, -100)}],
-    }
+  React.useEffect(() => {
+    Animated.timing(interp, {
+      toValue: store.shell.minimalShellMode ? 1 : 0,
+      duration: 100,
+      useNativeDriver: true,
+      isInteraction: false,
+    }).start()
+  }, [interp, store.shell.minimalShellMode])
+  const transform = {
+    transform: [{translateY: Animated.multiply(interp, -100)}],
+  }
 
-    const brandBlue = useColorSchemeStyle(s.brandBlue, s.blue3)
+  const brandBlue = useColorSchemeStyle(s.brandBlue, s.blue3)
 
-    const onPressAvi = React.useCallback(() => {
-      store.shell.openDrawer()
-    }, [store])
+  const onPressAvi = React.useCallback(() => {
+    store.shell.openDrawer()
+  }, [store])
 
-    const items = useMemo(
-      () => ['Following', ...store.me.savedFeeds.pinnedFeedNames],
-      [store.me.savedFeeds.pinnedFeedNames],
-    )
+  const items = useMemo(
+    () => ['Following', ...store.me.savedFeeds.pinnedFeedNames],
+    [store.me.savedFeeds.pinnedFeedNames],
+  )
 
-    return (
-      <Animated.View style={[pal.view, pal.border, styles.tabBar, transform]}>
-        <View style={[pal.view, styles.topBar]}>
-          <View style={[pal.view]}>
-            <TouchableOpacity
-              testID="viewHeaderDrawerBtn"
-              onPress={onPressAvi}
-              accessibilityRole="button"
-              accessibilityLabel="Open navigation"
-              accessibilityHint="Access profile and other navigation links"
-              hitSlop={HITSLOP_10}>
-              <FontAwesomeIcon
-                icon="bars"
-                size={18}
-                color={pal.colors.textLight}
-              />
-            </TouchableOpacity>
-          </View>
-          <Text style={[brandBlue, s.bold, styles.title]}>
-            {store.session.isSandbox ? 'SANDBOX' : 'Bluesky'}
-          </Text>
-          <View style={[pal.view]}>
-            <Link
-              href="/settings/saved-feeds"
-              hitSlop={HITSLOP_10}
-              accessibilityRole="button"
-              accessibilityLabel="Edit Saved Feeds"
-              accessibilityHint="Opens screen to edit Saved Feeds">
-              <CogIcon size={21} strokeWidth={2} style={pal.textLight} />
-            </Link>
-          </View>
+  return (
+    <Animated.View style={[pal.view, pal.border, styles.tabBar, transform]}>
+      <View style={[pal.view, styles.topBar]}>
+        <View style={[pal.view]}>
+          <TouchableOpacity
+            testID="viewHeaderDrawerBtn"
+            onPress={onPressAvi}
+            accessibilityRole="button"
+            accessibilityLabel="Open navigation"
+            accessibilityHint="Access profile and other navigation links"
+            hitSlop={HITSLOP_10}>
+            <FontAwesomeIcon
+              icon="bars"
+              size={18}
+              color={pal.colors.textLight}
+            />
+          </TouchableOpacity>
         </View>
-        <TabBar
-          key={items.join(',')}
-          {...props}
-          items={items}
-          indicatorColor={pal.colors.link}
-        />
-      </Animated.View>
-    )
-  },
-)
+        <Text style={[brandBlue, s.bold, styles.title]}>
+          {store.session.isSandbox ? 'SANDBOX' : 'Bluesky'}
+        </Text>
+        <View style={[pal.view]}>
+          <Link
+            href="/settings/saved-feeds"
+            hitSlop={HITSLOP_10}
+            accessibilityRole="button"
+            accessibilityLabel="Edit Saved Feeds"
+            accessibilityHint="Opens screen to edit Saved Feeds">
+            <CogIcon size={21} strokeWidth={2} style={pal.textLight} />
+          </Link>
+        </View>
+      </View>
+      <TabBar
+        key={items.join(',')}
+        {...props}
+        items={items}
+        indicatorColor={pal.colors.link}
+      />
+    </Animated.View>
+  )
+})
 
 const styles = StyleSheet.create({
   tabBar: {
diff --git a/src/view/com/post-thread/PostLikedBy.tsx b/src/view/com/post-thread/PostLikedBy.tsx
index 80dd59072..574fe1e8e 100644
--- a/src/view/com/post-thread/PostLikedBy.tsx
+++ b/src/view/com/post-thread/PostLikedBy.tsx
@@ -8,7 +8,11 @@ import {ProfileCardWithFollowBtn} from '../profile/ProfileCard'
 import {useStores} from 'state/index'
 import {usePalette} from 'lib/hooks/usePalette'
 
-export const PostLikedBy = observer(function ({uri}: {uri: string}) {
+export const PostLikedBy = observer(function PostLikedByImpl({
+  uri,
+}: {
+  uri: string
+}) {
   const pal = usePalette('default')
   const store = useStores()
   const view = React.useMemo(() => new LikesModel(store, {uri}), [store, uri])
@@ -64,6 +68,8 @@ export const PostLikedBy = observer(function ({uri}: {uri: string}) {
       onEndReached={onEndReached}
       renderItem={renderItem}
       initialNumToRender={15}
+      // FIXME(dan)
+      // eslint-disable-next-line react/no-unstable-nested-components
       ListFooterComponent={() => (
         <View style={styles.footer}>
           {view.isLoading && <ActivityIndicator />}
diff --git a/src/view/com/post-thread/PostRepostedBy.tsx b/src/view/com/post-thread/PostRepostedBy.tsx
index 31fa0cf7f..e4b592779 100644
--- a/src/view/com/post-thread/PostRepostedBy.tsx
+++ b/src/view/com/post-thread/PostRepostedBy.tsx
@@ -8,7 +8,7 @@ import {ErrorMessage} from '../util/error/ErrorMessage'
 import {useStores} from 'state/index'
 import {usePalette} from 'lib/hooks/usePalette'
 
-export const PostRepostedBy = observer(function PostRepostedBy({
+export const PostRepostedBy = observer(function PostRepostedByImpl({
   uri,
 }: {
   uri: string
@@ -75,6 +75,8 @@ export const PostRepostedBy = observer(function PostRepostedBy({
       onEndReached={onEndReached}
       renderItem={renderItem}
       initialNumToRender={15}
+      // FIXME(dan)
+      // eslint-disable-next-line react/no-unstable-nested-components
       ListFooterComponent={() => (
         <View style={styles.footer}>
           {view.isLoading && <ActivityIndicator />}
diff --git a/src/view/com/post/Post.tsx b/src/view/com/post/Post.tsx
index 661b3a899..0855f25bf 100644
--- a/src/view/com/post/Post.tsx
+++ b/src/view/com/post/Post.tsx
@@ -31,7 +31,7 @@ import {usePalette} from 'lib/hooks/usePalette'
 import {getTranslatorLink, isPostInLanguage} from '../../../locale/helpers'
 import {makeProfileLink} from 'lib/routes/links'
 
-export const Post = observer(function Post({
+export const Post = observer(function PostImpl({
   view,
   showReplyLine,
   hideError,
@@ -88,214 +88,212 @@ export const Post = observer(function Post({
   )
 })
 
-const PostLoaded = observer(
-  ({
-    item,
-    record,
-    setDeleted,
-    showReplyLine,
-    style,
-  }: {
-    item: PostThreadItemModel
-    record: FeedPost.Record
-    setDeleted: (v: boolean) => void
-    showReplyLine?: boolean
-    style?: StyleProp<ViewStyle>
-  }) => {
-    const pal = usePalette('default')
-    const store = useStores()
+const PostLoaded = observer(function PostLoadedImpl({
+  item,
+  record,
+  setDeleted,
+  showReplyLine,
+  style,
+}: {
+  item: PostThreadItemModel
+  record: FeedPost.Record
+  setDeleted: (v: boolean) => void
+  showReplyLine?: boolean
+  style?: StyleProp<ViewStyle>
+}) {
+  const pal = usePalette('default')
+  const store = useStores()
 
-    const itemUri = item.post.uri
-    const itemCid = item.post.cid
-    const itemUrip = new AtUri(item.post.uri)
-    const itemHref = makeProfileLink(item.post.author, 'post', itemUrip.rkey)
-    const itemTitle = `Post by ${item.post.author.handle}`
-    let replyAuthorDid = ''
-    if (record.reply) {
-      const urip = new AtUri(record.reply.parent?.uri || record.reply.root.uri)
-      replyAuthorDid = urip.hostname
-    }
+  const itemUri = item.post.uri
+  const itemCid = item.post.cid
+  const itemUrip = new AtUri(item.post.uri)
+  const itemHref = makeProfileLink(item.post.author, 'post', itemUrip.rkey)
+  const itemTitle = `Post by ${item.post.author.handle}`
+  let replyAuthorDid = ''
+  if (record.reply) {
+    const urip = new AtUri(record.reply.parent?.uri || record.reply.root.uri)
+    replyAuthorDid = urip.hostname
+  }
 
-    const translatorUrl = getTranslatorLink(record?.text || '')
-    const needsTranslation = useMemo(
-      () =>
-        store.preferences.contentLanguages.length > 0 &&
-        !isPostInLanguage(item.post, store.preferences.contentLanguages),
-      [item.post, store.preferences.contentLanguages],
-    )
+  const translatorUrl = getTranslatorLink(record?.text || '')
+  const needsTranslation = useMemo(
+    () =>
+      store.preferences.contentLanguages.length > 0 &&
+      !isPostInLanguage(item.post, store.preferences.contentLanguages),
+    [item.post, store.preferences.contentLanguages],
+  )
 
-    const onPressReply = React.useCallback(() => {
-      store.shell.openComposer({
-        replyTo: {
-          uri: item.post.uri,
-          cid: item.post.cid,
-          text: record.text as string,
-          author: {
-            handle: item.post.author.handle,
-            displayName: item.post.author.displayName,
-            avatar: item.post.author.avatar,
-          },
+  const onPressReply = React.useCallback(() => {
+    store.shell.openComposer({
+      replyTo: {
+        uri: item.post.uri,
+        cid: item.post.cid,
+        text: record.text as string,
+        author: {
+          handle: item.post.author.handle,
+          displayName: item.post.author.displayName,
+          avatar: item.post.author.avatar,
         },
-      })
-    }, [store, item, record])
+      },
+    })
+  }, [store, item, record])
 
-    const onPressToggleRepost = React.useCallback(() => {
-      return item
-        .toggleRepost()
-        .catch(e => store.log.error('Failed to toggle repost', e))
-    }, [item, store])
+  const onPressToggleRepost = React.useCallback(() => {
+    return item
+      .toggleRepost()
+      .catch(e => store.log.error('Failed to toggle repost', e))
+  }, [item, store])
 
-    const onPressToggleLike = React.useCallback(() => {
-      return item
-        .toggleLike()
-        .catch(e => store.log.error('Failed to toggle like', e))
-    }, [item, store])
+  const onPressToggleLike = React.useCallback(() => {
+    return item
+      .toggleLike()
+      .catch(e => store.log.error('Failed to toggle like', e))
+  }, [item, store])
 
-    const onCopyPostText = React.useCallback(() => {
-      Clipboard.setString(record.text)
-      Toast.show('Copied to clipboard')
-    }, [record])
+  const onCopyPostText = React.useCallback(() => {
+    Clipboard.setString(record.text)
+    Toast.show('Copied to clipboard')
+  }, [record])
 
-    const onOpenTranslate = React.useCallback(() => {
-      Linking.openURL(translatorUrl)
-    }, [translatorUrl])
+  const onOpenTranslate = React.useCallback(() => {
+    Linking.openURL(translatorUrl)
+  }, [translatorUrl])
 
-    const onToggleThreadMute = React.useCallback(async () => {
-      try {
-        await item.toggleThreadMute()
-        if (item.isThreadMuted) {
-          Toast.show('You will no longer receive notifications for this thread')
-        } else {
-          Toast.show('You will now receive notifications for this thread')
-        }
-      } catch (e) {
-        store.log.error('Failed to toggle thread mute', e)
+  const onToggleThreadMute = React.useCallback(async () => {
+    try {
+      await item.toggleThreadMute()
+      if (item.isThreadMuted) {
+        Toast.show('You will no longer receive notifications for this thread')
+      } else {
+        Toast.show('You will now receive notifications for this thread')
       }
-    }, [item, store])
+    } catch (e) {
+      store.log.error('Failed to toggle thread mute', e)
+    }
+  }, [item, store])
 
-    const onDeletePost = React.useCallback(() => {
-      item.delete().then(
-        () => {
-          setDeleted(true)
-          Toast.show('Post deleted')
-        },
-        e => {
-          store.log.error('Failed to delete post', e)
-          Toast.show('Failed to delete post, please try again')
-        },
-      )
-    }, [item, setDeleted, store])
+  const onDeletePost = React.useCallback(() => {
+    item.delete().then(
+      () => {
+        setDeleted(true)
+        Toast.show('Post deleted')
+      },
+      e => {
+        store.log.error('Failed to delete post', e)
+        Toast.show('Failed to delete post, please try again')
+      },
+    )
+  }, [item, setDeleted, store])
 
-    return (
-      <Link href={itemHref} style={[styles.outer, pal.view, pal.border, style]}>
-        {showReplyLine && <View style={styles.replyLine} />}
-        <View style={styles.layout}>
-          <View style={styles.layoutAvi}>
-            <PreviewableUserAvatar
-              size={52}
-              did={item.post.author.did}
-              handle={item.post.author.handle}
-              avatar={item.post.author.avatar}
-              moderation={item.moderation.avatar}
-            />
-          </View>
-          <View style={styles.layoutContent}>
-            <PostMeta
-              author={item.post.author}
-              authorHasWarning={!!item.post.author.labels?.length}
-              timestamp={item.post.indexedAt}
-              postHref={itemHref}
+  return (
+    <Link href={itemHref} style={[styles.outer, pal.view, pal.border, style]}>
+      {showReplyLine && <View style={styles.replyLine} />}
+      <View style={styles.layout}>
+        <View style={styles.layoutAvi}>
+          <PreviewableUserAvatar
+            size={52}
+            did={item.post.author.did}
+            handle={item.post.author.handle}
+            avatar={item.post.author.avatar}
+            moderation={item.moderation.avatar}
+          />
+        </View>
+        <View style={styles.layoutContent}>
+          <PostMeta
+            author={item.post.author}
+            authorHasWarning={!!item.post.author.labels?.length}
+            timestamp={item.post.indexedAt}
+            postHref={itemHref}
+          />
+          {replyAuthorDid !== '' && (
+            <View style={[s.flexRow, s.mb2, s.alignCenter]}>
+              <FontAwesomeIcon
+                icon="reply"
+                size={9}
+                style={[pal.textLight, s.mr5]}
+              />
+              <Text
+                type="sm"
+                style={[pal.textLight, s.mr2]}
+                lineHeight={1.2}
+                numberOfLines={1}>
+                Reply to{' '}
+                <UserInfoText
+                  type="sm"
+                  did={replyAuthorDid}
+                  attr="displayName"
+                  style={[pal.textLight]}
+                />
+              </Text>
+            </View>
+          )}
+          <ContentHider
+            moderation={item.moderation.content}
+            style={styles.contentHider}
+            childContainerStyle={styles.contentHiderChild}>
+            <PostAlerts
+              moderation={item.moderation.content}
+              style={styles.alert}
             />
-            {replyAuthorDid !== '' && (
-              <View style={[s.flexRow, s.mb2, s.alignCenter]}>
-                <FontAwesomeIcon
-                  icon="reply"
-                  size={9}
-                  style={[pal.textLight, s.mr5]}
+            {item.richText?.text ? (
+              <View style={styles.postTextContainer}>
+                <RichText
+                  testID="postText"
+                  type="post-text"
+                  richText={item.richText}
+                  lineHeight={1.3}
+                  style={s.flex1}
                 />
-                <Text
-                  type="sm"
-                  style={[pal.textLight, s.mr2]}
-                  lineHeight={1.2}
-                  numberOfLines={1}>
-                  Reply to{' '}
-                  <UserInfoText
-                    type="sm"
-                    did={replyAuthorDid}
-                    attr="displayName"
-                    style={[pal.textLight]}
-                  />
-                </Text>
               </View>
-            )}
-            <ContentHider
-              moderation={item.moderation.content}
-              style={styles.contentHider}
-              childContainerStyle={styles.contentHiderChild}>
-              <PostAlerts
-                moderation={item.moderation.content}
-                style={styles.alert}
-              />
-              {item.richText?.text ? (
-                <View style={styles.postTextContainer}>
-                  <RichText
-                    testID="postText"
-                    type="post-text"
-                    richText={item.richText}
-                    lineHeight={1.3}
-                    style={s.flex1}
-                  />
-                </View>
-              ) : undefined}
-              {item.post.embed ? (
-                <ContentHider
+            ) : undefined}
+            {item.post.embed ? (
+              <ContentHider
+                moderation={item.moderation.embed}
+                style={styles.contentHider}>
+                <PostEmbeds
+                  embed={item.post.embed}
                   moderation={item.moderation.embed}
-                  style={styles.contentHider}>
-                  <PostEmbeds
-                    embed={item.post.embed}
-                    moderation={item.moderation.embed}
-                  />
-                </ContentHider>
-              ) : null}
-              {needsTranslation && (
-                <View style={[pal.borderDark, styles.translateLink]}>
-                  <Link href={translatorUrl} title="Translate">
-                    <Text type="sm" style={pal.link}>
-                      Translate this post
-                    </Text>
-                  </Link>
-                </View>
-              )}
-            </ContentHider>
-            <PostCtrls
-              itemUri={itemUri}
-              itemCid={itemCid}
-              itemHref={itemHref}
-              itemTitle={itemTitle}
-              author={item.post.author}
-              indexedAt={item.post.indexedAt}
-              text={item.richText?.text || record.text}
-              isAuthor={item.post.author.did === store.me.did}
-              replyCount={item.post.replyCount}
-              repostCount={item.post.repostCount}
-              likeCount={item.post.likeCount}
-              isReposted={!!item.post.viewer?.repost}
-              isLiked={!!item.post.viewer?.like}
-              isThreadMuted={item.isThreadMuted}
-              onPressReply={onPressReply}
-              onPressToggleRepost={onPressToggleRepost}
-              onPressToggleLike={onPressToggleLike}
-              onCopyPostText={onCopyPostText}
-              onOpenTranslate={onOpenTranslate}
-              onToggleThreadMute={onToggleThreadMute}
-              onDeletePost={onDeletePost}
-            />
-          </View>
+                />
+              </ContentHider>
+            ) : null}
+            {needsTranslation && (
+              <View style={[pal.borderDark, styles.translateLink]}>
+                <Link href={translatorUrl} title="Translate">
+                  <Text type="sm" style={pal.link}>
+                    Translate this post
+                  </Text>
+                </Link>
+              </View>
+            )}
+          </ContentHider>
+          <PostCtrls
+            itemUri={itemUri}
+            itemCid={itemCid}
+            itemHref={itemHref}
+            itemTitle={itemTitle}
+            author={item.post.author}
+            indexedAt={item.post.indexedAt}
+            text={item.richText?.text || record.text}
+            isAuthor={item.post.author.did === store.me.did}
+            replyCount={item.post.replyCount}
+            repostCount={item.post.repostCount}
+            likeCount={item.post.likeCount}
+            isReposted={!!item.post.viewer?.repost}
+            isLiked={!!item.post.viewer?.like}
+            isThreadMuted={item.isThreadMuted}
+            onPressReply={onPressReply}
+            onPressToggleRepost={onPressToggleRepost}
+            onPressToggleLike={onPressToggleLike}
+            onCopyPostText={onCopyPostText}
+            onOpenTranslate={onOpenTranslate}
+            onToggleThreadMute={onToggleThreadMute}
+            onDeletePost={onDeletePost}
+          />
         </View>
-      </Link>
-    )
-  },
-)
+      </View>
+    </Link>
+  )
+})
 
 const styles = StyleSheet.create({
   outer: {
diff --git a/src/view/com/posts/FeedItem.tsx b/src/view/com/posts/FeedItem.tsx
index e11f258a5..de08af746 100644
--- a/src/view/com/posts/FeedItem.tsx
+++ b/src/view/com/posts/FeedItem.tsx
@@ -30,7 +30,7 @@ import {getTranslatorLink, isPostInLanguage} from '../../../locale/helpers'
 import {makeProfileLink} from 'lib/routes/links'
 import {isEmbedByEmbedder} from 'lib/embeds'
 
-export const FeedItem = observer(function ({
+export const FeedItem = observer(function FeedItemImpl({
   item,
   isThreadChild,
   isThreadLastChild,
diff --git a/src/view/com/posts/FeedSlice.tsx b/src/view/com/posts/FeedSlice.tsx
index 6fc169db9..47313ee27 100644
--- a/src/view/com/posts/FeedSlice.tsx
+++ b/src/view/com/posts/FeedSlice.tsx
@@ -10,63 +10,61 @@ import {FeedItem} from './FeedItem'
 import {usePalette} from 'lib/hooks/usePalette'
 import {makeProfileLink} from 'lib/routes/links'
 
-export const FeedSlice = observer(
-  ({
-    slice,
-    ignoreFilterFor,
-  }: {
-    slice: PostsFeedSliceModel
-    ignoreFilterFor?: string
-  }) => {
-    if (slice.shouldFilter(ignoreFilterFor)) {
-      return null
-    }
-
-    if (slice.isThread && slice.items.length > 3) {
-      const last = slice.items.length - 1
-      return (
-        <>
-          <FeedItem
-            key={slice.items[0]._reactKey}
-            item={slice.items[0]}
-            isThreadParent={slice.isThreadParentAt(0)}
-            isThreadChild={slice.isThreadChildAt(0)}
-          />
-          <FeedItem
-            key={slice.items[1]._reactKey}
-            item={slice.items[1]}
-            isThreadParent={slice.isThreadParentAt(1)}
-            isThreadChild={slice.isThreadChildAt(1)}
-          />
-          <ViewFullThread slice={slice} />
-          <FeedItem
-            key={slice.items[last]._reactKey}
-            item={slice.items[last]}
-            isThreadParent={slice.isThreadParentAt(last)}
-            isThreadChild={slice.isThreadChildAt(last)}
-            isThreadLastChild
-          />
-        </>
-      )
-    }
+export const FeedSlice = observer(function FeedSliceImpl({
+  slice,
+  ignoreFilterFor,
+}: {
+  slice: PostsFeedSliceModel
+  ignoreFilterFor?: string
+}) {
+  if (slice.shouldFilter(ignoreFilterFor)) {
+    return null
+  }
 
+  if (slice.isThread && slice.items.length > 3) {
+    const last = slice.items.length - 1
     return (
       <>
-        {slice.items.map((item, i) => (
-          <FeedItem
-            key={item._reactKey}
-            item={item}
-            isThreadParent={slice.isThreadParentAt(i)}
-            isThreadChild={slice.isThreadChildAt(i)}
-            isThreadLastChild={
-              slice.isThreadChildAt(i) && slice.items.length === i + 1
-            }
-          />
-        ))}
+        <FeedItem
+          key={slice.items[0]._reactKey}
+          item={slice.items[0]}
+          isThreadParent={slice.isThreadParentAt(0)}
+          isThreadChild={slice.isThreadChildAt(0)}
+        />
+        <FeedItem
+          key={slice.items[1]._reactKey}
+          item={slice.items[1]}
+          isThreadParent={slice.isThreadParentAt(1)}
+          isThreadChild={slice.isThreadChildAt(1)}
+        />
+        <ViewFullThread slice={slice} />
+        <FeedItem
+          key={slice.items[last]._reactKey}
+          item={slice.items[last]}
+          isThreadParent={slice.isThreadParentAt(last)}
+          isThreadChild={slice.isThreadChildAt(last)}
+          isThreadLastChild
+        />
       </>
     )
-  },
-)
+  }
+
+  return (
+    <>
+      {slice.items.map((item, i) => (
+        <FeedItem
+          key={item._reactKey}
+          item={item}
+          isThreadParent={slice.isThreadParentAt(i)}
+          isThreadChild={slice.isThreadChildAt(i)}
+          isThreadLastChild={
+            slice.isThreadChildAt(i) && slice.items.length === i + 1
+          }
+        />
+      ))}
+    </>
+  )
+})
 
 function ViewFullThread({slice}: {slice: PostsFeedSliceModel}) {
   const pal = usePalette('default')
diff --git a/src/view/com/profile/FollowButton.tsx b/src/view/com/profile/FollowButton.tsx
index fcb2225da..6f6286e69 100644
--- a/src/view/com/profile/FollowButton.tsx
+++ b/src/view/com/profile/FollowButton.tsx
@@ -6,56 +6,54 @@ import {useStores} from 'state/index'
 import * as Toast from '../util/Toast'
 import {FollowState} from 'state/models/cache/my-follows'
 
-export const FollowButton = observer(
-  ({
-    unfollowedType = 'inverted',
-    followedType = 'default',
-    did,
-    onToggleFollow,
-  }: {
-    unfollowedType?: ButtonType
-    followedType?: ButtonType
-    did: string
-    onToggleFollow?: (v: boolean) => void
-  }) => {
-    const store = useStores()
-    const followState = store.me.follows.getFollowState(did)
+export const FollowButton = observer(function FollowButtonImpl({
+  unfollowedType = 'inverted',
+  followedType = 'default',
+  did,
+  onToggleFollow,
+}: {
+  unfollowedType?: ButtonType
+  followedType?: ButtonType
+  did: string
+  onToggleFollow?: (v: boolean) => void
+}) {
+  const store = useStores()
+  const followState = store.me.follows.getFollowState(did)
 
-    if (followState === FollowState.Unknown) {
-      return <View />
-    }
+  if (followState === FollowState.Unknown) {
+    return <View />
+  }
 
-    const onToggleFollowInner = async () => {
-      const updatedFollowState = await store.me.follows.fetchFollowState(did)
-      if (updatedFollowState === FollowState.Following) {
-        try {
-          await store.agent.deleteFollow(store.me.follows.getFollowUri(did))
-          store.me.follows.removeFollow(did)
-          onToggleFollow?.(false)
-        } catch (e: any) {
-          store.log.error('Failed to delete follow', e)
-          Toast.show('An issue occurred, please try again.')
-        }
-      } else if (updatedFollowState === FollowState.NotFollowing) {
-        try {
-          const res = await store.agent.follow(did)
-          store.me.follows.addFollow(did, res.uri)
-          onToggleFollow?.(true)
-        } catch (e: any) {
-          store.log.error('Failed to create follow', e)
-          Toast.show('An issue occurred, please try again.')
-        }
+  const onToggleFollowInner = async () => {
+    const updatedFollowState = await store.me.follows.fetchFollowState(did)
+    if (updatedFollowState === FollowState.Following) {
+      try {
+        await store.agent.deleteFollow(store.me.follows.getFollowUri(did))
+        store.me.follows.removeFollow(did)
+        onToggleFollow?.(false)
+      } catch (e: any) {
+        store.log.error('Failed to delete follow', e)
+        Toast.show('An issue occurred, please try again.')
+      }
+    } else if (updatedFollowState === FollowState.NotFollowing) {
+      try {
+        const res = await store.agent.follow(did)
+        store.me.follows.addFollow(did, res.uri)
+        onToggleFollow?.(true)
+      } catch (e: any) {
+        store.log.error('Failed to create follow', e)
+        Toast.show('An issue occurred, please try again.')
       }
     }
+  }
 
-    return (
-      <Button
-        type={
-          followState === FollowState.Following ? followedType : unfollowedType
-        }
-        onPress={onToggleFollowInner}
-        label={followState === FollowState.Following ? 'Unfollow' : 'Follow'}
-      />
-    )
-  },
-)
+  return (
+    <Button
+      type={
+        followState === FollowState.Following ? followedType : unfollowedType
+      }
+      onPress={onToggleFollowInner}
+      label={followState === FollowState.Following ? 'Unfollow' : 'Follow'}
+    />
+  )
+})
diff --git a/src/view/com/profile/ProfileCard.tsx b/src/view/com/profile/ProfileCard.tsx
index 771785ee9..e0c8ad21a 100644
--- a/src/view/com/profile/ProfileCard.tsx
+++ b/src/view/com/profile/ProfileCard.tsx
@@ -22,89 +22,82 @@ import {
   getModerationCauseKey,
 } from 'lib/moderation'
 
-export const ProfileCard = observer(
-  ({
-    testID,
-    profile,
-    noBg,
-    noBorder,
-    followers,
-    renderButton,
-  }: {
-    testID?: string
-    profile: AppBskyActorDefs.ProfileViewBasic
-    noBg?: boolean
-    noBorder?: boolean
-    followers?: AppBskyActorDefs.ProfileView[] | undefined
-    renderButton?: (
-      profile: AppBskyActorDefs.ProfileViewBasic,
-    ) => React.ReactNode
-  }) => {
-    const store = useStores()
-    const pal = usePalette('default')
+export const ProfileCard = observer(function ProfileCardImpl({
+  testID,
+  profile,
+  noBg,
+  noBorder,
+  followers,
+  renderButton,
+}: {
+  testID?: string
+  profile: AppBskyActorDefs.ProfileViewBasic
+  noBg?: boolean
+  noBorder?: boolean
+  followers?: AppBskyActorDefs.ProfileView[] | undefined
+  renderButton?: (profile: AppBskyActorDefs.ProfileViewBasic) => React.ReactNode
+}) {
+  const store = useStores()
+  const pal = usePalette('default')
 
-    const moderation = moderateProfile(
-      profile,
-      store.preferences.moderationOpts,
-    )
+  const moderation = moderateProfile(profile, store.preferences.moderationOpts)
 
-    return (
-      <Link
-        testID={testID}
-        style={[
-          styles.outer,
-          pal.border,
-          noBorder && styles.outerNoBorder,
-          !noBg && pal.view,
-        ]}
-        href={makeProfileLink(profile)}
-        title={profile.handle}
-        asAnchor
-        anchorNoUnderline>
-        <View style={styles.layout}>
-          <View style={styles.layoutAvi}>
-            <UserAvatar
-              size={40}
-              avatar={profile.avatar}
-              moderation={moderation.avatar}
-            />
-          </View>
-          <View style={styles.layoutContent}>
-            <Text
-              type="lg"
-              style={[s.bold, pal.text]}
-              numberOfLines={1}
-              lineHeight={1.2}>
-              {sanitizeDisplayName(
-                profile.displayName || sanitizeHandle(profile.handle),
-                moderation.profile,
-              )}
-            </Text>
-            <Text type="md" style={[pal.textLight]} numberOfLines={1}>
-              {sanitizeHandle(profile.handle, '@')}
-            </Text>
-            <ProfileCardPills
-              followedBy={!!profile.viewer?.followedBy}
-              moderation={moderation}
-            />
-            {!!profile.viewer?.followedBy && <View style={s.flexRow} />}
-          </View>
-          {renderButton ? (
-            <View style={styles.layoutButton}>{renderButton(profile)}</View>
-          ) : undefined}
+  return (
+    <Link
+      testID={testID}
+      style={[
+        styles.outer,
+        pal.border,
+        noBorder && styles.outerNoBorder,
+        !noBg && pal.view,
+      ]}
+      href={makeProfileLink(profile)}
+      title={profile.handle}
+      asAnchor
+      anchorNoUnderline>
+      <View style={styles.layout}>
+        <View style={styles.layoutAvi}>
+          <UserAvatar
+            size={40}
+            avatar={profile.avatar}
+            moderation={moderation.avatar}
+          />
         </View>
-        {profile.description ? (
-          <View style={styles.details}>
-            <Text style={pal.text} numberOfLines={4}>
-              {profile.description as string}
-            </Text>
-          </View>
+        <View style={styles.layoutContent}>
+          <Text
+            type="lg"
+            style={[s.bold, pal.text]}
+            numberOfLines={1}
+            lineHeight={1.2}>
+            {sanitizeDisplayName(
+              profile.displayName || sanitizeHandle(profile.handle),
+              moderation.profile,
+            )}
+          </Text>
+          <Text type="md" style={[pal.textLight]} numberOfLines={1}>
+            {sanitizeHandle(profile.handle, '@')}
+          </Text>
+          <ProfileCardPills
+            followedBy={!!profile.viewer?.followedBy}
+            moderation={moderation}
+          />
+          {!!profile.viewer?.followedBy && <View style={s.flexRow} />}
+        </View>
+        {renderButton ? (
+          <View style={styles.layoutButton}>{renderButton(profile)}</View>
         ) : undefined}
-        <FollowersList followers={followers} />
-      </Link>
-    )
-  },
-)
+      </View>
+      {profile.description ? (
+        <View style={styles.details}>
+          <Text style={pal.text} numberOfLines={4}>
+            {profile.description as string}
+          </Text>
+        </View>
+      ) : undefined}
+      <FollowersList followers={followers} />
+    </Link>
+  )
+})
 
 function ProfileCardPills({
   followedBy,
@@ -146,45 +139,47 @@ function ProfileCardPills({
   )
 }
 
-const FollowersList = observer(
-  ({followers}: {followers?: AppBskyActorDefs.ProfileView[] | undefined}) => {
-    const store = useStores()
-    const pal = usePalette('default')
-    if (!followers?.length) {
-      return null
-    }
+const FollowersList = observer(function FollowersListImpl({
+  followers,
+}: {
+  followers?: AppBskyActorDefs.ProfileView[] | undefined
+}) {
+  const store = useStores()
+  const pal = usePalette('default')
+  if (!followers?.length) {
+    return null
+  }
 
-    const followersWithMods = followers
-      .map(f => ({
-        f,
-        mod: moderateProfile(f, store.preferences.moderationOpts),
-      }))
-      .filter(({mod}) => !mod.account.filter)
+  const followersWithMods = followers
+    .map(f => ({
+      f,
+      mod: moderateProfile(f, store.preferences.moderationOpts),
+    }))
+    .filter(({mod}) => !mod.account.filter)
 
-    return (
-      <View style={styles.followedBy}>
-        <Text
-          type="sm"
-          style={[styles.followsByDesc, pal.textLight]}
-          numberOfLines={2}
-          lineHeight={1.2}>
-          Followed by{' '}
-          {followersWithMods.map(({f}) => f.displayName || f.handle).join(', ')}
-        </Text>
-        {followersWithMods.slice(0, 3).map(({f, mod}) => (
-          <View key={f.did} style={styles.followedByAviContainer}>
-            <View style={[styles.followedByAvi, pal.view]}>
-              <UserAvatar avatar={f.avatar} size={32} moderation={mod.avatar} />
-            </View>
+  return (
+    <View style={styles.followedBy}>
+      <Text
+        type="sm"
+        style={[styles.followsByDesc, pal.textLight]}
+        numberOfLines={2}
+        lineHeight={1.2}>
+        Followed by{' '}
+        {followersWithMods.map(({f}) => f.displayName || f.handle).join(', ')}
+      </Text>
+      {followersWithMods.slice(0, 3).map(({f, mod}) => (
+        <View key={f.did} style={styles.followedByAviContainer}>
+          <View style={[styles.followedByAvi, pal.view]}>
+            <UserAvatar avatar={f.avatar} size={32} moderation={mod.avatar} />
           </View>
-        ))}
-      </View>
-    )
-  },
-)
+        </View>
+      ))}
+    </View>
+  )
+})
 
 export const ProfileCardWithFollowBtn = observer(
-  ({
+  function ProfileCardWithFollowBtnImpl({
     profile,
     noBg,
     noBorder,
@@ -194,7 +189,7 @@ export const ProfileCardWithFollowBtn = observer(
     noBg?: boolean
     noBorder?: boolean
     followers?: AppBskyActorDefs.ProfileView[] | undefined
-  }) => {
+  }) {
     const store = useStores()
     const isMe = store.me.did === profile.did
 
diff --git a/src/view/com/profile/ProfileFollowers.tsx b/src/view/com/profile/ProfileFollowers.tsx
index aeb2fcba9..beb9609b6 100644
--- a/src/view/com/profile/ProfileFollowers.tsx
+++ b/src/view/com/profile/ProfileFollowers.tsx
@@ -78,6 +78,8 @@ export const ProfileFollowers = observer(function ProfileFollowers({
       onEndReached={onEndReached}
       renderItem={renderItem}
       initialNumToRender={15}
+      // FIXME(dan)
+      // eslint-disable-next-line react/no-unstable-nested-components
       ListFooterComponent={() => (
         <View style={styles.footer}>
           {view.isLoading && <ActivityIndicator />}
diff --git a/src/view/com/profile/ProfileFollows.tsx b/src/view/com/profile/ProfileFollows.tsx
index 0632fac02..22722ee63 100644
--- a/src/view/com/profile/ProfileFollows.tsx
+++ b/src/view/com/profile/ProfileFollows.tsx
@@ -75,6 +75,8 @@ export const ProfileFollows = observer(function ProfileFollows({
       onEndReached={onEndReached}
       renderItem={renderItem}
       initialNumToRender={15}
+      // FIXME(dan)
+      // eslint-disable-next-line react/no-unstable-nested-components
       ListFooterComponent={() => (
         <View style={styles.footer}>
           {view.isLoading && <ActivityIndicator />}
diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx
index 1c683ab9a..b52d338aa 100644
--- a/src/view/com/profile/ProfileHeader.tsx
+++ b/src/view/com/profile/ProfileHeader.tsx
@@ -45,510 +45,502 @@ interface Props {
   hideBackButton?: boolean
 }
 
-export const ProfileHeader = observer(
-  ({view, onRefreshAll, hideBackButton = false}: Props) => {
-    const pal = usePalette('default')
+export const ProfileHeader = observer(function ProfileHeaderImpl({
+  view,
+  onRefreshAll,
+  hideBackButton = false,
+}: Props) {
+  const pal = usePalette('default')
 
-    // loading
-    // =
-    if (!view || !view.hasLoaded) {
-      return (
-        <View style={pal.view}>
-          <LoadingPlaceholder width="100%" height={120} />
-          <View
-            style={[
-              pal.view,
-              {borderColor: pal.colors.background},
-              styles.avi,
-            ]}>
-            <LoadingPlaceholder width={80} height={80} style={styles.br40} />
+  // loading
+  // =
+  if (!view || !view.hasLoaded) {
+    return (
+      <View style={pal.view}>
+        <LoadingPlaceholder width="100%" height={120} />
+        <View
+          style={[pal.view, {borderColor: pal.colors.background}, styles.avi]}>
+          <LoadingPlaceholder width={80} height={80} style={styles.br40} />
+        </View>
+        <View style={styles.content}>
+          <View style={[styles.buttonsLine]}>
+            <LoadingPlaceholder width={100} height={31} style={styles.br50} />
           </View>
-          <View style={styles.content}>
-            <View style={[styles.buttonsLine]}>
-              <LoadingPlaceholder width={100} height={31} style={styles.br50} />
-            </View>
-            <View>
-              <Text type="title-2xl" style={[pal.text, styles.title]}>
-                {sanitizeDisplayName(
-                  view.displayName || sanitizeHandle(view.handle),
-                )}
-              </Text>
-            </View>
+          <View>
+            <Text type="title-2xl" style={[pal.text, styles.title]}>
+              {sanitizeDisplayName(
+                view.displayName || sanitizeHandle(view.handle),
+              )}
+            </Text>
           </View>
         </View>
-      )
-    }
-
-    // error
-    // =
-    if (view.hasError) {
-      return (
-        <View testID="profileHeaderHasError">
-          <Text>{view.error}</Text>
-        </View>
-      )
-    }
+      </View>
+    )
+  }
 
-    // loaded
-    // =
+  // error
+  // =
+  if (view.hasError) {
     return (
-      <ProfileHeaderLoaded
-        view={view}
-        onRefreshAll={onRefreshAll}
-        hideBackButton={hideBackButton}
-      />
+      <View testID="profileHeaderHasError">
+        <Text>{view.error}</Text>
+      </View>
     )
-  },
-)
+  }
 
-const ProfileHeaderLoaded = observer(
-  ({view, onRefreshAll, hideBackButton = false}: Props) => {
-    const pal = usePalette('default')
-    const palInverted = usePalette('inverted')
-    const store = useStores()
-    const navigation = useNavigation<NavigationProp>()
-    const {track} = useAnalytics()
-    const invalidHandle = isInvalidHandle(view.handle)
-    const {isDesktop} = useWebMediaQueries()
+  // loaded
+  // =
+  return (
+    <ProfileHeaderLoaded
+      view={view}
+      onRefreshAll={onRefreshAll}
+      hideBackButton={hideBackButton}
+    />
+  )
+})
 
-    const onPressBack = React.useCallback(() => {
-      navigation.goBack()
-    }, [navigation])
+const ProfileHeaderLoaded = observer(function ProfileHeaderLoadedImpl({
+  view,
+  onRefreshAll,
+  hideBackButton = false,
+}: Props) {
+  const pal = usePalette('default')
+  const palInverted = usePalette('inverted')
+  const store = useStores()
+  const navigation = useNavigation<NavigationProp>()
+  const {track} = useAnalytics()
+  const invalidHandle = isInvalidHandle(view.handle)
+  const {isDesktop} = useWebMediaQueries()
 
-    const onPressAvi = React.useCallback(() => {
-      if (
-        view.avatar &&
-        !(view.moderation.avatar.blur && view.moderation.avatar.noOverride)
-      ) {
-        store.shell.openLightbox(new ProfileImageLightbox(view))
-      }
-    }, [store, view])
+  const onPressBack = React.useCallback(() => {
+    navigation.goBack()
+  }, [navigation])
 
-    const onPressToggleFollow = React.useCallback(() => {
-      track(
-        view.viewer.following
-          ? 'ProfileHeader:FollowButtonClicked'
-          : 'ProfileHeader:UnfollowButtonClicked',
-      )
-      view?.toggleFollowing().then(
-        () => {
-          Toast.show(
-            `${
-              view.viewer.following ? 'Following' : 'No longer following'
-            } ${sanitizeDisplayName(view.displayName || view.handle)}`,
-          )
-        },
-        err => store.log.error('Failed to toggle follow', err),
-      )
-    }, [track, view, store.log])
+  const onPressAvi = React.useCallback(() => {
+    if (
+      view.avatar &&
+      !(view.moderation.avatar.blur && view.moderation.avatar.noOverride)
+    ) {
+      store.shell.openLightbox(new ProfileImageLightbox(view))
+    }
+  }, [store, view])
 
-    const onPressEditProfile = React.useCallback(() => {
-      track('ProfileHeader:EditProfileButtonClicked')
-      store.shell.openModal({
-        name: 'edit-profile',
-        profileView: view,
-        onUpdate: onRefreshAll,
-      })
-    }, [track, store, view, onRefreshAll])
+  const onPressToggleFollow = React.useCallback(() => {
+    track(
+      view.viewer.following
+        ? 'ProfileHeader:FollowButtonClicked'
+        : 'ProfileHeader:UnfollowButtonClicked',
+    )
+    view?.toggleFollowing().then(
+      () => {
+        Toast.show(
+          `${
+            view.viewer.following ? 'Following' : 'No longer following'
+          } ${sanitizeDisplayName(view.displayName || view.handle)}`,
+        )
+      },
+      err => store.log.error('Failed to toggle follow', err),
+    )
+  }, [track, view, store.log])
 
-    const onPressFollowers = React.useCallback(() => {
-      track('ProfileHeader:FollowersButtonClicked')
-      navigate('ProfileFollowers', {
-        name: isInvalidHandle(view.handle) ? view.did : view.handle,
-      })
-      store.shell.closeAllActiveElements() // for when used in the profile preview modal
-    }, [track, view, store.shell])
+  const onPressEditProfile = React.useCallback(() => {
+    track('ProfileHeader:EditProfileButtonClicked')
+    store.shell.openModal({
+      name: 'edit-profile',
+      profileView: view,
+      onUpdate: onRefreshAll,
+    })
+  }, [track, store, view, onRefreshAll])
 
-    const onPressFollows = React.useCallback(() => {
-      track('ProfileHeader:FollowsButtonClicked')
-      navigate('ProfileFollows', {
-        name: isInvalidHandle(view.handle) ? view.did : view.handle,
-      })
-      store.shell.closeAllActiveElements() // for when used in the profile preview modal
-    }, [track, view, store.shell])
+  const onPressFollowers = React.useCallback(() => {
+    track('ProfileHeader:FollowersButtonClicked')
+    navigate('ProfileFollowers', {
+      name: isInvalidHandle(view.handle) ? view.did : view.handle,
+    })
+    store.shell.closeAllActiveElements() // for when used in the profile preview modal
+  }, [track, view, store.shell])
 
-    const onPressShare = React.useCallback(() => {
-      track('ProfileHeader:ShareButtonClicked')
-      const url = toShareUrl(makeProfileLink(view))
-      shareUrl(url)
-    }, [track, view])
+  const onPressFollows = React.useCallback(() => {
+    track('ProfileHeader:FollowsButtonClicked')
+    navigate('ProfileFollows', {
+      name: isInvalidHandle(view.handle) ? view.did : view.handle,
+    })
+    store.shell.closeAllActiveElements() // for when used in the profile preview modal
+  }, [track, view, store.shell])
 
-    const onPressAddRemoveLists = React.useCallback(() => {
-      track('ProfileHeader:AddToListsButtonClicked')
-      store.shell.openModal({
-        name: 'list-add-remove-user',
-        subject: view.did,
-        displayName: view.displayName || view.handle,
-      })
-    }, [track, view, store])
+  const onPressShare = React.useCallback(() => {
+    track('ProfileHeader:ShareButtonClicked')
+    const url = toShareUrl(makeProfileLink(view))
+    shareUrl(url)
+  }, [track, view])
 
-    const onPressMuteAccount = React.useCallback(async () => {
-      track('ProfileHeader:MuteAccountButtonClicked')
-      try {
-        await view.muteAccount()
-        Toast.show('Account muted')
-      } catch (e: any) {
-        store.log.error('Failed to mute account', e)
-        Toast.show(`There was an issue! ${e.toString()}`)
-      }
-    }, [track, view, store])
+  const onPressAddRemoveLists = React.useCallback(() => {
+    track('ProfileHeader:AddToListsButtonClicked')
+    store.shell.openModal({
+      name: 'list-add-remove-user',
+      subject: view.did,
+      displayName: view.displayName || view.handle,
+    })
+  }, [track, view, store])
 
-    const onPressUnmuteAccount = React.useCallback(async () => {
-      track('ProfileHeader:UnmuteAccountButtonClicked')
-      try {
-        await view.unmuteAccount()
-        Toast.show('Account unmuted')
-      } catch (e: any) {
-        store.log.error('Failed to unmute account', e)
-        Toast.show(`There was an issue! ${e.toString()}`)
-      }
-    }, [track, view, store])
+  const onPressMuteAccount = React.useCallback(async () => {
+    track('ProfileHeader:MuteAccountButtonClicked')
+    try {
+      await view.muteAccount()
+      Toast.show('Account muted')
+    } catch (e: any) {
+      store.log.error('Failed to mute account', e)
+      Toast.show(`There was an issue! ${e.toString()}`)
+    }
+  }, [track, view, store])
 
-    const onPressBlockAccount = React.useCallback(async () => {
-      track('ProfileHeader:BlockAccountButtonClicked')
-      store.shell.openModal({
-        name: 'confirm',
-        title: 'Block Account',
-        message:
-          'Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you.',
-        onPressConfirm: async () => {
-          try {
-            await view.blockAccount()
-            onRefreshAll()
-            Toast.show('Account blocked')
-          } catch (e: any) {
-            store.log.error('Failed to block account', e)
-            Toast.show(`There was an issue! ${e.toString()}`)
-          }
-        },
-      })
-    }, [track, view, store, onRefreshAll])
+  const onPressUnmuteAccount = React.useCallback(async () => {
+    track('ProfileHeader:UnmuteAccountButtonClicked')
+    try {
+      await view.unmuteAccount()
+      Toast.show('Account unmuted')
+    } catch (e: any) {
+      store.log.error('Failed to unmute account', e)
+      Toast.show(`There was an issue! ${e.toString()}`)
+    }
+  }, [track, view, store])
 
-    const onPressUnblockAccount = React.useCallback(async () => {
-      track('ProfileHeader:UnblockAccountButtonClicked')
-      store.shell.openModal({
-        name: 'confirm',
-        title: 'Unblock Account',
-        message:
-          'The account will be able to interact with you after unblocking.',
-        onPressConfirm: async () => {
-          try {
-            await view.unblockAccount()
-            onRefreshAll()
-            Toast.show('Account unblocked')
-          } catch (e: any) {
-            store.log.error('Failed to unblock account', e)
-            Toast.show(`There was an issue! ${e.toString()}`)
-          }
-        },
-      })
-    }, [track, view, store, onRefreshAll])
+  const onPressBlockAccount = React.useCallback(async () => {
+    track('ProfileHeader:BlockAccountButtonClicked')
+    store.shell.openModal({
+      name: 'confirm',
+      title: 'Block Account',
+      message:
+        'Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you.',
+      onPressConfirm: async () => {
+        try {
+          await view.blockAccount()
+          onRefreshAll()
+          Toast.show('Account blocked')
+        } catch (e: any) {
+          store.log.error('Failed to block account', e)
+          Toast.show(`There was an issue! ${e.toString()}`)
+        }
+      },
+    })
+  }, [track, view, store, onRefreshAll])
 
-    const onPressReportAccount = React.useCallback(() => {
-      track('ProfileHeader:ReportAccountButtonClicked')
-      store.shell.openModal({
-        name: 'report',
-        did: view.did,
-      })
-    }, [track, store, view])
+  const onPressUnblockAccount = React.useCallback(async () => {
+    track('ProfileHeader:UnblockAccountButtonClicked')
+    store.shell.openModal({
+      name: 'confirm',
+      title: 'Unblock Account',
+      message:
+        'The account will be able to interact with you after unblocking.',
+      onPressConfirm: async () => {
+        try {
+          await view.unblockAccount()
+          onRefreshAll()
+          Toast.show('Account unblocked')
+        } catch (e: any) {
+          store.log.error('Failed to unblock account', e)
+          Toast.show(`There was an issue! ${e.toString()}`)
+        }
+      },
+    })
+  }, [track, view, store, onRefreshAll])
 
-    const isMe = React.useMemo(
-      () => store.me.did === view.did,
-      [store.me.did, view.did],
-    )
-    const dropdownItems: DropdownItem[] = React.useMemo(() => {
-      let items: DropdownItem[] = [
-        {
-          testID: 'profileHeaderDropdownShareBtn',
-          label: 'Share',
-          onPress: onPressShare,
-          icon: {
-            ios: {
-              name: 'square.and.arrow.up',
-            },
-            android: 'ic_menu_share',
-            web: 'share',
+  const onPressReportAccount = React.useCallback(() => {
+    track('ProfileHeader:ReportAccountButtonClicked')
+    store.shell.openModal({
+      name: 'report',
+      did: view.did,
+    })
+  }, [track, store, view])
+
+  const isMe = React.useMemo(
+    () => store.me.did === view.did,
+    [store.me.did, view.did],
+  )
+  const dropdownItems: DropdownItem[] = React.useMemo(() => {
+    let items: DropdownItem[] = [
+      {
+        testID: 'profileHeaderDropdownShareBtn',
+        label: 'Share',
+        onPress: onPressShare,
+        icon: {
+          ios: {
+            name: 'square.and.arrow.up',
           },
+          android: 'ic_menu_share',
+          web: 'share',
         },
-      ]
-      if (!isMe) {
-        items.push({label: 'separator'})
-        // Only add "Add to Lists" on other user's profiles, doesn't make sense to mute my own self!
-        items.push({
-          testID: 'profileHeaderDropdownListAddRemoveBtn',
-          label: 'Add to Lists',
-          onPress: onPressAddRemoveLists,
-          icon: {
-            ios: {
-              name: 'list.bullet',
-            },
-            android: 'ic_menu_add',
-            web: 'list',
-          },
-        })
-        if (!view.viewer.blocking) {
-          items.push({
-            testID: 'profileHeaderDropdownMuteBtn',
-            label: view.viewer.muted ? 'Unmute Account' : 'Mute Account',
-            onPress: view.viewer.muted
-              ? onPressUnmuteAccount
-              : onPressMuteAccount,
-            icon: {
-              ios: {
-                name: 'speaker.slash',
-              },
-              android: 'ic_lock_silent_mode',
-              web: 'comment-slash',
-            },
-          })
-        }
-        items.push({
-          testID: 'profileHeaderDropdownBlockBtn',
-          label: view.viewer.blocking ? 'Unblock Account' : 'Block Account',
-          onPress: view.viewer.blocking
-            ? onPressUnblockAccount
-            : onPressBlockAccount,
-          icon: {
-            ios: {
-              name: 'person.fill.xmark',
-            },
-            android: 'ic_menu_close_clear_cancel',
-            web: 'user-slash',
+      },
+    ]
+    if (!isMe) {
+      items.push({label: 'separator'})
+      // Only add "Add to Lists" on other user's profiles, doesn't make sense to mute my own self!
+      items.push({
+        testID: 'profileHeaderDropdownListAddRemoveBtn',
+        label: 'Add to Lists',
+        onPress: onPressAddRemoveLists,
+        icon: {
+          ios: {
+            name: 'list.bullet',
           },
-        })
+          android: 'ic_menu_add',
+          web: 'list',
+        },
+      })
+      if (!view.viewer.blocking) {
         items.push({
-          testID: 'profileHeaderDropdownReportBtn',
-          label: 'Report Account',
-          onPress: onPressReportAccount,
+          testID: 'profileHeaderDropdownMuteBtn',
+          label: view.viewer.muted ? 'Unmute Account' : 'Mute Account',
+          onPress: view.viewer.muted
+            ? onPressUnmuteAccount
+            : onPressMuteAccount,
           icon: {
             ios: {
-              name: 'exclamationmark.triangle',
+              name: 'speaker.slash',
             },
-            android: 'ic_menu_report_image',
-            web: 'circle-exclamation',
+            android: 'ic_lock_silent_mode',
+            web: 'comment-slash',
           },
         })
       }
-      return items
-    }, [
-      isMe,
-      view.viewer.muted,
-      view.viewer.blocking,
-      onPressShare,
-      onPressUnmuteAccount,
-      onPressMuteAccount,
-      onPressUnblockAccount,
-      onPressBlockAccount,
-      onPressReportAccount,
-      onPressAddRemoveLists,
-    ])
+      items.push({
+        testID: 'profileHeaderDropdownBlockBtn',
+        label: view.viewer.blocking ? 'Unblock Account' : 'Block Account',
+        onPress: view.viewer.blocking
+          ? onPressUnblockAccount
+          : onPressBlockAccount,
+        icon: {
+          ios: {
+            name: 'person.fill.xmark',
+          },
+          android: 'ic_menu_close_clear_cancel',
+          web: 'user-slash',
+        },
+      })
+      items.push({
+        testID: 'profileHeaderDropdownReportBtn',
+        label: 'Report Account',
+        onPress: onPressReportAccount,
+        icon: {
+          ios: {
+            name: 'exclamationmark.triangle',
+          },
+          android: 'ic_menu_report_image',
+          web: 'circle-exclamation',
+        },
+      })
+    }
+    return items
+  }, [
+    isMe,
+    view.viewer.muted,
+    view.viewer.blocking,
+    onPressShare,
+    onPressUnmuteAccount,
+    onPressMuteAccount,
+    onPressUnblockAccount,
+    onPressBlockAccount,
+    onPressReportAccount,
+    onPressAddRemoveLists,
+  ])
 
-    const blockHide = !isMe && (view.viewer.blocking || view.viewer.blockedBy)
-    const following = formatCount(view.followsCount)
-    const followers = formatCount(view.followersCount)
-    const pluralizedFollowers = pluralize(view.followersCount, 'follower')
+  const blockHide = !isMe && (view.viewer.blocking || view.viewer.blockedBy)
+  const following = formatCount(view.followsCount)
+  const followers = formatCount(view.followersCount)
+  const pluralizedFollowers = pluralize(view.followersCount, 'follower')
 
-    return (
-      <View style={pal.view}>
-        <UserBanner banner={view.banner} moderation={view.moderation.avatar} />
-        <View style={styles.content}>
-          <View style={[styles.buttonsLine]}>
-            {isMe ? (
-              <TouchableOpacity
-                testID="profileHeaderEditProfileButton"
-                onPress={onPressEditProfile}
-                style={[styles.btn, styles.mainBtn, pal.btn]}
-                accessibilityRole="button"
-                accessibilityLabel="Edit profile"
-                accessibilityHint="Opens editor for profile display name, avatar, background image, and description">
-                <Text type="button" style={pal.text}>
-                  Edit Profile
-                </Text>
-              </TouchableOpacity>
-            ) : view.viewer.blocking ? (
-              <TouchableOpacity
-                testID="unblockBtn"
-                onPress={onPressUnblockAccount}
-                style={[styles.btn, styles.mainBtn, pal.btn]}
-                accessibilityRole="button"
-                accessibilityLabel="Unblock"
-                accessibilityHint="">
-                <Text type="button" style={[pal.text, s.bold]}>
-                  Unblock
-                </Text>
-              </TouchableOpacity>
-            ) : !view.viewer.blockedBy ? (
-              <>
-                {store.me.follows.getFollowState(view.did) ===
-                FollowState.Following ? (
-                  <TouchableOpacity
-                    testID="unfollowBtn"
-                    onPress={onPressToggleFollow}
-                    style={[styles.btn, styles.mainBtn, pal.btn]}
-                    accessibilityRole="button"
-                    accessibilityLabel={`Unfollow ${view.handle}`}
-                    accessibilityHint={`Hides posts from ${view.handle} in your feed`}>
-                    <FontAwesomeIcon
-                      icon="check"
-                      style={[pal.text, s.mr5]}
-                      size={14}
-                    />
-                    <Text type="button" style={pal.text}>
-                      Following
-                    </Text>
-                  </TouchableOpacity>
-                ) : (
-                  <TouchableOpacity
-                    testID="followBtn"
-                    onPress={onPressToggleFollow}
-                    style={[styles.btn, styles.mainBtn, palInverted.view]}
-                    accessibilityRole="button"
-                    accessibilityLabel={`Follow ${view.handle}`}
-                    accessibilityHint={`Shows posts from ${view.handle} in your feed`}>
-                    <FontAwesomeIcon
-                      icon="plus"
-                      style={[palInverted.text, s.mr5]}
-                    />
-                    <Text type="button" style={[palInverted.text, s.bold]}>
-                      Follow
-                    </Text>
-                  </TouchableOpacity>
-                )}
-              </>
-            ) : null}
-            {dropdownItems?.length ? (
-              <NativeDropdown
-                testID="profileHeaderDropdownBtn"
-                items={dropdownItems}>
-                <View style={[styles.btn, styles.secondaryBtn, pal.btn]}>
-                  <FontAwesomeIcon
-                    icon="ellipsis"
-                    size={20}
-                    style={[pal.text]}
-                  />
-                </View>
-              </NativeDropdown>
-            ) : undefined}
-          </View>
-          <View>
-            <Text
-              testID="profileHeaderDisplayName"
-              type="title-2xl"
-              style={[pal.text, styles.title]}>
-              {sanitizeDisplayName(
-                view.displayName || sanitizeHandle(view.handle),
-                view.moderation.profile,
-              )}
-            </Text>
-          </View>
-          <View style={styles.handleLine}>
-            {view.viewer.followedBy && !blockHide ? (
-              <View style={[styles.pill, pal.btn, s.mr5]}>
-                <Text type="xs" style={[pal.text]}>
-                  Follows you
-                </Text>
-              </View>
-            ) : undefined}
-            <ThemedText
-              type={invalidHandle ? 'xs' : 'md'}
-              fg={invalidHandle ? 'error' : 'light'}
-              border={invalidHandle ? 'error' : undefined}
-              style={[
-                invalidHandle ? styles.invalidHandle : undefined,
-                styles.handle,
-              ]}>
-              {invalidHandle ? 'âš Invalid Handle' : `@${view.handle}`}
-            </ThemedText>
-          </View>
-          {!blockHide && (
+  return (
+    <View style={pal.view}>
+      <UserBanner banner={view.banner} moderation={view.moderation.avatar} />
+      <View style={styles.content}>
+        <View style={[styles.buttonsLine]}>
+          {isMe ? (
+            <TouchableOpacity
+              testID="profileHeaderEditProfileButton"
+              onPress={onPressEditProfile}
+              style={[styles.btn, styles.mainBtn, pal.btn]}
+              accessibilityRole="button"
+              accessibilityLabel="Edit profile"
+              accessibilityHint="Opens editor for profile display name, avatar, background image, and description">
+              <Text type="button" style={pal.text}>
+                Edit Profile
+              </Text>
+            </TouchableOpacity>
+          ) : view.viewer.blocking ? (
+            <TouchableOpacity
+              testID="unblockBtn"
+              onPress={onPressUnblockAccount}
+              style={[styles.btn, styles.mainBtn, pal.btn]}
+              accessibilityRole="button"
+              accessibilityLabel="Unblock"
+              accessibilityHint="">
+              <Text type="button" style={[pal.text, s.bold]}>
+                Unblock
+              </Text>
+            </TouchableOpacity>
+          ) : !view.viewer.blockedBy ? (
             <>
-              <View style={styles.metricsLine}>
+              {store.me.follows.getFollowState(view.did) ===
+              FollowState.Following ? (
                 <TouchableOpacity
-                  testID="profileHeaderFollowersButton"
-                  style={[s.flexRow, s.mr10]}
-                  onPress={onPressFollowers}
+                  testID="unfollowBtn"
+                  onPress={onPressToggleFollow}
+                  style={[styles.btn, styles.mainBtn, pal.btn]}
                   accessibilityRole="button"
-                  accessibilityLabel={`${followers} ${pluralizedFollowers}`}
-                  accessibilityHint={'Opens followers list'}>
-                  <Text type="md" style={[s.bold, pal.text]}>
-                    {followers}{' '}
-                  </Text>
-                  <Text type="md" style={[pal.textLight]}>
-                    {pluralizedFollowers}
+                  accessibilityLabel={`Unfollow ${view.handle}`}
+                  accessibilityHint={`Hides posts from ${view.handle} in your feed`}>
+                  <FontAwesomeIcon
+                    icon="check"
+                    style={[pal.text, s.mr5]}
+                    size={14}
+                  />
+                  <Text type="button" style={pal.text}>
+                    Following
                   </Text>
                 </TouchableOpacity>
+              ) : (
                 <TouchableOpacity
-                  testID="profileHeaderFollowsButton"
-                  style={[s.flexRow, s.mr10]}
-                  onPress={onPressFollows}
+                  testID="followBtn"
+                  onPress={onPressToggleFollow}
+                  style={[styles.btn, styles.mainBtn, palInverted.view]}
                   accessibilityRole="button"
-                  accessibilityLabel={`${following} following`}
-                  accessibilityHint={'Opens following list'}>
-                  <Text type="md" style={[s.bold, pal.text]}>
-                    {following}{' '}
-                  </Text>
-                  <Text type="md" style={[pal.textLight]}>
-                    following
+                  accessibilityLabel={`Follow ${view.handle}`}
+                  accessibilityHint={`Shows posts from ${view.handle} in your feed`}>
+                  <FontAwesomeIcon
+                    icon="plus"
+                    style={[palInverted.text, s.mr5]}
+                  />
+                  <Text type="button" style={[palInverted.text, s.bold]}>
+                    Follow
                   </Text>
                 </TouchableOpacity>
-                <Text type="md" style={[s.bold, pal.text]}>
-                  {formatCount(view.postsCount)}{' '}
-                  <Text type="md" style={[pal.textLight]}>
-                    {pluralize(view.postsCount, 'post')}
-                  </Text>
-                </Text>
-              </View>
-              {view.description &&
-              view.descriptionRichText &&
-              !view.moderation.profile.blur ? (
-                <RichText
-                  testID="profileHeaderDescription"
-                  style={[styles.description, pal.text]}
-                  numberOfLines={15}
-                  richText={view.descriptionRichText}
-                />
-              ) : undefined}
+              )}
             </>
-          )}
-          <ProfileHeaderAlerts moderation={view.moderation} />
+          ) : null}
+          {dropdownItems?.length ? (
+            <NativeDropdown
+              testID="profileHeaderDropdownBtn"
+              items={dropdownItems}>
+              <View style={[styles.btn, styles.secondaryBtn, pal.btn]}>
+                <FontAwesomeIcon icon="ellipsis" size={20} style={[pal.text]} />
+              </View>
+            </NativeDropdown>
+          ) : undefined}
+        </View>
+        <View>
+          <Text
+            testID="profileHeaderDisplayName"
+            type="title-2xl"
+            style={[pal.text, styles.title]}>
+            {sanitizeDisplayName(
+              view.displayName || sanitizeHandle(view.handle),
+              view.moderation.profile,
+            )}
+          </Text>
+        </View>
+        <View style={styles.handleLine}>
+          {view.viewer.followedBy && !blockHide ? (
+            <View style={[styles.pill, pal.btn, s.mr5]}>
+              <Text type="xs" style={[pal.text]}>
+                Follows you
+              </Text>
+            </View>
+          ) : undefined}
+          <ThemedText
+            type={invalidHandle ? 'xs' : 'md'}
+            fg={invalidHandle ? 'error' : 'light'}
+            border={invalidHandle ? 'error' : undefined}
+            style={[
+              invalidHandle ? styles.invalidHandle : undefined,
+              styles.handle,
+            ]}>
+            {invalidHandle ? 'âš Invalid Handle' : `@${view.handle}`}
+          </ThemedText>
         </View>
-        {!isDesktop && !hideBackButton && (
-          <TouchableWithoutFeedback
-            onPress={onPressBack}
-            hitSlop={BACK_HITSLOP}
-            accessibilityRole="button"
-            accessibilityLabel="Back"
-            accessibilityHint="">
-            <View style={styles.backBtnWrapper}>
-              <BlurView style={styles.backBtn} blurType="dark">
-                <FontAwesomeIcon size={18} icon="angle-left" style={s.white} />
-              </BlurView>
+        {!blockHide && (
+          <>
+            <View style={styles.metricsLine}>
+              <TouchableOpacity
+                testID="profileHeaderFollowersButton"
+                style={[s.flexRow, s.mr10]}
+                onPress={onPressFollowers}
+                accessibilityRole="button"
+                accessibilityLabel={`${followers} ${pluralizedFollowers}`}
+                accessibilityHint={'Opens followers list'}>
+                <Text type="md" style={[s.bold, pal.text]}>
+                  {followers}{' '}
+                </Text>
+                <Text type="md" style={[pal.textLight]}>
+                  {pluralizedFollowers}
+                </Text>
+              </TouchableOpacity>
+              <TouchableOpacity
+                testID="profileHeaderFollowsButton"
+                style={[s.flexRow, s.mr10]}
+                onPress={onPressFollows}
+                accessibilityRole="button"
+                accessibilityLabel={`${following} following`}
+                accessibilityHint={'Opens following list'}>
+                <Text type="md" style={[s.bold, pal.text]}>
+                  {following}{' '}
+                </Text>
+                <Text type="md" style={[pal.textLight]}>
+                  following
+                </Text>
+              </TouchableOpacity>
+              <Text type="md" style={[s.bold, pal.text]}>
+                {formatCount(view.postsCount)}{' '}
+                <Text type="md" style={[pal.textLight]}>
+                  {pluralize(view.postsCount, 'post')}
+                </Text>
+              </Text>
             </View>
-          </TouchableWithoutFeedback>
+            {view.description &&
+            view.descriptionRichText &&
+            !view.moderation.profile.blur ? (
+              <RichText
+                testID="profileHeaderDescription"
+                style={[styles.description, pal.text]}
+                numberOfLines={15}
+                richText={view.descriptionRichText}
+              />
+            ) : undefined}
+          </>
         )}
+        <ProfileHeaderAlerts moderation={view.moderation} />
+      </View>
+      {!isDesktop && !hideBackButton && (
         <TouchableWithoutFeedback
-          testID="profileHeaderAviButton"
-          onPress={onPressAvi}
-          accessibilityRole="image"
-          accessibilityLabel={`View ${view.handle}'s avatar`}
+          onPress={onPressBack}
+          hitSlop={BACK_HITSLOP}
+          accessibilityRole="button"
+          accessibilityLabel="Back"
           accessibilityHint="">
-          <View
-            style={[
-              pal.view,
-              {borderColor: pal.colors.background},
-              styles.avi,
-            ]}>
-            <UserAvatar
-              size={80}
-              avatar={view.avatar}
-              moderation={view.moderation.avatar}
-            />
+          <View style={styles.backBtnWrapper}>
+            <BlurView style={styles.backBtn} blurType="dark">
+              <FontAwesomeIcon size={18} icon="angle-left" style={s.white} />
+            </BlurView>
           </View>
         </TouchableWithoutFeedback>
-      </View>
-    )
-  },
-)
+      )}
+      <TouchableWithoutFeedback
+        testID="profileHeaderAviButton"
+        onPress={onPressAvi}
+        accessibilityRole="image"
+        accessibilityLabel={`View ${view.handle}'s avatar`}
+        accessibilityHint="">
+        <View
+          style={[pal.view, {borderColor: pal.colors.background}, styles.avi]}>
+          <UserAvatar
+            size={80}
+            avatar={view.avatar}
+            moderation={view.moderation.avatar}
+          />
+        </View>
+      </TouchableWithoutFeedback>
+    </View>
+  )
+})
 
 const styles = StyleSheet.create({
   banner: {
diff --git a/src/view/com/search/SearchResults.tsx b/src/view/com/search/SearchResults.tsx
index e74a8cfe4..87378bba7 100644
--- a/src/view/com/search/SearchResults.tsx
+++ b/src/view/com/search/SearchResults.tsx
@@ -18,7 +18,11 @@ import {s} from 'lib/styles'
 
 const SECTIONS = ['Posts', 'Users']
 
-export const SearchResults = observer(({model}: {model: SearchUIModel}) => {
+export const SearchResults = observer(function SearchResultsImpl({
+  model,
+}: {
+  model: SearchUIModel
+}) {
   const pal = usePalette('default')
   const {isMobile} = useWebMediaQueries()
 
@@ -56,7 +60,11 @@ export const SearchResults = observer(({model}: {model: SearchUIModel}) => {
   )
 })
 
-const PostResults = observer(({model}: {model: SearchUIModel}) => {
+const PostResults = observer(function PostResultsImpl({
+  model,
+}: {
+  model: SearchUIModel
+}) {
   const pal = usePalette('default')
   if (model.isPostsLoading) {
     return (
@@ -88,7 +96,11 @@ const PostResults = observer(({model}: {model: SearchUIModel}) => {
   )
 })
 
-const Profiles = observer(({model}: {model: SearchUIModel}) => {
+const Profiles = observer(function ProfilesImpl({
+  model,
+}: {
+  model: SearchUIModel
+}) {
   const pal = usePalette('default')
   if (model.isProfilesLoading) {
     return (
diff --git a/src/view/com/search/Suggestions.tsx b/src/view/com/search/Suggestions.tsx
index 6f9fff52f..02a38a0eb 100644
--- a/src/view/com/search/Suggestions.tsx
+++ b/src/view/com/search/Suggestions.tsx
@@ -38,6 +38,9 @@ interface ProfileView {
 }
 type Item = Heading | RefWrapper | SuggestWrapper | ProfileView
 
+// FIXME(dan): Figure out why the false positives
+/* eslint-disable react/prop-types */
+
 export const Suggestions = observer(
   forwardRef(function SuggestionsImpl(
     {
diff --git a/src/view/com/util/PostMeta.tsx b/src/view/com/util/PostMeta.tsx
index b0ad01754..21cbbc547 100644
--- a/src/view/com/util/PostMeta.tsx
+++ b/src/view/com/util/PostMeta.tsx
@@ -25,7 +25,7 @@ interface PostMetaOpts {
   timestamp: string
 }
 
-export const PostMeta = observer(function (opts: PostMetaOpts) {
+export const PostMeta = observer(function PostMetaImpl(opts: PostMetaOpts) {
   const pal = usePalette('default')
   const displayName = opts.author.displayName || opts.author.handle
   const handle = opts.author.handle
diff --git a/src/view/com/util/TimeElapsed.tsx b/src/view/com/util/TimeElapsed.tsx
index 7b2dd61f3..0765f65b2 100644
--- a/src/view/com/util/TimeElapsed.tsx
+++ b/src/view/com/util/TimeElapsed.tsx
@@ -3,6 +3,9 @@ import {observer} from 'mobx-react-lite'
 import {ago} from 'lib/strings/time'
 import {useStores} from 'state/index'
 
+// FIXME(dan): Figure out why the false positives
+/* eslint-disable react/prop-types */
+
 export const TimeElapsed = observer(function TimeElapsed({
   timestamp,
   children,
diff --git a/src/view/com/util/ViewHeader.tsx b/src/view/com/util/ViewHeader.tsx
index 91cdb08c7..164028708 100644
--- a/src/view/com/util/ViewHeader.tsx
+++ b/src/view/com/util/ViewHeader.tsx
@@ -14,7 +14,7 @@ import {NavigationProp} from 'lib/routes/types'
 
 const BACK_HITSLOP = {left: 20, top: 20, right: 50, bottom: 20}
 
-export const ViewHeader = observer(function ({
+export const ViewHeader = observer(function ViewHeaderImpl({
   title,
   canGoBack,
   showBackButton = true,
@@ -140,70 +140,68 @@ function DesktopWebHeader({
   )
 }
 
-const Container = observer(
-  ({
-    children,
-    hideOnScroll,
-    showBorder,
-  }: {
-    children: React.ReactNode
-    hideOnScroll: boolean
-    showBorder?: boolean
-  }) => {
-    const store = useStores()
-    const pal = usePalette('default')
-    const interp = useAnimatedValue(0)
+const Container = observer(function ContainerImpl({
+  children,
+  hideOnScroll,
+  showBorder,
+}: {
+  children: React.ReactNode
+  hideOnScroll: boolean
+  showBorder?: boolean
+}) {
+  const store = useStores()
+  const pal = usePalette('default')
+  const interp = useAnimatedValue(0)
 
-    React.useEffect(() => {
-      if (store.shell.minimalShellMode) {
-        Animated.timing(interp, {
-          toValue: 1,
-          duration: 100,
-          useNativeDriver: true,
-          isInteraction: false,
-        }).start()
-      } else {
-        Animated.timing(interp, {
-          toValue: 0,
-          duration: 100,
-          useNativeDriver: true,
-          isInteraction: false,
-        }).start()
-      }
-    }, [interp, store.shell.minimalShellMode])
-    const transform = {
-      transform: [{translateY: Animated.multiply(interp, -100)}],
+  React.useEffect(() => {
+    if (store.shell.minimalShellMode) {
+      Animated.timing(interp, {
+        toValue: 1,
+        duration: 100,
+        useNativeDriver: true,
+        isInteraction: false,
+      }).start()
+    } else {
+      Animated.timing(interp, {
+        toValue: 0,
+        duration: 100,
+        useNativeDriver: true,
+        isInteraction: false,
+      }).start()
     }
+  }, [interp, store.shell.minimalShellMode])
+  const transform = {
+    transform: [{translateY: Animated.multiply(interp, -100)}],
+  }
 
-    if (!hideOnScroll) {
-      return (
-        <View
-          style={[
-            styles.header,
-            styles.headerFixed,
-            pal.view,
-            pal.border,
-            showBorder && styles.border,
-          ]}>
-          {children}
-        </View>
-      )
-    }
+  if (!hideOnScroll) {
     return (
-      <Animated.View
+      <View
         style={[
           styles.header,
-          styles.headerFloating,
+          styles.headerFixed,
           pal.view,
           pal.border,
-          transform,
           showBorder && styles.border,
         ]}>
         {children}
-      </Animated.View>
+      </View>
     )
-  },
-)
+  }
+  return (
+    <Animated.View
+      style={[
+        styles.header,
+        styles.headerFloating,
+        pal.view,
+        pal.border,
+        transform,
+        showBorder && styles.border,
+      ]}>
+      {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 afd172c82..f5a3e6b50 100644
--- a/src/view/com/util/fab/FABInner.tsx
+++ b/src/view/com/util/fab/FABInner.tsx
@@ -14,7 +14,11 @@ export interface FABProps
   icon: JSX.Element
 }
 
-export const FABInner = observer(({testID, icon, ...props}: FABProps) => {
+export const FABInner = observer(function FABInnerImpl({
+  testID,
+  icon,
+  ...props
+}: FABProps) {
   const {isTablet} = useWebMediaQueries()
   const store = useStores()
   const interp = useAnimatedValue(0)
diff --git a/src/view/com/util/load-latest/LoadLatestBtnMobile.tsx b/src/view/com/util/load-latest/LoadLatestBtnMobile.tsx
index eb7eaaa49..3e8add5e9 100644
--- a/src/view/com/util/load-latest/LoadLatestBtnMobile.tsx
+++ b/src/view/com/util/load-latest/LoadLatestBtnMobile.tsx
@@ -9,41 +9,39 @@ import {usePalette} from 'lib/hooks/usePalette'
 import {colors} from 'lib/styles'
 import {HITSLOP_20} from 'lib/constants'
 
-export const LoadLatestBtn = observer(
-  ({
-    onPress,
-    label,
-    showIndicator,
-  }: {
-    onPress: () => void
-    label: string
-    showIndicator: boolean
-    minimalShellMode?: boolean // NOTE not used on mobile -prf
-  }) => {
-    const store = useStores()
-    const pal = usePalette('default')
-    const safeAreaInsets = useSafeAreaInsets()
-    return (
-      <TouchableOpacity
-        style={[
-          styles.loadLatest,
-          pal.borderDark,
-          pal.view,
-          !store.shell.minimalShellMode && {
-            bottom: 60 + clamp(safeAreaInsets.bottom, 15, 30),
-          },
-        ]}
-        onPress={onPress}
-        hitSlop={HITSLOP_20}
-        accessibilityRole="button"
-        accessibilityLabel={label}
-        accessibilityHint="">
-        <FontAwesomeIcon icon="angle-up" color={pal.colors.text} size={19} />
-        {showIndicator && <View style={[styles.indicator, pal.borderDark]} />}
-      </TouchableOpacity>
-    )
-  },
-)
+export const LoadLatestBtn = observer(function LoadLatestBtnImpl({
+  onPress,
+  label,
+  showIndicator,
+}: {
+  onPress: () => void
+  label: string
+  showIndicator: boolean
+  minimalShellMode?: boolean // NOTE not used on mobile -prf
+}) {
+  const store = useStores()
+  const pal = usePalette('default')
+  const safeAreaInsets = useSafeAreaInsets()
+  return (
+    <TouchableOpacity
+      style={[
+        styles.loadLatest,
+        pal.borderDark,
+        pal.view,
+        !store.shell.minimalShellMode && {
+          bottom: 60 + clamp(safeAreaInsets.bottom, 15, 30),
+        },
+      ]}
+      onPress={onPress}
+      hitSlop={HITSLOP_20}
+      accessibilityRole="button"
+      accessibilityLabel={label}
+      accessibilityHint="">
+      <FontAwesomeIcon icon="angle-up" color={pal.colors.text} size={19} />
+      {showIndicator && <View style={[styles.indicator, pal.borderDark]} />}
+    </TouchableOpacity>
+  )
+})
 
 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 6f40b3e1a..dbf350039 100644
--- a/src/view/com/util/post-embeds/ListEmbed.tsx
+++ b/src/view/com/util/post-embeds/ListEmbed.tsx
@@ -6,23 +6,21 @@ import {ListCard} from 'view/com/lists/ListCard'
 import {AppBskyGraphDefs} from '@atproto/api'
 import {s} from 'lib/styles'
 
-export const ListEmbed = observer(
-  ({
-    item,
-    style,
-  }: {
-    item: AppBskyGraphDefs.ListView
-    style?: StyleProp<ViewStyle>
-  }) => {
-    const pal = usePalette('default')
+export const ListEmbed = observer(function ListEmbedImpl({
+  item,
+  style,
+}: {
+  item: AppBskyGraphDefs.ListView
+  style?: StyleProp<ViewStyle>
+}) {
+  const pal = usePalette('default')
 
-    return (
-      <View style={[pal.view, pal.border, s.border1, styles.container]}>
-        <ListCard list={item} style={[style, styles.card]} />
-      </View>
-    )
-  },
-)
+  return (
+    <View style={[pal.view, pal.border, s.border1, styles.container]}>
+      <ListCard list={item} style={[style, styles.card]} />
+    </View>
+  )
+})
 
 const styles = StyleSheet.create({
   container: {
diff --git a/src/view/screens/AppPasswords.tsx b/src/view/screens/AppPasswords.tsx
index 8fac86d34..32f9e13e1 100644
--- a/src/view/screens/AppPasswords.tsx
+++ b/src/view/screens/AppPasswords.tsx
@@ -19,7 +19,7 @@ import {CenteredView} from 'view/com/util/Views'
 
 type Props = NativeStackScreenProps<CommonNavigatorParams, 'AppPasswords'>
 export const AppPasswords = withAuthRequired(
-  observer(({}: Props) => {
+  observer(function AppPasswordsImpl({}: Props) {
     const pal = usePalette('default')
     const store = useStores()
     const {screen} = useAnalytics()
diff --git a/src/view/screens/CustomFeed.tsx b/src/view/screens/CustomFeed.tsx
index f4e1b0eb7..af4d01843 100644
--- a/src/view/screens/CustomFeed.tsx
+++ b/src/view/screens/CustomFeed.tsx
@@ -42,7 +42,7 @@ import {NavigationProp} from 'lib/routes/types'
 type Props = NativeStackScreenProps<CommonNavigatorParams, 'CustomFeed'>
 
 export const CustomFeedScreen = withAuthRequired(
-  observer((props: Props) => {
+  observer(function CustomFeedScreenImpl(props: Props) {
     const pal = usePalette('default')
     const store = useStores()
     const navigation = useNavigation<NavigationProp>()
@@ -119,7 +119,10 @@ export const CustomFeedScreen = withAuthRequired(
 )
 
 export const CustomFeedScreenInner = observer(
-  ({route, feedOwnerDid}: Props & {feedOwnerDid: string}) => {
+  function CustomFeedScreenInnerImpl({
+    route,
+    feedOwnerDid,
+  }: Props & {feedOwnerDid: string}) {
     const store = useStores()
     const pal = usePalette('default')
     const {isTabletOrDesktop} = useWebMediaQueries()
diff --git a/src/view/screens/DiscoverFeeds.tsx b/src/view/screens/DiscoverFeeds.tsx
index 11f38c26a..6aa7a9e31 100644
--- a/src/view/screens/DiscoverFeeds.tsx
+++ b/src/view/screens/DiscoverFeeds.tsx
@@ -19,7 +19,7 @@ import debounce from 'lodash.debounce'
 
 type Props = NativeStackScreenProps<CommonNavigatorParams, 'DiscoverFeeds'>
 export const DiscoverFeedsScreen = withAuthRequired(
-  observer(({}: Props) => {
+  observer(function DiscoverFeedsScreenImpl({}: Props) {
     const store = useStores()
     const pal = usePalette('default')
     const feeds = React.useMemo(() => new FeedsDiscoveryModel(store), [store])
diff --git a/src/view/screens/Feeds.tsx b/src/view/screens/Feeds.tsx
index 6e0706737..97c6e8672 100644
--- a/src/view/screens/Feeds.tsx
+++ b/src/view/screens/Feeds.tsx
@@ -25,7 +25,7 @@ const MOBILE_HEADER_OFFSET = 40
 
 type Props = NativeStackScreenProps<FeedsTabNavigatorParams, 'Feeds'>
 export const FeedsScreen = withAuthRequired(
-  observer<Props>(({}: Props) => {
+  observer<Props>(function FeedsScreenImpl({}: Props) {
     const pal = usePalette('default')
     const store = useStores()
     const {isMobile} = useWebMediaQueries()
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
index 795d813d1..33cc2e110 100644
--- a/src/view/screens/Home.tsx
+++ b/src/view/screens/Home.tsx
@@ -28,7 +28,7 @@ const POLL_FREQ = 30e3 // 30sec
 
 type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'>
 export const HomeScreen = withAuthRequired(
-  observer(({}: Props) => {
+  observer(function HomeScreenImpl({}: Props) {
     const store = useStores()
     const pagerRef = React.useRef<PagerRef>(null)
     const [selectedPage, setSelectedPage] = React.useState(0)
@@ -142,152 +142,141 @@ export const HomeScreen = withAuthRequired(
   }),
 )
 
-const FeedPage = observer(
-  ({
-    testID,
-    isPageFocused,
-    feed,
-    renderEmptyState,
-  }: {
-    testID?: string
-    feed: PostsFeedModel
-    isPageFocused: boolean
-    renderEmptyState?: () => JSX.Element
-  }) => {
-    const store = useStores()
-    const {isMobile} = useWebMediaQueries()
-    const [onMainScroll, isScrolledDown, resetMainScroll] =
-      useOnMainScroll(store)
-    const {screen, track} = useAnalytics()
-    const [headerOffset, setHeaderOffset] = React.useState(
-      isMobile ? HEADER_OFFSET_MOBILE : HEADER_OFFSET_DESKTOP,
-    )
-    const scrollElRef = React.useRef<FlatList>(null)
-    const {appState} = useAppState({
-      onForeground: () => doPoll(true),
-    })
-    const isScreenFocused = useIsFocused()
+const FeedPage = observer(function FeedPageImpl({
+  testID,
+  isPageFocused,
+  feed,
+  renderEmptyState,
+}: {
+  testID?: string
+  feed: PostsFeedModel
+  isPageFocused: boolean
+  renderEmptyState?: () => JSX.Element
+}) {
+  const store = useStores()
+  const {isMobile} = useWebMediaQueries()
+  const [onMainScroll, isScrolledDown, resetMainScroll] = useOnMainScroll(store)
+  const {screen, track} = useAnalytics()
+  const [headerOffset, setHeaderOffset] = React.useState(
+    isMobile ? HEADER_OFFSET_MOBILE : HEADER_OFFSET_DESKTOP,
+  )
+  const scrollElRef = React.useRef<FlatList>(null)
+  const {appState} = useAppState({
+    onForeground: () => doPoll(true),
+  })
+  const isScreenFocused = useIsFocused()
 
-    React.useEffect(() => {
-      // called on first load
-      if (!feed.hasLoaded && isPageFocused) {
-        feed.setup()
-      }
-    }, [isPageFocused, feed])
+  React.useEffect(() => {
+    // called on first load
+    if (!feed.hasLoaded && isPageFocused) {
+      feed.setup()
+    }
+  }, [isPageFocused, feed])
 
-    const doPoll = React.useCallback(
-      (knownActive = false) => {
-        if (
-          (!knownActive && appState !== 'active') ||
-          !isScreenFocused ||
-          !isPageFocused
-        ) {
-          return
-        }
-        if (feed.isLoading) {
-          return
-        }
-        store.log.debug('HomeScreen: Polling for new posts')
-        feed.checkForLatest()
-      },
-      [appState, isScreenFocused, isPageFocused, store, feed],
-    )
+  const doPoll = React.useCallback(
+    (knownActive = false) => {
+      if (
+        (!knownActive && appState !== 'active') ||
+        !isScreenFocused ||
+        !isPageFocused
+      ) {
+        return
+      }
+      if (feed.isLoading) {
+        return
+      }
+      store.log.debug('HomeScreen: Polling for new posts')
+      feed.checkForLatest()
+    },
+    [appState, isScreenFocused, isPageFocused, store, feed],
+  )
 
-    const scrollToTop = React.useCallback(() => {
-      scrollElRef.current?.scrollToOffset({offset: -headerOffset})
-      resetMainScroll()
-    }, [headerOffset, resetMainScroll])
+  const scrollToTop = React.useCallback(() => {
+    scrollElRef.current?.scrollToOffset({offset: -headerOffset})
+    resetMainScroll()
+  }, [headerOffset, resetMainScroll])
 
-    const onSoftReset = React.useCallback(() => {
-      if (isPageFocused) {
-        scrollToTop()
-        feed.refresh()
-      }
-    }, [isPageFocused, scrollToTop, feed])
+  const onSoftReset = React.useCallback(() => {
+    if (isPageFocused) {
+      scrollToTop()
+      feed.refresh()
+    }
+  }, [isPageFocused, scrollToTop, feed])
 
-    // listens for resize events
-    React.useEffect(() => {
-      setHeaderOffset(isMobile ? HEADER_OFFSET_MOBILE : HEADER_OFFSET_DESKTOP)
-    }, [isMobile])
+  // listens for resize events
+  React.useEffect(() => {
+    setHeaderOffset(isMobile ? HEADER_OFFSET_MOBILE : HEADER_OFFSET_DESKTOP)
+  }, [isMobile])
 
-    // fires when page within screen is activated/deactivated
-    // - check for latest
-    React.useEffect(() => {
-      if (!isPageFocused || !isScreenFocused) {
-        return
-      }
+  // fires when page within screen is activated/deactivated
+  // - check for latest
+  React.useEffect(() => {
+    if (!isPageFocused || !isScreenFocused) {
+      return
+    }
 
-      const softResetSub = store.onScreenSoftReset(onSoftReset)
-      const feedCleanup = feed.registerListeners()
-      const pollInterval = setInterval(doPoll, POLL_FREQ)
+    const softResetSub = store.onScreenSoftReset(onSoftReset)
+    const feedCleanup = feed.registerListeners()
+    const pollInterval = setInterval(doPoll, POLL_FREQ)
 
-      screen('Feed')
-      store.log.debug('HomeScreen: Updating feed')
-      feed.checkForLatest()
-      if (feed.hasContent) {
-        feed.update()
-      }
+    screen('Feed')
+    store.log.debug('HomeScreen: Updating feed')
+    feed.checkForLatest()
+    if (feed.hasContent) {
+      feed.update()
+    }
 
-      return () => {
-        clearInterval(pollInterval)
-        softResetSub.remove()
-        feedCleanup()
-      }
-    }, [
-      store,
-      doPoll,
-      onSoftReset,
-      screen,
-      feed,
-      isPageFocused,
-      isScreenFocused,
-    ])
+    return () => {
+      clearInterval(pollInterval)
+      softResetSub.remove()
+      feedCleanup()
+    }
+  }, [store, doPoll, onSoftReset, screen, feed, isPageFocused, isScreenFocused])
 
-    const onPressCompose = React.useCallback(() => {
-      track('HomeScreen:PressCompose')
-      store.shell.openComposer({})
-    }, [store, track])
+  const onPressCompose = React.useCallback(() => {
+    track('HomeScreen:PressCompose')
+    store.shell.openComposer({})
+  }, [store, track])
 
-    const onPressTryAgain = React.useCallback(() => {
-      feed.refresh()
-    }, [feed])
+  const onPressTryAgain = React.useCallback(() => {
+    feed.refresh()
+  }, [feed])
 
-    const onPressLoadLatest = React.useCallback(() => {
-      scrollToTop()
-      feed.refresh()
-    }, [feed, scrollToTop])
+  const onPressLoadLatest = React.useCallback(() => {
+    scrollToTop()
+    feed.refresh()
+  }, [feed, scrollToTop])
 
-    const hasNew = feed.hasNewLatest && !feed.isRefreshing
-    return (
-      <View testID={testID} style={s.h100pct}>
-        <Feed
-          testID={testID ? `${testID}-feed` : undefined}
-          key="default"
-          feed={feed}
-          scrollElRef={scrollElRef}
-          onPressTryAgain={onPressTryAgain}
-          onScroll={onMainScroll}
-          scrollEventThrottle={100}
-          renderEmptyState={renderEmptyState}
-          headerOffset={headerOffset}
+  const hasNew = feed.hasNewLatest && !feed.isRefreshing
+  return (
+    <View testID={testID} style={s.h100pct}>
+      <Feed
+        testID={testID ? `${testID}-feed` : undefined}
+        key="default"
+        feed={feed}
+        scrollElRef={scrollElRef}
+        onPressTryAgain={onPressTryAgain}
+        onScroll={onMainScroll}
+        scrollEventThrottle={100}
+        renderEmptyState={renderEmptyState}
+        headerOffset={headerOffset}
+      />
+      {(isScrolledDown || hasNew) && (
+        <LoadLatestBtn
+          onPress={onPressLoadLatest}
+          label="Load new posts"
+          showIndicator={hasNew}
+          minimalShellMode={store.shell.minimalShellMode}
         />
-        {(isScrolledDown || hasNew) && (
-          <LoadLatestBtn
-            onPress={onPressLoadLatest}
-            label="Load new posts"
-            showIndicator={hasNew}
-            minimalShellMode={store.shell.minimalShellMode}
-          />
-        )}
-        <FAB
-          testID="composeFAB"
-          onPress={onPressCompose}
-          icon={<ComposeIcon2 strokeWidth={1.5} size={29} style={s.white} />}
-          accessibilityRole="button"
-          accessibilityLabel="New post"
-          accessibilityHint=""
-        />
-      </View>
-    )
-  },
-)
+      )}
+      <FAB
+        testID="composeFAB"
+        onPress={onPressCompose}
+        icon={<ComposeIcon2 strokeWidth={1.5} size={29} style={s.white} />}
+        accessibilityRole="button"
+        accessibilityLabel="New post"
+        accessibilityHint=""
+      />
+    </View>
+  )
+})
diff --git a/src/view/screens/ModerationBlockedAccounts.tsx b/src/view/screens/ModerationBlockedAccounts.tsx
index 10fa87080..7bbb6beee 100644
--- a/src/view/screens/ModerationBlockedAccounts.tsx
+++ b/src/view/screens/ModerationBlockedAccounts.tsx
@@ -27,7 +27,7 @@ type Props = NativeStackScreenProps<
   'ModerationBlockedAccounts'
 >
 export const ModerationBlockedAccounts = withAuthRequired(
-  observer(({}: Props) => {
+  observer(function ModerationBlockedAccountsImpl({}: Props) {
     const pal = usePalette('default')
     const store = useStores()
     const {isTabletOrDesktop} = useWebMediaQueries()
@@ -116,6 +116,8 @@ export const ModerationBlockedAccounts = withAuthRequired(
             onEndReached={onEndReached}
             renderItem={renderItem}
             initialNumToRender={15}
+            // FIXME(dan)
+            // eslint-disable-next-line react/no-unstable-nested-components
             ListFooterComponent={() => (
               <View style={styles.footer}>
                 {blockedAccounts.isLoading && <ActivityIndicator />}
diff --git a/src/view/screens/ModerationMutedAccounts.tsx b/src/view/screens/ModerationMutedAccounts.tsx
index eb822270a..31c46e640 100644
--- a/src/view/screens/ModerationMutedAccounts.tsx
+++ b/src/view/screens/ModerationMutedAccounts.tsx
@@ -27,7 +27,7 @@ type Props = NativeStackScreenProps<
   'ModerationMutedAccounts'
 >
 export const ModerationMutedAccounts = withAuthRequired(
-  observer(({}: Props) => {
+  observer(function ModerationMutedAccountsImpl({}: Props) {
     const pal = usePalette('default')
     const store = useStores()
     const {isTabletOrDesktop} = useWebMediaQueries()
@@ -112,6 +112,8 @@ export const ModerationMutedAccounts = withAuthRequired(
             onEndReached={onEndReached}
             renderItem={renderItem}
             initialNumToRender={15}
+            // FIXME(dan)
+            // eslint-disable-next-line react/no-unstable-nested-components
             ListFooterComponent={() => (
               <View style={styles.footer}>
                 {mutedAccounts.isLoading && <ActivityIndicator />}
diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx
index 5dda965db..058f09034 100644
--- a/src/view/screens/Notifications.tsx
+++ b/src/view/screens/Notifications.tsx
@@ -24,7 +24,7 @@ type Props = NativeStackScreenProps<
   'Notifications'
 >
 export const NotificationsScreen = withAuthRequired(
-  observer(({}: Props) => {
+  observer(function NotificationsScreenImpl({}: Props) {
     const store = useStores()
     const [onMainScroll, isScrolledDown, resetMainScroll] =
       useOnMainScroll(store)
diff --git a/src/view/screens/PreferencesHomeFeed.tsx b/src/view/screens/PreferencesHomeFeed.tsx
index bd6dd8b39..49c13bfa3 100644
--- a/src/view/screens/PreferencesHomeFeed.tsx
+++ b/src/view/screens/PreferencesHomeFeed.tsx
@@ -48,7 +48,9 @@ type Props = NativeStackScreenProps<
   CommonNavigatorParams,
   'PreferencesHomeFeed'
 >
-export const PreferencesHomeFeed = observer(({navigation}: Props) => {
+export const PreferencesHomeFeed = observer(function PreferencesHomeFeedImpl({
+  navigation,
+}: Props) {
   const pal = usePalette('default')
   const store = useStores()
   const {isTabletOrDesktop} = useWebMediaQueries()
diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx
index a78650b3f..69b5ceee6 100644
--- a/src/view/screens/Profile.tsx
+++ b/src/view/screens/Profile.tsx
@@ -32,7 +32,7 @@ import {combinedDisplayName} from 'lib/strings/display-names'
 
 type Props = NativeStackScreenProps<CommonNavigatorParams, 'Profile'>
 export const ProfileScreen = withAuthRequired(
-  observer(({route}: Props) => {
+  observer(function ProfileScreenImpl({route}: Props) {
     const store = useStores()
     const {screen, track} = useAnalytics()
     const viewSelectorRef = React.useRef<ViewSelectorHandle>(null)
diff --git a/src/view/screens/ProfileList.tsx b/src/view/screens/ProfileList.tsx
index e86a457b6..322f99486 100644
--- a/src/view/screens/ProfileList.tsx
+++ b/src/view/screens/ProfileList.tsx
@@ -23,7 +23,7 @@ import {s} from 'lib/styles'
 
 type Props = NativeStackScreenProps<CommonNavigatorParams, 'ProfileList'>
 export const ProfileListScreen = withAuthRequired(
-  observer(({route}: Props) => {
+  observer(function ProfileListScreenImpl({route}: Props) {
     const store = useStores()
     const navigation = useNavigation<NavigationProp>()
     const {isTabletOrDesktop} = useWebMediaQueries()
diff --git a/src/view/screens/SavedFeeds.tsx b/src/view/screens/SavedFeeds.tsx
index dc9c253cb..d5c02ba63 100644
--- a/src/view/screens/SavedFeeds.tsx
+++ b/src/view/screens/SavedFeeds.tsx
@@ -35,7 +35,7 @@ import {Link, TextLink} from 'view/com/util/Link'
 type Props = NativeStackScreenProps<CommonNavigatorParams, 'SavedFeeds'>
 
 export const SavedFeeds = withAuthRequired(
-  observer(({}: Props) => {
+  observer(function SavedFeedsImpl({}: Props) {
     const pal = usePalette('default')
     const store = useStores()
     const {isMobile, isTabletOrDesktop} = useWebMediaQueries()
@@ -151,96 +151,98 @@ export const SavedFeeds = withAuthRequired(
   }),
 )
 
-const ListItem = observer(
-  ({item, drag}: {item: CustomFeedModel; drag: () => void}) => {
-    const pal = usePalette('default')
-    const store = useStores()
-    const savedFeeds = useMemo(() => store.me.savedFeeds, [store])
-    const isPinned = savedFeeds.isPinned(item)
+const ListItem = observer(function ListItemImpl({
+  item,
+  drag,
+}: {
+  item: CustomFeedModel
+  drag: () => void
+}) {
+  const pal = usePalette('default')
+  const store = useStores()
+  const savedFeeds = useMemo(() => store.me.savedFeeds, [store])
+  const isPinned = savedFeeds.isPinned(item)
 
-    const onTogglePinned = useCallback(() => {
-      Haptics.default()
-      savedFeeds.togglePinnedFeed(item).catch(e => {
+  const onTogglePinned = useCallback(() => {
+    Haptics.default()
+    savedFeeds.togglePinnedFeed(item).catch(e => {
+      Toast.show('There was an issue contacting the server')
+      store.log.error('Failed to toggle pinned feed', {e})
+    })
+  }, [savedFeeds, item, store])
+  const onPressUp = useCallback(
+    () =>
+      savedFeeds.movePinnedFeed(item, 'up').catch(e => {
         Toast.show('There was an issue contacting the server')
-        store.log.error('Failed to toggle pinned feed', {e})
-      })
-    }, [savedFeeds, item, store])
-    const onPressUp = useCallback(
-      () =>
-        savedFeeds.movePinnedFeed(item, 'up').catch(e => {
-          Toast.show('There was an issue contacting the server')
-          store.log.error('Failed to set pinned feed order', {e})
-        }),
-      [store, savedFeeds, item],
-    )
-    const onPressDown = useCallback(
-      () =>
-        savedFeeds.movePinnedFeed(item, 'down').catch(e => {
-          Toast.show('There was an issue contacting the server')
-          store.log.error('Failed to set pinned feed order', {e})
-        }),
-      [store, savedFeeds, item],
-    )
+        store.log.error('Failed to set pinned feed order', {e})
+      }),
+    [store, savedFeeds, item],
+  )
+  const onPressDown = useCallback(
+    () =>
+      savedFeeds.movePinnedFeed(item, 'down').catch(e => {
+        Toast.show('There was an issue contacting the server')
+        store.log.error('Failed to set pinned feed order', {e})
+      }),
+    [store, savedFeeds, item],
+  )
 
-    return (
-      <ScaleDecorator>
-        <ShadowDecorator>
-          <Pressable
+  return (
+    <ScaleDecorator>
+      <ShadowDecorator>
+        <Pressable
+          accessibilityRole="button"
+          onLongPress={isPinned ? drag : undefined}
+          delayLongPress={200}
+          style={[styles.itemContainer, pal.border]}>
+          {isPinned && isWeb ? (
+            <View style={styles.webArrowButtonsContainer}>
+              <TouchableOpacity accessibilityRole="button" onPress={onPressUp}>
+                <FontAwesomeIcon
+                  icon="arrow-up"
+                  size={12}
+                  style={[pal.text, styles.webArrowUpButton]}
+                />
+              </TouchableOpacity>
+              <TouchableOpacity
+                accessibilityRole="button"
+                onPress={onPressDown}>
+                <FontAwesomeIcon
+                  icon="arrow-down"
+                  size={12}
+                  style={[pal.text]}
+                />
+              </TouchableOpacity>
+            </View>
+          ) : isPinned ? (
+            <FontAwesomeIcon
+              icon="bars"
+              size={20}
+              color={pal.colors.text}
+              style={s.ml20}
+            />
+          ) : null}
+          <CustomFeed
+            key={item.data.uri}
+            item={item}
+            showSaveBtn
+            style={styles.noBorder}
+          />
+          <TouchableOpacity
             accessibilityRole="button"
-            onLongPress={isPinned ? drag : undefined}
-            delayLongPress={200}
-            style={[styles.itemContainer, pal.border]}>
-            {isPinned && isWeb ? (
-              <View style={styles.webArrowButtonsContainer}>
-                <TouchableOpacity
-                  accessibilityRole="button"
-                  onPress={onPressUp}>
-                  <FontAwesomeIcon
-                    icon="arrow-up"
-                    size={12}
-                    style={[pal.text, styles.webArrowUpButton]}
-                  />
-                </TouchableOpacity>
-                <TouchableOpacity
-                  accessibilityRole="button"
-                  onPress={onPressDown}>
-                  <FontAwesomeIcon
-                    icon="arrow-down"
-                    size={12}
-                    style={[pal.text]}
-                  />
-                </TouchableOpacity>
-              </View>
-            ) : isPinned ? (
-              <FontAwesomeIcon
-                icon="bars"
-                size={20}
-                color={pal.colors.text}
-                style={s.ml20}
-              />
-            ) : null}
-            <CustomFeed
-              key={item.data.uri}
-              item={item}
-              showSaveBtn
-              style={styles.noBorder}
+            hitSlop={10}
+            onPress={onTogglePinned}>
+            <FontAwesomeIcon
+              icon="thumb-tack"
+              size={20}
+              color={isPinned ? colors.blue3 : pal.colors.icon}
             />
-            <TouchableOpacity
-              accessibilityRole="button"
-              hitSlop={10}
-              onPress={onTogglePinned}>
-              <FontAwesomeIcon
-                icon="thumb-tack"
-                size={20}
-                color={isPinned ? colors.blue3 : pal.colors.icon}
-              />
-            </TouchableOpacity>
-          </Pressable>
-        </ShadowDecorator>
-      </ScaleDecorator>
-    )
-  },
-)
+          </TouchableOpacity>
+        </Pressable>
+      </ShadowDecorator>
+    </ScaleDecorator>
+  )
+})
 
 const styles = StyleSheet.create({
   desktopContainer: {
diff --git a/src/view/screens/Search.web.tsx b/src/view/screens/Search.web.tsx
index f325b1233..2d0c0288a 100644
--- a/src/view/screens/Search.web.tsx
+++ b/src/view/screens/Search.web.tsx
@@ -18,7 +18,7 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
 
 type Props = NativeStackScreenProps<SearchTabNavigatorParams, 'Search'>
 export const SearchScreen = withAuthRequired(
-  observer(({navigation, route}: Props) => {
+  observer(function SearchScreenImpl({navigation, route}: Props) {
     const store = useStores()
     const params = route.params || {}
     const foafs = React.useMemo<FoafsModel>(
diff --git a/src/view/screens/SearchMobile.tsx b/src/view/screens/SearchMobile.tsx
index 6ad468fa9..b545a643d 100644
--- a/src/view/screens/SearchMobile.tsx
+++ b/src/view/screens/SearchMobile.tsx
@@ -30,7 +30,7 @@ import {isAndroid, isIOS} from 'platform/detection'
 
 type Props = NativeStackScreenProps<SearchTabNavigatorParams, 'Search'>
 export const SearchScreen = withAuthRequired(
-  observer<Props>(({}: Props) => {
+  observer<Props>(function SearchScreenImpl({}: Props) {
     const pal = usePalette('default')
     const store = useStores()
     const scrollViewRef = React.useRef<ScrollView>(null)
diff --git a/src/view/shell/Composer.tsx b/src/view/shell/Composer.tsx
index ac155887c..d7c6a80b7 100644
--- a/src/view/shell/Composer.tsx
+++ b/src/view/shell/Composer.tsx
@@ -6,73 +6,71 @@ import {ComposerOpts} from 'state/models/ui/shell'
 import {useAnimatedValue} from 'lib/hooks/useAnimatedValue'
 import {usePalette} from 'lib/hooks/usePalette'
 
-export const Composer = observer(
-  ({
-    active,
-    winHeight,
-    replyTo,
-    onPost,
-    onClose,
-    quote,
-    mention,
-  }: {
-    active: boolean
-    winHeight: number
-    replyTo?: ComposerOpts['replyTo']
-    onPost?: ComposerOpts['onPost']
-    onClose: () => void
-    quote?: ComposerOpts['quote']
-    mention?: ComposerOpts['mention']
-  }) => {
-    const pal = usePalette('default')
-    const initInterp = useAnimatedValue(0)
+export const Composer = observer(function ComposerImpl({
+  active,
+  winHeight,
+  replyTo,
+  onPost,
+  onClose,
+  quote,
+  mention,
+}: {
+  active: boolean
+  winHeight: number
+  replyTo?: ComposerOpts['replyTo']
+  onPost?: ComposerOpts['onPost']
+  onClose: () => void
+  quote?: ComposerOpts['quote']
+  mention?: ComposerOpts['mention']
+}) {
+  const pal = usePalette('default')
+  const initInterp = useAnimatedValue(0)
 
-    useEffect(() => {
-      if (active) {
-        Animated.timing(initInterp, {
-          toValue: 1,
-          duration: 300,
-          easing: Easing.out(Easing.exp),
-          useNativeDriver: true,
-        }).start()
-      } else {
-        initInterp.setValue(0)
-      }
-    }, [initInterp, active])
-    const wrapperAnimStyle = {
-      transform: [
-        {
-          translateY: initInterp.interpolate({
-            inputRange: [0, 1],
-            outputRange: [winHeight, 0],
-          }),
-        },
-      ],
+  useEffect(() => {
+    if (active) {
+      Animated.timing(initInterp, {
+        toValue: 1,
+        duration: 300,
+        easing: Easing.out(Easing.exp),
+        useNativeDriver: true,
+      }).start()
+    } else {
+      initInterp.setValue(0)
     }
+  }, [initInterp, active])
+  const wrapperAnimStyle = {
+    transform: [
+      {
+        translateY: initInterp.interpolate({
+          inputRange: [0, 1],
+          outputRange: [winHeight, 0],
+        }),
+      },
+    ],
+  }
 
-    // rendering
-    // =
+  // rendering
+  // =
 
-    if (!active) {
-      return <View />
-    }
+  if (!active) {
+    return <View />
+  }
 
-    return (
-      <Animated.View
-        style={[styles.wrapper, pal.view, wrapperAnimStyle]}
-        aria-modal
-        accessibilityViewIsModal>
-        <ComposePost
-          replyTo={replyTo}
-          onPost={onPost}
-          onClose={onClose}
-          quote={quote}
-          mention={mention}
-        />
-      </Animated.View>
-    )
-  },
-)
+  return (
+    <Animated.View
+      style={[styles.wrapper, pal.view, wrapperAnimStyle]}
+      aria-modal
+      accessibilityViewIsModal>
+      <ComposePost
+        replyTo={replyTo}
+        onPost={onPost}
+        onClose={onClose}
+        quote={quote}
+        mention={mention}
+      />
+    </Animated.View>
+  )
+})
 
 const styles = StyleSheet.create({
   wrapper: {
diff --git a/src/view/shell/Composer.web.tsx b/src/view/shell/Composer.web.tsx
index b32ba90c4..f4b2d9a4c 100644
--- a/src/view/shell/Composer.web.tsx
+++ b/src/view/shell/Composer.web.tsx
@@ -8,54 +8,52 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
 
 const BOTTOM_BAR_HEIGHT = 61
 
-export const Composer = observer(
-  ({
-    active,
-    replyTo,
-    quote,
-    onPost,
-    onClose,
-    mention,
-  }: {
-    active: boolean
-    winHeight: number
-    replyTo?: ComposerOpts['replyTo']
-    quote: ComposerOpts['quote']
-    onPost?: ComposerOpts['onPost']
-    onClose: () => void
-    mention?: ComposerOpts['mention']
-  }) => {
-    const pal = usePalette('default')
-    const {isMobile} = useWebMediaQueries()
+export const Composer = observer(function ComposerImpl({
+  active,
+  replyTo,
+  quote,
+  onPost,
+  onClose,
+  mention,
+}: {
+  active: boolean
+  winHeight: number
+  replyTo?: ComposerOpts['replyTo']
+  quote: ComposerOpts['quote']
+  onPost?: ComposerOpts['onPost']
+  onClose: () => void
+  mention?: ComposerOpts['mention']
+}) {
+  const pal = usePalette('default')
+  const {isMobile} = useWebMediaQueries()
 
-    // rendering
-    // =
+  // rendering
+  // =
 
-    if (!active) {
-      return <View />
-    }
+  if (!active) {
+    return <View />
+  }
 
-    return (
-      <View style={styles.mask} aria-modal accessibilityViewIsModal>
-        <View
-          style={[
-            styles.container,
-            isMobile && styles.containerMobile,
-            pal.view,
-            pal.border,
-          ]}>
-          <ComposePost
-            replyTo={replyTo}
-            quote={quote}
-            onPost={onPost}
-            onClose={onClose}
-            mention={mention}
-          />
-        </View>
+  return (
+    <View style={styles.mask} aria-modal accessibilityViewIsModal>
+      <View
+        style={[
+          styles.container,
+          isMobile && styles.containerMobile,
+          pal.view,
+          pal.border,
+        ]}>
+        <ComposePost
+          replyTo={replyTo}
+          quote={quote}
+          onPost={onPost}
+          onClose={onClose}
+          mention={mention}
+        />
       </View>
-    )
-  },
-)
+    </View>
+  )
+})
 
 const styles = StyleSheet.create({
   mask: {
diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx
index 0428e54c3..3379d0501 100644
--- a/src/view/shell/Drawer.tsx
+++ b/src/view/shell/Drawer.tsx
@@ -44,7 +44,7 @@ import {useNavigationTabState} from 'lib/hooks/useNavigationTabState'
 import {isWeb} from 'platform/detection'
 import {formatCount, formatCountShortOnly} from 'view/com/util/numeric/format'
 
-export const DrawerContent = observer(() => {
+export const DrawerContent = observer(function DrawerContentImpl() {
   const theme = useTheme()
   const pal = usePalette('default')
   const store = useStores()
@@ -400,7 +400,7 @@ function MenuItem({
   )
 }
 
-const InviteCodes = observer(() => {
+const InviteCodes = observer(function InviteCodesImpl() {
   const {track} = useAnalytics()
   const store = useStores()
   const pal = usePalette('default')
diff --git a/src/view/shell/bottom-bar/BottomBar.tsx b/src/view/shell/bottom-bar/BottomBar.tsx
index 60a6c8e67..4a34371ea 100644
--- a/src/view/shell/bottom-bar/BottomBar.tsx
+++ b/src/view/shell/bottom-bar/BottomBar.tsx
@@ -32,7 +32,9 @@ import {UserAvatar} from 'view/com/util/UserAvatar'
 
 type TabOptions = 'Home' | 'Search' | 'Notifications' | 'MyProfile' | 'Feeds'
 
-export const BottomBar = observer(({navigation}: BottomTabBarProps) => {
+export const BottomBar = observer(function BottomBarImpl({
+  navigation,
+}: BottomTabBarProps) {
   const store = useStores()
   const pal = usePalette('default')
   const safeAreaInsets = useSafeAreaInsets()
diff --git a/src/view/shell/bottom-bar/BottomBarWeb.tsx b/src/view/shell/bottom-bar/BottomBarWeb.tsx
index 50cfa0570..ee575c217 100644
--- a/src/view/shell/bottom-bar/BottomBarWeb.tsx
+++ b/src/view/shell/bottom-bar/BottomBarWeb.tsx
@@ -23,7 +23,7 @@ import {Link} from 'view/com/util/Link'
 import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
 import {makeProfileLink} from 'lib/routes/links'
 
-export const BottomBarWeb = observer(() => {
+export const BottomBarWeb = observer(function BottomBarWebImpl() {
   const store = useStores()
   const pal = usePalette('default')
   const safeAreaInsets = useSafeAreaInsets()
diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx
index 6df121fae..852731950 100644
--- a/src/view/shell/desktop/LeftNav.tsx
+++ b/src/view/shell/desktop/LeftNav.tsx
@@ -40,7 +40,7 @@ import {NavigationProp, CommonNavigatorParams} from 'lib/routes/types'
 import {router} from '../../../routes'
 import {makeProfileLink} from 'lib/routes/links'
 
-const ProfileCard = observer(() => {
+const ProfileCard = observer(function ProfileCardImpl() {
   const store = useStores()
   const {isDesktop} = useWebMediaQueries()
   const size = isDesktop ? 64 : 48
@@ -103,78 +103,82 @@ interface NavItemProps {
   iconFilled: JSX.Element
   label: string
 }
-const NavItem = observer(
-  ({count, href, icon, iconFilled, label}: NavItemProps) => {
-    const pal = usePalette('default')
-    const store = useStores()
-    const {isDesktop, isTablet} = useWebMediaQueries()
-    const [pathName] = React.useMemo(() => router.matchPath(href), [href])
-    const currentRouteInfo = useNavigationState(state => {
-      if (!state) {
-        return {name: 'Home'}
+const NavItem = observer(function NavItemImpl({
+  count,
+  href,
+  icon,
+  iconFilled,
+  label,
+}: NavItemProps) {
+  const pal = usePalette('default')
+  const store = useStores()
+  const {isDesktop, isTablet} = useWebMediaQueries()
+  const [pathName] = React.useMemo(() => router.matchPath(href), [href])
+  const currentRouteInfo = useNavigationState(state => {
+    if (!state) {
+      return {name: 'Home'}
+    }
+    return getCurrentRoute(state)
+  })
+  let isCurrent =
+    currentRouteInfo.name === 'Profile'
+      ? isTab(currentRouteInfo.name, pathName) &&
+        (currentRouteInfo.params as CommonNavigatorParams['Profile']).name ===
+          store.me.handle
+      : isTab(currentRouteInfo.name, pathName)
+  const {onPress} = useLinkProps({to: href})
+  const onPressWrapped = React.useCallback(
+    (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
+      if (e.ctrlKey || e.metaKey || e.altKey) {
+        return
       }
-      return getCurrentRoute(state)
-    })
-    let isCurrent =
-      currentRouteInfo.name === 'Profile'
-        ? isTab(currentRouteInfo.name, pathName) &&
-          (currentRouteInfo.params as CommonNavigatorParams['Profile']).name ===
-            store.me.handle
-        : isTab(currentRouteInfo.name, pathName)
-    const {onPress} = useLinkProps({to: href})
-    const onPressWrapped = React.useCallback(
-      (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
-        if (e.ctrlKey || e.metaKey || e.altKey) {
-          return
-        }
-        e.preventDefault()
-        if (isCurrent) {
-          store.emitScreenSoftReset()
-        } else {
-          onPress()
-        }
-      },
-      [onPress, isCurrent, store],
-    )
+      e.preventDefault()
+      if (isCurrent) {
+        store.emitScreenSoftReset()
+      } else {
+        onPress()
+      }
+    },
+    [onPress, isCurrent, store],
+  )
 
-    return (
-      <PressableWithHover
-        style={styles.navItemWrapper}
-        hoverStyle={pal.viewLight}
-        // @ts-ignore the function signature differs on web -prf
-        onPress={onPressWrapped}
-        // @ts-ignore web only -prf
-        href={href}
-        dataSet={{noUnderline: 1}}
-        accessibilityRole="tab"
-        accessibilityLabel={label}
-        accessibilityHint="">
-        <View
-          style={[
-            styles.navItemIconWrapper,
-            isTablet && styles.navItemIconWrapperTablet,
-          ]}>
-          {isCurrent ? iconFilled : icon}
-          {typeof count === 'string' && count ? (
-            <Text
-              type="button"
-              style={[
-                styles.navItemCount,
-                isTablet && styles.navItemCountTablet,
-              ]}>
-              {count}
-            </Text>
-          ) : null}
-        </View>
-        {isDesktop && (
-          <Text type="title" style={[isCurrent ? s.bold : s.normal, pal.text]}>
-            {label}
+  return (
+    <PressableWithHover
+      style={styles.navItemWrapper}
+      hoverStyle={pal.viewLight}
+      // @ts-ignore the function signature differs on web -prf
+      onPress={onPressWrapped}
+      // @ts-ignore web only -prf
+      href={href}
+      dataSet={{noUnderline: 1}}
+      accessibilityRole="tab"
+      accessibilityLabel={label}
+      accessibilityHint="">
+      <View
+        style={[
+          styles.navItemIconWrapper,
+          isTablet && styles.navItemIconWrapperTablet,
+        ]}>
+        {isCurrent ? iconFilled : icon}
+        {typeof count === 'string' && count ? (
+          <Text
+            type="button"
+            style={[
+              styles.navItemCount,
+              isTablet && styles.navItemCountTablet,
+            ]}>
+            {count}
           </Text>
-        )}
-      </PressableWithHover>
-    )
-  },
-)
+        ) : null}
+      </View>
+      {isDesktop && (
+        <Text type="title" style={[isCurrent ? s.bold : s.normal, pal.text]}>
+          {label}
+        </Text>
+      )}
+    </PressableWithHover>
+  )
+})
 
 function ComposeBtn() {
   const store = useStores()
diff --git a/src/view/shell/desktop/RightNav.tsx b/src/view/shell/desktop/RightNav.tsx
index 797058d6c..e17fa6a84 100644
--- a/src/view/shell/desktop/RightNav.tsx
+++ b/src/view/shell/desktop/RightNav.tsx
@@ -13,7 +13,7 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
 import {pluralize} from 'lib/strings/helpers'
 import {formatCount} from 'view/com/util/numeric/format'
 
-export const DesktopRightNav = observer(function DesktopRightNav() {
+export const DesktopRightNav = observer(function DesktopRightNavImpl() {
   const store = useStores()
   const pal = usePalette('default')
   const palError = usePalette('error')
@@ -78,7 +78,7 @@ export const DesktopRightNav = observer(function DesktopRightNav() {
   )
 })
 
-const InviteCodes = observer(() => {
+const InviteCodes = observer(function InviteCodesImpl() {
   const store = useStores()
   const pal = usePalette('default')
 
diff --git a/src/view/shell/index.tsx b/src/view/shell/index.tsx
index c5080e866..e313450f1 100644
--- a/src/view/shell/index.tsx
+++ b/src/view/shell/index.tsx
@@ -24,7 +24,7 @@ import {isStateAtTabRoot} from 'lib/routes/helpers'
 import {SafeAreaProvider} from 'react-native-safe-area-context'
 import {useOTAUpdate} from 'lib/hooks/useOTAUpdate'
 
-const ShellInner = observer(() => {
+const ShellInner = observer(function ShellInnerImpl() {
   const store = useStores()
   useOTAUpdate() // this hook polls for OTA updates every few seconds
   const winDim = useWindowDimensions()
@@ -81,7 +81,7 @@ const ShellInner = observer(() => {
   )
 })
 
-export const Shell: React.FC = observer(() => {
+export const Shell: React.FC = observer(function ShellImpl() {
   const pal = usePalette('default')
   const theme = useTheme()
   return (
diff --git a/src/view/shell/index.web.tsx b/src/view/shell/index.web.tsx
index 6182f1ba4..124341917 100644
--- a/src/view/shell/index.web.tsx
+++ b/src/view/shell/index.web.tsx
@@ -17,7 +17,7 @@ import {BottomBarWeb} from './bottom-bar/BottomBarWeb'
 import {useNavigation} from '@react-navigation/native'
 import {NavigationProp} from 'lib/routes/types'
 
-const ShellInner = observer(() => {
+const ShellInner = observer(function ShellInnerImpl() {
   const store = useStores()
   const {isDesktop, isMobile} = useWebMediaQueries()
   const navigator = useNavigation<NavigationProp>()
@@ -71,7 +71,7 @@ const ShellInner = observer(() => {
   )
 })
 
-export const Shell: React.FC = observer(() => {
+export const Shell: React.FC = observer(function ShellImpl() {
   const pageBg = useColorSchemeStyle(styles.bgLight, styles.bgDark)
   return (
     <View style={[s.hContentRegion, pageBg]}>