about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorAlex Benzer <abenzer@users.noreply.github.com>2025-08-26 08:52:38 -0700
committerGitHub <noreply@github.com>2025-08-26 08:52:38 -0700
commitacd7211b357f2bfc74bf0828994e12f0c41d39d5 (patch)
tree34a09d23bf3862ccb9683ba4975e3af0889c11d0 /src
parentb70e5b2f387e8de6dac5d388aee1ccbf5b217adc (diff)
downloadvoidsky-acd7211b357f2bfc74bf0828994e12f0c41d39d5.tar.zst
Adds signup CTA tests for logged-out visitors (#8906)
Diffstat (limited to 'src')
-rw-r--r--src/components/LoggedOutCTA.tsx80
-rw-r--r--src/lib/statsig/gates.ts2
-rw-r--r--src/screens/PostThread/components/ThreadItemAnchor.tsx6
-rw-r--r--src/screens/PostThread/index.tsx3
4 files changed, 90 insertions, 1 deletions
diff --git a/src/components/LoggedOutCTA.tsx b/src/components/LoggedOutCTA.tsx
new file mode 100644
index 000000000..7ec8c2264
--- /dev/null
+++ b/src/components/LoggedOutCTA.tsx
@@ -0,0 +1,80 @@
+import {View, type ViewStyle} from 'react-native'
+import {Trans} from '@lingui/macro'
+
+import {type Gate} from '#/lib/statsig/gates'
+import {useGate} from '#/lib/statsig/statsig'
+import {isWeb} from '#/platform/detection'
+import {useSession} from '#/state/session'
+import {useLoggedOutViewControls} from '#/state/shell/logged-out'
+import {Logo} from '#/view/icons/Logo'
+import {atoms as a, useTheme} from '#/alf'
+import {Button, ButtonText} from '#/components/Button'
+import {Text} from '#/components/Typography'
+
+interface LoggedOutCTAProps {
+  style?: ViewStyle
+  gateName: Gate
+}
+
+export function LoggedOutCTA({style, gateName}: LoggedOutCTAProps) {
+  const {hasSession} = useSession()
+  const {requestSwitchToAccount} = useLoggedOutViewControls()
+  const gate = useGate()
+  const t = useTheme()
+
+  // Only show for logged-out users on web
+  if (hasSession || !isWeb) {
+    return null
+  }
+
+  // Check gate at the last possible moment to avoid counting users as exposed when they won't see the element
+  if (!gate(gateName)) {
+    return null
+  }
+
+  return (
+    <View style={[a.pb_md, style]}>
+      <View
+        style={[
+          a.flex_row,
+          a.align_center,
+          a.justify_between,
+          a.px_lg,
+          a.py_md,
+          a.rounded_md,
+          a.mb_xs,
+          t.atoms.bg_contrast_25,
+        ]}>
+        <View style={[a.flex_row, a.align_center, a.flex_1, a.pr_md]}>
+          <Logo width={30} style={[a.mr_md]} />
+          <View style={[a.flex_1]}>
+            <Text style={[a.text_lg, a.font_bold, a.leading_snug]}>
+              <Trans>Join Bluesky</Trans>
+            </Text>
+            <Text
+              style={[
+                a.text_md,
+                a.font_medium,
+                a.leading_snug,
+                t.atoms.text_contrast_medium,
+              ]}>
+              <Trans>The open social network.</Trans>
+            </Text>
+          </View>
+        </View>
+        <Button
+          onPress={() => {
+            requestSwitchToAccount({requestedAccount: 'new'})
+          }}
+          label="Create account"
+          size="small"
+          variant="solid"
+          color="primary">
+          <ButtonText>
+            <Trans>Create account</Trans>
+          </ButtonText>
+        </Button>
+      </View>
+    </View>
+  )
+}
diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts
index 90ccd3443..114048ab7 100644
--- a/src/lib/statsig/gates.ts
+++ b/src/lib/statsig/gates.ts
@@ -1,6 +1,8 @@
 export type Gate =
   // Keep this alphabetic please.
   | 'alt_share_icon'
+  | 'cta_above_post_heading'
+  | 'cta_above_post_replies'
   | 'debug_show_feedcontext'
   | 'debug_subscriptions'
   | 'disable_onboarding_policy_update_notice'
diff --git a/src/screens/PostThread/components/ThreadItemAnchor.tsx b/src/screens/PostThread/components/ThreadItemAnchor.tsx
index 550bddc6a..7b0d567b8 100644
--- a/src/screens/PostThread/components/ThreadItemAnchor.tsx
+++ b/src/screens/PostThread/components/ThreadItemAnchor.tsx
@@ -40,12 +40,13 @@ import {
   OUTER_SPACE,
   REPLY_LINE_WIDTH,
 } from '#/screens/PostThread/const'
-import {atoms as a, useTheme} from '#/alf'
+import {atoms as a, useBreakpoints, useTheme} from '#/alf'
 import {colors} from '#/components/Admonition'
 import {Button} from '#/components/Button'
 import {CalendarClock_Stroke2_Corner0_Rounded as CalendarClockIcon} from '#/components/icons/CalendarClock'
 import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from '#/components/icons/Trash'
 import {InlineLinkText, Link} from '#/components/Link'
+import {LoggedOutCTA} from '#/components/LoggedOutCTA'
 import {ContentHider} from '#/components/moderation/ContentHider'
 import {LabelsOnMyPost} from '#/components/moderation/LabelsOnMe'
 import {PostAlerts} from '#/components/moderation/PostAlerts'
@@ -178,6 +179,7 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({
   const {_, i18n} = useLingui()
   const {openComposer} = useOpenComposer()
   const {currentAccount, hasSession} = useSession()
+  const {gtTablet} = useBreakpoints()
   const feedFeedback = useFeedFeedback(postSource?.feed, hasSession)
 
   const post = postShadow
@@ -311,6 +313,8 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({
           },
           isRoot && [a.pt_lg],
         ]}>
+        {/* Show CTA for logged-out visitors - hide on desktop and check gate */}
+        {!gtTablet && <LoggedOutCTA gateName="cta_above_post_heading" />}
         <View style={[a.flex_row, a.gap_md, a.pb_md]}>
           <View collapsable={false}>
             <PreviewableUserAvatar
diff --git a/src/screens/PostThread/index.tsx b/src/screens/PostThread/index.tsx
index 7432f71db..9cb4173e3 100644
--- a/src/screens/PostThread/index.tsx
+++ b/src/screens/PostThread/index.tsx
@@ -38,6 +38,7 @@ import {
 import {atoms as a, native, platform, useBreakpoints, web} from '#/alf'
 import * as Layout from '#/components/Layout'
 import {ListFooter} from '#/components/Lists'
+import {LoggedOutCTA} from '#/components/LoggedOutCTA'
 
 const PARENT_CHUNK_SIZE = 5
 const CHILDREN_CHUNK_SIZE = 50
@@ -405,6 +406,8 @@ export function PostThread({uri}: {uri: string}) {
                 onPostSuccess={optimisticOnPostReply}
                 postSource={anchorPostSource}
               />
+              {/* Show CTA for logged-out visitors */}
+              <LoggedOutCTA style={a.px_lg} gateName="cta_above_post_replies" />
             </View>
           )
         } else {