This commit is contained in:
Samuel Newman
2025-06-30 11:34:51 +03:00
parent 130f795dfb
commit 9dd1eae498
+146 -117
View File
@@ -1,5 +1,11 @@
import {useMemo, useState} from 'react' import {useMemo, useState} from 'react'
import {type TextStyle, View, type ViewStyle} from 'react-native' import {type TextStyle, View, type ViewStyle} from 'react-native'
import Animated, {
FadeIn,
FadeInDown,
FadeOut,
LinearTransition,
} from 'react-native-reanimated'
import {msg, Trans} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {type NativeStackScreenProps} from '@react-navigation/native-stack' import {type NativeStackScreenProps} from '@react-navigation/native-stack'
@@ -46,20 +52,6 @@ export function InterestsSettingsScreen({}: Props) {
</Layout.Header.Outer> </Layout.Header.Outer>
<Layout.Content> <Layout.Content>
<View style={[gutters, a.gap_lg]}> <View style={[gutters, a.gap_lg]}>
<Text
style={[
a.flex_1,
a.text_sm,
a.leading_snug,
t.atoms.text_contrast_medium,
]}>
<Trans>
Your selected interests help us serve you content you care about.
</Trans>
</Text>
<Divider />
{preferences ? ( {preferences ? (
<Inner preferences={preferences} setIsSaving={setIsSaving} /> <Inner preferences={preferences} setIsSaving={setIsSaving} />
) : ( ) : (
@@ -73,96 +65,33 @@ export function InterestsSettingsScreen({}: Props) {
) )
} }
function Inner({ function Inner({}: {
preferences,
setIsSaving,
}: {
preferences: UsePreferencesQueryResponse preferences: UsePreferencesQueryResponse
setIsSaving: (isSaving: boolean) => void setIsSaving: (isSaving: boolean) => void
}) { }) {
const {_} = useLingui() const {_} = useLingui()
const agent = useAgent()
const qc = useQueryClient()
const interestsDisplayNames = useInterestsDisplayNames() const interestsDisplayNames = useInterestsDisplayNames()
const preselectedInterests = useMemo( const [interests, setInterests] = useState<string[]>([])
() => preferences.interests.tags || [],
[preferences.interests.tags],
)
const [interests, setInterests] = useState<string[]>(preselectedInterests)
const saveInterests = useMemo(() => {
return debounce(async (interests: string[]) => {
const noEdits =
interests.length === preselectedInterests.length &&
preselectedInterests.every(pre => {
return interests.find(int => int === pre)
})
if (noEdits) return
setIsSaving(true)
try {
await agent.setInterestsPref({tags: interests})
qc.setQueriesData(
{queryKey: preferencesQueryKey},
(old?: UsePreferencesQueryResponse) => {
if (!old) return old
old.interests.tags = interests
return old
},
)
await Promise.all([
qc.resetQueries({queryKey: createSuggestedStarterPacksQueryKey()}),
qc.resetQueries({queryKey: createGetSuggestedFeedsQueryKey()}),
qc.resetQueries({queryKey: createGetSuggestedUsersQueryKey({})}),
])
Toast.show(
_(
msg({
message: 'Your interests have been updated!',
context: 'toast',
}),
),
)
} catch (error) {
Toast.show(
_(
msg({
message: 'Failed to save your interests.',
context: 'toast',
}),
),
'xmark',
)
} finally {
setIsSaving(false)
}
}, 1500)
}, [_, agent, setIsSaving, qc, preselectedInterests])
const onChangeInterests = async (interests: string[]) => { const onChangeInterests = async (interests: string[]) => {
setInterests(interests) setInterests(interests)
saveInterests(interests)
} }
const additional = interests.filter(
interest =>
Object.keys(findRecursive(interest, INTERESTS) ?? {}).length > 0,
)
console.log('react', findRecursive('react', INTERESTS))
return ( return (
<> <>
{interests.length === 0 && (
<Admonition type="tip">
<Trans>We recommend selecting at least two interests.</Trans>
</Admonition>
)}
<Toggle.Group <Toggle.Group
values={interests} values={interests}
onChange={onChangeInterests} onChange={onChangeInterests}
label={_(msg`Select your interests from the options below`)}> label={_(msg`Select your interests from the options below`)}>
<View style={[a.flex_row, a.flex_wrap, a.gap_sm]}> <View style={[a.flex_row, a.flex_wrap, a.gap_sm]}>
{INTERESTS.map(interest => { {Object.keys(INTERESTS).map(interest => {
const name = interestsDisplayNames[interest]
if (!name) return null
return ( return (
<Toggle.Item <Toggle.Item
key={interest} key={interest}
@@ -173,6 +102,56 @@ function Inner({
) )
})} })}
</View> </View>
{additional.map(interest => {
const found = findRecursive(interest, INTERESTS) ?? {}
console.log(interest)
console.log(found)
console.log(
interests.map(
interest => interest + findRecursive(interest, found),
),
)
console.log(
interests
.map(interest =>
Object.keys(findRecursive(interest, found) ?? {}),
)
.filter(x => Boolean(x) && x.length > 0)
.flat(),
)
const pills = [
...Object.keys(found),
...interests
.map(interest =>
Object.keys(findRecursive(interest, found) ?? {}),
)
.filter(x => Boolean(x) && x.length > 0)
.flat(),
]
return (
<Animated.View
key={interest}
style={[a.mt_xl]}
entering={FadeInDown}
exiting={FadeOut}
layout={LinearTransition.duration(100)}>
<Text style={[a.font_heavy, a.text_lg, a.mb_lg]}>{interest}</Text>
<View style={[a.flex_row, a.flex_wrap, a.gap_sm]}>
{pills.map((interest, index) => {
return (
<Animated.View
key={interest}
entering={FadeIn.delay(index * 100)}>
<Toggle.Item name={interest} label={interest}>
<InterestButton interest={interest} />
</Toggle.Item>
</Animated.View>
)
})}
</View>
</Animated.View>
)
})}
</Toggle.Group> </Toggle.Group>
</> </>
) )
@@ -180,7 +159,6 @@ function Inner({
export function InterestButton({interest}: {interest: string}) { export function InterestButton({interest}: {interest: string}) {
const t = useTheme() const t = useTheme()
const interestsDisplayNames = useInterestsDisplayNames()
const ctx = Toggle.useItemContext() const ctx = Toggle.useItemContext()
const styles = useMemo(() => { const styles = useMemo(() => {
@@ -219,40 +197,91 @@ export function InterestButton({interest}: {interest: string}) {
<Text <Text
selectable={false} selectable={false}
style={[ style={[
{ {color: t.palette.contrast_900},
color: t.palette.contrast_900,
},
a.font_bold, a.font_bold,
ctx.selected ? styles.textSelected : {}, ctx.selected ? styles.textSelected : {},
]}> ]}>
{interestsDisplayNames[interest]} {interest}
</Text> </Text>
</View> </View>
) )
} }
const INTERESTS = [ type Interests = Record<
'animals', string,
'art', {} | Record<string, {} | Record<string, {} | Record<string, {}>>>
'books', >
'comedy',
'comics', function findRecursive(
'culture', interest: string,
'dev', interests: Interests,
'education', ): Interests | null {
'food', if (interests[interest]) return interests[interest]
'gaming', for (const key in interests) {
'journalism', const found = findRecursive(key, interests[key])
'movies', if (found) return found
'music', }
'nature', return null
'news', }
'pets',
'photography', const INTERESTS = {
'politics', animals: {},
'science', art: {
'sports', comics: {},
'tech', illustration: {
'tv', 'digital art': {},
'writers', 'hand drawn': {},
] 'furry art': {},
},
painting: {
watercolor: {},
'fine art': {},
},
'fan art': {},
sculpture: {},
'art history': {},
},
books: {},
comedy: {},
culture: {},
dev: {
'web dev': {
design: {},
css: {},
react: {
'react native': {},
},
svelte: {},
typescript: {},
},
'game dev': {},
atproto: {},
},
education: {},
food: {},
gaming: {},
journalism: {},
movies: {},
music: {},
nature: {},
news: {},
pets: {
cats: {},
dogs: {},
birds: {
parrots: {},
canaries: {},
finches: {},
},
fish: {},
reptiles: {},
bunnies: {},
},
photography: {},
politics: {},
science: {},
sports: {},
tech: {},
tv: {},
writers: {},
}