Files
iNaturalistReactNative/src/components/Explore/MapView.tsx
Johannes Klein a52996f535 Changes to the way permissions are asked for (#1793)
* Replace name in permission requests

* TakePhoto TS

* PermissionGate TS

* Type

* PermissionGateContainer TS

* Interface

* Types

* LocationGate TS

* Remove LocationPermissionGate from Camera

* Remove write only permission

* Type

* ObsPhotoSelectionList TS

* Code style

* Show the improve with location button

* Create useLocationPermission.tsx

* Use new hook on suggestions

* Doc comment

* Use new hook in camera view

* Add strings

* Refactor Explore main content

* Use permission hook on RootExplore

* Add no location permission component

* Rename function

* Prop request permissions and use with button

* Default to Nearby label

* Remove Node type

* Projects TS

* Use useLocationPermission hook in projects screen

* Add string

* Prop permission down

* Refactor list render

* Refactor tab id into enum

* Tab type

* On nearby tab if without permission show button to prompt

* Leftovers

* Remove location permission gate from ObsEdit

* Use location permission hook on evidence section

* SearchBar TS

* Do not autoFocus on search bar in location picker.

Closes #1743

* Update type

* LocationSearch TS

* Show location permission gate on location picker's mount

* Add location permission to CurrentLocationButton

* Remove unused props of Map

* Remove unused exports from useMapLocation

* Migration

* Revert "Show location permission gate on location picker's mount"

This reverts commit 30ff75698c53d54d0b14cd2bd629f7155b743bf8.

* Add callbacks to useLocationPermission hook

* Show location permission ask on Obs Edit

* Remove unused string

* Reset explore filters should set location always to worldwide

* Add helper function to show place text in Explore

* Remove unused state of filter modal

* Show place text in filters modal with helper

* Show location permission button only for Nearby explore state

* Add a placeMode state

* Do not send placeMode to API

* Also treat limited permission as yes

* useLocationPermission in ExploreLocationSearch

* Refactor to setting place mode

Instead of logic based on the translated text of the place_guess string that is stored in ExploreContext, we are switching to an enum state that signifies which mode to show on explore:
1.) Nearby: Filters explore results based on the user's location. This also has a state without location permission that does not query the API.
2.) Place: Filtering by a specific place (as retrieved by /places API).
3.) Worldwide: Retrieve worldwide results, i.e. not having a place filter set.
4.) Map area: Filtering explore results precisely to the map rectangle shown on the explore map.

* Remove import from test

* Remove export

* Use blocked title only for blocked permission asks

* Move gallery permission container to Tab navigator as are the others

* Add gallery save title

* Split location permission explanation into two

* Update strings.ftl

* Only nav to location picker if permission was not  granted

* Check permission on app being foregrounded

* The location permission part is handled by useLocationPermission

* Do not store permission result in hook

* Use hasPermission from permissions hook

* Update fetchUserLocation.e2e-mock

* Move hook one higher

* Show user location if permission is given

* PermissionGate callbacks should use useCallback

* Add permission hook to map usage

* Fix test

* Update layout to be asserted

* Add location permission hook to Explore

* Remove console.log

* Few TS fixes

* Indentation

* Remove superficial check

* Update Podfile.lock
2024-07-12 11:00:24 +02:00

159 lines
4.5 KiB
TypeScript

import { searchObservations } from "api/observations";
import classnames from "classnames";
import {
Body1,
Button,
Map
} from "components/SharedComponents";
import { getMapRegion } from "components/SharedComponents/Map/helpers/mapHelpers.ts";
import { View } from "components/styledComponents";
import React, { useState } from "react";
import { Platform } from "react-native";
import { Region } from "react-native-maps";
import { useDebugMode, useTranslation } from "sharedHooks";
import useAuthenticatedQuery from "sharedHooks/useAuthenticatedQuery";
import { getShadowForColor } from "styles/global";
import colors from "styles/tailwindColors";
import useMapLocation from "./hooks/useMapLocation";
const DROP_SHADOW = getShadowForColor( colors.darkGray, {
offsetHeight: 4,
elevation: 6
} );
interface Props {
observations: Object[];
queryParams: {
taxon_id?: number;
};
}
const MapView = ( {
observations,
queryParams
}: Props ) => {
const { t } = useTranslation( );
const { isDebug } = useDebugMode( );
const [zoom, setZoom] = useState( -1 );
const {
onPanDrag,
onZoomToNearby,
redoSearchInMapArea,
region,
showMapBoundaryButton,
startAtNearby,
updateMapBoundaries
} = useMapLocation( );
/*
* Query for the bounding box of the taxon_id, if it hasn't been fetched yet, and
* zoom to that bounding box on the map
*/
interface StoredBoundingBoxes {
[taxonId: number]: Region;
}
const [storedBoundingBoxes, setStoredBoundingBoxes] = useState<StoredBoundingBoxes>( {} );
const obsParams = {
...queryParams, return_bounds: true, per_page: 0
};
const {
data
} = useAuthenticatedQuery(
["fetchTaxonBoundingBox"],
( optsWithAuth: Object ) => searchObservations( obsParams, optsWithAuth ),
{
enabled: obsParams.taxon_id && !storedBoundingBoxes[obsParams.taxon_id]
}
);
// Only update the map once per taxon_id, so that it only zooms to the
// bounding box on initial load
if ( obsParams.taxon_id && !storedBoundingBoxes[obsParams.taxon_id] ) {
if ( data && data.total_bounds && data.total_bounds.nelat !== undefined ) {
const boundsRegion = getMapRegion( data.total_bounds );
updateMapBoundaries( boundsRegion );
setStoredBoundingBoxes( {
...storedBoundingBoxes,
[obsParams.taxon_id]: boundsRegion
} );
}
}
const tileMapParams = {
...queryParams
};
// Tile queries never need these params
delete tileMapParams.return_bounds;
delete tileMapParams.order;
delete tileMapParams.orderBy;
return (
<View className="flex-1 overflow-hidden h-full">
<View className="z-10">
{showMapBoundaryButton && (
<View
className="mx-auto"
style={DROP_SHADOW}
>
<Button
text={t( "REDO-SEARCH-IN-MAP-AREA" )}
level="focus"
className="top-[60px] absolute self-center"
onPress={redoSearchInMapArea}
/>
</View>
)}
</View>
{ isDebug && (
<View
className={classnames(
"absolute",
"left-5",
"bottom-[140px]",
"bg-deeppink",
"p-1",
"z-10"
)}
>
<Body1 className="text-white">
{`Zoom: ${zoom}`}
</Body1>
</View>
) }
<Map
currentLocationButtonClassName="left-5 bottom-20"
observations={observations}
onPanDrag={onPanDrag}
onRegionChangeComplete={async ( newRegion, boundaries ) => {
// Seems to be a bug in react-native-maps where
// onRegionChangeComplete fires once on initial load before the
// region actually changes, so we're just ignoring that update
// here
if ( Platform.OS === "android" && Math.round( newRegion.latitude ) === 0 ) {
return;
}
await updateMapBoundaries( newRegion, boundaries );
if ( startAtNearby ) {
onZoomToNearby( newRegion, boundaries );
}
}}
onZoomToNearby={onZoomToNearby}
onZoomChange={newZoom => setZoom( newZoom )}
region={region}
showCurrentLocationButton
showExplore
showSwitchMapTypeButton
showsCompass={false}
startAtNearby={startAtNearby}
switchMapTypeButtonClassName="left-20 bottom-20"
tileMapParams={tileMapParams}
withPressableObsTiles={tileMapParams !== null}
currentLocationZoomLevel={15}
/>
</View>
);
};
export default MapView;