diff --git a/src/lib/strings/capitalize.ts b/src/lib/strings/capitalize.ts new file mode 100644 index 00000000..a3415bd0 --- /dev/null +++ b/src/lib/strings/capitalize.ts @@ -0,0 +1,3 @@ +export function capitalize(str: string) { + return str.charAt(0).toUpperCase() + str.slice(1) +} diff --git a/src/screens/Onboarding/StepInterests/InterestButton.tsx b/src/screens/Onboarding/StepInterests/InterestButton.tsx index 02413b18..cc692daf 100644 --- a/src/screens/Onboarding/StepInterests/InterestButton.tsx +++ b/src/screens/Onboarding/StepInterests/InterestButton.tsx @@ -4,11 +4,13 @@ import {View, ViewStyle, TextStyle} from 'react-native' import {useTheme, atoms as a, native} from '#/alf' import * as Toggle from '#/components/forms/Toggle' import {Text} from '#/components/Typography' +import {capitalize} from '#/lib/strings/capitalize' -import {INTEREST_TO_DISPLAY_NAME} from '#/screens/Onboarding/StepInterests/data' +import {Context} from '#/screens/Onboarding/state' export function InterestButton({interest}: {interest: string}) { const t = useTheme() + const {interestsDisplayNames} = React.useContext(Context) const ctx = Toggle.useItemContext() const styles = React.useMemo(() => { @@ -72,7 +74,7 @@ export function InterestButton({interest}: {interest: string}) { native({paddingTop: 2}), ctx.selected ? styles.textSelected : {}, ]}> - {INTEREST_TO_DISPLAY_NAME[interest]} + {interestsDisplayNames[interest] || capitalize(interest)} ) diff --git a/src/screens/Onboarding/StepInterests/data.ts b/src/screens/Onboarding/StepInterests/data.ts deleted file mode 100644 index 00a25331..00000000 --- a/src/screens/Onboarding/StepInterests/data.ts +++ /dev/null @@ -1,36 +0,0 @@ -export const INTEREST_TO_DISPLAY_NAME: { - [key: string]: string -} = { - news: 'News', - journalism: 'Journalism', - nature: 'Nature', - art: 'Art', - comics: 'Comics', - writers: 'Writers', - culture: 'Culture', - sports: 'Sports', - pets: 'Pets', - animals: 'Animals', - books: 'Books', - education: 'Education', - climate: 'Climate', - science: 'Science', - politics: 'Politics', - fitness: 'Fitness', - tech: 'Tech', - dev: 'Software Dev', - comedy: 'Comedy', - gaming: 'Video Games', - food: 'Food', - cooking: 'Cooking', -} - -export type ApiResponseMap = { - interests: string[] - suggestedAccountDids: { - [key: string]: string[] - } - suggestedFeedUris: { - [key: string]: string[] - } -} diff --git a/src/screens/Onboarding/StepInterests/index.tsx b/src/screens/Onboarding/StepInterests/index.tsx index 6f60991d..5440dcd2 100644 --- a/src/screens/Onboarding/StepInterests/index.tsx +++ b/src/screens/Onboarding/StepInterests/index.tsx @@ -17,17 +17,14 @@ import {getAgent} from '#/state/session' import {useAnalytics} from '#/lib/analytics/analytics' import {Text} from '#/components/Typography' import {useOnboardingDispatch} from '#/state/shell' +import {capitalize} from '#/lib/strings/capitalize' -import {Context} from '#/screens/Onboarding/state' +import {Context, ApiResponseMap} from '#/screens/Onboarding/state' import { Title, Description, OnboardingControls, } from '#/screens/Onboarding/Layout' -import { - ApiResponseMap, - INTEREST_TO_DISPLAY_NAME, -} from '#/screens/Onboarding/StepInterests/data' import {InterestButton} from '#/screens/Onboarding/StepInterests/InterestButton' import {IconCircle} from '#/screens/Onboarding/IconCircle' @@ -36,7 +33,7 @@ export function StepInterests() { const t = useTheme() const {track} = useAnalytics() const {gtMobile} = useBreakpoints() - const {state, dispatch} = React.useContext(Context) + const {state, dispatch, interestsDisplayNames} = React.useContext(Context) const [saving, setSaving] = React.useState(false) const [interests, setInterests] = React.useState( state.interestsStepResults.selectedInterests.map(i => i), @@ -202,7 +199,9 @@ export function StepInterests() { + label={ + interestsDisplayNames[interest] || capitalize(interest) + }> ))} diff --git a/src/screens/Onboarding/StepSuggestedAccounts/index.tsx b/src/screens/Onboarding/StepSuggestedAccounts/index.tsx index d3831791..cf1f8255 100644 --- a/src/screens/Onboarding/StepSuggestedAccounts/index.tsx +++ b/src/screens/Onboarding/StepSuggestedAccounts/index.tsx @@ -14,6 +14,7 @@ import {Loader} from '#/components/Loader' import * as Toggle from '#/components/forms/Toggle' import {useModerationOpts} from '#/state/queries/preferences' import {useAnalytics} from '#/lib/analytics/analytics' +import {capitalize} from '#/lib/strings/capitalize' import {Context} from '#/screens/Onboarding/state' import { @@ -25,7 +26,6 @@ import { SuggestedAccountCard, SuggestedAccountCardPlaceholder, } from '#/screens/Onboarding/StepSuggestedAccounts/SuggestedAccountCard' -import {INTEREST_TO_DISPLAY_NAME} from '#/screens/Onboarding/StepInterests/data' import {aggregateInterestItems} from '#/screens/Onboarding/util' import {IconCircle} from '#/screens/Onboarding/IconCircle' @@ -70,7 +70,7 @@ export function Inner({ export function StepSuggestedAccounts() { const {_} = useLingui() const {track} = useAnalytics() - const {state, dispatch} = React.useContext(Context) + const {state, dispatch, interestsDisplayNames} = React.useContext(Context) const {gtMobile} = useBreakpoints() const suggestedDids = React.useMemo(() => { return aggregateInterestItems( @@ -93,10 +93,10 @@ export function StepSuggestedAccounts() { const interestsText = React.useMemo(() => { const i = state.interestsStepResults.selectedInterests.map( - i => INTEREST_TO_DISPLAY_NAME[i], + i => interestsDisplayNames[i] || capitalize(i), ) return i.join(', ') - }, [state.interestsStepResults.selectedInterests]) + }, [state.interestsStepResults.selectedInterests, interestsDisplayNames]) const handleContinue = React.useCallback(async () => { setSaving(true) diff --git a/src/screens/Onboarding/StepTopicalFeeds.tsx b/src/screens/Onboarding/StepTopicalFeeds.tsx index 516c18e6..ef77cc12 100644 --- a/src/screens/Onboarding/StepTopicalFeeds.tsx +++ b/src/screens/Onboarding/StepTopicalFeeds.tsx @@ -10,6 +10,7 @@ import {Button, ButtonIcon, ButtonText} from '#/components/Button' import * as Toggle from '#/components/forms/Toggle' import {Loader} from '#/components/Loader' import {useAnalytics} from '#/lib/analytics/analytics' +import {capitalize} from '#/lib/strings/capitalize' import {Context} from '#/screens/Onboarding/state' import { @@ -18,14 +19,13 @@ import { OnboardingControls, } from '#/screens/Onboarding/Layout' import {FeedCard} from '#/screens/Onboarding/StepAlgoFeeds/FeedCard' -import {INTEREST_TO_DISPLAY_NAME} from '#/screens/Onboarding/StepInterests/data' import {aggregateInterestItems} from '#/screens/Onboarding/util' import {IconCircle} from '#/screens/Onboarding/IconCircle' export function StepTopicalFeeds() { const {_} = useLingui() const {track} = useAnalytics() - const {state, dispatch} = React.useContext(Context) + const {state, dispatch, interestsDisplayNames} = React.useContext(Context) const [selectedFeedUris, setSelectedFeedUris] = React.useState([]) const [saving, setSaving] = React.useState(false) const suggestedFeedUris = React.useMemo(() => { @@ -38,10 +38,10 @@ export function StepTopicalFeeds() { const interestsText = React.useMemo(() => { const i = state.interestsStepResults.selectedInterests.map( - i => INTEREST_TO_DISPLAY_NAME[i], + i => interestsDisplayNames[i] || capitalize(i), ) return i.join(', ') - }, [state.interestsStepResults.selectedInterests]) + }, [state.interestsStepResults.selectedInterests, interestsDisplayNames]) const saveFeeds = React.useCallback(async () => { setSaving(true) diff --git a/src/screens/Onboarding/index.tsx b/src/screens/Onboarding/index.tsx index a4eb0401..9e5029e8 100644 --- a/src/screens/Onboarding/index.tsx +++ b/src/screens/Onboarding/index.tsx @@ -1,4 +1,6 @@ import React from 'react' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' import {Portal} from '#/components/Portal' @@ -13,13 +15,44 @@ import {StepFinished} from '#/screens/Onboarding/StepFinished' import {StepModeration} from '#/screens/Onboarding/StepModeration' export function Onboarding() { + const {_} = useLingui() const [state, dispatch] = React.useReducer(reducer, {...initialState}) + const interestsDisplayNames = React.useMemo(() => { + return { + news: _(msg`News`), + journalism: _(msg`Journalism`), + nature: _(msg`Nature`), + art: _(msg`Art`), + comics: _(msg`Comics`), + writers: _(msg`Writers`), + culture: _(msg`Culture`), + sports: _(msg`Sports`), + pets: _(msg`Pets`), + animals: _(msg`Animals`), + books: _(msg`Books`), + education: _(msg`Education`), + climate: _(msg`Climate`), + science: _(msg`Science`), + politics: _(msg`Politics`), + fitness: _(msg`Fitness`), + tech: _(msg`Tech`), + dev: _(msg`Software Dev`), + comedy: _(msg`Comedy`), + gaming: _(msg`Video Games`), + food: _(msg`Food`), + cooking: _(msg`Cooking`), + } + }, [_]) + return ( ({state, dispatch}), [state, dispatch])}> + value={React.useMemo( + () => ({state, dispatch, interestsDisplayNames}), + [state, dispatch, interestsDisplayNames], + )}> {state.activeStep === 'interests' && } {state.activeStep === 'suggestedAccounts' && ( diff --git a/src/screens/Onboarding/state.ts b/src/screens/Onboarding/state.ts index 164c2f5f..bd8205ca 100644 --- a/src/screens/Onboarding/state.ts +++ b/src/screens/Onboarding/state.ts @@ -1,6 +1,5 @@ import React from 'react' -import {ApiResponseMap} from '#/screens/Onboarding/StepInterests/data' import {logger} from '#/logger' export type OnboardingState = { @@ -59,6 +58,16 @@ export type OnboardingAction = feedUris: string[] } +export type ApiResponseMap = { + interests: string[] + suggestedAccountDids: { + [key: string]: string[] + } + suggestedFeedUris: { + [key: string]: string[] + } +} + export const initialState: OnboardingState = { hasPrev: false, totalSteps: 7, @@ -84,12 +93,41 @@ export const initialState: OnboardingState = { }, } +export const INTEREST_TO_DISPLAY_NAME_DEFAULTS: { + [key: string]: string +} = { + news: 'News', + journalism: 'Journalism', + nature: 'Nature', + art: 'Art', + comics: 'Comics', + writers: 'Writers', + culture: 'Culture', + sports: 'Sports', + pets: 'Pets', + animals: 'Animals', + books: 'Books', + education: 'Education', + climate: 'Climate', + science: 'Science', + politics: 'Politics', + fitness: 'Fitness', + tech: 'Tech', + dev: 'Software Dev', + comedy: 'Comedy', + gaming: 'Video Games', + food: 'Food', + cooking: 'Cooking', +} + export const Context = React.createContext<{ state: OnboardingState dispatch: React.Dispatch + interestsDisplayNames: {[key: string]: string} }>({ state: {...initialState}, dispatch: () => {}, + interestsDisplayNames: INTEREST_TO_DISPLAY_NAME_DEFAULTS, }) export function reducer( diff --git a/src/screens/Onboarding/util.ts b/src/screens/Onboarding/util.ts index 2a709a67..eae661aa 100644 --- a/src/screens/Onboarding/util.ts +++ b/src/screens/Onboarding/util.ts @@ -31,7 +31,15 @@ export function aggregateInterestItems( const selected = interests.length const all = interests .map(i => { - const suggestions = shuffle(map[i]) + // suggestions from server + const rawSuggestions = map[i] + + // safeguard against a missing interest->suggestion mapping + if (!rawSuggestions || !rawSuggestions.length) { + return [] + } + + const suggestions = shuffle(rawSuggestions) if (selected === 1) { return suggestions // return all