Browse Source

intro screens

Viktoriia 1 năm trước cách đây
mục cha
commit
4a9cf79460
28 tập tin đã thay đổi với 605 bổ sung64 xóa
  1. 8 0
      Route.tsx
  2. 13 0
      assets/icons/info-solid.svg
  3. BIN
      assets/images/map.jpeg
  4. BIN
      assets/images/nm-background.png
  5. 21 11
      src/components/Header/index.tsx
  6. 57 0
      src/screens/DiscoverInfoScreen/index.tsx
  7. 39 0
      src/screens/DiscoverInfoScreen/styles.tsx
  8. 1 1
      src/screens/InAppScreens/MapScreen/FilterModal/index.tsx
  9. 8 1
      src/screens/InAppScreens/MapScreen/UniversalSearch/index.tsx
  10. 14 10
      src/screens/InAppScreens/MapScreen/index.tsx
  11. 16 0
      src/screens/InAppScreens/MapScreen/style.tsx
  12. 16 2
      src/screens/InAppScreens/TravellersScreen/index.tsx
  13. 21 3
      src/screens/InAppScreens/TravelsScreen/CountriesScreen/index.tsx
  14. 15 2
      src/screens/InAppScreens/TravelsScreen/DareScreen/index.tsx
  15. 16 2
      src/screens/InAppScreens/TravelsScreen/EarthScreen/index.tsx
  16. 27 4
      src/screens/InAppScreens/TravelsScreen/RegionsScreen/index.tsx
  17. 12 1
      src/screens/InAppScreens/TravelsScreen/TripsScreen/index.tsx
  18. 16 2
      src/screens/InAppScreens/TravelsScreen/index.tsx
  19. 87 0
      src/screens/InfoScreen/index.tsx
  20. 43 0
      src/screens/InfoScreen/styles.tsx
  21. 35 0
      src/screens/JoinInfoScreen/index.tsx
  22. 10 0
      src/screens/JoinInfoScreen/styles.tsx
  23. 18 8
      src/screens/LoginScreen/index.tsx
  24. 17 7
      src/screens/RegisterScreen/EditAccount/index.tsx
  25. 55 0
      src/screens/TravelsInfoScreen/index.tsx
  26. 36 0
      src/screens/TravelsInfoScreen/styles.tsx
  27. 4 0
      src/types/navigation.ts
  28. 0 10
      src/utils/mapHelpers.ts

+ 8 - 0
Route.tsx

@@ -58,6 +58,10 @@ import setupDatabaseAndSync from 'src/database';
 import { MenuDrawer } from 'src/components';
 import { setFastestMapHost } from 'src/constants';
 import { determineFastestServer } from 'src/utils/determineServer';
+import InfoScreen from 'src/screens/InfoScreen';
+import JoinInfoScreen from 'src/screens/JoinInfoScreen';
+import DiscoverInfoScreen from 'src/screens/DiscoverInfoScreen';
+import TravelsInfoScreen from 'src/screens/TravelsInfoScreen';
 
 const ScreenStack = createStackNavigator();
 const BottomTab = createBottomTabNavigator();
@@ -174,6 +178,10 @@ const Route = () => {
         name={NAVIGATION_PAGES.RESET_PASSWORD_DEEP}
         component={ResetPasswordDeepScreen}
       />
+      <ScreenStack.Screen name={NAVIGATION_PAGES.INFO} component={InfoScreen} />
+      <ScreenStack.Screen name={NAVIGATION_PAGES.JOIN_INFO} component={JoinInfoScreen} />
+      <ScreenStack.Screen name={NAVIGATION_PAGES.DISCOVER_INFO} component={DiscoverInfoScreen} />
+      <ScreenStack.Screen name={NAVIGATION_PAGES.TRAVELS_INFO} component={TravelsInfoScreen} />
       <ScreenStack.Screen name={NAVIGATION_PAGES.IN_APP}>
         {() => (
           <BottomTab.Navigator screenOptions={screenOptions}>

+ 13 - 0
assets/icons/info-solid.svg

@@ -0,0 +1,13 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_3150_62387)">
+<path d="M12 24C5.38595 24 0 18.6141 0 12C0 5.38595 5.38595 0 12 0C18.6141 0 24 5.38596 24 12C24 18.6141 18.6141 24 12 24ZM12 21.7194C17.3816 21.7194 21.7194 17.3816 21.7194 12C21.7194 6.6185 17.3816 2.28062 12 2.28062C6.6185 2.28062 2.28062 6.6185 2.28062 12C2.28062 17.3816 6.6185 21.7194 12 21.7194Z" fill="#0F3F4F"/>
+<path d="M11.9998 7.86572C11.7067 7.86572 11.4255 7.74613 11.2183 7.53325C11.011 7.32037 10.8945 7.03164 10.8945 6.73059C10.8945 6.42953 11.011 6.14081 11.2183 5.92793C11.4255 5.71505 11.7067 5.59545 11.9998 5.59545H12.0121C12.3053 5.59545 12.5864 5.71505 12.7937 5.92793C13.0009 6.14081 13.1174 6.42953 13.1174 6.73059C13.1174 7.03164 13.0009 7.32037 12.7937 7.53325C12.5864 7.74613 12.3053 7.86572 12.0121 7.86572H11.9998Z" fill="#0F3F4F"/>
+<path d="M11.9998 16.5136C11.7067 16.5136 11.4255 16.394 11.2183 16.1811C11.011 15.9683 10.8945 15.6795 10.8945 15.3785V11.0546C10.8945 10.7535 11.011 10.4648 11.2183 10.2519C11.4255 10.039 11.7067 9.91943 11.9998 9.91943C12.2929 9.91943 12.5741 10.039 12.7813 10.2519C12.9886 10.4648 13.1051 10.7535 13.1051 11.0546V15.3785C13.1051 15.6795 12.9886 15.9683 12.7813 16.1811C12.5741 16.394 12.2929 16.5136 11.9998 16.5136Z" fill="#0F3F4F"/>
+<path d="M11.9998 18.5136C11.7067 18.5136 11.4255 18.394 11.2183 18.1811C11.011 17.9683 10.8945 17.6795 10.8945 17.3785V13.0546C10.8945 12.7535 11.011 12.4648 11.2183 12.2519C11.4255 12.039 11.7067 11.9194 11.9998 11.9194C12.2929 11.9194 12.5741 12.039 12.7813 12.2519C12.9886 12.4648 13.1051 12.7535 13.1051 13.0546V17.3785C13.1051 17.6795 12.9886 17.9683 12.7813 18.1811C12.5741 18.394 12.2929 18.5136 11.9998 18.5136Z" fill="#0F3F4F"/>
+</g>
+<defs>
+<clipPath id="clip0_3150_62387">
+<rect width="24" height="24" fill="white" transform="matrix(1 0 0 -1 0 24)"/>
+</clipPath>
+</defs>
+</svg>

BIN
assets/images/map.jpeg


BIN
assets/images/nm-background.png


+ 21 - 11
src/components/Header/index.tsx

@@ -1,34 +1,44 @@
 import React, { FC, ReactNode } from 'react';
 import { Text, TouchableOpacity, View } from 'react-native';
-import { useNavigation } from '@react-navigation/native';
+import { CommonActions, useNavigation } from '@react-navigation/native';
 
 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';
 
 type Props = {
   label: string;
   rightElement?: ReactNode;
+  shouldClose?: boolean;
 };
 
-export const Header: FC<Props> = ({ label, rightElement }) => {
+export const Header: FC<Props> = ({ label, rightElement, shouldClose }) => {
   const navigation = useNavigation();
 
+  const handlePress = () => {
+    if (shouldClose) {
+      navigation.dispatch(
+        CommonActions.reset({
+          index: 1,
+          routes: [{ name: NAVIGATION_PAGES.IN_APP }]
+        })
+      );
+    } else {
+      navigation.goBack();
+    }
+  };
+
   // navigation.setOptions() TODO
 
   return (
     <View style={styles.wrapper}>
-      <TouchableOpacity onPress={() => navigation.goBack()}>
-        <View style={styles.chevronWrapper}>
-          <ChevronLeft />
-        </View>
+      <TouchableOpacity onPress={handlePress}>
+        <View style={styles.chevronWrapper}>{shouldClose ? <CloseSvg /> : <ChevronLeft />}</View>
       </TouchableOpacity>
       <Text style={styles.label}>{label}</Text>
-      {rightElement ? (
-        <View>{rightElement}</View>
-      ) : (
-        <View style={styles.placeholder} />
-      )}
+      {rightElement ? <View>{rightElement}</View> : <View style={styles.placeholder} />}
     </View>
   );
 };

+ 57 - 0
src/screens/DiscoverInfoScreen/index.tsx

@@ -0,0 +1,57 @@
+import { FC } from 'react';
+import { ImageBackground, View, Text, Image, ScrollView } from 'react-native';
+import type { NavigationProp } from '@react-navigation/native';
+
+import { Header, PageWrapper } from '../../components/';
+import { styles } from './styles';
+import { Colors } from 'src/theme';
+import TravellersIcon from 'assets/icons/bottom-navigation/travellers.svg';
+
+type Props = {
+  navigation: NavigationProp<any>;
+};
+
+const DiscoverInfoScreen: FC<Props> = ({ navigation }) => {
+  return (
+    <PageWrapper>
+      <Header label={'Discover the World'} />
+      <ImageBackground
+        style={{ height: '100%' }}
+        source={require('../../../assets/images/nm-background.png')}
+      >
+        <ScrollView
+          style={styles.wrapper}
+          showsVerticalScrollIndicator={false}
+          contentContainerStyle={{ gap: 16 }}
+        >
+          <View style={{ gap: 10 }}>
+            <Text style={styles.title}>Use the map to explore</Text>
+            <View style={styles.infoBlock}>
+              <View style={styles.imgContainer}>
+                <Image source={require('../../../assets/images/map.jpeg')} style={styles.image} />
+              </View>
+              <Text style={styles.text}>
+                Tap a region to see more details. Zoom the map to reveal our series.
+              </Text>
+            </View>
+          </View>
+
+          <View style={{ gap: 10 }}>
+            <Text style={styles.title}>User Travels section</Text>
+            <View style={styles.infoBlock}>
+              <View style={[styles.imgContainer, { width: 80, height: 80 }]}>
+                <TravellersIcon fill={Colors.DARK_BLUE} />
+                <Text style={styles.imgText}>Travellers</Text>
+              </View>
+              <Text style={styles.text}>
+                Use our Travels section to explore information in a structured layout.
+              </Text>
+            </View>
+          </View>
+        </ScrollView>
+      </ImageBackground>
+    </PageWrapper>
+  );
+};
+
+export default DiscoverInfoScreen;

+ 39 - 0
src/screens/DiscoverInfoScreen/styles.tsx

@@ -0,0 +1,39 @@
+import { StyleSheet } from 'react-native';
+import { Colors } from '../../theme';
+
+export const styles = StyleSheet.create({
+  wrapper: { height: '100%', display: 'flex' },
+  title: {
+    fontFamily: 'montserrat-700',
+    fontSize: 16,
+    color: Colors.DARK_BLUE
+  },
+  text: {
+    fontWeight: '600',
+    color: Colors.DARK_BLUE,
+    flex: 1
+  },
+  imgContainer: {
+    width: 130,
+    height: 130,
+    borderRadius: 8,
+    backgroundColor: Colors.WHITE,
+    alignItems: 'center',
+    justifyContent: 'center',
+    gap: 6,
+    shadowColor: '#000',
+    shadowOffset: { width: 0, height: 0 },
+    shadowOpacity: 0.15,
+    shadowRadius: 8,
+    elevation: 8
+  },
+  image: {
+    width: 130,
+    height: 130,
+    borderRadius: 8,
+    borderWidth: 2,
+    borderColor: Colors.WHITE
+  },
+  infoBlock: { flexDirection: 'row', gap: 8 },
+  imgText: { fontSize: 10, color: Colors.DARK_BLUE }
+});

+ 1 - 1
src/screens/InAppScreens/MapScreen/FilterModal/index.tsx

@@ -44,7 +44,7 @@ const FilterModal = ({
           </View>
           <View style={{ display: 'flex', alignItems: 'center' }}>
             <Text style={{ color: Colors.DARK_BLUE, fontSize: 20, fontWeight: '700' }}>
-              Map filter
+              Map filter show visited
             </Text>
             <View style={[ModalStyles.ageAndRankingWrapper, { marginTop: 24 }]}>
               <Dropdown

+ 8 - 1
src/screens/InAppScreens/MapScreen/UniversalSearch/index.tsx

@@ -56,7 +56,14 @@ const SearchModal = ({
         }}
       >
         <View style={styles.container}>
-          {item.avatar && <Image source={{ uri: API_HOST + item.avatar }} style={styles.img} />}
+          <Image
+            source={
+              item.avatar
+                ? { uri: API_HOST + item.avatar }
+                : require('../../../../../assets/logo-ua.png')
+            }
+            style={styles.img}
+          />
           <View style={styles.textContainer}>
             <Text style={styles.title}>{item.name}</Text>
             <View style={{ flexDirection: 'row', alignItems: 'center' }}>

+ 14 - 10
src/screens/InAppScreens/MapScreen/index.tsx

@@ -14,11 +14,9 @@ import * as turf from '@turf/turf';
 import * as FileSystem from 'expo-file-system';
 import * as Location from 'expo-location';
 import { storage, StoreType } from '../../../storage';
-import Modal from 'react-native-modal';
 
 import MenuIcon from '../../../../assets/icons/menu.svg';
 import SearchIcon from '../../../../assets/icons/search.svg';
-import RadarIcon from '../../../../assets/icons/radar.svg';
 import LocationIcon from '../../../../assets/icons/location.svg';
 import CloseSvg from '../../../../assets/icons/close.svg';
 import FilterIcon from 'assets/icons/filter.svg';
@@ -35,7 +33,6 @@ import {
   filterCandidates,
   filterCandidatesMarkers,
   findRegionInDataset,
-  processIconUrl,
   processMarkerData
 } from '../../../utils/mapHelpers';
 import { getData } from '../../../modules/map/regionData';
@@ -43,7 +40,7 @@ import { fetchSeriesData, usePostSetToggleItem } from '@api/series';
 import MarkerItem from './MarkerItem';
 import ClusterItem from './ClusterItem';
 import { FeatureCollection, ItemSeries, MapScreenProps, Region, Series } from '../../../types/map';
-import { FASTEST_MAP_HOST } from 'src/constants';
+import { API_HOST, FASTEST_MAP_HOST } from 'src/constants';
 import { useConnection } from 'src/contexts/ConnectionContext';
 import ClusteredMapView from 'react-native-map-clustering';
 import { fetchUserData, fetchUserDataDare } from '@api/regions';
@@ -57,10 +54,9 @@ import { useSharedValue, useAnimatedStyle, withTiming } from 'react-native-reani
 import { Colors } from 'src/theme';
 import { useGetUniversalSearch } from '@api/search';
 import SearchModal from './UniversalSearch';
-import { Dropdown } from 'react-native-searchable-dropdown-kj';
-import { ButtonVariants } from 'src/types/components';
-import { ModalStyles } from '../TravellersScreen/Components/styles';
 import FilterModal from './FilterModal';
+import InfoIcon from 'assets/icons/info-solid.svg';
+import { NAVIGATION_PAGES } from 'src/types';
 
 const localTileDir = `${FileSystem.cacheDirectory}tiles/background`;
 const localGridDir = `${FileSystem.cacheDirectory}tiles/grid`;
@@ -599,7 +595,7 @@ const MapScreen: React.FC<MapScreenProps> = ({ navigation }) => {
     return markers.map((marker, idx) => {
       const coordinate = { latitude: marker.pointJSON[0], longitude: marker.pointJSON[1] };
       const markerSeries = series?.find((s) => s.id === marker.series_id);
-      const iconUrl = markerSeries ? processIconUrl(markerSeries.icon) : 'default_icon_url';
+      const iconUrl = markerSeries ? API_HOST + markerSeries.icon : 'default_icon_url';
       const seriesName = markerSeries ? markerSeries.name : 'Unknown';
 
       return (
@@ -785,6 +781,13 @@ const MapScreen: React.FC<MapScreenProps> = ({ navigation }) => {
             </TouchableOpacity>
           ) : null}
 
+          <TouchableOpacity
+            style={styles.cornerInfoButton}
+            onPress={() => navigation.navigate(NAVIGATION_PAGES.DISCOVER_INFO)}
+          >
+            <InfoIcon />
+          </TouchableOpacity>
+
           <Animated.View
             style={[
               styles.searchContainer,
@@ -820,7 +823,9 @@ const MapScreen: React.FC<MapScreenProps> = ({ navigation }) => {
 
           <TouchableOpacity
             style={[styles.cornerButton, styles.bottomButton, styles.bottomLeftButton]}
-            onPress={() => setIsFilterVisible(true)}
+            onPress={() => {
+              token ? setIsFilterVisible(true) : setIsWarningModalVisible(true);
+            }}
           >
             <FilterIcon />
           </TouchableOpacity>
@@ -862,7 +867,6 @@ const MapScreen: React.FC<MapScreenProps> = ({ navigation }) => {
         setTilesType={setTilesType}
         type={type}
         setType={setType}
-        
       />
     </View>
   );

+ 16 - 0
src/screens/InAppScreens/MapScreen/style.tsx

@@ -25,6 +25,22 @@ export const styles = StyleSheet.create({
     shadowRadius: 8,
     elevation: 5,
   },
+  cornerInfoButton: {
+    position: 'absolute',
+    backgroundColor: Colors.WHITE,
+    padding: 12,
+    width: 34,
+    height: 34,
+    borderRadius: 24,
+    alignItems: 'center',
+    justifyContent: 'center',
+    shadowColor: 'rgba(33, 37, 41, 0.12)',
+    shadowOffset: { width: 0, height: 4 },
+    shadowRadius: 8,
+    elevation: 5,
+    top: 115,
+    right: 23,
+  },
   topLeftButton: {
     top: 52,
     left: 16,

+ 16 - 2
src/screens/InAppScreens/TravellersScreen/index.tsx

@@ -14,6 +14,7 @@ import ChartPieIcon from '../../../../assets/icons/chart-pie.svg';
 import MemoriamIcon from '../../../../assets/icons/monument.svg';
 import ScrollIcon from '../../../../assets/icons/scroll.svg';
 import TrophyIcon from '../../../../assets/icons/trophy.svg';
+import InfoIcon from 'assets/icons/info-solid.svg';
 
 const TravellersScreen = () => {
   const navigation = useNavigation();
@@ -47,8 +48,15 @@ const TravellersScreen = () => {
 
   return (
     <PageWrapper>
-      <View style={{ alignItems: 'center', paddingVertical: 16 }}>
+      <View style={styles.header}>
+        <View style={{ width: 30 }} />
         <Text style={styles.title}>Travellers</Text>
+        <TouchableOpacity
+          onPress={() => navigation.navigate(NAVIGATION_PAGES.JOIN_INFO as never)}
+          style={{ width: 30 }}
+        >
+          <InfoIcon />
+        </TouchableOpacity>
       </View>
 
       <FlatList
@@ -86,7 +94,13 @@ const styles = StyleSheet.create({
     fontSize: 13,
     fontWeight: 'bold'
   },
-  title: { color: Colors.DARK_BLUE, fontSize: 14, fontWeight: '600' }
+  title: { color: Colors.DARK_BLUE, fontSize: 14, fontWeight: '600' },
+  header: {
+    alignItems: 'center',
+    paddingVertical: 16,
+    flexDirection: 'row',
+    justifyContent: 'space-between'
+  }
 });
 
 export interface Ranking {

+ 21 - 3
src/screens/InAppScreens/TravelsScreen/CountriesScreen/index.tsx

@@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useState } from 'react';
 import { View, Text, TouchableOpacity, ScrollView, FlatList } from 'react-native';
 import ReactModal from 'react-native-modal';
 import * as Progress from 'react-native-progress';
-import { useFocusEffect } from '@react-navigation/native';
+import { useFocusEffect, useNavigation } from '@react-navigation/native';
 
 import CountryItem from '../Components/CountryItem';
 import EditModal from '../Components/EditSlowModal';
@@ -17,6 +17,8 @@ import { styles } from './styles';
 import ChevronIcon from 'assets/icons/travels-screens/down-arrow.svg';
 import IngoSvg from 'assets/icons/travels-screens/info.svg';
 import SearchIcon from '../../../../../assets/icons/search.svg';
+import InfoIcon from 'assets/icons/info-solid.svg';
+import { NAVIGATION_PAGES } from 'src/types';
 
 const CountriesScreen = () => {
   const token = storage.get('token', StoreType.STRING) as string;
@@ -34,6 +36,7 @@ const CountriesScreen = () => {
   const [infoModalVisible, setInfoModalVisible] = useState(false);
   const [search, setSearch] = useState<string>('');
   const [filteredSlow, setFilteredSlow] = useState<SlowData[] | null>(null);
+  const navigation = useNavigation();
 
   const handleOpenEditModal = (item: SlowData) => {
     setCurrentItem(item);
@@ -136,12 +139,27 @@ const CountriesScreen = () => {
   );
 
   const renderCountryItem = ({ item }: { item: SlowData }) => (
-    <CountryItem item={item} updateSlow={handleUpdateSlow} openEditModal={handleOpenEditModal} token={token} />
+    <CountryItem
+      item={item}
+      updateSlow={handleUpdateSlow}
+      openEditModal={handleOpenEditModal}
+      token={token}
+    />
   );
 
   return (
     <PageWrapper>
-      <Header label="Countries" />
+      <Header
+        label="Countries"
+        rightElement={
+          <TouchableOpacity
+            onPress={() => navigation.navigate(NAVIGATION_PAGES.TRAVELS_INFO as never)}
+            style={{ width: 30 }}
+          >
+            <InfoIcon />
+          </TouchableOpacity>
+        }
+      />
       <TouchableOpacity style={styles.megaSelector} onPress={() => setMegaSelectorVisible(true)}>
         <Text style={styles.megaButtonText}>{selectedMega.name}</Text>
         <ChevronIcon width={18} height={18} />

+ 15 - 2
src/screens/InAppScreens/TravelsScreen/DareScreen/index.tsx

@@ -1,7 +1,7 @@
 import React, { useCallback, useEffect, useState } from 'react';
 import { View, Text, TouchableOpacity, FlatList } from 'react-native';
 import * as Progress from 'react-native-progress';
-import { useFocusEffect } from '@react-navigation/native';
+import { useFocusEffect, useNavigation } from '@react-navigation/native';
 
 import { Header, PageWrapper } from 'src/components';
 import { CustomButton } from '../Components';
@@ -19,6 +19,8 @@ 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';
 
 const DareScreen = () => {
   const token = storage.get('token', StoreType.STRING) as string;
@@ -34,6 +36,7 @@ const DareScreen = () => {
   const [total, setTotal] = useState(0);
   const [contentIndex, setContentIndex] = useState(0);
   const { mutate: updateDARE } = usePostSetDareRegionMutation();
+  const navigation = useNavigation();
 
   useFocusEffect(
     useCallback(() => {
@@ -113,7 +116,17 @@ const DareScreen = () => {
 
   return (
     <PageWrapper>
-      <Header label="DARE" />
+      <Header
+        label="DARE"
+        rightElement={
+          <TouchableOpacity
+            onPress={() => navigation.navigate(NAVIGATION_PAGES.TRAVELS_INFO as never)}
+            style={{ width: 30 }}
+          >
+            <InfoIcon />
+          </TouchableOpacity>
+        }
+      />
       <TouchableOpacity style={styles.megaSelector} onPress={() => setMegaSelectorVisible(true)}>
         <Text style={styles.megaButtonText}>{selectedMega?.name}</Text>
         <ChevronIcon width={18} height={18} />

+ 16 - 2
src/screens/InAppScreens/TravelsScreen/EarthScreen/index.tsx

@@ -1,5 +1,5 @@
 import React, { useEffect, useState } from 'react';
-import { View, Platform, SafeAreaView, Text } from 'react-native';
+import { View, Platform, SafeAreaView, Text, TouchableOpacity } from 'react-native';
 import MapView, { Geojson, UrlTile } from 'react-native-maps';
 
 import kye from '../../../../../assets/geojson/kye.json';
@@ -10,6 +10,9 @@ import { StoreType, storage } from 'src/storage';
 import { FeatureCollection } from '@turf/turf';
 import { FASTEST_MAP_HOST } from 'src/constants';
 import { styles } from './styles';
+import InfoIcon from 'assets/icons/info-solid.svg';
+import { NAVIGATION_PAGES } from 'src/types';
+import { useNavigation } from '@react-navigation/native';
 
 interface PropertiesData {
   id: number;
@@ -27,6 +30,7 @@ const EarthScreen = () => {
   const [visited, setVisited] = useState<number[]>([]);
   const [score, setScore] = useState({ base: 0, percentage: 0 });
   const [quadrantsAll, setQuadrantsAll] = useState<number>(1);
+  const navigation = useNavigation();
 
   useEffect(() => {
     if (!data || !data.regions) return;
@@ -149,7 +153,17 @@ const EarthScreen = () => {
   return (
     <SafeAreaView style={{ height: '100%' }}>
       <View style={styles.wrapper}>
-        <Header label={'My Earth'} />
+        <Header
+          label={'My Earth'}
+          rightElement={
+            <TouchableOpacity
+              onPress={() => navigation.navigate(NAVIGATION_PAGES.TRAVELS_INFO as never)}
+              style={{ width: 30 }}
+            >
+              <InfoIcon />
+            </TouchableOpacity>
+          }
+        />
         {token && (
           <View style={styles.score}>
             <Text style={[styles.scoreText, { fontWeight: 'bold' }]}>Your score: {score.base}</Text>

+ 27 - 4
src/screens/InAppScreens/TravelsScreen/RegionsScreen/index.tsx

@@ -1,7 +1,7 @@
 import React, { useCallback, useEffect, useState } from 'react';
 import { View, Text, TouchableOpacity, FlatList } from 'react-native';
 import * as Progress from 'react-native-progress';
-import { useFocusEffect } from '@react-navigation/native';
+import { useFocusEffect, useNavigation } from '@react-navigation/native';
 import moment from 'moment';
 
 import { EditNmModal, Header, PageWrapper } from 'src/components';
@@ -23,6 +23,8 @@ import {
 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';
 
 const RegionsScreen = () => {
   const token = storage.get('token', StoreType.STRING) as string;
@@ -50,6 +52,7 @@ const RegionsScreen = () => {
     years: [],
     id: null
   });
+  const navigation = useNavigation();
 
   useEffect(() => {
     const currentYear = moment().year();
@@ -182,12 +185,27 @@ const RegionsScreen = () => {
   };
 
   const renderItem = ({ item }: { item: NmRegion }) => (
-    <NmRegionItem item={item} openEditModal={handleOpenEditModal} updateNM={handleUpdateNM} token={token} />
+    <NmRegionItem
+      item={item}
+      openEditModal={handleOpenEditModal}
+      updateNM={handleUpdateNM}
+      token={token}
+    />
   );
 
   return (
     <PageWrapper>
-      <Header label="Regions" />
+      <Header
+        label="Regions"
+        rightElement={
+          <TouchableOpacity
+            onPress={() => navigation.navigate(NAVIGATION_PAGES.TRAVELS_INFO as never)}
+            style={{ width: 30 }}
+          >
+            <InfoIcon />
+          </TouchableOpacity>
+        }
+      />
       <TouchableOpacity style={styles.megaSelector} onPress={() => setMegaSelectorVisible(true)}>
         <Text style={styles.megaButtonText}>{selectedMega?.name}</Text>
         <ChevronIcon width={18} height={18} />
@@ -244,7 +262,12 @@ const RegionsScreen = () => {
               <View style={{ marginVertical: 8 }}>
                 <Text style={[styles.textMedium, { textAlign: 'center' }]}>TCC regions</Text>
                 {filteredTccRegions?.map((item) => (
-                  <RegionItem item={item} updateRegion={handleUpdateTCC} key={item.id} token={token} />
+                  <RegionItem
+                    item={item}
+                    updateRegion={handleUpdateTCC}
+                    key={item.id}
+                    token={token}
+                  />
                 ))}
               </View>
             ) : null

+ 12 - 1
src/screens/InAppScreens/TravelsScreen/TripsScreen/index.tsx

@@ -14,6 +14,7 @@ import { styles } from './styles';
 import ChevronIcon from '../../../../../assets/icons/travels-screens/chevron-bottom.svg';
 import AddIcon from '../../../../../assets/icons/travels-screens/circle-plus.svg';
 import TripSvg from '../../../../../assets/icons/travels-screens/trip.svg';
+import InfoIcon from 'assets/icons/info-solid.svg';
 import { Colors } from 'src/theme';
 
 const TripsScreen = ({ route }: { route: any }) => {
@@ -74,7 +75,17 @@ const TripsScreen = ({ route }: { route: any }) => {
 
   return (
     <PageWrapper>
-      <Header label="Trips" />
+      <Header
+        label="Trips"
+        rightElement={
+          <TouchableOpacity
+            onPress={() => navigation.navigate(NAVIGATION_PAGES.TRAVELS_INFO as never)}
+            style={{ width: 30 }}
+          >
+            <InfoIcon />
+          </TouchableOpacity>
+        }
+      />
       <View style={styles.tabContainer}>
         <TouchableOpacity style={styles.regionSelector} onPress={() => setDatePickerVisible(true)}>
           <Text style={[styles.regionText]}>{selectedYear}</Text>

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

@@ -14,6 +14,7 @@ import SeriesIcon from '../../../../assets/icons/travels-section/series.svg';
 import EarthIcon from '../../../../assets/icons/travels-section/earth.svg';
 import TripIcon from '../../../../assets/icons/travels-section/trip.svg';
 import ImagesIcon from '../../../../assets/icons/travels-section/images.svg';
+import InfoIcon from 'assets/icons/info-solid.svg';
 
 const TravelsScreen = () => {
   const [isModalVisible, setIsModalVisible] = useState(false);
@@ -53,8 +54,15 @@ const TravelsScreen = () => {
 
   return (
     <PageWrapper>
-      <View style={{ alignItems: 'center', paddingVertical: 16 }}>
+      <View style={styles.header}>
+        <View style={{ width: 30 }} />
         <Text style={styles.title}>Travels</Text>
+        <TouchableOpacity
+          onPress={() => navigation.navigate(NAVIGATION_PAGES.TRAVELS_INFO as never)}
+          style={{ width: 30 }}
+        >
+          <InfoIcon />
+        </TouchableOpacity>
       </View>
       <FlatList
         data={buttons}
@@ -96,7 +104,13 @@ const styles = StyleSheet.create({
     fontSize: 13,
     fontWeight: 'bold'
   },
-  title: { color: Colors.DARK_BLUE, fontSize: 14, fontWeight: '600' }
+  title: { color: Colors.DARK_BLUE, fontSize: 14, fontWeight: '600' },
+  header: {
+    alignItems: 'center',
+    paddingVertical: 16,
+    flexDirection: 'row',
+    justifyContent: 'space-between'
+  }
 });
 
 export default TravelsScreen;

+ 87 - 0
src/screens/InfoScreen/index.tsx

@@ -0,0 +1,87 @@
+import { FC } from 'react';
+import { ImageBackground, View, Text, TouchableOpacity, ScrollView } from 'react-native';
+import type { NavigationProp } from '@react-navigation/native';
+
+import { Header, PageWrapper } from '../../components/';
+
+import { styles } from './styles';
+import { NAVIGATION_PAGES } from '../../types';
+
+type Props = {
+  navigation: NavigationProp<any>;
+};
+
+const InfoScreen: FC<Props> = ({ navigation }) => {
+  return (
+    <PageWrapper>
+      <Header label={'What is NomadMania...'} shouldClose={true} />
+      <ImageBackground
+        style={{ height: '100%' }}
+        source={require('../../../assets/images/nm-background.png')}
+      >
+        <ScrollView
+          style={styles.wrapper}
+          showsVerticalScrollIndicator={false}
+          contentContainerStyle={{ gap: 16 }}
+        >
+          <Text style={styles.title}>What is NomadMania...</Text>
+          <Text style={styles.text}>
+            Welcome to NomadMania, the ultimate hub for passionate travellers eager to explore the
+            world extensively and meaningfully, venturing beyond the ordinary and obvious. Our
+            vibrant community platform offers tools, information, and resources that empower your
+            extraordinary adventures.
+          </Text>
+
+          <View style={{ gap: 6 }}>
+            <Text style={styles.subtitle}>Join our community</Text>
+            <View style={styles.divider} />
+            <Text style={styles.text}>
+              Check out our ranking and statistics, see where others are traveling meet with fellow
+              travelers on your trips or plan a joint expedition
+            </Text>
+            <View style={styles.buttonsAndText}>
+              <TouchableOpacity
+                style={styles.button}
+                onPress={() => navigation.navigate(NAVIGATION_PAGES.JOIN_INFO)}
+              >
+                <Text style={styles.buttonText}>Read more...</Text>
+              </TouchableOpacity>
+            </View>
+          </View>
+
+          <View style={{ gap: 6 }}>
+            <Text style={styles.subtitle}>Discover the World</Text>
+            <View style={styles.divider} />
+            <Text style={styles.text}>Find new travel destinations and points of interest</Text>
+            <View style={styles.buttonsAndText}>
+              <TouchableOpacity
+                style={styles.button}
+                onPress={() => navigation.navigate(NAVIGATION_PAGES.DISCOVER_INFO)}
+              >
+                <Text style={styles.buttonText}>Read more...</Text>
+              </TouchableOpacity>
+            </View>
+          </View>
+
+          <View style={{ gap: 6 }}>
+            <Text style={styles.subtitle}>Plan & track your travels</Text>
+            <View style={styles.divider} />
+            <Text style={styles.text}>
+              Tell us where you've been and where you're going and we will create your travel map
+            </Text>
+            <View style={styles.buttonsAndText}>
+              <TouchableOpacity
+                style={styles.button}
+                onPress={() => navigation.navigate(NAVIGATION_PAGES.TRAVELS_INFO)}
+              >
+                <Text style={styles.buttonText}>Read more...</Text>
+              </TouchableOpacity>
+            </View>
+          </View>
+        </ScrollView>
+      </ImageBackground>
+    </PageWrapper>
+  );
+};
+
+export default InfoScreen;

+ 43 - 0
src/screens/InfoScreen/styles.tsx

@@ -0,0 +1,43 @@
+import { StyleSheet } from 'react-native';
+import { Colors } from '../../theme';
+
+export const styles = StyleSheet.create({
+  wrapper: {
+    display: 'flex',
+    height: '100%'
+  },
+  buttonsAndText: {
+    alignItems: 'flex-end',
+    marginTop: 4
+  },
+  button: {
+    backgroundColor: Colors.ORANGE,
+    paddingVertical: 6,
+    paddingHorizontal: 10,
+    borderRadius: 5,
+    alignItems: 'center'
+  },
+  buttonText: {
+    fontWeight: '700',
+    color: Colors.WHITE,
+    fontSize: 15
+  },
+  title: {
+    fontFamily: 'montserrat-700',
+    fontSize: 32,
+    color: Colors.DARK_BLUE
+  },
+  subtitle: {
+    fontFamily: 'montserrat-700',
+    fontSize: 18,
+    color: Colors.DARK_BLUE,
+    textTransform: 'uppercase'
+  },
+  text: { fontWeight: '600', color: Colors.DARK_BLUE },
+  divider: {
+    width: '30%',
+    height: 3,
+    backgroundColor: Colors.ORANGE,
+    borderRadius: 8
+  }
+});

+ 35 - 0
src/screens/JoinInfoScreen/index.tsx

@@ -0,0 +1,35 @@
+import { FC } from 'react';
+import { ImageBackground, View, Text, ScrollView } from 'react-native';
+import type { NavigationProp } from '@react-navigation/native';
+
+import { Header, PageWrapper } from '../../components/';
+import { styles } from './styles';
+
+type Props = {
+  navigation: NavigationProp<any>;
+};
+
+const JoinInfoScreen: FC<Props> = ({ navigation }) => {
+  return (
+    <PageWrapper>
+      <Header label={'Join our community'} />
+      <ImageBackground
+        style={{ height: '100%' }}
+        source={require('../../../assets/images/nm-background.png')}
+      >
+        <ScrollView
+          style={styles.wrapper}
+          showsVerticalScrollIndicator={false}
+          contentContainerStyle={{ gap: 16 }}
+        >
+          <Text style={styles.text}>
+            Check out our ranking and statistics, see where others are traveling meet with fellow
+            travelers on your trips or plan a joint expedition
+          </Text>
+        </ScrollView>
+      </ImageBackground>
+    </PageWrapper>
+  );
+};
+
+export default JoinInfoScreen;

+ 10 - 0
src/screens/JoinInfoScreen/styles.tsx

@@ -0,0 +1,10 @@
+import { StyleSheet } from 'react-native';
+import { Colors } from '../../theme';
+
+export const styles = StyleSheet.create({
+  wrapper: {
+    display: 'flex',
+    height: '100%'
+  },
+  text: { fontWeight: '600', color: Colors.DARK_BLUE }
+});

+ 18 - 8
src/screens/LoginScreen/index.tsx

@@ -7,7 +7,7 @@ import * as yup from 'yup';
 import { Header, Input, Button, BigText, PageWrapper } from '../../components';
 
 import { ButtonVariants } from '../../types/components';
-import { storage } from '../../storage';
+import { StoreType, storage } from '../../storage';
 import { NAVIGATION_PAGES } from '../../types';
 
 import { useLoginMutation } from '@api/auth';
@@ -24,24 +24,34 @@ const LoginSchema = yup.object({
 
 const LoginScreen: FC<Props> = ({ navigation }) => {
   const { dispatch } = useNavigation();
+  const isFirstLaunch = storage.get('isFirstLaunch', StoreType.BOOLEAN) ?? true;
 
   const { data, mutate: userLogin } = useLoginMutation();
 
   const updateLocalData = async (token: string) => {
     await fetchAndSaveStatistics(token);
-  }
+  };
 
   useEffect(() => {
     if (data && data.token) {
       storage.set('token', data.token);
       storage.set('uid', data.uid.toString());
+      storage.set('isFirstLaunch', false);
       updateLocalData(data.token);
-      dispatch(
-        CommonActions.reset({
-          index: 1,
-          routes: [{ name: NAVIGATION_PAGES.IN_APP }]
-        })
-      );
+
+      isFirstLaunch
+        ? dispatch(
+            CommonActions.reset({
+              index: 1,
+              routes: [{ name: NAVIGATION_PAGES.INFO }]
+            })
+          )
+        : dispatch(
+            CommonActions.reset({
+              index: 1,
+              routes: [{ name: NAVIGATION_PAGES.IN_APP }]
+            })
+          );
     }
   }, [data]);
 

+ 17 - 7
src/screens/RegisterScreen/EditAccount/index.tsx

@@ -9,7 +9,7 @@ import { AvatarPicker, BigText, Button, Header, Input, PageWrapper } from '../..
 import { InputDatePicker } from '../../../components/Calendar/InputDatePicker';
 import { ModalFlatList } from '../../../components/FlatList/modal-flatlist';
 import { useRegisterMutation } from '@api/auth';
-import { storage } from '../../../storage';
+import { StoreType, storage } from '../../../storage';
 
 import store from '../../../storage/zustand';
 import { NAVIGATION_PAGES } from '../../../types';
@@ -27,6 +27,7 @@ const SignUpSchema = yup.object({
 
 const EditAccount = () => {
   const { dispatch } = useNavigation();
+  const isFirstLaunch = storage.get('isFirstLaunch', StoreType.BOOLEAN) ?? true;
 
   const [user] = store((state) => [state.registration.user]);
 
@@ -47,13 +48,22 @@ const EditAccount = () => {
     if (data && data.token) {
       storage.set('token', data.token);
       storage.set('uid', data.uid.toString());
+      storage.set('isFirstLaunch', false);
       updateLocalData(data.token);
-      dispatch(
-        CommonActions.reset({
-          index: 1,
-          routes: [{ name: NAVIGATION_PAGES.IN_APP }]
-        })
-      );
+
+      isFirstLaunch
+        ? dispatch(
+            CommonActions.reset({
+              index: 1,
+              routes: [{ name: NAVIGATION_PAGES.INFO }]
+            })
+          )
+        : dispatch(
+            CommonActions.reset({
+              index: 1,
+              routes: [{ name: NAVIGATION_PAGES.IN_APP }]
+            })
+          );
     }
   }, [data]);
 

+ 55 - 0
src/screens/TravelsInfoScreen/index.tsx

@@ -0,0 +1,55 @@
+import { FC } from 'react';
+import { ImageBackground, View, Text, ScrollView } from 'react-native';
+import type { NavigationProp } from '@react-navigation/native';
+
+import { Header, PageWrapper } from '../../components/';
+import { styles } from './styles';
+import { Colors } from 'src/theme';
+import FlagsSvg from 'assets/icons/travels-section/flags.svg';
+
+type Props = {
+  navigation: NavigationProp<any>;
+};
+
+const TravelsInfoScreen: FC<Props> = ({ navigation }) => {
+  return (
+    <PageWrapper>
+      <Header label={'Plan & track your travels'} />
+      <ImageBackground
+        style={{ height: '100%' }}
+        source={require('../../../assets/images/nm-background.png')}
+      >
+        <ScrollView
+          style={styles.wrapper}
+          showsVerticalScrollIndicator={false}
+          contentContainerStyle={{ gap: 16 }}
+        >
+          <Text style={[styles.text, { flex: 0 }]}>
+            Travels section is a structured way to record your visited countries / regions / points
+            of interest and to explore future destinations.
+          </Text>
+
+          <View>
+            <Text style={[styles.title, { marginBottom: 10 }]}>Countries</Text>
+            <View style={styles.infoBlock}>
+              <View style={styles.imgContainer}>
+                <FlagsSvg fill={Colors.ORANGE} />
+                <Text style={styles.imgText}>Countries</Text>
+              </View>
+              <Text style={styles.text}>
+                Countries give you the easiest way of recording your travels - just mark a country
+                as visited and it will be reflected in your UN score. You can also tell us how long
+                you stayed in a
+              </Text>
+            </View>
+            <Text style={[styles.text, { flex: 0 }]}>
+              country and this will influence your SLOW score. Read more about slow here
+            </Text>
+          </View>
+        </ScrollView>
+      </ImageBackground>
+    </PageWrapper>
+  );
+};
+
+export default TravelsInfoScreen;

+ 36 - 0
src/screens/TravelsInfoScreen/styles.tsx

@@ -0,0 +1,36 @@
+import { StyleSheet } from 'react-native';
+import { Colors } from '../../theme';
+
+export const styles = StyleSheet.create({
+  wrapper: { height: '100%', display: 'flex' },
+  title: {
+    fontFamily: 'montserrat-700',
+    fontSize: 16,
+    color: Colors.DARK_BLUE
+  },
+  text: {
+    fontWeight: '600',
+    color: Colors.DARK_BLUE,
+    flex: 1
+  },
+  infoBlock: { flexDirection: 'row', gap: 8 },
+  imgContainer: {
+    width: 80,
+    height: 80,
+    borderRadius: 8,
+    backgroundColor: Colors.FILL_LIGHT,
+    alignItems: 'center',
+    justifyContent: 'center',
+    gap: 6,
+    shadowColor: '#000',
+    shadowOffset: { width: 0, height: 0 },
+    shadowOpacity: 0.15,
+    shadowRadius: 8,
+    elevation: 8
+  },
+  imgText: {
+    fontSize: 12,
+    fontWeight: '700',
+    color: Colors.DARK_BLUE
+  }
+});

+ 4 - 0
src/types/navigation.ts

@@ -5,6 +5,10 @@ export enum NAVIGATION_PAGES {
   REGISTER_ACCOUNT_DATA = 'registrationAccountData',
   RESET_PASSWORD = 'resetPassword',
   RESET_PASSWORD_DEEP = 'resetPasswordDeep',
+  INFO = 'info',
+  JOIN_INFO = 'joinInfo',
+  DISCOVER_INFO = 'discoverInfo',
+  TRAVELS_INFO = 'travelsInfo',
   IN_APP = 'inAppStack',
   IN_APP_MAP_TAB = 'Map',
   MAP_TAB = 'inAppMapTab',

+ 0 - 10
src/utils/mapHelpers.ts

@@ -72,16 +72,6 @@ export const processMarkerData = (marker: ItemSeries) => {
   };
 };
 
-export const processIconUrl = (url: string) => {
-  let processedUrl = url.startsWith('/static') ? url.substring(7) : url;
-
-  if (processedUrl.endsWith('.png.png')) {
-    processedUrl = processedUrl.substring(0, processedUrl.length - 4);
-  }
-
-  return `https://nomadmania.eu${processedUrl}`;
-};
-
 export const filterCandidatesMarkers = (candidatesMarkers: any[], visibleAreaPolygon: turf.helpers.Geometry | turf.helpers.Feature<any, turf.helpers.Properties>) => (
   candidatesMarkers.filter((marker: { pointJSON: string; polygonJSON: string; }) => {
     if (marker.pointJSON) {