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,7 +10,10 @@ 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 { + MediaLibrarySelection, + NativeMediaSelection, +} from 'lib/types/media-types.js'; import type { UpdateThreadRequest } from 'lib/types/thread-types.js'; import { useDispatchActionPromise, @@ -25,6 +28,10 @@ export type EditThreadAvatarContextType = { +threadAvatarSaveInProgress: boolean, +selectFromGalleryAndUpdateThreadAvatar: (threadID: string) => Promise, + +updateImageThreadAvatar: ( + selection: NativeMediaSelection, + threadID: string, + ) => Promise, +removeThreadAvatar: (threadID: string) => void, }; @@ -81,7 +88,7 @@ ); const updateImageThreadAvatar = React.useCallback( - async (selection: MediaLibrarySelection, threadID: string) => { + async (selection: NativeMediaSelection, threadID: string) => { const imageAvatarUpdateRequest = await uploadSelectedMedia(selection); if (!imageAvatarUpdateRequest) { @@ -165,11 +172,13 @@ () => ({ threadAvatarSaveInProgress, selectFromGalleryAndUpdateThreadAvatar, + updateImageThreadAvatar, removeThreadAvatar, }), [ removeThreadAvatar, selectFromGalleryAndUpdateThreadAvatar, + updateImageThreadAvatar, threadAvatarSaveInProgress, ], ); 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 @@ -11,7 +11,10 @@ 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 { EmojiThreadAvatarCreationRouteName } from '../navigation/route-names.js'; +import { + EmojiThreadAvatarCreationRouteName, + ThreadAvatarCameraModalRouteName, +} from '../navigation/route-names.js'; import { useStyles } from '../themes/colors.js'; type Props = { @@ -47,6 +50,13 @@ [selectFromGalleryAndUpdateThreadAvatar, threadInfo.id], ); + const navigateToCamera = React.useCallback(() => { + navigate<'ThreadAvatarCameraModal'>({ + name: ThreadAvatarCameraModalRouteName, + params: { threadID: threadInfo.id }, + }); + }, [navigate, threadInfo.id]); + const removeAvatar = React.useCallback( () => removeThreadAvatar(threadInfo.id), [removeThreadAvatar, threadInfo.id], @@ -56,9 +66,15 @@ () => [ { id: 'emoji', onPress: navigateToThreadEmojiAvatarCreation }, { id: 'image', onPress: selectFromGallery }, + { id: 'camera', onPress: navigateToCamera }, { id: 'remove', onPress: removeAvatar }, ], - [navigateToThreadEmojiAvatarCreation, removeAvatar, selectFromGallery], + [ + navigateToCamera, + navigateToThreadEmojiAvatarCreation, + removeAvatar, + selectFromGallery, + ], ); const showAvatarActionSheet = useShowAvatarActionSheet(actionSheetConfig); diff --git a/native/media/thread-avatar-camera-modal.react.js b/native/media/thread-avatar-camera-modal.react.js new file mode 100644 --- /dev/null +++ b/native/media/thread-avatar-camera-modal.react.js @@ -0,0 +1,40 @@ +// @flow + +import invariant from 'invariant'; +import * as React from 'react'; + +import type { PhotoCapture } from 'lib/types/media-types.js'; + +import { EditThreadAvatarContext } from '../avatars/edit-thread-avatar-provider.react.js'; +import CameraModal from '../media/camera-modal.react.js'; +import type { AppNavigationProp } from '../navigation/app-navigator.react.js'; +import type { NavigationRoute } from '../navigation/route-names.js'; + +export type ThreadAvatarCameraModalParams = { + +threadID: string, +}; + +type Props = { + +navigation: AppNavigationProp<'ThreadAvatarCameraModal'>, + +route: NavigationRoute<'ThreadAvatarCameraModal'>, +}; + +function ThreadAvatarCameraModal(props: Props): React.Node { + const { navigation, route } = props; + const { threadID } = route.params; + + const editThreadAvatarContext = React.useContext(EditThreadAvatarContext); + invariant(editThreadAvatarContext, 'editThreadAvatarContext should be set'); + const { updateImageThreadAvatar } = editThreadAvatarContext; + + const sendPhoto = React.useCallback( + (capture: PhotoCapture) => { + updateImageThreadAvatar(capture, threadID); + }, + [threadID, updateImageThreadAvatar], + ); + + return ; +} + +export default ThreadAvatarCameraModal; diff --git a/native/navigation/app-navigator.react.js b/native/navigation/app-navigator.react.js --- a/native/navigation/app-navigator.react.js +++ b/native/navigation/app-navigator.react.js @@ -15,6 +15,7 @@ import type { RootNavigationProp } from './root-navigator.react.js'; import { UserAvatarCameraModalRouteName, + ThreadAvatarCameraModalRouteName, ImageModalRouteName, MultimediaMessageTooltipModalRouteName, ActionResultModalRouteName, @@ -35,6 +36,7 @@ import KeyboardStateContainer from '../keyboard/keyboard-state-container.react.js'; import ChatCameraModal from '../media/chat-camera-modal.react.js'; import ImageModal from '../media/image-modal.react.js'; +import ThreadAvatarCameraModal from '../media/thread-avatar-camera-modal.react.js'; import UserAvatarCameraModal from '../media/user-avatar-camera-modal.react.js'; import VideoPlaybackModal from '../media/video-playback-modal.react.js'; import RelationshipListItemTooltipModal from '../profile/relationship-list-item-tooltip-modal.react.js'; @@ -141,6 +143,10 @@ name={UserAvatarCameraModalRouteName} component={UserAvatarCameraModal} /> +