Compare commits

...

4 Commits

Author SHA1 Message Date
Eric Bailey d3ee639a3e Copy 2025-04-04 12:31:34 -05:00
Eric Bailey a08e402028 Format 2025-04-04 12:17:05 -05:00
Eric Bailey 77c11f00d2 Add tip to add interests if they have none or deselect 2025-04-04 11:18:33 -05:00
Eric Bailey 3c9a41305c Add interests card to top of Explore 2025-04-04 11:13:13 -05:00
4 changed files with 195 additions and 27 deletions
+24
View File
@@ -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 />
}
}
},
[
@@ -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>
</>
)
}
+29 -21
View File
@@ -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>
</>
)
}
+14 -6
View File
@@ -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,
}