Skip to content

Commit

Permalink
Temporary fix for modal being on top of some action menus (#6362)
Browse files Browse the repository at this point in the history
  • Loading branch information
gewfy authored Dec 6, 2024
2 parents 3a96cc7 + 4e05082 commit b047f89
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 136 deletions.
5 changes: 4 additions & 1 deletion client/src/lib/components/ProfileInfo/ProfileInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import useUpdateProfileDetails from '../../user/hooks/useUpdateProfileDetails';
import ProfilePicture from '../User/ProfilePicture';
import useUser from '../../user/hooks/useUser';
import {COLORS} from '../../../../../shared/src/constants/colors';
import useHideModalUntilResolved from '../../navigation/hooks/useHideModalUntilResolved';

const Container = styled.View({
alignItems: 'center',
Expand Down Expand Up @@ -46,6 +47,8 @@ const ProfileInfo: React.FC<ProfileInfoProps> = ({onSaveCallback}) => {
useChangeProfilePicture();
const {updateProfileDetails, isUpdatingProfileDetails} =
useUpdateProfileDetails();
const hideModalAndChangeProfilePicture =
useHideModalUntilResolved(changeProfilePicture);
const [displayName, setDisplayName] = useState(user?.displayName ?? '');
const [nameMissing, setNameMissing] = useState(false);
const [pictureMissing, setPictureMissing] = useState(false);
Expand Down Expand Up @@ -94,7 +97,7 @@ const ProfileInfo: React.FC<ProfileInfoProps> = ({onSaveCallback}) => {
pictureURL={user?.photoURL}
hasError={pictureMissing}
loading={isUpdatingProfilePicture}
onPress={changeProfilePicture}
onPress={hideModalAndChangeProfilePicture}
size={SPACINGS.NINTYSIX}
/>
<Spacer16 />
Expand Down
4 changes: 2 additions & 2 deletions client/src/lib/navigation/ModalStack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const modalScreenOptions: BottomSheetNavigationOptions = {
pressBehavior="close"
animatedIndex={animatedIndex}
animatedPosition={animatedPosition}
disappearsOnIndex={-1}
disappearsOnIndex={-0.1}
appearsOnIndex={0}
opacity={0.1}
style={style}
Expand Down Expand Up @@ -85,7 +85,7 @@ const modalScreenOptions: BottomSheetNavigationOptions = {
https://github.com/gorhom/react-native-bottom-sheet/issues/618
*/
android_keyboardInputMode: 'adjustResize',
stackBehavior: 'push',
stackBehavior: 'replace',
};

const ModalStack = () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
import {useBottomSheet} from '@gorhom/bottom-sheet';
import {useCallback} from 'react';
import {Share, ShareContent, ShareOptions} from 'react-native';

/*
This hook hides the current modal and opens the share modal to prevent it from being rendered behind the current modal
Bottom sheet modals are rendered with FullWindowModal, which is a full-screen modal that covers the entire screen
https://github.com/th3rdwave/react-navigation-bottom-sheet/blob/ef8c616559a3fdbb67149d6e1ebc9bb662d71255/src/BottomSheetView.tsx#L27-L31
*/
const useShareFromModal = () => {
const useHideModalUntilResolved = <TArgs extends any[]>(
fn: (...args: TArgs) => Promise<any>,
) => {
const bottomSheet = useBottomSheet();
return useCallback(
async (content: ShareContent, options?: ShareOptions) => {
async (...args: TArgs) => {
const currentSnapIndex = bottomSheet.animatedIndex.value;
bottomSheet.snapToPosition(0.0000001); // Hide without closing it
await Share.share(content, options);
const ret = await fn(...args);
bottomSheet.snapToIndex(currentSnapIndex); // Restore the position
return ret;
},
[bottomSheet],
[bottomSheet, fn],
);
};

export default useShareFromModal;
export default useHideModalUntilResolved;
231 changes: 118 additions & 113 deletions client/src/lib/user/hooks/useChangeProfilePicture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,127 +65,132 @@ const useChangeProfilePicture = () => {
const [isUpdatingProfilePicture, setIsUpdatingProfilePicture] =
useState(false);

const changeProfilePicture = useCallback(async () => {
await ensureUserCreated();

const currentUser = auth().currentUser;

const optionTitles = [
t('takePhotoButtonTitle'),
t('chooseFromLibraryButtonTitle'),
t('cancelButtonTitle'),
currentUser?.photoURL ? t('removeButtonTitle') : null,
].filter(Boolean) as Array<string>;

const optionIndex = {
CAMERA: 0,
LIBRARY: 1,
CANCEL: 2,
REMOVE: 3,
};

const imagePickerOptions = {
width: 500,
height: 500,
cropping: true,
useFrontCamera: true,
forceJpg: true,
};

const captureProfilePicture = async () => {
try {
const image = await openCamera(imagePickerOptions);
setIsUpdatingProfilePicture(true);
await uploadProfilePicture(image.path, currentUser);
setIsUpdatingProfilePicture(false);
} catch (error: any) {
setIsUpdatingProfilePicture(false);
if (error.code !== E_PICKER_CANCELLED) {
console.error(
new Error('Select profile from camera failed:', {
cause: error,
}),
);
}
}
};

ActionSheet.showActionSheetWithOptions(
{
title: t('title'),
options: optionTitles,
tintColor: 'blue',
cancelButtonIndex: optionIndex.CANCEL,
},
async buttonIndex => {
switch (buttonIndex) {
case optionIndex.CAMERA: {
if (Platform.OS !== 'android') {
captureProfilePicture();
} else {
const isCameraAvailable = await PermissionsAndroid.check(
PermissionsAndroid.PERMISSIONS.CAMERA,
const changeProfilePicture = useCallback(
() =>
new Promise<void>(async resolve => {
await ensureUserCreated();

const currentUser = auth().currentUser;

const optionTitles = [
t('takePhotoButtonTitle'),
t('chooseFromLibraryButtonTitle'),
t('cancelButtonTitle'),
currentUser?.photoURL ? t('removeButtonTitle') : null,
].filter(Boolean) as Array<string>;

const optionIndex = {
CAMERA: 0,
LIBRARY: 1,
CANCEL: 2,
REMOVE: 3,
};

const imagePickerOptions = {
width: 500,
height: 500,
cropping: true,
useFrontCamera: true,
forceJpg: true,
};

const captureProfilePicture = async () => {
try {
const image = await openCamera(imagePickerOptions);
setIsUpdatingProfilePicture(true);
await uploadProfilePicture(image.path, currentUser);
setIsUpdatingProfilePicture(false);
} catch (error: any) {
setIsUpdatingProfilePicture(false);
if (error.code !== E_PICKER_CANCELLED) {
console.error(
new Error('Select profile from camera failed:', {
cause: error,
}),
);
if (isCameraAvailable) {
captureProfilePicture();
} else {
try {
const granted = await PermissionsAndroid.request(
}
}
};

ActionSheet.showActionSheetWithOptions(
{
title: t('title'),
options: optionTitles,
tintColor: 'blue',
cancelButtonIndex: optionIndex.CANCEL,
},
async buttonIndex => {
switch (buttonIndex) {
case optionIndex.CAMERA: {
if (Platform.OS !== 'android') {
await captureProfilePicture();
} else {
const isCameraAvailable = await PermissionsAndroid.check(
PermissionsAndroid.PERMISSIONS.CAMERA,
{
buttonPositive: t('ok'),
title: t('alertCameraTitle'),
message: t('alertCameraWhy'),
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
captureProfilePicture();
} else if (
granted === PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN
) {
Alert.alert(
t('alertCameraTitle'),
t('alertCameraSettings'),
);
if (isCameraAvailable) {
await captureProfilePicture();
} else {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.CAMERA,
{
buttonPositive: t('ok'),
title: t('alertCameraTitle'),
message: t('alertCameraWhy'),
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
await captureProfilePicture();
} else if (
granted === PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN
) {
Alert.alert(
t('alertCameraTitle'),
t('alertCameraSettings'),
);
}
} catch (err) {
console.error(err);
}
}
} catch (err) {
console.error(err);
}
break;
}
}
break;
}
case optionIndex.LIBRARY:
openPicker(imagePickerOptions).then(
async image => {
case optionIndex.LIBRARY:
await openPicker(imagePickerOptions).then(
async image => {
setIsUpdatingProfilePicture(true);
await uploadProfilePicture(image.path, currentUser);
setIsUpdatingProfilePicture(false);
},
error => {
setIsUpdatingProfilePicture(false);
if (error.code !== E_PICKER_CANCELLED) {
console.error(
new Error('Select profile from library failed:', {
cause: error,
}),
);
}
},
);
break;
case optionIndex.REMOVE: {
setIsUpdatingProfilePicture(true);
await uploadProfilePicture(image.path, currentUser);
setIsUpdatingProfilePicture(false);
},
error => {
await removeProfilePicture(currentUser);
setIsUpdatingProfilePicture(false);
if (error.code !== E_PICKER_CANCELLED) {
console.error(
new Error('Select profile from library failed:', {
cause: error,
}),
);
}
},
);
break;
case optionIndex.REMOVE: {
setIsUpdatingProfilePicture(true);
await removeProfilePicture(currentUser);
setIsUpdatingProfilePicture(false);
break;
}
default:
break;
}
},
);
}, [t, setIsUpdatingProfilePicture]);
break;
}
default:
break;
}
resolve();
},
);
}),
[t, setIsUpdatingProfilePicture],
);

return {
changeProfilePicture,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {RouteProp, useIsFocused, useRoute} from '@react-navigation/native';
import React, {useCallback, useEffect} from 'react';
import {useTranslation} from 'react-i18next';
import {View} from 'react-native';
import {Share, View} from 'react-native';
import {BottomSheetScrollView} from '@gorhom/bottom-sheet';
import styled from 'styled-components/native';

Expand All @@ -25,7 +25,7 @@ import {SPACINGS} from '../../../lib/constants/spacings';
import {ModalHeading} from '../../../lib/components/Typography/Heading/Heading';
import useConfirmSessionReminder from '../../../lib/sessions/hooks/useConfirmSessionReminder';
import {getSessionHostingLink} from '../../../lib/sessions/api/session';
import useShareFromModal from '../../../lib/navigation/hooks/useShareFromModal';
import useHideModalUntilResolved from '../../../lib/navigation/hooks/useHideModalUntilResolved';

const Row = styled(View)({
flexDirection: 'row',
Expand All @@ -42,18 +42,18 @@ const AssignNewHostModal = () => {

const exercise = useExerciseById(session?.exerciseId, session?.language);
const confirmToggleReminder = useConfirmSessionReminder(session);
const share = useShareFromModal();
const hideModalAndShare = useHideModalUntilResolved(Share.share);

const isHost = user?.uid === session.hostId;

const onHostChange = useCallback(async () => {
const link = await getSessionHostingLink(session.id);
if (link) {
share({
hideModalAndShare({
message: link,
});
}
}, [session.id, share]);
}, [session.id, hideModalAndShare]);

useEffect(() => {
if (isHost) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, {Fragment, useCallback, useMemo} from 'react';
import {Share} from 'react-native';
import {useTranslation} from 'react-i18next';

import styled from 'styled-components/native';
Expand Down Expand Up @@ -56,7 +57,7 @@ import CoCreators from '../../../../../lib/components/CoCreators/CoCreators';
import CardGraphic from '../../../../../lib/components/CardGraphic/CardGraphic';
import BackgroundBlock from '../../../../../lib/components/BackgroundBlock/BackgroundBlock';
import useGetSessionCardTags from '../../../../../lib/components/Cards/SessionCard/hooks/useGetSessionCardTags';
import useShareFromModal from '../../../../../lib/navigation/hooks/useShareFromModal';
import useHideModalUntilResolved from '../../../../../lib/navigation/hooks/useHideModalUntilResolved';

const TypeItemWrapper = styled.View<{isLast?: boolean}>(({isLast}) => ({
flexDirection: 'row',
Expand Down Expand Up @@ -161,7 +162,7 @@ const SelectTypeStep: React.FC<StepProps> = ({
useNavigation<NativeStackNavigationProp<ModalStackProps>>();
const getExerciseById = useGetExerciseById();
const startSession = useStartAsyncSession();
const share = useShareFromModal();
const hideModalAndShare = useHideModalUntilResolved(Share.share);

const {rating} = useExerciseRating(selectedExercise);
const {feedback} = useExerciseFeedback(selectedExercise);
Expand Down Expand Up @@ -196,11 +197,11 @@ const SelectTypeStep: React.FC<StepProps> = ({

const onShare = useCallback(() => {
if (exercise?.link) {
share({
hideModalAndShare({
message: exercise.link,
});
}
}, [exercise?.link, share]);
}, [exercise?.link, hideModalAndShare]);

const onStartPress = useCallback(() => {
if (selectedExercise) {
Expand Down
Loading

0 comments on commit b047f89

Please sign in to comment.