Selaa lähdekoodia

last seen feature, location on users map + small sentry fixes

Viktoriia 6 kuukautta sitten
vanhempi
commit
7b9d8497b6

+ 9 - 1
src/modules/api/user/user-api.tsx

@@ -266,6 +266,14 @@ export interface PostGetProfileDataReturn extends ResponseType {
       homeregion: string;
       badge_ghost: 0 | 1;
     };
+    location_sharing: boolean;
+    location_last_seen_region: string | null;
+    location_last_seen_flag: string | null;
+    location_last_seen_date: string;
+    location_last_seen_location: {
+      lat: number;
+      lng: number;
+    };
   };
 }
 
@@ -317,7 +325,7 @@ export interface UnOrUnp {
   visited_countries: {
     country: string;
     flag: string;
-    id: number
+    id: number;
   }[];
 }
 

+ 2 - 1
src/screens/InAppScreens/MapScreen/MarkerItem/index.tsx

@@ -6,6 +6,7 @@ import { Colors } from 'src/theme';
 
 import CheckSvg from 'assets/icons/mark.svg';
 import MapLibreGL, { PointAnnotationRef } from '@maplibre/maplibre-react-native';
+import { Position } from '@turf/turf';
 
 const MarkerItem = ({
   marker,
@@ -18,7 +19,7 @@ const MarkerItem = ({
 }) => {
   const calloutRef = useRef<PointAnnotationRef>(null);
   useEffect(() => {
-    if (Platform.OS === 'android') {
+    if (Platform.OS === 'android' && calloutRef.current) {
       calloutRef.current?.refresh();
     }
   }, [marker]);

+ 13 - 11
src/screens/InAppScreens/MapScreen/index.tsx

@@ -1086,17 +1086,19 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
 
       const { name, description, series_name, series_id, id } = selectedFeature.properties;
 
-      setSelectedMarker({
-        coordinates,
-        name,
-        icon,
-        description,
-        series_name,
-        visited,
-        series_id,
-        id
-      });
-      setSelectedUser(null);
+      if (coordinates) {
+        setSelectedMarker({
+          coordinates,
+          name,
+          icon,
+          description,
+          series_name,
+          visited,
+          series_id,
+          id
+        });
+        setSelectedUser(null);
+      }
     }
   };
 

+ 32 - 0
src/screens/InAppScreens/ProfileScreen/Components/PersonalInfo.tsx

@@ -24,6 +24,7 @@ import { usePostFriendRequestMutation, usePostUpdateFriendStatusMutation } from
 import FriendStatus from './FriendStatus';
 import UpdatesRenderer from '../UpdatesRenderer';
 import { useFriendsNotificationsStore } from 'src/stores/friendsNotificationsStore';
+import moment from 'moment';
 
 type PersonalInfoProps = {
   data: {
@@ -45,6 +46,10 @@ type PersonalInfoProps = {
     isFriend: 0 | 1;
     friendDbId: number;
     ownProfile: 0 | 1;
+    locationSharing: boolean;
+    lastSeenRegion: string | null;
+    lastSeenDate: string;
+    lastSeenFlag: string | null;
   };
   updates: {
     un_visited: number;
@@ -203,6 +208,21 @@ export const PersonalInfo: FC<PersonalInfoProps> = ({
     setIsUpdatesModalVisible(true);
   };
 
+  const formatDateToLocalTime = (utcDate: string) => {
+    const date = moment.utc(utcDate).local();
+    const now = moment();
+
+    if (now.diff(date, 'days') === 1) {
+      return 'yesterday';
+    }
+
+    if (now.diff(date, 'weeks') === 1) {
+      return 'last week';
+    }
+
+    return date.fromNow();
+  };
+
   const screenWidth = Dimensions.get('window').width;
   const availableWidth = screenWidth * (1 - 2 * SCREEN_PADDING_PERCENT);
   const maxAvatars = Math.floor(availableWidth / (AVATAR_SIZE - AVATAR_MARGIN)) - 2;
@@ -332,6 +352,18 @@ export const PersonalInfo: FC<PersonalInfoProps> = ({
           </InfoItem>
         ) : null}
 
+        {data.locationSharing && data.lastSeenRegion && data.lastSeenDate ? (
+          <InfoItem title={'LAST SEEN'}>
+            <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
+              <Image source={{ uri: API_HOST + data.lastSeenFlag }} style={styles.lastSeenFlag} />
+
+              <Text style={styles.titleText}>
+                {formatDateToLocalTime(data.lastSeenDate)} in {data.lastSeenRegion}
+              </Text>
+            </View>
+          </InfoItem>
+        ) : null}
+
         {updates && hasUpdates() ? (
           <InfoItem title={'VISITED IN THE LAST 90 DAYS'}>
             <View style={{ flexDirection: 'row', flexWrap: 'wrap' }}>

+ 8 - 1
src/screens/InAppScreens/ProfileScreen/Components/styles.tsx

@@ -108,5 +108,12 @@ export const styles = StyleSheet.create({
   },
   statusContainer: { flexDirection: 'row', gap: 8, alignItems: 'center' },
   statusTitle: { flex: 2, color: Colors.DARK_BLUE, fontWeight: '600' },
-  statusTitleSmall: { fontSize: 11, color: Colors.TEXT_GRAY, fontWeight: '500' }
+  statusTitleSmall: { fontSize: 11, color: Colors.TEXT_GRAY, fontWeight: '500' },
+  lastSeenFlag: {
+    width: 20,
+    height: 20,
+    borderRadius: 20 / 2,
+    borderWidth: 0.5,
+    borderColor: Colors.BORDER_LIGHT
+  }
 });

+ 41 - 0
src/screens/InAppScreens/ProfileScreen/UsersMap/index.tsx

@@ -47,6 +47,7 @@ import TravelsIcon from 'assets/icons/bottom-navigation/globe-solid.svg';
 import MapButton from 'src/components/MapButton';
 import ScaleBar from 'src/components/ScaleBar';
 import _ from 'lodash';
+const defaultUserAvatar = require('assets/icon-user-share-location-solid.png');
 
 MapLibreGL.setAccessToken(null);
 
@@ -337,6 +338,15 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
     );
   };
 
+  const locationFeature: GeoJSON.Feature<GeoJSON.Point> = {
+    type: 'Feature',
+    geometry: {
+      type: 'Point',
+      coordinates: [data.location_last_seen_location?.lng, data.location_last_seen_location?.lat]
+    },
+    properties: {}
+  };
+
   return (
     <SafeAreaView style={{ height: '100%' }}>
       <StatusBar translucent backgroundColor="transparent" />
@@ -368,6 +378,7 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
                 lineWidth: ['interpolate', ['linear'], ['zoom'], 0, 0.2, 4, 1, 5, 1.5, 12, 3],
                 lineWidthTransition: { duration: 300, delay: 0 }
               }}
+              belowLayerID="waterway-name"
             />
             <MapLibreGL.FillLayer
               id={regions.id}
@@ -402,6 +413,7 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
                 lineWidth: ['interpolate', ['linear'], ['zoom'], 0, 0.2, 4, 1, 5, 1.5, 12, 3],
                 lineWidthTransition: { duration: 300, delay: 0 }
               }}
+              belowLayerID="waterway-name"
             />
             <MapLibreGL.FillLayer
               id={countries.id}
@@ -446,6 +458,35 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
           </>
         )}
 
+        {data.location_sharing && data.location_last_seen_location && data.own_profile !== 1 && (
+          <MapLibreGL.ShapeSource id="user_location" shape={locationFeature}>
+            <MapLibreGL.SymbolLayer
+              id="user_symbol"
+              filter={['!', ['has', 'point_count']]}
+              aboveLayerID={Platform.OS === 'android' ? 'place-continent' : undefined}
+              style={{
+                iconImage: defaultUserAvatar,
+                iconSize: [
+                  'interpolate',
+                  ['linear'],
+                  ['zoom'],
+                  0,
+                  0.24,
+                  5,
+                  0.28,
+                  10,
+                  0.33,
+                  15,
+                  0.38,
+                  20,
+                  0.42
+                ],
+                iconAllowOverlap: true
+              }}
+            />
+          </MapLibreGL.ShapeSource>
+        )}
+
         <MapLibreGL.Camera ref={cameraRef} />
         {location && (
           <MapLibreGL.UserLocation

+ 13 - 1
src/screens/InAppScreens/ProfileScreen/index.tsx

@@ -38,6 +38,7 @@ import ChevronIcon from '../../../../assets/icons/chevron-left.svg';
 import ShareIcon from '../../../../assets/icons/share.svg';
 import UnverifiedIcon from '../../../../assets/icons/unverified.svg';
 import CommentsIcon from '../../../../assets/icons/messages/comments.svg';
+import MapSvg from 'assets/icons/travels-screens/map-location.svg';
 
 import { ProfileStyles, ScoreStyles, TBTStyles } from '../TravellersScreen/Components/styles';
 import UnauthenticatedProfileScreen from './UnauthenticatedProfileScreen';
@@ -247,6 +248,13 @@ const ProfileScreen: FC<Props> = ({ navigation, route }) => {
             }}
             style={styles.usersMap}
           />
+          {data.location_sharing && data.location_last_seen_location && data.own_profile !== 1 ? (
+            <View style={styles.locationWrapper}>
+              <View style={styles.locationBtn}>
+                <MapSvg fill={Colors.WHITE} />
+              </View>
+            </View>
+          ) : null}
         </TouchableOpacity>
 
         <View style={styles.pageWrapper}>
@@ -434,7 +442,11 @@ const ProfileScreen: FC<Props> = ({ navigation, route }) => {
             friendRequestReceived: data.friend_request_received,
             isFriend,
             friendDbId: data.friend_db_id,
-            ownProfile: data.own_profile
+            ownProfile: data.own_profile,
+            locationSharing: data.location_sharing,
+            lastSeenRegion: data.location_last_seen_region,
+            lastSeenDate: data.location_last_seen_date,
+            lastSeenFlag: data.location_last_seen_flag
           }}
           updates={lastUpdates?.data ? lastUpdates.data?.updates : null}
           userId={isPublicView ? route.params?.userId : +currentUserId}

+ 20 - 2
src/screens/InAppScreens/ProfileScreen/styles.ts

@@ -47,7 +47,7 @@ export const styles = StyleSheet.create({
     height: 20,
     borderRadius: 20 / 2,
     borderWidth: 0.5,
-    borderColor: 'gray'
+    borderColor: Colors.BORDER_LIGHT
   },
   settingsButton: {
     width: 40,
@@ -111,5 +111,23 @@ export const styles = StyleSheet.create({
     gap: 12,
     justifyContent: 'space-between'
   },
-  btnModalEditText: { fontSize: 12, fontWeight: '600', color: Colors.DARK_BLUE }
+  btnModalEditText: { fontSize: 12, fontWeight: '600', color: Colors.DARK_BLUE },
+  locationWrapper: {
+    position: 'absolute',
+    width: 50,
+    height: 50,
+    top: 0,
+    right: 0,
+    justifyContent: 'center',
+    alignItems: 'center',
+    zIndex: 2
+  },
+  locationBtn: {
+    width: 42,
+    height: 42,
+    borderRadius: 21,
+    justifyContent: 'center',
+    alignItems: 'center',
+    backgroundColor: 'rgba(0, 0, 0, 0.3)'
+  }
 });

+ 2 - 2
src/screens/InAppScreens/TravelsScreen/PhotosScreen/index.tsx

@@ -34,9 +34,9 @@ const ByRegionContent = ({
       setPhotos(data);
     } else {
       setPhotos(
-        data.filter(
+        data?.filter(
           (item) => item.country === selectedRegion?.country && item.name === selectedRegion?.name
-        )
+        ) ?? []
       );
     }
   }, [selectedRegion]);