فهرست منبع

update profile

Viktoriia 1 سال پیش
والد
کامیت
307296b153
27فایلهای تغییر یافته به همراه995 افزوده شده و 416 حذف شده
  1. 28 0
      Route.tsx
  2. 1 1
      assets/icons/travels-section/compass.svg
  3. 2 2
      assets/icons/travels-section/flags.svg
  4. 2 2
      assets/icons/travels-section/images.svg
  5. 0 0
      assets/icons/travels-section/regions.svg
  6. 3 3
      assets/icons/travels-section/series.svg
  7. 4 0
      assets/icons/travels-section/whs.svg
  8. 1 1
      src/modules/api/regions/regions-api.tsx
  9. 3 0
      src/modules/api/user/queries/index.ts
  10. 17 0
      src/modules/api/user/queries/use-post-get-profile-data.tsx
  11. 17 0
      src/modules/api/user/queries/use-post-get-profile-updates.tsx
  12. 27 0
      src/modules/api/user/queries/use-post-load-friends-app.tsx
  13. 124 3
      src/modules/api/user/user-api.tsx
  14. 4 1
      src/modules/api/user/user-query-keys.tsx
  15. 2 2
      src/screens/InAppScreens/MapScreen/RegionViewScreen/index.tsx
  16. 2 1
      src/screens/InAppScreens/MapScreen/RegionViewScreen/styles.tsx
  17. 46 8
      src/screens/InAppScreens/MapScreen/UsersListScreen/index.tsx
  18. 3 4
      src/screens/InAppScreens/MapScreen/index.tsx
  19. 27 0
      src/screens/InAppScreens/ProfileScreen/Components/InfoItem.tsx
  20. 359 0
      src/screens/InAppScreens/ProfileScreen/Components/PersonalInfo.tsx
  21. 93 0
      src/screens/InAppScreens/ProfileScreen/Components/styles.tsx
  22. 32 0
      src/screens/InAppScreens/ProfileScreen/UnauthenticatedProfileScreen/index.tsx
  23. 4 4
      src/screens/InAppScreens/ProfileScreen/UsersMap/index.tsx
  24. 177 351
      src/screens/InAppScreens/ProfileScreen/index.tsx
  25. 5 30
      src/screens/InAppScreens/ProfileScreen/styles.ts
  26. 11 3
      src/types/api.ts
  27. 1 0
      src/types/navigation.ts

+ 28 - 0
Route.tsx

@@ -239,6 +239,11 @@ const Route = () => {
                     component={UsersListScreen}
                     options={regionViewScreenOptions}
                   />
+                  <ScreenStack.Screen
+                    name={NAVIGATION_PAGES.FRIENDS_LIST}
+                    component={UsersListScreen}
+                    options={regionViewScreenOptions}
+                  />
                 </ScreenStack.Navigator>
               )}
             </BottomTab.Screen>
@@ -291,6 +296,11 @@ const Route = () => {
                     name={NAVIGATION_PAGES.USERS_MAP}
                     component={UsersMapScreen}
                   />
+                  <ScreenStack.Screen
+                    name={NAVIGATION_PAGES.FRIENDS_LIST}
+                    component={UsersListScreen}
+                    options={regionViewScreenOptions}
+                  />
                 </ScreenStack.Navigator>
               )}
             </BottomTab.Screen>
@@ -353,6 +363,11 @@ const Route = () => {
                     name={NAVIGATION_PAGES.SUGGEST_SERIES}
                     component={SuggestSeriesScreen}
                   />
+                  <ScreenStack.Screen
+                    name={NAVIGATION_PAGES.FRIENDS_LIST}
+                    component={UsersListScreen}
+                    options={regionViewScreenOptions}
+                  />
                 </ScreenStack.Navigator>
               )}
             </BottomTab.Screen>
@@ -368,6 +383,19 @@ const Route = () => {
                     component={EditPersonalInfo}
                   />
                   <ScreenStack.Screen name={NAVIGATION_PAGES.SETTINGS} component={Settings} />
+                  <ScreenStack.Screen
+                    name={NAVIGATION_PAGES.FRIENDS_LIST}
+                    component={UsersListScreen}
+                    options={regionViewScreenOptions}
+                  />
+                  <ScreenStack.Screen
+                    name={NAVIGATION_PAGES.PUBLIC_PROFILE_VIEW}
+                    component={ProfileScreen}
+                  />
+                  <ScreenStack.Screen
+                    name={NAVIGATION_PAGES.USERS_MAP}
+                    component={UsersMapScreen}
+                  />
                 </ScreenStack.Navigator>
               )}
             </BottomTab.Screen>

+ 1 - 1
assets/icons/travels-section/compass.svg

@@ -1,6 +1,6 @@
 <svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
 <g clip-path="url(#clip0_3481_40858)">
-<path d="M16.75 32C20.9935 32 25.0631 30.3143 28.0637 27.3137C31.0643 24.3131 32.75 20.2435 32.75 16C32.75 11.7565 31.0643 7.68687 28.0637 4.68629C25.0631 1.68571 20.9935 0 16.75 0C12.5065 0 8.43687 1.68571 5.43629 4.68629C2.43571 7.68687 0.75 11.7565 0.75 16C0.75 20.2435 2.43571 24.3131 5.43629 27.3137C8.43687 30.3143 12.5065 32 16.75 32ZM19.9188 20.3188L10.9 23.7875C9.6875 24.2563 8.49375 23.0625 8.9625 21.85L12.4312 12.8313C12.6375 12.3 13.05 11.8875 13.5813 11.6812L22.6 8.2125C23.8125 7.74375 25.0063 8.9375 24.5375 10.15L21.0688 19.1688C20.8687 19.7 20.45 20.1125 19.9188 20.3188ZM18.75 16C18.75 15.4696 18.5393 14.9609 18.1642 14.5858C17.7891 14.2107 17.2804 14 16.75 14C16.2196 14 15.7109 14.2107 15.3358 14.5858C14.9607 14.9609 14.75 15.4696 14.75 16C14.75 16.5304 14.9607 17.0391 15.3358 17.4142C15.7109 17.7893 16.2196 18 16.75 18C17.2804 18 17.7891 17.7893 18.1642 17.4142C18.5393 17.0391 18.75 16.5304 18.75 16Z" fill="#ED9334"/>
+<path d="M16.75 32C20.9935 32 25.0631 30.3143 28.0637 27.3137C31.0643 24.3131 32.75 20.2435 32.75 16C32.75 11.7565 31.0643 7.68687 28.0637 4.68629C25.0631 1.68571 20.9935 0 16.75 0C12.5065 0 8.43687 1.68571 5.43629 4.68629C2.43571 7.68687 0.75 11.7565 0.75 16C0.75 20.2435 2.43571 24.3131 5.43629 27.3137C8.43687 30.3143 12.5065 32 16.75 32ZM19.9188 20.3188L10.9 23.7875C9.6875 24.2563 8.49375 23.0625 8.9625 21.85L12.4312 12.8313C12.6375 12.3 13.05 11.8875 13.5813 11.6812L22.6 8.2125C23.8125 7.74375 25.0063 8.9375 24.5375 10.15L21.0688 19.1688C20.8687 19.7 20.45 20.1125 19.9188 20.3188ZM18.75 16C18.75 15.4696 18.5393 14.9609 18.1642 14.5858C17.7891 14.2107 17.2804 14 16.75 14C16.2196 14 15.7109 14.2107 15.3358 14.5858C14.9607 14.9609 14.75 15.4696 14.75 16C14.75 16.5304 14.9607 17.0391 15.3358 17.4142C15.7109 17.7893 16.2196 18 16.75 18C17.2804 18 17.7891 17.7893 18.1642 17.4142C18.5393 17.0391 18.75 16.5304 18.75 16Z"/>
 </g>
 <defs>
 <clipPath id="clip0_3481_40858">

+ 2 - 2
assets/icons/travels-section/flags.svg

@@ -1,7 +1,7 @@
 <svg width="30" height="32" viewBox="0 0 30 32" fill="none" xmlns="http://www.w3.org/2000/svg">
 <g clip-path="url(#clip0_2651_21759)">
-<path d="M6.80395 0C5.84719 0 5.07422 0.772975 5.07422 1.72973V3.45946V4.15066C5.20769 4.39611 5.29044 4.67341 5.29044 4.97298V5.83785L9.02017 4.90267C11.0796 4.38915 13.258 4.62699 15.1553 5.57834C17.658 6.8324 20.6039 6.8324 23.1067 5.57834L23.6256 5.31884C24.7391 4.76208 26.0472 5.57296 26.0472 6.8162V19.3655C26.1283 19.3374 26.2099 19.3114 26.2905 19.2811L28.1661 18.5784C28.8418 18.3243 29.2905 17.6811 29.2905 16.9567V3.57296C29.2905 2.32971 27.9823 1.51884 26.8688 2.07559L26.3499 2.3351C23.8472 3.58915 20.9013 3.58915 18.3986 2.3351C16.5013 1.38374 14.3229 1.14591 12.2634 1.65942L8.53368 2.5946V1.72973C8.53368 0.772975 7.76071 0 6.80395 0ZM8.53368 21.4581L5.29044 22.2703V26.7684C5.58328 27.3067 6.14685 27.6757 6.80395 27.6757C7.76071 27.6757 8.53368 26.9027 8.53368 25.946V21.4581Z" fill="#ED9334"/>
-<path d="M4.20946 6.05395C4.20946 5.09719 3.43649 4.32422 2.47973 4.32422C1.52297 4.32422 0.75 5.09719 0.75 6.05395V7.78368V24.2161V30.2702C0.75 31.227 1.52297 31.9999 2.47973 31.9999C3.43649 31.9999 4.20946 31.227 4.20946 30.2702V23.3513L7.68515 22.481C9.90677 21.9242 12.2581 22.1837 14.3068 23.2053C16.696 24.3999 19.4689 24.5459 21.9662 23.6053L23.8419 22.9026C24.5176 22.6486 24.9662 22.0053 24.9662 21.281V7.8972C24.9662 6.65395 23.6581 5.84314 22.5446 6.3999L22.0257 6.65936C19.523 7.91341 16.577 7.91341 14.0743 6.65936C12.177 5.708 9.99866 5.47017 7.9392 5.98368L4.20946 6.91882V6.05395Z" fill="#ED9334"/>
+<path d="M6.80395 0C5.84719 0 5.07422 0.772975 5.07422 1.72973V3.45946V4.15066C5.20769 4.39611 5.29044 4.67341 5.29044 4.97298V5.83785L9.02017 4.90267C11.0796 4.38915 13.258 4.62699 15.1553 5.57834C17.658 6.8324 20.6039 6.8324 23.1067 5.57834L23.6256 5.31884C24.7391 4.76208 26.0472 5.57296 26.0472 6.8162V19.3655C26.1283 19.3374 26.2099 19.3114 26.2905 19.2811L28.1661 18.5784C28.8418 18.3243 29.2905 17.6811 29.2905 16.9567V3.57296C29.2905 2.32971 27.9823 1.51884 26.8688 2.07559L26.3499 2.3351C23.8472 3.58915 20.9013 3.58915 18.3986 2.3351C16.5013 1.38374 14.3229 1.14591 12.2634 1.65942L8.53368 2.5946V1.72973C8.53368 0.772975 7.76071 0 6.80395 0ZM8.53368 21.4581L5.29044 22.2703V26.7684C5.58328 27.3067 6.14685 27.6757 6.80395 27.6757C7.76071 27.6757 8.53368 26.9027 8.53368 25.946V21.4581Z"/>
+<path d="M4.20946 6.05395C4.20946 5.09719 3.43649 4.32422 2.47973 4.32422C1.52297 4.32422 0.75 5.09719 0.75 6.05395V7.78368V24.2161V30.2702C0.75 31.227 1.52297 31.9999 2.47973 31.9999C3.43649 31.9999 4.20946 31.227 4.20946 30.2702V23.3513L7.68515 22.481C9.90677 21.9242 12.2581 22.1837 14.3068 23.2053C16.696 24.3999 19.4689 24.5459 21.9662 23.6053L23.8419 22.9026C24.5176 22.6486 24.9662 22.0053 24.9662 21.281V7.8972C24.9662 6.65395 23.6581 5.84314 22.5446 6.3999L22.0257 6.65936C19.523 7.91341 16.577 7.91341 14.0743 6.65936C12.177 5.708 9.99866 5.47017 7.9392 5.98368L4.20946 6.91882V6.05395Z"/>
 </g>
 <defs>
 <clipPath id="clip0_2651_21759">

+ 2 - 2
assets/icons/travels-section/images.svg

@@ -1,7 +1,7 @@
 <svg width="37" height="32" viewBox="0 0 37 32" fill="none" xmlns="http://www.w3.org/2000/svg">
 <g clip-path="url(#clip0_2651_21800)">
-<path d="M33.75 24.5C33.75 25.3312 34.4187 26 35.25 26C36.0812 26 36.75 25.3312 36.75 24.5V10.5C36.75 5.80625 32.9437 2 28.25 2H8.25C7.41875 2 6.75 2.66875 6.75 3.5C6.75 4.33125 7.41875 5 8.25 5H28.25C31.2875 5 33.75 7.4625 33.75 10.5V24.5Z" fill="#ED9334"/>
-<path d="M4.75 8C2.54375 8 0.75 9.79375 0.75 12V26C0.75 28.2062 2.54375 30 4.75 30H26.75C28.9562 30 30.75 28.2062 30.75 26V12C30.75 9.79375 28.9562 8 26.75 8H4.75ZM19.5 14.6688L25.5 23.6688C25.8062 24.1313 25.8375 24.7188 25.575 25.2062C25.3125 25.6937 24.8062 26 24.25 26H15.25H12.25H7.25C6.675 26 6.15 25.6687 5.9 25.15C5.65 24.6313 5.71875 24.0125 6.08125 23.5625L10.0813 18.5625C10.3688 18.2063 10.7938 18 11.25 18C11.7063 18 12.1375 18.2063 12.4188 18.5625L13.5 19.9125L17 14.6625C17.2812 14.25 17.75 14 18.25 14C18.75 14 19.2188 14.25 19.5 14.6688ZM6.75 14C6.75 12.8954 7.64543 12 8.75 12C9.85457 12 10.75 12.8954 10.75 14C10.75 15.1046 9.85457 16 8.75 16C7.64543 16 6.75 15.1046 6.75 14Z" fill="#ED9334"/>
+<path d="M33.75 24.5C33.75 25.3312 34.4187 26 35.25 26C36.0812 26 36.75 25.3312 36.75 24.5V10.5C36.75 5.80625 32.9437 2 28.25 2H8.25C7.41875 2 6.75 2.66875 6.75 3.5C6.75 4.33125 7.41875 5 8.25 5H28.25C31.2875 5 33.75 7.4625 33.75 10.5V24.5Z" />
+<path d="M4.75 8C2.54375 8 0.75 9.79375 0.75 12V26C0.75 28.2062 2.54375 30 4.75 30H26.75C28.9562 30 30.75 28.2062 30.75 26V12C30.75 9.79375 28.9562 8 26.75 8H4.75ZM19.5 14.6688L25.5 23.6688C25.8062 24.1313 25.8375 24.7188 25.575 25.2062C25.3125 25.6937 24.8062 26 24.25 26H15.25H12.25H7.25C6.675 26 6.15 25.6687 5.9 25.15C5.65 24.6313 5.71875 24.0125 6.08125 23.5625L10.0813 18.5625C10.3688 18.2063 10.7938 18 11.25 18C11.7063 18 12.1375 18.2063 12.4188 18.5625L13.5 19.9125L17 14.6625C17.2812 14.25 17.75 14 18.25 14C18.75 14 19.2188 14.25 19.5 14.6688ZM6.75 14C6.75 12.8954 7.64543 12 8.75 12C9.85457 12 10.75 12.8954 10.75 14C10.75 15.1046 9.85457 16 8.75 16C7.64543 16 6.75 15.1046 6.75 14Z"/>
 </g>
 <defs>
 <clipPath id="clip0_2651_21800">

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
assets/icons/travels-section/regions.svg


+ 3 - 3
assets/icons/travels-section/series.svg

@@ -1,8 +1,8 @@
 <svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
 <g clip-path="url(#clip0_2651_21776)">
-<path d="M24.4673 21.6846C23.8809 22.4799 23.3483 23.1757 22.8733 23.7799C23.0703 24.2118 23.4677 24.5452 23.9688 24.6377C25.8562 24.9814 27.325 25.4377 28.2812 25.9189C28.4813 26.0189 28.6438 26.1127 28.775 26.2002C29 26.3502 29 26.6503 28.775 26.8003C28.225 27.1565 27.3313 27.5377 26.0875 27.8815C23.6438 28.5627 20.1562 29.0002 16.25 29.0002C12.3438 29.0002 8.85629 28.5627 6.3938 27.8815C5.15005 27.5377 4.2563 27.1565 3.7063 26.8003C3.4813 26.6503 3.4813 26.3502 3.7063 26.2002C3.83755 26.1127 4.0062 26.0189 4.19995 25.9189C5.1562 25.4377 6.62496 24.9877 8.51245 24.6377H8.5188C8.74266 24.5964 8.94302 24.5038 9.11719 24.3792C8.54201 23.6325 7.90046 22.7744 7.23206 21.8334C5.51721 22.1887 4.00972 22.6583 2.8562 23.2378C2.19995 23.569 1.58752 23.969 1.11877 24.4628C0.650025 24.969 0.25 25.6565 0.25 26.5002C0.25 27.8377 1.21873 28.7565 2.06873 29.3127C2.98747 29.9127 4.2125 30.394 5.59375 30.7753C8.3875 31.544 12.15 32.0002 16.25 32.0002C20.35 32.0002 24.1125 31.544 26.9 30.7753C28.2813 30.394 29.5126 29.9127 30.425 29.3127C31.2813 28.7565 32.2438 27.8377 32.2438 26.5002C32.2438 25.6565 31.8438 24.969 31.3688 24.4628C30.9 23.969 30.2875 23.569 29.6312 23.2378C28.3187 22.5753 26.5375 22.0565 24.5062 21.6877C24.4931 21.6853 24.4804 21.6866 24.4673 21.6846Z" fill="#ED9334"/>
-<path d="M21.1572 24.375C23.6621 21.2402 29.375 13.6426 29.375 9.375C29.375 4.19922 25.1758 0 20 0C14.8242 0 10.625 4.19922 10.625 9.375C10.625 13.6426 16.3379 21.2402 18.8428 24.375C19.4434 25.1221 20.5566 25.1221 21.1572 24.375ZM20 6.25C20.4104 6.24988 20.8169 6.33063 21.1962 6.48762C21.5754 6.64462 21.92 6.87478 22.2103 7.16498C22.5006 7.45517 22.7308 7.79971 22.8879 8.17891C23.045 8.55811 23.1259 8.96454 23.1259 9.375C23.1259 9.78546 23.045 10.1919 22.8879 10.5711C22.7308 10.9503 22.5006 11.2948 22.2103 11.585C21.92 11.8752 21.5754 12.1054 21.1962 12.2624C20.8169 12.4194 20.4104 12.5001 20 12.5C19.5895 12.5001 19.1831 12.4194 18.8038 12.2624C18.4246 12.1054 18.08 11.8752 17.7897 11.585C17.4994 11.2948 17.2692 10.9503 17.1121 10.5711C16.955 10.1919 16.8741 9.78546 16.8741 9.375C16.8741 8.96454 16.955 8.55811 17.1121 8.17891C17.2692 7.79971 17.4994 7.45517 17.7897 7.16498C18.08 6.87478 18.4246 6.64462 18.8038 6.48762C19.1831 6.33063 19.5895 6.24988 20 6.25Z" fill="#ED9334"/>
-<path d="M12.5 1.25C7.32422 1.25 3.125 5.44922 3.125 10.625C3.125 14.8926 8.83789 22.4902 11.3428 25.625C11.9434 26.3721 13.0566 26.3721 13.6572 25.625C14.3048 24.8145 15.168 23.7026 16.0903 22.4302C14.0543 19.7326 11.3943 15.8857 10.1165 12.6082C9.55547 11.9279 9.30799 11.0422 9.43506 10.1697C9.39663 9.89438 9.375 9.62844 9.375 9.375C9.375 5.97578 11.1864 2.99788 13.896 1.354C13.4405 1.28593 12.9744 1.25 12.5 1.25Z" fill="#ED9334"/>
+<path d="M24.4673 21.6846C23.8809 22.4799 23.3483 23.1757 22.8733 23.7799C23.0703 24.2118 23.4677 24.5452 23.9688 24.6377C25.8562 24.9814 27.325 25.4377 28.2812 25.9189C28.4813 26.0189 28.6438 26.1127 28.775 26.2002C29 26.3502 29 26.6503 28.775 26.8003C28.225 27.1565 27.3313 27.5377 26.0875 27.8815C23.6438 28.5627 20.1562 29.0002 16.25 29.0002C12.3438 29.0002 8.85629 28.5627 6.3938 27.8815C5.15005 27.5377 4.2563 27.1565 3.7063 26.8003C3.4813 26.6503 3.4813 26.3502 3.7063 26.2002C3.83755 26.1127 4.0062 26.0189 4.19995 25.9189C5.1562 25.4377 6.62496 24.9877 8.51245 24.6377H8.5188C8.74266 24.5964 8.94302 24.5038 9.11719 24.3792C8.54201 23.6325 7.90046 22.7744 7.23206 21.8334C5.51721 22.1887 4.00972 22.6583 2.8562 23.2378C2.19995 23.569 1.58752 23.969 1.11877 24.4628C0.650025 24.969 0.25 25.6565 0.25 26.5002C0.25 27.8377 1.21873 28.7565 2.06873 29.3127C2.98747 29.9127 4.2125 30.394 5.59375 30.7753C8.3875 31.544 12.15 32.0002 16.25 32.0002C20.35 32.0002 24.1125 31.544 26.9 30.7753C28.2813 30.394 29.5126 29.9127 30.425 29.3127C31.2813 28.7565 32.2438 27.8377 32.2438 26.5002C32.2438 25.6565 31.8438 24.969 31.3688 24.4628C30.9 23.969 30.2875 23.569 29.6312 23.2378C28.3187 22.5753 26.5375 22.0565 24.5062 21.6877C24.4931 21.6853 24.4804 21.6866 24.4673 21.6846Z" />
+<path d="M21.1572 24.375C23.6621 21.2402 29.375 13.6426 29.375 9.375C29.375 4.19922 25.1758 0 20 0C14.8242 0 10.625 4.19922 10.625 9.375C10.625 13.6426 16.3379 21.2402 18.8428 24.375C19.4434 25.1221 20.5566 25.1221 21.1572 24.375ZM20 6.25C20.4104 6.24988 20.8169 6.33063 21.1962 6.48762C21.5754 6.64462 21.92 6.87478 22.2103 7.16498C22.5006 7.45517 22.7308 7.79971 22.8879 8.17891C23.045 8.55811 23.1259 8.96454 23.1259 9.375C23.1259 9.78546 23.045 10.1919 22.8879 10.5711C22.7308 10.9503 22.5006 11.2948 22.2103 11.585C21.92 11.8752 21.5754 12.1054 21.1962 12.2624C20.8169 12.4194 20.4104 12.5001 20 12.5C19.5895 12.5001 19.1831 12.4194 18.8038 12.2624C18.4246 12.1054 18.08 11.8752 17.7897 11.585C17.4994 11.2948 17.2692 10.9503 17.1121 10.5711C16.955 10.1919 16.8741 9.78546 16.8741 9.375C16.8741 8.96454 16.955 8.55811 17.1121 8.17891C17.2692 7.79971 17.4994 7.45517 17.7897 7.16498C18.08 6.87478 18.4246 6.64462 18.8038 6.48762C19.1831 6.33063 19.5895 6.24988 20 6.25Z" />
+<path d="M12.5 1.25C7.32422 1.25 3.125 5.44922 3.125 10.625C3.125 14.8926 8.83789 22.4902 11.3428 25.625C11.9434 26.3721 13.0566 26.3721 13.6572 25.625C14.3048 24.8145 15.168 23.7026 16.0903 22.4302C14.0543 19.7326 11.3943 15.8857 10.1165 12.6082C9.55547 11.9279 9.30799 11.0422 9.43506 10.1697C9.39663 9.89438 9.375 9.62844 9.375 9.375C9.375 5.97578 11.1864 2.99788 13.896 1.354C13.4405 1.28593 12.9744 1.25 12.5 1.25Z" />
 </g>
 <defs>
 <clipPath id="clip0_2651_21776">

+ 4 - 0
assets/icons/travels-section/whs.svg

@@ -0,0 +1,4 @@
+<svg width="20" height="22" viewBox="0 0 20 22" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M10 1C4.47715 1 0 5.50038 0 11.0519C0 15.6914 3.12754 19.5974 7.37847 20.755L7.37847 16.2811L2.14032 11.055L10 3.15451L17.8568 11.0521L12.6562 16.2787V20.7455C16.8895 19.5761 20 15.6788 20 11.0519C20 5.50038 15.5228 1 10 1Z" fill="#0F3F4F"/>
+<path d="M10.434 20.986V15.3534L14.714 11.0519L10 6.31351L5.28889 11.0491L9.60069 15.351V20.986C9.7917 20.9953 9.7696 21 9.96288 21C10.1562 21 10.243 20.9953 10.434 20.986Z" fill="#0F3F4F"/>
+</svg>

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

@@ -57,7 +57,7 @@ export interface PostGetRegionDataReturn extends ResponseType {
   };
 }
 
-interface User {
+export interface User {
   score_nm: number;
   score_dare: number;
   score_un: number;

+ 3 - 0
src/modules/api/user/queries/index.ts

@@ -3,3 +3,6 @@ export * from './use-post-set-profile';
 export * from './use-post-get-profile-info';
 export * from './use-post-get-profile-info-public';
 export * from './use-post-get-profile-regions';
+export * from './use-post-get-profile-data';
+export * from './use-post-get-profile-updates';
+export * from './use-post-load-friends-app';

+ 17 - 0
src/modules/api/user/queries/use-post-get-profile-data.tsx

@@ -0,0 +1,17 @@
+import { useQuery } from '@tanstack/react-query';
+
+import { userQueryKeys } from '../user-query-keys';
+import { type PostGetProfileDataReturn, userApi } from '../user-api';
+
+import type { BaseAxiosError } from '../../../../types';
+
+export const usePostGetProfileInfoDataQuery = (token: string, userId: number, enabled: boolean) => {
+  return useQuery<PostGetProfileDataReturn, BaseAxiosError>({
+    queryKey: userQueryKeys.getProfileInfoData(userId),
+    queryFn: async () => {
+      const response = await userApi.getProfileInfoData(token, userId);
+      return response.data;
+    },
+    enabled
+  });
+};

+ 17 - 0
src/modules/api/user/queries/use-post-get-profile-updates.tsx

@@ -0,0 +1,17 @@
+import { useQuery } from '@tanstack/react-query';
+
+import { userQueryKeys } from '../user-query-keys';
+import { type PostGetProfileUpdatesReturn, userApi } from '../user-api';
+
+import type { BaseAxiosError } from '../../../../types';
+
+export const usePostGetProfileUpdatesQuery = (token: string, userId: number, enabled: boolean) => {
+  return useQuery<PostGetProfileUpdatesReturn, BaseAxiosError>({
+    queryKey: userQueryKeys.getProfileUpdates(userId),
+    queryFn: async () => {
+      const response = await userApi.getProfileUpdates(token, userId);
+      return response.data;
+    },
+    enabled
+  });
+};

+ 27 - 0
src/modules/api/user/queries/use-post-load-friends-app.tsx

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

+ 124 - 3
src/modules/api/user/user-api.tsx

@@ -1,6 +1,7 @@
 import { request } from '../../../utils';
 import { API } from '../../../types';
 import { ResponseType } from '../response-type';
+import { User } from '@api/regions';
 
 export interface PostGetProfileData extends ResponseType {
   user_id: number;
@@ -87,9 +88,10 @@ export interface PostGetProfileInfoReturn extends ResponseType {
 
 export type Series = {
   id: number;
-  score: number;
+  score: string;
   name: string;
-  icon_png: string;
+  icon: string;
+  app_icon: string;
 };
 
 export type Score = {
@@ -190,6 +192,107 @@ export type WHS = {
   visited: boolean;
 }[];
 
+export interface PostGetProfileDataReturn extends ResponseType {
+  data: {
+    can_authenticate: 0 | 1;
+    can_see_updates: 0 | 1;
+    can_send_friend_request: 0 | 1;
+    friend_request_received: number;
+    friend_request_sent: number;
+    friends: {
+      avatar: string | null;
+      user_id: number;
+      first_name: string;
+      last_name: string;
+      flag: string;
+    }[];
+    interviews: {
+      link: string;
+      year: number;
+    }[];
+    is_friend: 0 | 1;
+    own_profile: 0 | 1;
+    png_map: 0 | 1;
+    scores: {
+      rank_country: number;
+      rank_nm: number;
+      rank_tbt: number;
+      score_kye: number;
+      score_mqp: number;
+      score_mtp: number;
+      score_nm: number;
+      score_slow: string;
+      score_slow11: string;
+      score_slow31: string;
+      score_slow101: string;
+      score_tbt: number;
+      score_tcc: number;
+      score_un: number;
+      score_unp: number;
+      score_whs: number;
+      score_yes: number;
+      series: string;
+      series_max: string;
+      series_total: string;
+    };
+    series: Series[];
+    user_data: {
+      age: number;
+      alive: 0 | 1;
+      auth: 0 | 1;
+      avatar: string | null;
+      badge_nm: number | null;
+      badge_tbt: 0 | 1;
+      badge_un: 0 | 1;
+      badges: number;
+      bio: string;
+      email: string;
+      first_name: string;
+      flag1: string;
+      flag2: string | null;
+      last_name: string;
+      last_seen_date: string | null;
+      last_seen_in: string | null;
+      links_json: string;
+      offline: number | null;
+      patreon: 0 | 1;
+      supreme: 0 | 1;
+      ukr: 0 | 1;
+      homeregion: string;
+    };
+  };
+}
+
+export interface PostGetProfileUpdatesReturn extends ResponseType {
+  data: {
+    can_see_updates: 0 | 1;
+    friends_total: number;
+    updates: {
+      countries: number;
+      dare: number;
+      friends: number;
+      new_nm: number;
+      photos: number;
+      series: number;
+      visited_regions: number;
+      whs: number;
+    };
+  };
+}
+
+export interface PostGetFriendsDataReturn extends ResponseType {
+  data: {
+    max_pages: number;
+    countries: {
+      [key: string]: {
+        country: string;
+        flag: string;
+      };
+    };
+    users: User[];
+  };
+}
+
 export const userApi = {
   getProfileData: (token: string) =>
     request.postForm<PostGetProfileData>(API.GET_USER_SETTINGS_DATA, { token }),
@@ -211,5 +314,23 @@ export const userApi = {
       uid
     }),
   getProfileRegions: (uid: number, type: string) =>
-    request.postForm<PostGetProfileRegionsReturn>(API.GET_PROFILE_REGIONS, { uid, type })
+    request.postForm<PostGetProfileRegionsReturn>(API.GET_PROFILE_REGIONS, { uid, type }),
+  getProfileInfoData: (token: string, profile_id: number) =>
+    request.postForm<PostGetProfileDataReturn>(API.GET_PROGILE_DATA, {
+      token,
+      profile_id
+    }),
+  getProfileUpdates: (token: string, profile_id: number) =>
+    request.postForm<PostGetProfileUpdatesReturn>(API.GET_PROFILE_UPDATES, {
+      token,
+      profile_id
+    }),
+  getFriends: (uid: number, page: number, sort?: string, age?: number, country?: string) =>
+    request.postForm<PostGetFriendsDataReturn>(API.GET_FRIENDS, {
+      uid,
+      page,
+      sort,
+      age,
+      country
+    })
 };

+ 4 - 1
src/modules/api/user/user-query-keys.tsx

@@ -3,5 +3,8 @@ export const userQueryKeys = {
   setProfileData: () => ['setProfileData'] as const,
   getProfileInfo: () => ['getProfileInfo'] as const,
   getProfileInfoPublic: () => ['getProfileInfoPublic'] as const,
-  getProfileRegions: (uid: number, type: string) => ['getProfileRegions', uid, type] as const
+  getProfileRegions: (uid: number, type: string) => ['getProfileRegions', uid, type] as const,
+  getProfileInfoData: (userId: number) => ['getProfileInfoData', userId] as const,
+  getProfileUpdates: (userId: number) => ['getProfileUpdates', userId] as const,
+  getFriends: () => ['getFriends'] as const
 };

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

@@ -367,7 +367,7 @@ const RegionViewScreen: FC<Props> = ({ navigation, route }) => {
                     ...([
                       NAVIGATION_PAGES.USERS_LIST,
                       {
-                        regionId,
+                        id: regionId,
                         isFromHere: true,
                         type: 'nm'
                       }
@@ -415,7 +415,7 @@ const RegionViewScreen: FC<Props> = ({ navigation, route }) => {
                     ...([
                       NAVIGATION_PAGES.USERS_LIST,
                       {
-                        regionId,
+                        id: regionId,
                         isFromHere: false,
                         type
                       }

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

@@ -73,7 +73,8 @@ export const styles = StyleSheet.create({
   title: {
     fontSize: 18,
     fontFamily: 'montserrat-700',
-    color: Colors.DARK_BLUE
+    color: Colors.DARK_BLUE,
+    flex: 1
   },
   subtitle: {
     fontSize: 12,

+ 46 - 8
src/screens/InAppScreens/MapScreen/UsersListScreen/index.tsx

@@ -16,6 +16,7 @@ import { Profile } from './Profile';
 import { RankingDropdown } from '../../TravellersScreen/utils/types';
 import { dataRanking } from '../../TravellersScreen/utils';
 import { Ranking } from '../../TravellersScreen';
+import { useGetFriendsMutation } from '@api/user';
 
 type Props = {
   navigation: NavigationProp<any>;
@@ -23,11 +24,12 @@ type Props = {
 };
 
 const UsersListScreen: FC<Props> = ({ navigation, route }) => {
-  const regionId = route.params?.regionId;
+  const id = route.params?.id;
   const type = route.params?.type;
   const { mutateAsync: getUsersFromRegion } = useGetUsersFromRegionMutation();
   const { mutateAsync: getUsersWhoVisitedRegion } = useGetUsersWhoVisitetRegionMutation();
   const { mutateAsync: getUsersWhoVisitedDare } = useGetUsersWhoVisitedDareMutation();
+  const { mutateAsync: getFriends } = useGetFriendsMutation();
   const token = storage.get('token', StoreType.STRING);
   const [users, setUsers] = useState<Ranking[]>([]);
   const [loading, setLoading] = useState(true);
@@ -59,7 +61,7 @@ const UsersListScreen: FC<Props> = ({ navigation, route }) => {
       if (isFromHere) {
         await getUsersFromRegion(
           {
-            id: regionId,
+            id,
             page,
             sort: filter.ranking,
             age: filter.age
@@ -75,7 +77,24 @@ const UsersListScreen: FC<Props> = ({ navigation, route }) => {
       } else if (type === 'nm') {
         await getUsersWhoVisitedRegion(
           {
-            id: regionId,
+            id,
+            page,
+            sort: filter.ranking,
+            age: filter.age,
+            country: filter.country
+          },
+          {
+            onSuccess: (data) => {
+              setIsLoadingMore(false);
+              setUsers((prevState) => [...prevState, ...data?.data?.users]);
+              setSelectedUsers((prevState) => [...prevState, ...data?.data?.users]);
+            }
+          }
+        );
+      } else if (type === 'friends') {
+        await getFriends(
+          {
+            id,
             page,
             sort: filter.ranking,
             age: filter.age,
@@ -92,7 +111,7 @@ const UsersListScreen: FC<Props> = ({ navigation, route }) => {
       } else {
         await getUsersWhoVisitedDare(
           {
-            id: regionId,
+            id,
             page,
             sort: filter.ranking,
             age: filter.age,
@@ -117,7 +136,7 @@ const UsersListScreen: FC<Props> = ({ navigation, route }) => {
     if (isFromHere) {
       await getUsersFromRegion(
         {
-          id: regionId,
+          id,
           page,
           sort: filter.ranking,
           age: filter.age
@@ -134,7 +153,26 @@ const UsersListScreen: FC<Props> = ({ navigation, route }) => {
     } else if (type === 'nm') {
       await getUsersWhoVisitedRegion(
         {
-          id: regionId,
+          id,
+          page,
+          sort: filter.ranking,
+          age: filter.age,
+          country: filter.country
+        },
+        {
+          onSuccess: (data) => {
+            setUsers(data?.data?.users);
+            setSelectedUsers(data?.data?.users);
+            setMaxPages(data?.data?.max_pages);
+            setMasterCountries(convertData(data?.data?.countries) ?? []);
+            setLoading(false);
+          }
+        }
+      );
+    } else if (type === 'friends') {
+      await getFriends(
+        {
+          id,
           page,
           sort: filter.ranking,
           age: filter.age,
@@ -153,7 +191,7 @@ const UsersListScreen: FC<Props> = ({ navigation, route }) => {
     } else {
       await getUsersWhoVisitedDare(
         {
-          id: regionId,
+          id,
           page,
           sort: filter.ranking,
           age: filter.age,
@@ -200,7 +238,7 @@ const UsersListScreen: FC<Props> = ({ navigation, route }) => {
   return (
     <PageWrapper>
       <Header
-        label={isFromHere ? 'From here' : 'Been here'}
+        label={isFromHere ? 'From here' : type === 'friends' ? 'Friends' : 'Been here'}
         rightElement={<FilterButton onPress={() => setModalVisible(!isModalVisible)} />}
       />
       <FlashList

+ 3 - 4
src/screens/InAppScreens/MapScreen/index.tsx

@@ -539,7 +539,7 @@ const MapScreen: React.FC<MapScreenProps> = ({ navigation }) => {
       maximumZ={15}
       maximumNativeZ={13}
       tileCachePath={cacheDir !== localVisitedDir ? `${cacheDir}` : undefined}
-      // shouldReplaceMapContent
+      shouldReplaceMapContent
       minimumZ={0}
       offlineMode={!isConnected}
       opacity={opacity}
@@ -681,7 +681,7 @@ const MapScreen: React.FC<MapScreenProps> = ({ navigation }) => {
         zoomControlEnabled={false}
         onPress={handleMapPress}
         style={styles.map}
-        mapType={'standard'}
+        mapType={Platform.OS == 'android' ? 'none' : 'standard'}
         maxZoomLevel={15}
         minZoomLevel={0}
         onRegionChangeComplete={findFeaturesInVisibleMapArea}
@@ -690,8 +690,7 @@ const MapScreen: React.FC<MapScreenProps> = ({ navigation }) => {
         renderCluster={(cluster) => <ClusterItem key={cluster.id} cluster={cluster} />}
       >
         {renderedGeoJSON}
-        {/* nm tiles */}
-        {/* {renderMapTiles(tilesBaseURL, localTileDir, 1)} */}
+        {renderMapTiles(tilesBaseURL, localTileDir, 1)}
         {renderMapTiles(gridUrl, localGridDir, 2)}
         {userId &&
           renderMapTiles(type === 1 ? visitedUNTiles : visitedTiles, localVisitedDir, 2, 0.5)}

+ 27 - 0
src/screens/InAppScreens/ProfileScreen/Components/InfoItem.tsx

@@ -0,0 +1,27 @@
+import { FC, ReactNode } from 'react';
+import { View, Text } from 'react-native';
+import { styles } from './styles';
+
+export const InfoItem: FC<{
+  title: string;
+  inline?: boolean;
+  children: ReactNode;
+  showMore?: boolean;
+}> = ({ title, inline, children, showMore }) => (
+  <View>
+    <Text style={[styles.headerText, { flex: 0 }]}>{title}</Text>
+    <View
+      style={[
+        {
+          display: 'flex',
+          flexDirection: inline ? 'row' : 'column',
+          justifyContent: 'space-evenly',
+          marginTop: 10
+        },
+        showMore ? { flexWrap: 'wrap', gap: 8 } : {}
+      ]}
+    >
+      {children}
+    </View>
+  </View>
+);

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

@@ -0,0 +1,359 @@
+import { FC, useState } from 'react';
+import { TouchableOpacity, View, Text, Image } from 'react-native';
+import { Series, usePostGetProfileRegions } from '@api/user';
+import { NavigationProp } from '@react-navigation/native';
+import Modal from 'react-native-modal';
+import Tooltip from 'react-native-walkthrough-tooltip';
+import RegionsRenderer from '../RegionsRenderer';
+
+import CompassIcon from 'assets/icons/travels-section/compass.svg';
+import FriendsIcon from 'assets/icons/user-group.svg';
+import FlagsIcon from 'assets/icons/travels-section/flags.svg';
+import PhotosIcon from 'assets/icons/travels-section/images.svg';
+import RegionsIcon from 'assets/icons/travels-section/regions.svg';
+import SeriesIcon from 'assets/icons/travels-section/series.svg';
+import WHSIcon from 'assets/icons/travels-section/whs.svg';
+import ArrowIcon from 'assets/icons/next.svg';
+
+import { styles } from './styles';
+import { InfoItem } from './InfoItem';
+import { Colors } from 'src/theme';
+import { API_HOST } from 'src/constants';
+import { NAVIGATION_PAGES } from 'src/types';
+
+type PersonalInfoProps = {
+  data: {
+    bio: string;
+    scores: { [key: string]: number | string };
+    homebase: string;
+    series: Series[];
+    friends: {
+      avatar: string | null;
+      user_id: number;
+      first_name: string;
+      last_name: string;
+      flag: string;
+    }[];
+  };
+  updates: {
+    countries: number;
+    dare: number;
+    friends: number;
+    new_nm: number;
+    photos: number;
+    series: number;
+    visited_regions: number;
+    whs: number;
+  };
+  userId: number;
+  navigation: NavigationProp<any>;
+  isFriend: 0 | 1;
+};
+
+export const PersonalInfo: FC<PersonalInfoProps> = ({
+  data,
+  userId,
+  updates,
+  navigation,
+  isFriend
+}) => {
+  const [showMoreSeries, setShowMoreSeries] = useState(false);
+  const [type, setType] = useState<string>('nm');
+  const [isModalVisible, setIsModalVisible] = useState(false);
+  const [toolTipVisible, setToolTipVisible] = useState<number | null>(null);
+  const [tooltipUser, setTooltipUser] = useState<number | null>(null);
+
+  const { data: regions } = usePostGetProfileRegions(userId, type);
+
+  const scores = [
+    { name: 'score_nm', score: 'NM' },
+    { name: 'score_mqp', score: 'DARE' },
+    { name: 'score_un', score: 'UN' },
+    { name: 'score_unp', score: 'UN+' },
+    { name: 'score_tcc', score: 'TCC' },
+    { name: 'score_deep', score: 'DEEP' },
+    { name: 'score_yes', score: 'YES' },
+    { name: 'score_slow', score: 'SLOW' },
+    { name: 'score_whs', score: 'WHS' },
+    { name: 'score_kye', score: 'KYE' }
+  ];
+
+  const handleOpenModal = (type: string) => {
+    switch (type) {
+      case 'NM':
+        setType('nm');
+        break;
+      case 'DARE':
+        setType('mqp');
+        break;
+      case 'UN':
+        setType('un');
+        break;
+      case 'UN+':
+        setType('unp');
+        break;
+      case 'TCC':
+        setType('tcc');
+        break;
+      case 'SLOW':
+        setType('slow');
+        break;
+      case 'YES':
+        setType('yes');
+        break;
+      case 'WHS':
+        setType('whs');
+        break;
+      case 'KYE':
+        setType('kye');
+      case 'DEEP':
+        setType('deep');
+    }
+    setIsModalVisible(true);
+  };
+
+  const hasUpdates = () => {
+    return (
+      (updates.countries && updates.countries > 0) ||
+      (updates.visited_regions && updates.visited_regions > 0) ||
+      (updates.dare && updates.dare > 0) ||
+      (updates.series && updates.series > 0) ||
+      (updates.whs && updates.whs > 0) ||
+      (updates.new_nm && updates.new_nm > 0) ||
+      (updates.photos && updates.photos > 0) ||
+      (updates.friends && updates.friends > 0)
+    );
+  };
+
+  return (
+    <>
+      <View style={styles.wrapper}>
+        <View style={styles.scoreContainer}>
+          {scores.map((score, index) => {
+            let scoreRank = +data.scores[score.name] > 0 ? data.scores[score.name] : '-';
+
+            if (score.score === 'YES' && +scoreRank >= 4500) {
+              scoreRank = '-';
+            }
+
+            return (
+              <TouchableOpacity
+                key={index}
+                style={styles.rankingItem}
+                disabled={score.score === 'DEEP' || score.score === 'KYE'}
+                onPress={() => handleOpenModal(score.score)}
+              >
+                <Text style={styles.rankingScore}>{scoreRank}</Text>
+                <Text style={[styles.titleText, { flex: 0 }]}>{score.score}</Text>
+              </TouchableOpacity>
+            );
+          })}
+        </View>
+
+        {data.friends.length > 0 ? (
+          <InfoItem inline={true} title={'FRIENDS'}>
+            {data.friends
+              .filter((f) => f.avatar)
+              .slice(0, 7)
+              .map((friend, index) => (
+                <Tooltip
+                  isVisible={tooltipUser === index}
+                  content={
+                    <Text style={{}}>
+                      {friend.first_name} {friend.last_name}
+                    </Text>
+                  }
+                  contentStyle={{ backgroundColor: Colors.FILL_LIGHT }}
+                  placement="top"
+                  onClose={() => setTooltipUser(null)}
+                  key={index}
+                  backgroundColor="transparent"
+                  allowChildInteraction={false}
+                >
+                  <TouchableOpacity onPress={() => setTooltipUser(index)}>
+                    <Image
+                      style={styles.avatar}
+                      source={{ uri: API_HOST + '/img/avatars/' + friend.avatar }}
+                    />
+                  </TouchableOpacity>
+                </Tooltip>
+              ))}
+            <Tooltip
+              isVisible={tooltipUser === -1}
+              content={<Text style={{}}>Only friends can see details.</Text>}
+              contentStyle={{ backgroundColor: Colors.FILL_LIGHT }}
+              placement="top"
+              onClose={() => setTooltipUser(null)}
+              backgroundColor="transparent"
+              allowChildInteraction={false}
+            >
+              <TouchableOpacity
+                style={[styles.avatar, styles.userShowMore]}
+                onPress={() => {
+                  if (isFriend === 0) {
+                    setTooltipUser(-1);
+                  } else {
+                    navigation.navigate(
+                      ...([
+                        NAVIGATION_PAGES.FRIENDS_LIST,
+                        {
+                          id: userId,
+                          type: 'friends'
+                        }
+                      ] as never)
+                    );
+                  }
+                }}
+              >
+                <View style={styles.dots}></View>
+                <View style={styles.dots}></View>
+                <View style={styles.dots}></View>
+              </TouchableOpacity>
+            </Tooltip>
+          </InfoItem>
+        ) : null}
+
+        {hasUpdates() ? (
+          <InfoItem title={'UPDATES (last 90 days)'}>
+            <View style={{ flexDirection: 'row', flexWrap: 'wrap' }}>
+              {updates.countries && updates.countries > 0 ? (
+                <View style={styles.updates}>
+                  <FlagsIcon fill={Colors.DARK_BLUE} height={20} width={20} />
+                  <View>
+                    <Text style={styles.updatesTextCount}>+{updates.countries}</Text>
+                    <Text style={styles.updatesText}>visited countries</Text>
+                  </View>
+                </View>
+              ) : null}
+              {updates.visited_regions && updates.visited_regions > 0 ? (
+                <View style={styles.updates}>
+                  <RegionsIcon fill={Colors.DARK_BLUE} height={20} width={20} />
+                  <View>
+                    <Text style={styles.updatesTextCount}>+{updates.visited_regions}</Text>
+                    <Text style={styles.updatesText}>visited regions</Text>
+                  </View>
+                </View>
+              ) : null}
+              {updates.dare && updates.dare > 0 ? (
+                <View style={styles.updates}>
+                  <CompassIcon fill={Colors.DARK_BLUE} height={20} width={20} />
+                  <View>
+                    <Text style={styles.updatesTextCount}>+{updates.dare}</Text>
+                    <Text style={styles.updatesText}>new DARE places</Text>
+                  </View>
+                </View>
+              ) : null}
+              {updates.series && updates.series > 0 ? (
+                <View style={styles.updates}>
+                  <SeriesIcon fill={Colors.DARK_BLUE} height={20} width={20} />
+                  <View>
+                    <Text style={styles.updatesTextCount}>+{updates.series}</Text>
+                    <Text style={styles.updatesText}>new series</Text>
+                  </View>
+                </View>
+              ) : null}
+              {updates.whs && updates.whs > 0 ? (
+                <View style={styles.updates}>
+                  <WHSIcon fill={Colors.DARK_BLUE} height={20} width={20} />
+                  <View>
+                    <Text style={styles.updatesTextCount}>+{updates.whs}</Text>
+                    <Text style={styles.updatesText}>new WHS sites</Text>
+                  </View>
+                </View>
+              ) : null}
+              {updates.new_nm && updates.new_nm > 0 ? (
+                <View style={styles.updates}>
+                  <RegionsIcon fill={Colors.DARK_BLUE} height={20} width={20} />
+                  <View>
+                    <Text style={styles.updatesTextCount}>+{updates.new_nm}</Text>
+                    <Text style={styles.updatesText}>new NM regions</Text>
+                  </View>
+                </View>
+              ) : null}
+              {updates.photos && updates.photos > 0 ? (
+                <View style={styles.updates}>
+                  <PhotosIcon fill={Colors.DARK_BLUE} height={20} width={20} />
+                  <View>
+                    <Text style={styles.updatesTextCount}>+{updates.photos}</Text>
+                    <Text style={styles.updatesText}>new photos</Text>
+                  </View>
+                </View>
+              ) : null}
+              {updates.friends && updates.friends > 0 ? (
+                <View style={styles.updates}>
+                  <FriendsIcon fill={Colors.DARK_BLUE} height={20} width={20} />
+                  <View>
+                    <Text style={styles.updatesTextCount}>+{updates.friends}</Text>
+                    <Text style={styles.updatesText}>new friends</Text>
+                  </View>
+                </View>
+              ) : null}
+            </View>
+          </InfoItem>
+        ) : null}
+
+        {data.series?.length > 0 && (
+          <InfoItem showMore={showMoreSeries} inline={true} title={'SERIES'}>
+            {data.series?.slice(0, showMoreSeries ? data.series.length : 8).map((data, index) => (
+              <Tooltip
+                isVisible={toolTipVisible === index}
+                content={<Text style={{}}>{data.name}</Text>}
+                contentStyle={{ backgroundColor: Colors.FILL_LIGHT }}
+                placement="top"
+                onClose={() => setToolTipVisible(null)}
+                key={index}
+                backgroundColor="transparent"
+                allowChildInteraction={false}
+              >
+                <TouchableOpacity style={styles.series} onPress={() => setToolTipVisible(index)}>
+                  <Image
+                    source={{ uri: API_HOST + data.app_icon }}
+                    style={{ width: 28, height: 28 }}
+                  />
+                  <Text style={[styles.headerText, { flex: 0 }]}>{data.score}</Text>
+                </TouchableOpacity>
+              </Tooltip>
+            ))}
+          </InfoItem>
+        )}
+
+        {data.series?.length > 8 ? (
+          <TouchableOpacity onPress={() => setShowMoreSeries(!showMoreSeries)}>
+            <View
+              style={[
+                { alignItems: 'center' },
+                showMoreSeries
+                  ? { transform: 'rotate(180deg)', paddingTop: 8 }
+                  : { paddingBottom: 8 }
+              ]}
+            >
+              <ArrowIcon stroke={'#B7C6CB'} />
+            </View>
+          </TouchableOpacity>
+        ) : null}
+
+        <View style={{ display: 'flex', flexDirection: 'row' }}>
+          <Text style={styles.headerText}>REGION OF ORIGIN</Text>
+          <Text style={styles.titleText}>{data.homebase}</Text>
+        </View>
+
+        {data.bio && data.bio.length > 0 && (
+          <InfoItem title={'BIO'}>
+            <Text style={[styles.titleText, { flex: 0 }]}>{data.bio}</Text>
+          </InfoItem>
+        )}
+      </View>
+
+      <Modal
+        isVisible={isModalVisible}
+        onBackdropPress={() => setIsModalVisible(false)}
+        onBackButtonPress={() => setIsModalVisible(false)}
+        style={styles.modal}
+        statusBarTranslucent={true}
+        presentationStyle="overFullScreen"
+      >
+        <RegionsRenderer type={type} regions={regions} setIsModalVisible={setIsModalVisible} />
+      </Modal>
+    </>
+  );
+};

+ 93 - 0
src/screens/InAppScreens/ProfileScreen/Components/styles.tsx

@@ -0,0 +1,93 @@
+import { StyleSheet } from 'react-native';
+import { Colors } from 'src/theme';
+import { getFontSize } from 'src/utils';
+
+export const styles = StyleSheet.create({
+  wrapper: {
+    paddingTop: 16,
+    marginTop: 4,
+    gap: 20,
+    paddingBottom: 32
+  },
+  scoreContainer: {
+    flexDirection: 'row',
+    flexWrap: 'wrap',
+    justifyContent: 'space-between'
+  },
+  headerText: {
+    flex: 1,
+    fontFamily: 'redhat-700',
+    color: Colors.DARK_BLUE,
+    fontSize: getFontSize(14)
+  },
+  modal: {
+    justifyContent: 'flex-end',
+    margin: 0
+  },
+  updatesTextCount: {
+    fontSize: 12,
+    fontWeight: '700',
+    color: Colors.DARK_BLUE
+  },
+  updatesText: {
+    fontSize: 12,
+    fontWeight: '600',
+    color: Colors.DARK_BLUE
+  },
+  titleText: {
+    flex: 1,
+    color: Colors.DARK_BLUE,
+    fontWeight: '600',
+    fontSize: getFontSize(12)
+  },
+  rankingItem: {
+    width: '18%',
+    margin: '1%',
+    display: 'flex',
+    flexDirection: 'column',
+    alignItems: 'center',
+    backgroundColor: Colors.FILL_LIGHT,
+    borderRadius: 4,
+    padding: 4,
+    gap: 1
+  },
+  rankingScore: {
+    flex: 0,
+    fontFamily: 'montserrat-700',
+    color: Colors.DARK_BLUE,
+    fontSize: getFontSize(14)
+  },
+  avatar: {
+    borderRadius: 28 / 2,
+    width: 28,
+    height: 28,
+    borderWidth: 1,
+    borderColor: Colors.DARK_LIGHT
+  },
+  userShowMore: {
+    backgroundColor: Colors.FILL_LIGHT,
+    flexDirection: 'row',
+    alignItems: 'center',
+    justifyContent: 'center',
+    gap: 2
+  },
+  dots: {
+    width: 3,
+    height: 3,
+    borderRadius: 3 / 2,
+    backgroundColor: Colors.DARK_BLUE
+  },
+  updates: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    gap: 6,
+    width: '50%',
+    padding: 4
+  },
+  series: {
+    display: 'flex',
+    flexDirection: 'column',
+    gap: 5,
+    alignItems: 'center'
+  }
+});

+ 32 - 0
src/screens/InAppScreens/ProfileScreen/UnauthenticatedProfileScreen/index.tsx

@@ -0,0 +1,32 @@
+import { CommonActions, useNavigation } from '@react-navigation/native';
+import { View } from 'react-native';
+import { BigText, Button, PageWrapper } from 'src/components';
+import { NAVIGATION_PAGES } from 'src/types';
+import { ButtonVariants } from 'src/types/components';
+
+const UnauthenticatedProfileScreen = () => {
+  const navigation = useNavigation();
+
+  return (
+    <PageWrapper>
+      <View style={{ marginTop: 15, display: 'flex', gap: 10 }}>
+        <BigText children={'You are not logged in. Please login or register to access profile.'} />
+        <Button
+          onPress={() =>
+            navigation.dispatch(
+              CommonActions.reset({
+                index: 1,
+                routes: [{ name: NAVIGATION_PAGES.WELCOME }]
+              })
+            )
+          }
+          variant={ButtonVariants.FILL}
+        >
+          Go to login/register
+        </Button>
+      </View>
+    </PageWrapper>
+  );
+};
+
+export default UnauthenticatedProfileScreen;

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

@@ -97,12 +97,12 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
         <Text style={styles.textClose}>Close</Text>
       </TouchableOpacity>
       <View style={[styles.cornerButton, styles.topRightButton]}>
-        {data.avatar ? (
-          <Image style={styles.avatar} source={{ uri: API_HOST + data.avatar }} />
+        {data.user_data.avatar ? (
+          <Image style={styles.avatar} source={{ uri: API_HOST + '/img/avatars/' + data.user_data.avatar }} />
         ) : (
           <AvatarWithInitials
-            text={`${data.first_name[0] ?? ''}${data.last_name[0] ?? ''}`}
-            flag={API_HOST + data.homebase_flag}
+            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}
           />

+ 177 - 351
src/screens/InAppScreens/ProfileScreen/index.tsx

@@ -1,39 +1,17 @@
-import React, { FC, ReactNode, useCallback, useState } from 'react';
+import React, { FC, useCallback } from 'react';
 import { Linking, ScrollView, Text, TouchableOpacity, View, Image, Platform } from 'react-native';
-import {
-  CommonActions,
-  NavigationProp,
-  useFocusEffect,
-  useNavigation
-} from '@react-navigation/native';
-import Modal from 'react-native-modal';
-import Tooltip from 'react-native-walkthrough-tooltip';
+import { CommonActions, NavigationProp, useFocusEffect } from '@react-navigation/native';
 
-import {
-  type Score,
-  type Series,
-  type SocialData,
-  usePostGetProfileInfoPublicQuery,
-  usePostGetProfileInfoQuery,
-  usePostGetProfileRegions
-} from '@api/user';
+import { usePostGetProfileInfoDataQuery, usePostGetProfileUpdatesQuery } from '@api/user';
 
-import {
-  BigText,
-  Button,
-  PageWrapper,
-  Loading,
-  AvatarWithInitials,
-  Header
-} from '../../../components';
-import { Colors } from '../../../theme';
+import { PageWrapper, Loading, AvatarWithInitials, Header } from '../../../components';
+import { adaptiveStyle, Colors } from '../../../theme';
 import { styles } from './styles';
-import { ButtonVariants } from '../../../types/components';
 
 import { API_HOST } from '../../../constants';
 import { NAVIGATION_PAGES } from '../../../types';
 import { storage, StoreType } from '../../../storage';
-import { getFontSize, getYears } from '../../../utils';
+import { getFontSize } from '../../../utils';
 
 import IconFacebook from '../../../../assets/icons/facebook.svg';
 import IconInstagram from '../../../../assets/icons/instagram.svg';
@@ -42,8 +20,14 @@ import IconYouTube from '../../../../assets/icons/youtube.svg';
 import IconGlobe from '../../../../assets/icons/bottom-navigation/globe.svg';
 import IconLink from '../../../../assets/icons/link.svg';
 import GearIcon from '../../../../assets/icons/gear.svg';
-import ArrowIcon from '../../../../assets/icons/next.svg';
-import RegionsRenderer from './RegionsRenderer';
+import TBTIcon from '../../../../assets/icons/tbt.svg';
+import TickIcon from '../../../../assets/icons/tick.svg';
+import UNIcon from '../../../../assets/icons/un_icon.svg';
+import NMIcon from '../../../../assets/icons/nm_icon.svg';
+
+import { ProfileStyles, ScoreStyles, TBTStyles } from '../TravellersScreen/Components/styles';
+import UnauthenticatedProfileScreen from './UnauthenticatedProfileScreen';
+import { PersonalInfo } from './Components/PersonalInfo';
 
 type Props = {
   navigation: NavigationProp<any>;
@@ -58,9 +42,16 @@ const ProfileScreen: FC<Props> = ({ navigation, route }) => {
 
   if (!token) return <UnauthenticatedProfileScreen />;
 
-  const { data, isFetching } = isPublicView
-    ? usePostGetProfileInfoPublicQuery(route.params?.userId, true)
-    : usePostGetProfileInfoQuery(token, true);
+  const { data: userData, isFetching } = usePostGetProfileInfoDataQuery(
+    token,
+    isPublicView ? route.params?.userId : +currentUserId,
+    true
+  );
+  const { data: lastUpdates } = usePostGetProfileUpdatesQuery(
+    token,
+    isPublicView ? route.params?.userId : +currentUserId,
+    true
+  );
 
   useFocusEffect(
     useCallback(() => {
@@ -79,7 +70,10 @@ const ProfileScreen: FC<Props> = ({ navigation, route }) => {
     }, [navigation])
   );
 
-  if (!data || isFetching) return <Loading />;
+  if (!userData?.data || !lastUpdates?.data || isFetching) return <Loading />;
+
+  const data = userData.data;
+  const links = JSON.parse(data.user_data.links_json);
 
   const handleGoToMap = () => {
     isPublicView
@@ -92,116 +86,41 @@ const ProfileScreen: FC<Props> = ({ navigation, route }) => {
         );
   };
 
-  return (
-    <PageWrapper>
-      {isPublicView && <Header label="Profile" />}
+  const TBRanking = () => {
+    const colors = [
+      'rgba(237, 147, 52, 1)',
+      'rgba(128, 128, 128, 1)',
+      'rgba(211, 211, 211, 1)',
+      'rgba(187, 95, 5, 1)',
+      '#808080'
+    ];
+
+    const Rank = ({ color }: { color: string }) => (
+      <View style={adaptiveStyle([ProfileStyles.badge, { backgroundColor: color }], {})}>
+        <TBTIcon />
+      </View>
+    );
+
+    return (
       <TouchableOpacity
-        style={[styles.usersMap, { backgroundColor: '#EBF2F5' }]}
-        onPress={handleGoToMap}
+        style={adaptiveStyle([TBTStyles.badgeRoot, styles.badgeRoot], {})}
+        disabled={!data.scores.rank_tbt || data.scores.rank_tbt < 1}
       >
-        <Image
-          source={{
-            uri: `${API_HOST}/img/single_maps/${isPublicView ? route.params?.userId : currentUserId}.png`
-          }}
-          style={styles.usersMap}
-        />
-      </TouchableOpacity>
-
-      <View style={styles.pageWrapper}>
-        <View style={{ top: -34 }}>
-          {data.avatar ? (
-            <Image style={styles.avatar} source={{ uri: API_HOST + data.avatar }} />
+        <View style={adaptiveStyle([TBTStyles.badgeWrapper, { gap: 10 }], {})}>
+          {data.user_data.badge_tbt && data.scores.rank_tbt ? (
+            <Rank color={colors[data.scores.rank_tbt - 1]} />
+          ) : null}
+          {data.scores.rank_tbt && data.scores.rank_tbt >= 1 ? (
+            <Text style={adaptiveStyle([ScoreStyles.scoreNameText], {})}>
+              TBT # {data.scores.rank_tbt}
+            </Text>
           ) : (
-            <AvatarWithInitials
-              text={`${data.first_name[0] ?? ''}${data.last_name[0] ?? ''}`}
-              flag={API_HOST + data.homebase_flag}
-              size={64}
-              borderColor={Colors.WHITE}
-            />
+            <View style={{ height: 11 }} />
           )}
         </View>
-        <View style={{ gap: 5, flex: 1 }}>
-          <Text style={[styles.headerText, { fontSize: getFontSize(18), flex: 0 }]}>
-            {data.first_name} {data.last_name}
-          </Text>
-          <View style={styles.userInfoContainer}>
-            <View style={styles.userInfo}>
-              <Text
-                style={{ color: Colors.DARK_BLUE, fontWeight: '600', fontSize: getFontSize(12) }}
-              >
-                Age: {getYears(data.date_of_birth)}
-              </Text>
-              <Image source={{ uri: API_HOST + data.homebase_flag }} style={styles.countryFlag} />
-              {data.homebase2_flag && data.homebase2_flag !== data.homebase_flag ? (
-                <Image
-                  source={{ uri: API_HOST + data.homebase2_flag }}
-                  style={[styles.countryFlag, { marginLeft: -15 }]}
-                />
-              ) : null}
-            </View>
-
-            {!isPublicView ? (
-              <TouchableOpacity
-                style={styles.settings}
-                onPress={() => navigation.navigate(NAVIGATION_PAGES.EDIT_PERSONAL_INFO)}
-              >
-                <GearIcon
-                  width={20}
-                  height={20}
-                  fill={Colors.DARK_BLUE}
-                  style={{ alignSelf: 'center' }}
-                />
-              </TouchableOpacity>
-            ) : null}
-          </View>
-        </View>
-      </View>
-
-      <PersonalInfo
-        data={{
-          bio: data.bio,
-          date_of_birth: data.date_of_birth,
-          scores: data.scores,
-          links: data.links,
-          homebase: data.homebase_name,
-          homebase2: data.homebase2_name,
-          series: data.series
-        }}
-        userId={isPublicView ? route.params?.userId : +currentUserId}
-      />
-    </PageWrapper>
-  );
-};
-
-type PersonalInfoProps = {
-  data: {
-    bio: string;
-    date_of_birth: string;
-    scores: Score[];
-    links: {
-      f?: SocialData;
-      t?: SocialData;
-      i?: SocialData;
-      y?: SocialData;
-      www?: SocialData;
-      other?: SocialData;
-    };
-    homebase: string;
-    homebase2: string;
-    series: Series[];
+      </TouchableOpacity>
+    );
   };
-  userId: number;
-};
-
-const PersonalInfo: FC<PersonalInfoProps> = ({ data, userId }) => {
-  const [showMoreSeries, setShowMoreSeries] = useState(false);
-  const [type, setType] = useState<string>('nm');
-  const [isModalVisible, setIsModalVisible] = useState(false);
-  const [toolTipVisible, setToolTipVisible] = useState<number | null>(null);
-
-  const { data: regions } = usePostGetProfileRegions(userId, type);
-
-  const scores = ['NM1301', 'DARE', 'UN', 'UN+', 'TCC', 'SLOW', 'YES', 'WHS'];
 
   const handleOpenUrl = (url: string | undefined) => {
     url && Linking.openURL(url);
@@ -209,233 +128,140 @@ const PersonalInfo: FC<PersonalInfoProps> = ({ data, userId }) => {
 
   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)
+      (links?.f?.link && links?.f?.active !== 0) ||
+      (links?.i?.link && links?.i?.active !== 0) ||
+      (links?.t?.link && links?.t?.active !== 0) ||
+      (links?.y?.link && links?.y?.active !== 0) ||
+      (links?.www?.link && links?.www?.active !== 0) ||
+      (links?.other?.link && links?.other?.active !== 0)
     );
   };
 
-  const handleOpenModal = (type: string) => {
-    switch (type) {
-      case 'NM1301':
-        setType('nm');
-        break;
-      case 'DARE':
-        setType('mqp');
-        break;
-      case 'UN':
-        setType('un');
-        break;
-      case 'UN+':
-        setType('unp');
-        break;
-      case 'TCC':
-        setType('tcc');
-        break;
-      case 'SLOW':
-        setType('slow');
-        break;
-      case 'YES':
-        setType('yes');
-        break;
-      case 'WHS':
-        setType('whs');
-        break;
-    }
-    setIsModalVisible(true);
-  };
-
   return (
-    <>
-      <ScrollView
-        showsVerticalScrollIndicator={false}
-        contentContainerStyle={{ gap: 20, paddingBottom: 32 }}
-        style={{ paddingTop: 20 }}
-      >
-        <InfoItem inline={true} title={'RANKING'}>
-          <View style={{ flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-between' }}>
-            {scores.map((score, index) => {
-              let scoreRank =
-                data.scores?.find((s) => s.name === score && s.score > 0)?.score ?? '-';
-
-              if (score === 'YES' && +scoreRank >= 4500) {
-                scoreRank = '-';
-              }
+    <PageWrapper>
+      {isPublicView && <Header label="Profile" />}
+      <ScrollView showsVerticalScrollIndicator={false}>
+        <TouchableOpacity
+          style={[styles.usersMap, { backgroundColor: '#EBF2F5' }]}
+          onPress={handleGoToMap}
+        >
+          <Image
+            source={{
+              uri: `${API_HOST}/img/single_maps/${isPublicView ? route.params?.userId : currentUserId}.png`
+            }}
+            style={styles.usersMap}
+          />
+        </TouchableOpacity>
 
-              return (
-                <TouchableOpacity
-                  key={index}
-                  style={styles.rankingItem}
-                  onPress={() => handleOpenModal(score)}
-                >
-                  <Text style={styles.rankingScore}>{scoreRank}</Text>
-                  <Text style={[styles.titleText, { flex: 0 }]}>
-                    {score === 'NM1301' ? 'NM' : score}
-                  </Text>
-                </TouchableOpacity>
-              );
-            })}
+        <View style={styles.pageWrapper}>
+          <View style={{ gap: 8 }}>
+            {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={64}
+                borderColor={Colors.WHITE}
+              />
+            )}
+            {data.scores.rank_tbt && data.scores.rank_tbt >= 1 ? <TBRanking /> : null}
           </View>
-        </InfoItem>
-        {data.series?.length > 0 && (
-          <InfoItem showMore={showMoreSeries} inline={true} title={'SERIES'}>
-            {data.series?.slice(0, showMoreSeries ? data.series.length : 8).map((data, index) => (
-              <Tooltip
-                isVisible={toolTipVisible === index}
-                content={<Text style={{}}>{data.name}</Text>}
-                contentStyle={{ backgroundColor: Colors.FILL_LIGHT }}
-                placement="top"
-                onClose={() => setToolTipVisible(null)}
-                key={index}
-                backgroundColor="transparent"
-                allowChildInteraction={false}
-              >
+          <View style={{ gap: 5, flex: 1 }}>
+            <View style={{ height: 34 }}></View>
+            <Text style={[styles.headerText, { fontSize: getFontSize(18), flex: 0 }]}>
+              {data.user_data.first_name} {data.user_data.last_name}
+            </Text>
+
+            <View style={styles.userInfoContainer}>
+              <View style={styles.userInfo}>
+                <Text style={styles.ageText}>Age: {data.user_data.age}</Text>
+                <Image
+                  source={{ uri: API_HOST + '/img/flags_new/' + data.user_data.flag1 }}
+                  style={styles.countryFlag}
+                />
+                {data.user_data.flag2 && data.user_data.flag2 !== data.user_data.flag1 ? (
+                  <Image
+                    source={{ uri: API_HOST + '/img/flags_new/' + data.user_data.flag2 }}
+                    style={[styles.countryFlag, { marginLeft: -15 }]}
+                  />
+                ) : null}
+                <View style={adaptiveStyle(ProfileStyles.badgesWrapper, {})}>
+                  {data.user_data.auth ? <TickIcon /> : null}
+                  {data.user_data.badge_un ? <UNIcon /> : null}
+                  {data.user_data.badge_nm ? <NMIcon /> : null}
+                </View>
+              </View>
+
+              {!isPublicView ? (
                 <TouchableOpacity
-                  style={{ display: 'flex', flexDirection: 'column', gap: 5, alignItems: 'center' }}
-                  onPress={() => setToolTipVisible(index)}
+                  style={styles.settings}
+                  onPress={() => navigation.navigate(NAVIGATION_PAGES.EDIT_PERSONAL_INFO)}
                 >
-                  <Image
-                    source={{ uri: API_HOST + data.icon_png }}
-                    style={{ width: 28, height: 28 }}
+                  <GearIcon
+                    width={20}
+                    height={20}
+                    fill={Colors.DARK_BLUE}
+                    style={{ alignSelf: 'center' }}
                   />
-                  <Text style={[styles.headerText, { flex: 0 }]}>{data.score}</Text>
-                </TouchableOpacity>
-              </Tooltip>
-            ))}
-          </InfoItem>
-        )}
-        {data.series?.length > 8 ? (
-          <TouchableOpacity onPress={() => setShowMoreSeries(!showMoreSeries)}>
-            <View
-              style={[
-                { alignItems: 'center' },
-                showMoreSeries
-                  ? { transform: 'rotate(180deg)', paddingTop: 8 }
-                  : { paddingBottom: 8 }
-              ]}
-            >
-              <ArrowIcon stroke={'#B7C6CB'} />
-            </View>
-          </TouchableOpacity>
-        ) : null}
-        {/* <View style={{ display: 'flex', flexDirection: 'row' }}>
-          <Text style={styles.headerText}>DATE OF BIRTH</Text>
-          <Text style={styles.titleText}>{new Date(data.date_of_birth).toDateString()}</Text>
-        </View>
-        <View style={{ display: 'flex', flexDirection: 'row' }}>
-          <Text style={styles.headerText}>REGION OF ORIGIN</Text>
-          <Text style={styles.titleText}>{data.homebase}</Text>
-        </View>
-        {data.homebase2 ? (
-          <View style={{ display: 'flex', flexDirection: 'row' }}>
-            <Text style={styles.headerText}>SECOND REGION</Text>
-            <Text style={styles.titleText}>{data.homebase2}</Text>
-          </View>
-        ) : null} */}
-        {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>
-      <Modal
-        isVisible={isModalVisible}
-        onBackdropPress={() => setIsModalVisible(false)}
-        onBackButtonPress={() => setIsModalVisible(false)}
-        style={styles.modal}
-        statusBarTranslucent={true}
-        presentationStyle="overFullScreen"
-      >
-        <RegionsRenderer type={type} regions={regions} setIsModalVisible={setIsModalVisible} />
-      </Modal>
-    </>
-  );
-};
-
-const InfoItem: FC<{
-  title: string;
-  inline?: boolean;
-  children: ReactNode;
-  showMore?: boolean;
-}> = ({ title, inline, children, showMore }) => (
-  <View>
-    <Text style={[styles.headerText, { flex: 0 }]}>{title}</Text>
-    <View
-      style={[
-        {
-          display: 'flex',
-          flexDirection: inline ? 'row' : 'column',
-          justifyContent: 'space-evenly',
-          marginTop: 10
-        },
-        showMore ? { flexWrap: 'wrap', gap: 8 } : {}
-      ]}
-    >
-      {children}
-    </View>
-  </View>
-);
 
-const UnauthenticatedProfileScreen = () => {
-  const navigation = useNavigation();
+            {hasActiveLinks() && (
+              <View style={styles.linksBox}>
+                {links?.f?.link && links?.f?.active !== 0 ? (
+                  <TouchableOpacity onPress={() => handleOpenUrl(links?.f?.link)}>
+                    <IconFacebook fill={Colors.DARK_BLUE} height={16} />
+                  </TouchableOpacity>
+                ) : null}
+                {links?.i?.link && links?.i?.active !== 0 ? (
+                  <TouchableOpacity onPress={() => handleOpenUrl(links?.i?.link)}>
+                    <IconInstagram fill={Colors.DARK_BLUE} height={16} />
+                  </TouchableOpacity>
+                ) : null}
+                {links?.t?.link && links?.t?.active !== 0 ? (
+                  <TouchableOpacity onPress={() => handleOpenUrl(links?.t?.link)}>
+                    <IconTwitter fill={Colors.DARK_BLUE} height={16} />
+                  </TouchableOpacity>
+                ) : null}
+                {links?.y?.link && links?.y?.active !== 0 ? (
+                  <TouchableOpacity onPress={() => handleOpenUrl(links?.y?.link)}>
+                    <IconYouTube fill={Colors.DARK_BLUE} height={16} />
+                  </TouchableOpacity>
+                ) : null}
+                {links?.www?.link && links?.www?.active !== 0 ? (
+                  <TouchableOpacity onPress={() => handleOpenUrl(links?.www?.link)}>
+                    <IconGlobe fill={Colors.DARK_BLUE} height={16} />
+                  </TouchableOpacity>
+                ) : null}
+                {links?.other?.link && links?.other?.active !== 0 ? (
+                  <TouchableOpacity onPress={() => handleOpenUrl(links?.other?.link)}>
+                    <IconLink fill={Colors.DARK_BLUE} height={16} />
+                  </TouchableOpacity>
+                ) : null}
+              </View>
+            )}
+          </View>
+        </View>
 
-  return (
-    <PageWrapper>
-      <View style={{ marginTop: 15, display: 'flex', gap: 10 }}>
-        <BigText children={'You are not logged in. Please login or register to access profile.'} />
-        <Button
-          onPress={() =>
-            navigation.dispatch(
-              CommonActions.reset({
-                index: 1,
-                routes: [{ name: NAVIGATION_PAGES.WELCOME }]
-              })
-            )
-          }
-          variant={ButtonVariants.FILL}
-        >
-          Go to login/register
-        </Button>
-      </View>
+        <PersonalInfo
+          data={{
+            bio: data.user_data.bio,
+            scores: data.scores,
+            homebase: data.user_data.homeregion,
+            series: data.series,
+            friends: data.friends
+          }}
+          updates={lastUpdates.data.updates}
+          userId={isPublicView ? route.params?.userId : +currentUserId}
+          navigation={navigation}
+          isFriend={data.is_friend}
+        />
+      </ScrollView>
     </PageWrapper>
   );
 };

+ 5 - 30
src/screens/InAppScreens/ProfileScreen/styles.ts

@@ -6,8 +6,8 @@ export const styles = StyleSheet.create({
   pageWrapper: {
     display: 'flex',
     flexDirection: 'row',
-    alignItems: 'center',
-    gap: 20
+    gap: 20,
+    marginTop: -34
   },
   headerText: {
     flex: 1,
@@ -35,35 +35,12 @@ export const styles = StyleSheet.create({
     gap: 10,
     alignItems: 'center'
   },
-  rankingItem: {
-    width: '23%',
-    margin: '1%',
-    display: 'flex',
-    flexDirection: 'column',
-    alignItems: 'center',
-    backgroundColor: Colors.FILL_LIGHT,
-    borderRadius: 4,
-    padding: 4,
-    gap: 1
-  },
-  rankingScore: {
-    flex: 0,
-    fontFamily: 'montserrat-700',
-    color: Colors.DARK_BLUE,
-    fontSize: getFontSize(14),
-  },
-  titleText: {
-    flex: 1,
-    color: Colors.DARK_BLUE,
-    fontWeight: '600',
-    fontSize: getFontSize(12)
-  },
   linksBox: {
     display: 'flex',
     flexDirection: 'row',
     gap: 15,
     alignItems: 'center',
-    paddingBottom: 16
+    marginTop: 4
   },
   countryFlag: {
     width: 20,
@@ -90,8 +67,6 @@ export const styles = StyleSheet.create({
     bottom: -10,
     justifyContent: 'center'
   },
-  modal: {
-    justifyContent: 'flex-end',
-    margin: 0,
-  }
+  badgeRoot: { flex: 0, justifyContent: 'flex-end', marginTop: 0 },
+  ageText: { color: Colors.DARK_BLUE, fontWeight: '600', fontSize: getFontSize(12) }
 });

+ 11 - 3
src/types/api.ts

@@ -16,7 +16,9 @@ export enum API_ROUTE {
   TRIUMPHS = 'triumphs',
   SERIES_RANKING = 'series-ranking',
   APP = 'app',
-  SEARCH = 'search'
+  SEARCH = 'search',
+  PROFILE = 'profile',
+  FRIENDS = 'friends'
 }
 
 export enum API_ENDPOINT {
@@ -93,7 +95,10 @@ export enum API_ENDPOINT {
   GET_USERS_WHO_VISITED_DARE = 'get-users-who-visited-dare',
   GET_DATA_FROM_POINT = 'get-data-from-point',
   GET_SUGGESTION_DATA = 'get-suggestion-data',
-  SUBMIT_SUGGESTION = 'submit-suggestion'
+  SUBMIT_SUGGESTION = 'submit-suggestion',
+  GET_PROGILE_DATA = 'get-profile-data',
+  GET_PROFILE_UPDATES = 'get-profile-updates',
+  GET_FRIENDS = 'load-friends-app'
 }
 
 export enum API {
@@ -169,7 +174,10 @@ export enum API {
   GET_USERS_WHO_VISITED_DARE = `${API_ROUTE.REGIONS}/${API_ENDPOINT.GET_USERS_WHO_VISITED_DARE}`,
   GET_DATA_FROM_POINT = `${API_ROUTE.SERIES}/${API_ENDPOINT.GET_DATA_FROM_POINT}`,
   GET_SUGGESTION_DATA = `${API_ROUTE.SERIES}/${API_ENDPOINT.GET_SUGGESTION_DATA}`,
-  SUBMIT_SUGGESTION = `${API_ROUTE.SERIES}/${API_ENDPOINT.SUBMIT_SUGGESTION}`
+  SUBMIT_SUGGESTION = `${API_ROUTE.SERIES}/${API_ENDPOINT.SUBMIT_SUGGESTION}`,
+  GET_PROGILE_DATA = `${API_ROUTE.PROFILE}/${API_ENDPOINT.GET_PROGILE_DATA}`,
+  GET_PROFILE_UPDATES = `${API_ROUTE.PROFILE}/${API_ENDPOINT.GET_PROFILE_UPDATES}`,
+  GET_FRIENDS = `${API_ROUTE.FRIENDS}/${API_ENDPOINT.GET_FRIENDS}`
 }
 
 export type BaseAxiosError = AxiosError;

+ 1 - 0
src/types/navigation.ts

@@ -53,4 +53,5 @@ export enum NAVIGATION_PAGES {
   REGION_PREVIEW = 'inAppRegionPreview',
   USERS_LIST = 'inAppUsersList',
   SUGGEST_SERIES = 'inAppSuggestSeries',
+  FRIENDS_LIST = 'inAppFriendsList',
 }

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است