Ver código fonte

friends notification + small fixes

Viktoriia 6 meses atrás
pai
commit
c519734b06

+ 5 - 8
App.tsx

@@ -13,7 +13,6 @@ import { ErrorProvider, useError } from 'src/contexts/ErrorContext';
 import { useEffect, useState } from 'react';
 import { setupInterceptors } from 'src/utils/request';
 import { ErrorModal, WarningModal } from 'src/components';
-import { NotificationProvider } from 'src/contexts/NotificationContext';
 import React from 'react';
 import { Linking, Platform } from 'react-native';
 import { API_URL, APP_VERSION } from 'src/constants';
@@ -38,11 +37,9 @@ Sentry.init({
 const App = () => {
   return (
     <QueryClientProvider client={queryClient}>
-      <NotificationProvider>
-        <ErrorProvider>
-          <InnerApp />
-        </ErrorProvider>
-      </NotificationProvider>
+      <ErrorProvider>
+        <InnerApp />
+      </ErrorProvider>
     </QueryClientProvider>
   );
 };
@@ -70,10 +67,10 @@ const InnerApp = () => {
         const formatVersion = (versionString: string) => {
           return parseInt(versionString.replace(/\./g, ''), 10);
         };
-  
+
         const currentVersionInt = formatVersion(APP_VERSION);
         const latestVersionInt = formatVersion(version);
-  
+
         if (latestVersionInt > currentVersionInt) {
           setIsUpdateAvailable(true);
         }

+ 5 - 2
Route.tsx

@@ -81,7 +81,6 @@ import MyFriendsScreen from 'src/screens/InAppScreens/ProfileScreen/MyFriendsScr
 import CountryViewScreen from 'src/screens/InAppScreens/MapScreen/CountryViewScreen';
 import { userApi } from '@api/user';
 import axios from 'axios';
-import { useNotification } from 'src/contexts/NotificationContext';
 import PreviewScreen from 'src/screens/InAppScreens/ProfileScreen/ShareScreen';
 import { PushNotificationProvider } from 'src/contexts/PushNotificationContext';
 import NotificationsScreen from 'src/screens/NotificationsScreen';
@@ -94,6 +93,7 @@ import ChatScreen from 'src/screens/InAppScreens/MessagesScreen/ChatScreen';
 import { Splash } from 'src/components/SplashSpinner';
 import { useMessagesStore } from 'src/stores/unreadMessagesStore';
 import LocationSharingScreen from 'src/screens/LocationSharingScreen';
+import { useFriendsNotificationsStore } from 'src/stores/friendsNotificationsStore';
 
 enableScreens();
 
@@ -118,7 +118,9 @@ const Route = () => {
   });
   const [dbLoaded, setDbLoaded] = useState(false);
   const uid = storage.get('uid', StoreType.STRING);
-  const { updateNotificationStatus } = useNotification();
+  const updateNotificationStatus = useFriendsNotificationsStore(
+    (state) => state.updateNotificationStatus
+  );
   const updateUnreadMessagesCount = useMessagesStore((state) => state.updateUnreadMessagesCount);
 
   const checkNmToken = async () => {
@@ -168,6 +170,7 @@ const Route = () => {
       // await checkTokenAndUpdate();
       await openDatabases();
       setDbLoaded(true);
+      updateNotificationStatus();
       updateUnreadMessagesCount();
     };
 

+ 0 - 51
src/components/BlinkingDot/index.tsx

@@ -1,51 +0,0 @@
-import React, { useEffect } from 'react';
-import { DimensionValue } from 'react-native';
-import Animated, {
-  useSharedValue,
-  useAnimatedStyle,
-  withRepeat,
-  withTiming,
-  Easing
-} from 'react-native-reanimated';
-import { Colors } from 'src/theme';
-
-const BlinkingDot = ({
-  diameter = 8,
-  right = 0,
-  top = 0
-}: {
-  diameter?: number;
-  right?: DimensionValue;
-  top?: DimensionValue;
-}) => {
-  const opacity = useSharedValue(1);
-
-  useEffect(() => {
-    opacity.value = withRepeat(withTiming(0, { duration: 600, easing: Easing.linear }), -1, true);
-  }, []);
-
-  const animatedStyle = useAnimatedStyle(() => {
-    return {
-      opacity: opacity.value
-    };
-  });
-
-  return (
-    <Animated.View
-      style={[
-        {
-          width: diameter,
-          height: diameter,
-          borderRadius: diameter / 2,
-          backgroundColor: Colors.RED,
-          position: 'absolute',
-          right,
-          top
-        },
-        animatedStyle
-      ]}
-    />
-  );
-};
-
-export default BlinkingDot;

+ 4 - 3
src/components/ErrorModal/index.tsx

@@ -12,16 +12,17 @@ import { Button } from '../Button';
 import { CommonActions, useNavigation } from '@react-navigation/native';
 import { NAVIGATION_PAGES } from 'src/types';
 import { storage } from 'src/storage';
-import { useNotification } from 'src/contexts/NotificationContext';
 import { useMessagesStore } from 'src/stores/unreadMessagesStore';
+import { useFriendsNotificationsStore } from 'src/stores/friendsNotificationsStore';
 
 export const ErrorModal = () => {
   const { error, hideError, navigateToLogin } = useError();
   const navigation = useNavigation();
-  const { updateNotificationStatus } = useNotification();
+  const updateNotificationStatus = useFriendsNotificationsStore(
+    (state) => state.updateNotificationStatus
+  );
   const updateUnreadMessagesCount = useMessagesStore((state) => state.updateUnreadMessagesCount);
 
-
   const handleClose = () => {
     if (navigateToLogin) {
       storage.remove('token');

+ 6 - 4
src/components/HorizontalTabView/index.tsx

@@ -6,8 +6,8 @@ import { styles } from './styles';
 import { Colors } from 'src/theme';
 
 import MarkToUpIcon from '../../../assets/icons/mark-to-up.svg';
-import BlinkingDot from '../BlinkingDot';
 import BanIcon from 'assets/icons/messages/ban.svg';
+import MessagesDot from '../MessagesDot';
 
 export const HorizontalTabView = ({
   index,
@@ -17,7 +17,7 @@ export const HorizontalTabView = ({
   withMark,
   onDoubleClick,
   lazy = false,
-  withNotification = false,
+  withNotification = 0,
   maxTabHeight,
   tabBarStyle = {}
 }: {
@@ -28,7 +28,7 @@ export const HorizontalTabView = ({
   withMark?: boolean;
   onDoubleClick?: () => void;
   lazy?: boolean;
-  withNotification?: boolean;
+  withNotification?: number;
   maxTabHeight?: number;
   tabBarStyle?: StyleProp<ViewStyle>;
 }) => {
@@ -54,7 +54,9 @@ export const HorizontalTabView = ({
               />
             ) : null}
           </View>
-          {withNotification && route.key === 'received' ? <BlinkingDot diameter={10} /> : null}
+          {withNotification > 0 && route.key === 'received' ? (
+            <MessagesDot messagesCount={withNotification} />
+          ) : null}
         </>
       )}
       scrollEnabled={true}

+ 4 - 2
src/components/MenuDrawer/index.tsx

@@ -20,10 +20,10 @@ import SharingIcon from 'assets/icons/location-sharing.svg';
 import BagIcon from 'assets/icons/bag.svg';
 
 import { APP_VERSION } from 'src/constants';
-import { useNotification } from 'src/contexts/NotificationContext';
 import { useMessagesStore } from 'src/stores/unreadMessagesStore';
 import { SafeAreaView } from 'react-native-safe-area-context';
 import { usePostIsFeatureActiveQuery } from '@api/location';
+import { useFriendsNotificationsStore } from 'src/stores/friendsNotificationsStore';
 
 export const MenuDrawer = (props: any) => {
   const { mutate: deleteUser } = useDeleteUserMutation();
@@ -36,7 +36,9 @@ export const MenuDrawer = (props: any) => {
     message: '',
     action: () => {}
   });
-  const { updateNotificationStatus } = useNotification();
+  const updateNotificationStatus = useFriendsNotificationsStore(
+    (state) => state.updateNotificationStatus
+  );
   const updateUnreadMessagesCount = useMessagesStore((state) => state.updateUnreadMessagesCount);
 
   const openModal = (type: string, message: string, action: any) => {

+ 5 - 3
src/components/MessagesDot/index.tsx

@@ -34,9 +34,11 @@ const MessagesDot = ({
           borderColor: Colors.WHITE
         }}
       >
-        <Text style={{ fontFamily: 'montserrat-700', fontSize: 10, color: Colors.WHITE }}>
-          {messagesCount > 99 ? '99+' : messagesCount}
-        </Text>
+        {messagesCount > 0 && (
+          <Text style={{ fontFamily: 'montserrat-700', fontSize: 10, color: Colors.WHITE }}>
+            {messagesCount > 99 ? '99+' : messagesCount}
+          </Text>
+        )}
       </View>
     </View>
   );

+ 17 - 3
src/components/ScaleBar/index.tsx

@@ -39,15 +39,29 @@ const SCALE_STEPS_IN_FEET = [
 interface ScaleBarProps {
   zoom: number;
   latitude: number;
+  isVisible: boolean;
   bottom?: any;
 }
 
-const ScaleBar: React.FC<ScaleBarProps> = ({ zoom, latitude, bottom = '21%' }) => {
+const ScaleBar: React.FC<ScaleBarProps> = ({ zoom, latitude, isVisible, bottom = '21%' }) => {
   const metricWidth = useSharedValue(0);
   const imperialWidth = useSharedValue(0);
   const metricText = useSharedValue('');
   const imperialText = useSharedValue('');
   const screenWidth = Dimensions.get('window').width;
+  const opacity = useSharedValue(isVisible ? 1 : 0);
+
+  useEffect(() => {
+    if (isVisible) {
+      opacity.value = withTiming(1, { duration: 100 });
+    } else {
+      opacity.value = withTiming(0, { duration: 1500 });
+    }
+  }, [isVisible]);
+
+  const animatedStyle = useAnimatedStyle(() => ({
+    opacity: opacity.value
+  }));
 
   const getResolutionFromZoomAndLatitude = (zoom: number, latitude: number) =>
     (TILE_SIZE_METERS_AT_0_ZOOM * Math.cos((latitude * Math.PI) / 180)) / Math.pow(2, zoom);
@@ -103,7 +117,7 @@ const ScaleBar: React.FC<ScaleBarProps> = ({ zoom, latitude, bottom = '21%' }) =
   if (!metricText.value || !imperialText.value) return null;
 
   return (
-    <View style={[styles.container, { bottom }]}>
+    <Animated.View style={[styles.container, { bottom }, animatedStyle]}>
       <Text style={[styles.text, { bottom: 0 }]}>{metricText.value}</Text>
 
       <Animated.View style={[styles.scaleBar, animatedCombinedStyle]}>
@@ -112,7 +126,7 @@ const ScaleBar: React.FC<ScaleBarProps> = ({ zoom, latitude, bottom = '21%' }) =
       </Animated.View>
 
       <Text style={[styles.text, { top: 0 }]}>{imperialText.value}</Text>
-    </View>
+    </Animated.View>
   );
 };
 

+ 4 - 5
src/components/TabBarButton/index.tsx

@@ -11,13 +11,12 @@ import MessagesIcon from '../../../assets/icons/bottom-navigation/messages.svg';
 
 import { Colors } from '../../theme';
 import { styles } from './style';
-import BlinkingDot from '../BlinkingDot';
-import { useNotification } from 'src/contexts/NotificationContext';
 import MessagesDot from '../MessagesDot';
 import { storage, StoreType } from 'src/storage';
 import { WarningModal } from '../WarningModal';
 import { useState } from 'react';
 import { useMessagesStore } from 'src/stores/unreadMessagesStore';
+import { useFriendsNotificationsStore } from 'src/stores/friendsNotificationsStore';
 
 const getTabIcon = (routeName: string) => {
   switch (routeName) {
@@ -49,7 +48,7 @@ const TabBarButton = ({
 }) => {
   const IconComponent: React.FC<SvgProps> | null = getTabIcon(label);
   const token = storage.get('token', StoreType.STRING);
-  const { isNotificationActive } = useNotification();
+  const isNotificationActive = useFriendsNotificationsStore((state) => state.isNotificationActive);
   const unreadMessagesCount = useMessagesStore((state) => state.unreadMessagesCount);
   const [isWarningModalVisible, setIsWarningModalVisible] = useState(false);
 
@@ -70,8 +69,8 @@ const TabBarButton = ({
       <View style={styles.buttonStyle}>
         <View style={{ alignItems: 'center' }}>
           {IconComponent && <IconComponent width={24} height={24} fill={currentColor} />}
-          {label === NAVIGATION_PAGES.IN_APP_TRAVELLERS_TAB && isNotificationActive && (
-            <BlinkingDot diameter={8} />
+          {label === NAVIGATION_PAGES.IN_APP_TRAVELLERS_TAB && isNotificationActive > 0 && (
+            <MessagesDot messagesCount={isNotificationActive} />
           )}
           {label === NAVIGATION_PAGES.IN_APP_MESSAGES_TAB && unreadMessagesCount > 0 && (
             <MessagesDot messagesCount={unreadMessagesCount} />

+ 0 - 1
src/components/index.ts

@@ -18,7 +18,6 @@ export * from './HorizontalTabView';
 export * from './EditNmModal';
 export * from './MenuDrawer';
 export * from './ErrorModal';
-export * from './BlinkingDot';
 export * from './MessagesDot';
 export * from './MapButton';
 export * from './ScaleBar';

+ 0 - 55
src/contexts/NotificationContext.tsx

@@ -1,55 +0,0 @@
-import React, { createContext, useState, useContext, useEffect, useCallback } from 'react';
-import { fetchFriendsNotification } from '@api/friends';
-import { StoreType, storage } from 'src/storage';
-
-const NotificationContext = createContext({
-  isNotificationActive: false,
-  updateNotificationStatus: () => {}
-});
-
-export const NotificationProvider = ({ children }: { children: React.ReactNode }) => {
-  const [isNotificationActive, setIsNotificationActive] = useState(
-    storage.get('friendsNotification', StoreType.BOOLEAN) as boolean
-  );
-
-  const updateNotificationStatus = async () => {
-    const token = storage.get('token', StoreType.STRING);
-
-    if (token) {
-      try {
-        const data = await fetchFriendsNotification(token as string);
-        const isActive = data && data.active;
-
-        if (typeof isActive === 'boolean') {
-          setIsNotificationActive(isActive);
-          storage.set('friendsNotification', isActive);
-        }
-      } catch (error) {
-        console.error('Failed to fetch notifications', error);
-      }
-    } else {
-      setIsNotificationActive(false);
-      storage.set('friendsNotification', false);
-    }
-  };
-
-  useEffect(() => {
-    updateNotificationStatus();
-  }, []);
-
-  useEffect(() => {
-    const interval = setInterval(() => {
-      updateNotificationStatus();
-    }, 15000);
-
-    return () => clearInterval(interval);
-  }, [updateNotificationStatus]);
-
-  return (
-    <NotificationContext.Provider value={{ isNotificationActive, updateNotificationStatus }}>
-      {children}
-    </NotificationContext.Provider>
-  );
-};
-
-export const useNotification = () => useContext(NotificationContext);

+ 6 - 0
src/contexts/PushNotificationContext.tsx

@@ -6,6 +6,7 @@ import { CommonActions, useNavigation } from '@react-navigation/native';
 import { NAVIGATION_PAGES } from 'src/types';
 import { usePostSetSettingsMutation } from '@api/notifications';
 import { useMessagesStore } from 'src/stores/unreadMessagesStore';
+import { useFriendsNotificationsStore } from 'src/stores/friendsNotificationsStore';
 
 const PushNotificationContext = createContext<{
   isSubscribed: boolean;
@@ -26,6 +27,9 @@ export const PushNotificationProvider = ({ children }: { children: React.ReactNo
   );
   const { mutateAsync: setNotificationsSettings } = usePostSetSettingsMutation();
   const navigation = useNavigation();
+  const updateNotificationStatus = useFriendsNotificationsStore(
+    (state) => state.updateNotificationStatus
+  );
   const updateUnreadMessagesCount = useMessagesStore((state) => state.updateUnreadMessagesCount);
   const [appState, setAppState] = useState(AppState.currentState);
 
@@ -39,6 +43,7 @@ export const PushNotificationProvider = ({ children }: { children: React.ReactNo
             Notifications.setBadgeCountAsync(-1);
           }
         });
+        updateNotificationStatus();
         updateUnreadMessagesCount();
       }
       setAppState(nextAppState);
@@ -130,6 +135,7 @@ export const PushNotificationProvider = ({ children }: { children: React.ReactNo
       });
 
       const notificationListener = Notifications.addNotificationReceivedListener((notification) => {
+        updateNotificationStatus();
         updateUnreadMessagesCount();
       });
 

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

@@ -36,6 +36,7 @@ export interface PostGetFriendsSettingsDataReturn extends ResponseType {
 
 export interface PostGetFriendsNotificationReturn extends ResponseType {
   active: boolean;
+  count: number;
 }
 
 export const friendsApi = {

+ 9 - 2
src/screens/InAppScreens/MapScreen/index.tsx

@@ -394,6 +394,8 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
   const [selectedUser, setSelectedUser] = useState<any>(null);
   const [zoom, setZoom] = useState(0);
   const [center, setCenter] = useState<number[] | null>(null);
+  const [isZooming, setIsZooming] = useState(true);
+  const hideTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
 
   const isSmallScreen = Dimensions.get('window').width < 383;
   const processedImages = useRef(new Set<string>());
@@ -732,6 +734,9 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
 
   const handleMapChange = async () => {
     if (!mapRef.current) return;
+    if (hideTimer.current) clearTimeout(hideTimer.current);
+
+    setIsZooming(true);
 
     const currentZoom = await mapRef.current.getZoom();
     const currentCenter = await mapRef.current.getCenter();
@@ -854,7 +859,9 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
   };
 
   const handleRegionDidChange = async (feature: GeoJSON.Feature<GeoJSON.Point, any>) => {
-    handleMapChange();    
+    hideTimer.current = setTimeout(() => {
+      setIsZooming(false);
+    }, 2000);
     if (!feature) return;
     const { zoomLevel } = feature.properties;
     const { coordinates } = feature.geometry;
@@ -1474,7 +1481,7 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
       </MapLibreGL.MapView>
 
       {center ? (
-        <ScaleBar zoom={zoom} latitude={center[1]} bottom={tabBarHeight + 80} />
+        <ScaleBar zoom={zoom} latitude={center[1]} isVisible={isZooming} bottom={tabBarHeight + 80} />
       ) : null}
 
       {regionPopupVisible && regionData ? (

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

@@ -23,6 +23,7 @@ import { AvatarWithInitials, WarningModal } from 'src/components';
 import { usePostFriendRequestMutation, usePostUpdateFriendStatusMutation } from '@api/friends';
 import FriendStatus from './FriendStatus';
 import UpdatesRenderer from '../UpdatesRenderer';
+import { useFriendsNotificationsStore } from 'src/stores/friendsNotificationsStore';
 
 type PersonalInfoProps = {
   data: {
@@ -95,6 +96,10 @@ export const PersonalInfo: FC<PersonalInfoProps> = ({
   const { data: regions } = usePostGetProfileRegions(token as string, userId, type);
   const { data: update } = usePostGetUpdateQuery(token as string, userId, updateType, true);
 
+  const updateNotificationStatus = useFriendsNotificationsStore(
+    (state) => state.updateNotificationStatus
+  );
+
   useEffect(() => {
     if (data.isFriend === 1) {
       setFriendStatus(1);
@@ -188,6 +193,7 @@ export const PersonalInfo: FC<PersonalInfoProps> = ({
           }
         }
       );
+      updateNotificationStatus();
     },
     [updateFriendStatus, token, data.friendDbId]
   );

+ 5 - 2
src/screens/InAppScreens/ProfileScreen/MyFriendsScreen/index.tsx

@@ -15,7 +15,7 @@ import {
   usePostUpdateFriendStatusMutation
 } from '@api/friends';
 import { FriendsProfile } from './FriendsProfile';
-import { useNotification } from 'src/contexts/NotificationContext';
+import { useFriendsNotificationsStore } from 'src/stores/friendsNotificationsStore';
 
 type Props = {
   navigation: NavigationProp<any>;
@@ -29,7 +29,10 @@ type Routes = {
 
 const MyFriendsScreen: FC<Props> = ({ navigation }) => {
   const token = storage.get('token', StoreType.STRING) as string;
-  const { isNotificationActive, updateNotificationStatus } = useNotification();
+  const updateNotificationStatus = useFriendsNotificationsStore(
+    (state) => state.updateNotificationStatus
+  );
+  const isNotificationActive = useFriendsNotificationsStore((state) => state.isNotificationActive);
 
   const { mutateAsync: getMyFriends } = useGetMyFriendsMutation();
   const [loading, setLoading] = useState(true);

+ 8 - 4
src/screens/InAppScreens/ProfileScreen/Profile/edit-personal-info.tsx

@@ -43,9 +43,9 @@ import LinkIcon from '../../../../../assets/icons/link.svg';
 import { ButtonVariants } from 'src/types/components';
 import { NAVIGATION_PAGES } from 'src/types';
 import { useDeleteUserMutation } from '@api/app';
-import { useNotification } from 'src/contexts/NotificationContext';
 import { useMessagesStore } from 'src/stores/unreadMessagesStore';
 import { useAvatarStore } from 'src/stores/avatarVersionStore';
+import { useFriendsNotificationsStore } from 'src/stores/friendsNotificationsStore';
 
 const ProfileSchema = yup.object({
   username: yup.string().optional(),
@@ -75,10 +75,12 @@ export const EditPersonalInfo = () => {
   const queryClient = useQueryClient();
 
   const { data, error } = usePostGetProfileQuery(String(token), true);
-  const { updateNotificationStatus } = useNotification();
+  const updateNotificationStatus = useFriendsNotificationsStore(
+    (state) => state.updateNotificationStatus
+  );
   const updateUnreadMessagesCount = useMessagesStore((state) => state.updateUnreadMessagesCount);
   const [isSubmitting, setIsSubmitting] = useState(false);
-  const {incrementAvatarVersion} = useAvatarStore();
+  const { incrementAvatarVersion } = useAvatarStore();
 
   const regions = useGetRegionsWithFlagQuery(true);
   const [modalInfo, setModalInfo] = useState({
@@ -343,7 +345,9 @@ export const EditPersonalInfo = () => {
                   formikError={props.touched.other && props.errors.other}
                 />
                 <View style={{ marginTop: 15, marginBottom: 15, gap: 8 }}>
-                  <Button onPress={props.handleSubmit} disabled={isSubmitting}>Save</Button>
+                  <Button onPress={props.handleSubmit} disabled={isSubmitting}>
+                    Save
+                  </Button>
                   <Button
                     variant={ButtonVariants.OPACITY}
                     containerStyles={{ backgroundColor: Colors.WHITE, borderColor: Colors.RED }}

+ 11 - 2
src/screens/InAppScreens/ProfileScreen/UsersMap/index.tsx

@@ -179,6 +179,8 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
 
   const [zoom, setZoom] = useState(0);
   const [center, setCenter] = useState<number[] | null>(null);
+  const [isZooming, setIsZooming] = useState(true);
+  const hideTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
 
   const { data: visitedRegionIds } = usePostGetVisitedRegionsIdsQuery(
     token,
@@ -226,6 +228,9 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
 
   const handleMapChange = async () => {
     if (!mapRef.current) return;
+    if (hideTimer.current) clearTimeout(hideTimer.current);
+
+    setIsZooming(true);
 
     const currentZoom = await mapRef.current.getZoom();
     const currentCenter = await mapRef.current.getCenter();
@@ -342,7 +347,11 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
         styleJSON={VECTOR_MAP_HOST + '/nomadmania-maps.json'}
         rotateEnabled={false}
         attributionEnabled={false}
-        onRegionDidChange={handleMapChange}
+        onRegionDidChange={() => {
+          hideTimer.current = setTimeout(() => {
+            setIsZooming(false);
+          }, 2000);
+        }}
         onRegionIsChanging={handleMapChange}
         onRegionWillChange={_.debounce(handleMapChange, 200)}
       >
@@ -447,7 +456,7 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
       </MapLibreGL.MapView>
 
       {center ? (
-        <ScaleBar zoom={zoom} latitude={center[1]} bottom={80} />
+        <ScaleBar zoom={zoom} latitude={center[1]} isVisible={isZooming} bottom={80} />
       ) : null}
 
       {!isExpanded ? (

+ 5 - 5
src/screens/InAppScreens/TravellersScreen/index.tsx

@@ -16,16 +16,16 @@ import ClockIcon from '../../../../assets/icons/clock.svg';
 import TrophyIcon from '../../../../assets/icons/trophy.svg';
 import InfoIcon from 'assets/icons/info-solid.svg';
 import FriendsIcon from 'assets/icons/friends.svg';
-import BlinkingDot from 'src/components/BlinkingDot';
-import { useNotification } from 'src/contexts/NotificationContext';
 import { getFontSize } from 'src/utils';
 import { useSafeAreaInsets } from 'react-native-safe-area-context';
+import { useFriendsNotificationsStore } from 'src/stores/friendsNotificationsStore';
+import MessagesDot from 'src/components/MessagesDot';
 
 const TravellersScreen = () => {
   const navigation = useNavigation();
   const token = storage.get('token', StoreType.STRING);
   const insets = useSafeAreaInsets();
-  const { isNotificationActive } = useNotification();
+  const isNotificationActive = useFriendsNotificationsStore((state) => state.isNotificationActive);
 
   const buttons = [
     { label: 'Master Ranking', icon: UserGroupIcon, page: NAVIGATION_PAGES.MASTER_RANKING },
@@ -55,8 +55,8 @@ const TravellersScreen = () => {
         />
         <Text style={styles.label}>{item.label}</Text>
       </View>
-      {item.label === 'Friends' && isNotificationActive && (
-        <BlinkingDot diameter={10} right={'15%'} top={'15%'} />
+      {item.label === 'Friends' && isNotificationActive > 0 && (
+        <MessagesDot messagesCount={isNotificationActive} right={'15%'} top={'10%'} />
       )}
     </TouchableOpacity>
   );

+ 4 - 2
src/screens/LoginScreen/index.tsx

@@ -13,9 +13,9 @@ import { NAVIGATION_PAGES } from '../../types';
 import { useLoginMutation } from '@api/auth';
 import { fetchAndSaveStatistics } from 'src/database/statisticsService';
 import { useNetInfo } from '@react-native-community/netinfo';
-import { useNotification } from 'src/contexts/NotificationContext';
 import { usePostGetProfileInfoDataQuery } from '@api/user';
 import { useMessagesStore } from 'src/stores/unreadMessagesStore';
+import { useFriendsNotificationsStore } from 'src/stores/friendsNotificationsStore';
 
 type Props = {
   navigation: NavigationProp<any>;
@@ -31,7 +31,9 @@ const LoginScreen: FC<Props> = ({ navigation }) => {
   const isFirstLaunch = storage.get('isFirstLaunch', StoreType.BOOLEAN) ?? true;
 
   const { data, mutate: userLogin } = useLoginMutation();
-  const { updateNotificationStatus } = useNotification();
+  const updateNotificationStatus = useFriendsNotificationsStore(
+    (state) => state.updateNotificationStatus
+  );
   const updateUnreadMessagesCount = useMessagesStore((state) => state.updateUnreadMessagesCount);
 
   const { data: profileData } = usePostGetProfileInfoDataQuery(

+ 33 - 0
src/stores/friendsNotificationsStore.ts

@@ -0,0 +1,33 @@
+import { fetchFriendsNotification } from '@api/friends';
+import { storage, StoreType } from 'src/storage';
+import { create } from 'zustand';
+
+interface FriendsNotificationState {
+  isNotificationActive: number;
+  setIsNotificationActive: (active: number) => void;
+  updateNotificationStatus: () => Promise<void>;
+}
+
+export const useFriendsNotificationsStore = create<FriendsNotificationState>((set) => ({
+  isNotificationActive: (storage.get('friendsNotification', StoreType.NUMBER) as number) ?? 0,
+  setIsNotificationActive: (active: number) => set({ isNotificationActive: active }),
+  updateNotificationStatus: async () => {
+    const token = storage.get('token', StoreType.STRING);
+    if (token) {
+      try {
+        const data = await fetchFriendsNotification(token as string);
+        const count = data && data.count;
+
+        if (typeof count === 'number') {
+          set({ isNotificationActive: count });
+          storage.set('friendsNotification', count);
+        }
+      } catch (error) {
+        console.error('Failed to fetch friends notification status', error);
+      }
+    } else {
+      set({ isNotificationActive: 0 });
+      storage.set('friendsNotification', 0);
+    }
+  }
+}));