From a0ff9b52aad3349b24118a0222e0f3d78e695887 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Fri, 4 Apr 2025 14:08:16 -0500 Subject: [Explore] Add interests card (#8125) * Add interests card to top of Explore * Add tip to add interests if they have none or deselect * Format * Copy --- src/screens/Search/Explore.tsx | 24 ++++ .../Search/modules/ExploreInterestsCard.tsx | 128 +++++++++++++++++++++ src/screens/Settings/SettingsInterests.tsx | 50 ++++---- src/state/queries/nuxs/definitions.ts | 20 +++- 4 files changed, 195 insertions(+), 27 deletions(-) create mode 100644 src/screens/Search/modules/ExploreInterestsCard.tsx (limited to 'src') 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(() => { + if (!showInterestsNux) return [] + return [ + { + type: 'interests-card', + key: 'interests-card', + }, + ] + }, [showInterestsNux]) + const isNewUser = guide?.guide === 'follow-10' && !guide.isComplete const items = useMemo(() => { 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 + } } }, [ 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 : ( + <> + + + + + + Your interests + + + {preferences?.interests?.tags && + preferences.interests.tags.length > 0 ? ( + + {preferences.interests.tags.map(tag => ( + + + {interestsDisplayNames[tag]} + + + ))} + + ) : null} + + + + Your selected interests help us serve you content you care about. + + + + + + Edit interests + + + + + + + + ) +} 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, ]}> - Selecting interests from the list below helps us deliver you - higher quality content. + Your selected interests help us serve you content you care about. @@ -124,25 +124,33 @@ function Inner({ } return ( - - - {INTERESTS.map(interest => { - const name = interestsDisplayNames[interest] - if (!name) return null - return ( - - - - ) - })} - - + <> + {interests.length === 0 && ( + + We recommend selecting at least two interests. + + )} + + + + {INTERESTS.map(interest => { + const name = interestsDisplayNames[interest] + if (!name) return null + return ( + + + + ) + })} + + + ) } 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 | undefined> = { [Nux.NeueTypography]: undefined, + [Nux.ExploreInterestsCard]: undefined, } -- cgit 1.4.1