Skip to content

Commit

Permalink
[native] Introduce useNativeUpdateUserImageAvatar() to `avatar-hook…
Browse files Browse the repository at this point in the history
…s.react`

Summary:
The `updateImageUserAvatar` function within `*EditUserAvatarProvider` previously contained a call to the `displayFailureAlert(...)` function, which was passed in via props. However, the `displayFailureAlert` function is only relevant on `native` as we surface errors differently on `web`.

As part of making `EditUserAvatarProvider` platform-agnostic, we introduce the `useNativeUpdateUserImageAvatar()` hook. The function it "creates" encapsulates its call to `updateImageUserAvatar` in a `try/catch` block that handles errors in a `native`-specific way. As a result, `updateImageUserAvatar` can throw a plain old exception that will be caught by platform-specific "wrapper" functions.

In subsequent diffs we'll do the same thing for `setUserAvatar` so we can completely remove the `displayFailureAlert` prop from `*EditUserAvatarProvider`. After that we'll work on removing the `useUploadSelectedMedia` prop so the provider is fully "platform-agnostic." At that point we'll be able to consolidate `BaseEditUserAvatarProvider`, `NativeEditUserAvatarProvider`, and `WebEditUserAvatarProvider` into a single `EditUserAvatarProvider` component.

Test Plan:
1. Modify `update_user_avatar` endpoint to throw `ServerError` (we can't just kill `keyserver` because we want to make sure the alert is triggered by the `update_user_avatar`-specific codepath which we modified).
2. Try to set a user image avatar via "Camera" flow
3. Ensure that I see expected Alert.

Also, make sure the "happy case" (unmodified `update_user_avatar` endpoint) continues working as expected).

WARNING: ~~I haven't actually gone through the Test Plan yet, will sequence at the end of all of the refactors and **definitely** before landing.~~

{F609721}

{F609722}

Reviewers: ashoat, ginsu, rohan

Reviewed By: ashoat

Subscribers: tomek

Differential Revision: https://phab.comm.dev/D8339
  • Loading branch information
atulsmadhugiri committed Jul 28, 2023
1 parent 4ca1082 commit 3e3e93c
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 36 deletions.
14 changes: 2 additions & 12 deletions lib/components/base-edit-user-avatar-provider.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,22 +93,12 @@ function BaseEditUserAvatarProvider(props: Props): React.Node {

const promise = (async () => {
setUserAvatarMediaUploadInProgress(false);
try {
return await updateUserAvatarCall(imageAvatarUpdateRequest);
} catch (e) {
displayFailureAlert && displayFailureAlert();
throw e;
}
return await updateUserAvatarCall(imageAvatarUpdateRequest);
})();
dispatchActionPromise(updateUserAvatarActionTypes, promise);
await promise;
},
[
uploadSelectedMedia,
dispatchActionPromise,
updateUserAvatarCall,
displayFailureAlert,
],
[uploadSelectedMedia, dispatchActionPromise, updateUserAvatarCall],
);

const setUserAvatar = React.useCallback(
Expand Down
32 changes: 32 additions & 0 deletions native/avatars/avatar-hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ import { useSelector } from '../redux/redux-utils.js';
import { useStyles } from '../themes/colors.js';
import { useStaffCanSee } from '../utils/staff-utils.js';

function displayAvatarUpdateFailureAlert(): void {
Alert.alert(
'Couldn’t save avatar',
'Please try again later',
[{ text: 'OK' }],
{ cancelable: true },
);
}

function useUploadProcessedMedia(): MediaResult => Promise<UploadMultimediaResult> {
const callUploadMultimedia = useServerCall(uploadMultimedia);
const uploadProcessedMultimedia: MediaResult => Promise<UploadMultimediaResult> =
Expand Down Expand Up @@ -183,6 +192,27 @@ function useUploadSelectedMedia(
);
}

function useNativeUpdateUserImageAvatar(): (
selection: NativeMediaSelection,
) => Promise<void> {
const editUserAvatarContext = React.useContext(EditUserAvatarContext);
invariant(editUserAvatarContext, 'editUserAvatarContext must be defined');
const { updateImageUserAvatar } = editUserAvatarContext;

const nativeUpdateUserImageAvatar = React.useCallback(
async (selection: NativeMediaSelection) => {
try {
await updateImageUserAvatar(selection);
} catch {
displayAvatarUpdateFailureAlert();
}
},
[updateImageUserAvatar],
);

return nativeUpdateUserImageAvatar;
}

function useSelectFromGalleryAndUpdateUserAvatar(): () => Promise<void> {
const editUserAvatarContext = React.useContext(EditUserAvatarContext);
invariant(editUserAvatarContext, 'updateImageUserAvatar must be defined');
Expand Down Expand Up @@ -324,10 +354,12 @@ const unboundStyles = {
};

export {
displayAvatarUpdateFailureAlert,
selectFromGallery,
useUploadSelectedMedia,
useUploadProcessedMedia,
useProcessSelectedMedia,
useShowAvatarActionSheet,
useSelectFromGalleryAndUpdateUserAvatar,
useNativeUpdateUserImageAvatar,
};
14 changes: 4 additions & 10 deletions native/avatars/native-edit-user-avatar-provider.react.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
// @flow

import * as React from 'react';
import { Alert } from 'react-native';

import { BaseEditUserAvatarProvider } from 'lib/components/base-edit-user-avatar-provider.react.js';

import { useUploadSelectedMedia } from './avatar-hooks.js';

const displayAvatarUpdateFailureAlert = () =>
Alert.alert(
'Couldn’t save avatar',
'Please try again later',
[{ text: 'OK' }],
{ cancelable: true },
);
import {
useUploadSelectedMedia,
displayAvatarUpdateFailureAlert,
} from './avatar-hooks.js';

type Props = {
+children: React.Node,
Expand Down
10 changes: 3 additions & 7 deletions native/media/registration-user-avatar-camera-modal.react.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
// @flow

import invariant from 'invariant';
import * as React from 'react';

import { EditUserAvatarContext } from 'lib/components/base-edit-user-avatar-provider.react.js';

import type { RegistrationNavigationProp } from '../account/registration/registration-navigator.react.js';
import { useNativeUpdateUserImageAvatar } from '../avatars/avatar-hooks.js';
import CameraModal from '../media/camera-modal.react.js';
import type { NavigationRoute } from '../navigation/route-names.js';

Expand All @@ -17,13 +15,11 @@ type Props = {
function RegistrationUserAvatarCameraModal(props: Props): React.Node {
const { navigation } = props;

const editUserAvatarContext = React.useContext(EditUserAvatarContext);
invariant(editUserAvatarContext, 'editUserAvatarContext should be set');
const { updateImageUserAvatar } = editUserAvatarContext;
const nativeUpdateUserImageAvatar = useNativeUpdateUserImageAvatar();

return (
<CameraModal
handlePhotoCapture={updateImageUserAvatar}
handlePhotoCapture={nativeUpdateUserImageAvatar}
navigation={navigation}
/>
);
Expand Down
10 changes: 3 additions & 7 deletions native/media/user-avatar-camera-modal.react.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
// @flow

import invariant from 'invariant';
import * as React from 'react';

import { EditUserAvatarContext } from 'lib/components/base-edit-user-avatar-provider.react.js';

import { useNativeUpdateUserImageAvatar } 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';
Expand All @@ -17,13 +15,11 @@ type Props = {
function UserAvatarCameraModal(props: Props): React.Node {
const { navigation } = props;

const editUserAvatarContext = React.useContext(EditUserAvatarContext);
invariant(editUserAvatarContext, 'editUserAvatarContext should be set');
const { updateImageUserAvatar } = editUserAvatarContext;
const nativeUpdateUserImageAvatar = useNativeUpdateUserImageAvatar();

return (
<CameraModal
handlePhotoCapture={updateImageUserAvatar}
handlePhotoCapture={nativeUpdateUserImageAvatar}
navigation={navigation}
/>
);
Expand Down

0 comments on commit 3e3e93c

Please sign in to comment.