-
Notifications
You must be signed in to change notification settings - Fork 572
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(DIA-561): CompleteMyProfile flow (#10446)
* feat: first steps of the complete profit :wqle * feat: remove bio from user form * refactor: input generation simplification * feat: fixes broken link and minor adjusts * feat: fix the header as a navigation header element, minor adjusts to Android, iOS layout * feat: remove test placeholder from complete profile CTA
- Loading branch information
1 parent
8920134
commit 901ef2d
Showing
25 changed files
with
1,835 additions
and
34 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
import { | ||
Text, | ||
Screen, | ||
Spacer, | ||
Flex, | ||
Touchable, | ||
Button, | ||
CheckCircleFillIcon, | ||
AddCircleIcon, | ||
useSpace, | ||
Image, | ||
Skeleton, | ||
SkeletonBox, | ||
} from "@artsy/palette-mobile" | ||
import { useActionSheet } from "@expo/react-native-action-sheet" | ||
import { | ||
State, | ||
useCompleteMyProfileContext, | ||
} from "app/Scenes/CompleteMyProfile/CompleteMyProfileProvider" | ||
import { Footer } from "app/Scenes/CompleteMyProfile/Footer" | ||
import { useCompleteProfile } from "app/Scenes/CompleteMyProfile/useCompleteProfile" | ||
import { getConvertedImageUrlFromS3 } from "app/utils/getConvertedImageUrlFromS3" | ||
import { showPhotoActionSheet } from "app/utils/requestPhotos" | ||
import { FC, useState } from "react" | ||
|
||
export const AvatarStep = () => { | ||
const { goNext, isCurrentRouteDirty, setField, field } = useCompleteProfile<State["iconUrl"]>() | ||
|
||
const handleOnImageSelect = ({ | ||
localPath, | ||
geminiUrl, | ||
}: { | ||
localPath: string | ||
geminiUrl: string | ||
}) => { | ||
setField({ localPath, geminiUrl }) | ||
} | ||
|
||
return ( | ||
<Screen> | ||
<Screen.Body> | ||
<Flex justifyContent="space-between" height="100%"> | ||
<Flex> | ||
<Text variant="lg">Add a profile image</Text> | ||
|
||
<Spacer y={1} /> | ||
|
||
<Text color="black60"> | ||
Make your profile more engaging and help foster trust with galleries on Artsy. | ||
</Text> | ||
|
||
<Spacer y={2} /> | ||
|
||
<ImageSelector onImageSelect={handleOnImageSelect} src={field} /> | ||
</Flex> | ||
|
||
<Footer isFormDirty={isCurrentRouteDirty} onGoNext={goNext} /> | ||
</Flex> | ||
</Screen.Body> | ||
</Screen> | ||
) | ||
} | ||
|
||
interface ImageSelectorProps { | ||
src?: State["iconUrl"] | ||
onImageSelect: (props: { localPath: string; geminiUrl: string }) => void | ||
} | ||
|
||
const ImageSelector: FC<ImageSelectorProps> = ({ src, onImageSelect }) => { | ||
const [isLoading, setIsLoading] = useState(false) | ||
const space = useSpace() | ||
const { showActionSheetWithOptions } = useActionSheet() | ||
const { user } = useCompleteMyProfileContext() | ||
|
||
const handleImagePress = async () => { | ||
if (isLoading) { | ||
return | ||
} | ||
|
||
try { | ||
const images = await showPhotoActionSheet(showActionSheetWithOptions, true, false) | ||
|
||
if (images.length === 1) { | ||
setIsLoading(true) | ||
const localPath = images[0].path | ||
const geminiUrl = await getConvertedImageUrlFromS3(localPath) | ||
onImageSelect({ localPath, geminiUrl }) | ||
} | ||
} catch (error) { | ||
console.error("Error when uploading an image from the device", JSON.stringify(error)) | ||
} finally { | ||
setIsLoading(false) | ||
} | ||
} | ||
|
||
const displayAvatar = !isLoading && src?.localPath && src?.geminiUrl | ||
const displayInitials = !isLoading && !displayAvatar | ||
|
||
return ( | ||
<Touchable aria-label="Choose a photo" accessibilityRole="button" onPress={handleImagePress}> | ||
<Flex alignItems="center" gap={space(2)}> | ||
<Flex alignItems="center"> | ||
<Flex | ||
alignItems="center" | ||
justifyContent="center" | ||
width={AVATAR_SIZE} | ||
height={AVATAR_SIZE} | ||
border={displayInitials ? 1 : 0} | ||
borderColor="black10" | ||
borderRadius={AVATAR_SIZE / 2} | ||
> | ||
{!!isLoading && ( | ||
<Skeleton> | ||
<SkeletonBox | ||
width={AVATAR_SIZE} | ||
height={AVATAR_SIZE} | ||
borderRadius={AVATAR_SIZE / 2} | ||
/> | ||
</Skeleton> | ||
)} | ||
|
||
{!!displayAvatar && ( | ||
<Flex overflow="hidden" borderRadius={AVATAR_SIZE / 2}> | ||
<Image | ||
width={AVATAR_SIZE} | ||
height={AVATAR_SIZE} | ||
performResize={false} | ||
src={src.localPath} | ||
/> | ||
</Flex> | ||
)} | ||
|
||
{!!displayInitials && <Text variant="lg-display">{user.initials}</Text>} | ||
</Flex> | ||
|
||
<Flex | ||
display={isLoading ? "none" : "flex"} | ||
position="absolute" | ||
bottom={ICON_SIZE / 4} | ||
right={ICON_SIZE / 4} | ||
width={ICON_SIZE} | ||
height={ICON_SIZE} | ||
backgroundColor="white100" | ||
borderRadius={ICON_SIZE / 2} | ||
> | ||
{!!displayAvatar && ( | ||
<CheckCircleFillIcon width={ICON_SIZE} height={ICON_SIZE} fill="green100" /> | ||
)} | ||
|
||
{!!displayInitials && <AddCircleIcon width={ICON_SIZE} height={ICON_SIZE} />} | ||
</Flex> | ||
</Flex> | ||
<Button variant="outline" size="small" onPress={handleImagePress} loading={isLoading}> | ||
{src ? "Change Image" : "Choose an Image"} | ||
</Button> | ||
</Flex> | ||
</Touchable> | ||
) | ||
} | ||
|
||
const AVATAR_SIZE = 124 | ||
const ICON_SIZE = 28 |
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,154 @@ | ||
import { | ||
Button, | ||
Flex, | ||
Screen, | ||
Spacer, | ||
Text, | ||
useSpace, | ||
CheckCircleFillIcon, | ||
CheckCircleIcon, | ||
} from "@artsy/palette-mobile" | ||
import { useCompleteMyProfileContext } from "app/Scenes/CompleteMyProfile/CompleteMyProfileProvider" | ||
import { useCompleteProfile } from "app/Scenes/CompleteMyProfile/useCompleteProfile" | ||
import { navigate } from "app/system/navigation/navigate" | ||
|
||
export const ChangesSummary = () => { | ||
const space = useSpace() | ||
const { saveAndExit, isLoading } = useCompleteProfile() | ||
const { progressState, progressStateWithoutUndefined, steps } = useCompleteMyProfileContext() | ||
|
||
const handleAddArtistsToMyCollection = () => { | ||
if (!isLoading) { | ||
navigate(`my-collection/collected-artists/new`) | ||
} | ||
} | ||
|
||
const completedStepsLength = Object.values(progressStateWithoutUndefined).length | ||
const isCompleted = completedStepsLength === steps.length - 1 | ||
const hasLocation = !!progressState.location | ||
const hasProfession = !!progressState.profession | ||
const hasIconUrl = !!progressState.iconUrl | ||
const hasIsIdentityVerified = !!progressState.isIdentityVerified | ||
|
||
return ( | ||
<Screen> | ||
<Screen.Body> | ||
<Flex py={2} gap={space(2)}> | ||
<Text variant="lg-display"> | ||
{isCompleted ? "Thank you for completing your profile." : "You’re almost there!"} | ||
</Text> | ||
|
||
<Text color="black60" variant="sm"> | ||
{isCompleted ? ( | ||
<> | ||
You can update your profile at any time in{" "} | ||
<Text | ||
variant="sm" | ||
backgroundColor="white100" | ||
style={{ textDecorationLine: "underline" }} | ||
onPress={() => navigate(`my-profile/edit`)} | ||
suppressHighlighting | ||
>{` Settings`}</Text> | ||
. | ||
</> | ||
) : ( | ||
"Complete these details to finalize your profile:" | ||
)} | ||
</Text> | ||
|
||
<Flex gap={space(1)}> | ||
{steps.includes("LocationStep") && ( | ||
<Flex flexDirection="row" alignItems="center" gap={space(1)}> | ||
{hasLocation ? ( | ||
<CheckCircleFillIcon fill="green100" /> | ||
) : ( | ||
<CheckCircleIcon fill="black60" /> | ||
)} | ||
<Text variant="md" color={hasLocation ? "black100" : "black60"}> | ||
Location | ||
</Text> | ||
</Flex> | ||
)} | ||
|
||
{steps.includes("ProfessionStep") && ( | ||
<Flex flexDirection="row" alignItems="center" gap={space(1)}> | ||
{hasProfession ? ( | ||
<CheckCircleFillIcon fill="green100" /> | ||
) : ( | ||
<CheckCircleIcon fill="black60" /> | ||
)} | ||
<Text variant="md" color={hasProfession ? "black100" : "black60"}> | ||
Profession | ||
</Text> | ||
</Flex> | ||
)} | ||
|
||
{steps.includes("AvatarStep") && ( | ||
<Flex flexDirection="row" alignItems="center" gap={space(1)}> | ||
{hasIconUrl ? ( | ||
<CheckCircleFillIcon fill="green100" /> | ||
) : ( | ||
<CheckCircleIcon fill="black60" /> | ||
)} | ||
<Text variant="md" color={hasIconUrl ? "black100" : "black60"}> | ||
Profile Image | ||
</Text> | ||
</Flex> | ||
)} | ||
|
||
{steps.includes("IdentityVerificationStep") && ( | ||
<Flex flexDirection="row" alignItems="center" gap={space(1)}> | ||
{hasIsIdentityVerified ? ( | ||
<CheckCircleFillIcon fill="green100" /> | ||
) : ( | ||
<CheckCircleIcon fill="black60" /> | ||
)} | ||
<Text variant="md" color={hasIsIdentityVerified ? "black100" : "black60"}> | ||
ID Verification Email | ||
</Text> | ||
</Flex> | ||
)} | ||
</Flex> | ||
|
||
<Text color="black60" variant="sm"> | ||
{isCompleted ? ( | ||
<> | ||
Continue building your profile by adding artists or artworks to{" "} | ||
<Text | ||
variant="sm" | ||
style={{ textDecorationLine: "underline" }} | ||
onPress={() => navigate(`my-collection`)} | ||
suppressHighlighting | ||
>{` My Collection`}</Text> | ||
. | ||
</> | ||
) : ( | ||
<> | ||
You can update your profile at any time in | ||
<Text | ||
variant="sm" | ||
style={{ textDecorationLine: "underline" }} | ||
onPress={() => navigate(`my-profile/edit`)} | ||
suppressHighlighting | ||
>{` Settings`}</Text> | ||
. | ||
</> | ||
)} | ||
</Text> | ||
</Flex> | ||
|
||
<Flex flex={1} justifyContent="flex-end" pb={4}> | ||
<Button onPress={saveAndExit} flex={1} loading={isLoading}> | ||
Save and Exit | ||
</Button> | ||
|
||
<Spacer y={2} /> | ||
|
||
<Button onPress={handleAddArtistsToMyCollection} flex={1} variant="outline"> | ||
Add Artists to My Collection | ||
</Button> | ||
</Flex> | ||
</Screen.Body> | ||
</Screen> | ||
) | ||
} |
Oops, something went wrong.