From 9b90d9e69ab2369b0eb633d58c9b539a2aa73b4e Mon Sep 17 00:00:00 2001 From: Cindy Green Date: Wed, 14 Aug 2024 18:20:18 -0400 Subject: [PATCH 01/25] Starts to add sync settings screen. --- src/frontend/Navigation/Stack/AppScreens.tsx | 6 ++ .../persistedState/usePersistedSettings.ts | 4 + .../Settings/ProjectSettings/SyncSettings.tsx | 100 ++++++++++++++++++ .../Settings/ProjectSettings/index.tsx | 11 ++ src/frontend/sharedTypes/navigation.ts | 1 + 5 files changed, 122 insertions(+) create mode 100644 src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx diff --git a/src/frontend/Navigation/Stack/AppScreens.tsx b/src/frontend/Navigation/Stack/AppScreens.tsx index e5e1d8c31..b11d8a3c9 100644 --- a/src/frontend/Navigation/Stack/AppScreens.tsx +++ b/src/frontend/Navigation/Stack/AppScreens.tsx @@ -66,6 +66,7 @@ import { } from '../../screens/ObservationCreate'; import {AboutSettings} from '../../screens/Settings/About'; import {CreateTestDataScreen} from '../../screens/Settings/CreateTestData'; +import {SyncSettings} from '../../screens/Settings/ProjectSettings/SyncSettings'; export const TAB_BAR_HEIGHT = 70; @@ -273,6 +274,11 @@ export const createDefaultScreenGroup = ({ component={AboutSettings} options={{headerTitle: intl(AboutSettings.navTitle)}} /> + {process.env.EXPO_PUBLIC_FEATURE_TEST_DATA_UI && ( void; setManualCoordinateEntryFormat: ( coordinateFormat: CoordinateFormat, ) => void; + setSyncSetting: (syncSetting: 'previews' | 'everything') => void; }; }; const settingsSlice: StateCreator = (set, get) => ({ coordinateFormat: 'utm', manualCoordinateEntryFormat: 'utm', + syncSetting: 'previews', actions: { setCoordinateFormat: coordinateFormat => set({coordinateFormat}), setManualCoordinateEntryFormat: coordinateFormat => set({manualCoordinateEntryFormat: coordinateFormat}), + setSyncSetting: syncSetting => set({syncSetting}), }, }); diff --git a/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx b/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx new file mode 100644 index 000000000..7d5b28c17 --- /dev/null +++ b/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx @@ -0,0 +1,100 @@ +import * as React from 'react'; +import {ScrollView, View, Text, StyleSheet} from 'react-native'; +import {useIntl, defineMessages} from 'react-intl'; +import {SelectOne} from '../../../sharedComponents/SelectOne'; +import {SYNC_BACKGROUND, NEW_DARK_GREY} from '../../../lib/styles'; +import { + usePersistedSettings, + usePersistedSettingsAction, +} from '../../../hooks/persistedState/usePersistedSettings'; + +const m = defineMessages({ + syncSettingsTitle: { + id: 'screens.SyncSettings.title', + defaultMessage: 'Sync Settings', + }, + syncPreviews: { + id: 'screens.SyncSettings.syncPreviews', + defaultMessage: 'Sync Previews (Photos Only)', + }, + syncPreviewsDescription: { + id: 'screens.SyncSettings.syncPreviewsDescription', + defaultMessage: + 'Photos will sync at a reduced smaller size. Device will not sync audio or video.', + }, + syncEverything: { + id: 'screens.SyncSettings.syncEverything', + defaultMessage: 'Sync Everything', + }, + syncEverythingDescription: { + id: 'screens.SyncSettings.syncEverythingDescription', + defaultMessage: + 'Your device will sync all content at full size, including photos, audio, and videos.\n\nNote: This will use more storage.', + }, +}); + +export const SyncSettings = () => { + const {formatMessage: t} = useIntl(); + const syncSetting = usePersistedSettings(store => store.syncSetting); + const {setSyncSetting} = usePersistedSettingsAction(); + + const options: { + value: 'previews' | 'everything'; + label: string; + hint?: string; + }[] = [ + { + value: 'previews', + label: t(m.syncPreviews), + hint: t(m.syncPreviewsDescription), + }, + { + value: 'everything', + label: t(m.syncEverything), + hint: t(m.syncEverythingDescription), + }, + ]; + + return ( + + + + ); +}; + +SyncSettings.navTitle = m.syncSettingsTitle; + +const styles = StyleSheet.create({ + container: { + padding: 20, + }, + title: { + fontSize: 20, + fontWeight: 'bold', + }, + optionContainer: { + marginVertical: 15, + flexDirection: 'row', + alignItems: 'center', + }, + label: { + fontSize: 14, + color: 'black', + flex: 1, + }, + hint: { + fontSize: 14, + color: NEW_DARK_GREY, + flex: 3, + marginLeft: 20, + }, + note: { + marginTop: 20, + fontSize: 12, + color: NEW_DARK_GREY, + }, +}); diff --git a/src/frontend/screens/Settings/ProjectSettings/index.tsx b/src/frontend/screens/Settings/ProjectSettings/index.tsx index ea0672f9f..92e3ab7e2 100644 --- a/src/frontend/screens/Settings/ProjectSettings/index.tsx +++ b/src/frontend/screens/Settings/ProjectSettings/index.tsx @@ -16,6 +16,10 @@ const m = defineMessages({ id: 'Screens.Settings.ProjectSettings.yourTeam', defaultMessage: 'Your Team', }, + syncSettings: { + id: 'Screens.Settings.ProjectSettings.syncSettings', + defaultMessage: 'Sync Settings', + }, }); export const ProjectSettings: NativeNavigationComponent<'ProjectSettings'> = ({ @@ -38,6 +42,13 @@ export const ProjectSettings: NativeNavigationComponent<'ProjectSettings'> = ({ }}> } /> + { + navigation.navigate('SyncSettings'); + }}> + } /> + ); diff --git a/src/frontend/sharedTypes/navigation.ts b/src/frontend/sharedTypes/navigation.ts index 3afd8a7c7..0f7351f70 100644 --- a/src/frontend/sharedTypes/navigation.ts +++ b/src/frontend/sharedTypes/navigation.ts @@ -96,6 +96,7 @@ export type RootStackParamsList = { SaveTrack: undefined; Sync: undefined; CreateTestData: undefined; + SyncSettings: undefined; }; export type DeviceNamingParamsList = { From 08f15c3bd437a73996415e7c72a0c5734d7873a3 Mon Sep 17 00:00:00 2001 From: Cindy Green Date: Wed, 14 Aug 2024 18:20:49 -0400 Subject: [PATCH 02/25] Messages. --- messages/en.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/messages/en.json b/messages/en.json index 3882e505d..eb32b8488 100644 --- a/messages/en.json +++ b/messages/en.json @@ -135,6 +135,9 @@ "Screens.Settings.ProjectSettings.deviceName": { "message": "Device Name" }, + "Screens.Settings.ProjectSettings.syncSettings": { + "message": "Sync Settings" + }, "Screens.Settings.ProjectSettings.title": { "message": "Project Settings" }, @@ -1029,6 +1032,21 @@ "screens.Sync.ProjectSyncDisplay.upToDate": { "message": "Up to Date! No data to Sync" }, + "screens.SyncSettings.syncEverything": { + "message": "Sync Everything" + }, + "screens.SyncSettings.syncEverythingDescription": { + "message": "Your device will sync all content at full size, including photos, audio, and videos. Note: This will use more storage." + }, + "screens.SyncSettings.syncPreviews": { + "message": "Sync Previews (Photos Only)" + }, + "screens.SyncSettings.syncPreviewsDescription": { + "message": "Photos will sync at a reduced smaller size. Device will not sync audio or video." + }, + "screens.SyncSettings.title": { + "message": "Sync Settings" + }, "shareComponent.KeyboardAccessory.showOptions": { "description": "title for observation options", "message": "Show Options" From 7cfd8e8f74f5424117baa28f71bb8fd0aea65257 Mon Sep 17 00:00:00 2001 From: Cindy Green Date: Tue, 3 Sep 2024 13:40:16 -0400 Subject: [PATCH 03/25] Styles sync settings screen. --- .../Settings/ProjectSettings/SyncSettings.tsx | 4 ++- .../sharedComponents/List/ListItemIcon.tsx | 4 +-- src/frontend/sharedComponents/SelectOne.tsx | 26 ++++++++++++++++++- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx b/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx index 7d5b28c17..f8e120f48 100644 --- a/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx +++ b/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import {ScrollView, View, Text, StyleSheet} from 'react-native'; +import {ScrollView, StyleSheet} from 'react-native'; import {useIntl, defineMessages} from 'react-intl'; import {SelectOne} from '../../../sharedComponents/SelectOne'; import {SYNC_BACKGROUND, NEW_DARK_GREY} from '../../../lib/styles'; @@ -61,6 +61,8 @@ export const SyncSettings = () => { value={syncSetting} onChange={setSyncSetting} options={options} + radioButtonPosition="right" + color={SYNC_BACKGROUND} /> ); diff --git a/src/frontend/sharedComponents/List/ListItemIcon.tsx b/src/frontend/sharedComponents/List/ListItemIcon.tsx index 01d6dae22..b63415aab 100644 --- a/src/frontend/sharedComponents/List/ListItemIcon.tsx +++ b/src/frontend/sharedComponents/List/ListItemIcon.tsx @@ -3,7 +3,7 @@ import {StyleSheet, View} from 'react-native'; import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; import {ViewStyleProp} from '../../sharedTypes'; -type ListItemIconProps = {style?: ViewStyleProp} & ( +type ListItemIconProps = {style?: ViewStyleProp; color?: string} & ( | { iconName: string; } @@ -22,7 +22,7 @@ export const ListItemIcon = (props: ListItemIconProps) => { )} diff --git a/src/frontend/sharedComponents/SelectOne.tsx b/src/frontend/sharedComponents/SelectOne.tsx index 6f2389c33..eca03630a 100644 --- a/src/frontend/sharedComponents/SelectOne.tsx +++ b/src/frontend/sharedComponents/SelectOne.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import {StyleSheet} from 'react-native'; import {List, ListItem, ListItemText, ListItemIcon} from './List'; @@ -10,12 +11,16 @@ type SelectOneProps = { label: string; hint?: string; }>; + radioButtonPosition?: 'left' | 'right'; + color?: string; }; export const SelectOne = ({ value, options, onChange, + radioButtonPosition = 'left', + color, }: SelectOneProps) => ( {options.map((item, index) => ( @@ -26,16 +31,35 @@ export const SelectOne = ({ : index } testID={`${item.value}LanguageButton`} - onPress={() => value !== item.value && onChange(item.value)}> + onPress={() => value !== item.value && onChange(item.value)} + style={[radioButtonPosition === 'right' && styles.rowReverse]}> ))} ); + +const styles = StyleSheet.create({ + rowReverse: { + flexDirection: 'row-reverse', + }, + alignRight: { + alignItems: 'flex-end', + }, + alignLeft: { + alignItems: 'flex-start', + }, +}); From bcfee3b9623f36226c0cb3e0c5b206681ff39cce Mon Sep 17 00:00:00 2001 From: Cindy Green Date: Tue, 3 Sep 2024 16:03:09 -0400 Subject: [PATCH 04/25] Adds bold --- .../screens/Settings/ProjectSettings/SyncSettings.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx b/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx index f8e120f48..28b0104dc 100644 --- a/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx +++ b/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx @@ -20,7 +20,7 @@ const m = defineMessages({ syncPreviewsDescription: { id: 'screens.SyncSettings.syncPreviewsDescription', defaultMessage: - 'Photos will sync at a reduced smaller size. Device will not sync audio or video.', + 'Photos will sync at a reduced smaller size. Device will not sync audio or video.', }, syncEverything: { id: 'screens.SyncSettings.syncEverything', @@ -29,7 +29,7 @@ const m = defineMessages({ syncEverythingDescription: { id: 'screens.SyncSettings.syncEverythingDescription', defaultMessage: - 'Your device will sync all content at full size, including photos, audio, and videos.\n\nNote: This will use more storage.', + 'Your device will sync all content at full size, including photos, audio, and videos.\n\nNote: This will use more storage.', }, }); From 8a05e888e747aa36a8fb3d1791a312b70e9cf9ff Mon Sep 17 00:00:00 2001 From: Cindy Green Date: Tue, 3 Sep 2024 16:04:12 -0400 Subject: [PATCH 05/25] Messages --- messages/en.json | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/messages/en.json b/messages/en.json index 36cc60b3a..ca003161f 100644 --- a/messages/en.json +++ b/messages/en.json @@ -1235,6 +1235,21 @@ "screens.Sync.ProjectSyncDisplay.waitingForDevices": { "message": "Waiting for devices" }, + "screens.SyncSettings.syncEverything": { + "message": "Sync Everything" + }, + "screens.SyncSettings.syncEverythingDescription": { + "message": "Your device will sync all content at full size, including photos, audio, and videos. Note: This will use more storage." + }, + "screens.SyncSettings.syncPreviews": { + "message": "Sync Previews (Photos Only)" + }, + "screens.SyncSettings.syncPreviewsDescription": { + "message": "Photos will sync at a reduced smaller size. Device will not sync audio or video." + }, + "screens.SyncSettings.title": { + "message": "Sync Settings" + }, "screens.Track.ObservationList.observations": { "message": "Observations" }, @@ -1249,22 +1264,6 @@ "screens.Track.tracks": { "message": "Tracks" }, - "screens.SyncSettings.syncEverything": { - "message": "Sync Everything" - }, - "screens.SyncSettings.syncEverythingDescription": { - "message": "Your device will sync all content at full size, including photos, audio, and videos. Note: This will use more storage." - }, - "screens.SyncSettings.syncPreviews": { - "message": "Sync Previews (Photos Only)" - }, - "screens.SyncSettings.syncPreviewsDescription": { - "message": "Photos will sync at a reduced smaller size. Device will not sync audio or video." - }, - "screens.SyncSettings.title": { - "message": "Sync Settings" - }, - "screens.TrackEdit.title": { "description": "Title for editing track screen", "message": "Edit Track" From e8bb6784b507c0865fdbc1ae0ffb98322d2a1de3 Mon Sep 17 00:00:00 2001 From: Cindy Green Date: Wed, 4 Sep 2024 13:04:10 -0400 Subject: [PATCH 06/25] Adds the bottom sheet modals. --- .../ProjectSettings/SyncActionSheet.tsx | 58 ++++++++++ .../Settings/ProjectSettings/SyncSettings.tsx | 106 +++++++++++++----- .../Settings/ProjectSettings/index.tsx | 2 +- 3 files changed, 138 insertions(+), 28 deletions(-) create mode 100644 src/frontend/screens/Settings/ProjectSettings/SyncActionSheet.tsx diff --git a/src/frontend/screens/Settings/ProjectSettings/SyncActionSheet.tsx b/src/frontend/screens/Settings/ProjectSettings/SyncActionSheet.tsx new file mode 100644 index 000000000..0f506ed72 --- /dev/null +++ b/src/frontend/screens/Settings/ProjectSettings/SyncActionSheet.tsx @@ -0,0 +1,58 @@ +import * as React from 'react'; +import {BottomSheetModalMethods} from '@gorhom/bottom-sheet/lib/typescript/types'; +import {defineMessages, useIntl} from 'react-intl'; +import Warning from '../../../images/Warning.svg'; +import { + BottomSheetModal, + BottomSheetModalContent, +} from '../../../sharedComponents/BottomSheetModal'; + +const m = defineMessages({ + cancel: { + id: 'screens.SyncActionSheet.cancel', + defaultMessage: 'Cancel', + }, +}); + +type SyncActionSheetProps = { + title: string; + description: string; + confirmActionText: string; + confirmAction: () => void; + isOpen: boolean; + onDismiss: () => void; +}; + +export const SyncActionSheet = React.forwardRef< + BottomSheetModalMethods, + SyncActionSheetProps +>( + ( + {title, description, confirmActionText, confirmAction, isOpen, onDismiss}, + sheetRef, + ) => { + const {formatMessage: t} = useIntl(); + + return ( + + } + buttonConfigs={[ + { + text: t(m.cancel), + onPress: onDismiss, + variation: 'outlined', + }, + { + text: confirmActionText, + variation: 'filled', + onPress: confirmAction, + }, + ]} + /> + + ); + }, +); diff --git a/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx b/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx index 28b0104dc..a9d2ac426 100644 --- a/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx +++ b/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx @@ -2,11 +2,13 @@ import * as React from 'react'; import {ScrollView, StyleSheet} from 'react-native'; import {useIntl, defineMessages} from 'react-intl'; import {SelectOne} from '../../../sharedComponents/SelectOne'; -import {SYNC_BACKGROUND, NEW_DARK_GREY} from '../../../lib/styles'; +import {SYNC_BACKGROUND} from '../../../lib/styles'; import { usePersistedSettings, usePersistedSettingsAction, } from '../../../hooks/persistedState/usePersistedSettings'; +import {SyncActionSheet} from './SyncActionSheet'; +import {useBottomSheetModal} from '../../../sharedComponents/BottomSheetModal'; const m = defineMessages({ syncSettingsTitle: { @@ -31,6 +33,28 @@ const m = defineMessages({ defaultMessage: 'Your device will sync all content at full size, including photos, audio, and videos.\n\nNote: This will use more storage.', }, + syncPreviewsBottomSheet: { + id: 'screens.SyncSettings.syncPreviewsButtonBottomSheet', + defaultMessage: 'Sync Previews?', + }, + syncEverythingBottomSheet: { + id: 'screens.SyncSettings.syncEverythingButtonBottomSheet', + defaultMessage: 'Sync Everything?', + }, + syncPreviewsDescriptionBottomSheet: { + id: 'screens.SyncSettings.syncPreviewsDescriptionBottomSheet', + defaultMessage: + 'Your device will keep all existing data but new observations will sync in a smaller, preview size.\n\nYou will no longer sync Audio or Video.', + }, + syncEverythingDescriptionBottomSheet: { + id: 'screens.SyncSettings.syncEverythingDescriptionBottomSheet', + defaultMessage: + 'You are about to sync everything. This may increase the disk space used on your device.', + }, + syncPreviewsBottomSheetConfirm: { + id: 'screens.SyncSettings.syncPreviewsBottomSheetConfirm', + defaultMessage: 'Sync Previews', + }, }); export const SyncSettings = () => { @@ -38,6 +62,41 @@ export const SyncSettings = () => { const syncSetting = usePersistedSettings(store => store.syncSetting); const {setSyncSetting} = usePersistedSettingsAction(); + const { + isOpen: isPreviewOpen, + openSheet: openPreviewSheet, + closeSheet: closePreviewSheet, + sheetRef: previewSheetRef, + } = useBottomSheetModal({openOnMount: false}); + + const { + isOpen: isEverythingOpen, + openSheet: openEverythingSheet, + closeSheet: closeEverythingSheet, + sheetRef: everythingSheetRef, + } = useBottomSheetModal({openOnMount: false}); + + const handleOptionChange = (value: 'previews' | 'everything') => { + if (value === 'previews') { + openPreviewSheet(); + } else if (value === 'everything') { + openEverythingSheet(); + } + setSyncSetting(value); + }; + + const syncPreviewsAction = () => { + console.log('Sync Previews action triggered'); + closePreviewSheet(); + // for now + }; + + const syncEverythingAction = () => { + console.log('Sync Everything action triggered'); + closeEverythingSheet(); + // until I know what to do + }; + const options: { value: 'previews' | 'everything'; label: string; @@ -59,11 +118,29 @@ export const SyncSettings = () => { + + ); }; @@ -74,29 +151,4 @@ const styles = StyleSheet.create({ container: { padding: 20, }, - title: { - fontSize: 20, - fontWeight: 'bold', - }, - optionContainer: { - marginVertical: 15, - flexDirection: 'row', - alignItems: 'center', - }, - label: { - fontSize: 14, - color: 'black', - flex: 1, - }, - hint: { - fontSize: 14, - color: NEW_DARK_GREY, - flex: 3, - marginLeft: 20, - }, - note: { - marginTop: 20, - fontSize: 12, - color: NEW_DARK_GREY, - }, }); diff --git a/src/frontend/screens/Settings/ProjectSettings/index.tsx b/src/frontend/screens/Settings/ProjectSettings/index.tsx index 96772f564..014ac20a1 100644 --- a/src/frontend/screens/Settings/ProjectSettings/index.tsx +++ b/src/frontend/screens/Settings/ProjectSettings/index.tsx @@ -58,7 +58,7 @@ export const ProjectSettings: NativeNavigationComponent<'ProjectSettings'> = ({ onPress={() => { navigation.navigate('SyncSettings'); }}> - } />{' '} + } /> { From 2c2c4f5bd2c5564edcacda5a143c8bbf2d835f1a Mon Sep 17 00:00:00 2001 From: Cindy Green Date: Wed, 4 Sep 2024 13:26:40 -0400 Subject: [PATCH 07/25] Adds confirmation to persisted settings. Messages --- messages/en.json | 18 ++++++++++++++++++ .../Settings/ProjectSettings/SyncSettings.tsx | 15 ++++++--------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/messages/en.json b/messages/en.json index 1fd15d7f1..ece94a155 100644 --- a/messages/en.json +++ b/messages/en.json @@ -1254,18 +1254,36 @@ "screens.Sync.ProjectSyncDisplay.waitingForDevices": { "message": "Waiting for devices" }, + "screens.SyncActionSheet.cancel": { + "message": "Cancel" + }, "screens.SyncSettings.syncEverything": { "message": "Sync Everything" }, + "screens.SyncSettings.syncEverythingButtonBottomSheet": { + "message": "Sync Everything?" + }, "screens.SyncSettings.syncEverythingDescription": { "message": "Your device will sync all content at full size, including photos, audio, and videos. Note: This will use more storage." }, + "screens.SyncSettings.syncEverythingDescriptionBottomSheet": { + "message": "You are about to sync everything. This may increase the disk space used on your device." + }, "screens.SyncSettings.syncPreviews": { "message": "Sync Previews (Photos Only)" }, + "screens.SyncSettings.syncPreviewsBottomSheetConfirm": { + "message": "Sync Previews" + }, + "screens.SyncSettings.syncPreviewsButtonBottomSheet": { + "message": "Sync Previews?" + }, "screens.SyncSettings.syncPreviewsDescription": { "message": "Photos will sync at a reduced smaller size. Device will not sync audio or video." }, + "screens.SyncSettings.syncPreviewsDescriptionBottomSheet": { + "message": "Your device will keep all existing data but new observations will sync in a smaller, preview size. You will no longer sync Audio or Video." + }, "screens.SyncSettings.title": { "message": "Sync Settings" }, diff --git a/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx b/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx index a9d2ac426..b285550ae 100644 --- a/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx +++ b/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx @@ -82,19 +82,16 @@ export const SyncSettings = () => { } else if (value === 'everything') { openEverythingSheet(); } - setSyncSetting(value); }; - const syncPreviewsAction = () => { - console.log('Sync Previews action triggered'); + const confirmPreviews = () => { + setSyncSetting('previews'); closePreviewSheet(); - // for now }; - const syncEverythingAction = () => { - console.log('Sync Everything action triggered'); + const confirmEverything = () => { + setSyncSetting('everything'); closeEverythingSheet(); - // until I know what to do }; const options: { @@ -127,7 +124,7 @@ export const SyncSettings = () => { title={t(m.syncPreviewsBottomSheet)} description={t(m.syncPreviewsDescriptionBottomSheet)} confirmActionText={t(m.syncPreviewsBottomSheetConfirm)} - confirmAction={syncPreviewsAction} + confirmAction={confirmPreviews} isOpen={isPreviewOpen} onDismiss={closePreviewSheet} ref={previewSheetRef} @@ -136,7 +133,7 @@ export const SyncSettings = () => { title={t(m.syncEverythingBottomSheet)} description={t(m.syncEverythingDescriptionBottomSheet)} confirmActionText={t(m.syncEverything)} - confirmAction={syncEverythingAction} + confirmAction={confirmEverything} isOpen={isEverythingOpen} onDismiss={closeEverythingSheet} ref={everythingSheetRef} From 9015640d52040ae6f45268e8bca659e500810b49 Mon Sep 17 00:00:00 2001 From: Cindy Green Date: Wed, 4 Sep 2024 13:34:29 -0400 Subject: [PATCH 08/25] Consolidates the bottom sheet modal. --- .../Settings/ProjectSettings/SyncSettings.tsx | 82 +++++++++---------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx b/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx index b285550ae..888ff1b3d 100644 --- a/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx +++ b/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx @@ -62,36 +62,30 @@ export const SyncSettings = () => { const syncSetting = usePersistedSettings(store => store.syncSetting); const {setSyncSetting} = usePersistedSettingsAction(); - const { - isOpen: isPreviewOpen, - openSheet: openPreviewSheet, - closeSheet: closePreviewSheet, - sheetRef: previewSheetRef, - } = useBottomSheetModal({openOnMount: false}); + const [modalState, setModalState] = React.useState<{ + type: 'previews' | 'everything' | null; + isOpen: boolean; + }>({ + type: null, + isOpen: false, + }); - const { - isOpen: isEverythingOpen, - openSheet: openEverythingSheet, - closeSheet: closeEverythingSheet, - sheetRef: everythingSheetRef, - } = useBottomSheetModal({openOnMount: false}); + const {openSheet, closeSheet, sheetRef} = useBottomSheetModal({ + openOnMount: false, + }); const handleOptionChange = (value: 'previews' | 'everything') => { - if (value === 'previews') { - openPreviewSheet(); - } else if (value === 'everything') { - openEverythingSheet(); - } - }; - - const confirmPreviews = () => { - setSyncSetting('previews'); - closePreviewSheet(); + setModalState({type: value, isOpen: true}); + openSheet(); }; - const confirmEverything = () => { - setSyncSetting('everything'); - closeEverythingSheet(); + const handleConfirm = () => { + if (modalState.type === 'previews') { + setSyncSetting('previews'); + } else if (modalState.type === 'everything') { + setSyncSetting('everything'); + } + closeSheet(); }; const options: { @@ -121,22 +115,28 @@ export const SyncSettings = () => { color={SYNC_BACKGROUND} /> - { + setModalState({type: null, isOpen: false}); + closeSheet(); + }} + ref={sheetRef} /> ); From 042be707b60e04cd361e21ee89e08b9b8fb72841 Mon Sep 17 00:00:00 2001 From: Cindy Green Date: Wed, 4 Sep 2024 14:37:59 -0400 Subject: [PATCH 09/25] Adds an env variable to use as a feature flag. --- .../screens/Settings/ProjectSettings/index.tsx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/frontend/screens/Settings/ProjectSettings/index.tsx b/src/frontend/screens/Settings/ProjectSettings/index.tsx index 014ac20a1..542d6c2d9 100644 --- a/src/frontend/screens/Settings/ProjectSettings/index.tsx +++ b/src/frontend/screens/Settings/ProjectSettings/index.tsx @@ -53,13 +53,16 @@ export const ProjectSettings: NativeNavigationComponent<'ProjectSettings'> = ({ }}> } /> - { - navigation.navigate('SyncSettings'); - }}> - } /> - + {process.env.EXPO_PUBLIC_FEATURE_MEDIA_MANAGER && ( + { + navigation.navigate('SyncSettings'); + }}> + } /> + + )} + { navigation.navigate('Config'); From 58e5dfec0a38fd660c938f9de5c1bccec701ba7b Mon Sep 17 00:00:00 2001 From: Cindy Green Date: Wed, 4 Sep 2024 16:00:11 -0400 Subject: [PATCH 10/25] Changes default --- src/frontend/hooks/persistedState/usePersistedSettings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/hooks/persistedState/usePersistedSettings.ts b/src/frontend/hooks/persistedState/usePersistedSettings.ts index c61b6de91..cdebad941 100644 --- a/src/frontend/hooks/persistedState/usePersistedSettings.ts +++ b/src/frontend/hooks/persistedState/usePersistedSettings.ts @@ -18,7 +18,7 @@ type SettingsSlice = { const settingsSlice: StateCreator = (set, get) => ({ coordinateFormat: 'utm', manualCoordinateEntryFormat: 'utm', - syncSetting: 'previews', + syncSetting: 'everything', actions: { setCoordinateFormat: coordinateFormat => set({coordinateFormat}), setManualCoordinateEntryFormat: coordinateFormat => From d0524453ef729573f09394b795c031c8adfef609 Mon Sep 17 00:00:00 2001 From: Cindy Green Date: Wed, 4 Sep 2024 16:52:51 -0400 Subject: [PATCH 11/25] Adds media label to observation. --- src/frontend/screens/Observation/index.tsx | 42 +++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/frontend/screens/Observation/index.tsx b/src/frontend/screens/Observation/index.tsx index 7de773c0b..ffc5ca316 100644 --- a/src/frontend/screens/Observation/index.tsx +++ b/src/frontend/screens/Observation/index.tsx @@ -2,7 +2,13 @@ import * as React from 'react'; import {Text, View, ScrollView, StyleSheet} from 'react-native'; import {defineMessages} from 'react-intl'; -import {BLACK, WHITE, DARK_GREY, LIGHT_GREY} from '../../lib/styles'; +import { + BLACK, + WHITE, + DARK_GREY, + LIGHT_GREY, + NEW_DARK_GREY, +} from '../../lib/styles'; import {UIActivityIndicator} from 'react-native-indicators'; import {FormattedObservationDate} from '../../sharedComponents/FormattedData'; @@ -20,6 +26,7 @@ import {useDeviceInfo} from '../../hooks/server/deviceInfo'; import {useOriginalVersionIdToDeviceId} from '../../hooks/server/projects.ts'; import {SavedPhoto} from '../../contexts/PhotoPromiseContext/types.ts'; import {ButtonFields} from './Buttons.tsx'; +import {usePersistedSettings} from '../../hooks/persistedState/usePersistedSettings'; const m = defineMessages({ deleteTitle: { @@ -33,6 +40,14 @@ const m = defineMessages({ description: 'Title of observation screen showing (non-editable) view of observation with map and answered questions', }, + labelFullSizePreviews: { + id: 'screens.Observation.labelFullSizePreviews', + defaultMessage: 'Full size and previews available', + }, + labelPreviewsOnly: { + id: 'screens.Observation.labelPreviewsOnly', + defaultMessage: 'Only previews available', + }, }); export const ObservationScreen: NativeNavigationComponent<'Observation'> = ({ @@ -40,6 +55,7 @@ export const ObservationScreen: NativeNavigationComponent<'Observation'> = ({ navigation, }) => { const {observationId} = route.params; + const syncSetting = usePersistedSettings(store => store.syncSetting); React.useLayoutEffect(() => { navigation.setOptions({ @@ -76,6 +92,23 @@ export const ObservationScreen: NativeNavigationComponent<'Observation'> = ({ (attachment): attachment is SavedPhoto => attachment.type === 'photo', ); + const renderMediaLabel = () => { + if (syncSetting === 'everything') { + return ( + + {m.labelFullSizePreviews.defaultMessage} + + ); + } else if (syncSetting === 'previews') { + return ( + + {m.labelPreviewsOnly.defaultMessage} + + ); + } + return null; + }; + return ( = ({ {observation.tags.notes} ) : null} + {photoAttachments.length > 0 && renderMediaLabel()} {photoAttachments.length > 0 && ( Date: Wed, 4 Sep 2024 17:33:36 -0400 Subject: [PATCH 12/25] Changes as suggested by PR review. --- .../persistedState/usePersistedSettings.ts | 6 +- .../ProjectSettings/SyncActionSheet.tsx | 58 ------------- .../SyncActionSheetContent.tsx | 53 ++++++++++++ .../Settings/ProjectSettings/SyncSettings.tsx | 83 +++++++++---------- src/frontend/sharedComponents/SelectOne.tsx | 8 +- src/frontend/sharedTypes/index.ts | 1 + 6 files changed, 100 insertions(+), 109 deletions(-) delete mode 100644 src/frontend/screens/Settings/ProjectSettings/SyncActionSheet.tsx create mode 100644 src/frontend/screens/Settings/ProjectSettings/SyncActionSheetContent.tsx diff --git a/src/frontend/hooks/persistedState/usePersistedSettings.ts b/src/frontend/hooks/persistedState/usePersistedSettings.ts index cdebad941..5fbc6deff 100644 --- a/src/frontend/hooks/persistedState/usePersistedSettings.ts +++ b/src/frontend/hooks/persistedState/usePersistedSettings.ts @@ -1,17 +1,17 @@ import {type StateCreator} from 'zustand'; import {createPersistedState} from './createPersistedState'; -import {CoordinateFormat} from '../../sharedTypes'; +import {CoordinateFormat, SyncSetting} from '../../sharedTypes'; type SettingsSlice = { coordinateFormat: CoordinateFormat; manualCoordinateEntryFormat: CoordinateFormat; - syncSetting: 'previews' | 'everything'; + syncSetting: SyncSetting; actions: { setCoordinateFormat: (coordinateFormat: CoordinateFormat) => void; setManualCoordinateEntryFormat: ( coordinateFormat: CoordinateFormat, ) => void; - setSyncSetting: (syncSetting: 'previews' | 'everything') => void; + setSyncSetting: (syncSetting: SyncSetting) => void; }; }; diff --git a/src/frontend/screens/Settings/ProjectSettings/SyncActionSheet.tsx b/src/frontend/screens/Settings/ProjectSettings/SyncActionSheet.tsx deleted file mode 100644 index 0f506ed72..000000000 --- a/src/frontend/screens/Settings/ProjectSettings/SyncActionSheet.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import * as React from 'react'; -import {BottomSheetModalMethods} from '@gorhom/bottom-sheet/lib/typescript/types'; -import {defineMessages, useIntl} from 'react-intl'; -import Warning from '../../../images/Warning.svg'; -import { - BottomSheetModal, - BottomSheetModalContent, -} from '../../../sharedComponents/BottomSheetModal'; - -const m = defineMessages({ - cancel: { - id: 'screens.SyncActionSheet.cancel', - defaultMessage: 'Cancel', - }, -}); - -type SyncActionSheetProps = { - title: string; - description: string; - confirmActionText: string; - confirmAction: () => void; - isOpen: boolean; - onDismiss: () => void; -}; - -export const SyncActionSheet = React.forwardRef< - BottomSheetModalMethods, - SyncActionSheetProps ->( - ( - {title, description, confirmActionText, confirmAction, isOpen, onDismiss}, - sheetRef, - ) => { - const {formatMessage: t} = useIntl(); - - return ( - - } - buttonConfigs={[ - { - text: t(m.cancel), - onPress: onDismiss, - variation: 'outlined', - }, - { - text: confirmActionText, - variation: 'filled', - onPress: confirmAction, - }, - ]} - /> - - ); - }, -); diff --git a/src/frontend/screens/Settings/ProjectSettings/SyncActionSheetContent.tsx b/src/frontend/screens/Settings/ProjectSettings/SyncActionSheetContent.tsx new file mode 100644 index 000000000..fab5fd13e --- /dev/null +++ b/src/frontend/screens/Settings/ProjectSettings/SyncActionSheetContent.tsx @@ -0,0 +1,53 @@ +import * as React from 'react'; +import {BottomSheetModalMethods} from '@gorhom/bottom-sheet/lib/typescript/types'; +import {defineMessages, useIntl} from 'react-intl'; +import Warning from '../../../images/Warning.svg'; +import { + BottomSheetModal, + BottomSheetModalContent, +} from '../../../sharedComponents/BottomSheetModal'; + +const m = defineMessages({ + cancel: { + id: 'screens.SyncActionSheet.cancel', + defaultMessage: 'Cancel', + }, +}); + +type SyncActionSheetContentProps = { + title: string; + description: string; + confirmActionText: string; + confirmAction: () => void; + onDismiss: () => void; +}; + +export const SyncActionSheetContent = ({ + title, + description, + confirmActionText, + confirmAction, + onDismiss, +}: SyncActionSheetContentProps) => { + const {formatMessage: t} = useIntl(); + + return ( + } + buttonConfigs={[ + { + text: t(m.cancel), + onPress: onDismiss, + variation: 'outlined', + }, + { + text: confirmActionText, + variation: 'filled', + onPress: confirmAction, + }, + ]} + /> + ); +}; diff --git a/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx b/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx index 888ff1b3d..3320c0502 100644 --- a/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx +++ b/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx @@ -7,8 +7,12 @@ import { usePersistedSettings, usePersistedSettingsAction, } from '../../../hooks/persistedState/usePersistedSettings'; -import {SyncActionSheet} from './SyncActionSheet'; -import {useBottomSheetModal} from '../../../sharedComponents/BottomSheetModal'; +import {SyncActionSheetContent} from './SyncActionSheetContent'; +import { + useBottomSheetModal, + BottomSheetModal, +} from '../../../sharedComponents/BottomSheetModal'; +import {SyncSetting} from '../../../sharedTypes'; const m = defineMessages({ syncSettingsTitle: { @@ -31,7 +35,7 @@ const m = defineMessages({ syncEverythingDescription: { id: 'screens.SyncSettings.syncEverythingDescription', defaultMessage: - 'Your device will sync all content at full size, including photos, audio, and videos.\n\nNote: This will use more storage.', + 'Your device will sync all content at full size, including photos, audio, and videos.Note: This will use more storage.', }, syncPreviewsBottomSheet: { id: 'screens.SyncSettings.syncPreviewsButtonBottomSheet', @@ -44,7 +48,7 @@ const m = defineMessages({ syncPreviewsDescriptionBottomSheet: { id: 'screens.SyncSettings.syncPreviewsDescriptionBottomSheet', defaultMessage: - 'Your device will keep all existing data but new observations will sync in a smaller, preview size.\n\nYou will no longer sync Audio or Video.', + 'Your device will keep all existing data but new observations will sync in a smaller, preview size.You will no longer sync Audio or Video.', }, syncEverythingDescriptionBottomSheet: { id: 'screens.SyncSettings.syncEverythingDescriptionBottomSheet', @@ -62,34 +66,26 @@ export const SyncSettings = () => { const syncSetting = usePersistedSettings(store => store.syncSetting); const {setSyncSetting} = usePersistedSettingsAction(); - const [modalState, setModalState] = React.useState<{ - type: 'previews' | 'everything' | null; - isOpen: boolean; - }>({ - type: null, - isOpen: false, - }); + const [modalType, setModalType] = React.useState( + () => syncSetting, + ); - const {openSheet, closeSheet, sheetRef} = useBottomSheetModal({ + const {isOpen, openSheet, closeSheet, sheetRef} = useBottomSheetModal({ openOnMount: false, }); - const handleOptionChange = (value: 'previews' | 'everything') => { - setModalState({type: value, isOpen: true}); + const handleOptionChange = (value: SyncSetting) => { + setModalType(value); openSheet(); }; const handleConfirm = () => { - if (modalState.type === 'previews') { - setSyncSetting('previews'); - } else if (modalState.type === 'everything') { - setSyncSetting('everything'); - } + setSyncSetting(modalType); closeSheet(); }; const options: { - value: 'previews' | 'everything'; + value: SyncSetting; label: string; hint?: string; }[] = [ @@ -114,30 +110,29 @@ export const SyncSettings = () => { radioButtonPosition="right" color={SYNC_BACKGROUND} /> - { - setModalState({type: null, isOpen: false}); - closeSheet(); - }} - ref={sheetRef} - /> + + { + closeSheet(); + }} + /> + ); }; diff --git a/src/frontend/sharedComponents/SelectOne.tsx b/src/frontend/sharedComponents/SelectOne.tsx index eca03630a..ac7fd0169 100644 --- a/src/frontend/sharedComponents/SelectOne.tsx +++ b/src/frontend/sharedComponents/SelectOne.tsx @@ -32,18 +32,18 @@ export const SelectOne = ({ } testID={`${item.value}LanguageButton`} onPress={() => value !== item.value && onChange(item.value)} - style={[radioButtonPosition === 'right' && styles.rowReverse]}> + style={radioButtonPosition === 'right' ? styles.rowReverse : undefined}> diff --git a/src/frontend/sharedTypes/index.ts b/src/frontend/sharedTypes/index.ts index 4a24b9928..857c6b14f 100644 --- a/src/frontend/sharedTypes/index.ts +++ b/src/frontend/sharedTypes/index.ts @@ -31,6 +31,7 @@ export type Attachment = Observation['attachments'][0]; export type PhotoVariant = 'original' | 'thumbnail' | 'preview'; export type CoordinateFormat = 'utm' | 'dd' | 'dms'; +export type SyncSetting = 'previews' | 'everything'; // Copied form /@mapeo/core/src/roles.js. Created an issue to eventuall expose this: https://github.com/digidem/mapeo-core-next/issues/532 export const CREATOR_ROLE_ID = 'a12a6702b93bd7ff'; From 09c62c20df016bf1462309120d12dd70c5035afd Mon Sep 17 00:00:00 2001 From: Cindy Green Date: Wed, 4 Sep 2024 17:40:53 -0400 Subject: [PATCH 13/25] Messages. --- messages/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/messages/en.json b/messages/en.json index ece94a155..a28710824 100644 --- a/messages/en.json +++ b/messages/en.json @@ -1264,7 +1264,7 @@ "message": "Sync Everything?" }, "screens.SyncSettings.syncEverythingDescription": { - "message": "Your device will sync all content at full size, including photos, audio, and videos. Note: This will use more storage." + "message": "Your device will sync all content at full size, including photos, audio, and videos.Note: This will use more storage." }, "screens.SyncSettings.syncEverythingDescriptionBottomSheet": { "message": "You are about to sync everything. This may increase the disk space used on your device." @@ -1282,7 +1282,7 @@ "message": "Photos will sync at a reduced smaller size. Device will not sync audio or video." }, "screens.SyncSettings.syncPreviewsDescriptionBottomSheet": { - "message": "Your device will keep all existing data but new observations will sync in a smaller, preview size. You will no longer sync Audio or Video." + "message": "Your device will keep all existing data but new observations will sync in a smaller, preview size.You will no longer sync Audio or Video." }, "screens.SyncSettings.title": { "message": "Sync Settings" From 234eb59f90dcf898d6a1b4461cca66bfc7eacecb Mon Sep 17 00:00:00 2001 From: Cindy Green Date: Thu, 5 Sep 2024 11:13:17 -0400 Subject: [PATCH 14/25] Adds default as a choice for sync settings. Adds the media label component. Uses the media label component in the observation and the photo preview. --- .../persistedState/usePersistedSettings.ts | 2 +- src/frontend/screens/Observation/index.tsx | 30 +++------- src/frontend/screens/PhotoPreviewModal.tsx | 11 ++++ src/frontend/sharedComponents/MediaLabel.tsx | 57 +++++++++++++++++++ src/frontend/sharedTypes/index.ts | 2 +- 5 files changed, 78 insertions(+), 24 deletions(-) create mode 100644 src/frontend/sharedComponents/MediaLabel.tsx diff --git a/src/frontend/hooks/persistedState/usePersistedSettings.ts b/src/frontend/hooks/persistedState/usePersistedSettings.ts index 5fbc6deff..b16717be8 100644 --- a/src/frontend/hooks/persistedState/usePersistedSettings.ts +++ b/src/frontend/hooks/persistedState/usePersistedSettings.ts @@ -18,7 +18,7 @@ type SettingsSlice = { const settingsSlice: StateCreator = (set, get) => ({ coordinateFormat: 'utm', manualCoordinateEntryFormat: 'utm', - syncSetting: 'everything', + syncSetting: 'default', actions: { setCoordinateFormat: coordinateFormat => set({coordinateFormat}), setManualCoordinateEntryFormat: coordinateFormat => diff --git a/src/frontend/screens/Observation/index.tsx b/src/frontend/screens/Observation/index.tsx index ffc5ca316..68c6230ef 100644 --- a/src/frontend/screens/Observation/index.tsx +++ b/src/frontend/screens/Observation/index.tsx @@ -26,7 +26,7 @@ import {useDeviceInfo} from '../../hooks/server/deviceInfo'; import {useOriginalVersionIdToDeviceId} from '../../hooks/server/projects.ts'; import {SavedPhoto} from '../../contexts/PhotoPromiseContext/types.ts'; import {ButtonFields} from './Buttons.tsx'; -import {usePersistedSettings} from '../../hooks/persistedState/usePersistedSettings'; +import {MediaLabel} from '../../sharedComponents/MediaLabel.tsx'; const m = defineMessages({ deleteTitle: { @@ -55,7 +55,6 @@ export const ObservationScreen: NativeNavigationComponent<'Observation'> = ({ navigation, }) => { const {observationId} = route.params; - const syncSetting = usePersistedSettings(store => store.syncSetting); React.useLayoutEffect(() => { navigation.setOptions({ @@ -92,23 +91,6 @@ export const ObservationScreen: NativeNavigationComponent<'Observation'> = ({ (attachment): attachment is SavedPhoto => attachment.type === 'photo', ); - const renderMediaLabel = () => { - if (syncSetting === 'everything') { - return ( - - {m.labelFullSizePreviews.defaultMessage} - - ); - } else if (syncSetting === 'previews') { - return ( - - {m.labelPreviewsOnly.defaultMessage} - - ); - } - return null; - }; - return ( = ({ {observation.tags.notes} ) : null} - {photoAttachments.length > 0 && renderMediaLabel()} + {photoAttachments.length > 0 && ( + + )} {photoAttachments.length > 0 && ( )} + = ({ + textColor = WHITE, + style, + context, +}) => { + const {formatMessage: t} = useIntl(); + const syncSetting = usePersistedSettings(store => store.syncSetting); + let labelText = ''; + if (syncSetting === 'everything') { + labelText = t(m.labelFullSizePreviews); + } else if (syncSetting === 'previews') { + labelText = + context === 'observation' + ? t(m.labelPreviewsOnlyObservation) + : t(m.labelPreviewsOnlyOpenMedia); + } + if (!labelText) return null; + + return ( + + {labelText} + + ); +}; + +const styles = StyleSheet.create({ + mediaLabel: { + fontSize: 14, + }, +}); diff --git a/src/frontend/sharedTypes/index.ts b/src/frontend/sharedTypes/index.ts index 857c6b14f..86afd0b59 100644 --- a/src/frontend/sharedTypes/index.ts +++ b/src/frontend/sharedTypes/index.ts @@ -31,7 +31,7 @@ export type Attachment = Observation['attachments'][0]; export type PhotoVariant = 'original' | 'thumbnail' | 'preview'; export type CoordinateFormat = 'utm' | 'dd' | 'dms'; -export type SyncSetting = 'previews' | 'everything'; +export type SyncSetting = 'default' | 'previews' | 'everything'; // Copied form /@mapeo/core/src/roles.js. Created an issue to eventuall expose this: https://github.com/digidem/mapeo-core-next/issues/532 export const CREATOR_ROLE_ID = 'a12a6702b93bd7ff'; From 76ee3ce7519bdcb837f4af1869f6bed6763ad5fa Mon Sep 17 00:00:00 2001 From: Cindy Green Date: Thu, 5 Sep 2024 11:17:29 -0400 Subject: [PATCH 15/25] Messages --- messages/en.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/messages/en.json b/messages/en.json index a28710824..693bb896d 100644 --- a/messages/en.json +++ b/messages/en.json @@ -673,6 +673,9 @@ "screens.ManualGpsScreen.zoneNumber": { "message": "Zone Number" }, + "screens.MediaLabel.labelFullSizePreviews": { + "message": "Full size and previews available" + }, "screens.ObscurePasscode.description": { "message": "Obscure Passcode is a security feature that allows you to open Mapeo in a decoy mode that hides all of your data. Entering the Obscure Passcode on the intro screen will display an empty version of Mapeo which allows you to create demonstration observations that are not saved to the Mapeo database." }, @@ -716,6 +719,15 @@ "description": "Title of dialog asking confirmation to delete an observation", "message": "Delete observation?" }, + "screens.Observation.labelFullSizePreviews": { + "message": "Full size and previews available" + }, + "screens.Observation.labelPreviewsOnly": { + "message": "Only previews available" + }, + "screens.Observation.labelPreviewsOnlyObservation": { + "message": "Only previews are available" + }, "screens.Observation.shareMediaTitle": { "description": "Title of dialog to share an observation with media", "message": "Sharing image" @@ -849,6 +861,9 @@ "screens.PhotoPreviewModal.DeletePhoto.headerButtonText": { "message": "Delete Photo" }, + "screens.PhotoPreviewModal.labelPreviewsOnlyOpenMedia": { + "message": "Only preview is available" + }, "screens.PrivacyPolicy.aboutAwana": { "message": "About Awana Digital" }, From ebe671e7ed8a705c1996fffb80548ef63ff4c2f4 Mon Sep 17 00:00:00 2001 From: Cindy Green Date: Thu, 5 Sep 2024 11:22:21 -0400 Subject: [PATCH 16/25] Removes extra messages. --- src/frontend/screens/Observation/index.tsx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/frontend/screens/Observation/index.tsx b/src/frontend/screens/Observation/index.tsx index 68c6230ef..a65e7c08f 100644 --- a/src/frontend/screens/Observation/index.tsx +++ b/src/frontend/screens/Observation/index.tsx @@ -40,14 +40,6 @@ const m = defineMessages({ description: 'Title of observation screen showing (non-editable) view of observation with map and answered questions', }, - labelFullSizePreviews: { - id: 'screens.Observation.labelFullSizePreviews', - defaultMessage: 'Full size and previews available', - }, - labelPreviewsOnly: { - id: 'screens.Observation.labelPreviewsOnly', - defaultMessage: 'Only previews available', - }, }); export const ObservationScreen: NativeNavigationComponent<'Observation'> = ({ From 5ccdfa0a9f44852fdd535b43fd2591e28cad3d4e Mon Sep 17 00:00:00 2001 From: Cindy Green Date: Thu, 5 Sep 2024 11:22:37 -0400 Subject: [PATCH 17/25] Updates messages --- messages/en.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/messages/en.json b/messages/en.json index 693bb896d..fe1627fde 100644 --- a/messages/en.json +++ b/messages/en.json @@ -719,12 +719,6 @@ "description": "Title of dialog asking confirmation to delete an observation", "message": "Delete observation?" }, - "screens.Observation.labelFullSizePreviews": { - "message": "Full size and previews available" - }, - "screens.Observation.labelPreviewsOnly": { - "message": "Only previews available" - }, "screens.Observation.labelPreviewsOnlyObservation": { "message": "Only previews are available" }, From 745bedc3ce6f14f9ef1d7d02db5fd3017ce7eee3 Mon Sep 17 00:00:00 2001 From: Cindy Green Date: Thu, 5 Sep 2024 11:26:50 -0400 Subject: [PATCH 18/25] Adds feature flag --- src/frontend/screens/Observation/index.tsx | 13 +++++++------ src/frontend/screens/PhotoPreviewModal.tsx | 13 +++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/frontend/screens/Observation/index.tsx b/src/frontend/screens/Observation/index.tsx index a65e7c08f..c0561a239 100644 --- a/src/frontend/screens/Observation/index.tsx +++ b/src/frontend/screens/Observation/index.tsx @@ -113,12 +113,13 @@ export const ObservationScreen: NativeNavigationComponent<'Observation'> = ({ context="observation" /> )} - {photoAttachments.length > 0 && ( - - )} + {process.env.EXPO_PUBLIC_FEATURE_MEDIA_MANAGER && + photoAttachments.length > 0 && ( + + )} {fields.length > 0 && ( diff --git a/src/frontend/screens/PhotoPreviewModal.tsx b/src/frontend/screens/PhotoPreviewModal.tsx index 78f130f7b..2e9678637 100644 --- a/src/frontend/screens/PhotoPreviewModal.tsx +++ b/src/frontend/screens/PhotoPreviewModal.tsx @@ -89,12 +89,13 @@ export const PhotoPreviewModal: FC< photo={photo} /> )} - - + {process.env.EXPO_PUBLIC_FEATURE_MEDIA_MANAGER && ( + + )} Date: Thu, 5 Sep 2024 15:40:37 -0400 Subject: [PATCH 19/25] Adjusts text to use separate text pieces instead of newline. Changes sync setting to media sync setting. --- messages/en.json | 74 +++++++++-------- .../persistedState/usePersistedSettings.ts | 10 +-- ...nt.tsx => MediaSyncActionSheetContent.tsx} | 16 ++-- ...SyncSettings.tsx => MediaSyncSettings.tsx} | 82 ++++++++++++------- .../Settings/ProjectSettings/index.tsx | 10 ++- src/frontend/sharedComponents/SelectOne.tsx | 2 +- src/frontend/sharedTypes/index.ts | 2 +- src/frontend/sharedTypes/navigation.ts | 2 +- 8 files changed, 112 insertions(+), 86 deletions(-) rename src/frontend/screens/Settings/ProjectSettings/{SyncActionSheetContent.tsx => MediaSyncActionSheetContent.tsx} (69%) rename src/frontend/screens/Settings/ProjectSettings/{SyncSettings.tsx => MediaSyncSettings.tsx} (57%) diff --git a/messages/en.json b/messages/en.json index ece94a155..80cf1a16d 100644 --- a/messages/en.json +++ b/messages/en.json @@ -138,7 +138,7 @@ "Screens.Settings.ProjectSettings.deviceName": { "message": "Device Name" }, - "Screens.Settings.ProjectSettings.syncSettings": { + "Screens.Settings.ProjectSettings.mediaSyncSettings": { "message": "Sync Settings" }, "Screens.Settings.ProjectSettings.title": { @@ -673,6 +673,45 @@ "screens.ManualGpsScreen.zoneNumber": { "message": "Zone Number" }, + "screens.MediaSyncActionSheetContent.cancel": { + "message": "Cancel" + }, + "screens.MediaSyncSettings.syncEverything": { + "message": "Sync Everything" + }, + "screens.MediaSyncSettings.syncEverythingButtonBottomSheet": { + "message": "Sync Everything?" + }, + "screens.MediaSyncSettings.syncEverythingDescription": { + "message": "Your device will sync all content at full size, including photos, audio, and videos." + }, + "screens.MediaSyncSettings.syncEverythingDescriptionBottomSheet": { + "message": "You are about to sync everything. This may increase the disk space used on your device." + }, + "screens.MediaSyncSettings.syncEverythingWarning": { + "message": "Note: This will use more storage." + }, + "screens.MediaSyncSettings.syncPreviewWarningBottomSheet": { + "message": "You will no longer sync Audio or Video." + }, + "screens.MediaSyncSettings.syncPreviews": { + "message": "Sync Previews (Photos Only)" + }, + "screens.MediaSyncSettings.syncPreviewsBottomSheetConfirm": { + "message": "Sync Previews" + }, + "screens.MediaSyncSettings.syncPreviewsButtonBottomSheet": { + "message": "Sync Previews?" + }, + "screens.MediaSyncSettings.syncPreviewsDescription": { + "message": "Photos will sync at a reduced smaller size. Device will not sync audio or video." + }, + "screens.MediaSyncSettings.syncPreviewsDescriptionBottomSheet": { + "message": "Your device will keep all existing data but new observations will sync in a smaller, preview size." + }, + "screens.MediaSyncSettings.title": { + "message": "Sync Settings" + }, "screens.ObscurePasscode.description": { "message": "Obscure Passcode is a security feature that allows you to open Mapeo in a decoy mode that hides all of your data. Entering the Obscure Passcode on the intro screen will display an empty version of Mapeo which allows you to create demonstration observations that are not saved to the Mapeo database." }, @@ -1254,39 +1293,6 @@ "screens.Sync.ProjectSyncDisplay.waitingForDevices": { "message": "Waiting for devices" }, - "screens.SyncActionSheet.cancel": { - "message": "Cancel" - }, - "screens.SyncSettings.syncEverything": { - "message": "Sync Everything" - }, - "screens.SyncSettings.syncEverythingButtonBottomSheet": { - "message": "Sync Everything?" - }, - "screens.SyncSettings.syncEverythingDescription": { - "message": "Your device will sync all content at full size, including photos, audio, and videos. Note: This will use more storage." - }, - "screens.SyncSettings.syncEverythingDescriptionBottomSheet": { - "message": "You are about to sync everything. This may increase the disk space used on your device." - }, - "screens.SyncSettings.syncPreviews": { - "message": "Sync Previews (Photos Only)" - }, - "screens.SyncSettings.syncPreviewsBottomSheetConfirm": { - "message": "Sync Previews" - }, - "screens.SyncSettings.syncPreviewsButtonBottomSheet": { - "message": "Sync Previews?" - }, - "screens.SyncSettings.syncPreviewsDescription": { - "message": "Photos will sync at a reduced smaller size. Device will not sync audio or video." - }, - "screens.SyncSettings.syncPreviewsDescriptionBottomSheet": { - "message": "Your device will keep all existing data but new observations will sync in a smaller, preview size. You will no longer sync Audio or Video." - }, - "screens.SyncSettings.title": { - "message": "Sync Settings" - }, "screens.Track.ObservationList.observations": { "message": "Observations" }, diff --git a/src/frontend/hooks/persistedState/usePersistedSettings.ts b/src/frontend/hooks/persistedState/usePersistedSettings.ts index 5fbc6deff..260b07978 100644 --- a/src/frontend/hooks/persistedState/usePersistedSettings.ts +++ b/src/frontend/hooks/persistedState/usePersistedSettings.ts @@ -1,29 +1,29 @@ import {type StateCreator} from 'zustand'; import {createPersistedState} from './createPersistedState'; -import {CoordinateFormat, SyncSetting} from '../../sharedTypes'; +import {CoordinateFormat, MediaSyncSetting} from '../../sharedTypes'; type SettingsSlice = { coordinateFormat: CoordinateFormat; manualCoordinateEntryFormat: CoordinateFormat; - syncSetting: SyncSetting; + mediaSyncSetting: MediaSyncSetting; actions: { setCoordinateFormat: (coordinateFormat: CoordinateFormat) => void; setManualCoordinateEntryFormat: ( coordinateFormat: CoordinateFormat, ) => void; - setSyncSetting: (syncSetting: SyncSetting) => void; + setMediaSyncSetting: (mediaSyncSetting: MediaSyncSetting) => void; }; }; const settingsSlice: StateCreator = (set, get) => ({ coordinateFormat: 'utm', manualCoordinateEntryFormat: 'utm', - syncSetting: 'everything', + mediaSyncSetting: 'everything', actions: { setCoordinateFormat: coordinateFormat => set({coordinateFormat}), setManualCoordinateEntryFormat: coordinateFormat => set({manualCoordinateEntryFormat: coordinateFormat}), - setSyncSetting: syncSetting => set({syncSetting}), + setMediaSyncSetting: mediaSyncSetting => set({mediaSyncSetting}), }, }); diff --git a/src/frontend/screens/Settings/ProjectSettings/SyncActionSheetContent.tsx b/src/frontend/screens/Settings/ProjectSettings/MediaSyncActionSheetContent.tsx similarity index 69% rename from src/frontend/screens/Settings/ProjectSettings/SyncActionSheetContent.tsx rename to src/frontend/screens/Settings/ProjectSettings/MediaSyncActionSheetContent.tsx index fab5fd13e..22bf78674 100644 --- a/src/frontend/screens/Settings/ProjectSettings/SyncActionSheetContent.tsx +++ b/src/frontend/screens/Settings/ProjectSettings/MediaSyncActionSheetContent.tsx @@ -1,34 +1,30 @@ import * as React from 'react'; -import {BottomSheetModalMethods} from '@gorhom/bottom-sheet/lib/typescript/types'; import {defineMessages, useIntl} from 'react-intl'; import Warning from '../../../images/Warning.svg'; -import { - BottomSheetModal, - BottomSheetModalContent, -} from '../../../sharedComponents/BottomSheetModal'; +import {BottomSheetModalContent} from '../../../sharedComponents/BottomSheetModal'; const m = defineMessages({ cancel: { - id: 'screens.SyncActionSheet.cancel', + id: 'screens.MediaSyncActionSheetContent.cancel', defaultMessage: 'Cancel', }, }); -type SyncActionSheetContentProps = { +type MediaSyncActionSheetContentProps = { title: string; - description: string; + description: React.ReactNode; confirmActionText: string; confirmAction: () => void; onDismiss: () => void; }; -export const SyncActionSheetContent = ({ +export const MediaSyncActionSheetContent = ({ title, description, confirmActionText, confirmAction, onDismiss, -}: SyncActionSheetContentProps) => { +}: MediaSyncActionSheetContentProps) => { const {formatMessage: t} = useIntl(); return ( diff --git a/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx b/src/frontend/screens/Settings/ProjectSettings/MediaSyncSettings.tsx similarity index 57% rename from src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx rename to src/frontend/screens/Settings/ProjectSettings/MediaSyncSettings.tsx index 3320c0502..1f34b7c62 100644 --- a/src/frontend/screens/Settings/ProjectSettings/SyncSettings.tsx +++ b/src/frontend/screens/Settings/ProjectSettings/MediaSyncSettings.tsx @@ -7,87 +7,97 @@ import { usePersistedSettings, usePersistedSettingsAction, } from '../../../hooks/persistedState/usePersistedSettings'; -import {SyncActionSheetContent} from './SyncActionSheetContent'; +import {MediaSyncActionSheetContent} from './MediaSyncActionSheetContent'; import { useBottomSheetModal, BottomSheetModal, } from '../../../sharedComponents/BottomSheetModal'; -import {SyncSetting} from '../../../sharedTypes'; +import {MediaSyncSetting} from '../../../sharedTypes'; const m = defineMessages({ syncSettingsTitle: { - id: 'screens.SyncSettings.title', + id: 'screens.MediaSyncSettings.title', defaultMessage: 'Sync Settings', }, syncPreviews: { - id: 'screens.SyncSettings.syncPreviews', + id: 'screens.MediaSyncSettings.syncPreviews', defaultMessage: 'Sync Previews (Photos Only)', }, syncPreviewsDescription: { - id: 'screens.SyncSettings.syncPreviewsDescription', + id: 'screens.MediaSyncSettings.syncPreviewsDescription', defaultMessage: 'Photos will sync at a reduced smaller size. Device will not sync audio or video.', }, syncEverything: { - id: 'screens.SyncSettings.syncEverything', + id: 'screens.MediaSyncSettings.syncEverything', defaultMessage: 'Sync Everything', }, syncEverythingDescription: { - id: 'screens.SyncSettings.syncEverythingDescription', + id: 'screens.MediaSyncSettings.syncEverythingDescription', defaultMessage: - 'Your device will sync all content at full size, including photos, audio, and videos.Note: This will use more storage.', + 'Your device will sync all content at full size, including photos, audio, and videos.', + }, + syncEverythingWarning: { + id: 'screens.MediaSyncSettings.syncEverythingWarning', + defaultMessage: 'Note: This will use more storage.', }, syncPreviewsBottomSheet: { - id: 'screens.SyncSettings.syncPreviewsButtonBottomSheet', + id: 'screens.MediaSyncSettings.syncPreviewsButtonBottomSheet', defaultMessage: 'Sync Previews?', }, syncEverythingBottomSheet: { - id: 'screens.SyncSettings.syncEverythingButtonBottomSheet', + id: 'screens.MediaSyncSettings.syncEverythingButtonBottomSheet', defaultMessage: 'Sync Everything?', }, syncPreviewsDescriptionBottomSheet: { - id: 'screens.SyncSettings.syncPreviewsDescriptionBottomSheet', + id: 'screens.MediaSyncSettings.syncPreviewsDescriptionBottomSheet', defaultMessage: - 'Your device will keep all existing data but new observations will sync in a smaller, preview size.You will no longer sync Audio or Video.', + 'Your device will keep all existing data but new observations will sync in a smaller, preview size.', + }, + syncPreviewWarningBottomSheet: { + id: 'screens.MediaSyncSettings.syncPreviewWarningBottomSheet', + defaultMessage: 'You will no longer sync Audio or Video.', }, syncEverythingDescriptionBottomSheet: { - id: 'screens.SyncSettings.syncEverythingDescriptionBottomSheet', + id: 'screens.MediaSyncSettings.syncEverythingDescriptionBottomSheet', defaultMessage: 'You are about to sync everything. This may increase the disk space used on your device.', }, syncPreviewsBottomSheetConfirm: { - id: 'screens.SyncSettings.syncPreviewsBottomSheetConfirm', + id: 'screens.MediaSyncSettings.syncPreviewsBottomSheetConfirm', defaultMessage: 'Sync Previews', }, }); -export const SyncSettings = () => { +export const MediaSyncSettings = () => { const {formatMessage: t} = useIntl(); - const syncSetting = usePersistedSettings(store => store.syncSetting); - const {setSyncSetting} = usePersistedSettingsAction(); + const mediaSyncSetting = usePersistedSettings( + store => store.mediaSyncSetting, + ); + const {setMediaSyncSetting} = usePersistedSettingsAction(); - const [modalType, setModalType] = React.useState( - () => syncSetting, + const [modalType, setModalType] = React.useState( + () => mediaSyncSetting, ); const {isOpen, openSheet, closeSheet, sheetRef} = useBottomSheetModal({ openOnMount: false, }); - const handleOptionChange = (value: SyncSetting) => { + const handleOptionChange = (value: MediaSyncSetting) => { setModalType(value); openSheet(); }; const handleConfirm = () => { - setSyncSetting(modalType); + setMediaSyncSetting(modalType); closeSheet(); }; const options: { - value: SyncSetting; + value: MediaSyncSetting; label: string; - hint?: string; + hint?: React.ReactNode; }[] = [ { value: 'previews', @@ -97,30 +107,42 @@ export const SyncSettings = () => { { value: 'everything', label: t(m.syncEverything), - hint: t(m.syncEverythingDescription), + hint: ( + <> + {t(m.syncEverythingDescription)} + {'\n\n'} + {t(m.syncEverythingWarning)} + + ), }, ]; return ( - + {t(m.syncPreviewsDescriptionBottomSheet)} + {'\n\n'} + {t(m.syncPreviewWarningBottomSheet)} + + ) : ( + t(m.syncEverythingDescriptionBottomSheet) + ) } confirmActionText={ modalType === 'previews' @@ -137,7 +159,7 @@ export const SyncSettings = () => { ); }; -SyncSettings.navTitle = m.syncSettingsTitle; +MediaSyncSettings.navTitle = m.syncSettingsTitle; const styles = StyleSheet.create({ container: { diff --git a/src/frontend/screens/Settings/ProjectSettings/index.tsx b/src/frontend/screens/Settings/ProjectSettings/index.tsx index 542d6c2d9..11418a236 100644 --- a/src/frontend/screens/Settings/ProjectSettings/index.tsx +++ b/src/frontend/screens/Settings/ProjectSettings/index.tsx @@ -22,8 +22,8 @@ const m = defineMessages({ id: 'Screens.Settings.ProjectSettings.yourTeam', defaultMessage: 'Your Team', }, - syncSettings: { - id: 'Screens.Settings.ProjectSettings.syncSettings', + mediaSyncSettings: { + id: 'Screens.Settings.ProjectSettings.mediaSyncSettings', defaultMessage: 'Sync Settings', }, config: { @@ -57,9 +57,11 @@ export const ProjectSettings: NativeNavigationComponent<'ProjectSettings'> = ({ { - navigation.navigate('SyncSettings'); + navigation.navigate('MediaSyncSettings'); }}> - } /> + } + /> )} diff --git a/src/frontend/sharedComponents/SelectOne.tsx b/src/frontend/sharedComponents/SelectOne.tsx index ac7fd0169..f561895fa 100644 --- a/src/frontend/sharedComponents/SelectOne.tsx +++ b/src/frontend/sharedComponents/SelectOne.tsx @@ -9,7 +9,7 @@ type SelectOneProps = { options: Array<{ value: T; label: string; - hint?: string; + hint?: React.ReactNode; }>; radioButtonPosition?: 'left' | 'right'; color?: string; diff --git a/src/frontend/sharedTypes/index.ts b/src/frontend/sharedTypes/index.ts index 857c6b14f..1c981d6bd 100644 --- a/src/frontend/sharedTypes/index.ts +++ b/src/frontend/sharedTypes/index.ts @@ -31,7 +31,7 @@ export type Attachment = Observation['attachments'][0]; export type PhotoVariant = 'original' | 'thumbnail' | 'preview'; export type CoordinateFormat = 'utm' | 'dd' | 'dms'; -export type SyncSetting = 'previews' | 'everything'; +export type MediaSyncSetting = 'previews' | 'everything'; // Copied form /@mapeo/core/src/roles.js. Created an issue to eventuall expose this: https://github.com/digidem/mapeo-core-next/issues/532 export const CREATOR_ROLE_ID = 'a12a6702b93bd7ff'; diff --git a/src/frontend/sharedTypes/navigation.ts b/src/frontend/sharedTypes/navigation.ts index ed6f9e4e9..d2e91d157 100644 --- a/src/frontend/sharedTypes/navigation.ts +++ b/src/frontend/sharedTypes/navigation.ts @@ -98,7 +98,7 @@ export type RootStackParamsList = { Track: {trackId: string}; TrackEdit: {trackId: string}; CreateTestData: undefined; - SyncSettings: undefined; + MediaSyncSettings: undefined; DataAndPrivacy: undefined; SettingsPrivacyPolicy: undefined; }; From 5877df046eaeae2a8c01fe41d577a3391f8c8afe Mon Sep 17 00:00:00 2001 From: Cindy Green Date: Thu, 5 Sep 2024 15:43:59 -0400 Subject: [PATCH 20/25] Forgot to commit the new app screens. --- src/frontend/Navigation/Stack/AppScreens.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/frontend/Navigation/Stack/AppScreens.tsx b/src/frontend/Navigation/Stack/AppScreens.tsx index d98d50895..25686f9b3 100644 --- a/src/frontend/Navigation/Stack/AppScreens.tsx +++ b/src/frontend/Navigation/Stack/AppScreens.tsx @@ -70,7 +70,7 @@ import { TrackScreen, createNavigationOptions as createTrackNavigationOptions, } from '../../screens/Track/index.tsx'; -import {SyncSettings} from '../../screens/Settings/ProjectSettings/SyncSettings'; +import {MediaSyncSettings} from '../../screens/Settings/ProjectSettings/MediaSyncSettings.tsx'; import {DataAndPrivacy} from '../../screens/Settings/DataAndPrivacy/DataAndPrivacy'; import {SettingsPrivacyPolicy} from '../../screens/Settings/DataAndPrivacy/SettingsPrivacyPolicy'; import {TrackEdit} from '../../screens/TrackEdit/index.tsx'; @@ -289,9 +289,9 @@ export const createDefaultScreenGroup = ({ options={{headerTitle: intl(AboutSettings.navTitle)}} /> Date: Thu, 5 Sep 2024 15:53:08 -0400 Subject: [PATCH 21/25] Adjusts name from sync setting to media sync setting. --- src/frontend/sharedComponents/MediaLabel.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/frontend/sharedComponents/MediaLabel.tsx b/src/frontend/sharedComponents/MediaLabel.tsx index 4f2e4640e..f339526e8 100644 --- a/src/frontend/sharedComponents/MediaLabel.tsx +++ b/src/frontend/sharedComponents/MediaLabel.tsx @@ -31,11 +31,13 @@ export const MediaLabel: React.FC = ({ context, }) => { const {formatMessage: t} = useIntl(); - const syncSetting = usePersistedSettings(store => store.syncSetting); + const mediaSyncSetting = usePersistedSettings( + store => store.mediaSyncSetting, + ); let labelText = ''; - if (syncSetting === 'everything') { + if (mediaSyncSetting === 'everything') { labelText = t(m.labelFullSizePreviews); - } else if (syncSetting === 'previews') { + } else if (mediaSyncSetting === 'previews') { labelText = context === 'observation' ? t(m.labelPreviewsOnlyObservation) From 29c5770ab494e390cc3c3150beceeec819eb4df1 Mon Sep 17 00:00:00 2001 From: Cindy Green Date: Tue, 1 Oct 2024 13:42:53 -0400 Subject: [PATCH 22/25] Trying to make a method to get media type availability. --- messages/en.json | 6 +-- .../persistedState/usePersistedSettings.ts | 2 +- src/frontend/hooks/useMediaAvailability.ts | 37 +++++++++++++++++++ src/frontend/screens/Observation/index.tsx | 26 ++++++++----- src/frontend/screens/PhotoPreviewModal.tsx | 3 ++ src/frontend/sharedComponents/MediaLabel.tsx | 12 +++--- src/frontend/sharedTypes/index.ts | 2 +- 7 files changed, 67 insertions(+), 21 deletions(-) create mode 100644 src/frontend/hooks/useMediaAvailability.ts diff --git a/messages/en.json b/messages/en.json index 1ee137df4..f872d7177 100644 --- a/messages/en.json +++ b/messages/en.json @@ -779,13 +779,13 @@ "description": "Title of dialog asking confirmation to delete an observation", "message": "Delete observation?" }, - "screens.Observation.labelPreviewsOnlyObservation": { - "message": "Only previews are available" - }, "screens.Observation.fallbackCategoryName": { "description": "Fallback name used when category name cannot be determined for observation", "message": "Observation" }, + "screens.Observation.labelPreviewsOnlyObservation": { + "message": "Only previews are available" + }, "screens.Observation.shareMediaTitle": { "description": "Title of dialog to share an observation with media", "message": "Sharing image" diff --git a/src/frontend/hooks/persistedState/usePersistedSettings.ts b/src/frontend/hooks/persistedState/usePersistedSettings.ts index add5eab70..4c09188d0 100644 --- a/src/frontend/hooks/persistedState/usePersistedSettings.ts +++ b/src/frontend/hooks/persistedState/usePersistedSettings.ts @@ -18,7 +18,7 @@ type SettingsSlice = { const settingsSlice: StateCreator = (set, get) => ({ coordinateFormat: 'utm', manualCoordinateEntryFormat: 'utm', - mediaSyncSetting: 'default', + mediaSyncSetting: 'previews', actions: { setCoordinateFormat: coordinateFormat => set({coordinateFormat}), setManualCoordinateEntryFormat: coordinateFormat => diff --git a/src/frontend/hooks/useMediaAvailability.ts b/src/frontend/hooks/useMediaAvailability.ts new file mode 100644 index 000000000..473ce9401 --- /dev/null +++ b/src/frontend/hooks/useMediaAvailability.ts @@ -0,0 +1,37 @@ +import {useEffect, useState} from 'react'; +import {Attachment} from '../sharedTypes'; +import {useAttachmentUrlQueries} from './server/media'; + +export function useMediaAvailability(attachments: Attachment[]) { + const [availability, setAvailability] = useState< + 'full' | 'preview' | 'both' | null + >(null); + + const originalQueries = useAttachmentUrlQueries(attachments, 'original'); + const previewQueries = useAttachmentUrlQueries(attachments, 'preview'); + + useEffect(() => { + if ( + originalQueries.some(q => q.isPending) || + previewQueries.some(q => q.isPending) + ) { + setAvailability(null); + return; + } + + const hasFullSize = originalQueries.some(q => q.data?.url); + const hasPreviews = previewQueries.some(q => q.data?.url); + + if (hasFullSize && hasPreviews) { + setAvailability('both'); + } else if (hasFullSize) { + setAvailability('full'); + } else if (hasPreviews) { + setAvailability('preview'); + } else { + setAvailability(null); + } + }, [originalQueries, previewQueries]); + + return availability; +} diff --git a/src/frontend/screens/Observation/index.tsx b/src/frontend/screens/Observation/index.tsx index 2fb29ad6d..725add5c4 100644 --- a/src/frontend/screens/Observation/index.tsx +++ b/src/frontend/screens/Observation/index.tsx @@ -27,6 +27,7 @@ import {useOriginalVersionIdToDeviceId} from '../../hooks/server/projects.ts'; import {SavedPhoto} from '../../contexts/PhotoPromiseContext/types.ts'; import {ButtonFields} from './Buttons.tsx'; import {MediaLabel} from '../../sharedComponents/MediaLabel.tsx'; +import {useMediaAvailability} from '../../hooks/useMediaAvailability.ts'; const m = defineMessages({ deleteTitle: { @@ -86,6 +87,10 @@ export const ObservationScreen: NativeNavigationComponent<'Observation'> = ({ (attachment): attachment is SavedPhoto => attachment.type === 'photo', ); + console.log('Phto attachments', photoAttachments); + + const mediaAvailability = useMediaAvailability(photoAttachments); + return ( = ({ {observation.tags.notes} ) : null} - {photoAttachments.length > 0 && ( - - )} {process.env.EXPO_PUBLIC_FEATURE_MEDIA_MANAGER && photoAttachments.length > 0 && ( - )} + {photoAttachments.length > 0 && ( + + )} {fields.length > 0 && ( diff --git a/src/frontend/screens/PhotoPreviewModal.tsx b/src/frontend/screens/PhotoPreviewModal.tsx index 2e9678637..b7cf57dfe 100644 --- a/src/frontend/screens/PhotoPreviewModal.tsx +++ b/src/frontend/screens/PhotoPreviewModal.tsx @@ -15,6 +15,7 @@ import {useDraftObservation} from '../hooks/useDraftObservation.ts'; import {PhotoPreparedView} from '../sharedComponents/PhotoPreparedView.tsx'; import ErrorIcon from '../images/Error.svg'; import {MediaLabel} from '../sharedComponents/MediaLabel.tsx'; +import {useMediaAvailability} from '../hooks/useMediaAvailability.ts'; const m = defineMessages({ headerDeleteButtonText: { @@ -39,6 +40,7 @@ export const PhotoPreviewModal: FC< NativeRootNavigationProps<'PhotoPreviewModal'> > = ({route}) => { const {photo} = route.params; + const mediaAvailability = useMediaAvailability([photo]); const navigation = useNavigationFromRoot(); const [showHeader, setShowHeader] = useState(true); const draftObservation = useDraftObservation(); @@ -94,6 +96,7 @@ export const PhotoPreviewModal: FC< textColor={WHITE} style={styles.mediaLabel} context="openMedia" + mediaAvailability={mediaAvailability} /> )} diff --git a/src/frontend/sharedComponents/MediaLabel.tsx b/src/frontend/sharedComponents/MediaLabel.tsx index f339526e8..1bc52a903 100644 --- a/src/frontend/sharedComponents/MediaLabel.tsx +++ b/src/frontend/sharedComponents/MediaLabel.tsx @@ -2,7 +2,6 @@ import React from 'react'; import {Text, StyleSheet, TextStyle} from 'react-native'; import {defineMessages, useIntl} from 'react-intl'; import {WHITE} from '../lib/styles'; -import {usePersistedSettings} from '../hooks/persistedState/usePersistedSettings'; const m = defineMessages({ labelFullSizePreviews: { @@ -23,21 +22,22 @@ type MediaLabelProps = { textColor?: string; style?: TextStyle; context: 'observation' | 'openMedia'; + mediaAvailability: 'full' | 'preview' | 'both' | null; }; export const MediaLabel: React.FC = ({ textColor = WHITE, style, context, + mediaAvailability, }) => { const {formatMessage: t} = useIntl(); - const mediaSyncSetting = usePersistedSettings( - store => store.mediaSyncSetting, - ); + + console.log('mediaAvailability', mediaAvailability); let labelText = ''; - if (mediaSyncSetting === 'everything') { + if (mediaAvailability === 'both') { labelText = t(m.labelFullSizePreviews); - } else if (mediaSyncSetting === 'previews') { + } else if (mediaAvailability === 'preview') { labelText = context === 'observation' ? t(m.labelPreviewsOnlyObservation) diff --git a/src/frontend/sharedTypes/index.ts b/src/frontend/sharedTypes/index.ts index 54454b021..b4ccbe4a9 100644 --- a/src/frontend/sharedTypes/index.ts +++ b/src/frontend/sharedTypes/index.ts @@ -31,7 +31,7 @@ export type Attachment = Observation['attachments'][0]; export type PhotoVariant = 'original' | 'thumbnail' | 'preview'; export type CoordinateFormat = 'utm' | 'dd' | 'dms'; -export type MediaSyncSetting = 'default' | 'previews' | 'everything'; +export type MediaSyncSetting = 'previews' | 'everything'; // Copied from @comapeo/core/src/roles.js. Created an issue to eventually expose this: https://github.com/digidem/mapeo-core-next/issues/532 export const CREATOR_ROLE_ID = 'a12a6702b93bd7ff'; From 839e3afb980464bca20b9efd3ddb0c034ac494cf Mon Sep 17 00:00:00 2001 From: Cindy Green Date: Tue, 1 Oct 2024 16:16:39 -0400 Subject: [PATCH 23/25] Fixes some bugs with label. --- .../hooks/persistedState/usePersistedSettings.ts | 2 +- src/frontend/screens/PhotoPreviewModal.tsx | 12 +++++++++++- src/frontend/sharedComponents/MediaLabel.tsx | 8 ++++++++ src/frontend/sharedTypes/index.ts | 2 +- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/frontend/hooks/persistedState/usePersistedSettings.ts b/src/frontend/hooks/persistedState/usePersistedSettings.ts index 4c09188d0..bcabf65f2 100644 --- a/src/frontend/hooks/persistedState/usePersistedSettings.ts +++ b/src/frontend/hooks/persistedState/usePersistedSettings.ts @@ -18,7 +18,7 @@ type SettingsSlice = { const settingsSlice: StateCreator = (set, get) => ({ coordinateFormat: 'utm', manualCoordinateEntryFormat: 'utm', - mediaSyncSetting: 'previews', + mediaSyncSetting: null, actions: { setCoordinateFormat: coordinateFormat => set({coordinateFormat}), setManualCoordinateEntryFormat: coordinateFormat => diff --git a/src/frontend/screens/PhotoPreviewModal.tsx b/src/frontend/screens/PhotoPreviewModal.tsx index b7cf57dfe..d7cce1311 100644 --- a/src/frontend/screens/PhotoPreviewModal.tsx +++ b/src/frontend/screens/PhotoPreviewModal.tsx @@ -16,6 +16,10 @@ import {PhotoPreparedView} from '../sharedComponents/PhotoPreparedView.tsx'; import ErrorIcon from '../images/Error.svg'; import {MediaLabel} from '../sharedComponents/MediaLabel.tsx'; import {useMediaAvailability} from '../hooks/useMediaAvailability.ts'; +import { + SavedPhoto, + ProcessedDraftPhoto, +} from '../contexts/PhotoPromiseContext/types.ts'; const m = defineMessages({ headerDeleteButtonText: { @@ -40,7 +44,13 @@ export const PhotoPreviewModal: FC< NativeRootNavigationProps<'PhotoPreviewModal'> > = ({route}) => { const {photo} = route.params; - const mediaAvailability = useMediaAvailability([photo]); + const isSavedPhoto = ( + photo: SavedPhoto | ProcessedDraftPhoto, + ): photo is SavedPhoto => + 'driveDiscoveryId' in photo && 'name' in photo && 'hash' in photo; + const mediaAvailability = isSavedPhoto(photo) + ? useMediaAvailability([photo]) + : null; const navigation = useNavigationFromRoot(); const [showHeader, setShowHeader] = useState(true); const draftObservation = useDraftObservation(); diff --git a/src/frontend/sharedComponents/MediaLabel.tsx b/src/frontend/sharedComponents/MediaLabel.tsx index 1bc52a903..7a66880cf 100644 --- a/src/frontend/sharedComponents/MediaLabel.tsx +++ b/src/frontend/sharedComponents/MediaLabel.tsx @@ -2,6 +2,7 @@ import React from 'react'; import {Text, StyleSheet, TextStyle} from 'react-native'; import {defineMessages, useIntl} from 'react-intl'; import {WHITE} from '../lib/styles'; +import {usePersistedSettings} from '../hooks/persistedState/usePersistedSettings'; const m = defineMessages({ labelFullSizePreviews: { @@ -32,6 +33,13 @@ export const MediaLabel: React.FC = ({ mediaAvailability, }) => { const {formatMessage: t} = useIntl(); + const mediaSyncSetting = usePersistedSettings( + store => store.mediaSyncSetting, + ); + + if (!mediaSyncSetting) { + return null; + } console.log('mediaAvailability', mediaAvailability); let labelText = ''; diff --git a/src/frontend/sharedTypes/index.ts b/src/frontend/sharedTypes/index.ts index b4ccbe4a9..ed7f92e2f 100644 --- a/src/frontend/sharedTypes/index.ts +++ b/src/frontend/sharedTypes/index.ts @@ -31,7 +31,7 @@ export type Attachment = Observation['attachments'][0]; export type PhotoVariant = 'original' | 'thumbnail' | 'preview'; export type CoordinateFormat = 'utm' | 'dd' | 'dms'; -export type MediaSyncSetting = 'previews' | 'everything'; +export type MediaSyncSetting = 'previews' | 'everything' | null; // Copied from @comapeo/core/src/roles.js. Created an issue to eventually expose this: https://github.com/digidem/mapeo-core-next/issues/532 export const CREATOR_ROLE_ID = 'a12a6702b93bd7ff'; From 2216395bd59eca6b43bf748d55b8be59659d6c1a Mon Sep 17 00:00:00 2001 From: Cindy Green Date: Tue, 1 Oct 2024 16:19:03 -0400 Subject: [PATCH 24/25] removes console log. --- src/frontend/screens/Observation/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/frontend/screens/Observation/index.tsx b/src/frontend/screens/Observation/index.tsx index 725add5c4..d9211dcc5 100644 --- a/src/frontend/screens/Observation/index.tsx +++ b/src/frontend/screens/Observation/index.tsx @@ -87,8 +87,6 @@ export const ObservationScreen: NativeNavigationComponent<'Observation'> = ({ (attachment): attachment is SavedPhoto => attachment.type === 'photo', ); - console.log('Phto attachments', photoAttachments); - const mediaAvailability = useMediaAvailability(photoAttachments); return ( From 87ae468832a981c0873a62696ec31ad490bc8e81 Mon Sep 17 00:00:00 2001 From: Cindy Green Date: Tue, 1 Oct 2024 17:13:12 -0400 Subject: [PATCH 25/25] Adds a method to save only preview if that is the media sync setting. --- .../contexts/PhotoPromiseContext/index.tsx | 38 +++++++++++++------ src/frontend/hooks/useDraftObservation.ts | 5 +++ 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/frontend/contexts/PhotoPromiseContext/index.tsx b/src/frontend/contexts/PhotoPromiseContext/index.tsx index e036d44bd..a9182d666 100644 --- a/src/frontend/contexts/PhotoPromiseContext/index.tsx +++ b/src/frontend/contexts/PhotoPromiseContext/index.tsx @@ -12,11 +12,13 @@ import { DraftPhoto, } from './types'; import ImageResizer from '@bam.tech/react-native-image-resizer'; +import {MediaSyncSetting} from '../../sharedTypes'; type AddPhotoPromiseProps = { photo: Promise; mediaMetadata: MediaMetadata; draftPhotoId: string; + syncSetting?: MediaSyncSetting; }; type PhotoPromiseContextState = { @@ -46,7 +48,12 @@ export const PhotoPromiseProvider = ({ >([]); const addPhotoPromise = React.useCallback( - ({photo, draftPhotoId, mediaMetadata}: AddPhotoPromiseProps) => { + ({ + photo, + draftPhotoId, + mediaMetadata, + syncSetting, + }: AddPhotoPromiseProps) => { // Use signal to cancel processing by setting signal.didCancel = true // Important because image resize/rotate is expensive const signal: Signal = {}; @@ -56,6 +63,7 @@ export const PhotoPromiseProvider = ({ photo, draftPhotoId, mediaMetadata, + syncSetting: syncSetting ?? 'everything', }); photoPromise.signal = signal; @@ -117,12 +125,16 @@ async function processPhoto({ photo, draftPhotoId, mediaMetadata, + syncSetting = 'everything', signal, -}: AddPhotoPromiseProps & {signal: Signal}): Promise { +}: AddPhotoPromiseProps & { + signal: Signal; +}): Promise { const {uri: originalUri, rotate} = await photo; const {didCancel} = signal; if (didCancel) throw new Error('Cancelled'); + let previewUri; // rotate will be defined if the original photo failed to rotate (this // happens on low-memory devices) so we rotate the preview and @@ -138,21 +150,23 @@ async function processPhoto({ if (didCancel) throw new Error('Cancelled'); - const {uri: previewUri} = await ImageResizer.createResizedImage( - originalUri, - PREVIEW_SIZE, - PREVIEW_SIZE, - 'JPEG', - PREVIEW_QUALITY, - rotate, - ); + if (syncSetting === 'previews' || syncSetting === 'everything') { + ({uri: previewUri} = await ImageResizer.createResizedImage( + originalUri, + PREVIEW_SIZE, + PREVIEW_SIZE, + 'JPEG', + PREVIEW_QUALITY, + rotate, + )); + } if (didCancel) throw new Error('Cancelled'); return { draftPhotoId, - originalUri, - previewUri, + originalUri: syncSetting === 'everything' ? originalUri : '', + previewUri: previewUri || '', thumbnailUri, mediaMetadata, type: 'processed', diff --git a/src/frontend/hooks/useDraftObservation.ts b/src/frontend/hooks/useDraftObservation.ts index 084be8b71..8bea1d4a8 100644 --- a/src/frontend/hooks/useDraftObservation.ts +++ b/src/frontend/hooks/useDraftObservation.ts @@ -8,6 +8,7 @@ import { PhotoPromiseWithMetadata, UnprocessedDraftPhoto, } from '../contexts/PhotoPromiseContext/types'; +import {usePersistedSettings} from './persistedState/usePersistedSettings'; // react native does not have a random bytes generator, `non-secure` does not require a random bytes generator. import {nanoid} from 'nanoid/non-secure'; import * as Sentry from '@sentry/react-native'; @@ -31,6 +32,9 @@ export const useDraftObservation = () => { existingObservationToDraft, } = _usePersistedDraftObservationActions(); + const mediaSyncSetting = + usePersistedSettings(store => store.mediaSyncSetting) ?? 'everything'; + const addPhoto = useCallback( async ({capturePromise, mediaMetadata}: PhotoPromiseWithMetadata) => { // creates an id, that is stored as a placeholder in persisted photots. This is associated with the processed photo, so when the photo is done processsing, we can replace the placeholder with the actual photo @@ -41,6 +45,7 @@ export const useDraftObservation = () => { draftPhotoId, mediaMetadata, photo: capturePromise, + syncSetting: mediaSyncSetting, }); try { // the promise is run