Browse Source

feat: check username + email | new query | profile page

Oleksandr Honcharov 1 year ago
parent
commit
15ccd99272

+ 4 - 1
package.json

@@ -14,6 +14,7 @@
     "@react-native-community/datetimepicker": "7.2.0",
     "@react-native-community/netinfo": "9.3.10",
     "@react-navigation/bottom-tabs": "^6.5.11",
+    "@react-navigation/material-top-tabs": "^6.6.5",
     "@react-navigation/native": "^6.1.9",
     "@react-navigation/native-stack": "^6.9.17",
     "@react-navigation/stack": "^6.3.20",
@@ -39,8 +40,10 @@
     "react-native-safe-area-context": "4.6.3",
     "react-native-screens": "~3.22.0",
     "react-native-svg": "13.9.0",
+    "react-native-tab-view": "^3.5.2",
     "yup": "^1.3.3",
-    "zustand": "^4.4.7"
+    "zustand": "^4.4.7",
+    "react-native-pager-view": "6.2.0"
   },
   "devDependencies": {
     "@babel/core": "^7.20.0",

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

@@ -31,6 +31,7 @@ export const InputDatePicker: FC<Props> = ({ selectedDate, formikError }) => {
         value={new Date(spinnerSelectedDate).toLocaleDateString()}
         isFocused={(b) => setVisible(b)}
         inputMode={'none'}
+        placeholder={'Choose a birth date'}
         formikError={formikError}
       />
       <Modal

+ 7 - 1
src/modules/auth/api/auth-api.ts

@@ -4,6 +4,7 @@ import { ResponseType } from '../response-type';
 
 import { UserRegistrationData } from './queries/use-post-register';
 import { UserResetPasswordData } from './queries/use-post-reset-password';
+import { JoinTestTypes } from './queries/use-post-join-test';
 
 export interface PostLoginUserReturn extends ResponseType {
   token: string;
@@ -17,6 +18,10 @@ export interface PostRegisterUserReturn extends ResponseType {
 
 export interface UserResetPasswordReturn extends ResponseType {}
 
+export interface UserJoinTestReturn extends ResponseType {
+  field: 'USERNAME USED' | 'EMAIL USED';
+}
+
 export const authApi = {
   loginUser: (data: { login: string; pass: string }) =>
     request.postForm<PostLoginUserReturn>(API.LOGIN, data),
@@ -33,5 +38,6 @@ export const authApi = {
     return request.postForm<PostRegisterUserReturn>(API.REGISTER, formData);
   },
   resetPassword: (data: UserResetPasswordData) =>
-    request.postForm<UserResetPasswordReturn>(API.RESET_PASSWORD, data)
+    request.postForm<UserResetPasswordReturn>(API.RESET_PASSWORD, data),
+  joinTest: (data: JoinTestTypes) => request.postForm<UserJoinTestReturn>(API.JOIN_TEST, data)
 };

+ 19 - 0
src/modules/auth/api/queries/use-post-join-test.tsx

@@ -0,0 +1,19 @@
+import { useMutation } from '@tanstack/react-query';
+import { authApi, UserJoinTestReturn } from '../auth-api';
+import { BaseAxiosError } from '../../../../types';
+import { authQueryKeys } from '../auth-query-keys';
+
+export type JoinTestTypes = {
+  username: string;
+  email: string;
+};
+
+export const useJoinTestMutation = () => {
+  return useMutation<UserJoinTestReturn, BaseAxiosError, JoinTestTypes, UserJoinTestReturn>({
+    mutationKey: authQueryKeys.loginUser(),
+    mutationFn: async (data) => {
+      const response = await authApi.joinTest(data);
+      return response.data;
+    }
+  });
+};

+ 1 - 1
src/modules/auth/regions/queries/use-post-get-regions.tsx

@@ -3,7 +3,7 @@ import { regionsApi, PostGetRegionsReturn } from '../regions-api';
 import { BaseAxiosError } from '../../../../types';
 import { regionQueryKeys } from '../regions-query-keys';
 
-export const usePostGetRegions = (enabled: boolean) => {
+export const useGetRegionsQuery = (enabled: boolean) => {
   return useQuery<PostGetRegionsReturn, BaseAxiosError>({
     queryKey: regionQueryKeys.getRegions(),
     queryFn: async () => {

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

@@ -1,12 +1,215 @@
-import React from 'react';
-import { View, Text } from 'react-native';
+import React, { FC, ReactNode } from 'react';
+import { Image, Text, View } from 'react-native';
+import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
+
+import { PageWrapper } from '../../../components';
+import { Colors } from '../../../theme';
+import { getFontSize } from '../../../utils';
+import { styles } from './styles';
+import { navigationOpts } from './navigation-opts';
+
+const regions = [
+  {
+    count: 28,
+    name: 'NM1301'
+  },
+  {
+    count: 1,
+    name: 'M@P'
+  },
+  {
+    count: 13,
+    name: 'UN'
+  },
+  {
+    count: 14,
+    name: 'UN+'
+  },
+  {
+    count: 16,
+    name: 'TCC'
+  },
+  {
+    count: 3,
+    name: 'WHS'
+  }
+];
+
+const Tab = createMaterialTopTabNavigator();
+
+// TODO: refactor + connect with API
 
 const ProfileScreen = () => {
   return (
-    <View>
-      <Text>Profile Screen</Text>
+    <PageWrapper>
+      <View style={styles.pageWrapper}>
+        <View>
+          <Image
+            width={64}
+            height={64}
+            style={{ borderRadius: 64 / 2 }}
+            source={{
+              uri: 'https://harrymitsidis.com/wp-content/uploads/2023/05/harrymitsidis-SaoTome.jpg'
+            }}
+          />
+        </View>
+        <View>
+          <Text style={[styles.headerText, { fontSize: getFontSize(18) }]}>Harry Mitsidis</Text>
+          <View style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 10 }}>
+            <Image
+              source={{
+                uri: 'https://flagpedia.net/data/flags/w580/gr.webp'
+              }}
+              style={styles.countryFlag}
+              width={20}
+              height={20}
+            />
+            <Image
+              source={{
+                uri: 'https://upload.wikimedia.org/wikipedia/commons/b/b6/Flag_of_Canada.png'
+              }}
+              style={[styles.countryFlag, { marginLeft: -15 }]}
+              width={20}
+              height={20}
+            />
+            <Text>Age: 40</Text>
+          </View>
+        </View>
+      </View>
+      <Tab.Navigator
+        screenOptions={{
+          ...navigationOpts,
+          tabBarLabel: ({ children, color, focused }) => (
+            <Text style={[styles.headerText, { color }]}>{children}</Text>
+          )
+        }}
+      >
+        <Tab.Screen name="Personal Info" component={PersonalInfo} />
+        <Tab.Screen name="Visited Regions" component={() => <Text>Visited Regions</Text>} />
+        <Tab.Screen name="Photos" component={() => <Text>Photos</Text>} />
+      </Tab.Navigator>
+    </PageWrapper>
+  );
+};
+
+const PersonalInfo = () => {
+  return (
+    <View style={{ marginTop: 20, gap: 20 }}>
+      <InfoItem inline={true} title={'Visited Regions'}>
+        {regions.map((data) => (
+          <View style={{ display: 'flex', flexDirection: 'column', gap: 5, alignItems: 'center' }}>
+            <Text
+              style={{
+                fontFamily: 'redhat-700',
+                color: Colors.DARK_BLUE,
+                fontSize: getFontSize(14)
+              }}
+            >
+              {data.count}
+            </Text>
+            <Text style={{ color: 'rgba(62, 100, 113, 1)', fontSize: getFontSize(12) }}>
+              {data.name}
+            </Text>
+          </View>
+        ))}
+      </InfoItem>
+      <View style={{ display: 'flex', flexDirection: 'row' }}>
+        <Text
+          style={{
+            flex: 1,
+            fontFamily: 'redhat-700',
+            color: Colors.DARK_BLUE,
+            fontSize: getFontSize(14)
+          }}
+        >
+          Date of birth
+        </Text>
+        <Text
+          style={{
+            flex: 1,
+            color: 'rgba(62, 100, 113, 1)',
+            fontWeight: '400',
+            fontSize: getFontSize(14)
+          }}
+        >
+          Jan 01, 1980
+        </Text>
+      </View>
+      <View style={{ display: 'flex', flexDirection: 'row' }}>
+        <Text
+          style={{
+            flex: 1,
+            fontFamily: 'redhat-700',
+            color: Colors.DARK_BLUE,
+            fontSize: getFontSize(14)
+          }}
+        >
+          Region of origin
+        </Text>
+        <Text
+          style={{
+            flex: 1,
+            color: 'rgba(62, 100, 113, 1)',
+            fontWeight: '400',
+            fontSize: getFontSize(14)
+          }}
+        >
+          Greece - Attica, Central and West (Athens, Lamia, Agrinio)
+        </Text>
+      </View>
+      <InfoItem title={'Bio'}>
+        <Text>
+          Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
+          commodo consequat.
+        </Text>
+      </InfoItem>
+      <InfoItem title={'Social links'}>
+        <View style={{ display: 'flex', flexDirection: 'row', gap: 10 }}>
+          <Image
+            width={20}
+            height={20}
+            source={{
+              uri: 'https://upload.wikimedia.org/wikipedia/commons/6/6c/Facebook_Logo_2023.png'
+            }}
+          />
+          <Image
+            width={20}
+            height={20}
+            source={{
+              uri: 'https://upload.wikimedia.org/wikipedia/commons/a/a5/Instagram_icon.png'
+            }}
+          />
+          <Image
+            width={20}
+            height={20}
+            source={{
+              uri: 'https://w7.pngwing.com/pngs/208/269/png-transparent-youtube-play-button-computer-icons-youtube-youtube-logo-angle-rectangle-logo-thumbnail.png'
+            }}
+          />
+        </View>
+      </InfoItem>
     </View>
   );
 };
 
+const InfoItem: FC<{ title: string; inline?: boolean; children: ReactNode }> = ({
+  title,
+  inline,
+  children
+}) => (
+  <View>
+    <Text style={styles.headerText}>{title}</Text>
+    <View
+      style={{
+        display: 'flex',
+        flexDirection: inline ? 'row' : 'column',
+        justifyContent: 'space-evenly',
+        marginTop: 10
+      }}
+    >
+      {children}
+    </View>
+  </View>
+);
+
 export default ProfileScreen;

+ 19 - 0
src/screens/InAppScreens/ProfileScreen/navigation-opts.ts

@@ -0,0 +1,19 @@
+import { Colors } from '../../../theme';
+import { Dimensions, PixelRatio } from 'react-native';
+
+const widthPercentageToDP = (widthPercent: number) => {
+  const screenWidth = Dimensions.get('window').width;
+  return PixelRatio.roundToNearestPixel((screenWidth * widthPercent) / 100);
+};
+
+export const navigationOpts = {
+  tabBarStyle: { backgroundColor: 'rgba(0,0,0,0)' },
+  tabBarIndicatorStyle: {
+    backgroundColor: Colors.DARK_BLUE,
+    width: widthPercentageToDP(23),
+    marginHorizontal: 15
+  },
+  tabBarInactiveTintColor: 'rgba(15, 63, 79, 0.4)',
+  tabBarActiveTintColor: Colors.DARK_BLUE,
+  tabBarItemStyle: { height: 40 }
+};

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

@@ -0,0 +1,23 @@
+import { StyleSheet } from 'react-native';
+import { Colors } from '../../../theme';
+import { getFontSize } from '../../../utils';
+
+export const styles = StyleSheet.create({
+  pageWrapper: {
+    display: 'flex',
+    flexDirection: 'row',
+    alignItems: 'center',
+    marginTop: 20,
+    gap: 20
+  },
+  headerText: {
+    fontFamily: 'redhat-700',
+    color: Colors.DARK_BLUE,
+    fontSize: getFontSize(14)
+  },
+  countryFlag: {
+    borderRadius: 20 / 2,
+    borderWidth: 0.5,
+    borderColor: 'gray'
+  }
+});

+ 24 - 5
src/screens/RegisterScreen/JoinUs/index.tsx

@@ -6,6 +6,7 @@ import * as yup from 'yup';
 
 import { PageWrapper, Header, Button, BigText, CheckBox, Input } from '../../../components';
 import { NAVIGATION_PAGES } from '../../../types';
+import { useJoinTestMutation } from '../../../modules/auth/api/queries/use-post-join-test';
 import store from '../../../storage/zustand';
 
 type Props = {
@@ -24,6 +25,8 @@ const JoinSchema = yup.object({
 });
 
 const JoinUsScreen: FC<Props> = ({ navigation }) => {
+  const { data, mutateAsync: JoinTest } = useJoinTestMutation();
+
   const [updateRegistrationUserData] = store((state) => [
     state.registration.updateRegistrationUserData
   ]);
@@ -44,10 +47,18 @@ const JoinUsScreen: FC<Props> = ({ navigation }) => {
             }}
             validationSchema={JoinSchema}
             onSubmit={(values) => {
-              navigation.navigate(NAVIGATION_PAGES.REGISTER_ACCOUNT_DATA);
-
               const { email, username, password } = values;
-              updateRegistrationUserData({ email, username, password });
+
+              JoinTest({
+                email,
+                username
+              }).then((data) => {
+                if (!data?.field && data?.result === 'OK') {
+                  navigation.navigate(NAVIGATION_PAGES.REGISTER_ACCOUNT_DATA);
+
+                  updateRegistrationUserData({ email, username, password });
+                }
+              });
             }}
           >
             {(props) => (
@@ -58,7 +69,11 @@ const JoinUsScreen: FC<Props> = ({ navigation }) => {
                   onBlur={props.handleBlur('username')}
                   placeholder={'Enter your username'}
                   header={'Username'}
-                  formikError={props.touched.username && props.errors.username}
+                  formikError={
+                    data?.field === 'USERNAME USED'
+                      ? data?.field
+                      : props.touched.username && props.errors.username
+                  }
                 />
                 <Input
                   onChange={props.handleChange('email')}
@@ -67,7 +82,11 @@ const JoinUsScreen: FC<Props> = ({ navigation }) => {
                   placeholder={'Enter your email'}
                   header={'Email address'}
                   inputMode={'email'}
-                  formikError={props.touched.email && props.errors.email}
+                  formikError={
+                    data?.field === 'EMAIL USED'
+                      ? data?.field
+                      : props.touched.email && props.errors.email
+                  }
                 />
                 <Input
                   onChange={props.handleChange('password')}

+ 4 - 2
src/types/api.ts

@@ -9,14 +9,16 @@ export enum API_ENDPOINT {
   LOGIN = 'login',
   REGISTER = 'join',
   RESET_PASSWORD = 'recover-password',
-  GET_REGIONS = 'get-regions'
+  GET_REGIONS = 'get-regions',
+  JOIN_TEST = 'pre-join-test'
 }
 
 export enum API {
   LOGIN = `${API_ROUTE.USER}/${API_ENDPOINT.LOGIN}`,
   REGISTER = `${API_ROUTE.USER}/${API_ENDPOINT.REGISTER}`,
   RESET_PASSWORD = `${API_ROUTE.USER}/${API_ENDPOINT.RESET_PASSWORD}`,
-  GET_REGIONS = `${API_ROUTE.REGIONS}/${API_ENDPOINT.GET_REGIONS}`
+  GET_REGIONS = `${API_ROUTE.REGIONS}/${API_ENDPOINT.GET_REGIONS}`,
+  JOIN_TEST = `${API_ROUTE.USER}/${API_ENDPOINT.JOIN_TEST}`
 }
 
 export type BaseAxiosError = AxiosError;