-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Reduced Onboarding] Add profile step (#3933)
* Onboarding avatar creator or upload (#2860) * add screen to onboarding flow * update base * add icon * fix icon * fix after merge * create flatlist * add emoji list * add state context, pressables * select/update * add camera icon * add photo selection button * image selection * cleanup * add most needed icons * fix icon naming * add icons * export path strings for emoji * canvas drawing for web * types * move breakpoints to individual steps * create canvas * canvas working 🎉 * update state * it works! * working on both platforms * remove comments * remove log * remove unused web canvas * animate picture selection/removal * compress images on web correctly * add times icon * scrollable horizontal flatlist on web * prefetch * adjustments * add more assets * remove unused smiles * add all the icons * adjust color options * animate grow/shrink selections * change layout on tablet/desktop * better web layout * fix path * adjust web layout * organize * organize imports and cleanup styles * make generated images smaller * implement design changes use row for buttons on web use RNGH FlatList random color at start improve logic update dialog for web update dialog style on mobile some more progress create dialog simplify context start implementing design * rm change * cleanup imports * trigger a pr label * Formatting --------- Co-authored-by: Eric Bailey <[email protected]> (cherry picked from commit 087186e) * UI tweaks * Revert layout change * Gate avi upload * Support returning to profile step * Add Statsig --------- Co-authored-by: Hailey <[email protected]> Co-authored-by: Dan Abramov <[email protected]>
- Loading branch information
1 parent
80ce6f9
commit 4e37e2f
Showing
17 changed files
with
878 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import React from 'react' | ||
import {View} from 'react-native' | ||
import {Image as ExpoImage} from 'expo-image' | ||
import {msg} from '@lingui/macro' | ||
import {useLingui} from '@lingui/react' | ||
|
||
import {AvatarCreatorCircle} from '#/screens/Onboarding/StepProfile/AvatarCreatorCircle' | ||
import {useAvatar} from '#/screens/Onboarding/StepProfile/index' | ||
import {atoms as a, useTheme} from '#/alf' | ||
import {Button, ButtonIcon} from '#/components/Button' | ||
import {Pencil_Stroke2_Corner0_Rounded as Pencil} from '#/components/icons/Pencil' | ||
import {StreamingLive_Stroke2_Corner0_Rounded as StreamingLive} from '#/components/icons/StreamingLive' | ||
|
||
export function AvatarCircle({ | ||
openLibrary, | ||
openCreator, | ||
}: { | ||
openLibrary: () => unknown | ||
openCreator: () => unknown | ||
}) { | ||
const {_} = useLingui() | ||
const t = useTheme() | ||
const {avatar} = useAvatar() | ||
|
||
const styles = React.useMemo( | ||
() => ({ | ||
imageContainer: [ | ||
a.rounded_full, | ||
a.overflow_hidden, | ||
a.align_center, | ||
a.justify_center, | ||
a.border, | ||
t.atoms.border_contrast_low, | ||
t.atoms.bg_contrast_25, | ||
{ | ||
height: 200, | ||
width: 200, | ||
}, | ||
], | ||
}), | ||
[t.atoms.bg_contrast_25, t.atoms.border_contrast_low], | ||
) | ||
|
||
return ( | ||
<View> | ||
{avatar.useCreatedAvatar ? ( | ||
<AvatarCreatorCircle avatar={avatar} size={200} /> | ||
) : avatar.image ? ( | ||
<ExpoImage | ||
source={avatar.image.path} | ||
style={styles.imageContainer} | ||
accessibilityIgnoresInvertColors | ||
transition={{duration: 300, effect: 'cross-dissolve'}} | ||
/> | ||
) : ( | ||
<View style={styles.imageContainer}> | ||
<StreamingLive | ||
height={100} | ||
width={100} | ||
style={{color: t.palette.contrast_200}} | ||
/> | ||
</View> | ||
)} | ||
<View style={[a.absolute, {bottom: 2, right: 2}]}> | ||
<Button | ||
label={_(msg`Select an avatar`)} | ||
size="large" | ||
shape="round" | ||
variant="solid" | ||
color="primary" | ||
onPress={avatar.useCreatedAvatar ? openCreator : openLibrary}> | ||
<ButtonIcon icon={Pencil} /> | ||
</Button> | ||
</View> | ||
</View> | ||
) | ||
} |
43 changes: 43 additions & 0 deletions
43
src/screens/Onboarding/StepProfile/AvatarCreatorCircle.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import React from 'react' | ||
import {View} from 'react-native' | ||
|
||
import {Avatar} from '#/screens/Onboarding/StepProfile/index' | ||
import {atoms as a, useTheme} from '#/alf' | ||
|
||
export function AvatarCreatorCircle({ | ||
avatar, | ||
size = 125, | ||
}: { | ||
avatar: Avatar | ||
size?: number | ||
}) { | ||
const t = useTheme() | ||
const Icon = avatar.placeholder.component | ||
|
||
const styles = React.useMemo( | ||
() => ({ | ||
imageContainer: [ | ||
a.rounded_full, | ||
a.overflow_hidden, | ||
a.align_center, | ||
a.justify_center, | ||
a.border, | ||
t.atoms.border_contrast_high, | ||
{ | ||
height: size, | ||
width: size, | ||
backgroundColor: avatar.backgroundColor, | ||
}, | ||
], | ||
}), | ||
[avatar.backgroundColor, size, t.atoms.border_contrast_high], | ||
) | ||
|
||
return ( | ||
<View> | ||
<View style={styles.imageContainer}> | ||
<Icon height={85} width={85} style={{color: t.palette.white}} /> | ||
</View> | ||
</View> | ||
) | ||
} |
145 changes: 145 additions & 0 deletions
145
src/screens/Onboarding/StepProfile/AvatarCreatorItems.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import React from 'react' | ||
import {View} from 'react-native' | ||
import {msg, Trans} from '@lingui/macro' | ||
import {useLingui} from '@lingui/react' | ||
|
||
import {Avatar} from '#/screens/Onboarding/StepProfile/index' | ||
import { | ||
AvatarColor, | ||
avatarColors, | ||
emojiItems, | ||
EmojiName, | ||
emojiNames, | ||
} from '#/screens/Onboarding/StepProfile/types' | ||
import {atoms as a, useTheme} from '#/alf' | ||
import {Button, ButtonIcon} from '#/components/Button' | ||
import {Text} from '#/components/Typography' | ||
|
||
const ACTIVE_BORDER_WIDTH = 3 | ||
const ACTIVE_BORDER_STYLES = { | ||
top: -ACTIVE_BORDER_WIDTH, | ||
bottom: -ACTIVE_BORDER_WIDTH, | ||
left: -ACTIVE_BORDER_WIDTH, | ||
right: -ACTIVE_BORDER_WIDTH, | ||
opacity: 0.5, | ||
borderWidth: 3, | ||
} | ||
|
||
export function AvatarCreatorItems({ | ||
type, | ||
avatar, | ||
setAvatar, | ||
}: { | ||
type: 'emojis' | 'colors' | ||
avatar: Avatar | ||
setAvatar: React.Dispatch<React.SetStateAction<Avatar>> | ||
}) { | ||
const {_} = useLingui() | ||
const t = useTheme() | ||
const isEmojis = type === 'emojis' | ||
|
||
const onSelectEmoji = React.useCallback( | ||
(emoji: EmojiName) => { | ||
setAvatar(prev => ({ | ||
...prev, | ||
placeholder: emojiItems[emoji], | ||
})) | ||
}, | ||
[setAvatar], | ||
) | ||
|
||
const onSelectColor = React.useCallback( | ||
(color: AvatarColor) => { | ||
setAvatar(prev => ({ | ||
...prev, | ||
backgroundColor: color, | ||
})) | ||
}, | ||
[setAvatar], | ||
) | ||
|
||
return ( | ||
<View style={[a.w_full]}> | ||
<Text style={[a.pb_md, t.atoms.text_contrast_medium]}> | ||
{isEmojis ? ( | ||
<Trans>Select an emoji</Trans> | ||
) : ( | ||
<Trans>Select a color</Trans> | ||
)} | ||
</Text> | ||
|
||
<View | ||
style={[ | ||
a.flex_row, | ||
a.align_start, | ||
a.justify_start, | ||
a.flex_wrap, | ||
a.gap_md, | ||
]}> | ||
{isEmojis | ||
? emojiNames.map(emojiName => ( | ||
<Button | ||
key={emojiName} | ||
label={_(msg`Select the ${emojiName} emoji as your avatar`)} | ||
size="small" | ||
shape="round" | ||
variant="solid" | ||
color="secondary" | ||
onPress={() => onSelectEmoji(emojiName)}> | ||
<ButtonIcon icon={emojiItems[emojiName].component} /> | ||
{avatar.placeholder.name === emojiName && ( | ||
<View | ||
style={[ | ||
a.absolute, | ||
a.rounded_full, | ||
ACTIVE_BORDER_STYLES, | ||
{ | ||
borderColor: avatar.backgroundColor, | ||
}, | ||
]} | ||
/> | ||
)} | ||
</Button> | ||
)) | ||
: avatarColors.map(color => ( | ||
<Button | ||
key={color} | ||
label={_(msg`Choose this color as your avatar`)} | ||
size="small" | ||
shape="round" | ||
variant="solid" | ||
onPress={() => onSelectColor(color)}> | ||
{ctx => ( | ||
<> | ||
<View | ||
style={[ | ||
a.absolute, | ||
a.inset_0, | ||
a.rounded_full, | ||
{ | ||
opacity: ctx.hovered || ctx.pressed ? 0.8 : 1, | ||
backgroundColor: color, | ||
}, | ||
]} | ||
/> | ||
|
||
{avatar.backgroundColor === color && ( | ||
<View | ||
style={[ | ||
a.absolute, | ||
a.rounded_full, | ||
ACTIVE_BORDER_STYLES, | ||
{ | ||
borderColor: color, | ||
}, | ||
]} | ||
/> | ||
)} | ||
</> | ||
)} | ||
</Button> | ||
))} | ||
</View> | ||
</View> | ||
) | ||
} |
Oops, something went wrong.