i18n interests, allow for fallbacks (#2692)

zio/stable
Eric Bailey 2024-01-30 17:04:54 -06:00 committed by GitHub
parent 4058174678
commit bb7ce215f7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 103 additions and 56 deletions

View File

@ -0,0 +1,3 @@
export function capitalize(str: string) {
return str.charAt(0).toUpperCase() + str.slice(1)
}

View File

@ -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)}
</Text>
</View>
)

View File

@ -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[]
}
}

View File

@ -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<string[]>(
state.interestsStepResults.selectedInterests.map(i => i),
@ -202,7 +199,9 @@ export function StepInterests() {
<Toggle.Item
key={interest}
name={interest}
label={INTEREST_TO_DISPLAY_NAME[interest]}>
label={
interestsDisplayNames[interest] || capitalize(interest)
}>
<InterestButton interest={interest} />
</Toggle.Item>
))}

View File

@ -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)

View File

@ -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<string[]>([])
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)

View File

@ -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 (
<Portal>
<OnboardingControls.Provider>
<Context.Provider
value={React.useMemo(() => ({state, dispatch}), [state, dispatch])}>
value={React.useMemo(
() => ({state, dispatch, interestsDisplayNames}),
[state, dispatch, interestsDisplayNames],
)}>
<Layout>
{state.activeStep === 'interests' && <StepInterests />}
{state.activeStep === 'suggestedAccounts' && (

View File

@ -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<OnboardingAction>
interestsDisplayNames: {[key: string]: string}
}>({
state: {...initialState},
dispatch: () => {},
interestsDisplayNames: INTEREST_TO_DISPLAY_NAME_DEFAULTS,
})
export function reducer(

View File

@ -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