From 1dd8130cadda00f901b16136c125492056fbcb0a Mon Sep 17 00:00:00 2001 From: ErikSin <67773827+ErikSin@users.noreply.github.com> Date: Wed, 1 May 2024 14:26:42 -0700 Subject: [PATCH] feat: update header with new UI (#312) * chore: update header to include modal * chore: translations * chore: missing dependencies * chore: update save icon * chore: delete old save icon * chore: update use of save icon * chore: destructure foruseEffect dependecy --- messages/en.json | 16 +- src/frontend/images/CheckMark.svg | 4 + .../screens/ObservationEdit/SaveButton.tsx | 4 +- .../screens/ObservationEdit/index.tsx | 6 +- src/frontend/screens/PresetChooser.tsx | 2 +- .../ProjectSettings/DeviceName/EditScreen.tsx | 37 ++--- .../CustomHeaderLeftClose.tsx | 153 +++++++++++------- .../sharedComponents/icons/SaveIcon.tsx | 36 ----- src/frontend/sharedComponents/icons/index.tsx | 1 - 9 files changed, 127 insertions(+), 132 deletions(-) create mode 100644 src/frontend/images/CheckMark.svg delete mode 100644 src/frontend/sharedComponents/icons/SaveIcon.tsx diff --git a/messages/en.json b/messages/en.json index 8c74013b0..29b3c3229 100644 --- a/messages/en.json +++ b/messages/en.json @@ -3,17 +3,23 @@ "description": "Button on dialog to keep editing (cancelling close action)", "message": "Continue editing" }, - "AppContainer.EditHeader.discardChangesContent": { - "description": "Button on dialog to cancel observation edits", + "AppContainer.EditHeader.discardChangesButton": { + "description": "Title of dialog that shows when cancelling observation edits", "message": "Discard changes" }, + "AppContainer.EditHeader.discardChangesDescription": { + "message": "Your changes will not be saved. This cannot be undone." + }, "AppContainer.EditHeader.discardChangesTitle": { "description": "Title of dialog that shows when cancelling observation edits", "message": "Discard changes?" }, - "AppContainer.EditHeader.discardContent": { - "description": "Button on dialog to cancel a new observation", - "message": "Discard without saving" + "AppContainer.EditHeader.discardObservationButton": { + "description": "Title of dialog that shows when cancelling observation edits", + "message": "Discard Observation" + }, + "AppContainer.EditHeader.discardObservationDescription": { + "message": "Your Observation will not be saved. This cannot be undone." }, "AppContainer.EditHeader.discardTitle": { "description": "Title of dialog that shows when cancelling a new observation", diff --git a/src/frontend/images/CheckMark.svg b/src/frontend/images/CheckMark.svg new file mode 100644 index 000000000..1074ee1b0 --- /dev/null +++ b/src/frontend/images/CheckMark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/frontend/screens/ObservationEdit/SaveButton.tsx b/src/frontend/screens/ObservationEdit/SaveButton.tsx index a9440a01b..4df3f4dc0 100644 --- a/src/frontend/screens/ObservationEdit/SaveButton.tsx +++ b/src/frontend/screens/ObservationEdit/SaveButton.tsx @@ -4,7 +4,6 @@ import debug from 'debug'; import {defineMessages, useIntl} from 'react-intl'; import {IconButton} from '../../sharedComponents/IconButton'; -import {SaveIcon} from '../../sharedComponents/icons/SaveIcon'; import {useNavigationFromRoot} from '../../hooks/useNavigationWithTypes'; import {usePersistedDraftObservation} from '../../hooks/persistedState/usePersistedDraftObservation'; import {useCreateObservation} from '../../hooks/server/observations'; @@ -14,6 +13,7 @@ import {useCreateBlobMutation} from '../../hooks/server/media'; import {DraftPhoto, Photo} from '../../contexts/PhotoPromiseContext/types'; import {useDraftObservation} from '../../hooks/useDraftObservation'; import {useCurrentTrackStore} from '../../hooks/tracks/useCurrentTrackStore'; +import SaveCheck from '../../images/CheckMark.svg'; const m = defineMessages({ noGpsTitle: { @@ -242,7 +242,7 @@ export const SaveButton = ({ ) : ( - + ); }; diff --git a/src/frontend/screens/ObservationEdit/index.tsx b/src/frontend/screens/ObservationEdit/index.tsx index 6b6ee09ce..d9de9240a 100644 --- a/src/frontend/screens/ObservationEdit/index.tsx +++ b/src/frontend/screens/ObservationEdit/index.tsx @@ -56,9 +56,9 @@ export const ObservationEdit: NativeNavigationComponent<'ObservationEdit'> & { navigation.navigate('AddPhoto'); }, [navigation]); - const handleDetailsPress = React.useCallback(() => { - navigation.navigate('ObservationDetails', {question: 1}); - }, [navigation]); + // const handleDetailsPress = React.useCallback(() => { + // navigation.navigate('ObservationDetails', {question: 1}); + // }, [navigation]); const bottomSheetItems = [ { diff --git a/src/frontend/screens/PresetChooser.tsx b/src/frontend/screens/PresetChooser.tsx index 4b65f427f..f4f553b1b 100644 --- a/src/frontend/screens/PresetChooser.tsx +++ b/src/frontend/screens/PresetChooser.tsx @@ -51,7 +51,7 @@ export const PresetChooser: NativeNavigationComponent<'PresetChooser'> = ({ ), }); - }, [prevRouteNameInStack, CustomHeaderLeft, CustomHeaderLeftClose]); + }, [prevRouteNameInStack, navigation]); const presetsList = !presets ? null diff --git a/src/frontend/screens/Settings/ProjectSettings/DeviceName/EditScreen.tsx b/src/frontend/screens/Settings/ProjectSettings/DeviceName/EditScreen.tsx index d3b92fc95..0ea5c7d8e 100644 --- a/src/frontend/screens/Settings/ProjectSettings/DeviceName/EditScreen.tsx +++ b/src/frontend/screens/Settings/ProjectSettings/DeviceName/EditScreen.tsx @@ -13,7 +13,7 @@ import { import {BLACK} from '../../../../lib/styles'; import {HookFormTextInput} from '../../../../sharedComponents/HookFormTextInput'; import {IconButton} from '../../../../sharedComponents/IconButton'; -import {SaveIcon} from '../../../../sharedComponents/icons'; +import SaveIcon from '../../../../images/CheckMark.svg'; import {useBottomSheetModal} from '../../../../sharedComponents/BottomSheetModal'; import {ErrorModal} from '../../../../sharedComponents/ErrorModal'; import {FieldRow} from './FieldRow'; @@ -72,14 +72,16 @@ export const EditScreen = ({ deviceName: string; }>({defaultValues: {deviceName}}); - const editDeviceInfoMutation = useEditDeviceInfo(); + const {isPending, mutate} = useEditDeviceInfo(); const {isDirty: nameHasChanges} = control.getFieldState( 'deviceName', formState, ); - const errorModal = useBottomSheetModal({openOnMount: false}); + const {openSheet, sheetRef, closeSheet, isOpen} = useBottomSheetModal({ + openOnMount: false, + }); React.useEffect( function showDiscardChangesAlert() { @@ -117,7 +119,7 @@ export const EditScreen = ({ return ( {} : handleSubmit(async value => { if (!nameHasChanges) { @@ -125,33 +127,22 @@ export const EditScreen = ({ return; } - editDeviceInfoMutation.mutate(value.deviceName, { + mutate(value.deviceName, { onSuccess: () => navigation.navigate('DeviceNameDisplay'), onError: () => { - errorModal.openSheet(); + openSheet(); }, }); }) }> - {editDeviceInfoMutation.isPending ? ( - - ) : ( - - )} + {isPending ? : } ); }, }); }, - [ - handleSubmit, - navigation, - editDeviceInfoMutation.mutate, - editDeviceInfoMutation.isPending, - nameHasChanges, - errorModal.openSheet, - ], + [handleSubmit, navigation, isPending, mutate, nameHasChanges, openSheet], ); return ( @@ -168,15 +159,11 @@ export const EditScreen = ({ style={{flex: 1, color: BLACK, fontSize: 16}} showCharacterCount autoFocus - editable={!editDeviceInfoMutation.isPending} + editable={isPending} /> - + ); }; diff --git a/src/frontend/sharedComponents/CustomHeaderLeftClose.tsx b/src/frontend/sharedComponents/CustomHeaderLeftClose.tsx index 53fce6bba..0551caef9 100644 --- a/src/frontend/sharedComponents/CustomHeaderLeftClose.tsx +++ b/src/frontend/sharedComponents/CustomHeaderLeftClose.tsx @@ -1,7 +1,7 @@ import React from 'react'; import {HeaderBackButton} from '@react-navigation/elements'; import {HeaderBackButtonProps} from '@react-navigation/native-stack/lib/typescript/src/types'; -import {Alert, BackHandler} from 'react-native'; +import {BackHandler} from 'react-native'; import isEqual from 'lodash.isequal'; import {CloseIcon} from './icons'; @@ -18,6 +18,14 @@ import { useFocusEffect, useNavigation, } from '@react-navigation/native'; +import { + BottomSheetContent, + BottomSheetModal, + useBottomSheetModal, +} from './BottomSheetModal'; + +import ErrorIcon from '../images/Error.svg'; +import DiscardIcon from '../images/delete.svg'; const m = defineMessages({ discardTitle: { @@ -25,26 +33,35 @@ const m = defineMessages({ defaultMessage: 'Discard observation?', description: 'Title of dialog that shows when cancelling a new observation', }, - discardConfirm: { - id: 'AppContainer.EditHeader.discardContent', - defaultMessage: 'Discard without saving', - description: 'Button on dialog to cancel a new observation', + discardObservationDescription: { + id: 'AppContainer.EditHeader.discardObservationDescription', + defaultMessage: + 'Your Observation will not be saved. This cannot be undone. ', }, discardChangesTitle: { id: 'AppContainer.EditHeader.discardChangesTitle', defaultMessage: 'Discard changes?', description: 'Title of dialog that shows when cancelling observation edits', }, - discardChangesConfirm: { - id: 'AppContainer.EditHeader.discardChangesContent', - defaultMessage: 'Discard changes', - description: 'Button on dialog to cancel observation edits', + discardChangesDescription: { + id: 'AppContainer.EditHeader.discardChangesDescription', + defaultMessage: 'Your changes will not be saved. This cannot be undone. ', }, discardCancel: { id: 'AppContainer.EditHeader.discardCancel', defaultMessage: 'Continue editing', description: 'Button on dialog to keep editing (cancelling close action)', }, + discardObservationButton: { + id: 'AppContainer.EditHeader.discardObservationButton', + defaultMessage: 'Discard Observation', + description: 'Title of dialog that shows when cancelling observation edits', + }, + discardChangesButton: { + id: 'AppContainer.EditHeader.discardChangesButton', + defaultMessage: 'Discard changes', + description: 'Title of dialog that shows when cancelling observation edits', + }, }); // We use a slightly larger back icon, to improve accessibility @@ -68,51 +85,80 @@ export const CustomHeaderLeftClose = ({ headerBackButtonProps, observationId, }: CustomHeaderLeftCloseProps) => { - if (observationId) { - return ( - + const {isOpen, sheetRef, closeSheet, openSheet} = useBottomSheetModal({ + openOnMount: false, + }); + const {formatMessage} = useIntl(); + const {clearDraft} = useDraftObservation(); + const navigation = useNavigationFromRoot(); + + const handleDiscard = React.useCallback(() => { + clearDraft(); + navigation.dispatch( + CommonActions.reset({index: 0, routes: [{name: 'Home'}]}), ); - } + }, [clearDraft, navigation]); return ( - + <> + {observationId ? ( + + ) : ( + + )} + + , + }, + { + onPress: closeSheet, + text: formatMessage(m.discardCancel), + variation: 'outlined', + }, + ]} + icon={} + /> + + ); }; const HeaderBackNewObservation = ({ tintColor, headerBackButtonProps, -}: SharedBackButtonProps) => { - const navigation = useNavigationFromRoot(); - const {formatMessage: t} = useIntl(); - const {clearDraft} = useDraftObservation(); - - const onGoBack = React.useCallback(() => { - Alert.alert(t(m.discardTitle), undefined, [ - { - text: t(m.discardConfirm), - onPress: () => { - clearDraft(); - navigation.dispatch( - CommonActions.reset({index: 0, routes: [{name: 'Home'}]}), - ); - }, - }, - {text: t(m.discardCancel), onPress: () => {}}, - ]); - }, [navigation, clearDraft, t]); - + openBottomSheet, +}: SharedBackButtonProps & {openBottomSheet: () => void}) => { useFocusEffect( React.useCallback(() => { const onBackPress = () => { - onGoBack(); + openBottomSheet(); return true; }; @@ -122,32 +168,30 @@ const HeaderBackNewObservation = ({ ); return () => subscription.remove(); - }, [onGoBack]), + }, [openBottomSheet]), ); return ( ); }; type HeaderBackEditObservationProps = { observationId: string; + openBottomSheet: () => void; } & SharedBackButtonProps; const HeaderBackEditObservation = ({ headerBackButtonProps, tintColor, - + openBottomSheet, observationId, }: HeaderBackEditObservationProps) => { const navigation = useNavigationFromRoot(); - const {formatMessage: t} = useIntl(); - - const {clearDraft} = useDraftObservation(); const {observation} = useObservationWithPreset(observationId); const photos = usePersistedDraftObservation(store => store.photos); const draftObservation = usePersistedDraftObservation(store => store.value); @@ -165,20 +209,11 @@ const HeaderBackEditObservation = ({ e.preventDefault(); - Alert.alert(t(m.discardChangesTitle), undefined, [ - { - text: t(m.discardChangesConfirm), - onPress: () => { - clearDraft(); - navigation.dispatch(e.data.action); - }, - }, - {text: t(m.discardCancel), onPress: () => {}}, - ]); + openBottomSheet(); }); return () => unsubscribe(); - }, [observation, photos, draftObservation, navigation, clearDraft]); + }, [observation, photos, draftObservation, openBottomSheet, navigation]); return ( ( - - - - - -); - -const styles = StyleSheet.create({ - outerCircle: { - width: 30, - height: 30, - backgroundColor: DARK_MANGO, - borderRadius: 50, - justifyContent: 'center', - alignItems: 'center', - }, - innerCircle: { - backgroundColor: MANGO, - height: 25, - width: 25, - borderRadius: 50, - justifyContent: 'center', - alignItems: 'center', - }, -}); diff --git a/src/frontend/sharedComponents/icons/index.tsx b/src/frontend/sharedComponents/icons/index.tsx index 636955b85..eadbba958 100644 --- a/src/frontend/sharedComponents/icons/index.tsx +++ b/src/frontend/sharedComponents/icons/index.tsx @@ -21,7 +21,6 @@ type ImageIconProps = { export {GpsIcon} from './GpsIcon'; // export { CategoryIcon, CategoryCircleIcon } from "./CategoryIcon"; -export {SaveIcon} from './SaveIcon'; export {SyncIconCircle} from './SyncIconCircle'; export const AlertIcon = ({size = 30, color = RED, style}: FontIconProps) => (