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 @@ -7,30 +7,20 @@ import Alert from 'react-native/Libraries/Alert/Alert.js'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { - changeThreadSettings, - changeThreadSettingsActionTypes, -} from 'lib/actions/thread-actions.js'; import { uploadMultimedia } from 'lib/actions/upload-actions.js'; import { extensionFromFilename, filenameFromPathOrURI, } from 'lib/media/file-utils.js'; -import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.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 { NativeMediaSelection, MediaLibrarySelection, MediaMissionFailure, UploadMultimediaResult, } from 'lib/types/media-types.js'; -import type { UpdateThreadRequest } from 'lib/types/thread-types.js'; -import { - useDispatchActionPromise, - useServerCall, -} from 'lib/utils/action-utils.js'; +import { useServerCall } from 'lib/utils/action-utils.js'; import CommIcon from '../components/comm-icon.react.js'; import SWMansionIcon from '../components/swmansion-icon.react.js'; @@ -191,79 +181,6 @@ ); } -const threadAvatarLoadingStatusSelector = createLoadingStatusSelector( - changeThreadSettingsActionTypes, - `${changeThreadSettingsActionTypes.started}:avatar`, -); -function useSelectFromGalleryAndUpdateThreadAvatar( - threadID: string, -): [() => Promise, boolean] { - const dispatchActionPromise = useDispatchActionPromise(); - const changeThreadSettingsCall = useServerCall(changeThreadSettings); - - const [processingOrUploadInProgress, setProcessingOrUploadInProgress] = - React.useState(false); - - const updateThreadAvatarLoadingStatus: LoadingStatus = useSelector( - threadAvatarLoadingStatusSelector, - ); - - const inProgress = React.useMemo( - () => - processingOrUploadInProgress || - updateThreadAvatarLoadingStatus === 'loading', - [processingOrUploadInProgress, updateThreadAvatarLoadingStatus], - ); - - const uploadSelectedMedia = useUploadSelectedMedia( - setProcessingOrUploadInProgress, - ); - - const selectFromGalleryAndUpdateThreadAvatar = React.useCallback(async () => { - const selection: ?MediaLibrarySelection = await selectFromGallery(); - if (!selection) { - return; - } - - const imageAvatarUpdateRequest = await uploadSelectedMedia(selection); - - if (!imageAvatarUpdateRequest) { - return; - } - - const updateThreadRequest: UpdateThreadRequest = { - threadID, - changes: { - avatar: imageAvatarUpdateRequest, - }, - }; - - dispatchActionPromise( - changeThreadSettingsActionTypes, - (async () => { - setProcessingOrUploadInProgress(false); - 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, - uploadSelectedMedia, - ]); - - return React.useMemo( - () => [selectFromGalleryAndUpdateThreadAvatar, inProgress], - [inProgress, selectFromGalleryAndUpdateThreadAvatar], - ); -} - type ShowAvatarActionSheetOptions = { +id: 'emoji' | 'image' | 'camera' | 'ens' | 'cancel' | 'remove', +onPress?: () => mixed, @@ -393,5 +310,4 @@ useUploadProcessedMedia, useProcessSelectedMedia, useShowAvatarActionSheet, - useSelectFromGalleryAndUpdateThreadAvatar, }; diff --git a/native/avatars/edit-thread-avatar-provider.react.js b/native/avatars/edit-thread-avatar-provider.react.js --- a/native/avatars/edit-thread-avatar-provider.react.js +++ b/native/avatars/edit-thread-avatar-provider.react.js @@ -10,18 +10,21 @@ 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 { MediaLibrarySelection } from 'lib/types/media-types.js'; import type { UpdateThreadRequest } from 'lib/types/thread-types.js'; import { useDispatchActionPromise, useServerCall, } from 'lib/utils/action-utils.js'; +import { selectFromGallery, useUploadSelectedMedia } from './avatar-hooks.js'; import { activeThreadSelector } from '../navigation/nav-selectors.js'; import { NavContext } from '../navigation/navigation-context.js'; import { useSelector } from '../redux/redux-utils.js'; export type EditThreadAvatarContextType = { +threadAvatarSaveInProgress: boolean, + +selectFromGalleryAndUpdateThreadAvatar: (threadID: string) => Promise, +removeThreadAvatar: (threadID: string) => void, }; @@ -50,9 +53,57 @@ const dispatchActionPromise = useDispatchActionPromise(); const changeThreadSettingsCall = useServerCall(changeThreadSettings); + const [processingOrUploadInProgress, setProcessingOrUploadInProgress] = + React.useState(false); + const threadAvatarSaveInProgress = + processingOrUploadInProgress || updateThreadAvatarLoadingStatus === 'loading'; + const uploadSelectedMedia = useUploadSelectedMedia( + setProcessingOrUploadInProgress, + ); + + const selectFromGalleryAndUpdateThreadAvatar = React.useCallback( + async (threadID: string) => { + const selection: ?MediaLibrarySelection = await selectFromGallery(); + if (!selection) { + return; + } + + const imageAvatarUpdateRequest = await uploadSelectedMedia(selection); + + if (!imageAvatarUpdateRequest) { + return; + } + + const updateThreadRequest: UpdateThreadRequest = { + threadID, + changes: { + avatar: imageAvatarUpdateRequest, + }, + }; + + const action = changeThreadSettingsActionTypes.started; + dispatchActionPromise( + changeThreadSettingsActionTypes, + (async () => { + setProcessingOrUploadInProgress(false); + try { + return await changeThreadSettingsCall(updateThreadRequest); + } catch (e) { + Alert.alert('Avatar update failed', 'Unable to update avatar.'); + throw e; + } + })(), + { + customKeyName: `${action}:${threadID}:avatar`, + }, + ); + }, + [changeThreadSettingsCall, dispatchActionPromise, uploadSelectedMedia], + ); + const removeThreadAvatar = React.useCallback( (threadID: string) => { const removeAvatarRequest: UpdateUserAvatarRemoveRequest = { @@ -86,9 +137,14 @@ const context = React.useMemo( () => ({ threadAvatarSaveInProgress, + selectFromGalleryAndUpdateThreadAvatar, removeThreadAvatar, }), - [removeThreadAvatar, threadAvatarSaveInProgress], + [ + removeThreadAvatar, + selectFromGalleryAndUpdateThreadAvatar, + threadAvatarSaveInProgress, + ], ); return ( 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 @@ -6,10 +6,7 @@ import type { RawThreadInfo, ThreadInfo } from 'lib/types/thread-types.js'; -import { - useSelectFromGalleryAndUpdateThreadAvatar, - useShowAvatarActionSheet, -} from './avatar-hooks.js'; +import { 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'; @@ -26,14 +23,16 @@ const editThreadAvatarContext = React.useContext(EditThreadAvatarContext); invariant(editThreadAvatarContext, 'editThreadAvatarContext should be set'); - const { threadAvatarSaveInProgress, removeThreadAvatar } = - editThreadAvatarContext; + const { + threadAvatarSaveInProgress, + selectFromGalleryAndUpdateThreadAvatar, + removeThreadAvatar, + } = editThreadAvatarContext; - const [selectFromGalleryAndUpdateThreadAvatar, isGalleryAvatarUpdateLoading] = - useSelectFromGalleryAndUpdateThreadAvatar(threadInfo.id); - - const isAvatarUpdateInProgress = - isGalleryAvatarUpdateLoading || threadAvatarSaveInProgress; + const selectFromGallery = React.useCallback( + () => selectFromGalleryAndUpdateThreadAvatar(threadInfo.id), + [selectFromGalleryAndUpdateThreadAvatar, threadInfo.id], + ); const removeAvatar = React.useCallback( () => removeThreadAvatar(threadInfo.id), @@ -43,20 +42,16 @@ const actionSheetConfig = React.useMemo( () => [ { id: 'emoji', onPress: onPressEmojiAvatarFlow }, - { id: 'image', onPress: selectFromGalleryAndUpdateThreadAvatar }, + { id: 'image', onPress: selectFromGallery }, { id: 'remove', onPress: removeAvatar }, ], - [ - onPressEmojiAvatarFlow, - removeAvatar, - selectFromGalleryAndUpdateThreadAvatar, - ], + [onPressEmojiAvatarFlow, removeAvatar, selectFromGallery], ); const showAvatarActionSheet = useShowAvatarActionSheet(actionSheetConfig); let spinner; - if (isAvatarUpdateInProgress) { + if (threadAvatarSaveInProgress) { spinner = (