diff --git a/oxlint-suppressions.json b/oxlint-suppressions.json index 9f8f3cc426..2bc0ab246f 100644 --- a/oxlint-suppressions.json +++ b/oxlint-suppressions.json @@ -933,17 +933,6 @@ "count": 1 } }, - "src/screens/Onboarding/StepInterests/index.tsx": { - "typescript/no-explicit-any": { - "count": 1 - }, - "typescript/no-misused-promises": { - "count": 1 - }, - "typescript/require-await": { - "count": 1 - } - }, "src/screens/Onboarding/StepProfile/index.tsx": { "typescript/no-floating-promises": { "count": 2 diff --git a/src/analytics/features/types.ts b/src/analytics/features/types.ts index b7c70fa9a7..353073216b 100644 --- a/src/analytics/features/types.ts +++ b/src/analytics/features/types.ts @@ -22,6 +22,7 @@ export enum Features { VideoMultipartUploadEnable = 'video:multipart_upload:enable', SearchStarterPacksV2Enable = 'search_starter_packs_v2:enable', FollowSortEnable = 'follow_sort:enable', + OnboardingInterestsRequiredEnable = 'onboarding:interests:required:enable', // values TrendingDiscoverValues = 'trending_discover:values', diff --git a/src/analytics/metrics/types.ts b/src/analytics/metrics/types.ts index 740865326a..592f9a563d 100644 --- a/src/analytics/metrics/types.ts +++ b/src/analytics/metrics/types.ts @@ -143,6 +143,7 @@ export type Events = { selectedInterests: string[] selectedInterestsLength: number } + 'onboarding:interests:disabledNextPressed': {} 'onboarding:suggestedAccounts:tabPressed': { tab: string } diff --git a/src/screens/Onboarding/StepInterests/index.tsx b/src/screens/Onboarding/StepInterests/index.tsx index 2b9afa0630..10611c92d5 100644 --- a/src/screens/Onboarding/StepInterests/index.tsx +++ b/src/screens/Onboarding/StepInterests/index.tsx @@ -1,8 +1,6 @@ import {useCallback, useState} from 'react' -import {View} from 'react-native' -import {msg} from '@lingui/core/macro' -import {useLingui} from '@lingui/react' -import {Trans} from '@lingui/react/macro' +import {Pressable, View} from 'react-native' +import {Trans, useLingui} from '@lingui/react/macro' import {interests, useInterestsDisplayNames} from '#/lib/interests' import {capitalize} from '#/lib/strings/capitalize' @@ -19,20 +17,36 @@ import {atoms as a} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' import * as Toggle from '#/components/forms/Toggle' import {Loader} from '#/components/Loader' +import * as Tooltip from '#/components/Tooltip' import {useAnalytics} from '#/analytics' export function StepInterests() { - const {_} = useLingui() + const {t: l} = useLingui() const ax = useAnalytics() const interestsDisplayNames = useInterestsDisplayNames() const {state, dispatch} = useOnboardingInternalState() const [saving, setSaving] = useState(false) + const [tooltipVisible, setTooltipVisible] = useState(false) const [selectedInterests, setSelectedInterests] = useState( state.interestsStepResults.selectedInterests.map(i => i), ) + /* + * Behind this gate, users must choose at least one interest before they can + * continue. + */ + const interestRequired = ax.features.enabled( + ax.features.OnboardingInterestsRequiredEnable, + ) + const missingRequiredInterest = + interestRequired && selectedInterests.length === 0 - const saveInterests = useCallback(async () => { + const showMissingInterestTooltip = () => { + ax.metric('onboarding:interests:disabledNextPressed', {}) + setTooltipVisible(true) + } + + const saveInterests = useCallback(() => { setSaving(true) try { @@ -46,12 +60,37 @@ export function StepInterests() { selectedInterests, selectedInterestsLength: selectedInterests.length, }) - } catch (e: any) { - logger.info(`onboading: error saving interests`) + } catch (error) { + const e = error as Error + logger.info(`onboarding: error saving interests`) logger.error(e) } }, [ax, selectedInterests, setSaving, dispatch]) + const continueButton = ( + + ) + return ( @@ -59,14 +98,21 @@ export function StepInterests() { What are your interests? - We'll use this to help customize your experience. + {interestRequired ? ( + + Choose at least one. We'll use this to customize your experience. + You can change these anytime. + + ) : ( + We'll use this to help customize your experience. + )} + label={l`Select your interests from the options below`}> {interests.map(interest => ( - + + {missingRequiredInterest ? ( + + + + + {continueButton} + + + + + Choose at least one interest. + + + ) : ( + continueButton + )} + )