about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/screens/Search/Explore.tsx24
-rw-r--r--src/screens/Search/modules/ExploreInterestsCard.tsx128
-rw-r--r--src/screens/Settings/SettingsInterests.tsx50
-rw-r--r--src/state/queries/nuxs/definitions.ts20
4 files changed, 195 insertions, 27 deletions
diff --git a/src/screens/Search/Explore.tsx b/src/screens/Search/Explore.tsx
index 00eb6c433..088bc5724 100644
--- a/src/screens/Search/Explore.tsx
+++ b/src/screens/Search/Explore.tsx
@@ -21,6 +21,7 @@ import {
   useFeedPreviews,
 } from '#/state/queries/explore-feed-previews'
 import {useGetPopularFeedsQuery} from '#/state/queries/feed'
+import {Nux, useNux} from '#/state/queries/nuxs'
 import {usePreferencesQuery} from '#/state/queries/preferences'
 import {useSuggestedFollowsQuery} from '#/state/queries/suggested-follows'
 import {useGetSuggestedFeedsQuery} from '#/state/queries/trending/useGetSuggestedFeedsQuery'
@@ -36,6 +37,7 @@ import {
   StarterPackCard,
   StarterPackCardSkeleton,
 } from '#/screens/Search/components/StarterPackCard'
+import {ExploreInterestsCard} from '#/screens/Search/modules/ExploreInterestsCard'
 import {ExploreRecommendations} from '#/screens/Search/modules/ExploreRecommendations'
 import {ExploreTrendingTopics} from '#/screens/Search/modules/ExploreTrendingTopics'
 import {ExploreTrendingVideos} from '#/screens/Search/modules/ExploreTrendingVideos'
@@ -181,6 +183,10 @@ type ExploreScreenItems =
       key: string
     }
   | FeedPreviewItem
+  | {
+      type: 'interests-card'
+      key: 'interests-card'
+    }
 
 export function Explore({
   focusSearchInput,
@@ -233,6 +239,9 @@ export function Explore({
     error: feedsError,
     fetchNextPage: fetchNextFeedsPage,
   } = useGetPopularFeedsQuery({limit: 10})
+  const interestsNux = useNux(Nux.ExploreInterestsCard)
+  const showInterestsNux =
+    interestsNux.status === 'ready' && !interestsNux.nux?.completed
 
   const profiles: typeof suggestedProfiles & typeof interestProfiles =
     !selectedInterest ? suggestedProfiles : interestProfiles
@@ -558,6 +567,16 @@ export function Explore({
     return i
   }, [feedPreviewSlices, isFetchingNextPageFeedPreviews])
 
+  const interestsNuxModule = useMemo<ExploreScreenItems[]>(() => {
+    if (!showInterestsNux) return []
+    return [
+      {
+        type: 'interests-card',
+        key: 'interests-card',
+      },
+    ]
+  }, [showInterestsNux])
+
   const isNewUser = guide?.guide === 'follow-10' && !guide.isComplete
   const items = useMemo<ExploreScreenItems[]>(() => {
     const i: ExploreScreenItems[] = []
@@ -565,6 +584,7 @@ export function Explore({
     // Dynamic module ordering
 
     i.push(topBorder)
+    i.push(...interestsNuxModule)
     if (isNewUser) {
       i.push(...suggestedFollowsModule)
       i.push(...suggestedStarterPacksModule)
@@ -588,6 +608,7 @@ export function Explore({
     suggestedFeedsModule,
     trendingTopicsModule,
     feedPreviewsModule,
+    interestsNuxModule,
     gate,
   ])
 
@@ -854,6 +875,9 @@ export function Explore({
             />
           )
         }
+        case 'interests-card': {
+          return <ExploreInterestsCard />
+        }
       }
     },
     [
diff --git a/src/screens/Search/modules/ExploreInterestsCard.tsx b/src/screens/Search/modules/ExploreInterestsCard.tsx
new file mode 100644
index 000000000..fde5c3b1e
--- /dev/null
+++ b/src/screens/Search/modules/ExploreInterestsCard.tsx
@@ -0,0 +1,128 @@
+import {useState} from 'react'
+import {View} from 'react-native'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+
+import {Nux, useSaveNux} from '#/state/queries/nuxs'
+import {usePreferencesQuery} from '#/state/queries/preferences'
+import {useInterestsDisplayNames} from '#/screens/Onboarding/state'
+import {atoms as a, useGutters, useTheme} from '#/alf'
+import {Button, ButtonIcon, ButtonText} from '#/components/Button'
+import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
+import {Link} from '#/components/Link'
+import * as Prompt from '#/components/Prompt'
+import {Text} from '#/components/Typography'
+
+export function ExploreInterestsCard() {
+  const t = useTheme()
+  const {_} = useLingui()
+  const gutters = useGutters([0, 'base'])
+  const {data: preferences} = usePreferencesQuery()
+  const interestsDisplayNames = useInterestsDisplayNames()
+  const {mutateAsync: saveNux} = useSaveNux()
+  const trendingPrompt = Prompt.usePromptControl()
+  const [closing, setClosing] = useState(false)
+
+  const onClose = () => {
+    trendingPrompt.open()
+  }
+  const onConfirmClose = () => {
+    setClosing(true)
+    // if this fails, they can try again later
+    saveNux({
+      id: Nux.ExploreInterestsCard,
+      completed: true,
+      data: undefined,
+    }).catch(() => {})
+  }
+
+  return closing ? null : (
+    <>
+      <Prompt.Basic
+        control={trendingPrompt}
+        title={_(msg`Your interests`)}
+        description={_(
+          msg`You can adjust your interests at any time from your "Content and media" settings.`,
+        )}
+        confirmButtonCta={_(
+          msg({
+            message: `Copy that!`,
+            comment: `Confirm button text. Can be a short cheeky phrase that means "OK" e.g. "Copy that!"`,
+          }),
+        )}
+        onConfirm={onConfirmClose}
+      />
+
+      <View style={[gutters, a.pt_lg, a.pb_2xs]}>
+        <View
+          style={[
+            a.p_lg,
+            a.rounded_md,
+            a.border,
+            a.gap_sm,
+            t.atoms.border_contrast_medium,
+            t.atoms.bg_contrast_25,
+          ]}>
+          <Text style={[a.text_md, a.font_bold, a.leading_tight]}>
+            <Trans>Your interests</Trans>
+          </Text>
+
+          {preferences?.interests?.tags &&
+          preferences.interests.tags.length > 0 ? (
+            <View style={[a.flex_row, a.flex_wrap, {gap: 6}]}>
+              {preferences.interests.tags.map(tag => (
+                <View
+                  key={tag}
+                  style={[
+                    a.justify_center,
+                    a.align_center,
+                    a.rounded_full,
+                    a.border,
+                    t.atoms.border_contrast_medium,
+                    a.px_lg,
+                    {height: 32},
+                  ]}>
+                  <Text style={[a.text_sm, t.atoms.text_contrast_medium]}>
+                    {interestsDisplayNames[tag]}
+                  </Text>
+                </View>
+              ))}
+            </View>
+          ) : null}
+
+          <Text style={[a.text_sm, a.leading_snug, a.pb_xs]}>
+            <Trans>
+              Your selected interests help us serve you content you care about.
+            </Trans>
+          </Text>
+
+          <Link
+            label={_(msg`Edit interests`)}
+            to="/settings/interests"
+            size="small"
+            variant="solid"
+            color="primary"
+            style={[a.justify_center]}>
+            <ButtonText>
+              <Trans>Edit interests</Trans>
+            </ButtonText>
+          </Link>
+
+          <Button
+            label={_(msg`Hide this card`)}
+            size="small"
+            variant="solid"
+            color="secondary"
+            shape="round"
+            onPress={onClose}
+            style={[
+              a.absolute,
+              {top: a.pt_xs.paddingTop, right: a.pr_xs.paddingRight},
+            ]}>
+            <ButtonIcon icon={X} />
+          </Button>
+        </View>
+      </View>
+    </>
+  )
+}
diff --git a/src/screens/Settings/SettingsInterests.tsx b/src/screens/Settings/SettingsInterests.tsx
index 266545560..9a6132946 100644
--- a/src/screens/Settings/SettingsInterests.tsx
+++ b/src/screens/Settings/SettingsInterests.tsx
@@ -14,6 +14,7 @@ import {useAgent} from '#/state/session'
 import * as Toast from '#/view/com/util/Toast'
 import {useInterestsDisplayNames} from '#/screens/Onboarding/state'
 import {atoms as a, useGutters, useTheme} from '#/alf'
+import {Admonition} from '#/components/Admonition'
 import {Divider} from '#/components/Divider'
 import * as Toggle from '#/components/forms/Toggle'
 import * as Layout from '#/components/Layout'
@@ -47,8 +48,7 @@ export function SettingsInterests() {
               t.atoms.text_contrast_medium,
             ]}>
             <Trans>
-              Selecting interests from the list below helps us deliver you
-              higher quality content.
+              Your selected interests help us serve you content you care about.
             </Trans>
           </Text>
 
@@ -124,25 +124,33 @@ function Inner({
   }
 
   return (
-    <Toggle.Group
-      values={interests}
-      onChange={onChangeInterests}
-      label={_(msg`Select your interests from the options below`)}>
-      <View style={[a.flex_row, a.flex_wrap, a.gap_sm]}>
-        {INTERESTS.map(interest => {
-          const name = interestsDisplayNames[interest]
-          if (!name) return null
-          return (
-            <Toggle.Item
-              key={interest}
-              name={interest}
-              label={interestsDisplayNames[interest]}>
-              <InterestButton interest={interest} />
-            </Toggle.Item>
-          )
-        })}
-      </View>
-    </Toggle.Group>
+    <>
+      {interests.length === 0 && (
+        <Admonition type="tip">
+          <Trans>We recommend selecting at least two interests.</Trans>
+        </Admonition>
+      )}
+
+      <Toggle.Group
+        values={interests}
+        onChange={onChangeInterests}
+        label={_(msg`Select your interests from the options below`)}>
+        <View style={[a.flex_row, a.flex_wrap, a.gap_sm]}>
+          {INTERESTS.map(interest => {
+            const name = interestsDisplayNames[interest]
+            if (!name) return null
+            return (
+              <Toggle.Item
+                key={interest}
+                name={interest}
+                label={interestsDisplayNames[interest]}>
+                <InterestButton interest={interest} />
+              </Toggle.Item>
+            )
+          })}
+        </View>
+      </Toggle.Group>
+    </>
   )
 }
 
diff --git a/src/state/queries/nuxs/definitions.ts b/src/state/queries/nuxs/definitions.ts
index 8166602c8..8eb53a0a4 100644
--- a/src/state/queries/nuxs/definitions.ts
+++ b/src/state/queries/nuxs/definitions.ts
@@ -1,18 +1,26 @@
-import zod from 'zod'
+import type zod from 'zod'
 
-import {BaseNux} from '#/state/queries/nuxs/types'
+import {type BaseNux} from '#/state/queries/nuxs/types'
 
 export enum Nux {
   NeueTypography = 'NeueTypography',
+  ExploreInterestsCard = 'ExploreInterestsCard',
 }
 
 export const nuxNames = new Set(Object.values(Nux))
 
-export type AppNux = BaseNux<{
-  id: Nux.NeueTypography
-  data: undefined
-}>
+export type AppNux = BaseNux<
+  | {
+      id: Nux.NeueTypography
+      data: undefined
+    }
+  | {
+      id: Nux.ExploreInterestsCard
+      data: undefined
+    }
+>
 
 export const NuxSchemas: Record<Nux, zod.ZodObject<any> | undefined> = {
   [Nux.NeueTypography]: undefined,
+  [Nux.ExploreInterestsCard]: undefined,
 }