add state context, pressables

This commit is contained in:
Hailey
2024-02-13 11:44:53 -08:00
parent f553377e94
commit 805fffe5d8
+110 -54
View File
@@ -2,7 +2,9 @@ import React from 'react'
import { import {
ColorValue, ColorValue,
FlatList, FlatList,
FlatListProps,
ListRenderItemInfo, ListRenderItemInfo,
Pressable,
StyleSheet, StyleSheet,
View, View,
} from 'react-native' } from 'react-native'
@@ -34,10 +36,17 @@ type AvatarPlaceholder = 'thinking' | 'heart' | 'laughing'
interface Avatar { interface Avatar {
imageUri?: string imageUri?: string
backgroundColor?: ColorValue backgroundColor?: Color
placeholder?: AvatarPlaceholder placeholder?: Emoji
} }
const AvatarContext = React.createContext<Avatar>({} as Avatar)
const SetAvatarContext = React.createContext<
React.Dispatch<React.SetStateAction<Avatar>>
>({} as React.Dispatch<React.SetStateAction<Avatar>>)
const useAvatar = () => React.useContext(AvatarContext)
const useSetAvatar = () => React.useContext(SetAvatarContext)
export function StepProfile() { export function StepProfile() {
const {_} = useLingui() const {_} = useLingui()
const t = useTheme() const t = useTheme()
@@ -56,46 +65,46 @@ export function StepProfile() {
}, [track, dispatch]) }, [track, dispatch])
return ( return (
<View style={[a.align_start]}> <SetAvatarContext.Provider value={setAvatar}>
<IconCircle icon={StreamingLive} style={[a.mb_2xl]} /> <AvatarContext.Provider value={avatar}>
<View style={[a.align_start]}>
<IconCircle icon={StreamingLive} style={[a.mb_2xl]} />
<Title> <Title>
<Trans>Upload a profile picture</Trans> <Trans>Upload a profile picture</Trans>
</Title> </Title>
<Description> <Description>
<Trans>Help people know you're not a bot!</Trans> <Trans>Help people know you're not a bot!</Trans>
</Description> </Description>
<View style={[a.pt_5xl, a.gap_3xl]}> <View style={[a.pt_5xl, a.gap_3xl]}>
<Items type="colors" /> <Items type="emojis" />
<Items type="emojis" /> <Items type="colors" />
</View> </View>
<OnboardingControls.Portal> <OnboardingControls.Portal>
<Button <Button
key={state.activeStep} // remove focus state on nav key={state.activeStep} // remove focus state on nav
variant="gradient" variant="gradient"
color="gradient_sky" color="gradient_sky"
size="large" size="large"
label={_(msg`Continue to next step`)} label={_(msg`Continue to next step`)}
onPress={onContinue}> onPress={onContinue}>
<ButtonText> <ButtonText>
<Trans>Continue</Trans> <Trans>Continue</Trans>
</ButtonText> </ButtonText>
<ButtonIcon icon={ChevronRight} position="right" /> <ButtonIcon icon={ChevronRight} position="right" />
</Button> </Button>
</OnboardingControls.Portal> </OnboardingControls.Portal>
</View> </View>
</AvatarContext.Provider>
</SetAvatarContext.Provider>
) )
} }
function AvatarCircle({ function AvatarCircle() {
avatar, const avatar = useAvatar()
setAvatar,
}: {
avatar: Avatar
setAvatar: React.Dispatch<React.SetStateAction<Avatar>>
}) {
if (avatar.imageUri) { if (avatar.imageUri) {
return ( return (
<View style={[styles.imageContainer]}> <View style={[styles.imageContainer]}>
@@ -127,15 +136,30 @@ const emojiItems: Record<Emoji, string> = {
} }
function ColorItem({color}: {color: Color}) { function ColorItem({color}: {color: Color}) {
const avatar = useAvatar()
const setAvatar = useSetAvatar()
const onPress = React.useCallback(() => {
setAvatar(prev => ({
...prev,
backgroundColor: color,
}))
}, [color, setAvatar])
if (color === 'image') return null if (color === 'image') return null
return ( return (
<View <Pressable
accessibilityRole="button"
style={[ style={[
styles.imageContainer, styles.imageContainer,
styles.paletteContainer, styles.paletteContainer,
{backgroundColor: color}, {
backgroundColor: color,
borderWidth: avatar.backgroundColor === color ? 3 : 1,
},
]} ]}
onPress={onPress}
/> />
) )
} }
@@ -144,10 +168,31 @@ function colorRenderItem({item}: ListRenderItemInfo<Color>) {
} }
function EmojiItem({emoji}: {emoji: Emoji}) { function EmojiItem({emoji}: {emoji: Emoji}) {
const avatar = useAvatar()
const setAvatar = useSetAvatar()
const onPress = React.useCallback(() => {
setAvatar(prev => ({
...prev,
placeholder: emoji,
}))
}, [emoji, setAvatar])
return ( return (
<View style={[styles.imageContainer, styles.paletteContainer]}> <Pressable
<Text>{emojiItems[emoji]}</Text> accessibilityRole="button"
</View> style={[
styles.imageContainer,
styles.paletteContainer,
{
alignItems: 'center',
justifyContent: 'center',
borderWidth: avatar.placeholder === emoji ? 3 : 1,
},
]}
onPress={onPress}>
<Text style={[a.text_5xl]}>{emojiItems[emoji]}</Text>
</Pressable>
) )
} }
function emojiRenderItem({item}: ListRenderItemInfo<Emoji>) { function emojiRenderItem({item}: ListRenderItemInfo<Emoji>) {
@@ -157,23 +202,25 @@ function emojiRenderItem({item}: ListRenderItemInfo<Emoji>) {
function Items({type}: {type: 'emojis' | 'colors'}) { function Items({type}: {type: 'emojis' | 'colors'}) {
if (type === 'colors') { if (type === 'colors') {
return ( return (
<FlatList<Color> <View style={styles.flatListOuter}>
style={{height: 100}} <FlatList<Color>
data={colors} style={{height: 100}}
renderItem={colorRenderItem} data={colors}
horizontal renderItem={colorRenderItem}
contentContainerStyle={styles.flatListContainer} {...commonFlatListProps}
/> />
</View>
) )
} }
return ( return (
<FlatList<Emoji> <View style={styles.flatListOuter}>
data={emojis} <FlatList<Emoji>
renderItem={emojiRenderItem} data={emojis}
horizontal renderItem={emojiRenderItem}
contentContainerStyle={styles.flatListContainer} {...commonFlatListProps}
/> />
</View>
) )
} }
@@ -189,8 +236,17 @@ const styles = StyleSheet.create({
width: 90, width: 90,
marginHorizontal: 5, marginHorizontal: 5,
}, },
flatListOuter: {
height: 100,
},
flatListContainer: { flatListContainer: {
paddingLeft: 40, paddingLeft: 40,
paddingRight: 40, paddingRight: 40,
}, },
}) })
const commonFlatListProps: Partial<FlatListProps<any>> = {
horizontal: true,
contentContainerStyle: styles.flatListContainer,
showsHorizontalScrollIndicator: false,
}