Viktoriia il y a 1 an
Parent
commit
d2438068de
24 fichiers modifiés avec 436 ajouts et 305 suppressions
  1. BIN
      assets/logo-ua.png
  2. 2 0
      src/components/Calendar/SpinnerDatePicker.tsx
  3. 2 3
      src/modules/api/countries/queries/use-post-get-slow.tsx
  4. 1 2
      src/modules/api/myRegions/queries/use-post-get-megaregions.tsx
  5. 16 14
      src/modules/map/regionData.ts
  6. 22 20
      src/screens/InAppScreens/ProfileScreen/Settings/index.tsx
  7. 72 50
      src/screens/InAppScreens/ProfileScreen/index.tsx
  8. 8 0
      src/screens/InAppScreens/ProfileScreen/styles.ts
  9. 7 2
      src/screens/InAppScreens/TravellersScreen/index.tsx
  10. 17 10
      src/screens/InAppScreens/TravelsScreen/Components/AccordionListItem.tsx
  11. 40 31
      src/screens/InAppScreens/TravelsScreen/Components/CountryItem/index.tsx
  12. 37 33
      src/screens/InAppScreens/TravelsScreen/Components/MyRegionsItems/NmRegionItem.tsx
  13. 23 19
      src/screens/InAppScreens/TravelsScreen/Components/MyRegionsItems/RegionItem.tsx
  14. 4 4
      src/screens/InAppScreens/TravelsScreen/CountriesScreen/index.tsx
  15. 29 27
      src/screens/InAppScreens/TravelsScreen/DareScreen/index.tsx
  16. 11 7
      src/screens/InAppScreens/TravelsScreen/EarthScreen/index.tsx
  17. 23 21
      src/screens/InAppScreens/TravelsScreen/RegionsScreen/index.tsx
  18. 2 2
      src/screens/InAppScreens/TravelsScreen/Series/index.tsx
  19. 33 26
      src/screens/InAppScreens/TravelsScreen/SeriesItemScreen/index.tsx
  20. 7 3
      src/screens/InAppScreens/TravelsScreen/index.tsx
  21. 23 7
      src/screens/RegisterScreen/JoinUs/index.tsx
  22. 24 0
      src/screens/RegisterScreen/JoinUs/styles.tsx
  23. 27 21
      src/screens/WelcomeScreen/index.tsx
  24. 6 3
      src/screens/WelcomeScreen/style.ts

BIN
assets/logo-ua.png


+ 2 - 0
src/components/Calendar/SpinnerDatePicker.tsx

@@ -23,6 +23,8 @@ const SpinnerDatePicker: FC<Props> = ({ selectedDate }) => {
       mode={'date'}
       display={'spinner'}
       onChange={onChange}
+      minimumDate={new Date(1930, 0, 1)}
+      maximumDate={new Date()}
     />
   );
 };

+ 2 - 3
src/modules/api/countries/queries/use-post-get-slow.tsx

@@ -5,13 +5,12 @@ import { countriesApi, type PostGetSlowReturn } from '../countries-api';
 
 import type { BaseAxiosError } from '../../../../types';
 
-export const useGetSlowQuery = (token: string, enabled: boolean) => {
+export const useGetSlowQuery = (token: string) => {
   return useQuery<PostGetSlowReturn, BaseAxiosError>({
     queryKey: countriesQueryKeys.getSlow(token),
     queryFn: async () => {
       const response = await countriesApi.getSlow(token);
       return response.data;
-    },
-    enabled
+    }
   });
 };

+ 1 - 2
src/modules/api/myRegions/queries/use-post-get-megaregions.tsx

@@ -11,7 +11,6 @@ export const useGetMegaregionsQuery = (token: string, enabled: boolean) => {
     queryFn: async () => {
       const response = await regionsApi.getMegaregions(token);
       return response.data;
-    },
-    enabled
+    }
   });
 };

+ 16 - 14
src/modules/map/regionData.ts

@@ -14,20 +14,22 @@ export const getData = async (
         (_, { rows }) => {
           const regionData = rows._array[0];
   
-          const avatarPromises = JSON.parse(regionData.visitors_avatars)?.map((avatarId: number) => {
-            return new Promise<string>((resolveAvatar, rejectAvatar) => {
-              tx.executeSql(
-                `SELECT * FROM avatars WHERE id = ${avatarId};`,
-                [],
-                (_, { rows }) => resolveAvatar(rows._array[0].data),
-                (_, error) => {
-                  console.error('error', error);
-                  rejectAvatar(error);
-                  return false;
-                }
-              );
-            });
-          });
+          const avatarPromises = regionData?.visitors_avatars
+            ? JSON.parse(regionData.visitors_avatars)?.map((avatarId: number) => {
+              return new Promise<string>((resolveAvatar, rejectAvatar) => {
+                tx.executeSql(
+                  `SELECT * FROM avatars WHERE id = ${avatarId};`,
+                  [],
+                  (_, { rows }) => resolveAvatar(rows._array[0].data),
+                  (_, error) => {
+                    console.error('error', error);
+                    rejectAvatar(error);
+                    return false;
+                  }
+                );
+              });
+            })
+            : [];
   
           Promise.all(avatarPromises)
             .then(avatars => {

+ 22 - 20
src/screens/InAppScreens/ProfileScreen/Settings/index.tsx

@@ -55,30 +55,32 @@ const Settings = () => {
         navigation.navigate(NAVIGATION_PAGES.EDIT_PERSONAL_INFO);
       }
     },
-    {
-      label: 'Invite a Friend',
-      icon: <UserPlusIcon fill={Colors.DARK_BLUE} width={20} height={20} />
-    },
-    {
-      label: 'Notification',
-      icon: <BellIcon fill={Colors.DARK_BLUE} width={20} height={20} />
-    },
+    // {
+    //   label: 'Invite a Friend',
+    //   icon: <UserPlusIcon fill={Colors.DARK_BLUE} width={20} height={20} />
+    // },
+    // {
+    //   label: 'Notification',
+    //   icon: <BellIcon fill={Colors.DARK_BLUE} width={20} height={20} />
+    // },
     {
       label: 'Contact Us',
-      icon: <MailIcon fill={Colors.DARK_BLUE} width={20} height={20} />
-    },
-    {
-      label: 'FAQs',
-      icon: <FAQIcon fill={Colors.DARK_BLUE} width={20} height={20} />
+      icon: <MailIcon fill={Colors.DARK_BLUE} width={20} height={20} />,
+      buttonFn: () => Linking.openURL('https://nomadmania.com/contact/')
     },
+    // {
+    //   label: 'FAQs',
+    //   icon: <FAQIcon fill={Colors.DARK_BLUE} width={20} height={20} />
+    // },
     {
       label: 'Terms & Conditions',
-      icon: <DocumentIcon fill={Colors.DARK_BLUE} width={20} height={20} />
-    },
-    {
-      label: 'Privacy Notice',
-      icon: <ShieldIcon fill={Colors.DARK_BLUE} width={20} height={20} />
+      icon: <DocumentIcon fill={Colors.DARK_BLUE} width={20} height={20} />,
+      buttonFn: () => Linking.openURL('https://nomadmania.com/terms/')
     },
+    // {
+    //   label: 'Privacy Notice',
+    //   icon: <ShieldIcon fill={Colors.DARK_BLUE} width={20} height={20} />
+    // },
     {
       label: 'Logout',
       icon: <ExitIcon fill={Colors.RED} width={20} height={20} />,
@@ -194,7 +196,7 @@ const Settings = () => {
           buttonFn={button.buttonFn}
         />
       ))}
-      <View
+      {/* <View
         style={{
           display: 'flex',
           flexDirection: 'row',
@@ -212,7 +214,7 @@ const Settings = () => {
           onValueChange={toggleSwitch}
           value={isSubscribed}
         />
-      </View>
+      </View> */}
       <WarningModal
         isVisible={modalInfo.visible}
         onClose={closeModal}

+ 72 - 50
src/screens/InAppScreens/ProfileScreen/index.tsx

@@ -109,10 +109,15 @@ const ProfileScreen: FC<Props> = ({ navigation, route }) => {
 
             {!isPublicView ? (
               <TouchableOpacity
-                style={{ paddingVertical: 4 }}
+                style={styles.settings}
                 onPress={() => navigation.navigate(NAVIGATION_PAGES.SETTINGS)}
               >
-                <GearIcon fill={Colors.DARK_BLUE} />
+                <GearIcon
+                  width={20}
+                  height={20}
+                  fill={Colors.DARK_BLUE}
+                  style={{ alignSelf: 'center' }}
+                />
               </TouchableOpacity>
             ) : null}
           </View>
@@ -160,6 +165,17 @@ const PersonalInfo: FC<PersonalInfoProps> = ({ data }) => {
     url && Linking.openURL(url);
   };
 
+  const hasActiveLinks = () => {
+    return (
+      (data.links?.f?.link && data.links?.f?.active !== 0) ||
+      (data.links?.i?.link && data.links?.i?.active !== 0) ||
+      (data.links?.t?.link && data.links?.t?.active !== 0) ||
+      (data.links?.y?.link && data.links?.y?.active !== 0) ||
+      (data.links?.www?.link && data.links?.www?.active !== 0) ||
+      (data.links?.other?.link && data.links?.other?.active !== 0)
+    );
+  };
+
   return (
     <ScrollView
       showsVerticalScrollIndicator={false}
@@ -184,17 +200,19 @@ const PersonalInfo: FC<PersonalInfoProps> = ({ data }) => {
           })}
         </View>
       </InfoItem>
-      <InfoItem showMore={showMoreSeries} inline={true} title={'SERIES BADGES'}>
-        {data.series?.slice(0, showMoreSeries ? data.series.length : 8).map((data, index) => (
-          <View
-            key={index}
-            style={{ display: 'flex', flexDirection: 'column', gap: 5, alignItems: 'center' }}
-          >
-            <Image source={{ uri: API_HOST + data.icon_png }} style={{ width: 28, height: 28 }} />
-            <Text style={[styles.headerText, { flex: 0 }]}>{data.score}</Text>
-          </View>
-        ))}
-      </InfoItem>
+      {data.series?.length > 0 && (
+        <InfoItem showMore={showMoreSeries} inline={true} title={'SERIES BADGES'}>
+          {data.series?.slice(0, showMoreSeries ? data.series.length : 8).map((data, index) => (
+            <View
+              key={index}
+              style={{ display: 'flex', flexDirection: 'column', gap: 5, alignItems: 'center' }}
+            >
+              <Image source={{ uri: API_HOST + data.icon_png }} style={{ width: 28, height: 28 }} />
+              <Text style={[styles.headerText, { flex: 0 }]}>{data.score}</Text>
+            </View>
+          ))}
+        </InfoItem>
+      )}
       {data.series?.length > 8 ? (
         <TouchableOpacity onPress={() => setShowMoreSeries(!showMoreSeries)}>
           <View
@@ -221,43 +239,47 @@ const PersonalInfo: FC<PersonalInfoProps> = ({ data }) => {
           <Text style={styles.titleText}>{data.homebase2}</Text>
         </View>
       ) : null}
-      <InfoItem title={'BIO'}>
-        <Text style={[styles.titleText, { flex: 0 }]}>{data.bio}</Text>
-      </InfoItem>
-      <InfoItem title={'SOCIAL LINKS'}>
-        <View style={styles.linksBox}>
-          {data.links?.f?.link && data.links?.f?.active !== 0 ? (
-            <TouchableOpacity onPress={() => handleOpenUrl(data.links?.f?.link)}>
-              <IconFacebook fill={Colors.DARK_BLUE} />
-            </TouchableOpacity>
-          ) : null}
-          {data.links?.i?.link && data.links?.i?.active !== 0 ? (
-            <TouchableOpacity onPress={() => handleOpenUrl(data.links?.i?.link)}>
-              <IconInstagram fill={Colors.DARK_BLUE} />
-            </TouchableOpacity>
-          ) : null}
-          {data.links?.t?.link && data.links?.t?.active !== 0 ? (
-            <TouchableOpacity onPress={() => handleOpenUrl(data.links?.t?.link)}>
-              <IconTwitter fill={Colors.DARK_BLUE} />
-            </TouchableOpacity>
-          ) : null}
-          {data.links?.y?.link && data.links?.y?.active !== 0 ? (
-            <TouchableOpacity onPress={() => handleOpenUrl(data.links?.y?.link)}>
-              <IconYouTube fill={Colors.DARK_BLUE} />
-            </TouchableOpacity>
-          ) : null}
-          {data.links?.www?.link && data.links?.www?.active !== 0 ? (
-            <TouchableOpacity onPress={() => handleOpenUrl(data.links?.www?.link)}>
-              <IconGlobe fill={Colors.DARK_BLUE} />
-            </TouchableOpacity>
-          ) : null}
-          {data.links?.other?.link && data.links?.other?.active !== 0 ? (
-            <TouchableOpacity onPress={() => handleOpenUrl(data.links?.other?.link)}>
-              <IconLink fill={Colors.DARK_BLUE} />
-            </TouchableOpacity>
-          ) : null}
-        </View>
-      </InfoItem>
+      {data.bio && data.bio.length > 0 && (
+        <InfoItem title={'BIO'}>
+          <Text style={[styles.titleText, { flex: 0 }]}>{data.bio}</Text>
+        </InfoItem>
+      )}
+      {hasActiveLinks() && (
+        <InfoItem title={'SOCIAL LINKS'}>
+          <View style={styles.linksBox}>
+            {data.links?.f?.link && data.links?.f?.active !== 0 ? (
+              <TouchableOpacity onPress={() => handleOpenUrl(data.links?.f?.link)}>
+                <IconFacebook fill={Colors.DARK_BLUE} />
+              </TouchableOpacity>
+            ) : null}
+            {data.links?.i?.link && data.links?.i?.active !== 0 ? (
+              <TouchableOpacity onPress={() => handleOpenUrl(data.links?.i?.link)}>
+                <IconInstagram fill={Colors.DARK_BLUE} />
+              </TouchableOpacity>
+            ) : null}
+            {data.links?.t?.link && data.links?.t?.active !== 0 ? (
+              <TouchableOpacity onPress={() => handleOpenUrl(data.links?.t?.link)}>
+                <IconTwitter fill={Colors.DARK_BLUE} />
+              </TouchableOpacity>
+            ) : null}
+            {data.links?.y?.link && data.links?.y?.active !== 0 ? (
+              <TouchableOpacity onPress={() => handleOpenUrl(data.links?.y?.link)}>
+                <IconYouTube fill={Colors.DARK_BLUE} />
+              </TouchableOpacity>
+            ) : null}
+            {data.links?.www?.link && data.links?.www?.active !== 0 ? (
+              <TouchableOpacity onPress={() => handleOpenUrl(data.links?.www?.link)}>
+                <IconGlobe fill={Colors.DARK_BLUE} />
+              </TouchableOpacity>
+            ) : null}
+            {data.links?.other?.link && data.links?.other?.active !== 0 ? (
+              <TouchableOpacity onPress={() => handleOpenUrl(data.links?.other?.link)}>
+                <IconLink fill={Colors.DARK_BLUE} />
+              </TouchableOpacity>
+            ) : null}
+          </View>
+        </InfoItem>
+      )}
     </ScrollView>
   );
 };

+ 8 - 0
src/screens/InAppScreens/ProfileScreen/styles.ts

@@ -58,5 +58,13 @@ export const styles = StyleSheet.create({
     display: 'flex',
     justifyContent: 'center',
     alignItems: 'center'
+  },
+  settings: {
+    position: 'absolute',
+    height: 40,
+    width: 40,
+    right: -10,
+    bottom: -10,
+    justifyContent: 'center'
   }
 });

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

@@ -47,13 +47,17 @@ const TravellersScreen = () => {
 
   return (
     <PageWrapper>
+      <View style={{ alignItems: 'center', paddingVertical: 16 }}>
+        <Text style={styles.title}>Travellers</Text>
+      </View>
+
       <FlatList
         data={buttons}
         renderItem={renderItem}
         keyExtractor={(item, index) => 'key' + index}
         numColumns={2}
         contentContainerStyle={styles.container}
-        style={{ flex: 1, paddingTop: 16 }}
+        style={{ flex: 1 }}
         columnWrapperStyle={{ justifyContent: 'space-between' }}
       />
     </PageWrapper>
@@ -81,7 +85,8 @@ const styles = StyleSheet.create({
     color: Colors.DARK_BLUE,
     fontSize: 13,
     fontWeight: 'bold'
-  }
+  },
+  title: { color: Colors.DARK_BLUE, fontSize: 14, fontWeight: '600' }
 });
 
 export interface Ranking {

+ 17 - 10
src/screens/InAppScreens/TravelsScreen/Components/AccordionListItem.tsx

@@ -48,13 +48,15 @@ export const AccordionListItem = React.memo(
     onCheckboxChange,
     setIsInfoModalVisible,
     setInfoItem,
-    isSeries
+    isSeries,
+    token
   }: {
     item: SeriesGroup;
     onCheckboxChange: (subItem: SeriesItem, groupName: string, double?: boolean) => void;
     setIsInfoModalVisible: (isVisible: boolean) => void;
     setInfoItem: (item: SeriesItem) => void;
     isSeries: boolean;
+    token: string;
   }) => {
     const [isExpanded, setIsExpanded] = useState(false);
 
@@ -87,16 +89,18 @@ export const AccordionListItem = React.memo(
               <View key={index} style={styles.itemContainer}>
                 <TouchableOpacity
                   style={styles.headerContainer}
-                  onPress={() => !subItem.readonly && onCheckboxChange(subItem, item.name)}
+                  onPress={() => !subItem.readonly && token && onCheckboxChange(subItem, item.name)}
                   activeOpacity={subItem.readonly ? 1 : 0.2}
                 >
-                  <CheckBox
-                    onChange={() => !subItem.readonly && onCheckboxChange(subItem, item.name)}
-                    value={subItem.checked}
-                    color={'#0F3F4F'}
-                    disabled={subItem.readonly}
-                  />
-                  {subItem.double && (
+                  {token && (
+                    <CheckBox
+                      onChange={() => !subItem.readonly && onCheckboxChange(subItem, item.name)}
+                      value={subItem.checked}
+                      color={'#0F3F4F'}
+                      disabled={subItem.readonly}
+                    />
+                  )}
+                  {subItem.double && token && (
                     <View style={{ marginLeft: 8 }}>
                       <CheckBox
                         onChange={() => onCheckboxChange(subItem, item.name, true)}
@@ -106,7 +110,10 @@ export const AccordionListItem = React.memo(
                     </View>
                   )}
                   {subItem.icon && (
-                    <Image source={{ uri: API_HOST + subItem.icon }} style={styles.itemIcon} />
+                    <Image
+                      source={{ uri: API_HOST + subItem.icon }}
+                      style={[styles.itemIcon, !token ? { marginLeft: 0 } : {}]}
+                    />
                   )}
                   <Text style={styles.contentText}>
                     {subItem.name}

+ 40 - 31
src/screens/InAppScreens/TravelsScreen/Components/CountryItem/index.tsx

@@ -15,14 +15,16 @@ const CountryItem = React.memo(
   ({
     item,
     updateSlow,
-    openEditModal
+    openEditModal,
+    token
   }: {
     item: SlowData;
     updateSlow: (id: number, v: boolean, s11: boolean, s31: boolean, s101: boolean) => void;
     openEditModal: (item: SlowData) => void;
+    token: string;
   }) => {
     const renderDurationIcon = (condition: 0 | 1) =>
-      condition ? <CheckSvg fill={Colors.DARK_BLUE} /> : <CheckRegularSvg />;
+      condition && token ? <CheckSvg fill={Colors.DARK_BLUE} /> : <CheckRegularSvg />;
 
     return (
       <View style={styles.countryItem}>
@@ -32,7 +34,12 @@ const CountryItem = React.memo(
         </View>
         <View style={styles.divider}></View>
 
-        <View style={styles.countryDetails}>
+        <View
+          style={[
+            styles.countryDetails,
+            !token ? { alignItems: 'flex-start', flexDirection: 'column' } : {}
+          ]}
+        >
           <View style={styles.durationContainer}>
             <View style={styles.durationItem}>
               {renderDurationIcon(item.slow11)}
@@ -48,35 +55,37 @@ const CountryItem = React.memo(
             </View>
           </View>
 
-          <View style={styles.btnContainer}>
-            {item.visited ? (
-              <TouchableOpacity onPress={() => openEditModal(item)} style={styles.editScoreBtn}>
-                <EditSvg width={14} height={14} />
-              </TouchableOpacity>
-            ) : null}
-
-            <TouchableOpacity
-              style={item.visited ? styles.visitedButton : styles.markVisitedButton}
-              onPress={() =>
-                updateSlow(
-                  item.country_id,
-                  item.visited === 1 ? false : true,
-                  Boolean(item.slow11),
-                  Boolean(item.slow31),
-                  Boolean(item.slow101)
-                )
-              }
-            >
+          {token && (
+            <View style={styles.btnContainer}>
               {item.visited ? (
-                <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>
+                <TouchableOpacity onPress={() => openEditModal(item)} style={styles.editScoreBtn}>
+                  <EditSvg width={14} height={14} />
+                </TouchableOpacity>
+              ) : null}
+
+              <TouchableOpacity
+                style={item.visited ? styles.visitedButton : styles.markVisitedButton}
+                onPress={() =>
+                  updateSlow(
+                    item.country_id,
+                    item.visited === 1 ? false : true,
+                    Boolean(item.slow11),
+                    Boolean(item.slow31),
+                    Boolean(item.slow101)
+                  )
+                }
+              >
+                {item.visited ? (
+                  <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>
     );

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

@@ -17,7 +17,8 @@ export const NmRegionItem = React.memo(
   ({
     item,
     openEditModal,
-    updateNM
+    updateNM,
+    token
   }: {
     item: NmRegion;
     openEditModal: (item: NmRegion) => void;
@@ -28,6 +29,7 @@ export const NmRegionItem = React.memo(
       visits: number,
       quality: number
     ) => void;
+    token: string;
   }) => {
     const name = item.region_name.split(/ – | - /);
     const { regionData, avatars } = useRegionData(item.id);
@@ -69,7 +71,7 @@ export const NmRegionItem = React.memo(
             </View>
           )}
 
-          {item.visits > 0 && (
+          {item.visits > 0 && token && (
             <View style={styles.infoContent}>
               <RotateSvg fill={Colors.DARK_BLUE} />
               <Text style={styles.visitedButtonText}>
@@ -78,7 +80,7 @@ export const NmRegionItem = React.memo(
             </View>
           )}
 
-          {item.visits <= 0 && avatars && (
+          {(item.visits <= 0 || !token) && avatars && (
             <View style={styles.userContainer}>
               <View style={styles.userImageContainer}>
                 {avatars &&
@@ -92,37 +94,39 @@ export const NmRegionItem = React.memo(
             </View>
           )}
 
-          <View style={styles.btnContainer}>
-            {item.visits > 0 ? (
-              <TouchableOpacity onPress={() => openEditModal(item)} style={styles.editBtn}>
-                <EditSvg width={14} height={14} />
-              </TouchableOpacity>
-            ) : null}
-            <TouchableOpacity
-              style={[
-                styles.btn,
-                item.visits > 0 ? styles.visitedButton : styles.markVisitedButton
-              ]}
-              onPress={() =>
-                updateNM(
-                  item.id,
-                  item.visits > 0 ? 0 : 1,
-                  item.visits > 0 ? 0 : 1,
-                  item.visits > 0 ? 0 : 1,
-                  3
-                )
-              }
-            >
+          {token && (
+            <View style={styles.btnContainer}>
               {item.visits > 0 ? (
-                <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>
+                <TouchableOpacity onPress={() => openEditModal(item)} style={styles.editBtn}>
+                  <EditSvg width={14} height={14} />
+                </TouchableOpacity>
+              ) : null}
+              <TouchableOpacity
+                style={[
+                  styles.btn,
+                  item.visits > 0 ? styles.visitedButton : styles.markVisitedButton
+                ]}
+                onPress={() =>
+                  updateNM(
+                    item.id,
+                    item.visits > 0 ? 0 : 1,
+                    item.visits > 0 ? 0 : 1,
+                    item.visits > 0 ? 0 : 1,
+                    3
+                  )
+                }
+              >
+                {item.visits > 0 ? (
+                  <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>
     );

+ 23 - 19
src/screens/InAppScreens/TravelsScreen/Components/MyRegionsItems/RegionItem.tsx

@@ -13,11 +13,13 @@ export const RegionItem = React.memo(
   ({
     item,
     updateRegion,
-    dare = false
+    dare = false,
+    token
   }: {
     item: TCCRegion | DareRegion;
     updateRegion: (region: number, visits: 0 | 1) => void;
     dare?: boolean;
+    token: string;
   }) => {
     const name = item.name.split(/ – | - /);
     const { regionData, avatars } = useRegionData(item.id, dare);
@@ -65,24 +67,26 @@ export const RegionItem = React.memo(
               </View>
             </View>
           )}
-          <View style={styles.btnContainer}>
-            <TouchableOpacity
-              style={[
-                styles.btn,
-                +item.visited > 0 ? styles.visitedButton : styles.markVisitedButton
-              ]}
-              onPress={() => updateRegion(item.id, +item.visited > 0 ? 0 : 1)}
-            >
-              {+item.visited > 0 ? (
-                <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>
+          {token && (
+            <View style={styles.btnContainer}>
+              <TouchableOpacity
+                style={[
+                  styles.btn,
+                  +item.visited > 0 ? styles.visitedButton : styles.markVisitedButton
+                ]}
+                onPress={() => updateRegion(item.id, +item.visited > 0 ? 0 : 1)}
+              >
+                {+item.visited > 0 ? (
+                  <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>
     );

+ 4 - 4
src/screens/InAppScreens/TravelsScreen/CountriesScreen/index.tsx

@@ -20,7 +20,7 @@ import SearchIcon from '../../../../../assets/icons/search.svg';
 
 const CountriesScreen = () => {
   const token = storage.get('token', StoreType.STRING) as string;
-  const { data } = useGetSlowQuery(token, true);
+  const { data } = useGetSlowQuery(String(token));
   const { mutate: updateSlow } = usePostSetSlowMutation();
   const [slow, setSlow] = useState<SlowData[] | null>(null);
   const [megaSelectorVisible, setMegaSelectorVisible] = useState(false);
@@ -42,7 +42,7 @@ const CountriesScreen = () => {
 
   useEffect(() => {
     if (slow && slow.length) {
-      calcTotalCountries();
+      token && calcTotalCountries();
       setFilteredSlow(slow);
     }
   }, [slow]);
@@ -62,7 +62,7 @@ const CountriesScreen = () => {
     useCallback(() => {
       if (data && data.result === 'OK') {
         setSlow(data?.slow);
-        calcTotalScore();
+        token && calcTotalScore();
       }
     }, [data])
   );
@@ -136,7 +136,7 @@ const CountriesScreen = () => {
   );
 
   const renderCountryItem = ({ item }: { item: SlowData }) => (
-    <CountryItem item={item} updateSlow={handleUpdateSlow} openEditModal={handleOpenEditModal} />
+    <CountryItem item={item} updateSlow={handleUpdateSlow} openEditModal={handleOpenEditModal} token={token} />
   );
 
   return (

+ 29 - 27
src/screens/InAppScreens/TravelsScreen/DareScreen/index.tsx

@@ -22,13 +22,13 @@ import ChevronIcon from 'assets/icons/travels-screens/down-arrow.svg';
 
 const DareScreen = () => {
   const token = storage.get('token', StoreType.STRING) as string;
-  const { data: megaregions } = useGetMegaregionsDareQuery(token, true);
+  const { data: megaregions } = useGetMegaregionsDareQuery(String(token), true);
   const [megaSelectorVisible, setMegaSelectorVisible] = useState(false);
   const [selectedMega, setSelectedMega] = useState<{ name: string; id: number }>({
     id: 1,
     name: 'SOUTHERN EUROPE'
   });
-  const { data: dareData } = useGetRegionDareQuery(selectedMega.id, token, true);
+  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);
@@ -45,7 +45,7 @@ const DareScreen = () => {
 
   useEffect(() => {
     if (dareRegions && dareRegions.length) {
-      calcTotalCountries();
+      token && calcTotalCountries();
     }
   }, [dareRegions]);
 
@@ -108,7 +108,7 @@ const DareScreen = () => {
   );
 
   const renderItem = ({ item }: { item: DareRegion }) => (
-    <RegionItem item={item} dare={true} updateRegion={handleUpdateDare} />
+    <RegionItem item={item} dare={true} updateRegion={handleUpdateDare} token={token} />
   );
 
   return (
@@ -119,29 +119,31 @@ const DareScreen = () => {
         <ChevronIcon width={18} height={18} />
       </TouchableOpacity>
 
-      <View style={styles.buttonContainer}>
-        <CustomButton
-          title="All"
-          onPress={() => setContentIndex(0)}
-          isActive={contentIndex === 0}
-        />
-        <CustomButton
-          title="Not visited"
-          onPress={() => setContentIndex(1)}
-          isActive={contentIndex === 1}
-        />
-        <CustomButton
-          title="Visited"
-          onPress={() => setContentIndex(2)}
-          isActive={contentIndex === 2}
-        />
-        <CustomButton
-          title="New"
-          onPress={() => setContentIndex(3)}
-          isActive={contentIndex === 3}
-          flex={0.6}
-        />
-      </View>
+      {token && (
+        <View style={styles.buttonContainer}>
+          <CustomButton
+            title="All"
+            onPress={() => setContentIndex(0)}
+            isActive={contentIndex === 0}
+          />
+          <CustomButton
+            title="Not visited"
+            onPress={() => setContentIndex(1)}
+            isActive={contentIndex === 1}
+          />
+          <CustomButton
+            title="Visited"
+            onPress={() => setContentIndex(2)}
+            isActive={contentIndex === 2}
+          />
+          <CustomButton
+            title="New"
+            onPress={() => setContentIndex(3)}
+            isActive={contentIndex === 3}
+            flex={0.6}
+          />
+        </View>
+      )}
 
       <View style={styles.progressHeader}>
         <Text style={styles.textSmall}>Visited places</Text>

+ 11 - 7
src/screens/InAppScreens/TravelsScreen/EarthScreen/index.tsx

@@ -29,9 +29,9 @@ const EarthScreen = () => {
   const [quadrantsAll, setQuadrantsAll] = useState<number>(1);
 
   useEffect(() => {
-    if (!data) return;
-    setQuadrantsAll(data.regions.length);
-    setVisited(data.visited);
+    if (!data || !data.regions) return;
+    setQuadrantsAll(data.regions?.length);
+    token && setVisited(data.visited);
   }, [data]);
 
   useEffect(() => {
@@ -76,6 +76,8 @@ const EarthScreen = () => {
   };
 
   const markQuadrant = async (qid: number) => {
+    if (!token) return;
+
     let vis: 0 | 1 = 1;
     if (qid > 612) {
       if (Math.max(...visited) > 612) {
@@ -148,10 +150,12 @@ const EarthScreen = () => {
     <SafeAreaView style={{ height: '100%' }}>
       <View style={styles.wrapper}>
         <Header label={'My Earth'} />
-        <View style={styles.score}>
-          <Text style={[styles.scoreText, { fontWeight: 'bold' }]}>Your score: {score.base}</Text>
-          <Text style={[styles.scoreText, { fontSize: 14 }]}>{`(${score.percentage}%)`}</Text>
-        </View>
+        {token && (
+          <View style={styles.score}>
+            <Text style={[styles.scoreText, { fontWeight: 'bold' }]}>Your score: {score.base}</Text>
+            <Text style={[styles.scoreText, { fontSize: 14 }]}>{`(${score.percentage}%)`}</Text>
+          </View>
+        )}
       </View>
 
       <View style={styles.container}>

+ 23 - 21
src/screens/InAppScreens/TravelsScreen/RegionsScreen/index.tsx

@@ -32,7 +32,7 @@ const RegionsScreen = () => {
     id: 1,
     name: 'SOUTHERN EUROPE'
   });
-  const { data: regionsQe } = useGetRegionQeQuery(selectedMega.id, token, true);
+  const { data: regionsQe } = useGetRegionQeQuery(selectedMega.id, String(token), true);
   const [total, setTotal] = useState(0);
   const [isEditModalVisible, setIsEditModalVisible] = useState(false);
   const [contentIndex, setContentIndex] = useState(0);
@@ -133,7 +133,7 @@ const RegionsScreen = () => {
   );
 
   useEffect(() => {
-    if (nmRegions && nmRegions.length) {
+    if (nmRegions && nmRegions.length && token) {
       calcTotalCountries();
     }
   }, [nmRegions]);
@@ -182,7 +182,7 @@ const RegionsScreen = () => {
   };
 
   const renderItem = ({ item }: { item: NmRegion }) => (
-    <NmRegionItem item={item} openEditModal={handleOpenEditModal} updateNM={handleUpdateNM} />
+    <NmRegionItem item={item} openEditModal={handleOpenEditModal} updateNM={handleUpdateNM} token={token} />
   );
 
   return (
@@ -193,23 +193,25 @@ const RegionsScreen = () => {
         <ChevronIcon width={18} height={18} />
       </TouchableOpacity>
 
-      <View style={styles.buttonContainer}>
-        <CustomButton
-          title="All"
-          onPress={() => setContentIndex(0)}
-          isActive={contentIndex === 0}
-        />
-        <CustomButton
-          title="Not visited"
-          onPress={() => setContentIndex(1)}
-          isActive={contentIndex === 1}
-        />
-        <CustomButton
-          title="Visited"
-          onPress={() => setContentIndex(2)}
-          isActive={contentIndex === 2}
-        />
-      </View>
+      {token && (
+        <View style={styles.buttonContainer}>
+          <CustomButton
+            title="All"
+            onPress={() => setContentIndex(0)}
+            isActive={contentIndex === 0}
+          />
+          <CustomButton
+            title="Not visited"
+            onPress={() => setContentIndex(1)}
+            isActive={contentIndex === 1}
+          />
+          <CustomButton
+            title="Visited"
+            onPress={() => setContentIndex(2)}
+            isActive={contentIndex === 2}
+          />
+        </View>
+      )}
 
       <View style={styles.progressHeader}>
         <Text style={styles.textSmall}>Visited regions</Text>
@@ -242,7 +244,7 @@ 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} />
+                  <RegionItem item={item} updateRegion={handleUpdateTCC} key={item.id} token={token} />
                 ))}
               </View>
             ) : null

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

@@ -96,7 +96,7 @@ const SeriesList = React.memo(({ groupId, navigation }: { groupId: string; navig
     const fetchSeriesData = async () => {
       try {
         await mutateAsync(
-          { group: groupId, token: token },
+          { group: groupId, token: String(token) },
           {
             onSuccess: (data) => {
               setSeriesData(data.data);
@@ -134,7 +134,7 @@ const SeriesList = React.memo(({ groupId, navigation }: { groupId: string; navig
           <View style={styles.detailsContainer}>
             <Text style={styles.details}>Total objects: {item.count}</Text>
             <Text style={styles.count}>
-              {item.score} / {item.count}
+              {token ? item.score : 0} / {item.count}
             </Text>
           </View>
         </View>

+ 33 - 26
src/screens/InAppScreens/TravelsScreen/SeriesItemScreen/index.tsx

@@ -58,7 +58,7 @@ export const SeriesItemScreen = ({ route }: { route: any }) => {
   useFocusEffect(
     useCallback(() => {
       const fetchGroups = async () => {
-        const data = await fetchItemsForSeries(token, id);
+        const data = await fetchItemsForSeries(String(token), id);
         if (!data) {
           setIsLoading(false);
           return;
@@ -148,6 +148,7 @@ export const SeriesItemScreen = ({ route }: { route: any }) => {
             setIsInfoModalVisible={setIsInfoModalVisible}
             setInfoItem={setInfoItem}
             isSeries={id === -1}
+            token={token}
           />
         )}
       />
@@ -247,31 +248,37 @@ export const SeriesItemScreen = ({ route }: { route: any }) => {
         value={search}
         icon={<SearchIcon fill={'#C8C8C8'} width={14} height={14} />}
       />
-      <TabView
-        navigationState={{ index, routes }}
-        renderScene={renderScene}
-        onIndexChange={(i) => {
-          handleIndexChange(i);
-          setIndex(i);
-        }}
-        lazy={true}
-        renderTabBar={(props) => (
-          <TabBar
-            {...props}
-            indicatorStyle={{ backgroundColor: Colors.DARK_BLUE }}
-            style={styles.tabBar}
-            tabStyle={styles.tabStyle}
-            pressColor={'transparent'}
-            renderLabel={({ route, focused }) => (
-              <Text
-                style={[styles.tabLabel, { color: Colors.DARK_BLUE, opacity: focused ? 1 : 0.4 }]}
-              >
-                {route.title}
-              </Text>
-            )}
-          />
-        )}
-      />
+      {token ? (
+        <TabView
+          navigationState={{ index, routes }}
+          renderScene={renderScene}
+          onIndexChange={(i) => {
+            handleIndexChange(i);
+            setIndex(i);
+          }}
+          lazy={true}
+          renderTabBar={(props) => (
+            <TabBar
+              {...props}
+              indicatorStyle={{ backgroundColor: Colors.DARK_BLUE }}
+              style={styles.tabBar}
+              tabStyle={styles.tabStyle}
+              pressColor={'transparent'}
+              renderLabel={({ route, focused }) => (
+                <Text
+                  style={[styles.tabLabel, { color: Colors.DARK_BLUE, opacity: focused ? 1 : 0.4 }]}
+                >
+                  {route.title}
+                </Text>
+              )}
+            />
+          )}
+        />
+      ) : (
+        <View style={{paddingTop: 8, flex: 1}}>
+          {renderScene({ route: routes[index] })}
+        </View>
+      )}
     </PageWrapper>
   );
 };

+ 7 - 3
src/screens/InAppScreens/TravelsScreen/index.tsx

@@ -31,7 +31,7 @@ const TravelsScreen = () => {
   ];
 
   const handlePress = (page: string) => {
-    if (!token) {
+    if (!token && (page === NAVIGATION_PAGES.TRIPS || page === NAVIGATION_PAGES.PHOTOS)) {
       setIsModalVisible(true);
     } else {
       navigation.navigate(page as never);
@@ -53,13 +53,16 @@ const TravelsScreen = () => {
 
   return (
     <PageWrapper>
+      <View style={{ alignItems: 'center', paddingVertical: 16 }}>
+        <Text style={styles.title}>Travels</Text>
+      </View>
       <FlatList
         data={buttons}
         renderItem={renderItem}
         keyExtractor={(item, index) => 'key' + index}
         numColumns={2}
         contentContainerStyle={styles.container}
-        style={{ flex: 1, paddingTop: 16 }}
+        style={{ flex: 1 }}
         columnWrapperStyle={{ justifyContent: 'space-between' }}
       />
       <WarningModal
@@ -92,7 +95,8 @@ const styles = StyleSheet.create({
     color: Colors.DARK_BLUE,
     fontSize: 13,
     fontWeight: 'bold'
-  }
+  },
+  title: { color: Colors.DARK_BLUE, fontSize: 14, fontWeight: '600' }
 });
 
 export default TravelsScreen;

+ 23 - 7
src/screens/RegisterScreen/JoinUs/index.tsx

@@ -1,5 +1,5 @@
 import React, { FC } from 'react';
-import { View, Text, ScrollView } from 'react-native';
+import { View, Text, ScrollView, TouchableOpacity, Linking } from 'react-native';
 import { NavigationProp } from '@react-navigation/native';
 import { Formik } from 'formik';
 import * as yup from 'yup';
@@ -9,6 +9,7 @@ import { PageWrapper, Header, Button, BigText, CheckBox, Input } from '../../../
 import { NAVIGATION_PAGES } from '../../../types';
 import { useJoinTestMutation } from '@api/auth';
 import store from '../../../storage/zustand';
+import { styles } from './styles';
 
 type Props = {
   navigation: NavigationProp<any>;
@@ -108,12 +109,27 @@ const JoinUsScreen: FC<Props> = ({ navigation }) => {
                     isPrivate={true}
                     formikError={props.touched.repeatPassword && props.errors.repeatPassword}
                   />
-                  <CheckBox
-                    label={'I accept NM Terms & Conditions'}
-                    onChange={(value) => props.setFieldValue('checkboxAgreed', value)}
-                    value={props.values.checkboxAgreed}
-                  />
-                  <Text>
+                  <TouchableOpacity
+                    onPress={() =>
+                      props.setFieldValue('checkboxAgreed', !props.values.checkboxAgreed)
+                    }
+                    style={styles.checkbox}
+                  >
+                    <CheckBox
+                      onChange={(value) => props.setFieldValue('checkboxAgreed', value)}
+                      value={props.values.checkboxAgreed}
+                    />
+                    <Text style={styles.text}>
+                      I accept{' '}
+                      <Text
+                        onPress={() => Linking.openURL('https://nomadmania.com/terms/')}
+                        style={styles.checkboxLink}
+                      >
+                        NM Terms & Conditions
+                      </Text>
+                    </Text>
+                  </TouchableOpacity>
+                  <Text style={styles.textError}>
                     {props.touched.checkboxAgreed && props.errors.checkboxAgreed
                       ? 'To use our service you need to agree'
                       : null}

+ 24 - 0
src/screens/RegisterScreen/JoinUs/styles.tsx

@@ -0,0 +1,24 @@
+import { StyleSheet } from 'react-native';
+import { Colors } from 'src/theme';
+import { getFontSize } from 'src/utils';
+
+export const styles = StyleSheet.create({
+  checkbox: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    gap: 8
+  },
+  text: {
+    color: Colors.DARK_BLUE,
+    fontSize: getFontSize(12)
+  },
+  textError: {
+    fontSize: getFontSize(12),
+    fontWeight: '500',
+    color: '#EF5B5B'
+  },
+  checkboxLink: {
+    textDecorationLine: 'underline',
+    textDecorationColor: Colors.DARK_BLUE
+  }
+});

+ 27 - 21
src/screens/WelcomeScreen/index.tsx

@@ -8,8 +8,6 @@ import { ButtonVariants } from '../../types/components';
 import { styles } from './style';
 import { NAVIGATION_PAGES } from '../../types';
 
-import LogoSVG from '../../../assets/images/logo.svg';
-
 type Props = {
   navigation: NavigationProp<any>;
 };
@@ -24,26 +22,34 @@ const WelcomeScreen: FC<Props> = ({ navigation }) => {
         <View style={styles.overlay} />
 
         <SafeAreaView style={styles.wrapper}>
-          <LogoSVG style={{ display: 'flex', alignSelf: 'center' }} />
+          <View />
+          <View style={{ width: 150, height: 150, alignSelf: 'center' }}>
+            <ImageBackground
+              source={require('../../../assets/logo-ua.png')}
+              style={{ height: '100%' }}
+            />
+          </View>
+          <View>
+            <Text style={styles.text}>Ultimate Hub</Text>
+            <Text style={[styles.text, styles.subText]}>For Global Explorers</Text>
+          </View>
+
           <View style={styles.buttonsAndText}>
-            <Text style={styles.text}>Endless exploring...</Text>
-            <View style={{ gap: 10 }}>
-              <Button onPress={() => navigation.navigate(NAVIGATION_PAGES.REGISTER)}>
-                Get started
-              </Button>
-              <Button
-                onPress={() => navigation.navigate(NAVIGATION_PAGES.LOGIN)}
-                variant={ButtonVariants.OPACITY}
-              >
-                Login
-              </Button>
-              <Button
-                onPress={() => navigation.navigate(NAVIGATION_PAGES.IN_APP)}
-                variant={ButtonVariants.OPACITY}
-              >
-                Use without registration
-              </Button>
-            </View>
+            <Button onPress={() => navigation.navigate(NAVIGATION_PAGES.REGISTER)}>
+              Get started
+            </Button>
+            <Button
+              onPress={() => navigation.navigate(NAVIGATION_PAGES.LOGIN)}
+              variant={ButtonVariants.OPACITY}
+            >
+              Login
+            </Button>
+            <Button
+              onPress={() => navigation.navigate(NAVIGATION_PAGES.IN_APP)}
+              variant={ButtonVariants.OPACITY}
+            >
+              Use without registration
+            </Button>
           </View>
         </SafeAreaView>
       </ImageBackground>

+ 6 - 3
src/screens/WelcomeScreen/style.ts

@@ -10,17 +10,20 @@ export const styles = StyleSheet.create({
   text: {
     fontFamily: 'redhat-900',
     color: Colors.WHITE,
-    fontSize: getFontSize(60),
+    fontSize: getFontSize(50),
     alignSelf: 'center'
   },
+  subText: {
+    fontSize: getFontSize(24),
+  },
   wrapper: {
     display: 'flex',
     height: '100%',
     justifyContent: 'space-around',
-    margin: '5%'
+    marginHorizontal: '5%'
   },
   buttonsAndText: {
     display: 'flex',
-    gap: 10
+    gap: 12
   }
 });