diff options
Diffstat (limited to 'src/screens/Onboarding/StepSuggestedAccounts')
-rw-r--r-- | src/screens/Onboarding/StepSuggestedAccounts/SuggestedAccountCard.tsx | 188 | ||||
-rw-r--r-- | src/screens/Onboarding/StepSuggestedAccounts/index.tsx | 210 |
2 files changed, 0 insertions, 398 deletions
diff --git a/src/screens/Onboarding/StepSuggestedAccounts/SuggestedAccountCard.tsx b/src/screens/Onboarding/StepSuggestedAccounts/SuggestedAccountCard.tsx deleted file mode 100644 index f0ba36e39..000000000 --- a/src/screens/Onboarding/StepSuggestedAccounts/SuggestedAccountCard.tsx +++ /dev/null @@ -1,188 +0,0 @@ -import React from 'react' -import {View, ViewStyle} from 'react-native' -import {AppBskyActorDefs, moderateProfile} from '@atproto/api' - -import {useModerationOpts} from '#/state/preferences/moderation-opts' -import {UserAvatar} from '#/view/com/util/UserAvatar' -import {atoms as a, flatten, useTheme} from '#/alf' -import {useItemContext} from '#/components/forms/Toggle' -import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' -import {RichText} from '#/components/RichText' -import {Text} from '#/components/Typography' - -export function SuggestedAccountCard({ - profile, - moderationOpts, -}: { - profile: AppBskyActorDefs.ProfileViewDetailed - moderationOpts: ReturnType<typeof useModerationOpts> -}) { - const t = useTheme() - const ctx = useItemContext() - const moderation = moderateProfile(profile, moderationOpts!) - - const styles = React.useMemo(() => { - const light = t.name === 'light' - const base: ViewStyle[] = [t.atoms.bg_contrast_50] - const hover: ViewStyle[] = [t.atoms.bg_contrast_25] - const selected: ViewStyle[] = [ - { - backgroundColor: light ? t.palette.primary_50 : t.palette.primary_950, - }, - ] - const selectedHover: ViewStyle[] = [ - { - backgroundColor: light ? t.palette.primary_25 : t.palette.primary_975, - }, - ] - const checkboxBase: ViewStyle[] = [t.atoms.bg] - const checkboxSelected: ViewStyle[] = [ - { - backgroundColor: t.palette.primary_500, - }, - ] - const avatarBase: ViewStyle[] = [t.atoms.bg_contrast_100] - const avatarSelected: ViewStyle[] = [ - { - backgroundColor: light ? t.palette.primary_100 : t.palette.primary_900, - }, - ] - - return { - base, - hover: flatten(hover), - selected: flatten(selected), - selectedHover: flatten(selectedHover), - checkboxBase: flatten(checkboxBase), - checkboxSelected: flatten(checkboxSelected), - avatarBase: flatten(avatarBase), - avatarSelected: flatten(avatarSelected), - } - }, [t]) - - return ( - <View - style={[ - a.w_full, - a.p_md, - a.pr_lg, - a.gap_md, - a.rounded_md, - styles.base, - (ctx.hovered || ctx.focused || ctx.pressed) && styles.hover, - ctx.selected && styles.selected, - ctx.selected && - (ctx.hovered || ctx.focused || ctx.pressed) && - styles.selectedHover, - ]}> - <View style={[a.flex_row, a.align_center, a.justify_between, a.gap_lg]}> - <View style={[a.flex_row, a.flex_1, a.align_center, a.gap_md]}> - <View - style={[ - {width: 48, height: 48}, - a.relative, - a.rounded_full, - styles.avatarBase, - ctx.selected && styles.avatarSelected, - ]}> - <UserAvatar - size={48} - avatar={profile.avatar} - moderation={moderation.ui('avatar')} - /> - </View> - <View style={[a.flex_1]}> - <Text style={[a.font_bold, a.text_md, a.pb_xs]} numberOfLines={1}> - {profile.displayName} - </Text> - <Text style={[t.atoms.text_contrast_medium]}>{profile.handle}</Text> - </View> - </View> - - <View - style={[ - a.justify_center, - a.align_center, - a.rounded_sm, - styles.checkboxBase, - ctx.selected && styles.checkboxSelected, - { - width: 28, - height: 28, - }, - ]}> - {ctx.selected && <Check size="sm" fill={t.palette.white} />} - </View> - </View> - - {profile.description && ( - <> - <View - style={[ - { - opacity: ctx.selected ? 0.3 : 1, - borderTopWidth: 1, - }, - a.w_full, - t.atoms.border_contrast_low, - ctx.selected && { - borderTopColor: t.palette.primary_200, - }, - ]} - /> - - <RichText - value={profile.description} - disableLinks - numberOfLines={2} - /> - </> - )} - </View> - ) -} - -export function SuggestedAccountCardPlaceholder() { - const t = useTheme() - return ( - <View - style={[ - a.w_full, - a.flex_row, - a.justify_between, - a.align_center, - a.p_md, - a.pr_lg, - a.gap_xl, - a.rounded_md, - t.atoms.bg_contrast_25, - ]}> - <View style={[a.flex_row, a.align_center, a.gap_md]}> - <View - style={[ - {width: 48, height: 48}, - a.relative, - a.rounded_full, - t.atoms.bg_contrast_100, - ]} - /> - <View style={[a.gap_xs]}> - <View - style={[ - {width: 100, height: 16}, - a.rounded_sm, - t.atoms.bg_contrast_100, - ]} - /> - <View - style={[ - {width: 60, height: 12}, - a.rounded_sm, - t.atoms.bg_contrast_100, - ]} - /> - </View> - </View> - </View> - ) -} diff --git a/src/screens/Onboarding/StepSuggestedAccounts/index.tsx b/src/screens/Onboarding/StepSuggestedAccounts/index.tsx deleted file mode 100644 index 774f2d3b0..000000000 --- a/src/screens/Onboarding/StepSuggestedAccounts/index.tsx +++ /dev/null @@ -1,210 +0,0 @@ -import React from 'react' -import {View} from 'react-native' -import {AppBskyActorDefs} from '@atproto/api' -import {msg, Trans} from '@lingui/macro' -import {useLingui} from '@lingui/react' - -import {useAnalytics} from '#/lib/analytics/analytics' -import {logEvent} from '#/lib/statsig/statsig' -import {capitalize} from '#/lib/strings/capitalize' -import {useModerationOpts} from '#/state/preferences/moderation-opts' -import {useProfilesQuery} from '#/state/queries/profile' -import { - DescriptionText, - OnboardingControls, - TitleText, -} from '#/screens/Onboarding/Layout' -import {Context} from '#/screens/Onboarding/state' -import { - SuggestedAccountCard, - SuggestedAccountCardPlaceholder, -} from '#/screens/Onboarding/StepSuggestedAccounts/SuggestedAccountCard' -import {aggregateInterestItems} from '#/screens/Onboarding/util' -import {atoms as a, useBreakpoints} from '#/alf' -import {Button, ButtonIcon, ButtonText} from '#/components/Button' -import * as Toggle from '#/components/forms/Toggle' -import {IconCircle} from '#/components/IconCircle' -import {At_Stroke2_Corner0_Rounded as At} from '#/components/icons/At' -import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' -import {Loader} from '#/components/Loader' -import {Text} from '#/components/Typography' - -export function Inner({ - profiles, - onSelect, - moderationOpts, -}: { - profiles: AppBskyActorDefs.ProfileViewDetailed[] - onSelect: (dids: string[]) => void - moderationOpts: ReturnType<typeof useModerationOpts> -}) { - const {_} = useLingui() - const [dids, setDids] = React.useState<string[]>(profiles.map(p => p.did)) - - React.useEffect(() => { - onSelect(dids) - }, [dids, onSelect]) - - return ( - <Toggle.Group - values={dids} - onChange={setDids} - label={_(msg`Select some accounts below to follow`)}> - <View style={[a.gap_md]}> - {profiles.map(profile => ( - <Toggle.Item - key={profile.did} - name={profile.did} - label={_(msg`Follow ${profile.handle}`)}> - <SuggestedAccountCard - profile={profile} - moderationOpts={moderationOpts} - /> - </Toggle.Item> - ))} - </View> - </Toggle.Group> - ) -} - -export function StepSuggestedAccounts() { - const {_} = useLingui() - const {gtMobile} = useBreakpoints() - const {track} = useAnalytics() - const {state, dispatch, interestsDisplayNames} = React.useContext(Context) - const suggestedDids = React.useMemo(() => { - return aggregateInterestItems( - state.interestsStepResults.selectedInterests, - state.interestsStepResults.apiResponse.suggestedAccountDids, - state.interestsStepResults.apiResponse.suggestedAccountDids.default || [], - ) - }, [state.interestsStepResults]) - const moderationOpts = useModerationOpts() - const { - isLoading: isProfilesLoading, - isError, - data, - error, - } = useProfilesQuery({ - handles: suggestedDids, - }) - const [dids, setDids] = React.useState<string[]>([]) - const [saving, setSaving] = React.useState(false) - - const interestsText = React.useMemo(() => { - const i = state.interestsStepResults.selectedInterests.map( - i => interestsDisplayNames[i] || capitalize(i), - ) - return i.join(', ') - }, [state.interestsStepResults.selectedInterests, interestsDisplayNames]) - - const handleContinue = React.useCallback(async () => { - setSaving(true) - - if (dids.length) { - dispatch({type: 'setSuggestedAccountsStepResults', accountDids: dids}) - } - - setSaving(false) - dispatch({type: 'next'}) - track('OnboardingV2:StepSuggestedAccounts:End', { - selectedAccountsLength: dids.length, - }) - logEvent('onboarding:suggestedAccounts:nextPressed', { - selectedAccountsLength: dids.length, - skipped: false, - }) - }, [dids, setSaving, dispatch, track]) - - const handleSkip = React.useCallback(() => { - // if a user comes back and clicks skip, erase follows - dispatch({type: 'setSuggestedAccountsStepResults', accountDids: []}) - dispatch({type: 'next'}) - logEvent('onboarding:suggestedAccounts:nextPressed', { - selectedAccountsLength: 0, - skipped: true, - }) - }, [dispatch]) - - const isLoading = isProfilesLoading && moderationOpts - - React.useEffect(() => { - track('OnboardingV2:StepSuggestedAccounts:Start') - }, [track]) - - return ( - <View style={[a.align_start]}> - <IconCircle icon={At} style={[a.mb_2xl]} /> - - <TitleText> - <Trans>Here are some accounts for you to follow</Trans> - </TitleText> - <DescriptionText> - {state.interestsStepResults.selectedInterests.length ? ( - <Trans>Based on your interest in {interestsText}</Trans> - ) : ( - <Trans>These are popular accounts you might like:</Trans> - )} - </DescriptionText> - - <View style={[a.w_full, a.pt_xl]}> - {isLoading ? ( - <View style={[a.gap_md]}> - {Array(10) - .fill(0) - .map((_, i) => ( - <SuggestedAccountCardPlaceholder key={i} /> - ))} - </View> - ) : isError || !data ? ( - <Text>{error?.toString()}</Text> - ) : ( - <Inner - profiles={data.profiles} - onSelect={setDids} - moderationOpts={moderationOpts} - /> - )} - </View> - - <OnboardingControls.Portal> - <View - style={[ - a.gap_md, - gtMobile ? {flexDirection: 'row-reverse'} : a.flex_col, - ]}> - <Button - disabled={dids.length === 0} - variant="gradient" - color="gradient_sky" - size="large" - label={_( - msg`Follow selected accounts and continue to the next step`, - )} - onPress={handleContinue}> - <ButtonText> - {dids.length === 20 ? ( - <Trans>Follow All</Trans> - ) : ( - <Trans>Follow</Trans> - )} - </ButtonText> - <ButtonIcon icon={saving ? Loader : Plus} position="right" /> - </Button> - <Button - variant="solid" - color="secondary" - size="large" - label={_( - msg`Continue to the next step without following any accounts`, - )} - onPress={handleSkip}> - <ButtonText> - <Trans>Skip</Trans> - </ButtonText> - </Button> - </View> - </OnboardingControls.Portal> - </View> - ) -} |