diff --git a/package-lock.json b/package-lock.json
index 431bdad0..85020dd3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -21,6 +21,7 @@
"expo": "^45.0.5",
"expo-font": "^10.1.0",
"expo-haptics": "^11.2.0",
+ "ngeohash": "^0.6.3",
"react": "17.0.2",
"react-native": "0.68.2",
"react-native-background-fetch": "^4.1.0",
@@ -12195,6 +12196,14 @@
"resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.0.1.tgz",
"integrity": "sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A=="
},
+ "node_modules/ngeohash": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/ngeohash/-/ngeohash-0.6.3.tgz",
+ "integrity": "sha512-kltF0cOxgx1AbmVzKxYZaoB0aj7mOxZeHaerEtQV0YaqnkXNq26WWqMmJ6lTqShYxVRWZ/mwvvTrNeOwdslWiw==",
+ "engines": {
+ "node": ">=v0.2.0"
+ }
+ },
"node_modules/nice-try": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
@@ -25019,6 +25028,11 @@
"resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.0.1.tgz",
"integrity": "sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A=="
},
+ "ngeohash": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/ngeohash/-/ngeohash-0.6.3.tgz",
+ "integrity": "sha512-kltF0cOxgx1AbmVzKxYZaoB0aj7mOxZeHaerEtQV0YaqnkXNq26WWqMmJ6lTqShYxVRWZ/mwvvTrNeOwdslWiw=="
+ },
"nice-try": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
diff --git a/package.json b/package.json
index 7a230142..570acbc2 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,7 @@
"expo": "^45.0.5",
"expo-font": "^10.1.0",
"expo-haptics": "^11.2.0",
+ "ngeohash": "^0.6.3",
"react": "17.0.2",
"react-native": "0.68.2",
"react-native-background-fetch": "^4.1.0",
diff --git a/src/components/DropyMap.js b/src/components/DropyMap.js
index 863b300a..f758fd39 100644
--- a/src/components/DropyMap.js
+++ b/src/components/DropyMap.js
@@ -1,8 +1,9 @@
import React, { useEffect, useRef, useState } from 'react';
import { StyleSheet, Platform } from 'react-native';
-import MapView, { PROVIDER_GOOGLE } from 'react-native-maps';
+import MapView, { Circle, Polygon, PROVIDER_GOOGLE } from 'react-native-maps';
import LinearGradient from 'react-native-linear-gradient';
+import Geohash from 'ngeohash';
import { useNavigation } from '@react-navigation/native';
import { useInitializedGeolocation } from '../hooks/useGeolocation';
@@ -14,12 +15,14 @@ import mapStyleIOS from '../assets/mapStyleIOS.json';
import API from '../services/API';
import Haptics from '../utils/haptics';
+import useCurrentUser from '../hooks/useCurrentUser';
+import { GEOHASH_SIZE } from '../states/GeolocationContextProvider';
import { coordinatesDistance } from '../utils/coordinates';
import MapLoadingOverlay from './overlays/MapLoadingOverlay';
-import Sonar from './Sonar';
import DropyMapMarker from './DropyMapMarker';
import DebugText from './DebugText';
import RetrievedDropyMapMarker from './RetrievedDropyMapMarker';
+import Sonar from './Sonar';
const INITIAL_PITCH = 10;
const INITIAL_ZOOM = 17;
@@ -31,6 +34,10 @@ const DropyMap = ({ dropiesAround, retrieveDropy, museumVisible, selectedDropyIn
const { sendBottomAlert } = useOverlay();
const { userCoordinates, compassHeading, initialized: geolocationInitialized } = useInitializedGeolocation();
+ const { developerMode } = useCurrentUser();
+
+ const mapRef = useRef(null);
+ const [mapIsReady, setMapIsReady] = useState(false);
const handleDropyPressed = async (dropy) => {
try {
@@ -57,10 +64,6 @@ const DropyMap = ({ dropiesAround, retrieveDropy, museumVisible, selectedDropyIn
}
};
- const mapRef = useRef(null);
-
- const [mapIsReady, setMapIsReady] = useState(false);
-
useEffect(() => {
if(mapIsReady === false) return;
if(mapRef?.current == null) return;
@@ -145,6 +148,7 @@ const DropyMap = ({ dropiesAround, retrieveDropy, museumVisible, selectedDropyIn
))}
>
)}
+ {developerMode && }
@@ -156,9 +160,58 @@ const DropyMap = ({ dropiesAround, retrieveDropy, museumVisible, selectedDropyIn
style={StyleSheet.absoluteFillObject}
/>
{JSON.stringify(userCoordinates, null, 2)}
- {JSON.stringify(dropiesAround, null, 2)}
+ {JSON.stringify(dropiesAround, null, 2)}
>
);
};
export default DropyMap;
+
+const MapDebugger = ({ userCoordinates }) => {
+ const [debugPolygons, setDebugPolygons] = useState([]);
+
+ useEffect(() => {
+ if(!userCoordinates) return;
+
+ const polygons = [];
+ for (const chunkInt of userCoordinates.geoHashs) {
+ const [
+ minlat,
+ minlon,
+ maxlat,
+ maxlon
+ ] = Geohash.decode_bbox_int(chunkInt, GEOHASH_SIZE);
+ polygons.push([
+ { latitude: minlat, longitude: minlon },
+ { latitude: maxlat, longitude: minlon },
+ { latitude: maxlat, longitude: maxlon },
+ { latitude: minlat, longitude: maxlon }
+ ]);
+ }
+
+ setDebugPolygons(polygons);
+ }, [userCoordinates]);
+
+ return (
+ <>
+ {debugPolygons.map((polygon, index) => (
+
+
+
+ ))}
+
+
+ >
+ );
+};
diff --git a/src/components/Sonar.js b/src/components/Sonar.js
index 4b37e60c..c3645c28 100644
--- a/src/components/Sonar.js
+++ b/src/components/Sonar.js
@@ -5,6 +5,8 @@ import Svg, { Circle, RadialGradient, Stop } from 'react-native-svg';
import Styles, { Colors } from '../styles/Styles';
import GlassCircleButton from './GlassCircleButton';
+const CENTER_ICON_SIZE = 15;
+
const AnimatedSvg = Animated.createAnimatedComponent(Svg);
const Sonar = ({ visible }) => {
@@ -60,7 +62,8 @@ const Sonar = ({ visible }) => {
pointerEvents='none'
style={{
...Styles.center,
- transform: [{ scale: visibleAnimatedValue }],
+ ...StyleSheet.absoluteFillObject,
+ transform: [ { translateY: CENTER_ICON_SIZE / 2 }, { scale: visibleAnimatedValue }],
}}>
{
cx="50" cy="50" r="50" fill="url(#grad)"
/>
-
+
);
};
diff --git a/src/hooks/useDropiesAroundSocket.js b/src/hooks/useDropiesAroundSocket.js
index 9dc3adc7..950b0866 100644
--- a/src/hooks/useDropiesAroundSocket.js
+++ b/src/hooks/useDropiesAroundSocket.js
@@ -1,6 +1,5 @@
import { useEffect, useState } from 'react';
import useCurrentUser from './useCurrentUser';
-import useTravelDistanceCallback from './useTravelDistanceCallback';
import { useInitializedGeolocation } from './useGeolocation';
import useSocket from './useSocket';
@@ -13,17 +12,11 @@ const useDropiesAroundSocket = () => {
const [dropiesAround, setDropiesAround] = useState([]);
- useTravelDistanceCallback(updateAllDropiesAround, 100);
-
useEffect(() => {
if (geolocationInitialized === false) return;
if (dropySocket == null) return;
- updateAllDropiesAround();
- dropySocket.on('connect', updateAllDropiesAround);
-
dropySocket.on('dropy_created', (response) => {
-
if (response.error != null) {
console.error('Error getting created dropy', response.error);
return;
@@ -50,26 +43,22 @@ const useDropiesAroundSocket = () => {
};
}, [geolocationInitialized]);
- function updateAllDropiesAround() {
- if (dropySocket == null) return;
- if (dropySocket.connected === false) return;
- if(userCoordinates == null) return;
+ useEffect(() => {
+ dropySocket.emit('zones_update', { zones: userCoordinates.geoHashs }, (response) => {
- dropySocket.emit('all_dropies_around', {
- latitude: userCoordinates.latitude,
- longitude: userCoordinates.longitude,
- }, (response) => {
if(response.error != null) {
- console.error('Error getting dropies around', response.error);
+ console.error('Error updating zones', response.error);
return;
}
+
const dropies = response.data.slice(0, 30).map((dropy) => ({
...dropy,
isUserDropy: dropy.emitterId === user.id,
}));
+
setDropiesAround(dropies ?? []);
});
- }
+ }, [userCoordinates.geoHashs[0]]);
const createDropy = (latitude, longitude, mediaType, content) => {
return new Promise((resolve) => {
@@ -78,6 +67,7 @@ const useDropiesAroundSocket = () => {
};
const retrieveDropy = (dropyId) => {
+ setDropiesAround(olds => olds.filter(dropy => dropy.id !== dropyId));
return new Promise((resolve) => {
dropySocket.emit('dropy_retrieved', { dropyId }, resolve);
});
diff --git a/src/hooks/useTravelDistanceCallback.js b/src/hooks/useTravelDistanceCallback.js
deleted file mode 100644
index a3541b42..00000000
--- a/src/hooks/useTravelDistanceCallback.js
+++ /dev/null
@@ -1,62 +0,0 @@
-import { useEffect, useRef } from 'react';
-import { coordinatesDistance } from '../utils/coordinates';
-import useGeolocation from './useGeolocation';
-
-const useTravelDistanceCallback = (
- /**
- * Called when the user has moved more than the specified trigger
- * distance
- */
- distanceCallback = undefined,
- /**
- * Distance in meters the user need to move to trigger the callback
- * @default 20
- */
- triggerDistanceMeters = 20,
- /**
- * Call the callback even if the user has not moved after the specified
- * time delay (undefined = no timeout behaviour)
- * @default undefined
- */
- timeoutTime = undefined
-) => {
-
- const lastTrigger = useRef({ coordinates: null, timestamp: null });
-
- const { userCoordinates } = useGeolocation();
-
- useEffect(() => {
- if(timeoutTime == null) return;
- const interval = setInterval(() => {
- if(Date.now() - lastTrigger.current.timestamp > timeoutTime) {
- trigger();
- }
- }, 1000);
- return () => clearInterval(interval);
- }, []);
-
- useEffect(() => {
- if(userCoordinates == null) return;
- if(distanceCallback == null) return;
-
- if(lastTrigger.current.coordinates == null) {
- trigger();
- return;
- }
-
- const distance = coordinatesDistance(userCoordinates, lastTrigger.current.coordinates);
- if(distance > triggerDistanceMeters) {
- trigger();
- }
- }, [userCoordinates]);
-
- const trigger = () => {
- lastTrigger.current = {
- coordinates: userCoordinates,
- timestamp: Date.now(),
- };
- distanceCallback();
- };
-};
-
-export default useTravelDistanceCallback;
diff --git a/src/states/GeolocationContextProvider.js b/src/states/GeolocationContextProvider.js
index dd366533..9767c897 100644
--- a/src/states/GeolocationContextProvider.js
+++ b/src/states/GeolocationContextProvider.js
@@ -1,6 +1,7 @@
import React, { createContext, useState } from 'react';
import Geolocation from 'react-native-geolocation-service';
import CompassHeading from 'react-native-compass-heading';
+import Geohash from 'ngeohash';
import usePermissions from '../hooks/usePermissions';
import useEffectForegroundOnly from '../hooks/useEffectForegroundOnly';
@@ -9,6 +10,8 @@ import GeolocationModal from '../components/overlays/GeolocationModal';
export const GeolocationContext = createContext(null);
+export const GEOHASH_SIZE = 32;
+
const GeolocationProvider = ({ children }) => {
const [userCoordinates, setUserCoordinates] = useState(null);
@@ -28,8 +31,16 @@ const GeolocationProvider = ({ children }) => {
const registerGeolocationListener = () => Geolocation.watchPosition(
(infos) => {
- const { coords } = infos;
- setUserCoordinates(coords);
+ const { latitude, longitude } = infos.coords;
+
+ const hash = Geohash.encode_int(latitude, longitude, GEOHASH_SIZE);
+ const geoHashs = [hash, ...Geohash.neighbors_int(hash, GEOHASH_SIZE)];
+
+ setUserCoordinates({
+ latitude,
+ longitude,
+ geoHashs,
+ });
},
console.warn,
{