Page MenuHomePhorge

D7549.1768768624.diff
No OneTemporary

Size
9 KB
Referenced Files
None
Subscribers
None

D7549.1768768624.diff

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
@@ -29,6 +29,7 @@
import type { LoadingStatus } from 'lib/types/loading-types.js';
import type {
NativeMediaSelection,
+ PhotoCapture,
MediaLibrarySelection,
MediaMissionFailure,
UploadMultimediaResult,
@@ -399,8 +400,118 @@
);
}
+function useSelectFromCameraAndUpdateUserAvatar(): {
+ +updateUserAvatar: (selection: PhotoCapture) => Promise<void>,
+ +isCameraAvatarUpdateLoading: boolean,
+} {
+ const dispatchActionPromise = useDispatchActionPromise();
+ const updateUserAvatarCall = useServerCall(updateUserAvatar);
+
+ const processSelectedMedia = useProcessSelectedMedia();
+ const uploadProcessedMedia = useUploadProcessedMedia();
+
+ const [processingOrUploadInProgress, setProcessingOrUploadInProgress] =
+ React.useState(false);
+
+ const updateUserAvatarLoadingStatus: LoadingStatus = useSelector(
+ updateUserAvatarLoadingStatusSelector,
+ );
+
+ const inProgress = React.useMemo(
+ () =>
+ processingOrUploadInProgress ||
+ updateUserAvatarLoadingStatus === 'loading',
+ [processingOrUploadInProgress, updateUserAvatarLoadingStatus],
+ );
+
+ const selectFromCameraAndUpdateUserAvatar = React.useCallback(
+ async (selection: PhotoCapture) => {
+ if (!selection) {
+ Alert.alert(
+ 'Media selection failed',
+ 'Unable to select media from Media Library.',
+ );
+ return;
+ }
+
+ setProcessingOrUploadInProgress(true);
+ let processedMedia;
+ try {
+ processedMedia = await processSelectedMedia(selection);
+ } catch (e) {
+ Alert.alert(
+ 'Media processing failed',
+ 'Unable to process selected media.',
+ );
+ setProcessingOrUploadInProgress(false);
+ return;
+ }
+
+ if (!processedMedia || !processedMedia.success) {
+ Alert.alert(
+ 'Media processing failed',
+ 'Unable to process selected media.',
+ );
+ setProcessingOrUploadInProgress(false);
+ return;
+ }
+
+ let uploadedMedia: ?UploadMultimediaResult;
+ try {
+ uploadedMedia = await uploadProcessedMedia(processedMedia);
+ } catch {
+ Alert.alert(
+ 'Media upload failed',
+ 'Unable to upload selected media. Please try again.',
+ );
+ setProcessingOrUploadInProgress(false);
+ return;
+ }
+
+ if (!uploadedMedia) {
+ Alert.alert(
+ 'Media upload failed',
+ 'Unable to upload selected media. Please try again.',
+ );
+ setProcessingOrUploadInProgress(false);
+ return;
+ }
+
+ const imageAvatarUpdateRequest: ImageAvatarDBContent = {
+ type: 'image',
+ uploadID: uploadedMedia.id,
+ };
+
+ dispatchActionPromise(
+ updateUserAvatarActionTypes,
+ (async () => {
+ try {
+ return await updateUserAvatarCall(imageAvatarUpdateRequest);
+ } catch {
+ Alert.alert('Avatar update failed', 'Unable to update avatar.');
+ }
+ })(),
+ );
+ setProcessingOrUploadInProgress(false);
+ },
+ [
+ dispatchActionPromise,
+ processSelectedMedia,
+ updateUserAvatarCall,
+ uploadProcessedMedia,
+ ],
+ );
+
+ return React.useMemo(() => {
+ return {
+ updateUserAvatar: selectFromCameraAndUpdateUserAvatar,
+ isCameraAvatarUpdateLoading: inProgress,
+ };
+ }, [selectFromCameraAndUpdateUserAvatar, inProgress]);
+}
+
type ShowAvatarActionSheetOptions = {
- +id: 'emoji' | 'image' | 'ens' | 'cancel' | 'remove',
+ +id: 'emoji' | 'image' | 'camera' | 'ens' | 'cancel' | 'remove',
+onPress?: () => mixed,
};
function useShowAvatarActionSheet(
@@ -418,6 +529,8 @@
return 'Use Emoji';
} else if (option.id === 'image') {
return 'Select image';
+ } else if (option.id === 'camera') {
+ return 'Camera';
} else if (option.id === 'ens') {
return 'Use ENS Avatar';
} else if (option.id === 'remove') {
@@ -452,6 +565,14 @@
style={styles.bottomSheetIcon}
/>
);
+ } else if (option.id === 'camera') {
+ return (
+ <SWMansionIcon
+ name="camera"
+ size={22}
+ style={styles.bottomSheetIcon}
+ />
+ );
} else if (option.id === 'ens') {
return (
<CommIcon
@@ -521,4 +642,5 @@
useRemoveUserAvatar,
useRemoveThreadAvatar,
useENSUserAvatar,
+ useSelectFromCameraAndUpdateUserAvatar,
};
diff --git a/native/avatars/edit-user-avatar.react.js b/native/avatars/edit-user-avatar.react.js
--- a/native/avatars/edit-user-avatar.react.js
+++ b/native/avatars/edit-user-avatar.react.js
@@ -1,5 +1,6 @@
// @flow
+import { useNavigation } from '@react-navigation/native';
import * as React from 'react';
import { ActivityIndicator, TouchableOpacity, View } from 'react-native';
@@ -10,6 +11,7 @@
useENSUserAvatar,
useRemoveUserAvatar,
useSelectFromGalleryAndUpdateUserAvatar,
+ useSelectFromCameraAndUpdateUserAvatar,
useShowAvatarActionSheet,
} from './avatar-hooks.js';
import EditAvatarBadge from './edit-avatar-badge.react.js';
@@ -36,11 +38,19 @@
const [selectFromGalleryAndUpdateUserAvatar, isGalleryAvatarUpdateLoading] =
useSelectFromGalleryAndUpdateUserAvatar();
+ const { isCameraAvatarUpdateLoading } =
+ useSelectFromCameraAndUpdateUserAvatar();
+ const navigation = useNavigation();
+ const navigateToCamera = React.useCallback(() => {
+ navigation.navigate('UserAvatarCameraModal');
+ }, [navigation]);
+
const [saveENSUserAvatar, isENSAvatarUpdateLoading] = useENSUserAvatar();
const [removeUserAvatar, isRemoveAvatarUpdateLoading] = useRemoveUserAvatar();
const isAvatarUpdateInProgress =
isGalleryAvatarUpdateLoading ||
+ isCameraAvatarUpdateLoading ||
isRemoveAvatarUpdateLoading ||
isENSAvatarUpdateLoading;
@@ -48,6 +58,7 @@
const configOptions = [
{ id: 'emoji', onPress: onPressEmojiAvatarFlow },
{ id: 'image', onPress: selectFromGalleryAndUpdateUserAvatar },
+ { id: 'camera', onPress: navigateToCamera },
];
if (ensAvatarURI) {
@@ -59,6 +70,7 @@
return configOptions;
}, [
ensAvatarURI,
+ navigateToCamera,
onPressEmojiAvatarFlow,
removeUserAvatar,
saveENSUserAvatar,
diff --git a/native/media/user-avatar-camera-modal.react.js b/native/media/user-avatar-camera-modal.react.js
new file mode 100644
--- /dev/null
+++ b/native/media/user-avatar-camera-modal.react.js
@@ -0,0 +1,32 @@
+// @flow
+
+import * as React from 'react';
+
+import type { PhotoCapture } from 'lib/types/media-types.js';
+
+import { useSelectFromCameraAndUpdateUserAvatar } from '../avatars/avatar-hooks.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';
+
+type Props = {
+ +navigation: AppNavigationProp<'UserAvatarCameraModal'>,
+ +route: NavigationRoute<'UserAvatarCameraModal'>,
+};
+
+function UserAvatarCameraModal(props: Props): React.Node {
+ const { navigation } = props;
+
+ const { updateUserAvatar } = useSelectFromCameraAndUpdateUserAvatar();
+
+ const sendPhoto = React.useCallback(
+ (capture: PhotoCapture) => {
+ updateUserAvatar(capture);
+ },
+ [updateUserAvatar],
+ );
+
+ return <CameraModal handlePhotoCapture={sendPhoto} navigation={navigation} />;
+}
+
+export default UserAvatarCameraModal;
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
@@ -14,6 +14,7 @@
} from './overlay-navigator.react.js';
import type { RootNavigationProp } from './root-navigator.react.js';
import {
+ UserAvatarCameraModalRouteName,
ImageModalRouteName,
MultimediaMessageTooltipModalRouteName,
ActionResultModalRouteName,
@@ -34,6 +35,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 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';
import PushHandler from '../push/push-handler.react.js';
@@ -135,6 +137,10 @@
name={ChatCameraModalRouteName}
component={ChatCameraModal}
/>
+ <App.Screen
+ name={UserAvatarCameraModalRouteName}
+ component={UserAvatarCameraModal}
+ />
<App.Screen
name={VideoPlaybackModalRouteName}
component={VideoPlaybackModal}
diff --git a/native/navigation/route-names.js b/native/navigation/route-names.js
--- a/native/navigation/route-names.js
+++ b/native/navigation/route-names.js
@@ -78,6 +78,7 @@
export const ThreadSettingsMemberTooltipModalRouteName =
'ThreadSettingsMemberTooltipModal';
export const ThreadSettingsRouteName = 'ThreadSettings';
+export const UserAvatarCameraModalRouteName = 'UserAvatarCameraModal';
export const VideoPlaybackModalRouteName = 'VideoPlaybackModal';
export const TermsAndPrivacyRouteName = 'TermsAndPrivacyModal';
export const RegistrationRouteName = 'Registration';
@@ -117,6 +118,7 @@
+ImageModal: ImageModalParams,
+ActionResultModal: ActionResultModalParams,
+ChatCameraModal: ChatCameraModalParams,
+ +UserAvatarCameraModal: void,
+VideoPlaybackModal: VideoPlaybackModalParams,
...TooltipModalParamList,
};

File Metadata

Mime Type
text/plain
Expires
Sun, Jan 18, 8:37 PM (10 h, 16 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5952981
Default Alt Text
D7549.1768768624.diff (9 KB)

Event Timeline