diff --git a/babel.config.js b/babel.config.js index 5396d80..161beda 100644 --- a/babel.config.js +++ b/babel.config.js @@ -15,7 +15,8 @@ module.exports = { '@config': './src/config', '@utils': './src/utils', '@styles': './src/styles', - '@context': './src/context' + '@context': './src/context', + '@interfaces': './src/interfaces' } } ] diff --git a/package-lock.json b/package-lock.json index 61f5853..f2a5b98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1276,12 +1276,171 @@ "@types/istanbul-lib-report": "*" } }, + "@types/jest": { + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-25.2.1.tgz", + "integrity": "sha512-msra1bCaAeEdkSyA0CZ6gW1ukMIvZ5YoJkdXw/qhQdsuuDlFTcEUrUw8CLCPt2rVRUfXlClVvK2gvPs9IokZaA==", + "dev": true, + "requires": { + "jest-diff": "^25.2.1", + "pretty-format": "^25.2.1" + }, + "dependencies": { + "@jest/types": { + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.6.tgz", + "integrity": "sha512-myJTTV37bxK7+3NgKc4Y/DlQ5q92/NOwZsZ+Uch7OXdElxOg61QYc72fPYNAjlvbnJ2YvbXLamIsa9tj48BmyQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + } + }, + "@types/yargs": { + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", + "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "diff-sequences": { + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.6.tgz", + "integrity": "sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-diff": { + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.2.6.tgz", + "integrity": "sha512-KuadXImtRghTFga+/adnNrv9s61HudRMR7gVSbP35UKZdn4IK2/0N0PpGZIqtmllK9aUyye54I3nu28OYSnqOg==", + "dev": true, + "requires": { + "chalk": "^3.0.0", + "diff-sequences": "^25.2.6", + "jest-get-type": "^25.2.6", + "pretty-format": "^25.2.6" + } + }, + "jest-get-type": { + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", + "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==", + "dev": true + }, + "pretty-format": { + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.2.6.tgz", + "integrity": "sha512-DEiWxLBaCHneffrIT4B+TpMvkV9RNvvJrd3lY9ew1CEQobDzEXmYT1mg0hJhljZty7kCc10z13ohOFAE8jrUDg==", + "dev": true, + "requires": { + "@jest/types": "^25.2.6", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "@types/json-schema": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", "dev": true }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", + "dev": true + }, + "@types/react": { + "version": "16.9.32", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.32.tgz", + "integrity": "sha512-fmejdp0CTH00mOJmxUPPbWCEBWPvRIL4m8r0qD+BSDUqmutPyGQCHifzMpMzdvZwROdEdL78IuZItntFWgPXHQ==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "csstype": "^2.2.0" + } + }, + "@types/react-native": { + "version": "0.62.1", + "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.62.1.tgz", + "integrity": "sha512-4uKqYGek9/OstJWW0hEfF/6fx8BzNkzdAFxeY+VOxec+SPKK/hvGEjnX6+0jV1MnJDBHYKdWfWA4sYCQ8G3sqA==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/react-test-renderer": { + "version": "16.9.2", + "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-16.9.2.tgz", + "integrity": "sha512-4eJr1JFLIAlWhzDkBCkhrOIWOvOxcCAfQh+jiKg7l/nNZcCIL2MHl2dZhogIFKyHzedVWHaVP1Yydq/Ruu4agw==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -2466,6 +2625,12 @@ "cssom": "0.3.x" } }, + "csstype": { + "version": "2.6.10", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.10.tgz", + "integrity": "sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w==", + "dev": true + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -3500,6 +3665,76 @@ } } }, + "eslint-import-resolver-babel-module": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-babel-module/-/eslint-import-resolver-babel-module-5.1.2.tgz", + "integrity": "sha512-pzKE6UzXgT1Opp4N2P2yoE7OY29+LX3FNX6bqAjmGV+btR/ZYnE/oQFoGzL2/3RkLYRTBYRFwvrphRy5wEAUwg==", + "dev": true, + "requires": { + "pkg-up": "^2.0.0", + "resolve": "^1.10.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + } + } + }, "eslint-import-resolver-node": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz", diff --git a/package.json b/package.json index f6dfa98..d3d56ac 100644 --- a/package.json +++ b/package.json @@ -29,11 +29,16 @@ "@babel/core": "^7.6.2", "@babel/runtime": "^7.6.2", "@react-native-community/eslint-config": "^0.0.5", + "@types/jest": "^25.2.1", + "@types/react": "^16.9.32", + "@types/react-native": "^0.62.1", + "@types/react-test-renderer": "^16.9.2", "babel-jest": "^24.9.0", "babel-plugin-module-resolver": "^4.0.0", "eslint": "^6.5.1", "eslint-config-prettier": "^6.10.0", "eslint-config-wolox-react-native": "^1.0.1", + "eslint-import-resolver-babel-module": "^5.1.2", "eslint-plugin-import": "^2.20.1", "eslint-plugin-prettier": "^3.1.2", "install": "^0.13.0", @@ -41,7 +46,8 @@ "metro-react-native-babel-preset": "^0.56.0", "npm": "^6.14.2", "prettier": "^1.19.1", - "react-test-renderer": "16.9.0" + "react-test-renderer": "16.9.0", + "typescript": "^3.8.3" }, "jest": { "preset": "react-native" diff --git a/src/app/components/Header/components/BackButton/index.js b/src/app/components/Header/components/BackButton/index.js deleted file mode 100644 index 4536bad..0000000 --- a/src/app/components/Header/components/BackButton/index.js +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import { func, number } from 'prop-types'; -import { TouchableOpacity, Image } from 'react-native'; - -import defaultBackBtn from '@assets/ic_back.png'; - -import styles from './styles'; - -function BackButton({ onPress, backButton }) { - return ( - - - - ); -} - -BackButton.propTypes = { - onPress: func.isRequired, - backButton: number -}; - -export default BackButton; diff --git a/src/app/components/Header/components/BackButton/index.tsx b/src/app/components/Header/components/BackButton/index.tsx new file mode 100644 index 0000000..fec4bf7 --- /dev/null +++ b/src/app/components/Header/components/BackButton/index.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { TouchableOpacity, Image } from 'react-native'; + +import styles from './styles'; + +interface Props { + onPress: () => void; + backButton: number; +} + +function BackButton({ onPress, backButton }: Props) { + return ( + + + + ); +} + +export default BackButton; diff --git a/src/app/components/Header/components/BackButton/styles.js b/src/app/components/Header/components/BackButton/styles.ts similarity index 100% rename from src/app/components/Header/components/BackButton/styles.js rename to src/app/components/Header/components/BackButton/styles.ts diff --git a/src/app/components/Header/components/LogoutButton/index.js b/src/app/components/Header/components/LogoutButton/index.tsx similarity index 59% rename from src/app/components/Header/components/LogoutButton/index.js rename to src/app/components/Header/components/LogoutButton/index.tsx index 02ad602..6610d82 100644 --- a/src/app/components/Header/components/LogoutButton/index.js +++ b/src/app/components/Header/components/LogoutButton/index.tsx @@ -1,17 +1,17 @@ import React, { useState } from 'react'; -import { Image, ActivityIndicator, TouchableWithoutFeedback, View } from 'react-native'; +import { Image, ActivityIndicator, TouchableOpacity, View } from 'react-native'; import AsyncStorage from '@react-native-community/async-storage'; -import { useGlobalValue } from '@context'; +import { useGlobalValue } from '@context/index'; import { actionsCreator } from '@context/user/actions'; import logout from '@assets/logout.png'; -import { WHITE } from '@constants/colors'; +import COLORS from '@constants/colors'; import styles from './styles'; function LogoutButton() { const [, dispatch] = useGlobalValue(); - const [loading, setLoading] = useState(); + const [loading, setLoading] = useState(); const handleLogoutBtn = async () => { setLoading(true); try { @@ -24,11 +24,11 @@ function LogoutButton() { }; return ( - + - {loading ? : } + {loading ? : } - + ); } diff --git a/src/app/components/Header/components/LogoutButton/styles.js b/src/app/components/Header/components/LogoutButton/styles.ts similarity index 100% rename from src/app/components/Header/components/LogoutButton/styles.js rename to src/app/components/Header/components/LogoutButton/styles.ts diff --git a/src/app/components/Header/index.js b/src/app/components/Header/index.js deleted file mode 100644 index ac33f42..0000000 --- a/src/app/components/Header/index.js +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import { Text, ImageBackground } from 'react-native'; - -import navBg from '@assets/bc_navbar.png'; - -import BackButton from './components/BackButton'; -import LogoutButton from './components/LogoutButton'; -import styles from './styles'; - -function Header({ scene, navigation }) { - const { - options: { - backButton, - title, - headerStyle: { height }, - hasBackBtn = false, - hasLogoutBtn = false - } - } = scene.descriptor; - return ( - - {hasBackBtn && } - {hasLogoutBtn && } - {title} - - ); -} - -export default Header; diff --git a/src/app/components/Header/index.tsx b/src/app/components/Header/index.tsx new file mode 100644 index 0000000..a224d74 --- /dev/null +++ b/src/app/components/Header/index.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { Text, ImageBackground, View } from 'react-native'; + +import navBg from '@assets/bc_navbar.png'; +import defaultBackBtn from '@assets/ic_back.png'; + +import BackButton from './components/BackButton'; +import LogoutButton from './components/LogoutButton'; +import styles from './styles'; + +interface Props { + scene: { + descriptor: { options: { title?: string; headerStyle?: { height: number }; hasBackBtn?: boolean } }; + }; + navigation: { goBack(): void }; +} + +interface CustomProps { + backButton?: number; + hasLogoutBtn?: boolean; +} + +const withCustomHeader = ({ backButton, hasLogoutBtn = false }: CustomProps) => + function PropsProxy({ scene, navigation }: Props) { + const { + options: { title, headerStyle } + } = scene.descriptor; + const { height } = headerStyle!; + + return ( + + + {hasLogoutBtn ? ( + + ) : ( + + )} + + {title} + + ); + }; + +export default withCustomHeader; diff --git a/src/app/components/Header/styles.js b/src/app/components/Header/styles.ts similarity index 67% rename from src/app/components/Header/styles.js rename to src/app/components/Header/styles.ts index 64579bd..b5d042c 100644 --- a/src/app/components/Header/styles.js +++ b/src/app/components/Header/styles.ts @@ -1,6 +1,10 @@ import { StyleSheet, Platform } from 'react-native'; -import { WHITE } from '@constants/colors'; +import COLORS from '@constants/colors'; + +type OS = 'ios' | 'android'; + +const system = Platform.OS as OS; const OS_STYLES = { headerTitle: { @@ -14,7 +18,7 @@ const OS_STYLES = { textAlign: 'left' } } -}; +} as const; export const styles = StyleSheet.create({ headerBg: { @@ -24,10 +28,14 @@ export const styles = StyleSheet.create({ position: 'relative' }, headerTitle: { - ...OS_STYLES[Platform.OS], - color: WHITE, + ...OS_STYLES.headerTitle[system], + color: COLORS.WHITE, fontSize: 16, fontWeight: '700' + }, + headerButton: { + position: 'relative', + zIndex: 5 } }); diff --git a/src/app/components/Loader/constants.js b/src/app/components/Loader/constants.js deleted file mode 100644 index b96e262..0000000 --- a/src/app/components/Loader/constants.js +++ /dev/null @@ -1,4 +0,0 @@ -export const LOADER_SIZE = { - large: 'large', - small: 'small' -}; diff --git a/src/app/components/Loader/constants.ts b/src/app/components/Loader/constants.ts new file mode 100644 index 0000000..ed8346e --- /dev/null +++ b/src/app/components/Loader/constants.ts @@ -0,0 +1,4 @@ +export enum LOADER_SIZE { + large = 'large', + small = 'small' +} diff --git a/src/app/components/Loader/index.js b/src/app/components/Loader/index.tsx similarity index 53% rename from src/app/components/Loader/index.js rename to src/app/components/Loader/index.tsx index 24b4b19..17f3799 100644 --- a/src/app/components/Loader/index.js +++ b/src/app/components/Loader/index.tsx @@ -1,23 +1,29 @@ import React from 'react'; import { View, ActivityIndicator } from 'react-native'; -import { BLUE, TRANSPARENT } from '@constants/colors'; +import COLORS from '@constants/colors'; import styles from './styles'; import { LOADER_SIZE } from './constants'; +interface Props { + isLoading: boolean; +} + +type FunctionComponent = (param: T) => JSX.Element; + const withLoader = ({ - loaderColor = BLUE, + loaderColor = COLORS.BLUE, loaderSize = LOADER_SIZE.large, - bgColor = TRANSPARENT -}) => WrappedComponent => - function PropsProxy({ isLoading, ...props }) { + bgColor = COLORS.TRANSPARENT +}) => (WrappedComponent: FunctionComponent) => + function PropsProxy({ isLoading, ...props }: U) { return isLoading ? ( ) : ( - + ); }; diff --git a/src/app/components/Loader/styles.js b/src/app/components/Loader/styles.ts similarity index 100% rename from src/app/components/Loader/styles.js rename to src/app/components/Loader/styles.ts diff --git a/src/app/components/Routes/constants.js b/src/app/components/Routes/constants.ts similarity index 67% rename from src/app/components/Routes/constants.js rename to src/app/components/Routes/constants.ts index 7575cbf..ff861b3 100644 --- a/src/app/components/Routes/constants.js +++ b/src/app/components/Routes/constants.ts @@ -1,7 +1,7 @@ -import Header from '@components/Header'; +import withCustomHeader from '@components/Header'; export const APP_OPTIONS = { - header: Header, + header: withCustomHeader({}), headerMode: 'float', headerTransparent: true, headerStyle: { height: 114 } diff --git a/src/app/components/Routes/index.js b/src/app/components/Routes/index.tsx similarity index 70% rename from src/app/components/Routes/index.js rename to src/app/components/Routes/index.tsx index d3624c0..fb7bb26 100644 --- a/src/app/components/Routes/index.js +++ b/src/app/components/Routes/index.tsx @@ -1,18 +1,24 @@ import React, { useEffect } from 'react'; -import { useGlobalValue } from '@context'; +import { useGlobalValue } from '@context/index'; +import { useRequest } from '@hooks/index'; +import { AuthHeaders } from '@interfaces/api'; import { actionsCreator } from '@context/user/actions'; -import { useRequest } from '@hooks'; import { getAuthHeaders } from '@services/UserService'; import api from '@config/api'; import AppRoutes from './layout'; +interface Response { + data: AuthHeaders; +} + function RoutesContainer() { - const { data: response, loading } = useRequest(getAuthHeaders); + const { data: response, loading } = useRequest(getAuthHeaders); const [{ userState }, dispatch] = useGlobalValue(); const headers = response?.data; const token = userState?.token; + useEffect(() => { if (headers) { api.setHeaders(headers); diff --git a/src/app/components/Routes/layout.js b/src/app/components/Routes/layout.tsx similarity index 68% rename from src/app/components/Routes/layout.js rename to src/app/components/Routes/layout.tsx index f20125d..96ed5d7 100644 --- a/src/app/components/Routes/layout.js +++ b/src/app/components/Routes/layout.tsx @@ -1,17 +1,21 @@ import React from 'react'; -import { bool, string } from 'prop-types'; import { NavigationContainer } from '@react-navigation/native'; import { createStackNavigator } from '@react-navigation/stack'; -import { Library, BookDetail, Login } from '@screens'; +import { Library, BookDetail, Login } from '@screens/index'; import withLoader from '@components/Loader'; +import withCustomHeader from '@components/Header'; import * as Routes from '@constants/routes'; import { APP_OPTIONS, LOGIN_OPTIONS } from './constants'; +interface Props { + token: string | null; +} + const { Navigator, Screen } = createStackNavigator(); -function AppRoutes({ token }) { +function AppRoutes({ token }: Props) { const screenOptions = token ? APP_OPTIONS : LOGIN_OPTIONS; return ( @@ -21,12 +25,15 @@ function AppRoutes({ token }) { ) : ( @@ -37,9 +44,4 @@ function AppRoutes({ token }) { ); } -AppRoutes.propTypes = { - isLoading: bool, - token: string -}; - export default withLoader({})(AppRoutes); diff --git a/src/app/constants.js b/src/app/constants.js deleted file mode 100644 index da90ce0..0000000 --- a/src/app/constants.js +++ /dev/null @@ -1,4 +0,0 @@ -export const HEADER_MODE = { - ios: 'float', - android: 'screen' -}; diff --git a/src/app/constants.ts b/src/app/constants.ts new file mode 100644 index 0000000..71ff51c --- /dev/null +++ b/src/app/constants.ts @@ -0,0 +1,4 @@ +export enum HEADER_MODE { + ios = 'float', + android = 'screen' +} diff --git a/src/app/index.js b/src/app/index.tsx similarity index 96% rename from src/app/index.js rename to src/app/index.tsx index 4d8bd9c..96b3f83 100644 --- a/src/app/index.js +++ b/src/app/index.tsx @@ -2,7 +2,7 @@ import 'react-native-gesture-handler'; import React, { useReducer } from 'react'; import Routes from '@components/Routes'; -import { GlobalContext, globalState, globalReducer } from '@context'; +import { GlobalContext, globalState, globalReducer } from '@context/index'; function App() { return ( diff --git a/src/app/screens/BookDetail/index.js b/src/app/screens/BookDetail/index.js deleted file mode 100644 index 14e011a..0000000 --- a/src/app/screens/BookDetail/index.js +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; -import { shape, number } from 'prop-types'; - -import { useRequest } from '@hooks'; -import { serializeBook } from '@utils/serializers'; -import { getBook } from '@services/BookService'; -import { DEFAULT_BOOK } from '@constants/book'; - -import BookDetail from './layout'; - -function BookDetailContainer({ route }) { - const { id } = route.params; - const { data: response, loading } = useRequest(getBook, { id }); - const { image, title, author, year, genre } = serializeBook(response?.data) || DEFAULT_BOOK; - return ( - - ); -} - -BookDetailContainer.propTypes = { - route: shape({ - params: shape({ - id: number - }) - }) -}; - -export default BookDetailContainer; diff --git a/src/app/screens/BookDetail/index.tsx b/src/app/screens/BookDetail/index.tsx new file mode 100644 index 0000000..c53326b --- /dev/null +++ b/src/app/screens/BookDetail/index.tsx @@ -0,0 +1,27 @@ +import React from 'react'; + +import { useRequest } from '@hooks/index'; +import { getBook } from '@services/BookService'; +import { Book } from '@interfaces/book'; +import { DEFAULT_BOOK } from '@constants/book'; + +import BookDetail from './layout'; + +interface Props { + route: { params: { id: number } }; +} + +interface Response { + data: Book; +} + +function BookDetailContainer({ route }: Props) { + const { id } = route.params; + const { data: response, loading } = useRequest(getBook, { id }); + const { image, title, author, year, genre } = response?.data || DEFAULT_BOOK; + return ( + + ); +} + +export default BookDetailContainer; diff --git a/src/app/screens/BookDetail/layout.js b/src/app/screens/BookDetail/layout.tsx similarity index 81% rename from src/app/screens/BookDetail/layout.js rename to src/app/screens/BookDetail/layout.tsx index 54eb4b4..f18973b 100644 --- a/src/app/screens/BookDetail/layout.js +++ b/src/app/screens/BookDetail/layout.tsx @@ -1,23 +1,30 @@ import React from 'react'; -import { string } from 'prop-types'; import { Text, TouchableOpacity, View, Image } from 'react-native'; import LinearGradient from 'react-native-linear-gradient'; import { useHeaderHeight } from '@react-navigation/stack'; import withLoader from '@components/Loader'; -import { CERULEAN, JAVA, SCOOTER, POLAR } from '@constants/colors'; +import COLORS from '@constants/colors'; import { DEFAULT_BOOK } from '@constants/book'; import styles from './styles'; -function BookDetail({ title, author, year, genre, image: { url } }) { +interface Props { + title: string; + author: string; + year: string; + genre: string; + image: { url: string }; +} + +function BookDetail({ title, author, year, genre, image: { url } }: Props) { const headerHeight = useHeaderHeight(); const padding = styles.bookDetail.paddingTop; return ( - + {title} @@ -34,7 +41,7 @@ function BookDetail({ title, author, year, genre, image: { url } }) { Rent @@ -44,12 +51,4 @@ function BookDetail({ title, author, year, genre, image: { url } }) { ); } -BookDetail.propTypes = { - title: string, - author: string, - year: string, - genre: string, - imageUrl: string -}; - -export default withLoader({ bgColor: POLAR })(BookDetail); +export default withLoader({ bgColor: COLORS.POLAR })(BookDetail); diff --git a/src/app/screens/BookDetail/styles.js b/src/app/screens/BookDetail/styles.ts similarity index 76% rename from src/app/screens/BookDetail/styles.js rename to src/app/screens/BookDetail/styles.ts index dd928dd..a492f5e 100644 --- a/src/app/screens/BookDetail/styles.js +++ b/src/app/screens/BookDetail/styles.ts @@ -1,12 +1,12 @@ import { StyleSheet } from 'react-native'; -import { WHITE, POLAR, GRAY, CERULEAN, TUNDORA, TRANSPARENT } from '@constants/colors'; +import COLORS from '@constants/colors'; import { BOX_SHADOW } from '@styles/shadows'; const COMMON_STYLES = { button: { alignItems: 'center', - borderColor: CERULEAN, + borderColor: COLORS.CERULEAN, borderRadius: 100, borderStyle: 'solid', borderWidth: 2, @@ -19,11 +19,11 @@ const COMMON_STYLES = { fontWeight: '900', textTransform: 'uppercase' } -}; +} as const; export const styles = StyleSheet.create({ bookDetail: { - backgroundColor: POLAR, + backgroundColor: COLORS.POLAR, flex: 1, flexDirection: 'column', paddingHorizontal: 20, @@ -31,7 +31,7 @@ export const styles = StyleSheet.create({ }, bookContainer: { ...BOX_SHADOW.type1, - backgroundColor: WHITE, + backgroundColor: COLORS.WHITE, borderRadius: 4, flexDirection: 'column', paddingHorizontal: 30, @@ -46,38 +46,38 @@ export const styles = StyleSheet.create({ flexDirection: 'column' }, bookCover: { - backgroundColor: GRAY, + backgroundColor: COLORS.GRAY, height: 105, marginRight: 20, width: 70 }, bookTitle: { - color: TUNDORA, + color: COLORS.TUNDORA, fontSize: 22, fontWeight: '700', marginBottom: 30 }, bookProp: { - color: GRAY, + color: COLORS.GRAY, fontWeight: '600' }, buttonReturn: { ...COMMON_STYLES.button, - backgroundColor: CERULEAN, - borderColor: TRANSPARENT + backgroundColor: COLORS.CERULEAN, + borderColor: COLORS.TRANSPARENT }, buttonComments: { ...COMMON_STYLES.button, - backgroundColor: WHITE, + backgroundColor: COLORS.WHITE, marginBottom: 12 }, buttonReturnTitle: { ...COMMON_STYLES.buttonTitle, - color: WHITE + color: COLORS.WHITE }, buttonCommentsTitle: { ...COMMON_STYLES.buttonTitle, - color: CERULEAN + color: COLORS.CERULEAN } }); diff --git a/src/app/screens/Library/components/Book/index.js b/src/app/screens/Library/components/Book/index.tsx similarity index 79% rename from src/app/screens/Library/components/Book/index.js rename to src/app/screens/Library/components/Book/index.tsx index 339063e..fe3f069 100644 --- a/src/app/screens/Library/components/Book/index.js +++ b/src/app/screens/Library/components/Book/index.tsx @@ -1,6 +1,4 @@ import React from 'react'; -import { string, number } from 'prop-types'; - import { View, Image, Text, TouchableOpacity } from 'react-native'; import { useNavigation } from '@react-navigation/native'; @@ -9,13 +7,20 @@ import { DEFAULT_BOOK } from '@constants/book'; import styles from './styles'; -function Book({ id, title, author, image: { url } }) { +interface Props { + id: number; + title: string; + author: string; + image: { url: string }; +} + +function Book({ id, title, author, image: { url } }: Props) { const navigation = useNavigation(); const handleClick = () => navigation.navigate(Routes.BOOK_DETAIL.NAME, { id }); return ( - + {title} @@ -26,11 +31,4 @@ function Book({ id, title, author, image: { url } }) { ); } -Book.propTypes = { - id: number, - title: string, - author: string, - url: string -}; - export default Book; diff --git a/src/app/screens/Library/components/Book/styles.js b/src/app/screens/Library/components/Book/styles.ts similarity index 79% rename from src/app/screens/Library/components/Book/styles.js rename to src/app/screens/Library/components/Book/styles.ts index dd7b594..490a3bb 100644 --- a/src/app/screens/Library/components/Book/styles.js +++ b/src/app/screens/Library/components/Book/styles.ts @@ -1,13 +1,13 @@ import { StyleSheet } from 'react-native'; -import { SCORPION, WHITE, GRAY } from '@constants/colors'; +import COLORS from '@constants/colors'; import { BOX_SHADOW } from '@styles/shadows'; export default StyleSheet.create({ bookContainer: { ...BOX_SHADOW.type1, - backgroundColor: WHITE, + backgroundColor: COLORS.WHITE, borderRadius: 5, flexDirection: 'row', marginBottom: 10, @@ -15,7 +15,7 @@ export default StyleSheet.create({ paddingVertical: 20 }, bookCover: { - backgroundColor: GRAY, + backgroundColor: COLORS.GRAY, height: 75, marginRight: 20, width: 50 @@ -29,6 +29,6 @@ export default StyleSheet.create({ marginBottom: 5 }, bookAuthor: { - color: SCORPION + color: COLORS.SCORPION } }); diff --git a/src/app/screens/Library/index.js b/src/app/screens/Library/index.js deleted file mode 100644 index ed66bb9..0000000 --- a/src/app/screens/Library/index.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; - -import { getBooksList } from '@services/BookService'; -import { useRequest } from '@hooks'; -import { serializeBooks } from '@utils/serializers'; - -import Library from './layout'; - -function LibraryContainer() { - const { data: response, loading } = useRequest(getBooksList); - const books = serializeBooks(response?.data) || []; - return ; -} - -export default LibraryContainer; diff --git a/src/app/screens/Library/index.tsx b/src/app/screens/Library/index.tsx new file mode 100644 index 0000000..e8840f6 --- /dev/null +++ b/src/app/screens/Library/index.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +import { getBooksList } from '@services/BookService'; +import { useRequest } from '@hooks/index'; +import { Book } from '@interfaces/book'; + +import Library from './layout'; + +interface Response { + data: Book[]; +} + +function LibraryContainer() { + const { data: response, loading } = useRequest(getBooksList); + console.log(response); + const books = response?.data || []; + return ; +} + +export default LibraryContainer; diff --git a/src/app/screens/Library/layout.js b/src/app/screens/Library/layout.tsx similarity index 74% rename from src/app/screens/Library/layout.js rename to src/app/screens/Library/layout.tsx index 647682b..3ee5f99 100644 --- a/src/app/screens/Library/layout.js +++ b/src/app/screens/Library/layout.tsx @@ -1,16 +1,20 @@ import React from 'react'; -import { array } from 'prop-types'; import { FlatList } from 'react-native'; import { useHeaderHeight } from '@react-navigation/stack'; import withLoader from '@components/Loader'; import { keyExtractor } from '@utils/renders'; -import { POLAR } from '@constants/colors'; +import COLORS from '@constants/colors'; +import { Book } from '@interfaces/book'; import { renderListItem } from './utils'; import styles from './styles'; -function Library({ books }) { +interface Props { + books: Book[]; +} + +function Library({ books }: Props) { const headerHeight = useHeaderHeight(); const padding = styles.bookList.paddingTop; @@ -25,8 +29,4 @@ function Library({ books }) { ); } -Library.propTypes = { - books: array -}; - -export default withLoader({ bgColor: POLAR })(Library); +export default withLoader({ bgColor: COLORS.POLAR })(Library); diff --git a/src/app/screens/Library/styles.js b/src/app/screens/Library/styles.ts similarity index 73% rename from src/app/screens/Library/styles.js rename to src/app/screens/Library/styles.ts index fe36357..2fa7495 100644 --- a/src/app/screens/Library/styles.js +++ b/src/app/screens/Library/styles.ts @@ -1,10 +1,10 @@ import { StyleSheet } from 'react-native'; -import { POLAR } from '@constants/colors'; +import COLORS from '@constants/colors'; export default StyleSheet.create({ bookList: { - backgroundColor: POLAR, + backgroundColor: COLORS.POLAR, flex: 1, paddingHorizontal: 40, paddingTop: 10 diff --git a/src/app/screens/Library/utils.js b/src/app/screens/Library/utils.js deleted file mode 100644 index 479a963..0000000 --- a/src/app/screens/Library/utils.js +++ /dev/null @@ -1,5 +0,0 @@ -import React from 'react'; - -import Book from './components/Book'; - -export const renderListItem = ({ item }) => ; diff --git a/src/app/screens/Library/utils.tsx b/src/app/screens/Library/utils.tsx new file mode 100644 index 0000000..d1c69f7 --- /dev/null +++ b/src/app/screens/Library/utils.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { Book as BookType } from '@interfaces/book'; + +import Book from './components/Book'; + +interface ItemProps { + item: BookType; +} + +export const renderListItem = ({ item }: ItemProps) => ; diff --git a/src/app/screens/Login/constants.js b/src/app/screens/Login/constants.ts similarity index 100% rename from src/app/screens/Login/constants.js rename to src/app/screens/Login/constants.ts diff --git a/src/app/screens/Login/index.js b/src/app/screens/Login/index.tsx similarity index 71% rename from src/app/screens/Login/index.js rename to src/app/screens/Login/index.tsx index 5f2703e..4ed5b2b 100644 --- a/src/app/screens/Login/index.js +++ b/src/app/screens/Login/index.tsx @@ -9,30 +9,35 @@ import { View } from 'react-native'; -import { useGlobalValue } from '@context'; +import { useGlobalValue } from '@context/index'; import { actionsCreator } from '@context/user/actions'; -import { useLazyRequest } from '@hooks'; +import { useLazyRequest } from '@hooks/index'; import { authUser, setAuthHeaders } from '@services/UserService'; import LoginBg from '@assets/bc_inicio.png'; import LoginLogo from '@assets/Group.png'; import api from '@config/api'; -import { WHITE } from '@constants/colors'; +import COLORS from '@constants/colors'; +import { AuthHeaders } from '@interfaces/api'; import styles from './styles'; import { DEFAULT_ERROR_MSG } from './constants'; +interface Response { + headers: AuthHeaders; +} + function Login() { - const [email, setEmail] = useState(); - const [password, setPassword] = useState(); - const [error, setError] = useState(); - const [auth, { data: response, error: requestError, loading }] = useLazyRequest(authUser); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [error, setError] = useState(''); + const [auth, { data, error: requestError, loading }] = useLazyRequest(authUser); const [, dispatch] = useGlobalValue(); useEffect(() => { - if (response) { + if (data) { (async () => { try { - const { 'access-token': userToken, uid, client } = response?.headers; + const { 'access-token': userToken, uid, client } = data?.headers; const headers = { 'access-token': userToken, uid, client }; await setAuthHeaders(headers); api.setHeaders(headers); @@ -44,10 +49,10 @@ function Login() { } else if (requestError) { setError(DEFAULT_ERROR_MSG); } - }, [response, requestError, dispatch]); + }, [data, requestError, dispatch]); - const handleEmail = value => setEmail(value); - const handlePassword = value => setPassword(value); + const handleEmail = (value: string) => setEmail(value); + const handlePassword = (value: string) => setPassword(value); const handleSubmit = () => auth({ email, password }); @@ -64,7 +69,7 @@ function Login() { {loading ? ( - + ) : ( Log In )} diff --git a/src/app/screens/Login/styles.js b/src/app/screens/Login/styles.ts similarity index 85% rename from src/app/screens/Login/styles.js rename to src/app/screens/Login/styles.ts index 56abf03..ea62f31 100644 --- a/src/app/screens/Login/styles.js +++ b/src/app/screens/Login/styles.ts @@ -1,6 +1,6 @@ import { StyleSheet } from 'react-native'; -import { WHITE, RED } from '@constants/colors'; +import COLORS from '@constants/colors'; const COMMON_STYLES = { button: { @@ -26,24 +26,24 @@ export const styles = StyleSheet.create({ }, loginInput: { ...COMMON_STYLES.button, - backgroundColor: WHITE, + backgroundColor: COLORS.WHITE, padding: 10 }, loginSubmit: { ...COMMON_STYLES.button, alignItems: 'center', - borderColor: WHITE, + borderColor: COLORS.WHITE, borderStyle: 'solid', borderWidth: 1, justifyContent: 'center' }, loginSubmitText: { - color: WHITE, + color: COLORS.WHITE, fontWeight: '700', textTransform: 'uppercase' }, loginError: { - color: RED, + color: COLORS.RED, height: 20 }, loginFooter: { diff --git a/src/app/screens/index.js b/src/app/screens/index.ts similarity index 100% rename from src/app/screens/index.js rename to src/app/screens/index.ts diff --git a/src/config/api.js b/src/config/api.ts similarity index 100% rename from src/config/api.js rename to src/config/api.ts diff --git a/src/constants/book.js b/src/constants/book.ts similarity index 56% rename from src/constants/book.js rename to src/constants/book.ts index 600eabd..f33eb12 100644 --- a/src/constants/book.js +++ b/src/constants/book.ts @@ -1,7 +1,7 @@ export const DEFAULT_BOOK = { - image: 'http://wolox-training.s3.amazonaws.com/uploads/6942334-M.jpg', + image: { url: 'http://wolox-training.s3.amazonaws.com/uploads/6942334-M.jpg' }, title: 'Ring of Bright Water', author: 'Emme Thief', year: '1968', genre: 'terror' -}; +} as const; diff --git a/src/constants/colors.js b/src/constants/colors.js deleted file mode 100644 index cf78063..0000000 --- a/src/constants/colors.js +++ /dev/null @@ -1,12 +0,0 @@ -export const POLAR = '#E9F6FA'; -export const WHITE = '#FFF'; -export const BLACK = '#000'; -export const SCORPION = '#5B5B5B'; -export const GRAY = '#8D8D8D'; -export const CERULEAN = '#04ACEC'; -export const TUNDORA = '#4A4A4A'; -export const JAVA = '#16B3D9'; -export const SCOOTER = '#32C4C6'; -export const TRANSPARENT = 'transparent'; -export const RED = '#FF0000'; -export const BLUE = '#0000FF'; diff --git a/src/constants/colors.ts b/src/constants/colors.ts new file mode 100644 index 0000000..affa064 --- /dev/null +++ b/src/constants/colors.ts @@ -0,0 +1,16 @@ +enum COLORS { + POLAR = '#E9F6FA', + WHITE = '#FFF', + BLACK = '#000', + SCORPION = '#5B5B5B', + GRAY = '#8D8D8D', + CERULEAN = '#04ACEC', + TUNDORA = '#4A4A4A', + JAVA = '#16B3D9', + SCOOTER = '#32C4C6', + TRANSPARENT = 'transparent', + RED = '#FF0000', + BLUE = '#0000FF' +} + +export default COLORS; diff --git a/src/constants/routes.js b/src/constants/routes.js deleted file mode 100644 index 5e2e35c..0000000 --- a/src/constants/routes.js +++ /dev/null @@ -1,14 +0,0 @@ -export const LIBRARY = { - NAME: 'Library', - TITLE: 'LIBRARY' -}; - -export const BOOK_DETAIL = { - NAME: 'BookDetail', - TITLE: 'BOOK DETAIL' -}; - -export const LOGIN = { - NAME: 'Login', - TITLE: 'LOGIN' -}; diff --git a/src/constants/routes.ts b/src/constants/routes.ts new file mode 100644 index 0000000..e071e19 --- /dev/null +++ b/src/constants/routes.ts @@ -0,0 +1,14 @@ +export enum LIBRARY { + NAME = 'Library', + TITLE = 'LIBRARY' +} + +export enum BOOK_DETAIL { + NAME = 'BookDetail', + TITLE = 'BOOK DETAIL' +} + +export enum LOGIN { + NAME = 'Login', + TITLE = 'LOGIN' +} diff --git a/src/constants/types.js b/src/constants/types.js deleted file mode 100644 index 486961d..0000000 --- a/src/constants/types.js +++ /dev/null @@ -1,7 +0,0 @@ -export const DATA_TYPES = { - number: 'number', - string: 'string', - object: 'object', - fn: 'function', - undef: 'undefined' -}; diff --git a/src/constants/types.ts b/src/constants/types.ts new file mode 100644 index 0000000..feef930 --- /dev/null +++ b/src/constants/types.ts @@ -0,0 +1,7 @@ +export enum DATA_TYPES { + number = 'number', + string = 'string', + object = 'object', + fn = 'function', + undef = 'undefined' +} diff --git a/src/context/index.js b/src/context/index.js deleted file mode 100644 index a807799..0000000 --- a/src/context/index.js +++ /dev/null @@ -1,15 +0,0 @@ -import { useContext, createContext } from 'react'; - -import { userReducer, userState } from './user'; - -export const globalState = { - userState -}; - -export const globalReducer = (state, action) => ({ - userState: userReducer(state.userState, action) -}); - -export const GlobalContext = createContext(); - -export const useGlobalValue = () => useContext(GlobalContext); diff --git a/src/context/index.ts b/src/context/index.ts new file mode 100644 index 0000000..4b3403f --- /dev/null +++ b/src/context/index.ts @@ -0,0 +1,20 @@ +import React, { useContext, createContext } from 'react'; + +import { userReducer, userState } from './user'; +import { UserAction } from './user/types'; + +export const globalState = { + userState +}; + +const dispatch: React.Dispatch = () => ({}); + +export const globalReducer = (state: typeof globalState, action: unknown) => ({ + userState: userReducer(state.userState, action as UserAction) +}); + +const defaultValue = [globalState, dispatch] as const; + +export const GlobalContext = createContext(defaultValue); + +export const useGlobalValue = () => useContext(GlobalContext); diff --git a/src/context/user/actions.js b/src/context/user/actions.js deleted file mode 100644 index 12c2f92..0000000 --- a/src/context/user/actions.js +++ /dev/null @@ -1,13 +0,0 @@ -import { stringArrayToObject } from '@utils/arrays'; - -export const actions = stringArrayToObject(['LOG_IN', 'LOG_OUT']); - -export const actionsCreator = { - logIn: token => ({ - type: actions.LOG_IN, - payload: token - }), - logOut: () => ({ - type: actions.LOG_OUT - }) -}; diff --git a/src/context/user/actions.ts b/src/context/user/actions.ts new file mode 100644 index 0000000..50faee1 --- /dev/null +++ b/src/context/user/actions.ts @@ -0,0 +1,11 @@ +import { ActionsCreator, ActionTypes } from './types'; + +export const actionsCreator: ActionsCreator = { + logIn: token => ({ + type: ActionTypes.LOG_IN, + payload: token + }), + logOut: () => ({ + type: ActionTypes.LOG_OUT + }) +}; diff --git a/src/context/user/index.js b/src/context/user/index.js deleted file mode 100644 index a0f693a..0000000 --- a/src/context/user/index.js +++ /dev/null @@ -1,19 +0,0 @@ -import { actions } from './actions'; - -export const userState = { - token: null -}; - -const reducer = { - [actions.LOG_IN]: (state, action) => ({ - ...state, - token: action.payload - }), - [actions.LOG_OUT]: state => ({ - ...state, - token: null - }) -}; - -export const userReducer = (state = userState, action) => - reducer[action.type] ? reducer[action.type](state, action) : state; diff --git a/src/context/user/index.ts b/src/context/user/index.ts new file mode 100644 index 0000000..c38e7c5 --- /dev/null +++ b/src/context/user/index.ts @@ -0,0 +1,22 @@ +import { UserState, UserAction, ActionTypes } from './types'; + +export const userState: UserState = { + token: null +}; + +export const userReducer = (state: UserState, action: UserAction): UserState => { + switch (action.type) { + case ActionTypes.LOG_IN: + return { + ...state, + token: action.payload + }; + case ActionTypes.LOG_OUT: + return { + ...state, + token: null + }; + default: + return state; + } +}; diff --git a/src/context/user/types.ts b/src/context/user/types.ts new file mode 100644 index 0000000..3b1a2e9 --- /dev/null +++ b/src/context/user/types.ts @@ -0,0 +1,15 @@ +export enum ActionTypes { + LOG_IN = 'LOG_IN', + LOG_OUT = 'LOG_OUT' +} + +export interface ActionsCreator { + logIn(token: string): UserAction; + logOut(): UserAction; +} + +export interface UserState { + token: string | null; +} + +export type UserAction = { type: ActionTypes.LOG_IN; payload: string } | { type: ActionTypes.LOG_OUT }; diff --git a/src/hooks/index.js b/src/hooks/index.ts similarity index 100% rename from src/hooks/index.js rename to src/hooks/index.ts diff --git a/src/hooks/useLazyRequest.js b/src/hooks/useLazyRequest.js deleted file mode 100644 index 42c9aa5..0000000 --- a/src/hooks/useLazyRequest.js +++ /dev/null @@ -1,27 +0,0 @@ -import { useState } from 'react'; - -function useLazyRequest(query) { - const [data, setData] = useState(); - const [loading, setLoading] = useState(); - const [error, setError] = useState(); - - const request = async values => { - try { - setLoading(true); - const response = await query(values); - if (response?.ok) { - setLoading(false); - setData(response); - } else { - throw Error(); - } - } catch { - setError(true); - setLoading(false); - } - }; - - return [request, { data, loading, error }]; -} - -export default useLazyRequest; diff --git a/src/hooks/useLazyRequest.ts b/src/hooks/useLazyRequest.ts new file mode 100644 index 0000000..5b2f5f5 --- /dev/null +++ b/src/hooks/useLazyRequest.ts @@ -0,0 +1,29 @@ +import { useState } from 'react'; + +import { Query, QueryParams } from '@interfaces/api'; + +function useLazyRequest(query: Query) { + const [data, setData] = useState(); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(false); + + const request = async (params: QueryParams) => { + try { + setLoading(true); + const response = await query(params); + if (response?.ok) { + setLoading(false); + setData(response); + } else { + throw Error(); + } + } catch { + setError(true); + setLoading(false); + } + }; + + return [request, { data, loading, error }] as const; +} + +export default useLazyRequest; diff --git a/src/hooks/useRequest.js b/src/hooks/useRequest.ts similarity index 66% rename from src/hooks/useRequest.js rename to src/hooks/useRequest.ts index 593153e..6cbcf90 100644 --- a/src/hooks/useRequest.js +++ b/src/hooks/useRequest.ts @@ -1,14 +1,15 @@ import { useState, useEffect } from 'react'; -function useRequest(query, params) { +import { Query, QueryParams } from '@interfaces/api'; + +function useRequest(query: Query, params?: QueryParams) { const [reqParams] = useState(params); - const [loading, setLoading] = useState(); - const [data, setData] = useState(); - const [error, setError] = useState(); + const [data, setData] = useState(); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(false); useEffect(() => { (async () => { - setLoading(true); try { const response = await query(reqParams); if (response?.ok) { diff --git a/src/index.d.ts b/src/index.d.ts new file mode 100644 index 0000000..e2937d4 --- /dev/null +++ b/src/index.d.ts @@ -0,0 +1 @@ +declare module '*.png'; diff --git a/src/interfaces/api.ts b/src/interfaces/api.ts new file mode 100644 index 0000000..dfed1ee --- /dev/null +++ b/src/interfaces/api.ts @@ -0,0 +1,11 @@ +export type AuthHeaders = { + 'access-token': string; + uid: string; + client: string; +}; + +export type QueryParams = { + [key: string]: any; +}; + +export type Query = (...args: any[]) => Promise; diff --git a/src/interfaces/book.ts b/src/interfaces/book.ts new file mode 100644 index 0000000..dd96996 --- /dev/null +++ b/src/interfaces/book.ts @@ -0,0 +1,13 @@ +export interface Book { + id: number; + author: string; + title: string; + image: { url: string }; + year: string; + genre: string; + // eslint-disable-next-line camelcase + created_at: string; + // eslint-disable-next-line camelcase + updated_at: string; + rents: []; +} diff --git a/src/services/BookService.js b/src/services/BookService.js deleted file mode 100644 index 50aa4da..0000000 --- a/src/services/BookService.js +++ /dev/null @@ -1,5 +0,0 @@ -import api from '@config/api'; - -export const getBooksList = () => api.get('/books'); - -export const getBook = ({ id }) => api.get(`/books/${id}`); diff --git a/src/services/BookService.ts b/src/services/BookService.ts new file mode 100644 index 0000000..dc2a802 --- /dev/null +++ b/src/services/BookService.ts @@ -0,0 +1,6 @@ +import api from '@config/api'; +import { Book } from '@interfaces/book'; + +export const getBooksList = () => api.get('/books'); + +export const getBook = ({ id }: { id: number }) => api.get(`/books/${id}`); diff --git a/src/services/UserService.js b/src/services/UserService.js deleted file mode 100644 index 678b9f6..0000000 --- a/src/services/UserService.js +++ /dev/null @@ -1,9 +0,0 @@ -import api from '@config/api'; - -import { setStorageItem, getStorageItem } from '@utils/storage'; - -export const authUser = user => api.post('/auth/sign_in', user); - -export const getAuthHeaders = () => getStorageItem('authHeaders'); - -export const setAuthHeaders = data => data && setStorageItem('authHeaders', data); diff --git a/src/services/UserService.ts b/src/services/UserService.ts new file mode 100644 index 0000000..fa68398 --- /dev/null +++ b/src/services/UserService.ts @@ -0,0 +1,15 @@ +import api from '@config/api'; + +import { setStorageItem, getStorageItem } from '@utils/storage'; +import { AuthHeaders } from '@interfaces/api'; + +interface User { + email: string; + password: string; +} + +export const authUser = (user: User) => api.post('/auth/sign_in', user); + +export const getAuthHeaders = () => getStorageItem('authHeaders'); + +export const setAuthHeaders = (data: AuthHeaders) => setStorageItem('authHeaders', data); diff --git a/src/styles/shadows.js b/src/styles/shadows.ts similarity index 75% rename from src/styles/shadows.js rename to src/styles/shadows.ts index 37a60bb..7576053 100644 --- a/src/styles/shadows.js +++ b/src/styles/shadows.ts @@ -1,10 +1,10 @@ import { StyleSheet } from 'react-native'; -import { BLACK } from '@constants/colors'; +import COLORS from '@constants/colors'; export const BOX_SHADOW = StyleSheet.create({ type1: { - shadowColor: BLACK, + shadowColor: COLORS.BLACK, shadowOffset: { width: 0, height: 1 diff --git a/src/utils/arrays.js b/src/utils/arrays.js deleted file mode 100644 index 3eda025..0000000 --- a/src/utils/arrays.js +++ /dev/null @@ -1,4 +0,0 @@ -export const isNotEmptyArray = array => Array.isArray(array) && !!array.length; - -export const stringArrayToObject = array => - isNotEmptyArray(array) && array.reduce((result, value) => ({ ...result, [value]: value }), {}); diff --git a/src/utils/objects.js b/src/utils/objects.js deleted file mode 100644 index 8088eac..0000000 --- a/src/utils/objects.js +++ /dev/null @@ -1,4 +0,0 @@ -import { DATA_TYPES } from '@constants/types'; - -export const isNotEmptyObject = obj => - obj && !Array.isArray(obj) && typeof obj === DATA_TYPES.object && !!Object.keys(obj).length; diff --git a/src/utils/renders.js b/src/utils/renders.js deleted file mode 100644 index eeeba42..0000000 --- a/src/utils/renders.js +++ /dev/null @@ -1 +0,0 @@ -export const keyExtractor = ({ id }) => id.toString(); diff --git a/src/utils/renders.ts b/src/utils/renders.ts new file mode 100644 index 0000000..b9fcf43 --- /dev/null +++ b/src/utils/renders.ts @@ -0,0 +1 @@ +export const keyExtractor = ({ id }: { id: number }): string => id.toString(); diff --git a/src/utils/serializers.js b/src/utils/serializers.js deleted file mode 100644 index 6933a39..0000000 --- a/src/utils/serializers.js +++ /dev/null @@ -1,17 +0,0 @@ -/* eslint-disable camelcase */ -import { Serializer } from 'cerealizr'; - -import { isNotEmptyObject } from './objects'; -import { isNotEmptyArray } from './arrays'; - -const bookSerializer = new Serializer({ - descriptor: { - image_url: 'imageUrl' - }, - mapAllValues: true -}); - -export const serializeBook = book => isNotEmptyObject(book) && bookSerializer.serialize(book); - -export const serializeBooks = books => - isNotEmptyArray(books) && books.map(book => bookSerializer.serialize(book)); diff --git a/src/utils/storage.js b/src/utils/storage.ts similarity index 55% rename from src/utils/storage.js rename to src/utils/storage.ts index e176eb3..1ee6e56 100644 --- a/src/utils/storage.js +++ b/src/utils/storage.ts @@ -1,8 +1,13 @@ import AsyncStorage from '@react-native-community/async-storage'; -export const setStorageItem = (key, data) => AsyncStorage.setItem(key, JSON.stringify(data)); +export const setStorageItem = (key: string, data: T) => AsyncStorage.setItem(key, JSON.stringify(data)); -export const getStorageItem = async key => { +interface Response { + data: T | null; + ok: boolean; +} + +export const getStorageItem = async (key: string): Promise> => { try { const response = await AsyncStorage.getItem(key); if (response) return Promise.resolve({ data: JSON.parse(response), ok: true }); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..604f56d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "allowJs": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "isolatedModules": true, + "jsx": "react", + "lib": ["es6"], + "moduleResolution": "node", + "noEmit": true, + "strict": true, + "target": "esnext" + }, + "exclude": [ + "node_modules", + "babel.config.js", + "metro.config.js", + "jest.config.js" + ], + "include": ["src"], + "extends": "./tsconfig.paths.json" +} diff --git a/tsconfig.paths.json b/tsconfig.paths.json new file mode 100644 index 0000000..1b779fa --- /dev/null +++ b/tsconfig.paths.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "baseUrl": "src", + "paths": { + "@constants/*": ["constants/*"], + "@assets/*": ["assets/*"], + "@screens/*": ["app/screens/*"], + "@components/*": ["app/components/*"], + "@hooks/*": ["hooks/*"], + "@services/*": ["services/*"], + "@config/*": ["config/*"], + "@utils/*": ["utils/*"], + "@styles/*": ["styles/*"], + "@context/*": ["context/*"], + "@interfaces/*": ["interfaces/*"] + } + } +}