Viktoriia 1 rok pred
rodič
commit
1a02ec1d36
43 zmenil súbory, kde vykonal 2219 pridanie a 251 odobranie
  1. 7 4
      App.tsx
  2. 45 2
      Route.tsx
  3. 10 0
      assets/icons/briefcase.svg
  4. 1 1
      assets/icons/chevron-left.svg
  5. 10 0
      assets/icons/house.svg
  6. BIN
      assets/images/logo-opacity.png
  7. 5 2
      package.json
  8. 2 1
      src/components/Header/index.tsx
  9. 18 3
      src/components/Input/index.tsx
  10. 7 0
      src/components/Input/style.ts
  11. 82 65
      src/components/RegionPopup/index.tsx
  12. 134 0
      src/contexts/RegionContext.tsx
  13. 5 0
      src/modules/api/regions/queries/index.ts
  14. 17 0
      src/modules/api/regions/queries/use-post-get-dare-region-data.tsx
  15. 17 0
      src/modules/api/regions/queries/use-post-get-nm-region-data.tsx
  16. 26 0
      src/modules/api/regions/queries/use-post-get-users-from-region.tsx
  17. 27 0
      src/modules/api/regions/queries/use-post-get-users-who-visited-dare.tsx
  18. 27 0
      src/modules/api/regions/queries/use-post-get-users-who-visited-region.tsx
  19. 145 1
      src/modules/api/regions/regions-api.tsx
  20. 6 1
      src/modules/api/regions/regions-query-keys.tsx
  21. 66 0
      src/screens/InAppScreens/MapScreen/RegionViewScreen/ImageCarousel/index.tsx
  22. 14 0
      src/screens/InAppScreens/MapScreen/RegionViewScreen/ImageCarousel/styles.tsx
  23. 105 0
      src/screens/InAppScreens/MapScreen/RegionViewScreen/TravelSeriesList/index.tsx
  24. 56 0
      src/screens/InAppScreens/MapScreen/RegionViewScreen/TravelSeriesList/styles.tsx
  25. 547 0
      src/screens/InAppScreens/MapScreen/RegionViewScreen/index.tsx
  26. 172 0
      src/screens/InAppScreens/MapScreen/RegionViewScreen/styles.tsx
  27. 42 0
      src/screens/InAppScreens/MapScreen/RegionViewScreen/types.ts
  28. 146 0
      src/screens/InAppScreens/MapScreen/UsersListScreen/Profile/index.tsx
  29. 271 0
      src/screens/InAppScreens/MapScreen/UsersListScreen/index.tsx
  30. 41 55
      src/screens/InAppScreens/MapScreen/index.tsx
  31. 0 11
      src/screens/InAppScreens/ProfileScreen/UsersMap/index.tsx
  32. 26 3
      src/screens/InAppScreens/ProfileScreen/index.tsx
  33. 2 2
      src/screens/InAppScreens/TravellersScreen/Components/Profile.tsx
  34. 3 3
      src/screens/InAppScreens/TravellersScreen/index.tsx
  35. 1 1
      src/screens/InAppScreens/TravellersScreen/utils/types.ts
  36. 11 11
      src/screens/InAppScreens/TravellersScreen/utils/values.ts
  37. 1 1
      src/screens/InAppScreens/TravelsScreen/Components/AccordionListItem.tsx
  38. 1 1
      src/screens/InAppScreens/TravelsScreen/Components/MyRegionsItems/NmRegionItem.tsx
  39. 52 35
      src/screens/InAppScreens/TravelsScreen/DareScreen/index.tsx
  40. 51 45
      src/screens/InAppScreens/TravelsScreen/RegionsScreen/index.tsx
  41. 3 1
      src/screens/InAppScreens/TravelsScreen/utils/formatNumber.ts
  42. 14 2
      src/types/api.ts
  43. 3 0
      src/types/navigation.ts

+ 7 - 4
App.tsx

@@ -6,15 +6,18 @@ import { queryClient } from 'src/utils/queryClient';
 import Route from './Route';
 import { ConnectionProvider } from 'src/contexts/ConnectionContext';
 import ConnectionBanner from 'src/components/ConnectionBanner/ConnectionBanner';
+import { RegionProvider } from 'src/contexts/RegionContext';
 
 export default function App() {
   return (
     <QueryClientProvider client={queryClient}>
       <ConnectionProvider>
-        <NavigationContainer>
-          <ConnectionBanner />
-          <Route />
-        </NavigationContainer>
+        <RegionProvider>
+          <NavigationContainer>
+            <ConnectionBanner />
+            <Route />
+          </NavigationContainer>
+        </RegionProvider>
       </ConnectionProvider>
     </QueryClientProvider>
   );

+ 45 - 2
Route.tsx

@@ -4,7 +4,7 @@ import * as SplashScreen from 'expo-splash-screen';
 import { Platform } from 'react-native';
 import * as Notifications from 'expo-notifications';
 
-import { createStackNavigator } from '@react-navigation/stack';
+import { createStackNavigator, TransitionPresets } from '@react-navigation/stack';
 import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
 import { createDrawerNavigator } from '@react-navigation/drawer';
 
@@ -70,6 +70,11 @@ import {
   DareInfoScreen,
   TripsInfoScreen
 } from 'src/screens/InfoScreens';
+import RegionViewScreen from 'src/screens/InAppScreens/MapScreen/RegionViewScreen';
+import { enableScreens } from 'react-native-screens';
+import UsersListScreen from 'src/screens/InAppScreens/MapScreen/UsersListScreen';
+
+enableScreens();
 
 const ScreenStack = createStackNavigator();
 const BottomTab = createBottomTabNavigator();
@@ -153,9 +158,19 @@ const Route = () => {
       })
     },
     cardStyle: { backgroundColor: 'white' },
-    unmountOnBlur: true
+    unmountOnBlur: true,
+    gestureEnabled: true,
+    lazy: true
   });
 
+  const regionViewScreenOptions = {
+    ...screenOptions,
+    ...TransitionPresets.ModalSlideFromBottomIOS,
+    contentStyle: {
+      flex: 1
+    }
+  };
+
   const MapDrawer = createDrawerNavigator();
 
   function MapDrawerNavigator() {
@@ -215,6 +230,16 @@ const Route = () => {
                     name={NAVIGATION_PAGES.USERS_MAP}
                     component={UsersMapScreen}
                   />
+                  <ScreenStack.Screen
+                    name={NAVIGATION_PAGES.REGION_PREVIEW}
+                    component={RegionViewScreen}
+                    options={regionViewScreenOptions}
+                  />
+                  <ScreenStack.Screen
+                    name={NAVIGATION_PAGES.USERS_LIST}
+                    component={UsersListScreen}
+                    options={regionViewScreenOptions}
+                  />
                 </ScreenStack.Navigator>
               )}
             </BottomTab.Screen>
@@ -307,6 +332,24 @@ const Route = () => {
                   />
                   <ScreenStack.Screen name={NAVIGATION_PAGES.REGIONS} component={RegionsScreen} />
                   <ScreenStack.Screen name={NAVIGATION_PAGES.DARE} component={DareScreen} />
+                  <ScreenStack.Screen
+                    name={NAVIGATION_PAGES.REGION_PREVIEW}
+                    component={RegionViewScreen}
+                    options={regionViewScreenOptions}
+                  />
+                  <ScreenStack.Screen
+                    name={NAVIGATION_PAGES.USERS_LIST}
+                    component={UsersListScreen}
+                    options={regionViewScreenOptions}
+                  />
+                  <ScreenStack.Screen
+                    name={NAVIGATION_PAGES.PUBLIC_PROFILE_VIEW}
+                    component={ProfileScreen}
+                  />
+                  <ScreenStack.Screen
+                    name={NAVIGATION_PAGES.USERS_MAP}
+                    component={UsersMapScreen}
+                  />
                 </ScreenStack.Navigator>
               )}
             </BottomTab.Screen>

+ 10 - 0
assets/icons/briefcase.svg

@@ -0,0 +1,10 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_3322_27873)">
+<path d="M5.03125 1.3125H8.96875C9.08906 1.3125 9.1875 1.41094 9.1875 1.53125V2.625H4.8125V1.53125C4.8125 1.41094 4.91094 1.3125 5.03125 1.3125ZM3.5 1.53125V2.625H1.75C0.784766 2.625 0 3.40977 0 4.375V7H5.25H8.75H14V4.375C14 3.40977 13.2152 2.625 12.25 2.625H10.5V1.53125C10.5 0.686328 9.81367 0 8.96875 0H5.03125C4.18633 0 3.5 0.686328 3.5 1.53125ZM14 7.875H8.75V8.75C8.75 9.23398 8.35898 9.625 7.875 9.625H6.125C5.64102 9.625 5.25 9.23398 5.25 8.75V7.875H0V11.375C0 12.3402 0.784766 13.125 1.75 13.125H12.25C13.2152 13.125 14 12.3402 14 11.375V7.875Z" fill="white"/>
+</g>
+<defs>
+<clipPath id="clip0_3322_27873">
+<rect width="14" height="14" fill="white"/>
+</clipPath>
+</defs>
+</svg>

+ 1 - 1
assets/icons/chevron-left.svg

@@ -1,3 +1,3 @@
 <svg width="8" height="16" viewBox="0 0 8 16" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path fill-rule="evenodd" clip-rule="evenodd" d="M6.76009 0.904058C6.29289 0.530298 5.61115 0.606046 5.23739 1.07325L0.237392 7.32325C-0.0791306 7.7189 -0.0791306 8.2811 0.237392 8.67675L5.23739 14.9268C5.61115 15.394 6.29289 15.4697 6.76009 15.0959C7.22729 14.7222 7.30303 14.0404 6.92927 13.5732L2.47068 8L6.92927 2.42675C7.30304 1.95955 7.22729 1.27782 6.76009 0.904058Z" fill="#0F3F4F"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M6.76009 0.904058C6.29289 0.530298 5.61115 0.606046 5.23739 1.07325L0.237392 7.32325C-0.0791306 7.7189 -0.0791306 8.2811 0.237392 8.67675L5.23739 14.9268C5.61115 15.394 6.29289 15.4697 6.76009 15.0959C7.22729 14.7222 7.30303 14.0404 6.92927 13.5732L2.47068 8L6.92927 2.42675C7.30304 1.95955 7.22729 1.27782 6.76009 0.904058Z"/>
 </svg>

+ 10 - 0
assets/icons/house.svg

@@ -0,0 +1,10 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_3322_27861)">
+<path d="M13.2174 7.76805C13.6306 7.76805 13.9951 7.42777 13.9951 6.98784C14.0194 6.76909 13.9222 6.57465 13.7278 6.40451L12.4444 5.2743V2.33333C12.4444 1.90312 12.0969 1.55555 11.6667 1.55555H10.8889C10.4587 1.55555 10.1111 1.90312 10.1111 2.33333V3.22534L7.52257 0.94791C7.37674 0.826382 7.18229 0.777771 7.01215 0.777771C6.84201 0.777771 6.64757 0.802077 6.47743 0.972215L0.243056 6.40451C0.0729167 6.57465 0 6.76909 0 6.98784C0 7.42534 0.340278 7.76805 0.777778 7.76805H1.55556V9.46214C1.55312 9.48402 1.55312 9.5059 1.55312 9.5302V12.25C1.55312 12.7871 1.98819 13.2222 2.52535 13.2222H2.91424C2.9434 13.2222 2.97257 13.2198 3.00174 13.2174C3.03819 13.2198 3.07465 13.2222 3.11111 13.2222H3.88889H4.47222C5.00937 13.2222 5.44444 12.7871 5.44444 12.25V11.6667V10.1111C5.44444 9.6809 5.79201 9.33333 6.22222 9.33333H7.77778C8.20799 9.33333 8.55556 9.6809 8.55556 10.1111V11.6667V12.25C8.55556 12.7871 8.99062 13.2222 9.52778 13.2222H10.1111H10.901C10.9351 13.2222 10.9691 13.2222 11.0031 13.2198C11.0299 13.2222 11.0566 13.2222 11.0833 13.2222H11.4722C12.0094 13.2222 12.4444 12.7871 12.4444 12.25V11.8562C12.4517 11.793 12.4566 11.7274 12.4566 11.6594L12.4396 7.76562H13.2174V7.76805Z" fill="white"/>
+</g>
+<defs>
+<clipPath id="clip0_3322_27861">
+<rect width="14" height="12.4444" fill="white" transform="translate(0 0.777771)"/>
+</clipPath>
+</defs>
+</svg>

BIN
assets/images/logo-opacity.png


+ 5 - 2
package.json

@@ -25,9 +25,11 @@
     "@tanstack/react-query": "latest",
     "@turf/turf": "^6.5.0",
     "axios": "^1.6.1",
+    "better-react-native-image-viewing": "^0.2.7",
     "dotenv": "^16.3.1",
     "expo": "~49.0.15",
     "expo-asset": "8.10.1",
+    "expo-build-properties": "~0.8.3",
     "expo-checkbox": "~2.4.0",
     "expo-constants": "14.4.2",
     "expo-dev-client": "~2.4.12",
@@ -45,6 +47,7 @@
     "patch-package": "^8.0.0",
     "react": "18.2.0",
     "react-native": "0.72.10",
+    "react-native-animated-pagination-dot": "^0.4.0",
     "react-native-calendars": "^1.1304.1",
     "react-native-device-detection": "^0.2.1",
     "react-native-gesture-handler": "~2.12.0",
@@ -57,6 +60,7 @@
     "react-native-pager-view": "6.2.0",
     "react-native-progress": "^5.0.1",
     "react-native-reanimated": "~3.3.0",
+    "react-native-reanimated-carousel": "^3.5.1",
     "react-native-safe-area-context": "4.6.3",
     "react-native-screens": "~3.22.0",
     "react-native-searchable-dropdown-kj": "^1.9.1",
@@ -64,8 +68,7 @@
     "react-native-tab-view": "^3.5.2",
     "react-native-walkthrough-tooltip": "^1.6.0",
     "yup": "^1.3.3",
-    "zustand": "^4.4.7",
-    "expo-build-properties": "~0.8.3"
+    "zustand": "^4.4.7"
   },
   "devDependencies": {
     "@babel/core": "^7.20.0",

+ 2 - 1
src/components/Header/index.tsx

@@ -7,6 +7,7 @@ import { styles } from './style';
 import ChevronLeft from '../../../assets/icons/chevron-left.svg';
 import CloseSvg from '../../../assets/icons/close.svg';
 import { NAVIGATION_PAGES } from 'src/types';
+import { Colors } from 'src/theme';
 
 type Props = {
   label: string;
@@ -35,7 +36,7 @@ export const Header: FC<Props> = ({ label, rightElement, shouldClose }) => {
   return (
     <View style={styles.wrapper}>
       <TouchableOpacity onPress={handlePress}>
-        <View style={styles.chevronWrapper}>{shouldClose ? <CloseSvg /> : <ChevronLeft />}</View>
+        <View style={styles.chevronWrapper}>{shouldClose ? <CloseSvg /> : <ChevronLeft fill={Colors.DARK_BLUE} />}</View>
       </TouchableOpacity>
       <Text style={styles.label}>{label}</Text>
       {rightElement ? <View>{rightElement}</View> : <View style={styles.placeholder} />}

+ 18 - 3
src/components/Input/index.tsx

@@ -5,7 +5,8 @@ import {
   View,
   InputModeOptions,
   NativeSyntheticEvent,
-  TextInputFocusEventData
+  TextInputFocusEventData,
+  TouchableOpacity
 } from 'react-native';
 import { styling } from './style';
 import { Colors } from 'src/theme';
@@ -26,6 +27,8 @@ type Props = {
   editable?: boolean;
   height?: number;
   backgroundColor?: string;
+  clearIcon?: ReactNode;
+  setValue?: (value: string) => void;
 };
 
 export const Input: FC<Props> = ({
@@ -42,7 +45,9 @@ export const Input: FC<Props> = ({
   multiline,
   editable,
   height,
-  backgroundColor = Colors.FILL_LIGHT
+  backgroundColor = Colors.FILL_LIGHT,
+  clearIcon,
+  setValue
 }) => {
   const [focused, setFocused] = useState(false);
 
@@ -91,8 +96,18 @@ export const Input: FC<Props> = ({
               onBlur(e);
             }
           }}
-          style={[{ height: '100%', width: '100%' }, !icon ? { padding: 10 } : null]}
+          style={[{ height: '100%', width: '100%', flex: 1 }, !icon ? { padding: 10 } : null]}
         />
+        {clearIcon && value && value?.length > 0 ? (
+          <TouchableOpacity
+            style={styles.clearIcon}
+            onPress={() => {
+              setValue && setValue('');
+            }}
+          >
+            {clearIcon}
+          </TouchableOpacity>
+        ) : null}
       </View>
       {formikError ? <Text style={styles.errorText}>{formikError}</Text> : null}
     </View>

+ 7 - 0
src/components/Input/style.ts

@@ -30,5 +30,12 @@ export const styling = (focused: boolean) =>
       fontSize: getFontSize(12),
       fontFamily: 'redhat-600',
       marginTop: 5
+    },
+    clearIcon: {
+      height: 44,
+      width: 44,
+      display: 'flex',
+      justifyContent: 'center',
+      alignItems: 'center'
     }
   });

+ 82 - 65
src/components/RegionPopup/index.tsx

@@ -61,8 +61,10 @@ const RegionPopup: React.FC<RegionPopupProps> = ({
   const regionImg = JSON.parse(region.region_photos)[0];
 
   function formatNumber(number: number) {
-    if (number >= 1000) {
+    if (number >= 1000 && number < 10000) {
       return (number / 1000).toFixed(1) + 'k';
+    } else if (number >= 10000) {
+      return (number / 1000).toFixed(0) + 'k';
     }
     return number.toString();
   }
@@ -71,78 +73,93 @@ const RegionPopup: React.FC<RegionPopupProps> = ({
 
   return (
     <Animated.View style={[styles.popupContainer, { opacity: fadeAnim }]}>
-      <View style={styles.regionInfoContainer}>
-        {regionImg && <Image source={{ uri: regionImg }} style={styles.regionImage} />}
-        <View style={styles.regionTextContainer}>
-          <Text style={styles.regionTitle}>{regionTitle}</Text>
-          <Text style={styles.regionSubtitle}>{regionSubtitle}</Text>
+      <TouchableOpacity
+        onPress={() =>
+          navigation.navigate(
+            ...([
+              NAVIGATION_PAGES.REGION_PREVIEW,
+              {
+                regionId: region.id,
+                type: userData?.type,
+                disabled
+              }
+            ] as never)
+          )
+        }
+      >
+        <View style={styles.regionInfoContainer}>
+          {regionImg && <Image source={{ uri: regionImg }} style={styles.regionImage} />}
+          <View style={styles.regionTextContainer}>
+            <Text style={styles.regionTitle}>{regionTitle}</Text>
+            <Text style={styles.regionSubtitle}>{regionSubtitle}</Text>
+          </View>
         </View>
-      </View>
 
-      <View style={styles.separator} />
+        <View style={styles.separator} />
 
-      <View style={styles.bottomContainer}>
-        <View style={styles.userContainer}>
-          {userData?.visited && userData?.first_visit_year > 1 && !disabled && (
-            <View style={styles.infoContent}>
-              <CalendarSvg height={18} width={18} fill={Colors.DARK_BLUE} />
-              <Text style={styles.visitedButtonText}>{userData?.first_visit_year}</Text>
-            </View>
-          )}
+        <View style={styles.bottomContainer}>
+          <View style={styles.userContainer}>
+            {userData?.visited && userData?.first_visit_year > 1 && !disabled && (
+              <View style={styles.infoContent}>
+                <CalendarSvg height={18} width={18} fill={Colors.DARK_BLUE} />
+                <Text style={styles.visitedButtonText}>{userData?.first_visit_year}</Text>
+              </View>
+            )}
 
-          {userData?.visited && userData?.type === 'nm' && !disabled && (
-            <View style={styles.infoContent}>
-              <RotateSvg fill={Colors.DARK_BLUE} />
-              <Text style={styles.visitedButtonText}>
-                {userData.visits >= 10 ? '10+' : userData.no_of_visits}
-              </Text>
-            </View>
-          )}
-          {(!userData?.visited || userData?.type === 'dare' || disabled) && (
-            <View style={styles.userImageContainer}>
-              {userAvatars?.map((avatar, index) => (
-                <Image key={index} source={{ uri: avatar }} style={styles.userImage} />
-              ))}
-              <View style={styles.userCountContainer}>
-                <Text style={styles.userCount}>{formattedCount}</Text>
+            {userData?.visited && userData?.type === 'nm' && !disabled && (
+              <View style={styles.infoContent}>
+                <RotateSvg fill={Colors.DARK_BLUE} />
+                <Text style={styles.visitedButtonText}>
+                  {userData.no_of_visits >= 10 ? '10+' : userData.no_of_visits}
+                </Text>
               </View>
-            </View>
-          )}
-        </View>
-        <View style={styles.btnContainer}>
-          {userData?.visited && userData?.type === 'nm' && !disabled ? (
-            <TouchableOpacity onPress={openEditModal} style={styles.editBtn}>
-              <EditSvg width={14} height={14} />
-            </TouchableOpacity>
-          ) : null}
-          <TouchableOpacity
-            style={[
-              styles.btn,
-              userData?.visited && !disabled ? styles.visitedButton : styles.markVisitedButton
-            ]}
-            onPress={() =>
-              userData?.type === 'nm'
-                ? updateNM(
-                    region.id,
-                    userData.visited ? 0 : 1,
-                    userData.visited ? 0 : 1,
-                    userData.visited ? 0 : 1,
-                    3
-                  )
-                : updateDare(region.id, userData.visited ? 0 : 1)
-            }
-          >
-            {userData?.visited && !disabled ? (
-              <View style={styles.visitedContainer}>
-                <MarkIcon width={16} height={16} />
-                <Text style={styles.visitedButtonText}>Visited</Text>
+            )}
+            {(!userData?.visited || userData?.type === 'dare' || disabled) && (
+              <View style={styles.userImageContainer}>
+                {userAvatars?.map((avatar, index) => (
+                  <Image key={index} source={{ uri: avatar }} style={styles.userImage} />
+                ))}
+                <View style={styles.userCountContainer}>
+                  <Text style={styles.userCount}>{formattedCount}</Text>
+                </View>
               </View>
-            ) : (
-              <Text style={[styles.markVisitedButtonText]}>Mark Visited</Text>
             )}
-          </TouchableOpacity>
+          </View>
+          <View style={styles.btnContainer}>
+            {userData?.visited && userData?.type === 'nm' && !disabled ? (
+              <TouchableOpacity onPress={openEditModal} style={styles.editBtn}>
+                <EditSvg width={14} height={14} />
+              </TouchableOpacity>
+            ) : null}
+            <TouchableOpacity
+              style={[
+                styles.btn,
+                userData?.visited && !disabled ? styles.visitedButton : styles.markVisitedButton
+              ]}
+              onPress={() =>
+                userData?.type === 'nm'
+                  ? updateNM(
+                      region.id,
+                      userData.visited ? 0 : 1,
+                      userData.visited ? 0 : 1,
+                      userData.visited ? 0 : 1,
+                      3
+                    )
+                  : updateDare(region.id, userData.visited ? 0 : 1)
+              }
+            >
+              {userData?.visited && !disabled ? (
+                <View style={styles.visitedContainer}>
+                  <MarkIcon width={16} height={16} />
+                  <Text style={styles.visitedButtonText}>Visited</Text>
+                </View>
+              ) : (
+                <Text style={[styles.markVisitedButtonText]}>Mark Visited</Text>
+              )}
+            </TouchableOpacity>
+          </View>
         </View>
-      </View>
+      </TouchableOpacity>
     </Animated.View>
   );
 };

+ 134 - 0
src/contexts/RegionContext.tsx

@@ -0,0 +1,134 @@
+import { usePostSetDareRegionMutation } from '@api/myDARE';
+import { usePostSetNmRegionMutation } from '@api/myRegions';
+import React, { createContext, useContext, useState, useCallback } from 'react';
+import { DareRegion, NmRegion } from 'src/screens/InAppScreens/TravelsScreen/utils/types';
+import { StoreType, storage } from 'src/storage';
+
+const RegionContext = createContext<any>(null);
+
+export const useRegion = () => useContext(RegionContext);
+
+export const RegionProvider = ({ children }: { children: React.ReactNode }) => {
+  const [userData, setUserData] = useState({});
+  const [nmRegions, setNmRegions] = useState<NmRegion[]>([]);
+  const [dareRegions, setDareRegions] = useState<DareRegion[] | null>([]);
+  const token = storage.get('token', StoreType.STRING) as string;
+  const { mutate: updateNM } = usePostSetNmRegionMutation();
+  const { mutate: updateDARE } = usePostSetDareRegionMutation();
+
+  const handleUpdateNM = useCallback(
+    (region: number, first: number, last: number, visits: number, quality: number) => {
+      const updatedNM = {
+        ...userData,
+        first_visit_year: first,
+        last_visit_year: last,
+        best_visit_quality: quality,
+        no_of_visits: visits,
+        visited: visits > 0 ? true : false
+      };
+
+      const updatedNMData = {
+        token,
+        region,
+        first,
+        last,
+        visits,
+        quality
+      };
+
+      updateNM(updatedNMData);
+      updatedNM && setUserData(updatedNM);
+    },
+    [userData, token]
+  );
+
+  const handleUpdateDare = useCallback(
+    (region: number, visits: 0 | 1) => {
+      const updatedDARE = { ...userData, visited: visits > 0 ? true : false };
+
+      const updatedDareData = {
+        token,
+        region,
+        visits
+      };
+
+      updateDARE(updatedDareData);
+      updatedDARE && setUserData(updatedDARE);
+    },
+    [userData, token]
+  );
+
+  const handleUpdateNMList = useCallback(
+    (id: number, first: number, last: number, visits: number, quality: number) => {
+      const updatedNM = nmRegions.map((item) => {
+        if (item.id === id) {
+          return {
+            ...item,
+            year: first,
+            last,
+            quality,
+            visits
+          };
+        }
+        return item;
+      });
+
+      const updatedNMData = {
+        token,
+        region: id,
+        first,
+        last,
+        visits,
+        quality
+      };
+
+      updateNM(updatedNMData);
+      updatedNM && setNmRegions(updatedNM);
+    },
+    [nmRegions]
+  );
+
+  const handleUpdateDareList = useCallback(
+    (region: number, visits: 0 | 1) => {
+      const updatedDARE = dareRegions?.map((item) => {
+        if (item.id === region) {
+          return {
+            ...item,
+            visited: String(visits)
+          };
+        }
+
+        return item;
+      });
+
+      const updatedDareData = {
+        token,
+        region,
+        visits
+      };
+
+      updateDARE(updatedDareData);
+      updatedDARE && setDareRegions(updatedDARE);
+    },
+    [dareRegions]
+  );
+
+  return (
+    <RegionContext.Provider
+      value={{
+        userData,
+        setUserData,
+        setNmRegions,
+        nmRegions,
+        setDareRegions,
+        dareRegions,
+        handleUpdateNM,
+        handleUpdateDare,
+        handleUpdateNMList,
+        handleUpdateDareList
+      }}
+    >
+      {children}
+    </RegionContext.Provider>
+  );
+};

+ 5 - 0
src/modules/api/regions/queries/index.ts

@@ -1,3 +1,8 @@
 export * from './use-post-get-regions';
 export * from './use-post-get-user-data';
 export * from './use-post-get-user-data-dare';
+export * from './use-post-get-nm-region-data';
+export * from './use-post-get-dare-region-data';
+export * from './use-post-get-users-from-region';
+export * from './use-post-get-users-who-visited-region';
+export * from './use-post-get-users-who-visited-dare';

+ 17 - 0
src/modules/api/regions/queries/use-post-get-dare-region-data.tsx

@@ -0,0 +1,17 @@
+import { useQuery } from '@tanstack/react-query';
+
+import { regionQueryKeys } from '../regions-query-keys';
+import { regionsApi, type PostGetRegionDataReturn } from '../regions-api';
+
+import type { BaseAxiosError } from '../../../../types';
+
+export const useGetDareRegionDataQuery = (id: number, enabled: boolean, token?: string) => {
+  return useQuery<PostGetRegionDataReturn, BaseAxiosError>({
+    queryKey: regionQueryKeys.getDareRegionData(id, token),
+    queryFn: async () => {
+      const response = await regionsApi.getDareRegionData(id, token);
+      return response.data;
+    },
+    enabled
+  });
+};

+ 17 - 0
src/modules/api/regions/queries/use-post-get-nm-region-data.tsx

@@ -0,0 +1,17 @@
+import { useQuery } from '@tanstack/react-query';
+
+import { regionQueryKeys } from '../regions-query-keys';
+import { regionsApi, type PostGetRegionDataReturn } from '../regions-api';
+
+import type { BaseAxiosError } from '../../../../types';
+
+export const useGetNmRegionDataQuery = (id: number, enabled: boolean, token?: string) => {
+  return useQuery<PostGetRegionDataReturn, BaseAxiosError>({
+    queryKey: regionQueryKeys.getNmRegionData(id, token),
+    queryFn: async () => {
+      const response = await regionsApi.getNmRegionData(id, token);
+      return response.data;
+    },
+    enabled
+  });
+};

+ 26 - 0
src/modules/api/regions/queries/use-post-get-users-from-region.tsx

@@ -0,0 +1,26 @@
+import { regionQueryKeys } from '../regions-query-keys';
+import { regionsApi, type PostGetUsersFromRegionDataReturn } from '../regions-api';
+
+import type { BaseAxiosError } from '../../../../types';
+
+import { useMutation } from '@tanstack/react-query';
+
+export const useGetUsersFromRegionMutation = () => {
+  return useMutation<
+    PostGetUsersFromRegionDataReturn,
+    BaseAxiosError,
+    { id: number; page: number; sort?: string; age?: number },
+    PostGetUsersFromRegionDataReturn
+  >({
+    mutationKey: regionQueryKeys.getUsersFromRegion(),
+    mutationFn: async (variables) => {
+      const response = await regionsApi.getUsersFromRegion(
+        variables.id,
+        variables.page,
+        variables.sort,
+        variables.age
+      );
+      return response.data;
+    }
+  });
+};

+ 27 - 0
src/modules/api/regions/queries/use-post-get-users-who-visited-dare.tsx

@@ -0,0 +1,27 @@
+import { regionQueryKeys } from '../regions-query-keys';
+import { regionsApi, type PostGetUsersWhoVisitedDataReturn } from '../regions-api';
+
+import type { BaseAxiosError } from '../../../../types';
+
+import { useMutation } from '@tanstack/react-query';
+
+export const useGetUsersWhoVisitedDareMutation = () => {
+  return useMutation<
+    PostGetUsersWhoVisitedDataReturn,
+    BaseAxiosError,
+    { id: number; page: number; sort?: string; age?: number; country?: string },
+    PostGetUsersWhoVisitedDataReturn
+  >({
+    mutationKey: regionQueryKeys.getUsersWhoVisitedDare(),
+    mutationFn: async (variables) => {
+      const response = await regionsApi.getUsersWhoVisitedDare(
+        variables.id,
+        variables.page,
+        variables.sort,
+        variables.age,
+        variables.country
+      );
+      return response.data;
+    }
+  });
+};

+ 27 - 0
src/modules/api/regions/queries/use-post-get-users-who-visited-region.tsx

@@ -0,0 +1,27 @@
+import { regionQueryKeys } from '../regions-query-keys';
+import { regionsApi, type PostGetUsersWhoVisitedDataReturn } from '../regions-api';
+
+import type { BaseAxiosError } from '../../../../types';
+
+import { useMutation } from '@tanstack/react-query';
+
+export const useGetUsersWhoVisitetRegionMutation = () => {
+  return useMutation<
+    PostGetUsersWhoVisitedDataReturn,
+    BaseAxiosError,
+    { id: number; page: number; sort?: string; age?: number; country?: string },
+    PostGetUsersWhoVisitedDataReturn
+  >({
+    mutationKey: regionQueryKeys.getUsersWhoVisitedRegion(),
+    mutationFn: async (variables) => {
+      const response = await regionsApi.getUsersWhoVisitedRegion(
+        variables.id,
+        variables.page,
+        variables.sort,
+        variables.age,
+        variables.country
+      );
+      return response.data;
+    }
+  });
+};

+ 145 - 1
src/modules/api/regions/regions-api.tsx

@@ -18,10 +18,154 @@ export interface PostGetUserDataDareReturn extends ResponseType {
   first_visit_year: boolean;
 }
 
+export interface PostGetRegionDataReturn extends ResponseType {
+  data: {
+    region_name: string;
+    region_flag: string;
+    visited: boolean;
+    best_visit_quality: -1;
+    first_visit_year: number;
+    last_visit_year: number;
+    no_of_visits: number;
+    photos: {
+      title: string;
+      id: number;
+      user_id: number;
+      first_name: string;
+      last_name: string;
+      avatar: string | null;
+    }[];
+    users_who_visited_region_count: number;
+    users_from_region_count: number;
+    users_who_visited_region: string[];
+    users_from_region: string[];
+    series: {
+      series_id: number;
+      series_name: string;
+      icon: string;
+      items: {
+        id: number;
+        name: string;
+        description: string | null;
+        new: 0 | 1;
+        visited: 0 | 1;
+        double_point: 0 | 1;
+        visited_double: 0 | 1;
+        link: string;
+      }[];
+    }[];
+  };
+}
+
+interface User {
+  score_nm: number;
+  score_dare: number;
+  score_un: number;
+  score_unp: number;
+  score_tcc: number;
+  score_deep: number;
+  score_whs: number | null;
+  score_kye: number;
+  score_tbt: number;
+  score_yes: number;
+  score_slow: number;
+  rank_tbt: number;
+  user_id: number;
+  avatar: string | null;
+  first_name: string;
+  last_name: string;
+  age: number;
+  flag1: string;
+  flag2: string | null;
+  badge_1281: 0 | 1;
+  badge_un: 0 | 1;
+  badge_supreme: 0 | 1;
+  badge_tbt: 0 | 1;
+  badge_offline: 0 | 1;
+  patreon: 0 | 1;
+  country: string;
+  homebase: number;
+  auth: 0 | 1;
+  rank: number;
+  country_rank: number;
+  dod: 0 | 1;
+  ukr: 0 | 1;
+  badges: 0 | 1;
+  arrow_nm: 0 | 1;
+  arrow_un: 0 | 1;
+  arrow_unp: 0 | 1;
+  arrow_dare: 0 | 1;
+  arrow_yes: 0 | 1;
+  arrow_whs: 0 | 1;
+  arrow_tcc: 0 | 1;
+  arrow_tbt: 0 | 1;
+  arrow_slow: 0 | 1;
+  arrow_kye: 0 | 1;
+}
+
+export interface PostGetUsersFromRegionDataReturn extends ResponseType {
+  data: {
+    max_pages: number;
+    users: User[];
+  };
+}
+
+export interface PostGetUsersWhoVisitedDataReturn extends ResponseType {
+  data: {
+    max_pages: number;
+    countries: {
+      [key: string]: {
+        country: string;
+        flag: string;
+      };
+    };
+    users: User[];
+  };
+}
+
 export const regionsApi = {
   getRegionsWithFlag: () => request.postForm<PostGetRegionsReturn>(API.GET_REGIONS_WITH_FLAGS),
   getUserData: (region_id: number, token: string) =>
     request.postForm<PostGetUserDataReturn>(API.GET_USER_DATA, { region_id, token }),
   getUserDataDare: (dare_id: number, token: string) =>
-    request.postForm<PostGetUserDataDareReturn>(API.GET_USER_DATA_DARE, { dare_id, token })
+    request.postForm<PostGetUserDataDareReturn>(API.GET_USER_DATA_DARE, { dare_id, token }),
+  getNmRegionData: (id: number, token?: string) =>
+    request.postForm<PostGetRegionDataReturn>(API.GET_NM_REGION_DATA, { id, token }),
+  getDareRegionData: (id: number, token?: string) =>
+    request.postForm<PostGetRegionDataReturn>(API.GET_DARE_REGION_DATA, { id, token }),
+  getUsersFromRegion: (id: number, page: number, sort?: string, age?: number) =>
+    request.postForm<PostGetUsersFromRegionDataReturn>(API.GET_USERS_FROM_REGION, {
+      id,
+      page,
+      sort,
+      age
+    }),
+  getUsersWhoVisitedRegion: (
+    id: number,
+    page: number,
+    sort?: string,
+    age?: number,
+    country?: string
+  ) =>
+    request.postForm<PostGetUsersWhoVisitedDataReturn>(API.GET_USERS_WHO_VISITED_REGION, {
+      id,
+      page,
+      sort,
+      age,
+      country
+    }),
+  getUsersWhoVisitedDare: (
+    id: number,
+    page: number,
+    sort?: string,
+    age?: number,
+    country?: string
+  ) =>
+    request.postForm<PostGetUsersWhoVisitedDataReturn>(API.GET_USERS_WHO_VISITED_DARE, {
+      id,
+      page,
+      sort,
+      age,
+      country
+    })
 };

+ 6 - 1
src/modules/api/regions/regions-query-keys.tsx

@@ -1,5 +1,10 @@
 export const regionQueryKeys = {
   getRegions: () => ['getRegions'] as const,
   getUserData: () => ['getUserData'] as const,
-  getUserDataDare: () => ['getUserDataDare'] as const
+  getUserDataDare: () => ['getUserDataDare'] as const,
+  getNmRegionData: (id: number, token?: string) => ['getNmRegionData', id, token] as const,
+  getDareRegionData: (id: number, token?: string) => ['getNmRegionData', id, token] as const,
+  getUsersFromRegion: () => ['getUsersFromRegion'] as const,
+  getUsersWhoVisitedRegion: () => ['getUsersWhoVisitedRegion'] as const,
+  getUsersWhoVisitedDare: () => ['getUsersWhoVisitedDare'] as const
 };

+ 66 - 0
src/screens/InAppScreens/MapScreen/RegionViewScreen/ImageCarousel/index.tsx

@@ -0,0 +1,66 @@
+import React from 'react';
+import { Dimensions, TouchableOpacity, Image, View } from 'react-native';
+import Carousel from 'react-native-reanimated-carousel';
+import PaginationDot from 'react-native-animated-pagination-dot';
+import { Colors } from 'src/theme';
+import { styles } from './styles';
+import { interpolate } from 'react-native-reanimated';
+import { PhotosData } from '../types';
+
+const { width } = Dimensions.get('window');
+
+const ImageCarousel = ({
+  photos,
+  activeIndex,
+  setActiveIndex,
+  openModal
+}: {
+  photos: PhotosData[];
+  activeIndex: number;
+  setActiveIndex: (index: number) => void;
+  openModal: (index: number) => void;
+}) => {
+  const animationStyle = React.useCallback((value: number) => {
+    'worklet';
+
+    const zIndex = interpolate(value, [-1, 0, 1], [10, 20, 30]);
+    const translateX = interpolate(value, [-2, 0, 1], [-width, 0, width]);
+
+    return {
+      transform: [{ translateX }],
+      zIndex
+    };
+  }, []);
+
+  return (
+    <>
+      <Carousel
+        width={width}
+        height={220}
+        data={photos}
+        renderItem={({ item, index }) => (
+          <TouchableOpacity key={index} onPress={() => openModal(index)} activeOpacity={1}>
+            <Image source={{ uri: item.uriSmall }} style={styles.image} />
+          </TouchableOpacity>
+        )}
+        customAnimation={animationStyle}
+        onSnapToItem={setActiveIndex}
+        vertical={false}
+        defaultIndex={activeIndex}
+        loop={false}
+      />
+
+      <View style={styles.pagination}>
+        <PaginationDot
+          activeDotColor={Colors.DARK_BLUE}
+          inactiveDotColor={'rgba(15, 63, 79, 0.5)'}
+          curPage={activeIndex}
+          maxPage={photos.length}
+          sizeRatio={1}
+        />
+      </View>
+    </>
+  );
+};
+
+export default ImageCarousel;

+ 14 - 0
src/screens/InAppScreens/MapScreen/RegionViewScreen/ImageCarousel/styles.tsx

@@ -0,0 +1,14 @@
+import { StyleSheet } from 'react-native';
+
+export const styles = StyleSheet.create({
+  image: {
+    width: '100%',
+    height: '100%'
+  },
+  pagination: {
+    flexDirection: 'row',
+    justifyContent: 'center',
+    marginVertical: 12,
+    gap: 8
+  }
+});

+ 105 - 0
src/screens/InAppScreens/MapScreen/RegionViewScreen/TravelSeriesList/index.tsx

@@ -0,0 +1,105 @@
+import React from 'react';
+import { View, FlatList, Text, TouchableOpacity, Image } from 'react-native';
+import { CheckBox } from 'src/components/CheckBox';
+import InfoIcon from 'assets/icons/info.svg';
+import { API_HOST } from 'src/constants';
+import { styles } from './styles';
+import { SeriesData, SeriesGroup, SeriesItem } from '../types';
+
+const TravelSeriesList = ({
+  series,
+  indexSeries,
+  routes,
+  handleCheckboxChange,
+  setIsInfoModalVisible,
+  setInfoItem,
+  disabled
+}: {
+  series: SeriesData[];
+  indexSeries: number;
+  routes: SeriesGroup[];
+  handleCheckboxChange: (item: SeriesItem, isDouble: boolean, seriesId: number) => void;
+  setIsInfoModalVisible: (value: boolean) => void;
+  setInfoItem: (item: SeriesItem) => void;
+  disabled: boolean;
+}) => {
+  const renderTravelSeriesItem = (
+    { item }: { item: SeriesItem },
+    seriesId: number,
+    icon?: string
+  ) => (
+    <View key={item.id} style={styles.itemContainer}>
+      <TouchableOpacity
+        style={styles.headerContainer}
+        onPress={() => handleCheckboxChange(item, false, seriesId)}
+        disabled={disabled}
+      >
+        {!disabled && (
+          <CheckBox
+            onChange={() => handleCheckboxChange(item, false, seriesId)}
+            value={Boolean(item.visited)}
+            color={'#0F3F4F'}
+          />
+        )}
+        {item.double_point === 1 && !disabled && (
+          <View style={{ marginLeft: 8 }}>
+            <CheckBox
+              onChange={() => handleCheckboxChange(item, true, seriesId)}
+              value={Boolean(item.visited_double)}
+              color={'#0F3F4F'}
+            />
+          </View>
+        )}
+        {icon && indexSeries === 0 && (
+          <Image
+            source={{ uri: API_HOST + icon }}
+            style={[styles.itemIcon, disabled ? { marginLeft: 0 } : {}]}
+          />
+        )}
+        <Text style={styles.contentText}>
+          {item.name}
+          {item.new === 1 && (
+            <View>
+              <Text style={styles.textNew}> NEW</Text>
+            </View>
+          )}
+        </Text>
+      </TouchableOpacity>
+      {item.description && item.description?.length > 0 && (
+        <TouchableOpacity
+          style={styles.infoSeries}
+          onPress={() => {
+            setIsInfoModalVisible(true);
+            setInfoItem(item);
+          }}
+        >
+          <InfoIcon />
+        </TouchableOpacity>
+      )}
+    </View>
+  );
+
+  return (
+    <View style={[styles.travelSeriesList]}>
+      {indexSeries === 0
+        ? series.map((s) => {
+            return (
+              <View key={s?.series_id} style={{ minHeight: 40, gap: 8, marginBottom: 5 }}>
+                <Text style={styles.travelSeriesText}>{s?.series_name}</Text>
+                <FlatList
+                  data={s?.items}
+                  renderItem={(item) => renderTravelSeriesItem(item, s.series_id, s?.icon)}
+                  keyExtractor={(item) => item?.id.toString()}
+                  scrollEnabled={false}
+                />
+              </View>
+            );
+          })
+        : series
+            .find((s) => s.series_id === routes[indexSeries].series_id)
+            ?.items?.map((item) => renderTravelSeriesItem({ item }, routes[indexSeries].series_id))}
+    </View>
+  );
+};
+
+export default TravelSeriesList;

+ 56 - 0
src/screens/InAppScreens/MapScreen/RegionViewScreen/TravelSeriesList/styles.tsx

@@ -0,0 +1,56 @@
+import { StyleSheet } from 'react-native';
+import { Colors } from 'src/theme';
+
+export const styles = StyleSheet.create({
+  itemContainer: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    marginBottom: 16,
+    justifyContent: 'space-between',
+    flex: 1
+  },
+  info: {
+    paddingHorizontal: 10,
+    height: '100%',
+    alignItems: 'center',
+    justifyContent: 'center'
+  },
+  headerContainer: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    flex: 1
+  },
+  itemIcon: {
+    width: 20,
+    height: 20,
+    marginLeft: 10,
+    resizeMode: 'contain'
+  },
+  contentText: {
+    fontSize: 13,
+    color: '#0F3F4F',
+    textAlign: 'left',
+    marginLeft: 10,
+    flexShrink: 1,
+    marginRight: 10
+  },
+  textNew: {
+    color: Colors.ORANGE,
+    fontSize: 10,
+    fontWeight: '800'
+  },
+  infoSeries: {
+    paddingHorizontal: 10,
+    height: '100%',
+    alignItems: 'center',
+    justifyContent: 'center'
+  },
+  travelSeriesList: {
+    paddingBottom: 16
+  },
+  travelSeriesText: {
+    fontSize: 12,
+    fontFamily: 'montserrat-700',
+    color: Colors.DARK_BLUE
+  }
+});

+ 547 - 0
src/screens/InAppScreens/MapScreen/RegionViewScreen/index.tsx

@@ -0,0 +1,547 @@
+import React, { FC, useCallback, useEffect, useState } from 'react';
+import { View, Text, Image, TouchableOpacity, Platform } from 'react-native';
+import ImageView from 'better-react-native-image-viewing';
+import { styles } from './styles';
+import {
+  Button,
+  EditNmModal,
+  HorizontalTabView,
+  Loading,
+  Modal as ReactModal
+} from 'src/components';
+import { useFocusEffect } from '@react-navigation/native';
+import { Colors } from 'src/theme';
+import { styles as ButtonStyles } from 'src/components/RegionPopup/style';
+
+import { usePostSetToggleItem } from '@api/series';
+import { ScrollView } from 'react-native-gesture-handler';
+import { NAVIGATION_PAGES } from 'src/types';
+import { API_HOST } from 'src/constants';
+import { useGetDareRegionDataQuery, useGetNmRegionDataQuery } from '@api/regions';
+import { StoreType, storage } from 'src/storage';
+import { ButtonVariants } from 'src/types/components';
+import { qualityOptions } from '../../TravelsScreen/utils/constants';
+import moment from 'moment';
+import { useRegion } from 'src/contexts/RegionContext';
+import formatNumber from '../../TravelsScreen/utils/formatNumber';
+import { PhotosData, Props, SeriesData, SeriesGroup, SeriesItem } from './types';
+import ImageCarousel from './ImageCarousel';
+import TravelSeriesList from './TravelSeriesList';
+
+import MarkIcon from 'assets/icons/mark.svg';
+import ChevronLeft from 'assets/icons/chevron-left.svg';
+import CaseSvg from 'assets/icons/briefcase.svg';
+import HouseSvg from 'assets/icons/house.svg';
+import EditSvg from 'assets/icons/travels-screens/pen-to-square.svg';
+import CalendarSvg from 'assets/icons/travels-screens/calendar.svg';
+import RotateSvg from 'assets/icons/travels-screens/rotate.svg';
+
+const RegionViewScreen: FC<Props> = ({ navigation, route }) => {
+  const regionId = route.params?.regionId;
+  const type = route.params?.type;
+  const disabled = route.params?.disabled;
+  const token = storage.get('token', StoreType.STRING) as string;
+  const [isLoading, setIsLoading] = useState(true);
+  const [isModalVisible, setModalVisible] = useState(false);
+  const [currentImageIndex, setCurrentImageIndex] = useState(0);
+  const [activeIndex, setActiveIndex] = useState(0);
+  const [indexSeries, setIndexSeries] = useState(0);
+  const [routes, setRoutes] = useState<SeriesGroup[]>([]);
+  const [series, setSeries] = useState<SeriesData[]>([]);
+  const [photos, setPhotos] = useState<PhotosData[]>([]);
+  const [name, setName] = useState(['', '']);
+  const { data } =
+    type === 'nm'
+      ? useGetNmRegionDataQuery(regionId, type === 'nm', token && token)
+      : useGetDareRegionDataQuery(regionId, type === 'dare', token && token);
+  const { mutate: updateSeriesItem } = usePostSetToggleItem();
+  const [isInfoModalVisible, setIsInfoModalVisible] = useState<boolean>(false);
+  const [infoItem, setInfoItem] = useState<SeriesItem | null>(null);
+  const [isEditModalVisible, setIsEditModalVisible] = useState(false);
+  const [modalState, setModalState] = useState({
+    selectedFirstYear: 2021,
+    selectedLastYear: 2021,
+    selectedQuality: qualityOptions[2],
+    selectedNoOfVisits: 1,
+    years: [],
+    id: regionId
+  });
+
+  const {
+    handleUpdateNM: updateNM,
+    handleUpdateDare: updateDare,
+    userData: regionData,
+    setUserData: setRegionData,
+    handleUpdateNMList,
+    handleUpdateDareList
+  } = useRegion();
+
+  useEffect(() => {
+    navigation.getParent()?.setOptions({
+      tabBarStyle: {
+        display: 'none',
+        position: 'absolute',
+        ...Platform.select({
+          android: {
+            height: 58
+          }
+        })
+      }
+    });
+  }, [navigation]);
+
+  useFocusEffect(
+    useCallback(() => {
+      const fetchGroups = async () => {
+        let staticGroups = [
+          {
+            key: 'all',
+            title: 'All',
+            series_id: 0
+          }
+        ];
+        const routesData = data?.data?.series?.map((item) => ({
+          key: item.series_id?.toString(),
+          title: item.series_name,
+          series_id: item.series_id,
+          icon: item.icon,
+          items: item.items
+        }));
+
+        routesData && staticGroups.push(...routesData);
+
+        setPhotos(
+          data?.data?.photos?.map((item) => ({
+            ...item,
+            uriSmall: `${API_HOST}/ajax/pic/${item.id}/small`,
+            uri: `${API_HOST}/ajax/pic/${item.id}/full`
+          })) ?? []
+        );
+
+        const [regionName, ...rest] = data?.data?.region_name?.split(/ – | - /) ?? [];
+        const subname = rest?.join(' - ');
+        setName([regionName, subname]);
+        setSeries(data?.data?.series || []);
+        setRoutes(staticGroups);
+        setIsLoading(false);
+      };
+
+      if (data && data.result === 'OK') {
+        fetchGroups();
+      }
+    }, [data])
+  );
+
+  const handleCheckboxChange = useCallback(
+    async (item: SeriesItem, double: boolean, seriesId: number) => {
+      setSeries((currentData) => {
+        const groupIndex = currentData.findIndex((group) => group?.series_id === seriesId);
+
+        if (groupIndex === -1) return currentData;
+
+        const newData = [...currentData];
+        const newGroup = { ...newData[groupIndex] };
+
+        newGroup.items = newGroup.items.map((subItem) =>
+          subItem.id === item.id
+            ? {
+                ...subItem,
+                ...(double
+                  ? { visited_double: subItem.visited_double === 0 ? 1 : 0 }
+                  : { visited: subItem.visited === 0 ? 1 : 0 })
+              }
+            : subItem
+        );
+
+        newData[groupIndex] = newGroup;
+        return newData;
+      });
+
+      const itemData = {
+        token: token,
+        series_id: seriesId,
+        item_id: item.id,
+        checked: (item.visited === 1 ? 0 : 1) as 0 | 1,
+        double: (double && !item.visited_double ? 1 : 0) as 0 | 1
+      };
+
+      try {
+        updateSeriesItem(itemData);
+      } catch (error) {
+        console.error('Failed to update checkbox state', error);
+      }
+    },
+    [token, updateSeriesItem]
+  );
+
+  const handleModalStateChange = (updates: { [key: string]: any }) => {
+    setModalState((prevState) => ({ ...prevState, ...updates }));
+  };
+
+  useEffect(() => {
+    const currentYear = moment().year();
+    let yearSelector: { label: string; value: number }[] = [{ label: 'visited', value: 1 }];
+    for (let i = currentYear; i >= 1951; i--) {
+      yearSelector.push({ label: i.toString(), value: i });
+    }
+    handleModalStateChange({ years: yearSelector });
+  }, []);
+
+  const openModal = (index: number) => {
+    setCurrentImageIndex(index);
+    setModalVisible(true);
+  };
+
+  if (isLoading) return <Loading />;
+
+  const handleOpenEditModal = () => {
+    handleModalStateChange({
+      selectedFirstYear: regionData?.first_visit_year,
+      selectedLastYear: regionData?.last_visit_year,
+      selectedQuality:
+        qualityOptions.find((quality) => quality.id === regionData?.best_visit_quality) ||
+        qualityOptions[2],
+      selectedNoOfVisits: regionData?.no_of_visits || 1,
+      id: regionId
+    });
+    setIsEditModalVisible(true);
+  };
+
+  const handleUpdateNmModal = (
+    region: number = regionId,
+    first: number,
+    last: number,
+    visits: number,
+    quality: number
+  ) => {
+    const updatedNM = {
+      ...regionData,
+      first_visit_year: first,
+      last_visit_year: last,
+      best_visit_quality: quality,
+      no_of_visits: visits,
+      visited: visits > 0 ? true : false
+    };
+
+    route.params?.isTravelsScreen
+      ? handleUpdateNMList(region, first, last, visits, quality)
+      : updateNM(region, first, last, visits, quality);
+    updatedNM && setRegionData(updatedNM);
+  };
+
+  const handleUpdateNm = () => {
+    route.params?.isTravelsScreen
+      ? handleUpdateNMList(
+          regionId,
+          regionData.visited ? 0 : 1,
+          regionData.visited ? 0 : 1,
+          regionData.visited ? 0 : 1,
+          3
+        )
+      : updateNM(
+          regionId,
+          regionData.visited ? 0 : 1,
+          regionData.visited ? 0 : 1,
+          regionData.visited ? 0 : 1,
+          3
+        );
+
+    setRegionData({
+      ...regionData,
+      first_visit_year: regionData.visited ? 0 : 1,
+      last_visit_year: regionData.visited ? 0 : 1,
+      best_visit_quality: 3,
+      no_of_visits: regionData.visited ? 0 : 1,
+      visited: !regionData.visited
+    });
+  };
+
+  const handleUpdateDare = () => {
+    route.params?.isTravelsScreen
+      ? handleUpdateDareList(regionId, regionData.visited ? 0 : 1)
+      : updateDare(regionId, regionData.visited ? 0 : 1);
+
+    setRegionData({
+      ...regionData,
+      first_visit_year: regionData.visited ? 0 : 1,
+      last_visit_year: regionData.visited ? 0 : 1,
+      best_visit_quality: 3,
+      no_of_visits: regionData.visited ? 0 : 1,
+      visited: !regionData.visited
+    });
+  };
+
+  return (
+    <View style={styles.container}>
+      <TouchableOpacity
+        onPress={() => {
+          navigation.goBack();
+        }}
+        style={styles.backButton}
+      >
+        <View style={styles.chevronWrapper}>
+          <ChevronLeft fill={Colors.WHITE} />
+        </View>
+      </TouchableOpacity>
+      <ScrollView
+        contentContainerStyle={{ flexGrow: 1 }}
+        nestedScrollEnabled={true}
+        showsVerticalScrollIndicator={false}
+      >
+        {photos.length > 0 ? (
+          <ImageCarousel
+            photos={photos}
+            activeIndex={activeIndex}
+            setActiveIndex={setActiveIndex}
+            openModal={openModal}
+          />
+        ) : (
+          <View style={styles.emptyImage}>
+            <Image
+              source={require('../../../../../assets/images/logo-opacity.png')}
+              style={{ width: 100, height: 100 }}
+            />
+            <Text style={styles.emptyImageText}>No image available at this location</Text>
+          </View>
+        )}
+
+        <View style={styles.wrapper}>
+          <View style={{ flexDirection: 'row', gap: 8, justifyContent: 'flex-end' }}>
+            {regionData?.visited && regionData?.first_visit_year > 1 && !disabled && (
+              <View style={styles.infoContent}>
+                <CalendarSvg height={18} width={18} fill={Colors.DARK_BLUE} />
+                <Text style={styles.visitedButtonText}>{regionData?.first_visit_year}</Text>
+              </View>
+            )}
+
+            {regionData?.visited && type === 'nm' && !disabled && (
+              <View style={styles.infoContent}>
+                <RotateSvg fill={Colors.DARK_BLUE} />
+                <Text style={styles.visitedButtonText}>
+                  {regionData.no_of_visits >= 10 ? '10+' : regionData.no_of_visits}
+                </Text>
+              </View>
+            )}
+          </View>
+          <View style={styles.nameContainer}>
+            <Text style={styles.title}>{name[0]}</Text>
+            <View style={ButtonStyles.btnContainer}>
+              {regionData?.visited && type === 'nm' && !disabled ? (
+                <TouchableOpacity onPress={handleOpenEditModal} style={ButtonStyles.editBtn}>
+                  <EditSvg width={14} height={14} />
+                </TouchableOpacity>
+              ) : null}
+              {!disabled ? (
+                <TouchableOpacity
+                  style={[
+                    ButtonStyles.btn,
+                    regionData?.visited && !disabled
+                      ? ButtonStyles.visitedButton
+                      : ButtonStyles.markVisitedButton
+                  ]}
+                  onPress={() => (type === 'nm' ? handleUpdateNm() : handleUpdateDare())}
+                >
+                  {regionData?.visited ? (
+                    <View style={ButtonStyles.visitedContainer}>
+                      <MarkIcon width={16} height={16} />
+                      <Text style={ButtonStyles.visitedButtonText}>Visited</Text>
+                    </View>
+                  ) : (
+                    <Text style={[ButtonStyles.markVisitedButtonText]}>Mark Visited</Text>
+                  )}
+                </TouchableOpacity>
+              ) : null}
+            </View>
+          </View>
+
+          <Text style={styles.subtitle}>{name[1]}</Text>
+
+          <View style={styles.divider} />
+
+          <View style={styles.stats}>
+            {data?.data.users_from_region_count ?? 0 > 0 ? (
+              <TouchableOpacity
+                style={[styles.statItem, { justifyContent: 'flex-start' }]}
+                onPress={() =>
+                  navigation.navigate(
+                    ...([
+                      NAVIGATION_PAGES.USERS_LIST,
+                      {
+                        regionId,
+                        isFromHere: true,
+                        type: 'nm'
+                      }
+                    ] as never)
+                  )
+                }
+              >
+                <View style={styles.icon}>
+                  <HouseSvg />
+                </View>
+                <View
+                  style={{
+                    height: 12,
+                    width: 1,
+                    backgroundColor: Colors.DARK_BLUE,
+                    marginRight: 6
+                  }}
+                />
+                <View style={styles.userImageContainer}>
+                  {data?.data.users_from_region &&
+                    data?.data.users_from_region.length > 0 &&
+                    data?.data.users_from_region?.map((user, index: number) => (
+                      <Image
+                        key={index}
+                        source={{ uri: API_HOST + user }}
+                        style={styles.userImage}
+                      />
+                    ))}
+                  <View style={styles.userCountContainer}>
+                    <Text style={styles.userCount}>
+                      {formatNumber(data?.data?.users_from_region_count ?? 0)}
+                    </Text>
+                  </View>
+                </View>
+              </TouchableOpacity>
+            ) : (
+              <View style={[styles.statItem, { justifyContent: 'flex-start' }]} />
+            )}
+
+            {data?.data.users_who_visited_region_count ?? 0 > 0 ? (
+              <TouchableOpacity
+                style={[styles.statItem, { justifyContent: 'flex-end' }]}
+                onPress={() =>
+                  navigation.navigate(
+                    ...([
+                      NAVIGATION_PAGES.USERS_LIST,
+                      {
+                        regionId,
+                        isFromHere: false,
+                        type
+                      }
+                    ] as never)
+                  )
+                }
+              >
+                <View style={styles.icon}>
+                  <CaseSvg />
+                </View>
+                <View
+                  style={{
+                    height: 12,
+                    width: 1,
+                    backgroundColor: Colors.DARK_BLUE,
+                    marginRight: 6
+                  }}
+                />
+
+                <View style={styles.userImageContainer}>
+                  {data?.data.users_who_visited_region &&
+                    data?.data.users_who_visited_region.length > 0 &&
+                    data?.data.users_who_visited_region?.map((user, index) => (
+                      <Image
+                        key={index}
+                        source={{ uri: API_HOST + user }}
+                        style={[styles.userImage]}
+                      />
+                    ))}
+                  <View style={styles.userCountContainer}>
+                    <Text style={styles.userCount}>
+                      {formatNumber(data?.data.users_who_visited_region_count ?? 0)}
+                    </Text>
+                  </View>
+                </View>
+              </TouchableOpacity>
+            ) : (
+              <View style={[styles.statItem, { justifyContent: 'flex-end' }]} />
+            )}
+          </View>
+
+          <View style={[styles.divider, { marginBottom: 8 }]} />
+
+          {series.length > 0 ? (
+            <>
+              <Text style={styles.travelSeriesTitle}>TRAVEL SERIES</Text>
+              <HorizontalTabView
+                index={indexSeries}
+                setIndex={setIndexSeries}
+                routes={routes}
+                renderScene={({ route }: { route: SeriesGroup }) => <View style={{ height: 0 }} />}
+              />
+              <TravelSeriesList
+                series={series}
+                indexSeries={indexSeries}
+                routes={routes}
+                handleCheckboxChange={handleCheckboxChange}
+                setIsInfoModalVisible={setIsInfoModalVisible}
+                setInfoItem={setInfoItem}
+                disabled={disabled}
+              />
+            </>
+          ) : null}
+        </View>
+
+        <ImageView
+          images={photos}
+          imageIndex={currentImageIndex}
+          visible={isModalVisible}
+          onRequestClose={() => setModalVisible(false)}
+          backgroundColor={Colors.DARK_BLUE}
+          onImageIndexChange={setActiveIndex}
+          FooterComponent={({ imageIndex }) => (
+            <View style={styles.imageFooter}>
+              <Text style={styles.imageDescription}>{photos[imageIndex].title}</Text>
+              <TouchableOpacity
+                onPress={() => {
+                  setModalVisible(false);
+                  navigation.navigate(
+                    ...([
+                      NAVIGATION_PAGES.PUBLIC_PROFILE_VIEW,
+                      { userId: photos[imageIndex].user_id, hideTabBar: true }
+                    ] as never)
+                  );
+                }}
+                disabled={disabled}
+                style={styles.imageOwner}
+              >
+                <Text style={styles.imageOwnerText}>{photos[imageIndex].first_name}</Text>
+                <Text style={styles.imageOwnerText}>{photos[imageIndex].last_name}</Text>
+              </TouchableOpacity>
+            </View>
+          )}
+        />
+
+        <ReactModal
+          visible={isInfoModalVisible}
+          children={
+            <View style={styles.modalView}>
+              <Text style={styles.infoTitle}>{infoItem?.name}</Text>
+              <Text style={styles.infoText}>{infoItem?.description}</Text>
+              <Button
+                variant={ButtonVariants.OPACITY}
+                containerStyles={styles.btnContainer}
+                textStyles={{
+                  color: Colors.DARK_BLUE
+                }}
+                onPress={() => setIsInfoModalVisible(false)}
+                children={'Got it'}
+              />
+            </View>
+          }
+          onRequestClose={() => setIsInfoModalVisible(false)}
+          headerTitle={'Info'}
+          visibleInPercent={'auto'}
+        />
+      </ScrollView>
+      <EditNmModal
+        isVisible={isEditModalVisible}
+        onClose={() => setIsEditModalVisible(false)}
+        modalState={modalState}
+        updateModalState={handleModalStateChange}
+        updateNM={handleUpdateNmModal}
+      />
+    </View>
+  );
+};
+
+export default RegionViewScreen;

+ 172 - 0
src/screens/InAppScreens/MapScreen/RegionViewScreen/styles.tsx

@@ -0,0 +1,172 @@
+import { StyleSheet } from 'react-native';
+import { Colors } from '../../../../theme';
+
+export const styles = StyleSheet.create({
+  container: {
+    flex: 1,
+    backgroundColor: 'white',
+    position: 'relative'
+  },
+  chevronWrapper: {
+    width: 42,
+    height: 42,
+    borderRadius: 21,
+    justifyContent: 'center',
+    alignItems: 'center',
+    backgroundColor: 'rgba(15, 63, 79, 0.6)'
+  },
+  backButton: {
+    position: 'absolute',
+    width: 50,
+    height: 50,
+    top: 50,
+    left: 5,
+    justifyContent: 'center',
+    alignItems: 'center',
+    zIndex: 2
+  },
+  emptyImage: {
+    height: 220,
+    width: '100%',
+    marginBottom: 12,
+    backgroundColor: Colors.FILL_LIGHT,
+    alignItems: 'center',
+    justifyContent: 'center',
+    gap: 16,
+    paddingTop: 24,
+    shadowColor: '#00000026',
+    shadowOffset: { width: 0, height: 0 },
+    shadowOpacity: 0.15,
+    shadowRadius: 8,
+    elevation: 8
+  },
+  emptyImageText: { fontWeight: '600', color: '#808080', fontSize: 12 },
+  imageFooter: { paddingBottom: 50, paddingHorizontal: 16, gap: 16 },
+  imageDescription: { color: Colors.WHITE, textAlign: 'center', fontWeight: '600' },
+  imageOwner: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    justifyContent: 'center',
+    gap: 4,
+    backgroundColor: 'rgba(255, 255, 255, 0.8)',
+    paddingVertical: 8,
+    paddingHorizontal: 12,
+    borderRadius: 8,
+    alignSelf: 'center'
+  },
+  imageOwnerText: { color: Colors.DARK_BLUE, fontFamily: 'montserrat-700' },
+  wrapper: {
+    marginLeft: '5%',
+    marginRight: '5%',
+    gap: 16
+  },
+  divider: {
+    height: 1,
+    width: '100%',
+    backgroundColor: Colors.DARK_LIGHT
+  },
+  nameContainer: {
+    flexDirection: 'row',
+    justifyContent: 'space-between',
+    alignItems: 'center'
+  },
+  title: {
+    fontSize: 18,
+    fontFamily: 'montserrat-700',
+    color: Colors.DARK_BLUE
+  },
+  subtitle: {
+    fontSize: 12,
+    fontWeight: '600',
+    color: Colors.DARK_BLUE
+  },
+  stats: {
+    flexDirection: 'row',
+    justifyContent: 'space-between'
+  },
+  icon: {
+    width: 28,
+    height: 28,
+    borderRadius: 14,
+    backgroundColor: Colors.DARK_BLUE,
+    alignItems: 'center',
+    justifyContent: 'center'
+  },
+  statItem: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    gap: 4,
+    flex: 1
+  },
+  statText: {
+    fontSize: 14,
+    color: 'gray'
+  },
+  statNumber: {
+    fontSize: 16,
+    fontWeight: 'bold'
+  },
+  travelSeriesTitle: {
+    fontSize: 14,
+    fontFamily: 'montserrat-700',
+    textAlign: 'center',
+    marginBottom: -4
+  },
+  userImageContainer: {
+    flexDirection: 'row',
+    alignItems: 'center'
+  },
+  userImage: {
+    width: 28,
+    height: 28,
+    borderRadius: 14,
+    marginLeft: -6,
+    borderWidth: 1,
+    borderColor: Colors.DARK_LIGHT,
+    resizeMode: 'cover'
+  },
+  userCountContainer: {
+    width: 28,
+    height: 28,
+    borderRadius: 14,
+    backgroundColor: Colors.DARK_LIGHT,
+    alignItems: 'center',
+    justifyContent: 'center',
+    marginLeft: -6
+  },
+  userCount: {
+    fontSize: 12,
+    color: Colors.DARK_BLUE,
+    lineHeight: 24
+  },
+  modalView: {
+    paddingHorizontal: 8,
+    paddingVertical: 24,
+    alignItems: 'center'
+  },
+  infoTitle: {
+    color: Colors.DARK_BLUE,
+    fontSize: 16,
+    fontWeight: '700',
+    textAlign: 'center',
+    marginBottom: 16
+  },
+  infoText: {
+    color: Colors.DARK_BLUE,
+    fontSize: 14,
+    fontWeight: '400',
+    textAlign: 'left',
+    marginBottom: 24
+  },
+  btnContainer: {
+    borderColor: Colors.DARK_BLUE,
+    backgroundColor: Colors.WHITE,
+    width: '60%'
+  },
+  infoContent: { flexDirection: 'row', alignItems: 'center', gap: 4 },
+  visitedButtonText: {
+    color: Colors.DARK_BLUE,
+    fontWeight: 'bold',
+    fontSize: 13
+  }
+});

+ 42 - 0
src/screens/InAppScreens/MapScreen/RegionViewScreen/types.ts

@@ -0,0 +1,42 @@
+import { NavigationProp } from '@react-navigation/native';
+
+export interface SeriesGroup {
+  key: string;
+  title: string;
+  series_id: number;
+  icon?: string;
+  items?: any[];
+}
+
+export type Props = {
+  navigation: NavigationProp<any>;
+  route: any;
+};
+
+export interface SeriesItem {
+  id: number;
+  name: string;
+  description: string | null;
+  new: 0 | 1;
+  visited: 0 | 1;
+  double_point: 0 | 1;
+  visited_double: 0 | 1;
+  link: string;
+}
+
+export interface SeriesData {
+  series_id: number;
+  series_name: string;
+  icon: string;
+  items: SeriesItem[];
+}
+
+export interface PhotosData {
+  title: string;
+  id: number;
+  user_id: number;
+  first_name: string;
+  last_name: string;
+  uri: string;
+  uriSmall: string;
+}

+ 146 - 0
src/screens/InAppScreens/MapScreen/UsersListScreen/Profile/index.tsx

@@ -0,0 +1,146 @@
+import React, { FC, useState } from 'react';
+import { Text, TouchableOpacity, View } from 'react-native';
+import { Image } from 'expo-image';
+import { useNavigation } from '@react-navigation/native';
+
+import { storage, StoreType } from 'src/storage';
+import { AvatarWithInitials, WarningModal } from 'src/components';
+
+import TickIcon from 'assets/icons/tick.svg';
+import UNIcon from 'assets/icons/un_icon.svg';
+import NMIcon from 'assets/icons/nm_icon.svg';
+
+import { useConnection } from 'src/contexts/ConnectionContext';
+import { NAVIGATION_PAGES } from 'src/types';
+import { API_HOST } from 'src/constants';
+import { adaptiveStyle } from 'src/theme';
+import {
+  ProfileStyles,
+  ScoreStyles
+} from 'src/screens/InAppScreens/TravellersScreen/Components/styles';
+import { getFontSize } from 'src/utils';
+
+type Props = {
+  avatar: string | null;
+  first_name: string;
+  last_name: string;
+  date_of_birth: number;
+  homebase_flag: string;
+  homebase2_flag: string | null;
+  index: number;
+  score: any[];
+  active_score: number;
+  tbt_score?: number;
+  tbt_rank?: number;
+  badge_tbt?: number;
+  badge_1281: number;
+  badge_un: number;
+  auth: number;
+  userId: number;
+};
+
+export const Profile: FC<Props> = ({
+  avatar,
+  first_name,
+  last_name,
+  date_of_birth,
+  homebase_flag,
+  homebase2_flag,
+  index,
+  score,
+  active_score,
+  tbt_rank,
+  badge_tbt,
+  badge_1281,
+  badge_un,
+  auth,
+  userId
+}) => {
+  const navigation = useNavigation();
+
+  const scoreNames = ['NM', 'DARE', 'UN', 'UN+', 'TCC', 'DEEP', 'YES', 'SLOW', 'WHS', 'KYE', 'TBT'];
+  const netInfo = useConnection();
+  const token = storage.get('token', StoreType.STRING);
+  const [modalType, setModalType] = useState<string | null>(null);
+
+  const handlePress = () => {
+    if (!netInfo?.isInternetReachable) {
+      setModalType('offline');
+    } else if (!token) {
+      setModalType('unauthorized');
+    } else {
+      navigation.navigate(
+        ...([NAVIGATION_PAGES.PUBLIC_PROFILE_VIEW, { userId, hideTabBar: true }] as never)
+      );
+    }
+  };
+
+  return (
+    <View style={ProfileStyles.wrapper}>
+      <TouchableOpacity
+        onPress={() => handlePress()}
+        style={{ flex: 1, paddingHorizontal: 8, paddingVertical: 3 }}
+      >
+        <View style={adaptiveStyle(ProfileStyles.profileRoot, {})}>
+          {avatar ? (
+            <Image
+              style={adaptiveStyle(ProfileStyles.profileAvatar, {})}
+              source={{ uri: API_HOST + avatar }}
+            />
+          ) : homebase_flag ? (
+            <AvatarWithInitials
+              text={`${first_name[0] ?? ''}${last_name[0] ?? ''}`}
+              flag={API_HOST + homebase_flag}
+              size={48}
+            />
+          ) : null}
+          <View style={adaptiveStyle(ProfileStyles.profileDataRoot, {})}>
+            <Text
+              style={adaptiveStyle(
+                [ProfileStyles.profileFirstLastName, { fontSize: getFontSize(14), flex: 0 }],
+                {}
+              )}
+            >
+              {first_name ?? ''} {last_name ?? ''}
+            </Text>
+            <View style={adaptiveStyle(ProfileStyles.profileDataContainer, {})}>
+              <View style={adaptiveStyle(ProfileStyles.profileDataWrapper, {})}>
+                <Text style={adaptiveStyle(ProfileStyles.profileAge, {})}>
+                  Age: {date_of_birth ?? ''}
+                </Text>
+                {homebase_flag && (
+                  <Image
+                    source={{ uri: API_HOST + homebase_flag }}
+                    style={adaptiveStyle(ProfileStyles.countryFlag, {})}
+                  />
+                )}
+                {homebase2_flag && homebase2_flag !== homebase_flag ? (
+                  <Image
+                    source={{ uri: API_HOST + homebase2_flag }}
+                    style={adaptiveStyle([ProfileStyles.countryFlag, { marginLeft: -15 }], {})}
+                  />
+                ) : null}
+                <View style={adaptiveStyle(ProfileStyles.badgesWrapper, {})}>
+                  {auth ? <TickIcon /> : null}
+                  {badge_un ? <UNIcon /> : null}
+                  {badge_1281 ? <NMIcon /> : null}
+                </View>
+              </View>
+            </View>
+          </View>
+          <View style={{ alignItems: 'center' }}>
+            <Text style={adaptiveStyle(ScoreStyles.activeScoreRanking, {})}>
+              {score[active_score]}
+            </Text>
+            <Text style={adaptiveStyle(ScoreStyles.activeScoreName, {})}>
+              {scoreNames[active_score]}
+            </Text>
+          </View>
+        </View>
+      </TouchableOpacity>
+      {modalType && (
+        <WarningModal type={modalType} isVisible={true} onClose={() => setModalType(null)} />
+      )}
+    </View>
+  );
+};

+ 271 - 0
src/screens/InAppScreens/MapScreen/UsersListScreen/index.tsx

@@ -0,0 +1,271 @@
+import { NavigationProp } from '@react-navigation/native';
+import React, { FC, useCallback, useEffect, useState } from 'react';
+import { ActivityIndicator } from 'react-native';
+import { Header, Loading, PageWrapper } from 'src/components';
+
+import { Colors } from 'src/theme';
+import { FlashList } from '@shopify/flash-list';
+import { StoreType, storage } from 'src/storage';
+import { FilterButton, FilterModal } from '../../TravellersScreen/Components/FilterModal';
+import {
+  useGetUsersFromRegionMutation,
+  useGetUsersWhoVisitedDareMutation,
+  useGetUsersWhoVisitetRegionMutation
+} from '@api/regions';
+import { Profile } from './Profile';
+import { RankingDropdown } from '../../TravellersScreen/utils/types';
+import { dataRanking } from '../../TravellersScreen/utils';
+import { Ranking } from '../../TravellersScreen';
+
+type Props = {
+  navigation: NavigationProp<any>;
+  route: any;
+};
+
+const UsersListScreen: FC<Props> = ({ navigation, route }) => {
+  const regionId = route.params?.regionId;
+  const type = route.params?.type;
+  const { mutateAsync: getUsersFromRegion } = useGetUsersFromRegionMutation();
+  const { mutateAsync: getUsersWhoVisitedRegion } = useGetUsersWhoVisitetRegionMutation();
+  const { mutateAsync: getUsersWhoVisitedDare } = useGetUsersWhoVisitedDareMutation();
+  const token = storage.get('token', StoreType.STRING);
+  const [users, setUsers] = useState<Ranking[]>([]);
+  const [loading, setLoading] = useState(true);
+  const [selectedUsers, setSelectedUsers] = useState<Ranking[]>([]);
+  const [filteredUsers, setFilteredUsers] = useState<Ranking[]>([]);
+  const isFromHere = route.params?.isFromHere;
+  const [isModalVisible, setModalVisible] = useState(false);
+  const [confirmedValueRanking, setConfirmedValueRanking] = useState<RankingDropdown | null>();
+  const [masterCountries, setMasterCountries] = useState<any>([]);
+  const [maxPages, setMaxPages] = useState(1);
+  const [page, setPage] = useState(0);
+  const [isLoadingMore, setIsLoadingMore] = useState(false);
+  const [filter, setFilter] = useState<{
+    age: number | undefined;
+    ranking: string | undefined;
+    country: string | undefined;
+  }>({ age: undefined, ranking: undefined, country: undefined });
+
+  useEffect(() => {
+    setFilteredUsers(selectedUsers);
+  }, [selectedUsers]);
+
+  useEffect(() => {
+    applySort();
+  }, [filter]);
+
+  useEffect(() => {
+    const getNextPage = async () => {
+      if (isFromHere) {
+        await getUsersFromRegion(
+          {
+            id: regionId,
+            page,
+            sort: filter.ranking,
+            age: filter.age
+          },
+          {
+            onSuccess: (data) => {
+              setIsLoadingMore(false);
+              setUsers((prevState) => [...prevState, ...data?.data?.users]);
+              setSelectedUsers((prevState) => [...prevState, ...data?.data?.users]);
+            }
+          }
+        );
+      } else if (type === 'nm') {
+        await getUsersWhoVisitedRegion(
+          {
+            id: regionId,
+            page,
+            sort: filter.ranking,
+            age: filter.age,
+            country: filter.country
+          },
+          {
+            onSuccess: (data) => {
+              setIsLoadingMore(false);
+              setUsers((prevState) => [...prevState, ...data?.data?.users]);
+              setSelectedUsers((prevState) => [...prevState, ...data?.data?.users]);
+            }
+          }
+        );
+      } else {
+        await getUsersWhoVisitedDare(
+          {
+            id: regionId,
+            page,
+            sort: filter.ranking,
+            age: filter.age,
+            country: filter.country
+          },
+          {
+            onSuccess: (data) => {
+              setIsLoadingMore(false);
+              setUsers((prevState) => [...prevState, ...data?.data?.users]);
+              setSelectedUsers((prevState) => [...prevState, ...data?.data?.users]);
+            }
+          }
+        );
+      }
+    };
+    if (page !== 0) {
+      getNextPage();
+    }
+  }, [page]);
+
+  const applySort = async () => {
+    if (isFromHere) {
+      await getUsersFromRegion(
+        {
+          id: regionId,
+          page,
+          sort: filter.ranking,
+          age: filter.age
+        },
+        {
+          onSuccess: (data) => {
+            setUsers(data?.data?.users);
+            setSelectedUsers(data?.data?.users);
+            setMaxPages(data?.data?.max_pages);
+            setLoading(false);
+          }
+        }
+      );
+    } else if (type === 'nm') {
+      await getUsersWhoVisitedRegion(
+        {
+          id: regionId,
+          page,
+          sort: filter.ranking,
+          age: filter.age,
+          country: filter.country
+        },
+        {
+          onSuccess: (data) => {
+            setUsers(data?.data?.users);
+            setSelectedUsers(data?.data?.users);
+            setMaxPages(data?.data?.max_pages);
+            setMasterCountries(convertData(data?.data?.countries) ?? []);
+            setLoading(false);
+          }
+        }
+      );
+    } else {
+      await getUsersWhoVisitedDare(
+        {
+          id: regionId,
+          page,
+          sort: filter.ranking,
+          age: filter.age,
+          country: filter.country
+        },
+        {
+          onSuccess: (data) => {
+            setUsers(data?.data?.users);
+            setSelectedUsers(data?.data?.users);
+            setMaxPages(data?.data?.max_pages);
+            setMasterCountries(convertData(data?.data?.countries) ?? []);
+            setLoading(false);
+          }
+        }
+      );
+    }
+  };
+
+  const convertData = (data: { [key: string]: { country: string; flag: string } }) => {
+    return Object.entries(data).map(([key, value]) => ({
+      two: key,
+      name: value.country,
+      flag: value.flag
+    }));
+  };
+
+  const handleEndReached = useCallback(() => {
+    if (users && page < maxPages && !isLoadingMore) {
+      setIsLoadingMore(true);
+      setPage((prevPage) => prevPage + 1);
+    }
+  }, [users, page]);
+
+  if (loading) return <Loading />;
+
+  const ListFooter = ({ isLoading }: { isLoading: boolean }) =>
+    isLoading ? <ActivityIndicator size="large" color={Colors.DARK_BLUE} /> : null;
+
+  return (
+    <PageWrapper>
+      <Header
+        label={isFromHere ? 'From here' : 'Been here'}
+        rightElement={<FilterButton onPress={() => setModalVisible(!isModalVisible)} />}
+      />
+      <FlashList
+        viewabilityConfig={{
+          waitForInteraction: true,
+          itemVisiblePercentThreshold: 50,
+          minimumViewTime: 1000
+        }}
+        estimatedItemSize={50}
+        data={filteredUsers}
+        renderItem={({ item, index }) => (
+          <Profile
+            userId={item.user_id}
+            key={index}
+            index={index}
+            first_name={item.first_name}
+            last_name={item.last_name}
+            avatar={item.avatar}
+            date_of_birth={item.age}
+            homebase_flag={item.flag1}
+            homebase2_flag={item.flag2}
+            score={[
+              item.score_nm,
+              item.score_dare,
+              item.score_un,
+              item.score_unp,
+              item.score_tcc,
+              item.score_deep,
+              item.score_yes,
+              item.score_slow,
+              item.score_whs,
+              item.score_kye,
+              item.score_tbt
+            ]}
+            active_score={
+              confirmedValueRanking ? confirmedValueRanking.value - 1 : dataRanking[0].value - 1
+            }
+            tbt_score={item.score_tbt}
+            tbt_rank={item.rank_tbt}
+            badge_tbt={item.badge_tbt}
+            badge_1281={item.badge_1281}
+            badge_un={item.badge_un}
+            auth={item.auth}
+          />
+        )}
+        keyExtractor={(item) => item.user_id.toString()}
+        contentContainerStyle={{ paddingBottom: 16, paddingTop: 8 }}
+        showsVerticalScrollIndicator={false}
+        onEndReached={handleEndReached}
+        onEndReachedThreshold={0.2}
+        ListFooterComponent={<ListFooter isLoading={isLoadingMore} />}
+      />
+
+      <FilterModal
+        isModalVisible={isModalVisible}
+        setModalVisible={(value) => setModalVisible(value)}
+        applyFilter={(filterAge, filterRanking, filterCountry) => {
+          setConfirmedValueRanking(filterRanking);
+          setPage(0);
+          setFilter({
+            age: filterAge?.value ? +filterAge?.value : undefined,
+            ranking: filterRanking?.name,
+            country: filterCountry?.two
+          });
+          setModalVisible(false);
+        }}
+        countriesData={masterCountries}
+      />
+    </PageWrapper>
+  );
+};
+
+export default UsersListScreen;

+ 41 - 55
src/screens/InAppScreens/MapScreen/index.tsx

@@ -57,6 +57,8 @@ import SearchModal from './UniversalSearch';
 import FilterModal from './FilterModal';
 import InfoIcon from 'assets/icons/info-solid.svg';
 import { NAVIGATION_PAGES } from 'src/types';
+import { useRegion } from 'src/contexts/RegionContext';
+import { useFocusEffect } from '@react-navigation/native';
 
 const localTileDir = `${FileSystem.cacheDirectory}tiles/background`;
 const localGridDir = `${FileSystem.cacheDirectory}tiles/grid`;
@@ -79,8 +81,6 @@ const MapScreen: React.FC<MapScreenProps> = ({ navigation }) => {
   const { mutateAsync: mutateUserData } = fetchUserData();
   const { mutateAsync: mutateUserDataDare } = fetchUserDataDare();
   const { mutate: updateSeriesItem } = usePostSetToggleItem();
-  const { mutate: updateNM } = usePostSetNmRegionMutation();
-  const { mutate: updateDARE } = usePostSetDareRegionMutation();
   const visitedTiles = `${FASTEST_MAP_HOST}/tiles_nm/user_visited/${userId}`;
   const visitedUNTiles = `${FASTEST_MAP_HOST}/tiles_nm/user_visited_un/${userId}`;
   const mapRef = useRef<MapView>(null);
@@ -99,7 +99,6 @@ const MapScreen: React.FC<MapScreenProps> = ({ navigation }) => {
   const [series, setSeries] = useState<Series[] | null>(null);
   const [processedMarkers, setProcessedMarkers] = useState<ItemSeries[]>([]);
   const [zoomLevel, setZoomLevel] = useState<number>(0);
-  const [userData, setUserData] = useState<any>(null);
   const [isEditModalVisible, setIsEditModalVisible] = useState(false);
   const [modalState, setModalState] = useState({
     selectedFirstYear: 2021,
@@ -120,6 +119,31 @@ const MapScreen: React.FC<MapScreenProps> = ({ navigation }) => {
   ];
   const [type, setType] = useState(0);
 
+  const { handleUpdateNM, handleUpdateDare, userData, setUserData } = useRegion();
+
+  useFocusEffect(
+    useCallback(() => {
+      const updateMarkers = async () => {
+        await mutateAsync(
+          { regions: JSON.stringify([regionData?.id]), token: String(token) },
+          {
+            onSuccess: (data) => {
+              setSeries(data.series);
+
+              const allMarkers = data.items.map(processMarkerData);
+              setProcessedMarkers(allMarkers);
+              setMarkers(allMarkers);
+            }
+          }
+        );
+      };
+
+      if (userData && userData?.type === 'nm') {
+        updateMarkers();
+      }
+    }, [userData])
+  );
+
   useEffect(() => {
     if (!dareData) {
       const fetchData = async () => {
@@ -613,56 +637,6 @@ const MapScreen: React.FC<MapScreenProps> = ({ navigation }) => {
     });
   };
 
-  const handleUpdateNM = useCallback(
-    (region: number, first: number, last: number, visits: number, quality: number) => {
-      if (!token) {
-        setIsWarningModalVisible(true);
-        return;
-      }
-      const updatedNM = {
-        ...userData,
-        first_visit_year: first,
-        last_visit_year: last,
-        best_visit_quality: quality,
-        no_of_visits: visits,
-        visited: visits > 0 ? true : false
-      };
-
-      const updatedNMData = {
-        token,
-        region,
-        first,
-        last,
-        visits,
-        quality
-      };
-
-      updateNM(updatedNMData);
-      updatedNM && setUserData(updatedNM);
-    },
-    [userData, token]
-  );
-
-  const handleUpdateDare = useCallback(
-    (region: number, visits: 0 | 1) => {
-      if (!token) {
-        setIsWarningModalVisible(true);
-        return;
-      }
-      const updatedDARE = { ...userData, visited: visits > 0 ? true : false };
-
-      const updatedDareData = {
-        token,
-        region,
-        visits
-      };
-
-      updateDARE(updatedDareData);
-      updatedDARE && setUserData(updatedDARE);
-    },
-    [userData, token]
-  );
-
   const handlePress = () => {
     if (isExpanded) {
       setIndex(0);
@@ -766,8 +740,20 @@ const MapScreen: React.FC<MapScreenProps> = ({ navigation }) => {
             userAvatars={userAvatars}
             userData={userData}
             openEditModal={handleOpenEditModal}
-            updateNM={handleUpdateNM}
-            updateDare={handleUpdateDare}
+            updateNM={(id, first, last, visits, quality) => {
+              if (!token) {
+                setIsWarningModalVisible(true);
+                return;
+              }
+              handleUpdateNM(id, first, last, visits, quality);
+            }}
+            updateDare={(id, visits) => {
+              if (!token) {
+                setIsWarningModalVisible(true);
+                return;
+              }
+              handleUpdateDare(id, visits);
+            }}
             disabled={!token || !isConnected}
           />
         </>

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

@@ -63,17 +63,6 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
   );
 
   const handleGoBack = () => {
-    navigation.getParent()?.setOptions({
-      tabBarStyle: {
-        display: 'flex',
-        position: 'absolute',
-        ...Platform.select({
-          android: {
-            height: 58
-          }
-        })
-      }
-    });
     navigation.goBack();
   };
 

+ 26 - 3
src/screens/InAppScreens/ProfileScreen/index.tsx

@@ -1,6 +1,11 @@
-import React, { FC, ReactNode, useState } from 'react';
-import { Linking, ScrollView, Text, TouchableOpacity, View, Image } from 'react-native';
-import { CommonActions, NavigationProp, useNavigation } from '@react-navigation/native';
+import React, { FC, ReactNode, useCallback, useState } from 'react';
+import { Linking, ScrollView, Text, TouchableOpacity, View, Image, Platform } from 'react-native';
+import {
+  CommonActions,
+  NavigationProp,
+  useFocusEffect,
+  useNavigation
+} from '@react-navigation/native';
 import Modal from 'react-native-modal';
 import Tooltip from 'react-native-walkthrough-tooltip';
 
@@ -57,6 +62,24 @@ const ProfileScreen: FC<Props> = ({ navigation, route }) => {
     ? usePostGetProfileInfoPublicQuery(route.params?.userId, true)
     : usePostGetProfileInfoQuery(token, true);
 
+  useFocusEffect(
+    useCallback(() => {
+      if (!route.params?.hideTabBar) {
+        navigation.getParent()?.setOptions({
+          tabBarStyle: {
+            display: 'flex',
+            position: 'absolute',
+            ...Platform.select({
+              android: {
+                height: 58
+              }
+            })
+          }
+        });
+      }
+    }, [navigation])
+  );
+
   if (!data || isFetching) return <Loading />;
 
   const handleGoToMap = () => {

+ 2 - 2
src/screens/InAppScreens/TravellersScreen/Components/Profile.tsx

@@ -23,12 +23,12 @@ import { useConnection } from 'src/contexts/ConnectionContext';
 import Tooltip from 'react-native-walkthrough-tooltip';
 
 type Props = {
-  avatar: string;
+  avatar: string | null;
   first_name: string;
   last_name: string;
   date_of_birth: number;
   homebase_flag: string;
-  homebase2_flag: string;
+  homebase2_flag: string | null;
   index: number;
   score: any[];
   active_score: number;

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

@@ -111,18 +111,18 @@ export interface Ranking {
   score_unp: number;
   score_tcc: number;
   score_deep: number;
-  score_whs: number;
+  score_whs: number | null;
   score_kye: number;
   score_tbt: number;
   score_yes: number;
   score_slow: number;
   rank_tbt: number;
-  avatar: string;
+  avatar: string | null;
   first_name: string;
   last_name: string;
   age: number;
   flag1: string;
-  flag2: string;
+  flag2: string | null;
   badge_1281: number;
   badge_un: number;
   badge_supreme: number;

+ 1 - 1
src/screens/InAppScreens/TravellersScreen/utils/types.ts

@@ -5,7 +5,7 @@ export type filterAgeType = {
   max: number;
 } | null;
 
-export type filterRankingType = { value: number; label: string } | null;
+export type filterRankingType = { value: number; label: string, name: string } | null;
 
 export type filterCountryType = { two: string; name: string } | null;
 

+ 11 - 11
src/screens/InAppScreens/TravellersScreen/utils/values.ts

@@ -9,15 +9,15 @@ export const dataAge = [
 ];
 
 export const dataRanking = [
-  { label: 'NM', value: 1 },
-  { label: 'DARE', value: 2 },
-  { label: 'UN', value: 3 },
-  { label: 'UN+', value: 4 },
-  { label: 'TCC', value: 5 },
-  { label: 'DEEP', value: 6 },
-  { label: 'YES', value: 7 },
-  { label: 'SLOW', value: 8 },
-  { label: 'WHS', value: 9 },
-  { label: 'KYE', value: 10 },
-  { label: 'TBT', value: 11 }
+  { label: 'NM', value: 1, name: 'nm' },
+  { label: 'DARE', value: 2, name: 'dare' },
+  { label: 'UN', value: 3, name: 'un' },
+  { label: 'UN+', value: 4, name: 'unp' },
+  { label: 'TCC', value: 5, name: 'tcc' },
+  { label: 'DEEP', value: 6, name: 'deep' },
+  { label: 'YES', value: 7, name: 'yes' },
+  { label: 'SLOW', value: 8, name: 'slow' },
+  { label: 'WHS', value: 9, name: 'whs' },
+  { label: 'KYE', value: 10, name: 'kye' },
+  { label: 'TBT', value: 11, name: 'tbt' }
 ];

+ 1 - 1
src/screens/InAppScreens/TravelsScreen/Components/AccordionListItem.tsx

@@ -79,7 +79,7 @@ export const AccordionListItem = React.memo(
           </View>
 
           <View style={styles.chevronContainer}>
-            <ChevronIcon style={[styles.headerIcon, isExpanded ? styles.rotate : null]} />
+            <ChevronIcon fill={Colors.DARK_BLUE} style={[styles.headerIcon, isExpanded ? styles.rotate : null]} />
           </View>
         </TouchableOpacity>
 

+ 1 - 1
src/screens/InAppScreens/TravelsScreen/Components/MyRegionsItems/NmRegionItem.tsx

@@ -65,7 +65,7 @@ export const NmRegionItem = React.memo(
         <View style={styles.divider} />
 
         <View style={styles.regionItemContent}>
-          {item.visits > 0 && item.year > 1 && (
+          {item.visits > 0 && item.year > 1 && token && (
             <View style={styles.infoContent}>
               <CalendarSvg height={18} width={18} fill={Colors.DARK_BLUE} />
               <Text style={styles.visitedButtonText}>{item.year}</Text>

+ 52 - 35
src/screens/InAppScreens/TravelsScreen/DareScreen/index.tsx

@@ -1,5 +1,5 @@
 import React, { useCallback, useEffect, useState } from 'react';
-import { View, Text, TouchableOpacity, FlatList } from 'react-native';
+import { View, Text, TouchableOpacity, FlatList, Platform } from 'react-native';
 import * as Progress from 'react-native-progress';
 import { useFocusEffect, useNavigation } from '@react-navigation/native';
 
@@ -19,11 +19,11 @@ import {
 } from '@api/myDARE';
 
 import ChevronIcon from 'assets/icons/travels-screens/down-arrow.svg';
-import InfoIcon from 'assets/icons/info-solid.svg';
 import { NAVIGATION_PAGES } from 'src/types';
+import { useRegion } from 'src/contexts/RegionContext';
 
 const DareScreen = () => {
-  const token = storage.get('token', StoreType.STRING) as string || '';
+  const token = (storage.get('token', StoreType.STRING) as string) || '';
   const { data: megaregions } = useGetMegaregionsDareQuery(token, true);
   const [megaSelectorVisible, setMegaSelectorVisible] = useState(false);
   const [selectedMega, setSelectedMega] = useState<{ name: string; id: number }>({
@@ -31,12 +31,16 @@ const DareScreen = () => {
     name: 'SOUTHERN EUROPE'
   });
   const { data: dareData } = useGetRegionDareQuery(selectedMega.id, String(token), true);
-  const [dareRegions, setDareRegions] = useState<DareRegion[] | null>(null);
   const [filteredDareRegions, setFilteredDareRegions] = useState<DareRegion[] | null>(null);
   const [total, setTotal] = useState(0);
   const [contentIndex, setContentIndex] = useState(0);
-  const { mutate: updateDARE } = usePostSetDareRegionMutation();
   const navigation = useNavigation();
+  const {
+    handleUpdateDareList: handleUpdateDare,
+    dareRegions,
+    setDareRegions,
+    setUserData
+  } = useRegion();
 
   useFocusEffect(
     useCallback(() => {
@@ -46,6 +50,22 @@ const DareScreen = () => {
     }, [megaregions])
   );
 
+  useFocusEffect(
+    useCallback(() => {
+      navigation.getParent()?.setOptions({
+        tabBarStyle: {
+          display: 'flex',
+          position: 'absolute',
+          ...Platform.select({
+            android: {
+              height: 58
+            }
+          })
+        }
+      });
+    }, [navigation])
+  );
+
   useEffect(() => {
     if (dareRegions && dareRegions.length) {
       token && calcTotalCountries();
@@ -67,13 +87,13 @@ const DareScreen = () => {
   useEffect(() => {
     switch (contentIndex) {
       case 1:
-        setFilteredDareRegions(dareRegions?.filter((item) => +item.visited <= 0) || []);
+        setFilteredDareRegions(dareRegions?.filter((item: DareRegion) => +item.visited <= 0) || []);
         break;
       case 2:
-        setFilteredDareRegions(dareRegions?.filter((item) => +item.visited > 0) || []);
+        setFilteredDareRegions(dareRegions?.filter((item: DareRegion) => +item.visited > 0) || []);
         break;
       case 3:
-        setFilteredDareRegions(dareRegions?.filter((item) => item.new === 1) || []);
+        setFilteredDareRegions(dareRegions?.filter((item: DareRegion) => item.new === 1) || []);
         break;
       default:
         setFilteredDareRegions(dareRegions);
@@ -81,37 +101,34 @@ const DareScreen = () => {
   }, [contentIndex, dareRegions]);
 
   const calcTotalCountries = () => {
-    const visited = dareRegions?.filter((item) => +item.visited > 0).length || 0;
+    const visited = dareRegions?.filter((item: DareRegion) => +item.visited > 0).length || 0;
     setTotal(visited);
   };
 
-  const handleUpdateDare = useCallback(
-    (region: number, visits: 0 | 1) => {
-      const updatedDARE = dareRegions?.map((item) => {
-        if (item.id === region) {
-          return {
-            ...item,
-            visited: String(visits)
-          };
-        }
-
-        return item;
-      });
-
-      const updatedDareData = {
-        token,
-        region,
-        visits
-      };
-
-      updateDARE(updatedDareData);
-      updatedDARE && setDareRegions(updatedDARE);
-    },
-    [dareRegions]
-  );
-
   const renderItem = ({ item }: { item: DareRegion }) => (
-    <RegionItem item={item} dare={true} updateRegion={handleUpdateDare} token={token} />
+    <TouchableOpacity
+      onPress={() => {
+        setUserData({
+          type: 'dare',
+          region_flag: item.flag1,
+          region_name: item.name,
+          visited: +item.visited > 0
+        });
+        navigation.navigate(
+          ...([
+            NAVIGATION_PAGES.REGION_PREVIEW,
+            {
+              regionId: item.id,
+              isTravelsScreen: true,
+              type: 'dare',
+              disabled: token ? false : true
+            }
+          ] as never)
+        );
+      }}
+    >
+      <RegionItem item={item} dare={true} updateRegion={handleUpdateDare} token={token} />
+    </TouchableOpacity>
   );
 
   return (

+ 51 - 45
src/screens/InAppScreens/TravelsScreen/RegionsScreen/index.tsx

@@ -1,5 +1,5 @@
 import React, { useCallback, useEffect, useState } from 'react';
-import { View, Text, TouchableOpacity, FlatList } from 'react-native';
+import { View, Text, TouchableOpacity, FlatList, Platform } from 'react-native';
 import * as Progress from 'react-native-progress';
 import { useFocusEffect, useNavigation } from '@react-navigation/native';
 import moment from 'moment';
@@ -17,14 +17,13 @@ import { styles } from './styles';
 import {
   useGetMegaregionsQuery,
   useGetRegionQeQuery,
-  usePostSetNmRegionMutation,
   usePostSetTCCRegionMutation
 } from '@api/myRegions';
 import { qualityOptions } from '../utils/constants';
 
 import ChevronIcon from 'assets/icons/travels-screens/down-arrow.svg';
-import InfoIcon from 'assets/icons/info-solid.svg';
 import { NAVIGATION_PAGES } from 'src/types';
+import { useRegion } from 'src/contexts/RegionContext';
 
 const RegionsScreen = () => {
   const token = storage.get('token', StoreType.STRING) as string || '';
@@ -38,11 +37,9 @@ const RegionsScreen = () => {
   const [total, setTotal] = useState(0);
   const [isEditModalVisible, setIsEditModalVisible] = useState(false);
   const [contentIndex, setContentIndex] = useState(0);
-  const [nmRegions, setNmRegions] = useState<NmRegion[] | null>(null);
   const [filteredNmRegions, setFilteredNmRegions] = useState<NmRegion[] | null>(null);
   const [tccRegions, setTccRegions] = useState<TCCRegion[] | null>(null);
   const [filteredTccRegions, setFilteredTccRegions] = useState<TCCRegion[] | null>(null);
-  const { mutate: updateNM } = usePostSetNmRegionMutation();
   const { mutate: updateTCC } = usePostSetTCCRegionMutation();
   const [modalState, setModalState] = useState({
     selectedFirstYear: 2021,
@@ -53,6 +50,23 @@ const RegionsScreen = () => {
     id: null
   });
   const navigation = useNavigation();
+  const { handleUpdateNMList: handleUpdateNM, nmRegions, setNmRegions, setUserData } = useRegion();
+
+  useFocusEffect(
+    useCallback(() => {
+      navigation.getParent()?.setOptions({
+        tabBarStyle: {
+          display: 'flex',
+          position: 'absolute',
+          ...Platform.select({
+            android: {
+              height: 58
+            }
+          })
+        }
+      });
+    }, [navigation])
+  );
 
   useEffect(() => {
     const currentYear = moment().year();
@@ -79,37 +93,6 @@ const RegionsScreen = () => {
     setIsEditModalVisible(true);
   };
 
-  const handleUpdateNM = useCallback(
-    (id: number, first: number, last: number, visits: number, quality: number) => {
-      const updatedNM = nmRegions?.map((item) => {
-        if (item.id === id) {
-          return {
-            ...item,
-            year: first,
-            last,
-            quality,
-            visits
-          };
-        }
-
-        return item;
-      });
-
-      const updatedNMData = {
-        token,
-        region: id,
-        first,
-        last,
-        visits,
-        quality
-      };
-
-      updateNM(updatedNMData);
-      updatedNM && setNmRegions(updatedNM);
-    },
-    [nmRegions]
-  );
-
   const handleUpdateTCC = useCallback(
     (region: number, visits: 0 | 1) => {
       const updatedTCC = tccRegions?.map((item) => {
@@ -161,11 +144,11 @@ const RegionsScreen = () => {
         setFilteredTccRegions(tccRegions);
         break;
       case 1:
-        setFilteredNmRegions(nmRegions?.filter((item) => item.visits <= 0) || []);
+        setFilteredNmRegions(nmRegions?.filter((item: NmRegion) => item.visits <= 0) || []);
         setFilteredTccRegions(tccRegions?.filter((item) => item.visited <= 0) || []);
         break;
       case 2:
-        setFilteredNmRegions(nmRegions?.filter((item) => item.visits > 0) || []);
+        setFilteredNmRegions(nmRegions?.filter((item: NmRegion) => item.visits > 0) || []);
         setFilteredTccRegions(tccRegions?.filter((item) => item.visited > 0) || []);
         break;
     }
@@ -180,17 +163,40 @@ const RegionsScreen = () => {
   );
 
   const calcTotalCountries = () => {
-    const visited = nmRegions?.filter((item) => item.visits > 0).length || 0;
+    const visited = nmRegions?.filter((item: NmRegion) => item.visits > 0).length || 0;
     setTotal(visited);
   };
 
   const renderItem = ({ item }: { item: NmRegion }) => (
-    <NmRegionItem
-      item={item}
-      openEditModal={handleOpenEditModal}
-      updateNM={handleUpdateNM}
-      token={token}
-    />
+    <TouchableOpacity onPress={() => {
+      setUserData({
+        type: 'nm',
+        region_flag: item.flag_1,
+        region_name: item.region_name,
+        best_visit_quality: item.quality,
+        first_visit_year: item.year,
+        last_visit_year: item.last,
+        no_of_visits: item.visits,
+        visited: item.visits > 0,
+      });
+      navigation.navigate(
+      ...([
+        NAVIGATION_PAGES.REGION_PREVIEW,
+        {
+          regionId: item.id,
+          isTravelsScreen: true,
+          type: 'nm',
+          disabled: token ? false : true,
+        }
+      ] as never)
+    )}}>
+      <NmRegionItem
+        item={item}
+        openEditModal={handleOpenEditModal}
+        updateNM={handleUpdateNM}
+        token={token}
+      />
+    </TouchableOpacity>
   );
 
   return (

+ 3 - 1
src/screens/InAppScreens/TravelsScreen/utils/formatNumber.ts

@@ -1,6 +1,8 @@
 function formatNumber(number: number) {
-  if (number >= 1000) {
+  if (number >= 1000 && number < 10000) {
     return (number / 1000).toFixed(1) + 'k';
+  } else if (number >= 10000) {
+    return (number / 1000).toFixed(0) + 'k';
   }
   return number.toString();
 }

+ 14 - 2
src/types/api.ts

@@ -84,7 +84,13 @@ export enum API_ENDPOINT {
   GET_LAST_DARE_UPDATE = 'last-dare-db-update',
   GET_SERVERS = 'get-servers',
   GET_PROFILE_REGIONS = 'get-profile',
-  GET_UNIVERSAL = 'universal'
+  GET_UNIVERSAL = 'universal',
+  GET_REGIONS_DATA = 'get-app-region-screen-data',
+  GET_NM_REGION_DATA = 'get-nm-region-screen-data',
+  GET_DARE_REGION_DATA = 'get-dare-region-screen-data',
+  GET_USERS_FROM_REGION = 'get-users-from-region',
+  GET_USERS_WHO_VISITED_REGION = 'get-users-who-visited-region',
+  GET_USERS_WHO_VISITED_DARE = 'get-users-who-visited-dare',
 }
 
 export enum API {
@@ -151,7 +157,13 @@ export enum API {
   GET_LAST_DARE_DB_UPDATE = `${API_ROUTE.APP}/${API_ENDPOINT.GET_LAST_DARE_UPDATE}`,
   GET_SERVERS = `${API_ROUTE.APP}/${API_ENDPOINT.GET_SERVERS}`,
   GET_PROFILE_REGIONS = `${API_ROUTE.REGIONS}/${API_ENDPOINT.GET_PROFILE_REGIONS}`,
-  GET_UNIVERSAL = `${API_ROUTE.SEARCH}/${API_ENDPOINT.GET_UNIVERSAL}`
+  GET_UNIVERSAL = `${API_ROUTE.SEARCH}/${API_ENDPOINT.GET_UNIVERSAL}`,
+  GET_REGIONS_DATA = `${API_ROUTE.REGIONS}/${API_ENDPOINT.GET_REGIONS_DATA}`,
+  GET_NM_REGION_DATA = `${API_ROUTE.REGIONS}/${API_ENDPOINT.GET_NM_REGION_DATA}`,
+  GET_DARE_REGION_DATA = `${API_ROUTE.REGIONS}/${API_ENDPOINT.GET_DARE_REGION_DATA}`,
+  GET_USERS_FROM_REGION = `${API_ROUTE.REGIONS}/${API_ENDPOINT.GET_USERS_FROM_REGION}`,
+  GET_USERS_WHO_VISITED_REGION = `${API_ROUTE.REGIONS}/${API_ENDPOINT.GET_USERS_WHO_VISITED_REGION}`,
+  GET_USERS_WHO_VISITED_DARE = `${API_ROUTE.REGIONS}/${API_ENDPOINT.GET_USERS_WHO_VISITED_DARE}`,
 }
 
 export type BaseAxiosError = AxiosError;

+ 3 - 0
src/types/navigation.ts

@@ -50,4 +50,7 @@ export enum NAVIGATION_PAGES {
   SERIES_RANKING = 'inAppSeriesRanking',
   SERIES_RANKING_LIST = 'inAppSeriesRankingList',
   USERS_MAP = 'inAppUsersMap',
+  REGION_PREVIEW = 'inAppRegionPreview',
+  USERS_LIST = 'inAppUsersList',
+  SUGGEST_SERIES = 'inAppSuggestSeries',
 }