Pārlūkot izejas kodu

btns relocation fixes

Viktoriia 11 mēneši atpakaļ
vecāks
revīzija
99b8a79b9b

+ 8 - 0
Route.tsx

@@ -275,6 +275,10 @@ const Route = () => {
               name={NAVIGATION_PAGES.PUBLIC_PROFILE_VIEW}
               component={ProfileScreen}
             />
+            <ScreenStack.Screen
+              name={NAVIGATION_PAGES.EDIT_PERSONAL_INFO}
+              component={EditPersonalInfo}
+            />
             <ScreenStack.Screen name={NAVIGATION_PAGES.USERS_MAP} component={UsersMapScreen} />
             <ScreenStack.Screen
               name={NAVIGATION_PAGES.SUGGEST_SERIES}
@@ -313,6 +317,10 @@ const Route = () => {
               name={NAVIGATION_PAGES.PUBLIC_PROFILE_VIEW}
               component={ProfileScreen}
             />
+            <ScreenStack.Screen
+              name={NAVIGATION_PAGES.EDIT_PERSONAL_INFO}
+              component={EditPersonalInfo}
+            />
             <ScreenStack.Screen
               name={NAVIGATION_PAGES.STATISTICS_LIST_DATA}
               component={StatisticsListScreen}

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

@@ -58,8 +58,7 @@ const CountryViewScreen: FC<Props> = ({ navigation, route }) => {
   useEffect(() => {
     navigation.getParent()?.setOptions({
       tabBarStyle: {
-        display: 'none',
-        position: 'absolute',
+        display: 'flex',
         ...Platform.select({
           android: {
             height: 58

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

@@ -79,8 +79,7 @@ const RegionViewScreen: FC<Props> = ({ navigation, route }) => {
   useEffect(() => {
     navigation.getParent()?.setOptions({
       tabBarStyle: {
-        display: 'none',
-        position: 'absolute',
+        display: 'flex',
         ...Platform.select({
           android: {
             height: 58

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

@@ -75,7 +75,7 @@ const localDareDir = `${FileSystem.cacheDirectory}tiles/regions_mqp`;
 
 const AnimatedMarker = Animation.createAnimatedComponent(Marker);
 
-const MapScreen: React.FC<MapScreenProps> = ({ navigation }) => {
+const MapScreen: React.FC<MapScreenProps> = ({ navigation, route }) => {
   const [dareData, setDareData] = useState(jsonData);
   const tilesBaseURL = `${FASTEST_MAP_HOST}/tiles_osm`;
   const gridUrl = `${FASTEST_MAP_HOST}/tiles_nm/grid`;
@@ -163,6 +163,12 @@ const MapScreen: React.FC<MapScreenProps> = ({ navigation }) => {
     }, [userData])
   );
 
+  useEffect(() => {
+    if (route.params?.id && route.params?.type && dareData) {
+      handleFindRegion(route.params?.id, route.params?.type);
+    }
+  }, [route, dareData]);
+
   useEffect(() => {
     if (userInfo) {
       setUserInfoData(JSON.parse(userInfo));

+ 233 - 36
src/screens/InAppScreens/ProfileScreen/UsersMap/index.tsx

@@ -1,16 +1,41 @@
-import { Platform, TouchableOpacity, View, Image, Text } from 'react-native';
+import {
+  Platform,
+  TouchableOpacity,
+  View,
+  Image,
+  Animated as Animation,
+  Linking,
+  TextInput,
+  Dimensions
+} from 'react-native';
 import React, { FC, useEffect, useRef, useState } from 'react';
-import MapView, { UrlTile } from 'react-native-maps';
+import MapView, { UrlTile, Marker } from 'react-native-maps';
+import * as Location from 'expo-location';
+import Animated, {
+  Easing,
+  useSharedValue,
+  useAnimatedStyle,
+  withTiming
+} from 'react-native-reanimated';
 
 import { styles } from './styles';
 import { API_HOST, FASTEST_MAP_HOST } from 'src/constants';
-import { NavigationProp } from '@react-navigation/native';
-import { AvatarWithInitials } from 'src/components';
+import { CommonActions, NavigationProp } from '@react-navigation/native';
+import { AvatarWithInitials, LocationPopup } from 'src/components';
 import { Colors } from 'src/theme';
 
 import CloseSvg from 'assets/icons/close.svg';
 import FilterIcon from 'assets/icons/filter.svg';
+import LocationIcon from 'assets/icons/location.svg';
+import SearchIcon from 'assets/icons/search.svg';
+
 import FilterModal from '../../MapScreen/FilterModal';
+import SearchModal from '../../MapScreen/UniversalSearch';
+import { useGetUniversalSearch } from '@api/search';
+import { storage, StoreType } from 'src/storage';
+import { NAVIGATION_PAGES } from 'src/types';
+
+const AnimatedMarker = Animation.createAnimatedComponent(Marker);
 
 type Props = {
   navigation: NavigationProp<any>;
@@ -18,6 +43,7 @@ type Props = {
 };
 
 const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
+  const token = storage.get('token', StoreType.STRING) as string;
   const userId = route.params?.userId;
   const data = route.params?.data;
 
@@ -26,6 +52,7 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
   const visitedDefaultTiles = `${FASTEST_MAP_HOST}/tiles_nm/user_visited/${userId}`;
 
   const mapRef = useRef<MapView>(null);
+  const strokeWidthAnim = useRef(new Animation.Value(2)).current;
   const [isFilterVisible, setIsFilterVisible] = useState(false);
   const [tilesType, setTilesType] = useState({ label: 'NM regions', value: 0 });
   const tilesTypes = [
@@ -35,20 +62,97 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
   ];
   const [type, setType] = useState(0);
   const [visitedTiles, setVisitedTiles] = useState(visitedDefaultTiles);
+  const [location, setLocation] = useState<Location.LocationObjectCoords | null>(null);
+  const [askLocationVisible, setAskLocationVisible] = useState<boolean>(false);
+  const [openSettingsVisible, setOpenSettingsVisible] = useState<boolean>(false);
+  const [isExpanded, setIsExpanded] = useState(false);
+  const [searchVisible, setSearchVisible] = useState(false);
+  const [index, setIndex] = useState<number>(0);
+  const width = useSharedValue(48);
+  const usableWidth = Dimensions.get('window').width - 32;
+  const [search, setSearch] = useState('');
+  const [searchInput, setSearchInput] = useState('');
+  const { data: searchData } = useGetUniversalSearch(search, search.length > 0);
 
   useEffect(() => {
-    navigation.getParent()?.setOptions({
-      tabBarStyle: {
-        display: 'none',
-        position: 'absolute',
-        ...Platform.select({
-          android: {
-            height: 58
-          }
+    Animation.loop(
+      Animation.sequence([
+        Animation.timing(strokeWidthAnim, {
+          toValue: 3,
+          duration: 700,
+          useNativeDriver: false
+        }),
+        Animation.timing(strokeWidthAnim, {
+          toValue: 2,
+          duration: 700,
+          useNativeDriver: false
         })
-      }
+      ])
+    ).start();
+  }, [strokeWidthAnim]);
+
+  const handleGetLocation = async () => {
+    let { status, canAskAgain } = await Location.getForegroundPermissionsAsync();
+
+    if (status === 'granted') {
+      getLocation();
+    } else if (!canAskAgain) {
+      setOpenSettingsVisible(true);
+    } else {
+      setAskLocationVisible(true);
+    }
+  };
+
+  const getLocation = async () => {
+    let currentLocation = await Location.getCurrentPositionAsync({
+      accuracy: Location.Accuracy.Balanced
+    });
+    setLocation(currentLocation.coords);
+
+    mapRef.current?.animateToRegion(
+      {
+        latitude: currentLocation.coords.latitude,
+        longitude: currentLocation.coords.longitude,
+        latitudeDelta: 5,
+        longitudeDelta: 5
+      },
+      800
+    );
+  };
+
+  const handleAcceptPermission = async () => {
+    setAskLocationVisible(false);
+    let { status, canAskAgain } = await Location.requestForegroundPermissionsAsync();
+
+    if (status === 'granted') {
+      getLocation();
+    } else if (!canAskAgain) {
+      setOpenSettingsVisible(true);
+    }
+  };
+
+  const handlePress = () => {
+    if (isExpanded) {
+      setIndex(0);
+      setSearchInput('');
+    }
+    setIsExpanded((prev) => !prev);
+    width.value = withTiming(isExpanded ? 48 : usableWidth, {
+      duration: 300,
+      easing: Easing.inOut(Easing.ease)
     });
-  }, [navigation]);
+  };
+
+  const animatedStyle = useAnimatedStyle(() => {
+    return {
+      width: width.value
+    };
+  });
+
+  const handleSearch = async () => {
+    setSearch(searchInput);
+    setSearchVisible(true);
+  };
 
   const renderMapTiles = (url: string, zIndex: number, opacity = 1) => (
     <UrlTile
@@ -67,6 +171,33 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
     navigation.goBack();
   };
 
+  const handleCloseModal = () => {
+    setSearchInput('');
+    setSearchVisible(false);
+    handlePress();
+  };
+
+  const handleFindRegion = (id: number, type: string) => {
+    navigation.dispatch(
+      CommonActions.reset({
+        index: 1,
+        routes: [
+          {
+            name: NAVIGATION_PAGES.IN_APP_MAP_TAB,
+            state: {
+              routes: [
+                {
+                  name: NAVIGATION_PAGES.MAP_TAB,
+                  params: { id, type }
+                }
+              ]
+            }
+          }
+        ]
+      })
+    );
+  };
+
   return (
     <View style={styles.container}>
       <MapView
@@ -88,36 +219,79 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
         {renderMapTiles(tilesBaseURL, 1)}
         {type !== 1 && renderMapTiles(gridUrl, 2)}
         {userId && renderMapTiles(visitedTiles, 2, 0.5)}
+        {location && (
+          <AnimatedMarker coordinate={location} anchor={{ x: 0.5, y: 0.5 }}>
+            <Animation.View style={[styles.location, { borderWidth: strokeWidthAnim }]} />
+          </AnimatedMarker>
+        )}
       </MapView>
 
-      <TouchableOpacity
-        style={[styles.cornerButton, styles.topLeftButton, styles.closeLeftButton]}
-        onPress={handleGoBack}
-      >
-        <CloseSvg fill="white" width={13} height={13} />
-        <Text style={styles.textClose}>Close</Text>
-      </TouchableOpacity>
-      <View style={[styles.cornerButton, styles.topRightButton]}>
-        {data.user_data.avatar ? (
-          <Image
-            style={styles.avatar}
-            source={{ uri: API_HOST + '/img/avatars/' + data.user_data.avatar }}
-          />
-        ) : (
-          <AvatarWithInitials
-            text={`${data.user_data.first_name[0] ?? ''}${data.user_data.last_name[0] ?? ''}`}
-            flag={API_HOST + '/img/flags_new/' + data.user_data.flag1}
-            size={48}
-            borderColor={Colors.WHITE}
-          />
-        )}
-      </View>
+      {!isExpanded ? (
+        <TouchableOpacity
+          style={[styles.cornerButton, styles.topRightButton]}
+          onPress={handleGoBack}
+        >
+          {data.user_data.avatar ? (
+            <Image
+              style={styles.avatar}
+              source={{ uri: API_HOST + '/img/avatars/' + data.user_data.avatar }}
+            />
+          ) : (
+            <AvatarWithInitials
+              text={`${data.user_data.first_name[0] ?? ''}${data.user_data.last_name[0] ?? ''}`}
+              flag={API_HOST + '/img/flags_new/' + data.user_data.flag1}
+              size={48}
+              borderColor={Colors.WHITE}
+            />
+          )}
+        </TouchableOpacity>
+      ) : null}
+
       <TouchableOpacity
         style={[styles.cornerButton, styles.bottomButton, styles.bottomLeftButton]}
         onPress={() => setIsFilterVisible(true)}
       >
         <FilterIcon />
       </TouchableOpacity>
+
+      <TouchableOpacity
+        onPress={handleGetLocation}
+        style={[styles.cornerButton, styles.bottomButton, styles.bottomRightButton]}
+      >
+        <LocationIcon />
+      </TouchableOpacity>
+      <Animated.View
+        style={[
+          styles.searchContainer,
+          styles.cornerButton,
+          styles.topLeftButton,
+          animatedStyle,
+          { padding: 5 }
+        ]}
+      >
+        {isExpanded ? (
+          <>
+            <TouchableOpacity onPress={handlePress} style={styles.iconButton}>
+              <CloseSvg fill={'#0F3F4F'} />
+            </TouchableOpacity>
+            <TextInput
+              style={styles.input}
+              placeholder="Search regions, places, nomads"
+              placeholderTextColor={Colors.LIGHT_GRAY}
+              value={searchInput}
+              onChangeText={(text) => setSearchInput(text)}
+              onSubmitEditing={handleSearch}
+            />
+            <TouchableOpacity onPress={handleSearch} style={styles.iconButton}>
+              <SearchIcon fill={'#0F3F4F'} />
+            </TouchableOpacity>
+          </>
+        ) : (
+          <TouchableOpacity onPress={handlePress} style={[styles.iconButton]}>
+            <SearchIcon fill={'#0F3F4F'} />
+          </TouchableOpacity>
+        )}
+      </Animated.View>
       <FilterModal
         isFilterVisible={isFilterVisible}
         setIsFilterVisible={setIsFilterVisible}
@@ -130,6 +304,29 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
         setVisitedTiles={setVisitedTiles}
         isPublicView={true}
       />
+      <LocationPopup
+        visible={askLocationVisible}
+        onClose={() => setAskLocationVisible(false)}
+        onAccept={handleAcceptPermission}
+        modalText="To use this feature we need your permission to access your location. If you press OK your system will ask you to approve location sharing with NomadMania app."
+      />
+      <LocationPopup
+        visible={openSettingsVisible}
+        onClose={() => setOpenSettingsVisible(false)}
+        onAccept={() =>
+          Platform.OS === 'ios' ? Linking.openURL('app-settings:') : Linking.openSettings()
+        }
+        modalText="NomadMania app needs location permissions to function properly. Open settings?"
+      />
+      <SearchModal
+        searchVisible={searchVisible}
+        handleCloseModal={handleCloseModal}
+        handleFindRegion={handleFindRegion}
+        index={index}
+        searchData={searchData}
+        setIndex={setIndex}
+        token={token}
+      />
     </View>
   );
 };

+ 39 - 4
src/screens/InAppScreens/ProfileScreen/UsersMap/styles.tsx

@@ -25,7 +25,7 @@ export const styles = StyleSheet.create({
     elevation: 5
   },
   topLeftButton: {
-    top: 58,
+    top: 52,
     left: 16
   },
   closeLeftButton: {
@@ -49,13 +49,16 @@ export const styles = StyleSheet.create({
     right: 16
   },
   bottomButton: {
-    bottom: 58,
+    bottom: 22,
     width: 42,
     height: 42,
-    borderRadius: 21,
+    borderRadius: 21
+  },
+  bottomRightButton: {
+    right: 16
   },
   bottomLeftButton: {
-    left: 16,
+    left: 16
   },
   avatar: {
     borderRadius: 48 / 2,
@@ -63,5 +66,37 @@ export const styles = StyleSheet.create({
     height: 48,
     borderWidth: 2,
     borderColor: Colors.WHITE
+  },
+  location: {
+    width: 18,
+    height: 18,
+    borderRadius: 9,
+    borderColor: 'white',
+    backgroundColor: '#ED9334',
+    scale: 1,
+    shadow: {
+      shadowColor: '#212529',
+      shadowOffset: {
+        width: 0,
+        height: 4
+      },
+      shadowOpacity: 0.12,
+      shadowRadius: 8,
+      elevation: 5
+    }
+  },
+  searchContainer: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    overflow: 'hidden'
+  },
+  iconButton: {
+    padding: 10
+  },
+  input: {
+    flex: 1,
+    height: 40,
+    color: Colors.DARK_BLUE,
+    fontWeight: '600'
   }
 });

+ 4 - 4
src/screens/InAppScreens/ProfileScreen/index.tsx

@@ -89,7 +89,7 @@ const ProfileScreen: FC<Props> = ({ navigation, route }) => {
   useEffect(() => {
     setIsFriend(userData?.data?.is_friend ?? 0);
 
-    if (!isPublicView && userData) {
+    if (userData && userData?.data?.own_profile === 1) {
       const userInfo = {
         avatar: userData?.data?.user_data.avatar ?? '',
         first_name: userData?.data?.user_data.first_name,
@@ -106,7 +106,7 @@ const ProfileScreen: FC<Props> = ({ navigation, route }) => {
   const links = JSON.parse(data.user_data.links_json);
 
   const handleGoToMap = () => {
-    isPublicView
+    data.own_profile === 0
       ? navigation.navigate(NAVIGATION_PAGES.USERS_MAP, { userId: route.params?.userId, data })
       : navigation.dispatch(
           CommonActions.reset({
@@ -188,7 +188,7 @@ const ProfileScreen: FC<Props> = ({ navigation, route }) => {
 
   return (
     <PageWrapper>
-      {isPublicView && <Header label="Profile" />}
+      <Header label="Profile" />
       <ScrollView
         showsVerticalScrollIndicator={false}
         contentContainerStyle={{ paddingBottom: 58 }}
@@ -258,7 +258,7 @@ const ProfileScreen: FC<Props> = ({ navigation, route }) => {
                 </View>
               </View>
 
-              {!isPublicView ? (
+              {data.own_profile === 1 ? (
                 <TouchableOpacity
                   style={styles.settings}
                   onPress={() => navigation.navigate(NAVIGATION_PAGES.EDIT_PERSONAL_INFO)}

+ 0 - 15
src/screens/InAppScreens/TravelsScreen/CountriesScreen/index.tsx

@@ -43,21 +43,6 @@ const CountriesScreen = () => {
     setIsEditModalVisible(true);
   };
 
-  useFocusEffect(
-    useCallback(() => {
-      navigation.getParent()?.setOptions({
-        tabBarStyle: {
-          display: 'flex',
-          ...Platform.select({
-            android: {
-              height: 58
-            }
-          })
-        }
-      });
-    }, [navigation])
-  );
-
   useEffect(() => {
     if (slow && slow.length) {
       token && calcTotalScore();

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

@@ -48,21 +48,6 @@ const DareScreen = () => {
     }
   }, [megaregions])
 
-  useFocusEffect(
-    useCallback(() => {
-      navigation.getParent()?.setOptions({
-        tabBarStyle: {
-          display: 'flex',
-          ...Platform.select({
-            android: {
-              height: 58
-            }
-          })
-        }
-      });
-    }, [navigation])
-  );
-
   useEffect(() => {
     if (dareRegions && dareRegions.length) {
       token && calcTotalCountries();

+ 0 - 15
src/screens/InAppScreens/TravelsScreen/RegionsScreen/index.tsx

@@ -52,21 +52,6 @@ const RegionsScreen = () => {
   const navigation = useNavigation();
   const { handleUpdateNMList: handleUpdateNM, nmRegions, setNmRegions, setUserData } = useRegion();
 
-  useFocusEffect(
-    useCallback(() => {
-      navigation.getParent()?.setOptions({
-        tabBarStyle: {
-          display: 'flex',
-          ...Platform.select({
-            android: {
-              height: 58
-            }
-          })
-        }
-      });
-    }, [navigation])
-  );
-
   useEffect(() => {
     const currentYear = moment().year();
     let yearSelector: { label: string; value: number }[] = [{ label: 'visited', value: 1 }];

+ 1 - 0
src/types/map/index.ts

@@ -10,6 +10,7 @@ export interface Region {
 
 export interface MapScreenProps {
   navigation: BottomTabNavigationProp<any>;
+  route: any;
 }
 
 export interface Series {