Browse Source

range-calendar and add-photo components

Viktoriia 1 year ago
parent
commit
351a6517c7
40 changed files with 790 additions and 164 deletions
  1. 4 2
      Route.tsx
  2. 9 0
      app.config.ts
  3. 11 0
      assets/icons/add.svg
  4. 3 0
      assets/icons/mark.svg
  5. 0 3
      assets/icons/plus.svg
  6. 61 46
      package-lock.json
  7. 3 1
      package.json
  8. 22 1
      readme.md
  9. 22 63
      src/components/AddPhoto/index.tsx
  10. 55 0
      src/components/AddPhoto/style.tsx
  11. 63 0
      src/components/AvatarPicker/index.tsx
  12. 22 0
      src/components/AvatarPicker/styles.ts
  13. 1 3
      src/components/BigText/index.tsx
  14. 1 3
      src/components/Button/index.tsx
  15. 45 0
      src/components/Calendar/InputDatePicker/index.tsx
  16. 30 0
      src/components/Calendar/SpinnerDatePicker.tsx
  17. 3 2
      src/components/Calendars/RangeCalendar/index.tsx
  18. 26 0
      src/components/CheckBox/index.tsx
  19. 16 0
      src/components/CheckBox/styles.ts
  20. 90 0
      src/components/FlatList/index.tsx
  21. 40 0
      src/components/FlatList/item.tsx
  22. 44 0
      src/components/FlatList/modal-flatlist/index.tsx
  23. 24 0
      src/components/FlatList/styles.ts
  24. 1 3
      src/components/Header/index.tsx
  25. 20 6
      src/components/Input/index.tsx
  26. 10 5
      src/components/Modal/ModalHeader/modal-header.tsx
  27. 10 1
      src/components/Modal/ModalHeader/style.ts
  28. 30 4
      src/components/Modal/index.tsx
  29. 16 0
      src/components/Modal/style.ts
  30. 1 3
      src/components/PageWrapper/index.tsx
  31. 1 1
      src/components/PageWrapper/styles.ts
  32. 9 0
      src/components/index.ts
  33. 1 5
      src/screens/LoginScreen/index.tsx
  34. 39 0
      src/screens/RegisterScreen/EditAccount/index.tsx
  35. 49 0
      src/screens/RegisterScreen/JoinUs/index.tsx
  36. 1 4
      src/screens/ResetPasswordDeepScreen/index.tsx
  37. 1 6
      src/screens/ResetPasswordScreen/index.tsx
  38. 4 2
      src/screens/WelcomeScreen/index.tsx
  39. 1 0
      src/theme.ts
  40. 1 0
      src/types/navigation.ts

+ 4 - 2
Route.tsx

@@ -7,9 +7,10 @@ import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
 
 import WelcomeScreen from './src/screens/WelcomeScreen';
 import LoginScreen from './src/screens/LoginScreen';
-import RegisterScreen from './src/screens/RegisterScreen';
 import ResetPasswordScreen from './src/screens/ResetPasswordScreen';
 import ResetPasswordDeepScreen from './src/screens/ResetPasswordDeepScreen';
+import JoinUsScreen from './src/screens/RegisterScreen/JoinUs';
+import EditAccount from './src/screens/RegisterScreen/EditAccount';
 
 import { NAVIGATION_PAGES } from './src/types';
 import { storageGet } from './src/storage';
@@ -49,7 +50,8 @@ const Route = () => {
     >
       <ScreenStack.Screen name={NAVIGATION_PAGES.WELCOME} component={WelcomeScreen} />
       <ScreenStack.Screen name={NAVIGATION_PAGES.LOGIN} component={LoginScreen} />
-      <ScreenStack.Screen name={NAVIGATION_PAGES.REGISTER} component={RegisterScreen} />
+      <ScreenStack.Screen name={NAVIGATION_PAGES.REGISTER} component={JoinUsScreen} />
+      <ScreenStack.Screen name={NAVIGATION_PAGES.REGISTER_ACCOUNT_DATA} component={EditAccount} />
       <ScreenStack.Screen name={NAVIGATION_PAGES.RESET_PASSWORD} component={ResetPasswordScreen} />
       <ScreenStack.Screen
         name={NAVIGATION_PAGES.RESET_PASSWORD_DEEP}

+ 9 - 0
app.config.ts

@@ -10,6 +10,15 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
   orientation: 'portrait',
   icon: './assets/icon.png',
   userInterfaceStyle: 'light',
+  plugins: [
+    [
+      'expo-image-picker',
+      {
+        photosPermission: 'The app accesses your photos to let you share them with your friends.',
+        cameraPermission: 'The app accesses your photos to let you share them with your friends.'
+      }
+    ]
+  ],
   extra: {
     API_HOST: env.API_HOST
   },

+ 11 - 0
assets/icons/add.svg

@@ -0,0 +1,11 @@
+<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect width="36" height="36" rx="18" fill="#0F3F4F"/>
+<g clip-path="url(#clip0_599_3946)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M19.0841 11.5833C19.0841 10.985 18.5991 10.5 18.0007 10.5C17.4024 10.5 16.9174 10.985 16.9174 11.5833V16.9174H11.5833C10.985 16.9174 10.5 17.4024 10.5 18.0007C10.5 18.5991 10.985 19.0841 11.5833 19.0841H16.9174V24.4181C16.9174 25.0164 17.4024 25.5014 18.0007 25.5014C18.5991 25.5014 19.0841 25.0164 19.0841 24.4181V19.0841H24.4181C25.0164 19.0841 25.5014 18.5991 25.5014 18.0007C25.5014 17.4024 25.0164 16.9174 24.4181 16.9174H19.0841V11.5833Z" fill="white"/>
+</g>
+<defs>
+<clipPath id="clip0_599_3946">
+<rect width="20" height="20" fill="white" transform="translate(8 8)"/>
+</clipPath>
+</defs>
+</svg>

+ 3 - 0
assets/icons/mark.svg

@@ -0,0 +1,3 @@
+<svg width="21" height="14" viewBox="0 0 21 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M19.96 0.623409C20.4441 1.15362 20.4067 1.97589 19.8765 2.45999L8.3765 12.96C7.86324 13.4286 7.07216 13.4106 6.58071 12.9192L0.580712 6.9192C0.0730308 6.41152 0.0730308 5.5884 0.580712 5.08072C1.08839 4.57304 1.91151 4.57304 2.41919 5.08072L7.54079 10.2023L18.1234 0.539928C18.6536 0.0558231 19.4759 0.0931988 19.96 0.623409Z" fill="#0F3F4F"/>
+</svg>

+ 0 - 3
assets/icons/plus.svg

@@ -1,3 +0,0 @@
-<svg width="21" height="22" viewBox="0 0 21 22" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path fill-rule="evenodd" clip-rule="evenodd" d="M12.0177 2.01667C12.0177 1.17903 11.3387 0.5 10.501 0.5C9.66341 0.5 8.98438 1.17903 8.98438 2.01667V9.48438H1.51667C0.679035 9.48438 0 10.1634 0 11.001C0 11.8387 0.679035 12.5177 1.51667 12.5177H8.98438V19.9854C8.98438 20.823 9.66341 21.502 10.501 21.502C11.3387 21.502 12.0177 20.823 12.0177 19.9854V12.5177H19.4854C20.323 12.5177 21.002 11.8387 21.002 11.001C21.002 10.1634 20.323 9.48438 19.4854 9.48438H12.0177V2.01667Z" fill="white"/>
-</svg>

+ 61 - 46
package-lock.json

@@ -10,6 +10,7 @@
       "hasInstallScript": true,
       "dependencies": {
         "@react-native-async-storage/async-storage": "1.18.2",
+        "@react-native-community/datetimepicker": "7.2.0",
         "@react-navigation/bottom-tabs": "^6.5.11",
         "@react-navigation/native": "^6.1.9",
         "@react-navigation/native-stack": "^6.9.17",
@@ -18,7 +19,8 @@
         "axios": "^1.6.1",
         "dotenv": "^16.3.1",
         "expo": "~49.0.15",
-        "expo-image-picker": "^14.5.0",
+        "expo-checkbox": "~2.4.0",
+        "expo-image-picker": "~14.3.2",
         "expo-splash-screen": "~0.20.5",
         "expo-status-bar": "~1.6.0",
         "moment": "^2.29.4",
@@ -5823,6 +5825,14 @@
       "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
       "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
     },
+    "node_modules/@react-native-community/datetimepicker": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/@react-native-community/datetimepicker/-/datetimepicker-7.2.0.tgz",
+      "integrity": "sha512-dO1sQy83M/EvnHE2egto05iwXZX7EYn5f/VDMp6afZFRFXRiRo7CzB3VFg4B55gJRJMNBv06NYMLPM3SlpnEGQ==",
+      "dependencies": {
+        "invariant": "^2.2.4"
+      }
+    },
     "node_modules/@react-native/assets-registry": {
       "version": "0.72.0",
       "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.72.0.tgz",
@@ -6432,20 +6442,20 @@
       }
     },
     "node_modules/@tanstack/query-core": {
-      "version": "5.10.0",
-      "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.10.0.tgz",
-      "integrity": "sha512-wlw/l2E+U70iABaJnOtZIJN/5VMhuj4RPViafwUYiIGoqw1VqqqaxBnBL90qLhWswoOaK8RAj3+NiG0duk+cRg==",
+      "version": "5.12.1",
+      "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.12.1.tgz",
+      "integrity": "sha512-WbZztNmKq0t6QjdNmHzezbi/uifYo9j6e2GLJkodsYaYUlzMbAp91RDyeHkIZrm7EfO4wa6Sm5sxJZm5SPlh6w==",
       "funding": {
         "type": "github",
         "url": "https://github.com/sponsors/tannerlinsley"
       }
     },
     "node_modules/@tanstack/react-query": {
-      "version": "5.10.0",
-      "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.10.0.tgz",
-      "integrity": "sha512-LeJsyXUvhq1TBsEbt3SSEaxP2Att1sv/qW588GL/bvjxPxsLUBWKGuFJ5Z1YP+/nJqVmcXJE8AtvZaaxE9rsKQ==",
+      "version": "5.12.2",
+      "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.12.2.tgz",
+      "integrity": "sha512-BeWZu8zVFH20oRc+S/K9ADPgWjEzP/XQCGBNz5IbApUwPQAdwkQYbXODVL5AyAlWiSxhx+P2xlARPBApj2Yrog==",
       "dependencies": {
-        "@tanstack/query-core": "5.10.0"
+        "@tanstack/query-core": "5.12.1"
       },
       "funding": {
         "type": "github",
@@ -6492,9 +6502,9 @@
       }
     },
     "node_modules/@types/node": {
-      "version": "20.10.1",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.1.tgz",
-      "integrity": "sha512-T2qwhjWwGH81vUEx4EXmBKsTJRXFXNZTL4v0gi01+zyBmCwzE6TyHszqX01m+QHTEq+EZNo13NeJIdEqf+Myrg==",
+      "version": "20.10.3",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.3.tgz",
+      "integrity": "sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg==",
       "dependencies": {
         "undici-types": "~5.26.4"
       }
@@ -6506,9 +6516,9 @@
       "dev": true
     },
     "node_modules/@types/react": {
-      "version": "18.2.39",
-      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.39.tgz",
-      "integrity": "sha512-Oiw+ppED6IremMInLV4HXGbfbG6GyziY3kqAwJYOR0PNbkYDmLWQA3a95EhdSmamsvbkJN96ZNN+YD+fGjzSBA==",
+      "version": "18.2.41",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.41.tgz",
+      "integrity": "sha512-CwOGr/PiLiNBxEBqpJ7fO3kocP/2SSuC9fpH5K7tusrg4xPSRT/193rzolYwQnTN02We/ATXKnb6GqA5w4fRxw==",
       "dev": true,
       "dependencies": {
         "@types/prop-types": "*",
@@ -6517,9 +6527,9 @@
       }
     },
     "node_modules/@types/react-native": {
-      "version": "0.72.7",
-      "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.72.7.tgz",
-      "integrity": "sha512-LvxJD7VINBiJVcY9UgUVcmPMPiKGO5jHjPM6JrchfP+z1zx0AENL6UOGng1ueaHTRROMTVxZRzO7WVi6DFrMyw==",
+      "version": "0.72.8",
+      "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.72.8.tgz",
+      "integrity": "sha512-St6xA7+EoHN5mEYfdWnfYt0e8u6k2FR0P9s2arYgakQGFgU1f9FlPrIEcj0X24pLCF5c5i3WVuLCUdiCYHmOoA==",
       "dev": true,
       "dependencies": {
         "@react-native/virtualized-lists": "^0.72.4",
@@ -7126,9 +7136,9 @@
       }
     },
     "node_modules/browserslist": {
-      "version": "4.22.1",
-      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz",
-      "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==",
+      "version": "4.22.2",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz",
+      "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==",
       "funding": [
         {
           "type": "opencollective",
@@ -7144,9 +7154,9 @@
         }
       ],
       "dependencies": {
-        "caniuse-lite": "^1.0.30001541",
-        "electron-to-chromium": "^1.4.535",
-        "node-releases": "^2.0.13",
+        "caniuse-lite": "^1.0.30001565",
+        "electron-to-chromium": "^1.4.601",
+        "node-releases": "^2.0.14",
         "update-browserslist-db": "^1.0.13"
       },
       "bin": {
@@ -7348,9 +7358,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001565",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001565.tgz",
-      "integrity": "sha512-xrE//a3O7TP0vaJ8ikzkD2c2NgcVUvsEe2IvFTntV4Yd1Z9FVzh+gW+enX96L0psrbaFMcVcH2l90xNuGDWc8w==",
+      "version": "1.0.30001566",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001566.tgz",
+      "integrity": "sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==",
       "funding": [
         {
           "type": "opencollective",
@@ -8145,9 +8155,9 @@
       "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.4.597",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.597.tgz",
-      "integrity": "sha512-0XOQNqHhg2YgRVRUrS4M4vWjFCFIP2ETXcXe/0KIQBjXE9Cpy+tgzzYfuq6HGai3hWq0YywtG+5XK8fyG08EjA=="
+      "version": "1.4.601",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.601.tgz",
+      "integrity": "sha512-SpwUMDWe9tQu8JX5QCO1+p/hChAi9AE9UpoC3rcHVc+gdCGlbT3SGb5I1klgb952HRIyvt9wZhSz9bNBYz9swA=="
     },
     "node_modules/emoji-regex": {
       "version": "8.0.0",
@@ -8386,6 +8396,11 @@
         "url-parse": "^1.5.9"
       }
     },
+    "node_modules/expo-checkbox": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/expo-checkbox/-/expo-checkbox-2.4.0.tgz",
+      "integrity": "sha512-ZEe79B73I+NDkLZQ2pR1E5rv9ey1oleNI/s2/Jdb9zKWztS0KVU2K+drFn2t/zyoSbR1U+CNwik2v7h6JxrXcA=="
+    },
     "node_modules/expo-constants": {
       "version": "14.4.2",
       "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-14.4.2.tgz",
@@ -8421,19 +8436,19 @@
       }
     },
     "node_modules/expo-image-loader": {
-      "version": "4.4.0",
-      "resolved": "https://registry.npmjs.org/expo-image-loader/-/expo-image-loader-4.4.0.tgz",
-      "integrity": "sha512-dq88tiRB8aB1U+O/oJI7IY4VU9Nu2iDjEwR8QDfLtD4xWd5lNGoyic5Vh7911E1qNJFqNMSVIVzOD5l1SQ3A6g==",
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/expo-image-loader/-/expo-image-loader-4.3.0.tgz",
+      "integrity": "sha512-2kqJIO+oYM8J3GbvTUHLqTSpt1dLpOn/X0eB4U4RTuzz/faj8l/TyQELsMBLlGAkweNUuG9LqznbaBz+WuSFEw==",
       "peerDependencies": {
         "expo": "*"
       }
     },
     "node_modules/expo-image-picker": {
-      "version": "14.5.0",
-      "resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-14.5.0.tgz",
-      "integrity": "sha512-9icvfQuWMdNKufFqu+FxenKcfybz9ior+XYQCIl/q5+mpHPo2tKq8I30vu2BjsOJ0Cfo7V4vMeN7a9F/3ooUfQ==",
+      "version": "14.3.2",
+      "resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-14.3.2.tgz",
+      "integrity": "sha512-xr/YeQMIYheXecWP033F2SPwpBlBR5xVCx7YSfSCTH8Y9pw7Z886agqKGbS9QBVGlzJ5qecJktZ6ASSzeslDVg==",
       "dependencies": {
-        "expo-image-loader": "~4.4.0"
+        "expo-image-loader": "~4.3.0"
       },
       "peerDependencies": {
         "expo": "*"
@@ -12023,9 +12038,9 @@
       "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="
     },
     "node_modules/node-releases": {
-      "version": "2.0.13",
-      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
-      "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ=="
+      "version": "2.0.14",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
+      "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw=="
     },
     "node_modules/node-stream-zip": {
       "version": "1.15.0",
@@ -12878,9 +12893,9 @@
       }
     },
     "node_modules/postcss": {
-      "version": "8.4.31",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
-      "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+      "version": "8.4.32",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz",
+      "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==",
       "funding": [
         {
           "type": "opencollective",
@@ -12896,7 +12911,7 @@
         }
       ],
       "dependencies": {
-        "nanoid": "^3.3.6",
+        "nanoid": "^3.3.7",
         "picocolors": "^1.0.0",
         "source-map-js": "^1.0.2"
       },
@@ -14314,9 +14329,9 @@
       "dev": true
     },
     "node_modules/svgo": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.0.4.tgz",
-      "integrity": "sha512-T+Xul3JwuJ6VGXKo/p2ndqx1ibxNKnLTvRc1ZTWKCfyKS/GgNjRZcYsK84fxTsy/izr91g/Rwx6fGnVgaFSI5g==",
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.0.5.tgz",
+      "integrity": "sha512-HQKHEo73pMNOlDlBcLgZRcHW2+1wo7bFYayAXkGN0l/2+h68KjlfZyMRhdhaGvoHV2eApOovl12zoFz42sT6rQ==",
       "dev": true,
       "dependencies": {
         "@trysound/sax": "0.2.0",

+ 3 - 1
package.json

@@ -11,6 +11,7 @@
   },
   "dependencies": {
     "@react-native-async-storage/async-storage": "1.18.2",
+    "@react-native-community/datetimepicker": "7.2.0",
     "@react-navigation/bottom-tabs": "^6.5.11",
     "@react-navigation/native": "^6.1.9",
     "@react-navigation/native-stack": "^6.9.17",
@@ -19,7 +20,8 @@
     "axios": "^1.6.1",
     "dotenv": "^16.3.1",
     "expo": "~49.0.15",
-    "expo-image-picker": "^14.5.0",
+    "expo-checkbox": "~2.4.0",
+    "expo-image-picker": "~14.3.2",
     "expo-splash-screen": "~0.20.5",
     "expo-status-bar": "~1.6.0",
     "moment": "^2.29.4",

+ 22 - 1
readme.md

@@ -8,4 +8,25 @@
 #### Launching
 1. Download Expo Go on your phone.
 2. Start project with command `npm start`.
-3. Open Expo Go on your phone and scan the QR-code or enter you Expo Go IP server manually.
+3. Open Expo Go on your phone and scan the QR-code or enter you Expo Go IP server manually.
+
+
+#### App Structure
+
+App.tsx                       || App entrypoint file <br>
+Route.tsx                     || App routes <br>
+src/ <br>
+├── components/               || Reusable UI components <br>
+├── constants/                || Secrets and environment variables <br>
+├── modules/                  || API calls and data fetching logic <br>
+├── screens/                  || App screens built with components <br>
+├── storage/                  || Async storage functions <br>
+├── types/                    || Navigation types, enums, and component props <br>
+├── utils/                    || Reusable functions and utilities <br>
+└── theme.ts                  || Global theme styles <br>
+
+
+##### Future updates:
+
+- [ ] Error handler
+- [ ] Rework React-Query

+ 22 - 63
src/components/AddPhoto/index.tsx

@@ -1,21 +1,27 @@
 import React, { useState } from 'react';
-import { View, Image, TextInput, TouchableOpacity, StyleSheet, Alert, Text } from 'react-native';
+import {
+  View,
+  Image,
+  TouchableOpacity,
+  Alert,
+  Text
+} from 'react-native';
 import * as ImagePicker from 'expo-image-picker';
-import Modal from '../Modal';
-import Input from '../Input';
+import { Modal } from '../Modal';
+import { Input } from '../Input';
+import { styles } from './style';
 
-import PlusSVG from '../../../assets/icons/plus.svg';
+import AddSVG from '../../../assets/icons/add.svg';
 import TrashSVG from '../../../assets/icons/trash.svg';
 
 interface AddPhotoProps {
+  isModalVisible: boolean;
   initialImagePath?: string | null;
   initialImageDescription?: string;
-  onImagePicked?: (uri: string) => void;
-  onImageDeleted?: () => void;
   closeModal: () => void;
 }
 
-const AddPhoto: React.FC<AddPhotoProps> = ({ initialImagePath, initialImageDescription, onImagePicked, onImageDeleted, closeModal }) => {
+const AddPhoto: React.FC<AddPhotoProps> = ({ isModalVisible, initialImagePath, initialImageDescription, closeModal }) => {
   const [image, setImage] = useState(initialImagePath);
   const [description, setDescription] = useState(initialImageDescription ?? '');
 
@@ -24,7 +30,6 @@ const AddPhoto: React.FC<AddPhotoProps> = ({ initialImagePath, initialImageDescr
       { text: 'Cancel' },
       { text: 'Delete', onPress: () => {
         setImage(null);
-        // onImageDeleted();
       }},
     ]);
   };
@@ -38,37 +43,36 @@ const AddPhoto: React.FC<AddPhotoProps> = ({ initialImagePath, initialImageDescr
 
     let result = await ImagePicker.launchImageLibraryAsync({
       mediaTypes: ImagePicker.MediaTypeOptions.Images,
-      // aspect: [4, 3],
       quality: 1,
     });
 
     if (!result.canceled) {
       setImage(result.assets[0].uri);
-      // onImagePicked(result.uri);
     }
   };
 
   return (
     <Modal
-      visible={true}
+      visibleInPercent={'90%'}
+      visible={isModalVisible}
       onRequestClose={closeModal}
-      textHeader='Add Photo'
+      headerTitle='Add Photo'
     >
-      <View style={styles.container}>
-        <View style={styles.imageContainer}>
+      <View style={[styles.container, styles.centerPosition]}>
+        <View style={[styles.imageContainer, styles.centerPosition]}>
           {image ? (
             <>
               <TouchableOpacity onPress={handleDelete} style={styles.deleteButton}>
-                <View style={[styles.addButton, {width: 40, height: 40, backgroundColor: '#EF5B5B', borderRadius: 20}]}>
+                <View style={[styles.centerPosition, styles.deleteBtn]}>
                   <TrashSVG />
                 </View>
               </TouchableOpacity>
               <Image source={{ uri: image }} style={styles.image} />
             </>
           ) : (
-            <TouchableOpacity onPress={handleImagePick} style={styles.addButton}>
-              <View style={[styles.addButton, {width: 48, height: 48, backgroundColor: '#0F3F4F', borderRadius: 24}]}>
-                <PlusSVG />
+            <TouchableOpacity onPress={handleImagePick} style={styles.centerPosition}>
+              <View style={[styles.centerPosition, styles.addBtn]}>
+                <AddSVG width={48} height={48} />
               </View>
               
               <Text style={styles.addButtonText}>Upload Photo</Text>
@@ -87,49 +91,4 @@ const AddPhoto: React.FC<AddPhotoProps> = ({ initialImagePath, initialImageDescr
   );
 };
 
-const styles = StyleSheet.create({
-  container: {
-    alignItems: 'center',
-    justifyContent: 'center',
-    padding: 24,
-  },
-  imageContainer: {
-    position: 'relative',
-    marginBottom: 8,
-    width: '100%',
-    height: 327,
-    alignItems: 'center',
-    justifyContent: 'center',
-    borderRadius: 8,
-    backgroundColor: '#FAFAFA'
-  },
-  deleteButton: {
-    position: 'absolute',
-    top: 16,
-    right: 16,
-    zIndex: 1,
-  },
-  image: {
-    width: '100%',
-    height: '100%',
-    resizeMode: 'cover',
-    borderRadius: 8,
-  },
-  description: {
-    width: '100%',
-    marginTop: 8,
-  },
-  addButton: {
-    justifyContent: 'center',
-    alignItems: 'center',
-  },
-  addButtonText: {
-    fontSize: 16,
-    marginTop: 10,
-    fontWeight: '700',
-    color: '#0F3F4F',
-    lineHeight: 24,
-  },
-});
-
 export default AddPhoto;

+ 55 - 0
src/components/AddPhoto/style.tsx

@@ -0,0 +1,55 @@
+import { StyleSheet } from 'react-native';
+import { Colors } from '../../theme';
+
+export const styles = StyleSheet.create({
+  container: {
+    padding: 24,
+  },
+  imageContainer: {
+    position: 'relative',
+    marginBottom: 8,
+    width: '100%',
+    height: 327,
+    borderRadius: 8,
+    backgroundColor: '#FAFAFA'
+  },
+  deleteButton: {
+    position: 'absolute',
+    top: 16,
+    right: 16,
+    zIndex: 1,
+  },
+  image: {
+    width: '100%',
+    height: '100%',
+    resizeMode: 'cover',
+    borderRadius: 8,
+  },
+  description: {
+    width: '100%',
+    marginTop: 8,
+  },
+  centerPosition: {
+    justifyContent: 'center',
+    alignItems: 'center',
+  },
+  addButtonText: {
+    fontSize: 16,
+    marginTop: 10,
+    fontWeight: '700',
+    color: Colors.DARK_BLUE,
+    lineHeight: 24,
+  },
+  deleteBtn: {
+    width: 40,
+    height: 40,
+    backgroundColor: Colors.RED,
+    borderRadius: 20
+  },
+  addBtn: {
+    width: 48,
+    height: 48,
+    backgroundColor: Colors.DARK_BLUE,
+    borderRadius: 24
+  }
+});

+ 63 - 0
src/components/AvatarPicker/index.tsx

@@ -0,0 +1,63 @@
+import React, { FC, useState } from 'react';
+import { Image, Text, TouchableOpacity } from 'react-native';
+import * as ImagePicker from 'expo-image-picker';
+import * as FileSystem from 'expo-file-system';
+import AddIcon from '../../../assets/icons/add.svg';
+
+import { styles } from './styles';
+
+//TODO: simple refactor + download photo
+
+export const AvatarPicker: FC = () => {
+  const [image, setImage] = useState('');
+
+  const pickImage = async () => {
+    // No permissions request is necessary for launching the image library
+    let result = await ImagePicker.launchImageLibraryAsync({
+      mediaTypes: ImagePicker.MediaTypeOptions.Images,
+      allowsEditing: true,
+      aspect: [4, 3],
+      quality: 1
+    });
+
+    console.log(result);
+
+    if (!result.canceled) {
+      setImage(result.assets[0].uri);
+
+      // const uploadResult = await FileSystem.uploadAsync(
+      //   'https://nomadmania.eu/webapi/user/join2',
+      //   result.assets[0].uri,
+      //   {
+      //     httpMethod: 'POST',
+      //     uploadType: FileSystem.FileSystemUploadType.MULTIPART,
+      //     fieldName: 'demo_image',
+      //     headers: {
+      //       nmusername: 'test',
+      //       nmemail: 'test@gmail.com',
+      //       nmpassword: 'testpass123',
+      //       nmfirstname: 'Name',
+      //       nmlastname: 'Lastname',
+      //       nmdateofbirth: `${new Date()}`,
+      //       nmhomebase: 'testid10'
+      //     }
+      //   }
+      // );
+      //
+      // console.log(JSON.stringify(uploadResult));
+    }
+  };
+  return (
+    <TouchableOpacity style={styles.avatarWrapper} onPress={pickImage}>
+      {!image && (
+        <>
+          <AddIcon />
+          <Text style={styles.textAvatar}>Upload Photo</Text>
+        </>
+      )}
+      {image && (
+        <Image source={{ uri: image }} style={{ width: 100, height: 100, borderRadius: 100 / 2 }} />
+      )}
+    </TouchableOpacity>
+  );
+};

+ 22 - 0
src/components/AvatarPicker/styles.ts

@@ -0,0 +1,22 @@
+import { StyleSheet } from 'react-native';
+import { Colors } from '../../theme';
+
+export const styles = StyleSheet.create({
+  textAvatar: {
+    color: Colors.DARK_BLUE,
+    fontSize: 12,
+    fontFamily: 'redhat-700'
+  },
+  avatarWrapper: {
+    width: 100,
+    height: 100,
+    borderRadius: 100 / 2,
+    borderWidth: 0.5,
+    borderColor: Colors.WHITE,
+    backgroundColor: Colors.WHITE,
+    borderStyle: 'solid',
+    display: 'flex',
+    justifyContent: 'center',
+    alignItems: 'center'
+  }
+});

+ 1 - 3
src/components/BigText/index.tsx

@@ -6,8 +6,6 @@ type Props = {
   children: ReactNode;
 };
 
-const BigText: FC<Props> = ({ children }) => {
+export const BigText: FC<Props> = ({ children }) => {
   return <Text style={styles.text}>{children}</Text>;
 };
-
-export default BigText;

+ 1 - 3
src/components/Button/index.tsx

@@ -10,7 +10,7 @@ type Props = {
   onPress?: () => void;
 };
 
-const Button: FC<Props> = ({ children, variant, onPress }) => {
+export const Button: FC<Props> = ({ children, variant, onPress }) => {
   return (
     <>
       {variant === ButtonVariants.OPACITY ? (
@@ -48,5 +48,3 @@ const TextButton: FC<VariantProps> = ({ onPress, children }) => (
     <Text style={[styles.text, styles.textButtonText]}>{children}</Text>
   </TouchableOpacity>
 );
-
-export default Button;

+ 45 - 0
src/components/Calendar/InputDatePicker/index.tsx

@@ -0,0 +1,45 @@
+import React, { FC, useState } from 'react';
+import { View } from 'react-native';
+
+import { Input } from '../../Input';
+import { Modal } from '../../Modal';
+import SpinnerDatePicker from '../SpinnerDatePicker';
+import { Button } from '../../Button';
+
+type Props = {
+  selectedDate?: (date: Date) => void;
+};
+
+//TODO: waiting for redesign in figma + input fixes
+
+export const InputDatePicker: FC<Props> = ({ selectedDate }) => {
+  const [visible, setVisible] = useState(false);
+  const [spinnerSelectedDate, setSpinnerSelectedDate] = useState<Date>(new Date());
+
+  const onButtonPress = () => {
+    setVisible(false);
+    if (selectedDate) {
+      selectedDate(spinnerSelectedDate);
+    }
+  };
+
+  return (
+    <View>
+      <Input
+        header={'Date of birth'}
+        value={new Date(spinnerSelectedDate).toLocaleDateString()}
+        isFocused={(b) => setVisible(b)}
+        inputMode={'none'}
+      />
+      <Modal
+        visibleInPercent={'50%'}
+        onRequestClose={() => setVisible(false)}
+        headerTitle={'Select Region of Origin'}
+        visible={visible}
+      >
+        <SpinnerDatePicker selectedDate={(date) => setSpinnerSelectedDate(date)} />
+        <Button onPress={onButtonPress}>Done</Button>
+      </Modal>
+    </View>
+  );
+};

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

@@ -0,0 +1,30 @@
+import React, { FC, useState } from 'react';
+import { Colors } from '../../theme';
+import DateTimePicker, { DateTimePickerEvent } from '@react-native-community/datetimepicker';
+
+type Props = {
+  selectedDate: (date: Date) => void;
+};
+
+const SpinnerDatePicker: FC<Props> = ({ selectedDate }) => {
+  const [value, setValue] = useState<Date>(new Date());
+
+  const onChange = (event: DateTimePickerEvent, selectedSpinnerDate?: Date) => {
+    if (event.type === 'set') {
+      selectedDate(selectedSpinnerDate!);
+      setValue(selectedSpinnerDate!);
+    }
+  };
+
+  return (
+    <DateTimePicker
+      value={value}
+      textColor={Colors.DARK_BLUE}
+      mode={'date'}
+      display={'spinner'}
+      onChange={onChange}
+    />
+  );
+};
+
+export default SpinnerDatePicker;

+ 3 - 2
src/components/Calendars/RangeCalendar/index.tsx

@@ -2,7 +2,7 @@ import React, { useMemo, useState } from 'react';
 import { View } from 'react-native';
 import CalendarPicker, { CustomDatesStylesFunc, CustomDayHeaderStylesFunc } from 'react-native-calendar-picker';
 import moment from 'moment';
-import Modal from '../../Modal';
+import { Modal } from '../../Modal';
 import Navigation from './Navigation';
 
 import { styles } from './style';
@@ -66,9 +66,10 @@ export default function RangeCalendar(
 
   return (
     <Modal
+      visibleInPercent={'70%'}
       visible={isModalVisible}
       onRequestClose={resetSelections}
-      textHeader='Select Date'
+      headerTitle='Select Date'
     >
       <View style={styles.modalContent}>
         <CalendarPicker

+ 26 - 0
src/components/CheckBox/index.tsx

@@ -0,0 +1,26 @@
+import React, { FC } from 'react';
+import Checkbox from 'expo-checkbox';
+import { Text, View } from 'react-native';
+
+import { Colors } from '../../theme';
+import { styles } from './styles';
+
+type Props = {
+  onChange?: (value: boolean) => void;
+  value?: boolean;
+  label?: string;
+};
+
+export const CheckBox: FC<Props> = ({ value, onChange, label }) => {
+  return (
+    <View style={styles.wrapper}>
+      <Checkbox
+        style={{ backgroundColor: Colors.WHITE }}
+        color={Colors.ORANGE}
+        value={value}
+        onValueChange={onChange}
+      />
+      {label ? <Text style={styles.text}>{label}</Text> : null}
+    </View>
+  );
+};

+ 16 - 0
src/components/CheckBox/styles.ts

@@ -0,0 +1,16 @@
+import { StyleSheet } from 'react-native';
+import { Colors } from '../../theme';
+import { getFontSize } from '../../utils';
+
+export const styles = StyleSheet.create({
+  wrapper: {
+    display: 'flex',
+    flexDirection: 'row',
+    alignItems: 'center'
+  },
+  text: {
+    color: Colors.DARK_BLUE,
+    fontSize: getFontSize(12),
+    marginLeft: 10
+  }
+});

+ 90 - 0
src/components/FlatList/index.tsx

@@ -0,0 +1,90 @@
+import React, { FC, useEffect, useState } from 'react';
+import { FlatList as List, SafeAreaView } from 'react-native';
+import { Input } from '../Input';
+import { styles } from './styles';
+import { Item, ItemData } from './item';
+
+const DATA: ItemData[] = [
+  {
+    id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba',
+    title: 'First Item'
+  },
+  {
+    id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63',
+    title: 'Second Item'
+  },
+  {
+    id: '58694a0f-3da1-471f-bd96-145571e29d72',
+    title: 'Third Item'
+  }
+];
+
+type Props = {
+  itemObject: (object: any) => void;
+};
+
+//TODO: rework to generic types + custom props + fetch data
+
+export const FlatList: FC<Props> = ({ itemObject }) => {
+  const [selectedObject, setSelectedObject] = useState<{ id: string; title: string }>();
+  const [search, setSearch] = useState('');
+  const [filteredData, setFilteredData] = useState<ItemData[]>([]);
+  const [masterData, setMasterData] = useState<ItemData[]>([]);
+
+  useEffect(() => {
+    setFilteredData(DATA);
+    setMasterData(DATA);
+  }, []);
+
+  const selectItem = (object: { title: string; id: string }) => {
+    itemObject(object);
+    setSelectedObject(object);
+  };
+
+  const searchFilter = (text: string) => {
+    if (text) {
+      const newData = masterData.filter((item) => {
+        const itemData = item.title ? item.title.toLowerCase() : ''.toLowerCase();
+        const textData = text.toLowerCase();
+        return itemData.indexOf(textData) > -1;
+      });
+      setFilteredData(newData);
+      setSearch(text);
+    } else {
+      setFilteredData(masterData);
+      setSearch(text);
+    }
+  };
+
+  const renderItem = ({ item }: { item: ItemData }) => {
+    const selected = item.id === selectedObject?.id;
+
+    const backgroundColor = selected ? '#FAFAFA' : 'white';
+
+    return (
+      <Item
+        selected={selected}
+        item={item}
+        onPress={() => selectItem(item)}
+        backgroundColor={backgroundColor}
+      />
+    );
+  };
+
+  return (
+    <SafeAreaView style={styles.container}>
+      <Input
+        inputMode={'search'}
+        placeholder={'test'}
+        onChange={(text) => searchFilter(text)}
+        value={search}
+      />
+      <List
+        data={filteredData}
+        renderItem={renderItem}
+        keyExtractor={(item) => item.id}
+        extraData={selectedObject}
+      />
+    </SafeAreaView>
+  );
+};

+ 40 - 0
src/components/FlatList/item.tsx

@@ -0,0 +1,40 @@
+import React from 'react';
+import { Image, Text, TouchableOpacity, View } from 'react-native';
+import { Colors } from '../../theme';
+import { styles } from './styles';
+
+import MarkSVG from '../../../assets/icons/mark.svg';
+
+//TODO: waiting for API images + split name for title and description of region
+
+export const Item = ({ item, onPress, backgroundColor, selected }: ItemProps) => (
+  <TouchableOpacity onPress={onPress} style={[styles.item, { backgroundColor }]}>
+    <View style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 10 }}>
+      <Image
+        width={48}
+        height={48}
+        style={{ borderRadius: 48 / 2 }}
+        source={{
+          uri: 'file:///var/mobile/Containers/Data/Application/B7AB06B4-BE07-4161-A992-EFE45C948D82/Library/Caches/ExponentExperienceData/%2540anonymous%252Fnomadmania-app-3e93ece1-6230-4d9d-aad1-f0bff67932b5/ImagePicker/2ADA6086-B98C-4260-B2C2-B376DE410785.jpg'
+        }}
+      />
+      <View>
+        <Text style={[styles.title, { color: Colors.DARK_BLUE }]}>{item.title}</Text>
+        <Text style={[styles.text, { color: Colors.DARK_BLUE }]}>{item.title}</Text>
+      </View>
+    </View>
+    <View style={{ marginRight: 10 }}>{selected && <MarkSVG />}</View>
+  </TouchableOpacity>
+);
+
+export type ItemData = {
+  id: string;
+  title: string;
+};
+
+type ItemProps = {
+  item: ItemData;
+  onPress: () => void;
+  backgroundColor: string;
+  selected: boolean;
+};

+ 44 - 0
src/components/FlatList/modal-flatlist/index.tsx

@@ -0,0 +1,44 @@
+import React, { FC, useState } from 'react';
+import { View } from 'react-native';
+
+import { Input } from '../../Input';
+import { Modal } from '../../Modal';
+import { FlatList } from '../index';
+
+type Props = {
+  selectedObject?: (object: any) => void;
+  headerTitle: string;
+};
+
+//TODO: rework to generic types
+
+export const ModalFlatList: FC<Props> = ({ selectedObject, headerTitle }) => {
+  const [visible, setVisible] = useState(false);
+  const [selectedFlatListObject, setSelectedFlatListObject] = useState<{ title?: string }>({});
+
+  const onSelect = (object: { title: string }) => {
+    setVisible(false);
+    if (selectedObject) {
+      selectedObject(object);
+      setSelectedFlatListObject(object);
+    }
+  };
+
+  return (
+    <View>
+      <Input
+        header={headerTitle}
+        isFocused={(b) => setVisible(b)}
+        value={selectedFlatListObject?.title}
+        inputMode={'none'}
+      />
+      <Modal
+        onRequestClose={() => setVisible(false)}
+        headerTitle={'Select Region of Origin'}
+        visible={visible}
+      >
+        <FlatList itemObject={(object) => onSelect(object)} />
+      </Modal>
+    </View>
+  );
+};

+ 24 - 0
src/components/FlatList/styles.ts

@@ -0,0 +1,24 @@
+import { StatusBar, StyleSheet } from 'react-native';
+import { getFontSize } from '../../utils';
+
+export const styles = StyleSheet.create({
+  container: {
+    flex: 1,
+    marginTop: StatusBar.currentHeight || 0
+  },
+  item: {
+    width: '100%',
+    height: 60,
+    display: 'flex',
+    flexDirection: 'row',
+    alignItems: 'center',
+    justifyContent: 'space-between'
+  },
+  title: {
+    fontSize: getFontSize(14),
+    fontFamily: 'redhat-700'
+  },
+  text: {
+    fontSize: getFontSize(12)
+  }
+});

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

@@ -10,7 +10,7 @@ type Props = {
   label: string;
 };
 
-const Header: FC<Props> = ({ label }) => {
+export const Header: FC<Props> = ({ label }) => {
   const navigation = useNavigation();
 
   // navigation.setOptions() TODO
@@ -29,5 +29,3 @@ const Header: FC<Props> = ({ label }) => {
     </View>
   );
 };
-
-export default Header;

+ 20 - 6
src/components/Input/index.tsx

@@ -3,14 +3,24 @@ import { TextInput, Text, View, InputModeOptions } from 'react-native';
 import { styling } from './style';
 
 type Props = {
-  placeholder: string;
-  onChange: (text: string) => void;
+  placeholder?: string;
+  onChange?: (text: string) => void;
   header?: string;
   isPrivate?: boolean;
   inputMode?: InputModeOptions;
+  isFocused?: (boolean: boolean) => void;
+  value?: string;
 };
 
-const Input: FC<Props> = ({ onChange, placeholder, header, isPrivate, inputMode }) => {
+export const Input: FC<Props> = ({
+  onChange,
+  placeholder,
+  header,
+  isPrivate,
+  inputMode,
+  isFocused,
+  value
+}) => {
   const [focused, setFocused] = useState(false);
 
   const styles = styling(focused);
@@ -19,16 +29,20 @@ const Input: FC<Props> = ({ onChange, placeholder, header, isPrivate, inputMode
     <View>
       {header ? <Text style={styles.text}>{header}</Text> : null}
       <TextInput
+        value={value}
         inputMode={inputMode ?? 'text'}
         secureTextEntry={isPrivate ?? false}
         placeholder={placeholder}
         onChangeText={onChange}
-        onFocus={() => setFocused(true)}
+        onFocus={() => {
+          setFocused(true);
+          if (isFocused) {
+            isFocused(true);
+          }
+        }}
         onBlur={() => setFocused(false)}
         style={styles.wrapper}
       />
     </View>
   );
 };
-
-export default Input;

+ 10 - 5
src/components/Modal/ModalHeader/modal-header.tsx

@@ -1,5 +1,5 @@
 import React, { FC } from 'react';
-import { View, Text } from 'react-native';
+import { View, Text, TouchableOpacity } from 'react-native';
 
 import { styles } from './style';
 
@@ -7,14 +7,19 @@ import CloseSVG from '../../../../assets/icons/close.svg';
 
 type Props = {
   textHeader: string;
+  onRequestClose?: () => void;
 };
 
-const ModalHeader: FC<Props> = ({ textHeader }) => {
+//TODO
+
+export const ModalHeader: FC<Props> = ({ textHeader, onRequestClose }) => {
   return (
-    <View>
+    <View style={styles.wrapperHeader}>
+      <TouchableOpacity onPress={onRequestClose} style={{ height: 30, width: 30 }}>
+        <CloseSVG />
+      </TouchableOpacity>
       <Text style={styles.textHeader}>{textHeader}</Text>
+      <Text>Temp</Text>
     </View>
   );
 };
-
-export default ModalHeader;

+ 10 - 1
src/components/Modal/ModalHeader/style.ts

@@ -1,10 +1,19 @@
 import { StyleSheet } from 'react-native';
 import { Colors } from '../../../theme';
+import { getFontSize } from '../../../utils';
 
 export const styles = StyleSheet.create({
   textHeader: {
     fontFamily: 'redhat-600',
-    fontSize: 14,
+    fontSize: getFontSize(14),
     color: Colors.DARK_BLUE
+  },
+  wrapperHeader: {
+    width: '100%',
+    height: 30,
+    display: 'flex',
+    justifyContent: 'space-between',
+    flexDirection: 'row',
+    marginTop: 15
   }
 });

+ 30 - 4
src/components/Modal/index.tsx

@@ -1,13 +1,23 @@
 import React, { FC, ReactNode } from 'react';
-import { Modal as ReactModal } from 'react-native';
+import { DimensionValue, Modal as ReactModal, View } from 'react-native';
+import { ModalHeader } from './ModalHeader/modal-header';
+import { styles } from './style';
 
 type Props = {
   children: ReactNode;
   visible: boolean;
   onRequestClose?: () => void;
+  visibleInPercent?: DimensionValue;
+  headerTitle: string;
 };
 
-const Modal: FC<Props> = ({ children, onRequestClose, visible }) => {
+export const Modal: FC<Props> = ({
+  children,
+  onRequestClose,
+  visible,
+  visibleInPercent,
+  headerTitle
+}) => {
   return (
     <ReactModal
       animationType={'slide'}
@@ -15,10 +25,26 @@ const Modal: FC<Props> = ({ children, onRequestClose, visible }) => {
       presentationStyle={'pageSheet'}
       visible={visible}
       onRequestClose={onRequestClose}
+      transparent={true}
     >
-      {children}
+      <View
+        style={[
+          styles.wrapper,
+          {
+            height: visibleInPercent ?? '100%',
+            borderTopLeftRadius: visibleInPercent ? 10 : 0,
+            borderTopRightRadius: visibleInPercent ? 10 : 0
+          }
+        ]}
+      >
+        <ModalHeader onRequestClose={onRequestClose} textHeader={headerTitle} />
+        <Drawer />
+        {children}
+      </View>
     </ReactModal>
   );
 };
 
-export default Index;
+const Drawer = () => {
+  return <View style={styles.drawer} />;
+};

+ 16 - 0
src/components/Modal/style.ts

@@ -0,0 +1,16 @@
+import { StyleSheet } from 'react-native';
+import { Colors } from '../../theme';
+
+export const styles = StyleSheet.create({
+  drawer: {
+    width: '100%',
+    height: 1,
+    backgroundColor: '#E5E5E5'
+  },
+  wrapper: {
+    marginTop: 'auto',
+    backgroundColor: Colors.WHITE,
+    paddingLeft: 15,
+    paddingRight: 15
+  }
+});

+ 1 - 3
src/components/PageWrapper/index.tsx

@@ -8,8 +8,6 @@ type Props = {
   style?: StyleProp<ViewStyle>;
 };
 
-const PageWrapper: FC<Props> = ({ children, style }) => {
+export const PageWrapper: FC<Props> = ({ children, style }) => {
   return <SafeAreaView style={[styles.wrapper, style]}>{children}</SafeAreaView>;
 };
-
-export default PageWrapper;

+ 1 - 1
src/components/PageWrapper/styles.ts

@@ -1,5 +1,5 @@
 import { StyleSheet } from 'react-native';
 
 export const styles = StyleSheet.create({
-  wrapper: { marginLeft: '5%', marginRight: '5%' }
+  wrapper: { marginLeft: '5%', marginRight: '5%', height: '100%' }
 });

+ 9 - 0
src/components/index.ts

@@ -0,0 +1,9 @@
+export * from './CheckBox';
+export * from './Button';
+export * from './BigText';
+export * from './Header';
+export * from './Input';
+export * from './Modal';
+export * from './PageWrapper';
+export * from './AvatarPicker';
+export * from './FlatList';

+ 1 - 5
src/screens/LoginScreen/index.tsx

@@ -2,11 +2,7 @@ import { FC, useState } from 'react';
 import { View } from 'react-native';
 import { NavigationProp } from '@react-navigation/native';
 
-import Header from '../../components/Header';
-import Input from '../../components/Input';
-import Button from '../../components/Button';
-import BigText from '../../components/BigText';
-import PageWrapper from '../../components/PageWrapper';
+import { Header, Input, Button, BigText, PageWrapper } from '../../components';
 
 import { ButtonVariants } from '../../types/components';
 import { storageSet } from '../../storage';

+ 39 - 0
src/screens/RegisterScreen/EditAccount/index.tsx

@@ -0,0 +1,39 @@
+import React from 'react';
+import { View, ScrollView } from 'react-native';
+import { AvatarPicker, BigText, Button, Header, Input, PageWrapper } from '../../../components';
+import { InputDatePicker } from '../../../components/Calendar/InputDatePicker';
+import { ModalFlatList } from '../../../components/FlatList/modal-flatlist';
+
+//TODO: connect with API + simple refactor
+
+const EditAccount = () => {
+  return (
+    <PageWrapper>
+      <ScrollView showsVerticalScrollIndicator={false}>
+        <View style={{ gap: 10 }}>
+          <Header label={'Sign Up'} />
+          <BigText>Edit account data</BigText>
+          <View style={{ display: 'flex', alignItems: 'center' }}>
+            <AvatarPicker />
+          </View>
+          <Input placeholder={'Text'} onChange={() => {}} header={'First name'} />
+          <Input placeholder={'Text'} onChange={() => {}} header={'Last name'} />
+          <InputDatePicker selectedDate={(date) => console.log(date)} />
+          <ModalFlatList
+            headerTitle={'Region of origin'}
+            selectedObject={(data) => console.log(data)}
+          />
+          <ModalFlatList
+            headerTitle={'Second region'}
+            selectedObject={(data) => console.log(data)}
+          />
+          <View style={{ marginTop: 10 }}>
+            <Button>Sign up</Button>
+          </View>
+        </View>
+      </ScrollView>
+    </PageWrapper>
+  );
+};
+
+export default EditAccount;

+ 49 - 0
src/screens/RegisterScreen/JoinUs/index.tsx

@@ -0,0 +1,49 @@
+import React, { FC, useState } from 'react';
+import { View } from 'react-native';
+import { NavigationProp } from '@react-navigation/native';
+
+import { PageWrapper, Header, Button, BigText, CheckBox, Input } from '../../../components';
+import { NAVIGATION_PAGES } from '../../../types';
+
+type Props = {
+  navigation: NavigationProp<any>;
+};
+
+const JoinUsScreen: FC<Props> = ({ navigation }) => {
+  const [agreed, setAgreed] = useState(false);
+
+  return (
+    <PageWrapper>
+      <Header label={'Sign Up'} />
+      <View style={{ gap: 15 }}>
+        <BigText>Join us. It's free!</BigText>
+        <Input onChange={() => {}} placeholder={'Text'} header={'Username'} />
+        <Input
+          onChange={() => {}}
+          placeholder={'Email'}
+          inputMode={'email'}
+          header={'Email address'}
+        />
+        <Input onChange={() => {}} placeholder={'Text'} isPrivate={true} header={'Password'} />
+        <Input
+          onChange={() => {}}
+          placeholder={'Text'}
+          isPrivate={true}
+          header={'Confirm password'}
+        />
+        <CheckBox
+          label={'I accept NM Terms & Conditions'}
+          onChange={(b) => setAgreed(b)}
+          value={agreed}
+        />
+      </View>
+      <View style={{ marginTop: '15%' }}>
+        <Button onPress={() => navigation.navigate(NAVIGATION_PAGES.REGISTER_ACCOUNT_DATA)}>
+          Continue
+        </Button>
+      </View>
+    </PageWrapper>
+  );
+};
+
+export default JoinUsScreen;

+ 1 - 4
src/screens/ResetPasswordDeepScreen/index.tsx

@@ -1,10 +1,7 @@
 import React, { useState } from 'react';
 import { Text, View } from 'react-native';
 
-import PageWrapper from '../../components/PageWrapper';
-import BigText from '../../components/BigText';
-import Input from '../../components/Input';
-import Button from '../../components/Button';
+import { PageWrapper, BigText, Input, Button } from '../../components/';
 
 import { styles } from './styles';
 

+ 1 - 6
src/screens/ResetPasswordScreen/index.tsx

@@ -2,12 +2,7 @@ import React, { FC, useState } from 'react';
 import { Text, View } from 'react-native';
 import type { NavigationProp } from '@react-navigation/native';
 
-import PageWrapper from '../../components/PageWrapper';
-import Header from '../../components/Header';
-import BigText from '../../components/BigText';
-import Button from '../../components/Button';
-import Input from '../../components/Input';
-
+import { PageWrapper, Header, BigText, Button, Input } from '../../components/';
 import { NAVIGATION_PAGES } from '../../types';
 
 import { styles } from './styles';

+ 4 - 2
src/screens/WelcomeScreen/index.tsx

@@ -2,7 +2,7 @@ import { FC } from 'react';
 import { ImageBackground, SafeAreaView, View, Text } from 'react-native';
 import type { NavigationProp } from '@react-navigation/native';
 
-import Button from '../../components/Button';
+import { Button } from '../../components/';
 import { ButtonVariants } from '../../types/components';
 
 import { styles } from './style';
@@ -28,7 +28,9 @@ const WelcomeScreen: FC<Props> = ({ navigation }) => {
           <View style={styles.buttonsAndText}>
             <Text style={styles.text}>Endless exploring...</Text>
             <View style={{ gap: 10 }}>
-              <Button>Get started</Button>
+              <Button onPress={() => navigation.navigate(NAVIGATION_PAGES.REGISTER)}>
+                Get started
+              </Button>
               <Button
                 onPress={() => navigation.navigate(NAVIGATION_PAGES.LOGIN)}
                 variant={ButtonVariants.OPACITY}

+ 1 - 0
src/theme.ts

@@ -3,4 +3,5 @@ export enum Colors {
   DARK_BLUE = '#0F3F4F',
   WHITE = '#FFF',
   DARK_LIGHT = '#E5E5E5',
+  RED = '#EF5B5B',
 }

+ 1 - 0
src/types/navigation.ts

@@ -2,6 +2,7 @@ export enum NAVIGATION_PAGES {
   WELCOME = 'welcome',
   LOGIN = 'login',
   REGISTER = 'registration',
+  REGISTER_ACCOUNT_DATA = 'registrationAccountData',
   RESET_PASSWORD = 'resetPassword',
   RESET_PASSWORD_DEEP = 'resetPasswordDeep',
   IN_APP = 'inAppStack',