Browse Source

location sharing

Viktoriia 2 months ago
parent
commit
734028adb7

+ 1 - 0
src/modules/api/events/events-api.ts

@@ -27,6 +27,7 @@ export type SingleEvent = {
   joined: 0 | 1;
   visible?: 0 | 1;
   archived?: 0 | 1;
+  time?: string;
 };
 
 export type Participants = {

+ 4 - 1
src/modules/api/location/location-api.ts

@@ -5,6 +5,7 @@ import { FeatureCollection } from '@turf/turf';
 
 export interface PostGetSettingsReturn extends ResponseType {
   sharing: 0 | 1;
+  sharing_regions: 0 | 1;
 }
 
 export interface PostGetUsersLocationReturn extends ResponseType {
@@ -25,5 +26,7 @@ export const locationApi = {
   getUsersLocation: (token: string) =>
     request.postForm<PostGetUsersLocationReturn>(API.GET_USERS_LOCATION, { token }),
   getUsersCount: (token: string) =>
-    request.postForm<PostGetUserCountReturn>(API.GET_USERS_COUNT, { token })
+    request.postForm<PostGetUserCountReturn>(API.GET_USERS_COUNT, { token }),
+  setSettingsRegions: (token: string, sharing: 0 | 1) =>
+    request.postForm<ResponseType>(API.SET_LOCATION_REGIONS, { token, sharing })
 };

+ 2 - 1
src/modules/api/location/location-query-keys.tsx

@@ -3,5 +3,6 @@ export const locationQueryKeys = {
   setSettings: () => ['location', 'setSettings'],
   updateLocation: () => ['location', 'updateLocation'],
   getUsersLocation: (token: string) => ['location', 'getUsersLocation', token],
-  getUsersCount: (token: string) => ['location', 'getUsersCount', token]
+  getUsersCount: (token: string) => ['location', 'getUsersCount', token],
+  setSettingsRegions: () => ['location', 'setSettingsRegions']
 };

+ 1 - 0
src/modules/api/location/queries/index.ts

@@ -3,3 +3,4 @@ export * from './use-post-set-settings';
 export * from './use-post-update-location';
 export * from './use-post-get-users-location';
 export * from './use-post-get-users-on-map-count';
+export * from './use-post-set-settings-regions';

+ 19 - 0
src/modules/api/location/queries/use-post-set-settings-regions.tsx

@@ -0,0 +1,19 @@
+import { useMutation } from '@tanstack/react-query';
+
+import { locationQueryKeys } from '../location-query-keys';
+import { locationApi } from '../location-api';
+
+import type { BaseAxiosError } from '../../../../types';
+import { ResponseType } from '@api/response-type';
+
+export const usePostSetSettingsRegionsMutation = () => {
+  return useMutation<ResponseType, BaseAxiosError, { token: string; sharing: 0 | 1 }, ResponseType>(
+    {
+      mutationKey: locationQueryKeys.setSettingsRegions(),
+      mutationFn: async (variables) => {
+        const response = await locationApi.setSettingsRegions(variables.token, variables.sharing);
+        return response.data;
+      }
+    }
+  );
+};

+ 5 - 2
src/screens/InAppScreens/TravelsScreen/EventScreen/index.tsx

@@ -537,7 +537,11 @@ const EventScreen = ({ route }: { route: any }) => {
     if (event.settings.date_from && event.settings.date_to) {
       return `${moment(event.settings.date_from, 'YYYY-MM-DD').format('DD MMMM YYYY')} - ${moment(event.settings.date_to, 'YYYY-MM-DD').format('DD MMMM YYYY')}`;
     } else {
-      return moment(event.settings.date, 'YYYY-MM-DD').format('DD MMMM YYYY');
+      let date = moment(event.settings.date, 'YYYY-MM-DD').format('DD MMMM YYYY');
+      if (event.time) {
+        date += `, ${event.time}`;
+      }
+      return date;
     }
   };
 
@@ -1242,7 +1246,6 @@ const WebDisplay = React.memo(function WebDisplay({ html }: { html: string }) {
   const contentWidth = windowWidth * 0.9;
 
   const token = storage.get('token', StoreType.STRING) as string;
-
   const processedHtml = React.useMemo(() => {
     let updatedHtml = html;
 

+ 5 - 1
src/screens/InAppScreens/TravelsScreen/EventsScreen/index.tsx

@@ -62,7 +62,11 @@ const EventsScreen = () => {
     if (event.date_from && event.date_to) {
       return `${moment(event.date_from, 'YYYY-MM-DD').format('DD MMM YYYY')} - ${moment(event.date_to, 'YYYY-MM-DD').format('DD MMM YYYY')}`;
     } else {
-      return moment(event.date, 'YYYY-MM-DD').format('DD MMM YYYY');
+      let date = moment(event.date, 'YYYY-MM-DD').format('DD MMMM YYYY');
+      if (event.time) {
+        date += `, ${event.time}`;
+      }
+      return date;
     }
   };
 

+ 155 - 10
src/screens/LocationSharingScreen/index.tsx

@@ -1,4 +1,4 @@
-import React, { useEffect, useState } from 'react';
+import React, { useCallback, useEffect, useState } from 'react';
 import {
   View,
   Linking,
@@ -21,6 +21,7 @@ import { useFocusEffect } from '@react-navigation/native';
 import {
   usePostGetSettingsQuery,
   usePostSetSettingsMutation,
+  usePostSetSettingsRegionsMutation,
   usePostUpdateLocationMutation
 } from '@api/location';
 import LocationIcon from 'assets/icons/location.svg';
@@ -33,12 +34,15 @@ const LocationSharingScreen = ({ navigation }: { navigation: any }) => {
   const token = storage.get('token', StoreType.STRING) as string;
   const { data: locationSettings, refetch } = usePostGetSettingsQuery(token, !!token);
   const { mutateAsync: setSettings } = usePostSetSettingsMutation();
+  const { mutateAsync: setSettingsRegions } = usePostSetSettingsRegionsMutation();
+
   const { mutateAsync: updateLocation } = usePostUpdateLocationMutation();
 
   const [initialPermissionStatus, setInitialPermissionStatus] = useState<
     'granted' | 'denied' | 'undetermined' | null
   >(null);
   const [isSharingWithEveryone, setIsSharingWithEveryone] = useState(false);
+  const [canMarkRegions, setCanMarkRegions] = useState(false);
   const [askLocationVisible, setAskLocationVisible] = useState<boolean>(false);
   const [openSettingsVisible, setOpenSettingsVisible] = useState<boolean>(false);
   const [openSettingsBackgroundVisible, setOpenSettingsBackgroundVisible] =
@@ -53,6 +57,9 @@ const LocationSharingScreen = ({ navigation }: { navigation: any }) => {
         setIsSharingWithEveryone(
           locationSettings.sharing !== 0 && status === 'granted' && isServicesEnabled
         );
+        setCanMarkRegions(
+          locationSettings.sharing_regions !== 0 && status === 'granted' && isServicesEnabled
+        );
       }
     };
 
@@ -68,11 +75,14 @@ const LocationSharingScreen = ({ navigation }: { navigation: any }) => {
           setInitialPermissionStatus(currentStatus);
         } else if (
           currentStatus !== 'granted' &&
-          (isSharingWithEveryone || initialPermissionStatus === 'granted')
+          (isSharingWithEveryone || canMarkRegions || initialPermissionStatus === 'granted')
         ) {
           setSettings({ token, sharing: 0 });
+          setSettingsRegions({ token, sharing: 0 });
+
           storage.set('showNomads', false);
           setIsSharingWithEveryone(false);
+          setCanMarkRegions(false);
           await stopBackgroundLocationUpdates();
         }
       }
@@ -88,10 +98,16 @@ const LocationSharingScreen = ({ navigation }: { navigation: any }) => {
       const status = await checkLocationPermissions();
       const isServicesEnabled = await Location.hasServicesEnabledAsync();
 
-      if ((status !== 'granted' && isSharingWithEveryone) || !isServicesEnabled) {
+      if (
+        (status !== 'granted' && (isSharingWithEveryone || canMarkRegions)) ||
+        !isServicesEnabled
+      ) {
         setSettings({ token, sharing: 0 });
+        setSettingsRegions({ token, sharing: 0 });
+
         storage.set('showNomads', false);
         setIsSharingWithEveryone(false);
+        setCanMarkRegions(false);
         await stopBackgroundLocationUpdates();
       }
       setInitialPermissionStatus(status);
@@ -100,9 +116,11 @@ const LocationSharingScreen = ({ navigation }: { navigation: any }) => {
     getInitialPermissionsStatus();
   }, []);
 
-  useFocusEffect(() => {
-    refetchData();
-  });
+  useFocusEffect(
+    useCallback(() => {
+      refetchData();
+    }, [navigation])
+  );
 
   const refetchData = async () => {
     await refetch();
@@ -115,21 +133,30 @@ const LocationSharingScreen = ({ navigation }: { navigation: any }) => {
 
   const toggleSettingsSwitch = async () => {
     if (!isSharingWithEveryone) {
-      handleGetLocation();
+      handleGetLocation(true);
     } else {
       setSettings({ token, sharing: 0 });
       storage.set('showNomads', false);
       setIsSharingWithEveryone(false);
-      await stopBackgroundLocationUpdates();
     }
   };
 
-  const handleGetLocation = async () => {
+  const toggleRegionSettingsSwitch = async () => {
+    if (!canMarkRegions) {
+      handleGetLocation(false);
+    } else {
+      setSettingsRegions({ token, sharing: 0 });
+
+      setCanMarkRegions(false);
+    }
+  };
+
+  const handleGetLocation = async (isSettings: boolean) => {
     let { status, canAskAgain } = await Location.getForegroundPermissionsAsync();
     const isServicesEnabled = await Location.hasServicesEnabledAsync();
 
     if (status === 'granted' && isServicesEnabled) {
-      getLocation();
+      getLocationToggle(isSettings);
       const bgStatus = await Location.getBackgroundPermissionsAsync();
       if (bgStatus.status !== 'granted') {
         const { status } = await Location.requestBackgroundPermissionsAsync();
@@ -155,6 +182,28 @@ const LocationSharingScreen = ({ navigation }: { navigation: any }) => {
     });
     setSettings({ token, sharing: 1 });
     setIsSharingWithEveryone(true);
+    setSettingsRegions({ token, sharing: 1 });
+    setCanMarkRegions(true);
+
+    updateLocation({
+      token,
+      lat: currentLocation.coords.latitude,
+      lng: currentLocation.coords.longitude
+    });
+  };
+
+  const getLocationToggle = async (isSettings: boolean) => {
+    if (isSettings) {
+      setSettings({ token, sharing: 1 });
+      setIsSharingWithEveryone(true);
+    } else {
+      setSettingsRegions({ token, sharing: 1 });
+      setCanMarkRegions(true);
+    }
+
+    let currentLocation = await Location.getCurrentPositionAsync({
+      accuracy: Location.Accuracy.Balanced
+    });
     updateLocation({
       token,
       lat: currentLocation.coords.latitude,
@@ -217,6 +266,88 @@ const LocationSharingScreen = ({ navigation }: { navigation: any }) => {
         </View>
       </TouchableOpacity>
 
+      <TouchableOpacity
+        style={[
+          styles.alignStyle,
+          styles.buttonWrapper,
+          {
+            justifyContent: 'space-between',
+            height: undefined,
+            paddingVertical: 8
+          }
+        ]}
+        onPress={toggleRegionSettingsSwitch}
+      >
+        <View style={[styles.alignStyle, { flex: 1 }]}>
+          <UsersIcon fill={Colors.DARK_BLUE} width={20} height={20} />
+          <Text style={[styles.buttonLabel, { flex: 1 }]}>
+            Share location to automatically mark a region or DARE place as visited
+          </Text>
+        </View>
+        <View>
+          <Switch
+            trackColor={{ false: Colors.LIGHT_GRAY, true: Colors.DARK_BLUE }}
+            thumbColor={Colors.WHITE}
+            onValueChange={toggleRegionSettingsSwitch}
+            value={canMarkRegions}
+            style={{ transform: 'scale(0.8)' }}
+          />
+        </View>
+      </TouchableOpacity>
+
+      <View style={{ marginVertical: 12, gap: 6 }}>
+        <Text style={[textStyles.text, textStyles.boldText]}>
+          At NomadMania, we respect your privacy.
+        </Text>
+        <Text style={[textStyles.text]}>
+          If you choose to share your location, it can be used to:
+        </Text>
+        <View style={[textStyles.bulletItem]}>
+          <Text style={textStyles.bulletIcon}>{'\u2022'}</Text>
+          <Text style={[textStyles.text, { flex: 1 }]}>
+            show your current location to other users who also share theirs
+          </Text>
+        </View>
+        <View style={textStyles.bulletItem}>
+          <Text style={textStyles.bulletIcon}>{'\u2022'}</Text>
+          <Text style={[textStyles.text, { flex: 1 }]}>
+            automatically mark regions (and DARE places) as visited in the Regions section as well
+            as in the Trips section
+          </Text>
+        </View>
+
+        <Text style={[textStyles.text, { marginTop: 6 }]}>
+          You can choose how you want to share your location:
+        </Text>
+        <View style={[textStyles.bulletItem]}>
+          <Text style={textStyles.bulletIcon}>{'\u2022'}</Text>
+          <Text style={[textStyles.text, { flex: 1 }]}>
+            <Text style={textStyles.boldText}>Only when the app is open</Text> – Your location
+            updates only when you open the app. This uses less battery but may be less accurate.
+          </Text>
+        </View>
+        <View style={textStyles.bulletItem}>
+          <Text style={textStyles.bulletIcon}>{'\u2022'}</Text>
+          <Text style={[textStyles.text, { flex: 1 }]}>
+            <Text style={textStyles.boldText}>In the background</Text> – Your location stays up to
+            date even when the app is closed. Other users see your latest location, and visited
+            regions and DARE places are marked automatically.
+          </Text>
+        </View>
+        <Text style={[textStyles.text]}>
+          You’re always in control, and you can change these settings anytime in the app{' '}
+          <Text
+            style={{ color: Colors.ORANGE }}
+            onPress={() =>
+              Platform.OS === 'ios' ? Linking.openURL('app-settings:') : Linking.openSettings()
+            }
+          >
+            Settings
+          </Text>{' '}
+          section.
+        </Text>
+      </View>
+
       <WarningModal
         type={'success'}
         isVisible={askLocationVisible}
@@ -268,6 +399,20 @@ const textStyles = StyleSheet.create({
     shadowRadius: 4,
     shadowOpacity: 1,
     elevation: 8
+  },
+  boldText: {
+    fontWeight: '700'
+  },
+  bulletItem: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    width: '100%'
+  },
+  bulletIcon: {
+    fontSize: 12,
+    fontWeight: '700',
+    marginHorizontal: 6,
+    alignSelf: 'flex-start'
   }
 });
 

+ 4 - 2
src/types/api.ts

@@ -198,7 +198,8 @@ export enum API_ENDPOINT {
   GET_MORE_PHOTOS = 'get-more-photos',
   CAN_ADD_EVENT = 'can-add-event',
   GET_PHOTOS_FOR_REGION = 'get-photos-for-region',
-  ADD_EVENT = 'add-event'
+  ADD_EVENT = 'add-event',
+  SET_LOCATION_REGIONS = 'set-settings-regions'
 }
 
 export enum API {
@@ -369,7 +370,8 @@ export enum API {
   GET_MORE_PHOTOS = `${API_ROUTE.EVENTS}/${API_ENDPOINT.GET_MORE_PHOTOS}`,
   CAN_ADD_EVENT = `${API_ROUTE.EVENTS}/${API_ENDPOINT.CAN_ADD_EVENT}`,
   GET_PHOTOS_FOR_REGION = `${API_ROUTE.EVENTS}/${API_ENDPOINT.GET_PHOTOS_FOR_REGION}`,
-  ADD_EVENT = `${API_ROUTE.EVENTS}/${API_ENDPOINT.ADD_EVENT}`
+  ADD_EVENT = `${API_ROUTE.EVENTS}/${API_ENDPOINT.ADD_EVENT}`,
+  SET_LOCATION_REGIONS = `${API_ROUTE.LOCATION}/${API_ENDPOINT.SET_LOCATION_REGIONS}`
 }
 
 export type BaseAxiosError = AxiosError;