about summary refs log tree commit diff
path: root/src/view
diff options
context:
space:
mode:
authorPaul Frazee <pfrazee@gmail.com>2023-12-14 10:31:49 -0800
committerGitHub <noreply@github.com>2023-12-14 10:31:49 -0800
commit075ffdf583c5393b896d22dd179d03208311ef4a (patch)
treead86e90d2e2f0dd5a9e2f9f72292e5dcec6a59a0 /src/view
parent7fd79702371e3d7829be2188c2212c090bf76670 (diff)
downloadvoidsky-075ffdf583c5393b896d22dd179d03208311ef4a.tar.zst
PWI behavior updates (#2207)
* Enable PWI

* Disable access to feeds on PWI

* Remove feeds nav item from drawer when signed out

* Replace discover feed on home with a CTA

* Wire up the sign in and create account buttons to go straight to their respective screens

* Give a custom ScreenHider interface for no-pwi

* Add side borders on desktop to the screen hider

* Filter accounts in the autocomplete according to mod settings

* Trim replies in the post thread that are pwi opt-out

* Show 'learn more' on the content hider when no-override is enabled

* Apply the moderation filter on profile cards

* Disable post search on logged-out view

* Update locale files

* Bump api pkg

* Ensure feeds with no posts don't show as NSFPublic

* Fix types

---------

Co-authored-by: Eric Bailey <git@esb.lol>
Diffstat (limited to 'src/view')
-rw-r--r--src/view/com/auth/HomeLoggedOutCTA.tsx165
-rw-r--r--src/view/com/auth/LoggedOut.tsx4
-rw-r--r--src/view/com/post-thread/PostThread.tsx18
-rw-r--r--src/view/com/profile/ProfileCard.tsx3
-rw-r--r--src/view/com/util/moderation/ContentHider.tsx20
-rw-r--r--src/view/com/util/moderation/ScreenHider.tsx64
-rw-r--r--src/view/screens/Home.tsx8
-rw-r--r--src/view/screens/Profile.tsx2
-rw-r--r--src/view/screens/Search/Search.tsx99
-rw-r--r--src/view/shell/Drawer.tsx2
-rw-r--r--src/view/shell/NavSignupCard.tsx18
-rw-r--r--src/view/shell/bottom-bar/BottomBar.tsx44
-rw-r--r--src/view/shell/bottom-bar/BottomBarWeb.tsx22
-rw-r--r--src/view/shell/desktop/LeftNav.tsx37
14 files changed, 371 insertions, 135 deletions
diff --git a/src/view/com/auth/HomeLoggedOutCTA.tsx b/src/view/com/auth/HomeLoggedOutCTA.tsx
new file mode 100644
index 000000000..32b873ac6
--- /dev/null
+++ b/src/view/com/auth/HomeLoggedOutCTA.tsx
@@ -0,0 +1,165 @@
+import React from 'react'
+import {StyleSheet, TouchableOpacity, View} from 'react-native'
+import {useLingui} from '@lingui/react'
+import {Trans, msg} from '@lingui/macro'
+import {ScrollView} from '../util/Views'
+import {Text} from '../util/text/Text'
+import {usePalette} from '#/lib/hooks/usePalette'
+import {colors, s} from '#/lib/styles'
+import {TextLink} from '../util/Link'
+import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
+import {useLoggedOutViewControls} from '#/state/shell/logged-out'
+
+export function HomeLoggedOutCTA() {
+  const pal = usePalette('default')
+  const {_} = useLingui()
+  const {isMobile} = useWebMediaQueries()
+  const {requestSwitchToAccount} = useLoggedOutViewControls()
+
+  const showCreateAccount = React.useCallback(() => {
+    requestSwitchToAccount({requestedAccount: 'new'})
+  }, [requestSwitchToAccount])
+
+  const showSignIn = React.useCallback(() => {
+    requestSwitchToAccount({requestedAccount: 'none'})
+  }, [requestSwitchToAccount])
+
+  return (
+    <ScrollView style={styles.container} testID="loggedOutCTA">
+      <View style={[styles.hero, isMobile && styles.heroMobile]}>
+        <Text style={[styles.title, pal.link]}>
+          <Trans>Bluesky</Trans>
+        </Text>
+        <Text
+          style={[
+            styles.subtitle,
+            isMobile && styles.subtitleMobile,
+            pal.textLight,
+          ]}>
+          <Trans>See what's next</Trans>
+        </Text>
+      </View>
+      <View
+        testID="signinOrCreateAccount"
+        style={isMobile ? undefined : styles.btnsDesktop}>
+        <TouchableOpacity
+          testID="createAccountButton"
+          style={[
+            styles.btn,
+            isMobile && styles.btnMobile,
+            {backgroundColor: colors.blue3},
+          ]}
+          onPress={showCreateAccount}
+          accessibilityRole="button"
+          accessibilityLabel={_(msg`Create new account`)}
+          accessibilityHint="Opens flow to create a new Bluesky account">
+          <Text
+            style={[
+              s.white,
+              styles.btnLabel,
+              isMobile && styles.btnLabelMobile,
+            ]}>
+            <Trans>Create a new account</Trans>
+          </Text>
+        </TouchableOpacity>
+        <TouchableOpacity
+          testID="signInButton"
+          style={[styles.btn, isMobile && styles.btnMobile, pal.btn]}
+          onPress={showSignIn}
+          accessibilityRole="button"
+          accessibilityLabel={_(msg`Sign in`)}
+          accessibilityHint="Opens flow to sign into your existing Bluesky account">
+          <Text
+            style={[
+              pal.text,
+              styles.btnLabel,
+              isMobile && styles.btnLabelMobile,
+            ]}>
+            <Trans>Sign In</Trans>
+          </Text>
+        </TouchableOpacity>
+      </View>
+
+      <View style={[styles.footer, pal.view, pal.border]}>
+        <TextLink
+          type="2xl"
+          href="https://blueskyweb.xyz"
+          text={_(msg`Business`)}
+          style={[styles.footerLink, pal.link]}
+        />
+        <TextLink
+          type="2xl"
+          href="https://blueskyweb.xyz/blog"
+          text={_(msg`Blog`)}
+          style={[styles.footerLink, pal.link]}
+        />
+        <TextLink
+          type="2xl"
+          href="https://blueskyweb.xyz/join"
+          text={_(msg`Jobs`)}
+          style={[styles.footerLink, pal.link]}
+        />
+      </View>
+    </ScrollView>
+  )
+}
+
+const styles = StyleSheet.create({
+  container: {
+    height: '100%',
+  },
+  hero: {
+    justifyContent: 'center',
+    paddingTop: 100,
+    paddingBottom: 30,
+  },
+  heroMobile: {
+    paddingBottom: 50,
+  },
+  title: {
+    textAlign: 'center',
+    fontSize: 68,
+    fontWeight: 'bold',
+  },
+  subtitle: {
+    textAlign: 'center',
+    fontSize: 48,
+    fontWeight: 'bold',
+  },
+  subtitleMobile: {
+    fontSize: 42,
+  },
+  btnsDesktop: {
+    flexDirection: 'row',
+    justifyContent: 'center',
+    gap: 20,
+    marginHorizontal: 20,
+  },
+  btn: {
+    borderRadius: 32,
+    width: 230,
+    paddingVertical: 12,
+    marginBottom: 20,
+  },
+  btnMobile: {
+    flex: 1,
+    width: 'auto',
+    marginHorizontal: 20,
+    paddingVertical: 16,
+  },
+  btnLabel: {
+    textAlign: 'center',
+    fontSize: 18,
+  },
+  btnLabelMobile: {
+    textAlign: 'center',
+    fontSize: 21,
+  },
+
+  footer: {
+    flexDirection: 'row',
+    gap: 20,
+    justifyContent: 'center',
+  },
+  footerLink: {},
+})
diff --git a/src/view/com/auth/LoggedOut.tsx b/src/view/com/auth/LoggedOut.tsx
index fcff4f782..b0b2bf7ed 100644
--- a/src/view/com/auth/LoggedOut.tsx
+++ b/src/view/com/auth/LoggedOut.tsx
@@ -33,7 +33,9 @@ export function LoggedOut({onDismiss}: {onDismiss?: () => void}) {
   const {requestedAccountSwitchTo} = useLoggedOutView()
   const [screenState, setScreenState] = React.useState<ScreenState>(
     requestedAccountSwitchTo
-      ? ScreenState.S_Login
+      ? requestedAccountSwitchTo === 'new'
+        ? ScreenState.S_CreateAccount
+        : ScreenState.S_Login
       : ScreenState.S_LoginOrCreateAccount,
   )
   const {isMobile} = useWebMediaQueries()
diff --git a/src/view/com/post-thread/PostThread.tsx b/src/view/com/post-thread/PostThread.tsx
index f27da331f..917550884 100644
--- a/src/view/com/post-thread/PostThread.tsx
+++ b/src/view/com/post-thread/PostThread.tsx
@@ -157,7 +157,9 @@ function PostThreadLoaded({
   // construct content
   const posts = React.useMemo(() => {
     let arr = [TOP_COMPONENT].concat(
-      Array.from(flattenThreadSkeleton(sortThread(thread, threadViewPrefs))),
+      Array.from(
+        flattenThreadSkeleton(sortThread(thread, threadViewPrefs), hasSession),
+      ),
     )
     if (arr.length > maxVisible) {
       arr = arr.slice(0, maxVisible).concat([LOAD_MORE])
@@ -166,7 +168,7 @@ function PostThreadLoaded({
       arr.push(BOTTOM_COMPONENT)
     }
     return arr
-  }, [thread, maxVisible, threadViewPrefs])
+  }, [thread, maxVisible, threadViewPrefs, hasSession])
 
   /**
    * NOTE
@@ -468,20 +470,24 @@ function isThreadPost(v: unknown): v is ThreadPost {
 
 function* flattenThreadSkeleton(
   node: ThreadNode,
+  hasSession: boolean,
 ): Generator<YieldedItem, void> {
   if (node.type === 'post') {
     if (node.parent) {
-      yield* flattenThreadSkeleton(node.parent)
+      yield* flattenThreadSkeleton(node.parent, hasSession)
     } else if (node.ctx.isParentLoading) {
       yield PARENT_SPINNER
     }
+    if (!hasSession && node.ctx.depth > 0 && hasPwiOptOut(node)) {
+      return
+    }
     yield node
     if (node.ctx.isHighlightedPost && !node.post.viewer?.replyDisabled) {
       yield REPLY_PROMPT
     }
     if (node.replies?.length) {
       for (const reply of node.replies) {
-        yield* flattenThreadSkeleton(reply)
+        yield* flattenThreadSkeleton(reply, hasSession)
       }
     } else if (node.ctx.isChildLoading) {
       yield CHILD_SPINNER
@@ -493,6 +499,10 @@ function* flattenThreadSkeleton(
   }
 }
 
+function hasPwiOptOut(node: ThreadPost) {
+  return !!node.post.author.labels?.find(l => l.val === '!no-unauthenticated')
+}
+
 function hasBranchingReplies(node: ThreadNode) {
   if (node.type !== 'post') {
     return false
diff --git a/src/view/com/profile/ProfileCard.tsx b/src/view/com/profile/ProfileCard.tsx
index 21972f274..c5b2dc528 100644
--- a/src/view/com/profile/ProfileCard.tsx
+++ b/src/view/com/profile/ProfileCard.tsx
@@ -50,6 +50,9 @@ export function ProfileCard({
     return null
   }
   const moderation = moderateProfile(profile, moderationOpts)
+  if (moderation.account.filter) {
+    return null
+  }
 
   return (
     <Link
diff --git a/src/view/com/util/moderation/ContentHider.tsx b/src/view/com/util/moderation/ContentHider.tsx
index b1ea76621..1269b7ebf 100644
--- a/src/view/com/util/moderation/ContentHider.tsx
+++ b/src/view/com/util/moderation/ContentHider.tsx
@@ -7,7 +7,7 @@ import {Text} from '../text/Text'
 import {ShieldExclamation} from 'lib/icons'
 import {describeModerationCause} from 'lib/moderation'
 import {useLingui} from '@lingui/react'
-import {msg} from '@lingui/macro'
+import {msg, Trans} from '@lingui/macro'
 import {useModalControls} from '#/state/modals'
 import {isPostMediaBlurred} from 'lib/moderation'
 
@@ -95,13 +95,17 @@ export function ContentHider({
         <Text type="md" style={pal.text}>
           {desc.name}
         </Text>
-        {!moderation.noOverride && (
-          <View style={styles.showBtn}>
-            <Text type="lg" style={pal.link}>
-              {override ? 'Hide' : 'Show'}
-            </Text>
-          </View>
-        )}
+        <View style={styles.showBtn}>
+          <Text type="lg" style={pal.link}>
+            {moderation.noOverride ? (
+              <Trans>Learn more</Trans>
+            ) : override ? (
+              <Trans>Hide</Trans>
+            ) : (
+              <Trans>Show</Trans>
+            )}
+          </Text>
+        </View>
       </Pressable>
       {override && <View style={childContainerStyle}>{children}</View>}
     </View>
diff --git a/src/view/com/util/moderation/ScreenHider.tsx b/src/view/com/util/moderation/ScreenHider.tsx
index 946f937e9..86f0cbf7b 100644
--- a/src/view/com/util/moderation/ScreenHider.tsx
+++ b/src/view/com/util/moderation/ScreenHider.tsx
@@ -22,6 +22,7 @@ import {Trans, msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import {useModalControls} from '#/state/modals'
 import {s} from '#/lib/styles'
+import {CenteredView} from '../Views'
 
 export function ScreenHider({
   testID,
@@ -53,41 +54,58 @@ export function ScreenHider({
     )
   }
 
+  const isNoPwi =
+    moderation.cause?.type === 'label' &&
+    moderation.cause?.labelDef.id === '!no-unauthenticated'
   const desc = describeModerationCause(moderation.cause, 'account')
   return (
-    <View style={[styles.container, pal.view, containerStyle]}>
+    <CenteredView
+      style={[styles.container, pal.view, containerStyle]}
+      sideBorders>
       <View style={styles.iconContainer}>
         <View style={[styles.icon, palInverted.view]}>
           <FontAwesomeIcon
-            icon="exclamation"
+            icon={isNoPwi ? ['far', 'eye-slash'] : 'exclamation'}
             style={pal.textInverted as FontAwesomeIconStyle}
             size={24}
           />
         </View>
       </View>
       <Text type="title-2xl" style={[styles.title, pal.text]}>
-        <Trans>Content Warning</Trans>
+        {isNoPwi ? (
+          <Trans>Sign-in Required</Trans>
+        ) : (
+          <Trans>Content Warning</Trans>
+        )}
       </Text>
       <Text type="2xl" style={[styles.description, pal.textLight]}>
-        <Trans>This {screenDescription} has been flagged:</Trans>
-        <Text type="2xl-medium" style={[pal.text, s.ml5]}>
-          {desc.name}.
-        </Text>
-        <TouchableWithoutFeedback
-          onPress={() => {
-            openModal({
-              name: 'moderation-details',
-              context: 'account',
-              moderation,
-            })
-          }}
-          accessibilityRole="button"
-          accessibilityLabel={_(msg`Learn more about this warning`)}
-          accessibilityHint="">
-          <Text type="2xl" style={pal.link}>
-            <Trans>Learn More</Trans>
-          </Text>
-        </TouchableWithoutFeedback>
+        {isNoPwi ? (
+          <Trans>
+            This account has requested that users sign in to view their profile.
+          </Trans>
+        ) : (
+          <>
+            <Trans>This {screenDescription} has been flagged:</Trans>
+            <Text type="2xl-medium" style={[pal.text, s.ml5]}>
+              {desc.name}.
+            </Text>
+            <TouchableWithoutFeedback
+              onPress={() => {
+                openModal({
+                  name: 'moderation-details',
+                  context: 'account',
+                  moderation,
+                })
+              }}
+              accessibilityRole="button"
+              accessibilityLabel={_(msg`Learn more about this warning`)}
+              accessibilityHint="">
+              <Text type="2xl" style={pal.link}>
+                <Trans>Learn More</Trans>
+              </Text>
+            </TouchableWithoutFeedback>
+          </>
+        )}{' '}
       </Text>
       {isMobile && <View style={styles.spacer} />}
       <View style={styles.btnContainer}>
@@ -116,7 +134,7 @@ export function ScreenHider({
           </Button>
         )}
       </View>
-    </View>
+    </CenteredView>
   )
 }
 
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
index 42a958b95..bfe440265 100644
--- a/src/view/screens/Home.tsx
+++ b/src/view/screens/Home.tsx
@@ -9,6 +9,7 @@ import {CustomFeedEmptyState} from 'view/com/posts/CustomFeedEmptyState'
 import {FeedsTabBar} from '../com/pager/FeedsTabBar'
 import {Pager, RenderTabBarFnProps} from 'view/com/pager/Pager'
 import {FeedPage} from 'view/com/feeds/FeedPage'
+import {HomeLoggedOutCTA} from '../com/auth/HomeLoggedOutCTA'
 import {useSetMinimalShellMode, useSetDrawerSwipeDisabled} from '#/state/shell'
 import {usePreferencesQuery} from '#/state/queries/preferences'
 import {UsePreferencesQueryResponse} from '#/state/queries/preferences/types'
@@ -199,12 +200,7 @@ function HomeScreenReady({
       onPageScrollStateChanged={onPageScrollStateChanged}
       renderTabBar={renderTabBar}
       tabBarPosition="top">
-      <FeedPage
-        testID="customFeedPage"
-        isPageFocused={true}
-        feed={`feedgen|at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot`}
-        renderEmptyState={renderCustomFeedEmptyState}
-      />
+      <HomeLoggedOutCTA />
     </Pager>
   )
 }
diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx
index 3f2dd773e..f1c648a67 100644
--- a/src/view/screens/Profile.tsx
+++ b/src/view/screens/Profile.tsx
@@ -153,7 +153,7 @@ function ProfileScreenLoaded({
   const isMe = profile.did === currentAccount?.did
   const showRepliesTab = hasSession
   const showLikesTab = isMe
-  const showFeedsTab = isMe || extraInfoQuery.data?.hasFeedgens
+  const showFeedsTab = hasSession && (isMe || extraInfoQuery.data?.hasFeedgens)
   const showListsTab = hasSession && (isMe || extraInfoQuery.data?.hasLists)
   const sectionTitles = useMemo<string[]>(() => {
     return [
diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx
index 7d7b4098f..efd8507a7 100644
--- a/src/view/screens/Search/Search.tsx
+++ b/src/view/screens/Search/Search.tsx
@@ -304,7 +304,8 @@ function SearchScreenUserResults({query}: {query: string}) {
   )
 }
 
-const SECTIONS = ['Posts', 'Users']
+const SECTIONS_LOGGEDOUT = ['Users']
+const SECTIONS_LOGGEDIN = ['Posts', 'Users']
 export function SearchScreenInner({query}: {query?: string}) {
   const pal = usePalette('default')
   const setMinimalShellMode = useSetMinimalShellMode()
@@ -320,44 +321,62 @@ export function SearchScreenInner({query}: {query?: string}) {
     [setDrawerSwipeDisabled, setMinimalShellMode],
   )
 
+  if (hasSession) {
+    return query ? (
+      <Pager
+        tabBarPosition="top"
+        onPageSelected={onPageSelected}
+        renderTabBar={props => (
+          <CenteredView sideBorders style={pal.border}>
+            <TabBar items={SECTIONS_LOGGEDIN} {...props} />
+          </CenteredView>
+        )}
+        initialPage={0}>
+        <View>
+          <SearchScreenPostResults query={query} />
+        </View>
+        <View>
+          <SearchScreenUserResults query={query} />
+        </View>
+      </Pager>
+    ) : (
+      <View>
+        <CenteredView sideBorders style={pal.border}>
+          <Text
+            type="title"
+            style={[
+              pal.text,
+              pal.border,
+              {
+                display: 'flex',
+                paddingVertical: 12,
+                paddingHorizontal: 18,
+                fontWeight: 'bold',
+              },
+            ]}>
+            <Trans>Suggested Follows</Trans>
+          </Text>
+        </CenteredView>
+
+        <SearchScreenSuggestedFollows />
+      </View>
+    )
+  }
+
   return query ? (
     <Pager
       tabBarPosition="top"
       onPageSelected={onPageSelected}
       renderTabBar={props => (
         <CenteredView sideBorders style={pal.border}>
-          <TabBar items={SECTIONS} {...props} />
+          <TabBar items={SECTIONS_LOGGEDOUT} {...props} />
         </CenteredView>
       )}
       initialPage={0}>
       <View>
-        <SearchScreenPostResults query={query} />
-      </View>
-      <View>
         <SearchScreenUserResults query={query} />
       </View>
     </Pager>
-  ) : hasSession ? (
-    <View>
-      <CenteredView sideBorders style={pal.border}>
-        <Text
-          type="title"
-          style={[
-            pal.text,
-            pal.border,
-            {
-              display: 'flex',
-              paddingVertical: 12,
-              paddingHorizontal: 18,
-              fontWeight: 'bold',
-            },
-          ]}>
-          <Trans>Suggested Follows</Trans>
-        </Text>
-      </CenteredView>
-
-      <SearchScreenSuggestedFollows />
-    </View>
   ) : (
     <CenteredView sideBorders style={pal.border}>
       <View
@@ -383,13 +402,27 @@ export function SearchScreenInner({query}: {query?: string}) {
           </Text>
         )}
 
-        <Text
-          style={[
-            pal.textLight,
-            {textAlign: 'center', paddingVertical: 12, paddingHorizontal: 18},
-          ]}>
-          <Trans>Search for posts and users.</Trans>
-        </Text>
+        <View
+          style={{
+            flexDirection: 'column',
+            alignItems: 'center',
+            justifyContent: 'center',
+            paddingVertical: 30,
+            gap: 15,
+          }}>
+          <MagnifyingGlassIcon
+            strokeWidth={3}
+            size={isDesktop ? 60 : 60}
+            style={pal.textLight}
+          />
+          <Text type="xl" style={[pal.textLight, {paddingHorizontal: 18}]}>
+            {isDesktop ? (
+              <Trans>Find users with the search tool on the right</Trans>
+            ) : (
+              <Trans>Find users on Bluesky</Trans>
+            )}
+          </Text>
+        </View>
       </View>
     </CenteredView>
   )
diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx
index 4d3a9531d..4fb8565e8 100644
--- a/src/view/shell/Drawer.tsx
+++ b/src/view/shell/Drawer.tsx
@@ -231,9 +231,9 @@ let DrawerContent = ({}: {}): React.ReactNode => {
               onPress={onPressNotifications}
             />
           )}
-          <FeedsMenuItem isActive={isAtFeeds} onPress={onPressMyFeeds} />
           {hasSession && (
             <>
+              <FeedsMenuItem isActive={isAtFeeds} onPress={onPressMyFeeds} />
               <ListsMenuItem onPress={onPressLists} />
               <ModerationMenuItem onPress={onPressModeration} />
               <ProfileMenuItem
diff --git a/src/view/shell/NavSignupCard.tsx b/src/view/shell/NavSignupCard.tsx
index 11dd7ffee..8c0e2075d 100644
--- a/src/view/shell/NavSignupCard.tsx
+++ b/src/view/shell/NavSignupCard.tsx
@@ -14,13 +14,19 @@ import {useCloseAllActiveElements} from '#/state/util'
 let NavSignupCard = ({}: {}): React.ReactNode => {
   const {_} = useLingui()
   const pal = usePalette('default')
-  const {setShowLoggedOut} = useLoggedOutViewControls()
+  const {requestSwitchToAccount} = useLoggedOutViewControls()
   const closeAllActiveElements = useCloseAllActiveElements()
 
-  const showLoggedOut = React.useCallback(() => {
+  const showSignIn = React.useCallback(() => {
     closeAllActiveElements()
-    setShowLoggedOut(true)
-  }, [setShowLoggedOut, closeAllActiveElements])
+    requestSwitchToAccount({requestedAccount: 'none'})
+  }, [requestSwitchToAccount, closeAllActiveElements])
+
+  const showCreateAccount = React.useCallback(() => {
+    closeAllActiveElements()
+    requestSwitchToAccount({requestedAccount: 'new'})
+    // setShowLoggedOut(true)
+  }, [requestSwitchToAccount, closeAllActiveElements])
 
   return (
     <View
@@ -39,7 +45,7 @@ let NavSignupCard = ({}: {}): React.ReactNode => {
 
       <View style={{flexDirection: 'row', paddingTop: 12, gap: 8}}>
         <Button
-          onPress={showLoggedOut}
+          onPress={showCreateAccount}
           accessibilityHint={_(msg`Sign up`)}
           accessibilityLabel={_(msg`Sign up`)}>
           <Text type="md" style={[{color: 'white'}, s.bold]}>
@@ -48,7 +54,7 @@ let NavSignupCard = ({}: {}): React.ReactNode => {
         </Button>
         <Button
           type="default"
-          onPress={showLoggedOut}
+          onPress={showSignIn}
           accessibilityHint={_(msg`Sign in`)}
           accessibilityLabel={_(msg`Sign in`)}>
           <Text type="md" style={[pal.text, s.bold]}>
diff --git a/src/view/shell/bottom-bar/BottomBar.tsx b/src/view/shell/bottom-bar/BottomBar.tsx
index 7f1ba8a5f..ef147f27e 100644
--- a/src/view/shell/bottom-bar/BottomBar.tsx
+++ b/src/view/shell/bottom-bar/BottomBar.tsx
@@ -138,32 +138,32 @@ export function BottomBar({navigation}: BottomTabBarProps) {
         accessibilityLabel={_(msg`Search`)}
         accessibilityHint=""
       />
-      <Btn
-        testID="bottomBarFeedsBtn"
-        icon={
-          isAtFeeds ? (
-            <HashtagIcon
-              size={24}
-              style={[styles.ctrlIcon, pal.text, styles.feedsIcon]}
-              strokeWidth={4}
-            />
-          ) : (
-            <HashtagIcon
-              size={24}
-              style={[styles.ctrlIcon, pal.text, styles.feedsIcon]}
-              strokeWidth={2.25}
-            />
-          )
-        }
-        onPress={onPressFeeds}
-        accessibilityRole="tab"
-        accessibilityLabel={_(msg`Feeds`)}
-        accessibilityHint=""
-      />
 
       {hasSession && (
         <>
           <Btn
+            testID="bottomBarFeedsBtn"
+            icon={
+              isAtFeeds ? (
+                <HashtagIcon
+                  size={24}
+                  style={[styles.ctrlIcon, pal.text, styles.feedsIcon]}
+                  strokeWidth={4}
+                />
+              ) : (
+                <HashtagIcon
+                  size={24}
+                  style={[styles.ctrlIcon, pal.text, styles.feedsIcon]}
+                  strokeWidth={2.25}
+                />
+              )
+            }
+            onPress={onPressFeeds}
+            accessibilityRole="tab"
+            accessibilityLabel={_(msg`Feeds`)}
+            accessibilityHint=""
+          />
+          <Btn
             testID="bottomBarNotificationsBtn"
             icon={
               isAtNotifications ? (
diff --git a/src/view/shell/bottom-bar/BottomBarWeb.tsx b/src/view/shell/bottom-bar/BottomBarWeb.tsx
index 3a60bd3b1..6ed0a99f5 100644
--- a/src/view/shell/bottom-bar/BottomBarWeb.tsx
+++ b/src/view/shell/bottom-bar/BottomBarWeb.tsx
@@ -64,20 +64,20 @@ export function BottomBarWeb() {
           )
         }}
       </NavItem>
-      <NavItem routeName="Feeds" href="/feeds">
-        {({isActive}) => {
-          return (
-            <HashtagIcon
-              size={22}
-              style={[styles.ctrlIcon, pal.text, styles.feedsIcon]}
-              strokeWidth={isActive ? 4 : 2.5}
-            />
-          )
-        }}
-      </NavItem>
 
       {hasSession && (
         <>
+          <NavItem routeName="Feeds" href="/feeds">
+            {({isActive}) => {
+              return (
+                <HashtagIcon
+                  size={22}
+                  style={[styles.ctrlIcon, pal.text, styles.feedsIcon]}
+                  strokeWidth={isActive ? 4 : 2.5}
+                />
+              )
+            }}
+          </NavItem>
           <NavItem routeName="Notifications" href="/notifications">
             {({isActive}) => {
               const Icon = isActive ? BellIconSolid : BellIcon
diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx
index e294431f3..8078df802 100644
--- a/src/view/shell/desktop/LeftNav.tsx
+++ b/src/view/shell/desktop/LeftNav.tsx
@@ -314,28 +314,27 @@ export function DesktopLeftNav() {
         }
         label={_(msg`Search`)}
       />
-      <NavItem
-        href="/feeds"
-        icon={
-          <HashtagIcon
-            strokeWidth={2.25}
-            style={pal.text as FontAwesomeIconStyle}
-            size={isDesktop ? 24 : 28}
-          />
-        }
-        iconFilled={
-          <HashtagIcon
-            strokeWidth={2.5}
-            style={pal.text as FontAwesomeIconStyle}
-            size={isDesktop ? 24 : 28}
-          />
-        }
-        label={_(msg`Feeds`)}
-      />
-
       {hasSession && (
         <>
           <NavItem
+            href="/feeds"
+            icon={
+              <HashtagIcon
+                strokeWidth={2.25}
+                style={pal.text as FontAwesomeIconStyle}
+                size={isDesktop ? 24 : 28}
+              />
+            }
+            iconFilled={
+              <HashtagIcon
+                strokeWidth={2.5}
+                style={pal.text as FontAwesomeIconStyle}
+                size={isDesktop ? 24 : 28}
+              />
+            }
+            label={_(msg`Feeds`)}
+          />
+          <NavItem
             href="/notifications"
             count={numUnread}
             icon={