diff --git a/native/avatars/avatar-hooks.js b/native/avatars/avatar-hooks.js --- a/native/avatars/avatar-hooks.js +++ b/native/avatars/avatar-hooks.js @@ -17,10 +17,7 @@ filenameFromPathOrURI, } from 'lib/media/file-utils.js'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; -import type { - ImageAvatarDBContent, - UpdateUserAvatarRemoveRequest, -} from 'lib/types/avatar-types.js'; +import type { ImageAvatarDBContent } from 'lib/types/avatar-types.js'; import type { SetState } from 'lib/types/hook-types.js'; import type { LoadingStatus } from 'lib/types/loading-types.js'; import type { @@ -267,45 +264,6 @@ ); } -function useRemoveThreadAvatar(threadID: string): [() => void, boolean] { - const dispatchActionPromise = useDispatchActionPromise(); - const changeThreadSettingsCall = useServerCall(changeThreadSettings); - const updateThreadAvatarLoadingStatus: LoadingStatus = useSelector( - threadAvatarLoadingStatusSelector, - ); - - const removeThreadAvatar = React.useCallback(() => { - const removeAvatarRequest: UpdateUserAvatarRemoveRequest = { - type: 'remove', - }; - - const updateThreadRequest: UpdateThreadRequest = { - threadID, - changes: { - avatar: removeAvatarRequest, - }, - }; - - dispatchActionPromise( - changeThreadSettingsActionTypes, - (async () => { - try { - return await changeThreadSettingsCall(updateThreadRequest); - } catch (e) { - Alert.alert('Avatar update failed', 'Unable to update avatar.'); - throw e; - } - })(), - { customKeyName: `${changeThreadSettingsActionTypes.started}:avatar` }, - ); - }, [changeThreadSettingsCall, dispatchActionPromise, threadID]); - - return React.useMemo( - () => [removeThreadAvatar, updateThreadAvatarLoadingStatus === 'loading'], - [removeThreadAvatar, updateThreadAvatarLoadingStatus], - ); -} - type ShowAvatarActionSheetOptions = { +id: 'emoji' | 'image' | 'camera' | 'ens' | 'cancel' | 'remove', +onPress?: () => mixed, @@ -436,5 +394,4 @@ useProcessSelectedMedia, useShowAvatarActionSheet, useSelectFromGalleryAndUpdateThreadAvatar, - useRemoveThreadAvatar, }; diff --git a/native/avatars/edit-thread-avatar-provider.react.js b/native/avatars/edit-thread-avatar-provider.react.js new file mode 100644 --- /dev/null +++ b/native/avatars/edit-thread-avatar-provider.react.js @@ -0,0 +1,93 @@ +// @flow + +import * as React from 'react'; +import { Alert } from 'react-native'; + +import { + changeThreadSettings, + changeThreadSettingsActionTypes, +} from 'lib/actions/thread-actions.js'; +import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; +import type { UpdateUserAvatarRemoveRequest } from 'lib/types/avatar-types.js'; +import type { LoadingStatus } from 'lib/types/loading-types.js'; +import type { UpdateThreadRequest } from 'lib/types/thread-types.js'; +import { + useDispatchActionPromise, + useServerCall, +} from 'lib/utils/action-utils.js'; + +import { useSelector } from '../redux/redux-utils.js'; + +export type EditThreadAvatarContextType = { + +threadAvatarSaveInProgress: boolean, + +removeThreadAvatar: (threadID: string) => void, +}; + +const EditThreadAvatarContext: React.Context = + React.createContext(); + +const threadAvatarLoadingStatusSelector = createLoadingStatusSelector( + changeThreadSettingsActionTypes, + `${changeThreadSettingsActionTypes.started}:avatar`, +); + +type Props = { + +children: React.Node, +}; +function EditThreadAvatarProvider(props: Props): React.Node { + const { children } = props; + + const dispatchActionPromise = useDispatchActionPromise(); + const changeThreadSettingsCall = useServerCall(changeThreadSettings); + const updateThreadAvatarLoadingStatus: LoadingStatus = useSelector( + threadAvatarLoadingStatusSelector, + ); + + const threadAvatarSaveInProgress = + updateThreadAvatarLoadingStatus === 'loading'; + + const removeThreadAvatar = React.useCallback( + (threadID: string) => { + const removeAvatarRequest: UpdateUserAvatarRemoveRequest = { + type: 'remove', + }; + + const updateThreadRequest: UpdateThreadRequest = { + threadID, + changes: { + avatar: removeAvatarRequest, + }, + }; + + dispatchActionPromise( + changeThreadSettingsActionTypes, + (async () => { + try { + return await changeThreadSettingsCall(updateThreadRequest); + } catch (e) { + Alert.alert('Avatar update failed', 'Unable to update avatar.'); + throw e; + } + })(), + { customKeyName: `${changeThreadSettingsActionTypes.started}:avatar` }, + ); + }, + [changeThreadSettingsCall, dispatchActionPromise], + ); + + const context = React.useMemo( + () => ({ + threadAvatarSaveInProgress, + removeThreadAvatar, + }), + [removeThreadAvatar, threadAvatarSaveInProgress], + ); + + return ( + + {children} + + ); +} + +export { EditThreadAvatarContext, EditThreadAvatarProvider }; diff --git a/native/avatars/edit-thread-avatar.react.js b/native/avatars/edit-thread-avatar.react.js --- a/native/avatars/edit-thread-avatar.react.js +++ b/native/avatars/edit-thread-avatar.react.js @@ -1,16 +1,17 @@ // @flow +import invariant from 'invariant'; import * as React from 'react'; import { ActivityIndicator, TouchableOpacity, View } from 'react-native'; import type { RawThreadInfo, ThreadInfo } from 'lib/types/thread-types.js'; import { - useRemoveThreadAvatar, useSelectFromGalleryAndUpdateThreadAvatar, useShowAvatarActionSheet, } from './avatar-hooks.js'; import EditAvatarBadge from './edit-avatar-badge.react.js'; +import { EditThreadAvatarContext } from './edit-thread-avatar-provider.react.js'; import ThreadAvatar from './thread-avatar.react.js'; import { useStyles } from '../themes/colors.js'; @@ -23,24 +24,31 @@ const styles = useStyles(unboundStyles); const { threadInfo, onPressEmojiAvatarFlow, disabled } = props; + const editThreadAvatarContext = React.useContext(EditThreadAvatarContext); + invariant(editThreadAvatarContext, 'editThreadAvatarContext should be set'); + const { threadAvatarSaveInProgress, removeThreadAvatar } = + editThreadAvatarContext; + const [selectFromGalleryAndUpdateThreadAvatar, isGalleryAvatarUpdateLoading] = useSelectFromGalleryAndUpdateThreadAvatar(threadInfo.id); - const [removeThreadAvatar, isRemoveAvatarUpdateLoading] = - useRemoveThreadAvatar(threadInfo.id); - const isAvatarUpdateInProgress = - isGalleryAvatarUpdateLoading || isRemoveAvatarUpdateLoading; + isGalleryAvatarUpdateLoading || threadAvatarSaveInProgress; + + const removeAvatar = React.useCallback( + () => removeThreadAvatar(threadInfo.id), + [removeThreadAvatar, threadInfo.id], + ); const actionSheetConfig = React.useMemo( () => [ { id: 'emoji', onPress: onPressEmojiAvatarFlow }, { id: 'image', onPress: selectFromGalleryAndUpdateThreadAvatar }, - { id: 'remove', onPress: removeThreadAvatar }, + { id: 'remove', onPress: removeAvatar }, ], [ onPressEmojiAvatarFlow, - removeThreadAvatar, + removeAvatar, selectFromGalleryAndUpdateThreadAvatar, ], ); diff --git a/native/root.react.js b/native/root.react.js --- a/native/root.react.js +++ b/native/root.react.js @@ -22,6 +22,7 @@ import { MediaCacheProvider } from 'lib/components/media-cache-provider.react.js'; import { actionLogger } from 'lib/utils/action-logger.js'; +import { EditThreadAvatarProvider } from './avatars/edit-thread-avatar-provider.react.js'; import { EditUserAvatarProvider } from './avatars/edit-user-avatar-provider.react.js'; import ChatContextProvider from './chat/chat-context-provider.react.js'; import { FeatureFlagsProvider } from './components/feature-flags-provider.react.js'; @@ -258,23 +259,25 @@ - - - - - - {gated} - - - - - {navigation} - - + + + + + + + {gated} + + + + + {navigation} + + +