diff --git a/native/avatars/edit-user-avatar-provider.react.js b/native/avatars/edit-user-avatar-provider.react.js index 06648a0a0..a890125b4 100644 --- a/native/avatars/edit-user-avatar-provider.react.js +++ b/native/avatars/edit-user-avatar-provider.react.js @@ -1,156 +1,130 @@ // @flow import * as React from 'react'; import { Alert } from 'react-native'; import { updateUserAvatar, updateUserAvatarActionTypes, } from 'lib/actions/user-actions.js'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; -import type { - ENSAvatarDBContent, - UpdateUserAvatarRemoveRequest, -} from 'lib/types/avatar-types.js'; +import type { UpdateUserAvatarRequest } from 'lib/types/avatar-types.js'; import type { LoadingStatus } from 'lib/types/loading-types.js'; import type { NativeMediaSelection } from 'lib/types/media-types.js'; import { useDispatchActionPromise, useServerCall, } from 'lib/utils/action-utils.js'; import { selectFromGallery, useUploadSelectedMedia } from './avatar-hooks.js'; import { useSelector } from '../redux/redux-utils.js'; export type EditUserAvatarContextType = { +userAvatarSaveInProgress: boolean, +selectFromGalleryAndUpdateUserAvatar: () => Promise, +updateImageUserAvatar: (selection: NativeMediaSelection) => Promise, - +setENSUserAvatar: () => void, - +removeUserAvatar: () => void, + +setUserAvatar: (avatarRequest: UpdateUserAvatarRequest) => Promise, }; const EditUserAvatarContext: React.Context = React.createContext(); const updateUserAvatarLoadingStatusSelector = createLoadingStatusSelector( updateUserAvatarActionTypes, ); type Props = { +children: React.Node, }; function EditUserAvatarProvider(props: Props): React.Node { const { children } = props; const dispatchActionPromise = useDispatchActionPromise(); const updateUserAvatarCall = useServerCall(updateUserAvatar); const [userAvatarMediaUploadInProgress, setUserAvatarMediaUploadInProgress] = React.useState(false); const updateUserAvatarLoadingStatus: LoadingStatus = useSelector( updateUserAvatarLoadingStatusSelector, ); const userAvatarSaveInProgress = userAvatarMediaUploadInProgress || updateUserAvatarLoadingStatus === 'loading'; const uploadSelectedMedia = useUploadSelectedMedia( setUserAvatarMediaUploadInProgress, ); const updateImageUserAvatar = React.useCallback( async (selection: NativeMediaSelection) => { const imageAvatarUpdateRequest = await uploadSelectedMedia(selection); if (!imageAvatarUpdateRequest) { return; } dispatchActionPromise( updateUserAvatarActionTypes, (async () => { setUserAvatarMediaUploadInProgress(false); try { return await updateUserAvatarCall(imageAvatarUpdateRequest); } catch (e) { Alert.alert('Avatar update failed', 'Unable to update avatar.'); throw e; } })(), ); }, [dispatchActionPromise, updateUserAvatarCall, uploadSelectedMedia], ); const selectFromGalleryAndUpdateUserAvatar = React.useCallback(async () => { const selection = await selectFromGallery(); if (!selection) { return; } await updateImageUserAvatar(selection); }, [updateImageUserAvatar]); - const setENSUserAvatar = React.useCallback(() => { - const ensAvatarRequest: ENSAvatarDBContent = { - type: 'ens', - }; - - dispatchActionPromise( - updateUserAvatarActionTypes, - (async () => { - try { - return await updateUserAvatarCall(ensAvatarRequest); - } catch (e) { - Alert.alert('Avatar update failed', 'Unable to update avatar.'); - throw e; - } - })(), - ); - }, [dispatchActionPromise, updateUserAvatarCall]); - - const removeUserAvatar = React.useCallback(() => { - const removeAvatarRequest: UpdateUserAvatarRemoveRequest = { - type: 'remove', - }; - - dispatchActionPromise( - updateUserAvatarActionTypes, - (async () => { + const setUserAvatar = React.useCallback( + async (avatarRequest: UpdateUserAvatarRequest) => { + const promise = (async () => { try { - return await updateUserAvatarCall(removeAvatarRequest); + return await updateUserAvatarCall(avatarRequest); } catch (e) { Alert.alert('Avatar update failed', 'Unable to update avatar.'); throw e; } - })(), - ); - }, [dispatchActionPromise, updateUserAvatarCall]); + })(); + dispatchActionPromise(updateUserAvatarActionTypes, promise); + await promise; + }, + [dispatchActionPromise, updateUserAvatarCall], + ); const context = React.useMemo( () => ({ userAvatarSaveInProgress, selectFromGalleryAndUpdateUserAvatar, updateImageUserAvatar, - setENSUserAvatar, - removeUserAvatar, + setUserAvatar, }), [ - removeUserAvatar, + userAvatarSaveInProgress, selectFromGalleryAndUpdateUserAvatar, updateImageUserAvatar, - setENSUserAvatar, - userAvatarSaveInProgress, + setUserAvatar, ], ); return ( {children} ); } export { EditUserAvatarContext, EditUserAvatarProvider }; diff --git a/native/avatars/edit-user-avatar.react.js b/native/avatars/edit-user-avatar.react.js index c57863145..d8277e038 100644 --- a/native/avatars/edit-user-avatar.react.js +++ b/native/avatars/edit-user-avatar.react.js @@ -1,114 +1,121 @@ // @flow import { useNavigation } from '@react-navigation/native'; import invariant from 'invariant'; import * as React from 'react'; import { ActivityIndicator, TouchableOpacity, View } from 'react-native'; import { useENSAvatar } from 'lib/hooks/ens-cache.js'; import { getETHAddressForUserInfo } from 'lib/shared/account-utils.js'; import { useShowAvatarActionSheet } from './avatar-hooks.js'; import EditAvatarBadge from './edit-avatar-badge.react.js'; import { EditUserAvatarContext } from './edit-user-avatar-provider.react.js'; import UserAvatar from './user-avatar.react.js'; import { EmojiUserAvatarCreationRouteName, UserAvatarCameraModalRouteName, } from '../navigation/route-names.js'; import { useSelector } from '../redux/redux-utils.js'; import { useStyles } from '../themes/colors.js'; type Props = { +userID: ?string, +disabled?: boolean, }; function EditUserAvatar(props: Props): React.Node { const styles = useStyles(unboundStyles); const { userID, disabled } = props; const editUserAvatarContext = React.useContext(EditUserAvatarContext); invariant(editUserAvatarContext, 'editUserAvatarContext should be set'); const { userAvatarSaveInProgress, selectFromGalleryAndUpdateUserAvatar, - setENSUserAvatar, - removeUserAvatar, + setUserAvatar, } = editUserAvatarContext; const currentUserInfo = useSelector(state => state.currentUserInfo); const ethAddress = React.useMemo( () => getETHAddressForUserInfo(currentUserInfo), [currentUserInfo], ); const ensAvatarURI = useENSAvatar(ethAddress); const { navigate } = useNavigation(); const navigateToUserEmojiAvatarCreation = React.useCallback(() => { navigate(EmojiUserAvatarCreationRouteName); }, [navigate]); const navigateToCamera = React.useCallback(() => { navigate(UserAvatarCameraModalRouteName); }, [navigate]); + const setENSUserAvatar = React.useCallback(() => { + setUserAvatar({ type: 'ens' }); + }, [setUserAvatar]); + + const removeUserAvatar = React.useCallback(() => { + setUserAvatar({ type: 'remove' }); + }, [setUserAvatar]); + const actionSheetConfig = React.useMemo(() => { const configOptions = [ { id: 'emoji', onPress: navigateToUserEmojiAvatarCreation }, { id: 'image', onPress: selectFromGalleryAndUpdateUserAvatar }, { id: 'camera', onPress: navigateToCamera }, ]; if (ensAvatarURI) { configOptions.push({ id: 'ens', onPress: setENSUserAvatar }); } if (currentUserInfo?.avatar) { configOptions.push({ id: 'remove', onPress: removeUserAvatar }); } return configOptions; }, [ currentUserInfo?.avatar, ensAvatarURI, navigateToCamera, navigateToUserEmojiAvatarCreation, removeUserAvatar, setENSUserAvatar, selectFromGalleryAndUpdateUserAvatar, ]); const showAvatarActionSheet = useShowAvatarActionSheet(actionSheetConfig); let spinner; if (userAvatarSaveInProgress) { spinner = ( ); } return ( {spinner} {!disabled ? : null} ); } const unboundStyles = { spinnerContainer: { position: 'absolute', alignItems: 'center', justifyContent: 'center', top: 0, bottom: 0, left: 0, right: 0, }, }; export default EditUserAvatar;