From 43d7b7e3eb5c9c3aff8b59093a891d127bb0b991 Mon Sep 17 00:00:00 2001 From: Andrew Chou Date: Wed, 25 Sep 2024 14:15:48 -0400 Subject: [PATCH] most of the implementation remaining bugs: - modification time not being reliable - map name should not come from file name but from style.json - handle FileSystem.documentDirectory being null? --- messages/en.json | 63 ++++ src/frontend/Navigation/Stack/AppScreens.tsx | 21 +- src/frontend/hooks/customMaps.ts | 145 +++++++++ src/frontend/lib/bytesToMegabytes.ts | 3 + .../screens/Settings/AppSettings/index.tsx | 19 ++ .../Settings/MapManagement/BackgroundMaps.tsx | 280 ++++++++++++++++++ .../Settings/MapManagement/ChooseMapFile.tsx | 65 ++++ .../MapManagement/CustomMapDetails.tsx | 133 +++++++++ .../screens/Settings/MapManagement/index.tsx | 50 ++++ src/frontend/sharedComponents/icons/index.tsx | 6 + src/frontend/sharedTypes/navigation.ts | 10 +- 11 files changed, 784 insertions(+), 11 deletions(-) create mode 100644 src/frontend/hooks/customMaps.ts create mode 100644 src/frontend/lib/bytesToMegabytes.ts create mode 100644 src/frontend/screens/Settings/MapManagement/BackgroundMaps.tsx create mode 100644 src/frontend/screens/Settings/MapManagement/ChooseMapFile.tsx create mode 100644 src/frontend/screens/Settings/MapManagement/CustomMapDetails.tsx create mode 100644 src/frontend/screens/Settings/MapManagement/index.tsx diff --git a/messages/en.json b/messages/en.json index 5976c4616..0811f69f7 100644 --- a/messages/en.json +++ b/messages/en.json @@ -132,6 +132,12 @@ "Screens.Settings.AppSettings.languageDesc": { "message": "Display language for app" }, + "Screens.Settings.AppSettings.mapManagement": { + "message": "Map Management" + }, + "Screens.Settings.AppSettings.mapManagementDesc": { + "message": "Backgrounds, Map Data" + }, "Screens.Settings.AppSettings.title": { "message": "App Settings" }, @@ -1228,6 +1234,63 @@ "screens.Settings.CreateOrJoinProject.whatIsAProject": { "message": "What is a Project" }, + "screens.Settings.MapManagement.BackgroundMaps.ChooseMapFile.acceptedFileTypes": { + "message": "Accepted file types are .smp" + }, + "screens.Settings.MapManagement.BackgroundMaps.ChooseMapFile.chooseFile": { + "message": "Choose File" + }, + "screens.Settings.MapManagement.BackgroundMaps.about": { + "message": "About Custom Map" + }, + "screens.Settings.MapManagement.BackgroundMaps.cannotBeUndone": { + "message": "This cannot be undone." + }, + "screens.Settings.MapManagement.BackgroundMaps.close": { + "message": "Close" + }, + "screens.Settings.MapManagement.BackgroundMaps.customMapAddedTitle": { + "message": "Custom Map Added" + }, + "screens.Settings.MapManagement.BackgroundMaps.deleteCustomMapDescription": { + "message": "This will delete the map and its offline areas. No collected observation data will be deleted." + }, + "screens.Settings.MapManagement.BackgroundMaps.deleteCustomMapTitle": { + "message": "Delete CUstom Map?" + }, + "screens.Settings.MapManagement.BackgroundMaps.deleteMapButtonText": { + "message": "Delete Map" + }, + "screens.Settings.MapManagement.BackgroundMaps.description1": { + "message": "Adding a custom map will enable you to see a map when you are offline." + }, + "screens.Settings.MapManagement.BackgroundMaps.description2": { + "message": "Your custom map is not shared with other devices in your project." + }, + "screens.Settings.MapManagement.BackgroundMaps.offlineMapAddedDescription": { + "message": "You will see this map when you are offline, but you will not see a map outside the area defined in your custom map." + }, + "screens.Settings.MapManagement.BackgroundMaps.screenTitle": { + "message": "Background Maps" + }, + "screens.Settings.MapManagement.MapsList.CustomMapDetails.dateAdded": { + "message": "Date Added" + }, + "screens.Settings.MapManagement.MapsList.CustomMapDetails.mapNameColumn": { + "message": "Map Name" + }, + "screens.Settings.MapManagement.MapsList.CustomMapDetails.removeMap": { + "message": "Remove Map" + }, + "screens.Settings.MapManagement.MapsList.CustomMapDetails.sizeInMegabytes": { + "message": "{value} MB" + }, + "screens.Settings.MapManagement.backgroundMaps": { + "message": "Background Maps" + }, + "screens.Settings.MapManagement.screenTitle": { + "message": "Map Management" + }, "screens.Settings.YourTeam.InviteDeclined": { "message": "Invitation Declined" }, diff --git a/src/frontend/Navigation/Stack/AppScreens.tsx b/src/frontend/Navigation/Stack/AppScreens.tsx index 52c35a0c8..2039274b3 100644 --- a/src/frontend/Navigation/Stack/AppScreens.tsx +++ b/src/frontend/Navigation/Stack/AppScreens.tsx @@ -76,6 +76,14 @@ import {SettingsPrivacyPolicy} from '../../screens/Settings/DataAndPrivacy/Setti import {TrackEdit} from '../../screens/TrackEdit/index.tsx'; import {Config} from '../../screens/Settings/Config'; import {HowToLeaveProject} from '../../screens/HowToLeaveProject.tsx'; +import { + createNavigationOptions as createMapManagementNavigationOptions, + MapManagementScreen, +} from '../../screens/Settings/MapManagement'; +import { + createNavigationOptions as createBackgroundMapsNavigationOptions, + BackgroundMapsScreen, +} from '../../screens/Settings/MapManagement/BackgroundMaps.tsx'; export const TAB_BAR_HEIGHT = 70; @@ -310,19 +318,26 @@ export const createDefaultScreenGroup = ({ component={TrackEdit} options={{headerTitle: intl(TrackEdit.navTitle)}} /> - - - + + {process.env.EXPO_PUBLIC_FEATURE_TEST_DATA_UI && ( { + return selectFile({ + extensionFilters: ['smp'], + }); + }, + }); +} + +export function useImportCustomMapFile() { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (opts: {uri: string}) => { + if (!FileSystem.documentDirectory) { + throw new Error('Document directory is unknown'); + } + + const fileName = opts.uri.split('/').at(-1); + + const directoryFileInfo = await FileSystem.getInfoAsync( + CUSTOM_STYLED_MAPS_DIRECTORY, + ); + + if (!directoryFileInfo.exists) { + await FileSystem.makeDirectoryAsync(CUSTOM_STYLED_MAPS_DIRECTORY); + } + + return FileSystem.moveAsync({ + from: opts.uri, + to: CUSTOM_STYLED_MAPS_DIRECTORY + fileName, + }); + }, + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: [CUSTOM_MAPS_QUERY_KEY], + }); + }, + }); +} + +export function useRemoveCustomMapFile() { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (opts: {uri: string}) => { + return FileSystem.deleteAsync(opts.uri, { + idempotent: true, + }); + }, + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: [CUSTOM_MAPS_QUERY_KEY], + }); + }, + }); +} + +/** + * Returns `null` if no viable map is found. Throws an error if a detected map is invalid. + */ +export function useGetCustomMapDetails() { + return useQuery({ + queryKey: [CUSTOM_MAPS_QUERY_KEY, 'active'], + queryFn: async () => { + const files = await FileSystem.readDirectoryAsync( + CUSTOM_STYLED_MAPS_DIRECTORY, + ); + + const activeUri = files[0]; + + if (!activeUri) { + return null; + } + + const mapFileInfo = await FileSystem.getInfoAsync( + CUSTOM_STYLED_MAPS_DIRECTORY + activeUri, + ); + + if (!mapFileInfo.exists || mapFileInfo.isDirectory) { + return null; + } + + const mapName = mapFileInfo.uri.split('/').at(-1); + + if (!mapName) { + throw new Error('Unable to derive map name'); + } + + return { + size: mapFileInfo.size, + uri: mapFileInfo.uri, + // TODO: Cannot seem to rely on this being accurate. May need to keep an adjacent timestamp file + modificationTime: mapFileInfo.modificationTime, + name: mapName, + }; + }, + }); +} + +async function selectFile(opts: { + copyToCache?: boolean; + mimeFilters?: Array; + extensionFilters?: Array; +}) { + const documentResult = await DocumentPicker.getDocumentAsync({ + type: opts.mimeFilters, + copyToCacheDirectory: opts.copyToCache, + multiple: false, + }); + + if (documentResult.canceled) return null; + + const asset = documentResult.assets[0]; + + if (!asset) { + throw new Error(); + } + + const hasValidExtension = opts.extensionFilters + ? opts.extensionFilters.some(extension => + asset.uri.endsWith(`.${extension}`), + ) + : true; + + if (!hasValidExtension) { + FileSystem.deleteAsync(asset.uri).catch(err => { + console.log(err); + }); + throw new Error('Invalid extension'); + } + + return asset; +} diff --git a/src/frontend/lib/bytesToMegabytes.ts b/src/frontend/lib/bytesToMegabytes.ts new file mode 100644 index 000000000..0ab82039c --- /dev/null +++ b/src/frontend/lib/bytesToMegabytes.ts @@ -0,0 +1,3 @@ +export function bytesToMegabytes(bytes: number) { + return bytes / 2 ** 20; +} diff --git a/src/frontend/screens/Settings/AppSettings/index.tsx b/src/frontend/screens/Settings/AppSettings/index.tsx index 38312529b..8497661f6 100644 --- a/src/frontend/screens/Settings/AppSettings/index.tsx +++ b/src/frontend/screens/Settings/AppSettings/index.tsx @@ -30,6 +30,14 @@ const m = defineMessages({ id: 'Screens.Settings.AppSettings.coordinateSystemDesc', defaultMessage: 'UTM,Lat/Lon,DMS', }, + mapManagement: { + id: 'Screens.Settings.AppSettings.mapManagement', + defaultMessage: 'Map Management', + }, + mapManagementDesc: { + id: 'Screens.Settings.AppSettings.mapManagementDesc', + defaultMessage: 'Backgrounds, Map Data', + }, }); export const AppSettings: NativeNavigationComponent<'AppSettings'> = ({ @@ -57,6 +65,17 @@ export const AppSettings: NativeNavigationComponent<'AppSettings'> = ({ secondary={} /> + { + navigation.navigate('MapManagement'); + }} + testID="mapManagementButton"> + + } + secondary={} + /> + ); diff --git a/src/frontend/screens/Settings/MapManagement/BackgroundMaps.tsx b/src/frontend/screens/Settings/MapManagement/BackgroundMaps.tsx new file mode 100644 index 000000000..4a38b6877 --- /dev/null +++ b/src/frontend/screens/Settings/MapManagement/BackgroundMaps.tsx @@ -0,0 +1,280 @@ +import {type NativeStackNavigationOptions} from '@react-navigation/native-stack'; +import React from 'react'; +import {defineMessages, useIntl, type MessageDescriptor} from 'react-intl'; +import {ScrollView, StyleSheet, View} from 'react-native'; +import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; + +import { + useGetCustomMapDetails, + useImportCustomMapFile, + useRemoveCustomMapFile, + useSelectOfflineMapFile, +} from '../../../hooks/customMaps'; +import ErrorSvg from '../../../images/Error.svg'; +import GreenCheckSvg from '../../../images/GreenCheck.svg'; +import {DARK_GREY, WHITE} from '../../../lib/styles'; +import { + BottomSheetModal, + BottomSheetModalContent, + useBottomSheetModal, +} from '../../../sharedComponents/BottomSheetModal'; +import {ErrorBottomSheet} from '../../../sharedComponents/ErrorBottomSheet'; +import {Loading} from '../../../sharedComponents/Loading'; +import {Text} from '../../../sharedComponents/Text'; +import {type NativeRootNavigationProps} from '../../../sharedTypes/navigation'; +import {ChooseMapFile} from './ChooseMapFile'; +import {CustomMapDetails} from './CustomMapDetails'; + +const m = defineMessages({ + screenTitle: { + id: 'screens.Settings.MapManagement.BackgroundMaps.screenTitle', + defaultMessage: 'Background Maps', + }, + about: { + id: 'screens.Settings.MapManagement.BackgroundMaps.about', + defaultMessage: 'About Custom Map', + }, + description1: { + id: 'screens.Settings.MapManagement.BackgroundMaps.description1', + defaultMessage: + 'Adding a custom map will enable you to see a map when you are offline.', + }, + // TODO: Merge into description1 when https://github.com/digidem/comapeo-mobile/issues/669 is addressed + description2: { + id: 'screens.Settings.MapManagement.BackgroundMaps.description2', + defaultMessage: + 'Your custom map is not shared with other devices in your project.', + }, + + customMapAddedTitle: { + id: 'screens.Settings.MapManagement.BackgroundMaps.customMapAddedTitle', + defaultMessage: 'Custom Map Added', + }, + offlineMapAddedDescription: { + id: 'screens.Settings.MapManagement.BackgroundMaps.offlineMapAddedDescription', + defaultMessage: + 'You will see this map when you are offline, but you will not see a map outside the area defined in your custom map.', + }, + close: { + id: 'screens.Settings.MapManagement.BackgroundMaps.close', + defaultMessage: 'Close', + }, + + deleteCustomMapTitle: { + id: 'screens.Settings.MapManagement.BackgroundMaps.deleteCustomMapTitle', + defaultMessage: 'Delete CUstom Map?', + }, + deleteCustomMapDescription: { + id: 'screens.Settings.MapManagement.BackgroundMaps.deleteCustomMapDescription', + defaultMessage: + 'This will delete the map and its offline areas. No collected observation data will be deleted.', + }, + // TODO: Merge into deleteCustomMapDescription when https://github.com/digidem/comapeo-mobile/issues/669 is addressed + cannotBeUndone: { + id: 'screens.Settings.MapManagement.BackgroundMaps.cannotBeUndone', + defaultMessage: 'This cannot be undone.', + }, + deleteMapButtonText: { + id: 'screens.Settings.MapManagement.BackgroundMaps.deleteMapButtonText', + defaultMessage: 'Delete Map', + }, +}); + +export function createNavigationOptions({ + intl, +}: { + intl: (title: MessageDescriptor) => string; +}): ( + props: NativeRootNavigationProps<'BackgroundMaps'>, +) => NativeStackNavigationOptions { + return () => { + return { + title: intl(m.screenTitle), + }; + }; +} + +export function BackgroundMapsScreen() { + const {formatMessage: t} = useIntl(); + + const mapAddedBottomSheet = useBottomSheetModal({openOnMount: false}); + const removeMapBottomSheet = useBottomSheetModal({openOnMount: false}); + + const selectOfflineMapMutation = useSelectOfflineMapFile(); + const importOfflineMapMutation = useImportCustomMapFile(); + const removeOfflineMapMutation = useRemoveCustomMapFile(); + const offlineMapQuery = useGetCustomMapDetails(); + + let renderedMapInfo; + + switch (offlineMapQuery.status) { + case 'pending': { + renderedMapInfo = ; + + break; + } + case 'error': { + renderedMapInfo = ( + { + selectOfflineMapMutation.mutate(undefined, { + onSuccess: asset => { + if (!asset) return; + + return importOfflineMapMutation.mutateAsync( + { + uri: asset.uri, + }, + { + onSuccess: () => { + mapAddedBottomSheet.openSheet(); + }, + }, + ); + }, + }); + }} + /> + ); + + break; + } + case 'success': { + if (offlineMapQuery.isFetching) { + renderedMapInfo = ; + } else { + renderedMapInfo = offlineMapQuery.data ? ( + { + removeMapBottomSheet.openSheet(); + }} + /> + ) : ( + { + selectOfflineMapMutation.mutate(undefined, { + onSuccess: asset => { + if (!asset) return; + + return importOfflineMapMutation.mutateAsync( + { + uri: asset.uri, + }, + { + onSuccess: () => { + mapAddedBottomSheet.openSheet(); + }, + }, + ); + }, + }); + }} + /> + ); + } + + break; + } + } + return ( + <> + + {t(m.about)} + + {t(m.description1)} + {t(m.description2)} + + {renderedMapInfo} + + + + } + title={t(m.deleteCustomMapTitle)} + description={ + t(m.deleteCustomMapDescription) + '\n\n' + t(m.cannotBeUndone) + } + buttonConfigs={[ + { + dangerous: true, + variation: 'filled', + text: t(m.deleteMapButtonText), + icon: , + onPress: () => { + if (!offlineMapQuery.data?.uri) return; + + removeOfflineMapMutation.mutate({ + uri: offlineMapQuery.data.uri, + }); + + removeMapBottomSheet.closeSheet(); + }, + }, + { + variation: 'outlined', + text: t(m.close), + onPress: () => { + removeMapBottomSheet.closeSheet(); + }, + }, + ]} + /> + + + + } + title={t(m.customMapAddedTitle)} + description={t(m.offlineMapAddedDescription)} + buttonConfigs={[ + { + variation: 'outlined', + text: t(m.close), + onPress: () => { + mapAddedBottomSheet.closeSheet(); + }, + }, + ]} + /> + + + { + if (removeOfflineMapMutation.error) { + removeOfflineMapMutation.reset(); + } + if (selectOfflineMapMutation.error) { + selectOfflineMapMutation.reset(); + } + }} + /> + + ); +} + +const styles = StyleSheet.create({ + container: { + paddingHorizontal: 20, + paddingVertical: 40, + gap: 36, + }, + descriptionContainer: { + gap: 20, + }, + aboutText: { + textAlign: 'center', + fontWeight: 'bold', + fontSize: 36, + color: DARK_GREY, + }, +}); diff --git a/src/frontend/screens/Settings/MapManagement/ChooseMapFile.tsx b/src/frontend/screens/Settings/MapManagement/ChooseMapFile.tsx new file mode 100644 index 000000000..5cb284431 --- /dev/null +++ b/src/frontend/screens/Settings/MapManagement/ChooseMapFile.tsx @@ -0,0 +1,65 @@ +import React from 'react'; +import {defineMessages, useIntl} from 'react-intl'; +import {StyleSheet, View} from 'react-native'; + +import {MEDIUM_GREY, RED} from '../../../lib/styles'; +import {Button} from '../../../sharedComponents/Button'; +import {Text} from '../../../sharedComponents/Text'; +import {DownloadIcon} from '../../../sharedComponents/icons'; + +const m = defineMessages({ + chooseFile: { + id: 'screens.Settings.MapManagement.BackgroundMaps.ChooseMapFile.chooseFile', + defaultMessage: 'Choose File', + }, + acceptedFileTypes: { + id: 'screens.Settings.MapManagement.BackgroundMaps.ChooseMapFile.acceptedFileTypes', + defaultMessage: 'Accepted file types are .smp', + }, +}); + +export function ChooseMapFile({onChooseFile}: {onChooseFile: () => void}) { + const {formatMessage: t} = useIntl(); + + return ( + + + {t(m.acceptedFileTypes)} + + ); +} + +const styles = StyleSheet.create({ + container: { + gap: 20, + }, + buttonContentContainer: { + flexDirection: 'row', + alignItems: 'center', + gap: 12, + }, + buttonTextBase: { + fontWeight: '700', + letterSpacing: 0.5, + fontSize: 18, + }, + asteriskText: { + fontSize: 18, + color: RED, + }, + fileTypeText: { + color: MEDIUM_GREY, + fontSize: 14, + textAlign: 'center', + }, +}); diff --git a/src/frontend/screens/Settings/MapManagement/CustomMapDetails.tsx b/src/frontend/screens/Settings/MapManagement/CustomMapDetails.tsx new file mode 100644 index 000000000..f68bddbd7 --- /dev/null +++ b/src/frontend/screens/Settings/MapManagement/CustomMapDetails.tsx @@ -0,0 +1,133 @@ +import React from 'react'; +import {FormattedDate, defineMessages, useIntl} from 'react-intl'; +import {StyleSheet, View} from 'react-native'; +import {TouchableOpacity} from 'react-native-gesture-handler'; + +import {MEDIUM_GREY, RED, VERY_LIGHT_GREY} from '../../../lib/styles'; +import {Text} from '../../../sharedComponents/Text'; +import {bytesToMegabytes} from '../../../lib/bytesToMegabytes'; + +const m = defineMessages({ + mapNameColumn: { + id: 'screens.Settings.MapManagement.MapsList.CustomMapDetails.mapNameColumn', + defaultMessage: 'Map Name', + }, + dateAddedColumn: { + id: 'screens.Settings.MapManagement.MapsList.CustomMapDetails.dateAdded', + defaultMessage: 'Date Added', + }, + removeMap: { + id: 'screens.Settings.MapManagement.MapsList.CustomMapDetails.removeMap', + defaultMessage: 'Remove Map', + }, + sizeInMegabytes: { + id: 'screens.Settings.MapManagement.MapsList.CustomMapDetails.sizeInMegabytes', + defaultMessage: '{value} MB', + }, +}); + +export function CustomMapDetails({ + dateAdded, + name, + onRemove, + size, +}: { + dateAdded: number; + name: string; + onRemove: () => void; + size?: number; +}) { + const {formatMessage: t} = useIntl(); + + const calculatedSize = size ? bytesToMegabytes(size).toFixed(0) : undefined; + const displayedSize = + calculatedSize === undefined + ? undefined + : parseInt(calculatedSize, 10) < 1 + ? '<1' + : calculatedSize; + + return ( + + + {t(m.mapNameColumn)} + {t(m.dateAddedColumn)} + + + + + {name} + + {displayedSize !== undefined && + t(m.sizeInMegabytes, { + value: displayedSize, + })} + + + + + + + + + + + {t(m.removeMap)} + + + + + ); +} + +const styles = StyleSheet.create({ + rootContainer: { + gap: 12, + }, + columnHeadersContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + }, + cardContainer: { + paddingVertical: 20, + paddingHorizontal: 10, + borderRadius: 3, + borderColor: VERY_LIGHT_GREY, + borderWidth: 2, + gap: 36, + }, + cardRow: { + flexDirection: 'row', + justifyContent: 'space-between', + }, + columnTitleText: { + color: MEDIUM_GREY, + }, + dateAddedText: { + color: MEDIUM_GREY, + }, + sizeText: { + color: MEDIUM_GREY, + }, + removeMapText: { + fontWeight: 'bold', + color: RED, + }, + nameText: { + fontWeight: 'bold', + fontSize: 18, + }, + columnLeft: { + flex: 1, + alignItems: 'flex-start', + }, + columnRight: { + flex: 1, + alignItems: 'flex-end', + }, +}); diff --git a/src/frontend/screens/Settings/MapManagement/index.tsx b/src/frontend/screens/Settings/MapManagement/index.tsx new file mode 100644 index 000000000..cad00b4f8 --- /dev/null +++ b/src/frontend/screens/Settings/MapManagement/index.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import {type NativeStackNavigationOptions} from '@react-navigation/native-stack'; +import {defineMessages, useIntl, type MessageDescriptor} from 'react-intl'; +import {ScrollView} from 'react-native'; + +import {List, ListItem, ListItemText} from '../../../sharedComponents/List'; +import {type NativeRootNavigationProps} from '../../../sharedTypes/navigation'; + +const m = defineMessages({ + screenTitle: { + id: 'screens.Settings.MapManagement.screenTitle', + defaultMessage: 'Map Management', + }, + backgroundMaps: { + id: 'screens.Settings.MapManagement.backgroundMaps', + defaultMessage: 'Background Maps', + }, +}); + +export function MapManagementScreen({ + navigation, +}: NativeRootNavigationProps<'MapManagement'>) { + const {formatMessage: t} = useIntl(); + return ( + + + { + navigation.navigate('BackgroundMaps'); + }}> + + + + + ); +} + +export function createNavigationOptions({ + intl, +}: { + intl: (title: MessageDescriptor) => string; +}): ( + props: NativeRootNavigationProps<'MapManagement'>, +) => NativeStackNavigationOptions { + return () => { + return { + title: intl(m.screenTitle), + }; + }; +} diff --git a/src/frontend/sharedComponents/icons/index.tsx b/src/frontend/sharedComponents/icons/index.tsx index d69b0781d..4022e10d3 100644 --- a/src/frontend/sharedComponents/icons/index.tsx +++ b/src/frontend/sharedComponents/icons/index.tsx @@ -2,6 +2,8 @@ import React from 'react'; import MaterialCommunityIcon from 'react-native-vector-icons/MaterialCommunityIcons'; import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; import FontAwesomeIcon from 'react-native-vector-icons/FontAwesome'; +import AntDesignIcon from 'react-native-vector-icons/AntDesign'; + import {Image} from 'react-native'; import {Circle} from './Circle'; @@ -213,3 +215,7 @@ export const LocationFollowingIcon = ({ export const StopIcon = ({color = WHITE, size = 30}: FontIconProps) => ( ); + +export function DownloadIcon(props: FontIconProps) { + return ; +} diff --git a/src/frontend/sharedTypes/navigation.ts b/src/frontend/sharedTypes/navigation.ts index 82136bb73..8aa6abba1 100644 --- a/src/frontend/sharedTypes/navigation.ts +++ b/src/frontend/sharedTypes/navigation.ts @@ -61,14 +61,6 @@ export type RootStackParamsList = { Security: undefined; DirectionalArrow: undefined; P2pUpgrade: undefined; - MapSettings: undefined; - BackgroundMaps: undefined; - BackgroundMapInfo: { - bytesStored: number; - id: string; - styleUrl: string; - name: string; - }; ObservationFields: {question: number}; ObservationCreate: undefined; BGMapsSettings: undefined; @@ -102,6 +94,8 @@ export type RootStackParamsList = { DataAndPrivacy: undefined; SettingsPrivacyPolicy: undefined; HowToLeaveProject: undefined; + MapManagement: undefined; + BackgroundMaps: undefined; }; export type OnboardingParamsList = {