diff --git a/.expo-shared/README.md b/.expo-shared/README.md new file mode 100644 index 0000000..e9e5318 --- /dev/null +++ b/.expo-shared/README.md @@ -0,0 +1,11 @@ +> Why do I have a folder named ".expo-shared" in my project? + +The ".expo-shared" folder is created when running commands that produce state that is intended to be shared with all developers on the project. For example, "npx expo-optimize". + +> What does the "assets.json" file contain? + +The "assets.json" file describes the assets that have been optimized through "expo-optimize" and do not need to be processed again. + +> Should I commit the ".expo-shared" folder? + +Yes, you should share the ".expo-shared" folder with your collaborators. diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..2a3ae7a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +// Workspace settings +{ + // The following will hide the js and map files in the editor + "files.exclude": { + ".expo": true, + ".expo-shared": true, + "android": true, + "node_modules": true, + "package-lock.json": true, + "**/*.map": true + } +} \ No newline at end of file diff --git a/App.js b/App.js index 181f3ce..4f56375 100644 --- a/App.js +++ b/App.js @@ -1,21 +1,26 @@ -import { StatusBar } from 'expo-status-bar'; -import React from 'react'; -import { StyleSheet, Text, View } from 'react-native'; +import 'react-native-gesture-handler'; + +import React, { useState, useEffect, useContext } from 'react'; +import MainContext, { DispatchContext } from './src/utils/context/MainContext'; +import AppNavContainer from './src/components/navigation/AppNavContainer'; +import axios from 'axios' +import Define from './src/utils/helpers/Define'; + + +//axios setup +axios.defaults.baseURL = Define.API_BASE_URL +axios.defaults.withCredentials = true export default function App() { + + + return ( - - Open up App.js to start working on your app! - - + <> + + + + + ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: '#fff', - alignItems: 'center', - justifyContent: 'center', - }, -}); +} \ No newline at end of file diff --git a/app.json b/app.json index 96afb4e..db298d3 100644 --- a/app.json +++ b/app.json @@ -1,6 +1,6 @@ { "expo": { - "name": "password-manager", + "name": "Password Manager", "slug": "password-manager", "version": "1.0.0", "orientation": "portrait", @@ -23,10 +23,12 @@ "adaptiveIcon": { "foregroundImage": "./assets/adaptive-icon.png", "backgroundColor": "#FFFFFF" - } + }, + "package": "com.milon27.passwordmanager" }, "web": { "favicon": "./assets/favicon.png" - } + }, + "description": "" } } diff --git a/assets/adaptive-icon.png b/assets/adaptive-icon.png index 03d6f6b..5c77c7f 100644 Binary files a/assets/adaptive-icon.png and b/assets/adaptive-icon.png differ diff --git a/assets/favicon.png b/assets/favicon.png index e75f697..52f6255 100644 Binary files a/assets/favicon.png and b/assets/favicon.png differ diff --git a/assets/icon.png b/assets/icon.png index a0b1526..5c77c7f 100644 Binary files a/assets/icon.png and b/assets/icon.png differ diff --git a/assets/splash.png b/assets/splash.png index 6f47774..2829f79 100644 Binary files a/assets/splash.png and b/assets/splash.png differ diff --git a/installation.md b/installation.md new file mode 100644 index 0000000..d3bcdf5 --- /dev/null +++ b/installation.md @@ -0,0 +1,5 @@ +``` +expo install @react-native-async-storage/async-storage + + +``` \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 7ca12d2..d0d1cf9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1129,6 +1129,14 @@ "minimist": "^1.2.0" } }, + "@egjs/hammerjs": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", + "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==", + "requires": { + "@types/hammerjs": "^2.0.36" + } + }, "@expo/config": { "version": "3.3.42", "resolved": "https://registry.npmjs.org/@expo/config/-/config-3.3.42.tgz", @@ -1787,6 +1795,14 @@ "regenerator-runtime": "^0.13.3" } }, + "@react-native-async-storage/async-storage": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.15.4.tgz", + "integrity": "sha512-pC0MS6UBuv/YiVAxtzi7CgUed8oCQNYMtGt0yb/I9fI/BWTiJK5cj4YtW2XtL95K5IuvPX/6uGWaouZ8KqXwdg==", + "requires": { + "deep-assign": "^3.0.0" + } + }, "@react-native-community/cli-debugger-ui": { "version": "4.13.1", "resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-4.13.1.tgz", @@ -2165,6 +2181,83 @@ "resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-4.10.1.tgz", "integrity": "sha512-ael2f1onoPF3vF7YqHGWy7NnafzGu+yp88BbFbP0ydoCP2xGSUzmZVw0zakPTC040Id+JQ9WeFczujMkDy6jYQ==" }, + "@react-native-community/masked-view": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/@react-native-community/masked-view/-/masked-view-0.1.10.tgz", + "integrity": "sha512-rk4sWFsmtOw8oyx8SD3KSvawwaK7gRBSEIy2TAwURyGt+3TizssXP1r8nx3zY+R7v2vYYHXZ+k2/GULAT/bcaQ==" + }, + "@react-navigation/core": { + "version": "5.15.3", + "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-5.15.3.tgz", + "integrity": "sha512-3ZdyDInh8qg1kygCNkmh9lFgpDf29lTvPsaMe2mm/qvmxLKSgttWBz07P2fc181aV9jTdgQpzYfWZ5KWT036zw==", + "requires": { + "@react-navigation/routers": "^5.7.2", + "escape-string-regexp": "^4.0.0", + "nanoid": "^3.1.15", + "query-string": "^6.13.6", + "react-is": "^16.13.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, + "@react-navigation/drawer": { + "version": "5.12.5", + "resolved": "https://registry.npmjs.org/@react-navigation/drawer/-/drawer-5.12.5.tgz", + "integrity": "sha512-WMfz/tKg/K7QBb5rhjXW/pho4zXh3OoHXnHETk5SuVzHlDPM7r84uvAeC5l+ySp5jmipLrJn3zL+kfv9+KKHZQ==", + "requires": { + "color": "^3.1.3", + "react-native-iphone-x-helper": "^1.3.0" + } + }, + "@react-navigation/native": { + "version": "5.9.4", + "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-5.9.4.tgz", + "integrity": "sha512-BUCrOXfZDdKWBqM8OhOKQhCX5we4HUo5XG6tCQtVqQAep+7UcApZmMUuemUXDxVe8NPESUpoUlB0RaEpyIdfTQ==", + "requires": { + "@react-navigation/core": "^5.15.3", + "escape-string-regexp": "^4.0.0", + "nanoid": "^3.1.15" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + } + } + }, + "@react-navigation/routers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-5.7.2.tgz", + "integrity": "sha512-BxNSMLHpU+oS37Xok0ql6rc9U7IC8aUD4+U5ZPbjDJ0pwzZxGGh0YOEBzfV4k/Ig3cbPdvVWbc1C9HHbCVr2oQ==", + "requires": { + "nanoid": "^3.1.15" + } + }, + "@react-navigation/stack": { + "version": "5.14.5", + "resolved": "https://registry.npmjs.org/@react-navigation/stack/-/stack-5.14.5.tgz", + "integrity": "sha512-hpdn1SS0tc3/3atkV2Q2y++n5B4e0rUcCj4W43PODMu72yX2m0LkKAAcpkPDCWAvwnLLIoLAEl5BEifZigl/6A==", + "requires": { + "color": "^3.1.3", + "react-native-iphone-x-helper": "^1.3.0" + } + }, + "@types/hammerjs": { + "version": "2.0.39", + "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.39.tgz", + "integrity": "sha512-lYR2Y/tV2ujpk/WyUc7S0VLI0a9hrtVIN9EwnrNo5oSEJI2cK2/XrgwOQmXLL3eTulOESvh9qP6si9+DWM9cOA==" + }, "@types/istanbul-lib-coverage": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", @@ -2417,6 +2510,14 @@ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" }, + "axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "requires": { + "follow-redirects": "^1.10.0" + } + }, "babel-plugin-dynamic-import-node": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", @@ -2933,6 +3034,15 @@ "object-visit": "^1.0.0" } }, + "color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz", + "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==", + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.4" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -3128,6 +3238,14 @@ "object-assign": "^4.1.1" } }, + "cross-fetch": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.4.tgz", + "integrity": "sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==", + "requires": { + "node-fetch": "2.6.1" + } + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -3147,6 +3265,11 @@ } } }, + "crypto-es": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/crypto-es/-/crypto-es-1.2.7.tgz", + "integrity": "sha512-UUqiVJ2gUuZFmbFsKmud3uuLcNP2+Opt+5ysmljycFCyhA0+T16XJmo1ev/t5kMChMqWh7IEvURNCqsg+SjZGQ==" + }, "crypto-random-string": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", @@ -3497,6 +3620,11 @@ "url-parse": "^1.4.4" } }, + "expo-clipboard": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/expo-clipboard/-/expo-clipboard-1.0.2.tgz", + "integrity": "sha512-0uarcOHQKPW658eMhW2KBnTxtiS7yFQZ95j+Dz71Kn50X9V7HGXsexEETQ0lL05ArDAgpVRTPHz0YKkaq8Kn/Q==" + }, "expo-constants": { "version": "10.1.3", "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-10.1.3.tgz", @@ -3761,6 +3889,11 @@ } } }, + "filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs=" + }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -3830,6 +3963,11 @@ "path-exists": "^4.0.0" } }, + "follow-redirects": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", + "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==" + }, "fontfaceobserver": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fontfaceobserver/-/fontfaceobserver-2.1.0.tgz", @@ -4021,6 +4159,21 @@ } } }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, "http-errors": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", @@ -5650,6 +5803,11 @@ "minimist": "^1.2.5" } }, + "mockdate": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/mockdate/-/mockdate-3.0.5.tgz", + "integrity": "sha512-iniQP4rj1FhBdBYS/+eQv7j1tadJ9lJtdzgOpvsOHng/GbcDh2Fhdeq+ZRldrPYdXvCyfFUmFeEwEGXZB5I/AQ==" + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -5666,6 +5824,11 @@ "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", "optional": true }, + "nanoid": { + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", + "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==" + }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -6294,6 +6457,17 @@ "once": "^1.3.1" } }, + "query-string": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", + "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", + "requires": { + "decode-uri-component": "^0.2.0", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + } + }, "querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -6670,11 +6844,182 @@ } } }, + "react-native-dropdown-picker": { + "version": "5.1.21", + "resolved": "https://registry.npmjs.org/react-native-dropdown-picker/-/react-native-dropdown-picker-5.1.21.tgz", + "integrity": "sha512-PmRyQD0JkhNdA2eZuc3I7C1+WX6XLq+erI/hCvzYM1E+oGflI59mBBSBq2o0IDzzdTYeLQecj4yEEIHQRHloTw==", + "requires": { + "react-native-gesture-handler": "*" + } + }, + "react-native-gesture-handler": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-1.10.3.tgz", + "integrity": "sha512-cBGMi1IEsIVMgoox4RvMx7V2r6bNKw0uR1Mu1o7NbuHS6BRSVLq0dP34l2ecnPlC+jpWd3le6Yg1nrdCjby2Mw==", + "requires": { + "@egjs/hammerjs": "^2.0.17", + "fbjs": "^3.0.0", + "hoist-non-react-statics": "^3.3.0", + "invariant": "^2.2.4", + "prop-types": "^15.7.2" + }, + "dependencies": { + "fbjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.0.tgz", + "integrity": "sha512-dJd4PiDOFuhe7vk4F80Mba83Vr2QuK86FoxtgPmzBqEJahncp+13YCmfoa53KHCo6OnlXLG7eeMWPfB5CrpVKg==", + "requires": { + "cross-fetch": "^3.0.4", + "fbjs-css-vars": "^1.0.0", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + } + } + } + }, + "react-native-iphone-x-helper": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.3.1.tgz", + "integrity": "sha512-HOf0jzRnq2/aFUcdCJ9w9JGzN3gdEg0zFE4FyYlp4jtidqU03D5X7ZegGKfT1EWteR0gPBGp9ye5T5FvSWi9Yg==" + }, + "react-native-reanimated": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-2.1.0.tgz", + "integrity": "sha512-tlPvvcdf+X7HGQ7g/7npBFhwMznfdk7MHUc9gUB/kp2abSscXNe/kOVKlrNEOO4DS11rNOXc+llFxVFMuNk0zA==", + "requires": { + "@babel/plugin-transform-object-assign": "^7.10.4", + "fbjs": "^3.0.0", + "mockdate": "^3.0.2", + "string-hash-64": "^1.0.3" + }, + "dependencies": { + "fbjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.0.tgz", + "integrity": "sha512-dJd4PiDOFuhe7vk4F80Mba83Vr2QuK86FoxtgPmzBqEJahncp+13YCmfoa53KHCo6OnlXLG7eeMWPfB5CrpVKg==", + "requires": { + "cross-fetch": "^3.0.4", + "fbjs-css-vars": "^1.0.0", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + } + } + } + }, "react-native-safe-area-context": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-3.2.0.tgz", "integrity": "sha512-k2Nty4PwSnrg9HwrYeeE+EYqViYJoOFwEy9LxL5RIRfoqxAq/uQXNGwpUg2/u4gnKpBbEPa9eRh15KKMe/VHkA==" }, + "react-native-screens": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-3.0.0.tgz", + "integrity": "sha512-35II5oxTaVp3OP8y0eLPOPpQkxG4fRKQ+dL1YSE1we5kCZFOU0l/Rn0T79HbyUu1LPwUZr6lZupPs0ULnRyMuQ==" + }, + "react-native-user-inactivity": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/react-native-user-inactivity/-/react-native-user-inactivity-1.2.0.tgz", + "integrity": "sha512-VR+zv+cKBOSbJyHLJxvDUVluQ4OD/uW9FomAJVWrXCnSO4tYQieaAR3/5oNIfr6JXAJ/ChdJ/eyc06vHEBvlsA==", + "requires": { + "usetimeout-react-hook": "^0.1.2" + } + }, + "react-native-vector-icons": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-8.1.0.tgz", + "integrity": "sha512-sHIdBB6Y0dHaot2fMXgy5J/hhCn5YuyN7SKDNFgPzL8KA1oF2/v7mgYMavnK7LIIs2dJoGnDANKf61dsU+TZlg==", + "requires": { + "lodash.frompairs": "^4.0.1", + "lodash.isequal": "^4.5.0", + "lodash.isstring": "^4.0.1", + "lodash.omit": "^4.5.0", + "lodash.pick": "^4.4.0", + "lodash.template": "^4.5.0", + "prop-types": "^15.7.2", + "yargs": "^16.1.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.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==", + "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==" + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.7", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", + "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==" + } + } + }, "react-native-web": { "version": "0.13.18", "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.13.18.tgz", @@ -7284,6 +7629,11 @@ "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" }, + "split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==" + }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", @@ -7361,6 +7711,16 @@ "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", "integrity": "sha1-kdX1Ew0c75bc+n9yaUUYh0HQnuQ=" }, + "strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=" + }, + "string-hash-64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string-hash-64/-/string-hash-64-1.0.3.tgz", + "integrity": "sha512-D5OKWKvDhyVWWn2x5Y9b+37NUllks34q1dCDhk/vYcso9fmhs+Tl3KR/gE4v5UNj2UA35cnX4KdVVGkG1deKqw==" + }, "string-width": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", @@ -7710,6 +8070,11 @@ "object-assign": "^4.1.1" } }, + "usetimeout-react-hook": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/usetimeout-react-hook/-/usetimeout-react-hook-0.1.2.tgz", + "integrity": "sha512-uHc8QsWDznEhWkK+ygX0xWxyObRjy72685EZ5wr3/ipNsK0EYntGnbenKxLNirPKKsng+1KW5CPTAk7qQDY8VQ==" + }, "utif": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/utif/-/utif-2.0.1.tgz", diff --git a/package.json b/package.json index 988adce..4969d7c 100644 --- a/package.json +++ b/package.json @@ -3,20 +3,36 @@ "scripts": { "start": "expo start", "android": "expo start --android", + "build": "expo build:android", "ios": "expo start --ios", "web": "expo start --web", "eject": "expo eject" }, "dependencies": { + "@react-native-async-storage/async-storage": "^1.13.0", + "@react-native-community/masked-view": "0.1.10", + "@react-navigation/drawer": "^5.12.5", + "@react-navigation/native": "^5.9.4", + "@react-navigation/stack": "^5.14.5", + "axios": "^0.21.1", + "crypto-es": "^1.2.7", "expo": "~41.0.1", + "expo-clipboard": "~1.0.2", "expo-status-bar": "~1.0.4", "react": "16.13.1", "react-dom": "16.13.1", "react-native": "https://github.com/expo/react-native/archive/sdk-41.0.0.tar.gz", + "react-native-dropdown-picker": "^5.1.21", + "react-native-gesture-handler": "~1.10.2", + "react-native-reanimated": "~2.1.0", + "react-native-safe-area-context": "3.2.0", + "react-native-screens": "~3.0.0", + "react-native-user-inactivity": "^1.2.0", + "react-native-vector-icons": "^8.1.0", "react-native-web": "~0.13.12" }, "devDependencies": { "@babel/core": "^7.9.0" }, "private": true -} +} \ No newline at end of file diff --git a/src/assets/img/logo.png b/src/assets/img/logo.png new file mode 100644 index 0000000..ac27f25 Binary files /dev/null and b/src/assets/img/logo.png differ diff --git a/src/components/layouts/Container.js b/src/components/layouts/Container.js new file mode 100644 index 0000000..3dcbcde --- /dev/null +++ b/src/components/layouts/Container.js @@ -0,0 +1,21 @@ +import React from 'react' +import { SafeAreaView, StyleSheet, StatusBar, View } from 'react-native' +import Theme from '../../utils/helpers/Theme' + +export default function Container({ barStyle = "dark-content", bgColor, children, style }) { + return ( + + + + {children} + + + ) +} +const styles = StyleSheet.create({ + container: { + flex: 1, + padding: 24, + backgroundColor: Theme.COLOR_BG + } +}) diff --git a/src/components/layouts/ResponseLayout.js b/src/components/layouts/ResponseLayout.js new file mode 100644 index 0000000..1cc8079 --- /dev/null +++ b/src/components/layouts/ResponseLayout.js @@ -0,0 +1,43 @@ +import React, { useEffect } from 'react' +import { View, Text, StyleSheet } from 'react-native' +import Theme from './../../utils/helpers/Theme'; +import AppAction from './../../utils/context/actions/AppAction'; +import { useContext } from 'react'; +import { DispatchContext } from '../../utils/context/MainContext'; + +export default function ResponseLayout({ response }) { + + const { appDispatch } = useContext(DispatchContext) + + useEffect(() => { + setTimeout(() => { + new AppAction(appDispatch).REMOVE_RESPONSE() + }, 4000); + }, [response?.title]) + + if (!response.title) { + return + } + + return ( + + {response?.title} + {response?.desc} + + ) +} + +const styles = StyleSheet.create({ + container: { + justifyContent: "center", + alignItems: "center", + borderRadius: 5, + paddingHorizontal: 10, + elevation: 4, + paddingVertical: 20 + }, + text: { + color: Theme.COLOR_WHITE, + fontSize: 18 + } +}) diff --git a/src/components/layouts/form/Input.js b/src/components/layouts/form/Input.js new file mode 100644 index 0000000..de13b5e --- /dev/null +++ b/src/components/layouts/form/Input.js @@ -0,0 +1,87 @@ +import React, { Fragment, useState } from 'react' +import { View, StyleSheet, TextInput, Text } from 'react-native' +import DefineIcon from '../icon/DefineIcon'; +import Theme from './../../../utils/helpers/Theme'; +import Icon from './../icon/Icon'; + +export default function Input({ type = "default", label, icon, error, style, ...pros }) { + + const [secure, setSecure] = useState(true) + const [focused, setFocused] = useState(false) + + const getBorder = () => { + + if (error) { + return Theme.COLOR_DANGER + } + if (focused) { + return Theme.COLOR_PRIMARY + } + else { + return Theme.COLOR_GRAY + } + } + + return ( + + {label} + + + {icon} + + setFocused(true)} + onBlur={() => setFocused(false)} + {...pros} + /> + {type === "password" ? { setSecure(s => !s) }} style={styles.secureBtn}> : } + + + {error ? {error} : } + + + ) +} + +const styles = StyleSheet.create({ + container: { + marginVertical: 2 + }, + icon: { + color: Theme.COLOR_PRIMARY, + paddingHorizontal: 1, + paddingRight: 5 + }, + showBtn: { + color: Theme.COLOR_PRIMARY, + fontSize: 12 + }, + inputContainer: { + flexDirection: 'row', + justifyContent: "space-between", + alignItems: "center", + borderWidth: 1, + borderRadius: 5, + paddingHorizontal: 10, + height: 44 + }, + input: { + flex: 1, + flexDirection: 'row', + justifyContent: "space-between", + alignItems: "center", + }, + inputBox: { + color: Theme.COLOR_BLACK, + flex: 1, + }, + label: { + color: Theme.COLOR_GRAY, marginVertical: 5 + } +}) \ No newline at end of file diff --git a/src/components/layouts/form/MButton.js b/src/components/layouts/form/MButton.js new file mode 100644 index 0000000..2c903cc --- /dev/null +++ b/src/components/layouts/form/MButton.js @@ -0,0 +1,40 @@ +import React, { useState } from 'react' +import { Pressable, Text, StyleSheet, ActivityIndicator, View } from 'react-native' +import Theme from './../../../utils/helpers/Theme'; +//todo: add loading.. +export default function MButton({ title, style, color, loading, onPress, disabled, ...props }) { + + + return ( + + [{ backgroundColor: pressed ? loading ? Theme.COLOR_GRAY : Theme.COLOR_ACCENT : color }, styles.btn_container, { backgroundColor: loading ? Theme.COLOR_GRAY : color }]} + disabled={disabled} + {...props} + > + {loading ? : {title}} + + + ) +} + + +const styles = StyleSheet.create({ + btn_container: { + justifyContent: "center", + alignItems: "center", + borderRadius: 5, + paddingHorizontal: 10, + marginVertical: 15, + height: 45, + elevation: 4, + zIndex: 2 + }, + btn_text: { + paddingHorizontal: 10, + fontSize: 15, + color: Theme.COLOR_WHITE, + } +}) diff --git a/src/components/layouts/form/NavLink.js b/src/components/layouts/form/NavLink.js new file mode 100644 index 0000000..8135780 --- /dev/null +++ b/src/components/layouts/form/NavLink.js @@ -0,0 +1,24 @@ +import { useNavigation } from '@react-navigation/native' +import React from 'react' +import { TouchableOpacity, Text, StyleSheet } from 'react-native' +import Theme from './../../../utils/helpers/Theme'; + +export default function NavLink({ url, title }) { + const nav = useNavigation() + return ( + { nav.navigate(url) }}> + {title} + + ) +} + +const styles = StyleSheet.create({ + small_title: { + color: Theme.COLOR_BLACK, + textAlign: "center", + marginVertical: 5, + paddingBottom: 5, + textDecorationLine: "underline", + textDecorationStyle: "solid" + } +}) \ No newline at end of file diff --git a/src/components/layouts/icon/DefineIcon.js b/src/components/layouts/icon/DefineIcon.js new file mode 100644 index 0000000..799208b --- /dev/null +++ b/src/components/layouts/icon/DefineIcon.js @@ -0,0 +1,17 @@ +const DefineIcon = { + ZocialIcon: 'Zocial', + OcticonIcon: 'Octicons', + MaterialIcon: 'MaterialIcons', + MaterialCommunityIcon: 'MaterialCommunityIcons', + Ionicon: 'Ionicons', + FoundationIcon: 'Foundation', + EvilIcon: 'EvilIcons', + EntypoIcon: 'Entypo', + FAIcon: 'FontAwesome', + FAIcon5: 'FontAwesome5', + SimpleLineIcon: 'SimpleLineIcons', + AntDesign: 'AntDesign', + Feather: 'Feather', + Fontisto: 'Fontisto', +} +export default DefineIcon \ No newline at end of file diff --git a/src/components/layouts/icon/Icon.js b/src/components/layouts/icon/Icon.js new file mode 100644 index 0000000..d1215a7 --- /dev/null +++ b/src/components/layouts/icon/Icon.js @@ -0,0 +1,58 @@ +import React from 'react' +import ZocialIcon from 'react-native-vector-icons/Zocial'; +import OcticonIcon from 'react-native-vector-icons/Octicons'; +import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; +import MaterialCommunityIcon from 'react-native-vector-icons/MaterialCommunityIcons'; +import Ionicon from 'react-native-vector-icons/Ionicons'; +import FoundationIcon from 'react-native-vector-icons/Foundation'; +import EvilIcon from 'react-native-vector-icons/EvilIcons'; +import EntypoIcon from 'react-native-vector-icons/Entypo'; +import FAIcon from 'react-native-vector-icons/FontAwesome'; +import FAIcon5 from 'react-native-vector-icons/FontAwesome5'; +import SimpleLineIcon from 'react-native-vector-icons/SimpleLineIcons'; +import AntDesign from 'react-native-vector-icons/AntDesign'; +import Feather from 'react-native-vector-icons/Feather'; +import Fontisto from 'react-native-vector-icons/Fontisto'; +import DefineIcon from './DefineIcon'; +import Theme from './../../../utils/helpers/Theme'; + + +const getIconFont = (type) => { + switch (type) { + case DefineIcon.Fontisto: + return Fontisto; + case DefineIcon.MaterialIcon: + return MaterialIcon; + case DefineIcon.EvilIcon: + return EvilIcon; + case DefineIcon.Feather: + return Feather; + case DefineIcon.AntDesign: + return AntDesign; + case DefineIcon.SimpleLineIcon: + return SimpleLineIcon; + case DefineIcon.ZocialIcon: + return ZocialIcon; + case DefineIcon.FoundationIcon: + return FoundationIcon; + case DefineIcon.FAIcon5: + return FAIcon5; + case DefineIcon.FAIcon: + return FAIcon; + case DefineIcon.Ionicon: + return Ionicon; + case DefineIcon.MaterialCommunityIcon: + return MaterialCommunityIcon; + case DefineIcon.EntypoIcon: + return EntypoIcon; + case DefineIcon.OcticonIcon: + return OcticonIcon; + default: + return FAIcon; + } +}; + +export default function Icon({ type, color = Theme.COLOR_BLACK, size = 22, name, ...props }) { + const FontICon = getIconFont(type); + return ; +} diff --git a/src/components/layouts/modal/AreYouSureModal.js b/src/components/layouts/modal/AreYouSureModal.js new file mode 100644 index 0000000..8cb992d --- /dev/null +++ b/src/components/layouts/modal/AreYouSureModal.js @@ -0,0 +1,9 @@ +import React from 'react' +import MModal from './MModal' + +export default function AreYouSureModal({ open, setOpen, submitText, onSubmit }) { + return ( + + + ) +} diff --git a/src/components/layouts/modal/LogOutModal.js b/src/components/layouts/modal/LogOutModal.js new file mode 100644 index 0000000..7640656 --- /dev/null +++ b/src/components/layouts/modal/LogOutModal.js @@ -0,0 +1,21 @@ +import React, { useContext, useState } from 'react' +import { View, Text } from 'react-native' +import MModal from './MModal' +import AuthAction from './../../../utils/context/actions/AuthAction'; +import { DispatchContext } from '../../../utils/context/MainContext'; +import Theme from '../../../utils/helpers/Theme'; + +export default function LogOutModal({ open, setOpen }) { + + const { authDispatch } = useContext(DispatchContext) + + const onLogout = async () => { + await new AuthAction(authDispatch).Logout() + } + + return ( + + Are you sure to logout? + + ) +} diff --git a/src/components/layouts/modal/MModal.js b/src/components/layouts/modal/MModal.js new file mode 100644 index 0000000..7bf3a1a --- /dev/null +++ b/src/components/layouts/modal/MModal.js @@ -0,0 +1,66 @@ +import React from 'react' +import { View, Text, Modal, StyleSheet, StatusBar } from 'react-native' +import MButton from '../form/MButton'; +import Theme from '../../../utils/helpers/Theme'; + +export default function MModal({ + title, open, setOpen, onSubmit, submitText = "Done", children +}) { + + const onCancel = () => { + setOpen(false) + } + + + + return ( + + + + + + {title} + + {children} + + + + + + + + + + ) +} + +const styles = StyleSheet.create({ + body: { + flex: 1, + justifyContent: "center", + alignItems: "center", + backgroundColor: "rgba(0,0,0,0.6)", + }, + modal: { + backgroundColor: Theme.COLOR_BG, + minWidth: 300, + elevation: 20, + paddingHorizontal: 25, + paddingTop: 25, + paddingBottom: 10, + margin: 25, + borderRadius: 10, + }, + title: { + fontSize: 20, + color: Theme.COLOR_BLACK, + marginBottom: 10 + }, + btns: { + flexDirection: "row", + justifyContent: "flex-end", + }, + btn: { + marginHorizontal: 8 + } +}) \ No newline at end of file diff --git a/src/components/layouts/modal/PasswordModal.js b/src/components/layouts/modal/PasswordModal.js new file mode 100644 index 0000000..44dfcf2 --- /dev/null +++ b/src/components/layouts/modal/PasswordModal.js @@ -0,0 +1,32 @@ +import React, { useContext, useState } from 'react' +import { View, Text, StyleSheet } from 'react-native' +import MModal from './MModal' +import Theme from './../../../utils/helpers/Theme'; +import ListAction from './../../../utils/context/actions/ListAction'; +import { DispatchContext } from './../../../utils/context/MainContext'; + +export default function PasswordModal({ open, setOpen, item }) { + + const { passDispatch } = useContext(DispatchContext) + + const onSubmit = async () => { + setOpen(false) + const la = new ListAction(passDispatch) + const val = await la.deleteData('pass/' + item?._id, item?._id) + console.log("del: ", val) + } + return ( + + Site: {item?.title} + URL: {item?.url} + Username: {item?.username} + Email: {item?.email} + + ) +} + +const styles = StyleSheet.create({ + text: { + color: Theme.COLOR_BLACK + } +}) diff --git a/src/components/navigation/AppNavContainer.js b/src/components/navigation/AppNavContainer.js new file mode 100644 index 0000000..8badb87 --- /dev/null +++ b/src/components/navigation/AppNavContainer.js @@ -0,0 +1,81 @@ +import React, { useContext, useState, useEffect } from 'react' +import { NavigationContainer } from '@react-navigation/native'; +import AuthNav from './AuthNav'; +import DashDrawNav from './DashDrawNav'; +import Splash from './../screens/splash/Splash'; +import { DispatchContext, StateContext } from './../../utils/context/MainContext'; +import AuthAction from './../../utils/context/actions/AuthAction'; +import Helper from './../../utils/helpers/Helper'; +import UserInactivity from 'react-native-user-inactivity'; + +export default function AppNavContainer() { + + const { auth, } = useContext(StateContext) + const { authDispatch } = useContext(DispatchContext) + const [active, setactive] = useState(true) + const [loading, setLoading] = useState(true) + + useEffect(() => { + const load = async () => { + //start the loading + //setLoading(true) + //ck is logged in + const authAc = new AuthAction(authDispatch) + const ck = await authAc.IsLoggedIn() + console.log("AppNavContainer:isLoggedin=", ck) + if (ck) { + //ck in localstorage + const u = await Helper.getUser() + //console.log("test: ", u) + //const u2 = await Helper.getPass() + //console.log("passsss:", u2) + if (!u) { + //not logged in + await authAc.Logout() + } + } + + + //stop the loading + + setTimeout(() => { + setLoading(false) + }, 500); + } + load() + + + const ckactivity = async () => { + if (!active) { + await new AuthAction(authDispatch).Logout() + //do logout + Helper.Toast("logged out for inactivity.") + } + } + ckactivity() + + }, [active]) + + + + if (loading === true) { + return + } + + return ( + <> + { + setactive(a) + }} + > + + {auth?.logged_in ? : } + {/* */} + + + + ) +} diff --git a/src/components/navigation/AuthNav.js b/src/components/navigation/AuthNav.js new file mode 100644 index 0000000..9a4662e --- /dev/null +++ b/src/components/navigation/AuthNav.js @@ -0,0 +1,18 @@ +import React from 'react' +import { createStackNavigator } from '@react-navigation/stack'; +import SignIn from './../screens/auth/SignIn'; +import SignUp from './../screens/auth/SignUp'; +import URL from './../../utils/helpers/URL'; + +const authStack = createStackNavigator() +/** + * @description we will use it to navigate to different screen when the user is not logged in. + */ +export default function AuthNav() { + return (//headerMode="none" + + + + + ) +} diff --git a/src/components/navigation/DashDrawNav.js b/src/components/navigation/DashDrawNav.js new file mode 100644 index 0000000..26b7fed --- /dev/null +++ b/src/components/navigation/DashDrawNav.js @@ -0,0 +1,23 @@ +import React from 'react' +import { createDrawerNavigator } from '@react-navigation/drawer' +import DashStackNav from './DashStackNav'; +import URL from './../../utils/helpers/URL'; +import DrawerContent from './DrawerContent'; + +const drawer = createDrawerNavigator() + +/** + * @description we will use it to navigate from drawer nav bar. + */ + +export default function DashDrawNav() { + + return ( + { return }} + > + + + ) +} diff --git a/src/components/navigation/DashStackNav.js b/src/components/navigation/DashStackNav.js new file mode 100644 index 0000000..aab679e --- /dev/null +++ b/src/components/navigation/DashStackNav.js @@ -0,0 +1,62 @@ +import React from 'react' +import { createStackNavigator, HeaderTitle } from '@react-navigation/stack'; +import HomeScreen from './../screens/dashboard/HomeScreen'; +import ContactDetail from './../screens/dashboard/ContactDetail'; +import CreatePassword from './../screens/dashboard/CreatePassword'; +import About from './../screens/dashboard/About'; +import SignIn from './../screens/auth/SignIn'; +import URL from './../../utils/helpers/URL'; +import { useNavigation } from '@react-navigation/native'; +import Theme from './../../utils/helpers/Theme'; +import Icon from './../layouts/icon/Icon'; +import DefineIcon from '../layouts/icon/DefineIcon'; +import CreateCategory from './../screens/dashboard/CreateCategory'; +import Fav from './../screens/favourite/Fav'; + +const dashStack = createStackNavigator() +/** + * @description we will use it to navigate to different screen when the user is logged in. + */ +export default function DashStackNav() { + const nav = useNavigation() + return ( + ( { canGoBack ? nav.goBack() : "" }} />), + headerStyle: { + backgroundColor: Theme.COLOR_BG, + elevation: 1, + shadowColor: Theme.COLOR_GRAY, + shadowOpacity: 1, + } + } + }//global header style + > + + ( { nav.toggleDrawer() }} />), + headerRight: () => { + return { nav.navigate(URL.FAV_PASSWORD) }} /> + } + } + }//home header style + /> + + + + + + + + ) +} diff --git a/src/components/navigation/DrawerContent.js b/src/components/navigation/DrawerContent.js new file mode 100644 index 0000000..57f498c --- /dev/null +++ b/src/components/navigation/DrawerContent.js @@ -0,0 +1,81 @@ +import React, { useState } from 'react' +import { View, Text, TouchableOpacity, StyleSheet, Image, Alert } from 'react-native' +import URL from './../../utils/helpers/URL'; +import Icon from './../layouts/icon/Icon'; +import DefineIcon from '../layouts/icon/DefineIcon'; +import LogOutModal from '../layouts/modal/LogOutModal'; +import Theme from './../../utils/helpers/Theme'; + +export default function DrawerContent({ navigation: nav }) { + const [open, setOpen] = useState(false) + + + const logoutNow = () => { + nav.toggleDrawer() + setOpen(true) + } + + const items = [ + { title: "Home", icon: , onPress: () => { nav.navigate(URL.HOME_SCREEN) } }, + { title: "Create Password", icon: , onPress: () => { nav.navigate(URL.CREATE_PASSWORD) } }, + { title: "Create Category", icon: , onPress: () => { nav.navigate(URL.CREATE_CATEGORY) } }, + { title: "About", icon: , onPress: () => { nav.navigate(URL.ABOUT) } }, + { title: "Log Out", icon: , onPress: logoutNow } + ] + + + + return ( + + + + + + + + + {items.map(item => { + return ( + {item.icon}{item.title} + ) + })} + + + + + + ) +} + +const styles = StyleSheet.create({ + logo: { + width: 50, + height: 50, + }, + logo_container: { + marginTop: 20, + alignSelf: "flex-start", + borderRadius: 50, + backgroundColor: Theme.COLOR_PRIMARY, + padding: 10 + }, + container: { + flex: 1, + padding: 40, + backgroundColor: Theme.COLOR_BG + }, + item: { + flexDirection: "row", + marginVertical: 10, + }, + title: { + color: Theme.COLOR_BLACK, + fontSize: 17, + paddingLeft: 10 + } +}) \ No newline at end of file diff --git a/src/components/screens/auth/SignIn.js b/src/components/screens/auth/SignIn.js new file mode 100644 index 0000000..28f6a69 --- /dev/null +++ b/src/components/screens/auth/SignIn.js @@ -0,0 +1,115 @@ +import React, { useContext, useEffect, useState } from 'react' +import { StyleSheet, Text, Keyboard } from 'react-native'; +import Input from '../../layouts/form/Input'; +import Container from './../../layouts/Container'; +import URL from '../../../utils/helpers/URL'; +import NavLink from '../../layouts/form/NavLink'; +import MButton from '../../layouts/form/MButton'; +import Theme from './../../../utils/helpers/Theme'; +import Helper from './../../../utils/helpers/Helper'; +import { StateContext, DispatchContext } from './../../../utils/context/MainContext'; +import ResponseLayout from '../../layouts/ResponseLayout'; +import AppAction from './../../../utils/context/actions/AppAction'; +import AuthAction from './../../../utils/context/actions/AuthAction'; +import Response from './../../../utils/helpers/Response'; +import DefineIcon from '../../layouts/icon/DefineIcon'; +import Icon from './../../layouts/icon/Icon'; +import { StatusBar } from 'expo-status-bar'; + +export default function SignIn() { + //global state + const { app } = useContext(StateContext) + const { authDispatch, appDispatch } = useContext(DispatchContext) + + //local state + const N_EMAIL = "email" + const N_PASSWORD = "password" + + const init = { + [N_EMAIL]: "", + [N_PASSWORD]: "" + } + const initTest = { + [N_EMAIL]: "", + [N_PASSWORD]: "" + } + + const [input, setInput] = useState(initTest) + const [error, setError] = useState(init) + + + //local method + + const onSubmit = async () => { + //validation + const errorArray = Helper.validateObject(input)//should pass an object with key:value + errorArray.forEach(item => { + return setError((pre) => ({ ...pre, [item[0]]: `Enter ${item[0]}` })) + }) + if (errorArray.length > 0) { + return + } + //hide keyboard + Keyboard && Keyboard.dismiss() + //setLoading(true) + new AppAction(appDispatch).START_LOADING() + try { + await new AuthAction(authDispatch).login(input.email.toLowerCase(), input.password) + //setLoading(false) + new AppAction(appDispatch).STOP_LOADING() + } catch (e) { + console.log(e); + //setLoading(false) + new AppAction(appDispatch).STOP_LOADING() + new AppAction(appDispatch).SET_RESPONSE(Response(false, e.message, "try again.", Theme.COLOR_DANGER, e)) + } + + } + + + + return ( + + + + Hey, Login Now + + + + + + } value={input.email} error={error?.email} onChangeText={(text) => Helper.onChange({ name: N_EMAIL, value: text, setInput: setInput, setError: setError })} label="Enter Your Email" /> + + } value={input.password} error={error?.password} onChangeText={(text) => Helper.onChange({ name: N_PASSWORD, value: text, setInput: setInput, setError: setError })} type="password" label="Enter Password" /> + + + + {/* while loading.. it should be disabled */} + {/* */} + + + + + ); +} + +const styles = StyleSheet.create({ + small_title: { + textAlign: "center", + marginVertical: 5, + paddingBottom: 5, + textDecorationLine: "underline", + textDecorationStyle: "solid" + }, + title: { + textAlign: "center", + fontWeight: "bold", + color: Theme.COLOR_PRIMARY, + fontSize: 25, + marginVertical: 15, + padding: 7 + }, + container: { + justifyContent: "center" + } +}) \ No newline at end of file diff --git a/src/components/screens/auth/SignUp.js b/src/components/screens/auth/SignUp.js new file mode 100644 index 0000000..d92214e --- /dev/null +++ b/src/components/screens/auth/SignUp.js @@ -0,0 +1,149 @@ + +import React, { useState, useContext } from 'react' +import { StyleSheet, Text, Keyboard } from 'react-native'; +import Container from './../../layouts/Container'; +import Helper from './../../../utils/helpers/Helper'; +import Input from '../../layouts/form/Input'; +import Theme from '../../../utils/helpers/Theme'; +import URL from './../../../utils/helpers/URL'; +import NavLink from './../../layouts/form/NavLink'; +import MButton from './../../layouts/form/MButton'; +import { StateContext, DispatchContext } from './../../../utils/context/MainContext'; +import AuthAction from './../../../utils/context/actions/AuthAction'; +import Response from './../../../utils/helpers/Response'; +import ResponseLayout from './../../layouts/ResponseLayout'; +import AppAction from './../../../utils/context/actions/AppAction'; +import DefineIcon from '../../layouts/icon/DefineIcon'; +import Icon from './../../layouts/icon/Icon'; +import { StatusBar } from 'expo-status-bar'; + +export default function SignUp() { + + //global state + const { app } = useContext(StateContext) + const { authDispatch, appDispatch } = useContext(DispatchContext) + + //local state + const N_EMAIL = "email" + const N_NAME = "name" + const N_PASSWORD = "pass" + const N_C_PASSWORD = "c_pass" + + const init = { + [N_EMAIL]: "", + [N_NAME]: "", + [N_PASSWORD]: "", + [N_C_PASSWORD]: "" + } + + const [input, setInput] = useState(init) + const [error, setError] = useState(init) + + + const onSubmit = async () => { + //validation + const errorArray = Helper.validateObject(input)//should pass an object with key:value + errorArray.forEach(item => { + let msge = `Enter ${item[0]}` + if (N_C_PASSWORD === item[0]) { + msge = `Enter Master Password Again` + } + return setError((pre) => ({ ...pre, [item[0]]: msge })) + }) + if (errorArray.length > 0) { + return + } + + //verify passsword + if (input[N_PASSWORD] !== input[N_C_PASSWORD]) { + setError({ [N_C_PASSWORD]: 'Password doesn\'t match' }) + return + } + + //hide keyboard + Keyboard && Keyboard.dismiss() + //start loading.. + new AppAction(appDispatch).START_LOADING() + //make email small later + const usr = { + ...input, + email: input[N_EMAIL].toLowerCase() + } + delete usr[N_C_PASSWORD] + + try { + const res = await new AuthAction(authDispatch).signup(usr) + console.log("res signup ", res) + new AppAction(appDispatch).STOP_LOADING() + } catch (e) { + console.log(e); + new AppAction(appDispatch).STOP_LOADING() + new AppAction(appDispatch).SET_RESPONSE(Response(false, e.message, "try again.", Theme.COLOR_DANGER, e)) + } + + } + + return ( + + + Create Account Now + + + + } + error={error?.email} + onChangeText={(text) => Helper.onChange({ name: N_EMAIL, value: text, setInput: setInput, setError: setError })} label="Enter Your Email" /> + + } + onChangeText={(text) => Helper.onChange({ name: N_NAME, value: text, setInput: setInput, setError: setError })} label="Enter Your Name" /> + + } + onChangeText={(text) => Helper.onChange({ name: N_PASSWORD, value: text, setInput: setInput, setError: setError })} type="password" label="Enter Master Password" /> + + } + onChangeText={(text) => Helper.onChange({ name: N_C_PASSWORD, value: text, setInput: setInput, setError: setError })} type="password" label="Confirm Master Password" /> + + + {/* while loading.. it should be disabled */} + You can't reset master password,so don't forget it. + + + + ); +} + +const styles = StyleSheet.create({ + btn: { + marginVertical: 12, + }, + small_title1: { + color: Theme.COLOR_PRIMARY, + textAlign: "center", + marginVertical: 5, + paddingBottom: 5, + textDecorationStyle: "solid" + }, + small_title: { + color: Theme.COLOR_PRIMARY, + textAlign: "center", + marginVertical: 5, + paddingBottom: 5, + textDecorationLine: "underline", + textDecorationStyle: "solid" + }, + title: { + textAlign: "center", + fontWeight: "bold", + color: Theme.COLOR_PRIMARY, + fontSize: 25, + marginVertical: 15, + padding: 7 + }, + container: { + justifyContent: "center" + } +}) \ No newline at end of file diff --git a/src/components/screens/dashboard/About.js b/src/components/screens/dashboard/About.js new file mode 100644 index 0000000..1322dd3 --- /dev/null +++ b/src/components/screens/dashboard/About.js @@ -0,0 +1,16 @@ +import React from 'react' +import { Text, View, Linking } from 'react-native' +import Container from '../../layouts/Container'; +import Theme from './../../../utils/helpers/Theme'; + +export default function About() { + const onpress = () => { + Linking.openURL("https://milon27.web.app/") + } + return ( + + Developed By: milon27 + Visit: https://milon27.web.app/ + + ) +} diff --git a/src/components/screens/dashboard/ContactDetail.js b/src/components/screens/dashboard/ContactDetail.js new file mode 100644 index 0000000..5c69885 --- /dev/null +++ b/src/components/screens/dashboard/ContactDetail.js @@ -0,0 +1,130 @@ +import React, { useContext, useEffect } from 'react' +import { Text, View, StyleSheet, Image, TouchableOpacity, Linking } from 'react-native' +import Container from './../../layouts/Container'; +import Theme from './../../../utils/helpers/Theme'; +import Icon from '../../layouts/icon/Icon'; +import DefineIcon from './../../layouts/icon/DefineIcon'; +import SingleRow from './SingleRow'; +import Helper from './../../../utils/helpers/Helper'; +import MButton from './../../layouts/form/MButton'; +import { useNavigation } from '@react-navigation/native'; +import URL from './../../../utils/helpers/URL'; +import OfflineListAction from './../../../utils/context/actions/OfflineListAction'; +import Define from '../../../utils/helpers/Define'; +import { DispatchContext } from '../../../utils/context/MainContext'; + +export default function ContactDetail({ route: { params } }) { + const { navigate, setOptions } = useNavigation() + + const { contact_listDispatch } = useContext(DispatchContext) + + useEffect(() => { + setOptions({ + title: "" + params.name, + headerRight: () => { + // hearto,heart + return { }} /> + } + }) + }, []) + + + const deleteNow = () => { + new OfflineListAction(contact_listDispatch).deleteItem(Define.CONTACTS, params.id) + navigate(URL.HOME_SCREEN) + } + + + return ( + + + + + {params?.pic ? : + {params?.name.slice(0, 2)} + } + + + {params.name} + + + + + {/* call icon */} + + + { Linking.openURL(`tel:${params.phone_code}${params.phone}`) }} + style={styles.iconbox}> + + Call + + { Linking.openURL(`sms://:${params.phone_code}${params.phone}`) }} + style={styles.iconbox}> + + Text + + { Linking.openURL(`https://meet.google.com/`) }} + style={styles.iconbox}> + + Video + + + {/* phone number box icon */} + + + + + + + { + navigate(URL.CREATE_PASSWORD, params) + } + } + color={Theme.COLOR_PRIMARY} title="Edit Contact" + /> + + + + ) +} + +const styles = StyleSheet.create({ + img: { + height: 100, + width: 100, + borderRadius: 100, + justifyContent: "center", + alignItems: "center", + }, + nameBox: { + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center" + }, + name: { + color: Theme.COLOR_BLACK, + fontSize: 30, + }, + iconbox: { + justifyContent: "center", + alignItems: "center" + }, + borderBottm: { + borderBottomColor: Theme.COLOR_GRAY, + borderBottomWidth: 0.5, + paddingVertical: 15 + }, + edit: { + position: "absolute", + bottom: 0, + right: 0, + } +}) \ No newline at end of file diff --git a/src/components/screens/dashboard/CreateCategory.js b/src/components/screens/dashboard/CreateCategory.js new file mode 100644 index 0000000..b3bd83b --- /dev/null +++ b/src/components/screens/dashboard/CreateCategory.js @@ -0,0 +1,52 @@ +import React, { useState, useContext } from 'react' +import { StyleSheet } from 'react-native' +import Container from './../../layouts/Container'; +import Input from './../../layouts/form/Input'; +import Icon from './../../layouts/icon/Icon'; +import DefineIcon from './../../layouts/icon/DefineIcon'; +import Helper from './../../../utils/helpers/Helper'; +import MButton from './../../layouts/form/MButton'; +import { StateContext } from './../../../utils/context/MainContext'; +import Theme from './../../../utils/helpers/Theme'; +import AxiosHelper from './../../../utils/helpers/AxiosHelper'; +import { StatusBar } from 'expo-status-bar'; +export default function CreateCategory() { + const { app } = useContext(StateContext) + const [title, setTitle] = useState("") + const [error, setError] = useState("") + + const onSubmit = async () => { + if (!title) { + setError("Enter category title.") + return + } + const userId = await Helper.getUserID() + let val = await AxiosHelper.addData('pass/create-cat', { title, user: userId }) + console.log("CreateCategory", val) + if (val.success) { + setTitle("") + Helper.Toast("" + val.title) + } else { + Helper.Toast("" + val.title) + } + } + + return ( + + + } + label="Enter Category Name" + onChangeText={(text) => setTitle(text)} + /> + + {/* while loading.. it should be disabled */} + + ) +} + +const styles = StyleSheet.create({ + container: { + justifyContent: "center" + } +}) diff --git a/src/components/screens/dashboard/CreatePassword.js b/src/components/screens/dashboard/CreatePassword.js new file mode 100644 index 0000000..8197a86 --- /dev/null +++ b/src/components/screens/dashboard/CreatePassword.js @@ -0,0 +1,228 @@ +import React, { useState, useEffect, useContext } from 'react' +import { Text, View, StyleSheet, Switch, ScrollView } from 'react-native' +import Icon from '../../layouts/icon/Icon'; +import Container from '../../layouts/Container'; +import DefineIcon from '../../layouts/icon/DefineIcon'; +import Helper from '../../../utils/helpers/Helper'; +import { DispatchContext, StateContext } from '../../../utils/context/MainContext'; +import Input from './../../layouts/form/Input'; +import MButton from './../../layouts/form/MButton'; +import Theme from './../../../utils/helpers/Theme'; +import DropDownPicker from 'react-native-dropdown-picker' +import AxiosHelper from './../../../utils/helpers/AxiosHelper'; +import AppAction from './../../../utils/context/actions/AppAction'; +import { StatusBar } from 'expo-status-bar'; +export default function CreatePassword({ route: { params } }) { + + const { app } = useContext(StateContext) + const { appDispatch } = useContext(DispatchContext) + + const [value, setValue] = useState([]); + const [open, setOpen] = useState(false); + + const N_TITLE = "title" + const N_URL = "url" + const N_PASSWORD = "password" + const N_EMAIL = "email" + const N_USERNAME = "username" + const N_USER = "user" + const N_CAT = "category" + const N_IS_FAV = "is_fav" + + const init = { + [N_TITLE]: "", + [N_URL]: "", + [N_EMAIL]: "", + [N_USERNAME]: "", + [N_PASSWORD]: "", + [N_USER]: "", + [N_IS_FAV]: false, + } + const error_init = { + [N_TITLE]: "", + [N_URL]: "", + [N_EMAIL]: "", + [N_USERNAME]: "", + [N_PASSWORD]: "", + } + const [cat, setCat] = useState([]) + const [input, setInput] = useState(init) + const [error, setError] = useState(error_init) + + //load cat + useEffect(() => { + const token = AxiosHelper.getSource() + const load = async () => { + try { + const uid = await Helper.getUserID() + const data = await AxiosHelper.getData(`pass/get-all-cat/${uid}/`, token) + if (data.success) { + //console.log("cat: ", data.object) + setCat(data.object) + } + } catch (e) { + console.log("error Create Password.js->", e); + } + } + load() + + return () => { + token.cancel() + } + }, [cat.length]) + + // local method + const onSubmit = async () => { + + //validation + const errorArray = Helper.validateObject({ + [N_TITLE]: input[N_TITLE], + [N_URL]: input[N_URL], + [N_EMAIL]: input[N_EMAIL], + [N_USERNAME]: input[N_USERNAME], + [N_PASSWORD]: input[N_PASSWORD] + }) + errorArray.forEach(item => { + return setError((pre) => ({ ...pre, [item[0]]: `Enter ${item[0]}` })) + }) + if (errorArray.length > 0) { + return + } + + let appac = new AppAction(appDispatch) + + const data = { + ...input, + [N_PASSWORD]: await Helper.encryptPass(input[N_PASSWORD]), + [N_USER]: await Helper.getUserID(), + [N_CAT]: value + } + + console.log("before sending to server : ", data) + appac.START_LOADING() + + let val = await AxiosHelper.addData('pass/create', data) + console.log("CreatePass response form server: ", val) + appac.STOP_LOADING() + if (val.success) { + setInput(init) + setValue([]) + Helper.Toast("" + val.title) + } else { + Helper.Toast("" + val.title) + } + + } + + return ( + + + + + } + label="Enter Password Title" + onChangeText={(text) => Helper.onChange({ name: N_TITLE, value: text, setInput, setError })} + /> + } + label="Enter Site URL" + onChangeText={(text) => Helper.onChange({ name: N_URL, value: text, setInput, setError })} + /> + + } + label="Enter Site Username" + onChangeText={(text) => Helper.onChange({ name: N_USERNAME, value: text, setInput, setError })} + /> + + } + label="Enter Email" + onChangeText={(text) => Helper.onChange({ name: N_EMAIL, value: text, setInput, setError })} + /> + + } + label="Enter Site Password" + onChangeText={(text) => Helper.onChange({ name: N_PASSWORD, value: text, setInput, setError })} + /> + + + Add To Fabourite + Helper.onChange({ name: N_IS_FAV, value: text, setInput: setInput, setError: setError })} /> + + + 0 ? cat.map(item => { + return { + label: item?.title, value: item._id + } + }) : []} + zIndex={1000} + listMode="SCROLLVIEW" + zIndexInverse={3000} + /> + + + + + {/* while loading.. it should be disabled */} + + + ) +} + +const styles = StyleSheet.create({ + img: { + width: 120, + height: 120, + borderRadius: 120 + }, + text: { + textAlign: "center", + marginVertical: 15, + fontSize: 17 + }, + selectImg: { + alignSelf: "center" + }, + justifyRow: { + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + marginTop: 15, + marginBottom: 5 + }, + dropDown: { + borderColor: Theme.COLOR_GRAY, + backgroundColor: Theme.COLOR_BG, + marginVertical: 8, + color: Theme.COLOR_BLACK + } +}) diff --git a/src/components/screens/dashboard/HomeScreen.js b/src/components/screens/dashboard/HomeScreen.js new file mode 100644 index 0000000..70f1249 --- /dev/null +++ b/src/components/screens/dashboard/HomeScreen.js @@ -0,0 +1,139 @@ +import React, { useContext, useState, useEffect } from 'react' +import { ActivityIndicator, FlatList, Text, TouchableOpacity, StyleSheet } from 'react-native'; +import Container from '../../layouts/Container'; +import Theme from '../../../utils/helpers/Theme'; +import SingleRow from './SingleRow'; +import AxiosHelper from './../../../utils/helpers/AxiosHelper'; +import SingleCat from './SingleCat'; +import { DispatchContext, StateContext } from './../../../utils/context/MainContext'; +import ListAction from './../../../utils/context/actions/ListAction'; +import Helper from './../../../utils/helpers/Helper'; +import AppAction from './../../../utils/context/actions/AppAction'; +import { useIsFocused } from "@react-navigation/native"; +import Icon from './../../layouts/icon/Icon'; +import DefineIcon from './../../layouts/icon/DefineIcon'; +import { useNavigation } from '@react-navigation/native'; +import URL from './../../../utils/helpers/URL'; +import { StatusBar } from 'expo-status-bar'; +export default function HomeScreen() { + const nav = useNavigation() + const isFocused = useIsFocused(); + + const [cat, setCat] = useState([]) + const [select, setSelect] = useState(cat[0]?._id) + const { app, pass } = useContext(StateContext) + const { appDispatch, passDispatch } = useContext(DispatchContext) + + //load category at once + + useEffect(() => { + // console.log("load category") + const token = AxiosHelper.getSource() + const load = async () => { + try { + const uid = await Helper.getUserID() + const data = await AxiosHelper.getData(`pass/get-all-cat/${uid}/`, token) + if (data.success) { + //console.log(data) + setCat(data.object) + setSelect(cat[0]?._id) + Helper.encryptPass("") + } + } catch (e) { + console.log("error HomeScreen.js->", e); + } + } + if (isFocused) { + load() + } + return () => { + token.cancel() + } + }, [isFocused, cat.length]) + + useEffect(() => { + // console.log("load password") + const listAc = new ListAction(passDispatch) + const token = listAc.getSource() + const load = async () => { + const appac = new AppAction(appDispatch) + try { + //load password.. + appac.START_LOADING() + const uid = await Helper.getUserID() + const val = await listAc.getAll(`pass/get-all/${uid}/${select}`, []) + //console.log("home pass val =", val) + appac.STOP_LOADING() + } catch (e) { + appac.STOP_LOADING() + console.log("error HomeScreen.js->", e); + } + } + if (isFocused && select) { + load() + } + + return () => { + token.cancel() + } + }, [isFocused, select, pass.length]) + + + const renderItem = ({ item }) => { + return + } + + const renderCategory = ({ item }) => { + return + } + + return ( + <> + + + + (String(item._id))} + ListEmptyComponent={} + showsVerticalScrollIndicator={false} + showsHorizontalScrollIndicator={false} + /> + + {app.loading ? : <>} + + No Password Found In this Category!} + keyExtractor={item => (String(item._id))} + showsVerticalScrollIndicator={false} + showsHorizontalScrollIndicator={false} + /> + + + { + nav.navigate(URL.CREATE_PASSWORD) + }} style={styles.buttonCallout} > + + + + ) +} + +const styles = StyleSheet.create({ + buttonCallout: { + position: 'absolute', + bottom: 20, + right: 25 + }, + icon: { + color: Theme.COLOR_PRIMARY + } +}) \ No newline at end of file diff --git a/src/components/screens/dashboard/SingleCat.js b/src/components/screens/dashboard/SingleCat.js new file mode 100644 index 0000000..4438547 --- /dev/null +++ b/src/components/screens/dashboard/SingleCat.js @@ -0,0 +1,30 @@ +import React from 'react' +import { StyleSheet, Image, Text, View, TouchableOpacity, Linking } from 'react-native' +import Theme from './../../../utils/helpers/Theme'; +import Helper from './../../../utils/helpers/Helper'; + +export default function SingleCat({ item, setSelect }) { + + return + { + setSelect(item?._id) + Helper.Toast(item.title) + }}> + {item.title} + + +} + +const styles = StyleSheet.create({ + item_container: { + flexDirection: "row", + alignItems: "center", + justifyContent: "space-between", + paddingHorizontal: 15, + paddingVertical: 8, + marginHorizontal: 5, + backgroundColor: Theme.COLOR_PRIMARY, + borderRadius: 30, + }, + +}) diff --git a/src/components/screens/dashboard/SingleRow.js b/src/components/screens/dashboard/SingleRow.js new file mode 100644 index 0000000..c101ae4 --- /dev/null +++ b/src/components/screens/dashboard/SingleRow.js @@ -0,0 +1,75 @@ +import React, { useState } from 'react' +import { StyleSheet, Image, Text, View, TouchableOpacity } from 'react-native' +import Theme from './../../../utils/helpers/Theme'; +import Helper from './../../../utils/helpers/Helper'; +import DefineIcon from './../../layouts/icon/DefineIcon'; +import Icon from '../../layouts/icon/Icon'; +import Clipboard from 'expo-clipboard'; +import PasswordModal from './../../layouts/modal/PasswordModal'; +import AreYouSureModal from './../../layouts/modal/AreYouSureModal'; + +export default function SingleRow({ item }) { + const [open, setOpen] = useState(false) + const [open1, setOpen1] = useState(false) + const onPress = () => { + setOpen(true) + } + + const onLongPress = () => { + //setOpen1(true) + } + const onDelete = () => { + //setOpen(false) + } + + return + {/* {console.log("row -> ", item)} */} + + + + + + + {/* {item?.url ? : } */} + + {/* + {item?.title.slice(0, 2)} + */} + + + {item?.url} + {item?.email} + + + + { + const pp = await Helper.decryptPass(item?.password) + Clipboard.setString(pp); + Helper.Toast("Password Copied To Clipboard") + }}> + + + + +} + +const styles = StyleSheet.create({ + item_container: { + flexDirection: "row", + alignItems: "center", + justifyContent: "space-between", + marginVertical: 8, + }, + img: { + height: 45, + width: 45, + borderRadius: 45 / 2, + justifyContent: "center", + alignItems: "center", + margin: 2, + marginRight: 10 + }, + icons: { + flexDirection: "row" + } +}) diff --git a/src/components/screens/favourite/Fav.js b/src/components/screens/favourite/Fav.js new file mode 100644 index 0000000..d613f9f --- /dev/null +++ b/src/components/screens/favourite/Fav.js @@ -0,0 +1,70 @@ +import React, { useContext, useEffect } from 'react' +import { View, Text, ActivityIndicator, FlatList } from 'react-native' +import { useIsFocused } from "@react-navigation/native"; +import { StateContext, DispatchContext } from './../../../utils/context/MainContext'; +import SingleRow from './../dashboard/SingleRow'; +import Theme from '../../../utils/helpers/Theme'; +import Container from './../../layouts/Container'; +import ListAction from './../../../utils/context/actions/ListAction'; +import AppAction from './../../../utils/context/actions/AppAction'; +import Helper from './../../../utils/helpers/Helper'; +import { StatusBar } from 'expo-status-bar'; +export default function Fav() { + + const isFocused = useIsFocused(); + const { app, pass } = useContext(StateContext) + const { appDispatch, passDispatch } = useContext(DispatchContext) + + useEffect(() => { + // console.log("load password") + const listAc = new ListAction(passDispatch) + const token = listAc.getSource() + const load = async () => { + const appac = new AppAction(appDispatch) + try { + //load password.. + appac.START_LOADING() + const uid = await Helper.getUserID() + const val = await listAc.getAll(`pass/get-all-fav/${uid}/`, []) + //console.log("fav pass val =", val) + appac.STOP_LOADING() + } catch (e) { + appac.STOP_LOADING() + console.log("error FavScreen.js->", e); + } + } + if (isFocused) { + load() + } + + return () => { + token.cancel() + } + }, [isFocused, pass.length]) + + const renderItem = ({ item }) => { + return + } + + return ( + + + {app.loading ? : <>} + + No Password Found In Favourite List!} + + keyExtractor={item => (String(item._id))} + showsVerticalScrollIndicator={false} + showsHorizontalScrollIndicator={false} + /> + + ) +} diff --git a/src/components/screens/splash/Splash.js b/src/components/screens/splash/Splash.js new file mode 100644 index 0000000..09e0e54 --- /dev/null +++ b/src/components/screens/splash/Splash.js @@ -0,0 +1,51 @@ +import React from 'react' +import { View, Text, StyleSheet, Image } from 'react-native' +import Theme from './../../../utils/helpers/Theme'; +import { StatusBar } from 'expo-status-bar'; + +export default function Splash() { + return ( + + + + {/* */} + + + + Password + Manager + + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: "center", + justifyContent: "center", + backgroundColor: Theme.COLOR_PRIMARY + }, + text: { + color: Theme.COLOR_WHITE, + fontSize: 35, + textAlign: "center" + }, + logo: { + width: 75, + height: 75, + borderRadius: 75 / 2, + + }, + logo_container: { + margin: 15, + padding: 8, + alignSelf: "center", + borderRadius: 75 / 2, + backgroundColor: "white" + }, +}) diff --git a/src/utils/context/MainContext.js b/src/utils/context/MainContext.js new file mode 100644 index 0000000..1fec56e --- /dev/null +++ b/src/utils/context/MainContext.js @@ -0,0 +1,29 @@ +import React, { createContext, useReducer } from 'react' +import AppReducer, { initAppState } from './reducers/AppReducer'; +import AuthReducer, { initAuthState } from './reducers/AuthReducer'; +import ListReducer, { initListState } from './reducers/ListReducer'; + +export const StateContext = createContext(); +export const DispatchContext = createContext(); + +export default function MainContext(props) { + const [auth, authDispatch] = useReducer(AuthReducer, initAuthState);//for student auth + const [app, appDispatch] = useReducer(AppReducer, initAppState);//for app state + const [pass, passDispatch] = useReducer(ListReducer, initListState);//for any kind of list + + + const global_state = { + auth, app, pass + } + const global_dispatch = { + authDispatch, appDispatch, passDispatch + } + + return ( + + + {props.children} + + + ) +} diff --git a/src/utils/context/actions/AppAction.js b/src/utils/context/actions/AppAction.js new file mode 100644 index 0000000..df871d9 --- /dev/null +++ b/src/utils/context/actions/AppAction.js @@ -0,0 +1,36 @@ +import Types from './Types'; + +class AppAction { + constructor(dispatch) { + this.dispatch = dispatch + } + //process + START_LOADING = () => { + this.dispatch({ + type: Types.START_LOADING, + }); + } + STOP_LOADING = () => { + this.dispatch({ + type: Types.STOP_LOADING, + }); + } + SET_RESPONSE = (response) => { + this.dispatch({ + type: Types.SET_RESPONSE, + payload: response + }); + } + REMOVE_RESPONSE = () => { + this.dispatch({ + type: Types.REMOVE_RESPONSE, + }); + } + RELOAD = () => { + this.dispatch({ + type: Types.RELOAD, + }); + } +} + +export default AppAction; \ No newline at end of file diff --git a/src/utils/context/actions/AuthAction.js b/src/utils/context/actions/AuthAction.js new file mode 100644 index 0000000..fe330dd --- /dev/null +++ b/src/utils/context/actions/AuthAction.js @@ -0,0 +1,128 @@ +import axios from "axios" +import Response from './../../helpers/Response'; +import Define from './../../helpers/Define'; +import Types from "./Types"; +//import AsyncStorage from '@react-native-community/async-storage'; +import AsyncStorage from '@react-native-async-storage/async-storage' + +class AuthAction { + constructor(dispatch) { + this.dispatch = dispatch + } + //isLoggedIn + IsLoggedIn = () => { + return new Promise(async (resolve, reject) => { + try { + //hit api get response + const res = await axios.get('auth/is-loggedin') + const resBoolean = res.data + //clear async storage if false + if (!resBoolean) { + await AsyncStorage.removeItem(Define.C_USER) + } + //update auth state + this.dispatch({ + type: Types.AUTH_STATE, + payload: resBoolean + }) + resolve(resBoolean) + } catch (e) { + resolve(false) + } + })//end promise + } + //Logout + Logout = () => { + //student/logout + return new Promise(async (resolve, reject) => { + try { + const res = await axios.get('auth/logout') + const { error, message, data } = res.data + if (error) { + reject(new Error(message)) + } else { + //logout success + //remove from localstorage/asyncstorage + await AsyncStorage.removeItem(Define.C_USER) + await AsyncStorage.removeItem(Define.AUTH_PASS) + //update UI + this.dispatch({ + type: Types.AUTH_LOGOUT + }) + //resolve promise + const response_ui = Response(true, "Logged Out Successful", message, Define.BT_SUCCESS, data) + resolve(response_ui) + } + } catch (e) { + reject(new Error(e.message)) + } + }) + } + + //login student/user + login = (email, password) => { + return new Promise(async (resolve, reject) => { + try { + //hit api get response + const res = await axios.post('auth/login', { email, pass: password }) + const { error, message, data } = res.data + //console.log("res.data", res.data) + if (error) { + reject(new Error(message)) + } else { + //login success + //save to localstorage + //console.log("login response", data) + await AsyncStorage.setItem(Define.C_USER, JSON.stringify(data)) + await AsyncStorage.setItem(Define.AUTH_PASS, password) + + //update UI + this.dispatch({ + type: Types.AUTH_LOGIN, + payload: data//user object + }) + //resolve promise + const response_ui = Response(true, "Logged In Successful", message, Define.BT_SUCCESS, data) + resolve(response_ui) + } + } catch (e) { + reject(new Error(e.message)) + } + })//end promise + } + //signup a user/student + //@param student object{student_id,name,email,phone,parents_phone,password,present_address,photo_url} + signup = (student_obj) => { + return new Promise(async (resolve, reject) => { + try { + //hit api get response + const res = await axios.post('auth/signup', student_obj) + const { error, message, data } = res.data + if (error) { + console.log("we are here..1" + message); + reject(new Error(message)) + } else { + //login success + //save to localstorage + //delete data.token + await AsyncStorage.setItem(Define.C_USER, JSON.stringify(data)) + await AsyncStorage.setItem(Define.AUTH_PASS, student_obj.pass) + //update UI + this.dispatch({ + type: Types.AUTH_SIGNUP, + payload: data//user object + }) + //resolve promise + const response_ui = Response(true, "SignUP Successful", message, Define.BT_SUCCESS, data) + resolve(response_ui) + } + } catch (e) { + console.log("we are here.." + e.message); + reject(new Error(e.message)) + } + })//end promise + } + +} + +export default AuthAction \ No newline at end of file diff --git a/src/utils/context/actions/ListAction.js b/src/utils/context/actions/ListAction.js new file mode 100644 index 0000000..60a4552 --- /dev/null +++ b/src/utils/context/actions/ListAction.js @@ -0,0 +1,130 @@ +import axios from 'axios' +import Response from '../../helpers/Response'; +import Types from './Types'; +import Define from './../../helpers/Define'; + +class ListAction { + constructor(dispatch) { + this.dispatch = dispatch; + } + getSource = () => { + this.source = axios.CancelToken.source(); + return this.source + }//return token to cancel the request + + //get all data + getAll = async (url, errorObj = {}) => { + return new Promise((resolve, reject) => { + axios.get(`${url}` + , { + cancelToken: this.source.token + } + ).then(res => { + const { error, message, data } = res.data + if (error === false) {//no error + //dispatch the global state + this.dispatch({ + type: Types.GET_DATA, + payload: data//an array + }); + resolve(Response(true, "success", message, Define.BT_SUCCESS, data)); + } else { + this.dispatch({ + type: Types.GET_DATA, + payload: errorObj//an array + }); + resolve(Response(false, "failed", message, Define.BT_DANGER, errorObj)); + } + }).catch(e => { + if (axios.isCancel(e)) { + this.dispatch({ + type: Types.GET_DATA, + payload: errorObj//an array + }); + resolve(Response(false, "canceled the request", e.message, Define.BT_DANGER, errorObj)); + } else { + this.dispatch({ + type: Types.GET_DATA, + payload: errorObj//an array + }); + resolve(Response(false, "failed", e.message, Define.BT_DANGER, errorObj)); + } + }); + }); + }//end get all(make sure you got a response (object type) ) + addData = (url, newdata) => { + return new Promise((resolve, reject) => { + axios.post(url, newdata).then((res) => { + const { error, message, data } = res.data + if (error === false) {//no error + //dispatch the global state + this.dispatch({ + type: Types.ADD_DATA, + payload: data//a newly created object + }); + resolve(Response(true, "success", message, Define.BT_SUCCESS, data)); + } else {//error + resolve(Response(false, "failed", message, Define.BT_DANGER)); + } + }).catch((e) => { + resolve(Response(false, "failed", e.message, Define.BT_DANGER)); + }) + }); + }//end add data + + + deleteData = (url, id) => { + return new Promise((resolve, reject) => { + + // this.dispatch({ + // type: Types.DELETE_DATA, + // payload: id//a newly created object + // }); + // resolve(Response(true, "success", "message", Define.BT_SUCCESS, "")); + + axios.delete(url).then((res) => { + const { error, message, data } = res.data + + if (error === false) {//no error + //dispatch the global state + console.log("doing dispatch ->", res.data) + this.dispatch({ + type: Types.DELETE_DATA, + payload: id//a newly created object + }); + resolve(Response(true, "success", message, Define.BT_SUCCESS, data)); + } else {//error + resolve(Response(false, "failed", message, Define.BT_DANGER)); + } + }).catch((e) => { + resolve(Response(false, "failed", e.message, Define.BT_DANGER)); + }) + }); + } + + // updateData = (url, updateData) => { + // return new Promise((resolve, reject) => { + // axios.put(url, updateData).then((res) => { + // const { error, message, response } = res.data + // if (error === false) { + // //dispatch the global state + // this.dispatch({ + // type: Types.UPDATE_DATA, + // payload: response + // }); + // resolve(Response(true, "update succes", message, Define.BT_SUCCESS, response)); + // } else { + // reject(new Error(message)); + // } + // }).catch((e) => { + // console.error("erroe: ", e) + // reject(e); + // }) + // }); + // }//end update data + + +} + + +export default ListAction; \ No newline at end of file diff --git a/src/utils/context/actions/OfflineListAction.js b/src/utils/context/actions/OfflineListAction.js new file mode 100644 index 0000000..8dfcfb1 --- /dev/null +++ b/src/utils/context/actions/OfflineListAction.js @@ -0,0 +1,86 @@ +import Response from '../../helpers/Response'; +import Types from './Types'; +import Define from '../../helpers/Define'; +//import AsyncStorage from '@react-native-community/async-storage'; +import AsyncStorage from '@react-native-async-storage/async-storage' + +class OfflineListAction { + constructor(dispatch) { + this.dispatch = dispatch; + } + //get all data + getAll = (key) => { + return new Promise(async (resolve, reject) => { + let data = await AsyncStorage.getItem(key) + if (data) { + data = data + //dispatch the global state + this.dispatch({ + type: Types.GET_DATA, + payload: JSON.parse(data)//an array + }); + resolve(Response(true, "success", "", Define.BT_SUCCESS, JSON.parse(data))); + } else { + reject() + } + }); + }//end get all(make sure you got a response (object type) ) + addData = (key, newdata) => { + return new Promise(async (resolve, reject) => { + let data = await AsyncStorage.getItem(key) + let update = [newdata] + if (data) { + update = [newdata, ...JSON.parse(data)] + } + await AsyncStorage.setItem(key, JSON.stringify(update)) + //dispatch the global state + this.dispatch({ + type: Types.ADD_DATA, + payload: newdata//a newly created object + }); + resolve(Response(true, "success", "", Define.BT_SUCCESS, newdata)); + }); + }//end add data + + updateItem = async (key, obj) => { + let data = await AsyncStorage.getItem(key) + + if (data) { + let list = [...JSON.parse(data)] + list = list.map(item => { + if (item.id === obj.id) { + return obj + } else { + return item + } + }) + await AsyncStorage.setItem(key, JSON.stringify(list)) + // + this.dispatch({ + type: Types.UPDATE_DATA, + payload: { + id_field: "id", + obj: obj + } + }); + } + } + + deleteItem = async (key, id) => { + let data = await AsyncStorage.getItem(key) + + if (data) { + let list = [...JSON.parse(data)] + list = list.filter(item => item.id !== id) + await AsyncStorage.setItem(key, JSON.stringify(list)) + // + this.dispatch({ + type: Types.DELETE_DATA, + payload: id//deleted id + }); + } + } +} + + +export default OfflineListAction; \ No newline at end of file diff --git a/src/utils/context/actions/Types.js b/src/utils/context/actions/Types.js new file mode 100644 index 0000000..db1f746 --- /dev/null +++ b/src/utils/context/actions/Types.js @@ -0,0 +1,29 @@ +const Types = { + //auth + AUTH_LOGIN: "AUTH_LOGIN", + AUTH_LOGOUT: "AUTH_LOGOUT", + AUTH_SIGNUP: "AUTH_SIGNUP", + AUTH_STATE: "AUTH_STATE", + //app global state + START_LOADING: "START_LOADING", + STOP_LOADING: "STOP_LOADING", + SET_RESPONSE: "SET_RESPONSE", + REMOVE_RESPONSE: "REMOVE_RESPONSE", + RELOAD: "RELOAD", + //list + GET_DATA: "GET_DATA", + ADD_DATA: "ADD_DATA", + UPDATE_DATA: "UPDATE_DATA", + DELETE_DATA: "DELETE_DATA", + //optional + DARK_THEME: "DARK_THEME", + //payload structure + UPDATE_PAYLOAD: (id_field, obj) => { + return { + obj, + id_field + } + } +} + +export default Types; \ No newline at end of file diff --git a/src/utils/context/reducers/AppReducer.js b/src/utils/context/reducers/AppReducer.js new file mode 100644 index 0000000..216ad84 --- /dev/null +++ b/src/utils/context/reducers/AppReducer.js @@ -0,0 +1,42 @@ + +import Types from './../actions/Types'; +import Response from './../../helpers/Response'; + +export const initAppState = { + loading: false, + response: Response(null, null, null, null), + reload: false//just update it so it will effect useEffect Hook so will reload automatically:) +}; + +const AppReducer = (app_state, action) => { + switch (action.type) { + case Types.START_LOADING: + return { + ...app_state, + loading: true + } + case Types.STOP_LOADING: + return { + ...app_state, + loading: false + } + case Types.SET_RESPONSE: + return { + ...app_state, + response: action.payload//full response object + } + case Types.REMOVE_RESPONSE: + return { + ...app_state, + response: Response(null, null, null, null)//full response object + } + case Types.RELOAD: + return { + ...app_state, + reload: !app_state.reload//just change it will auto call useEffect + } + default: + return app_state; + } +} +export default AppReducer; \ No newline at end of file diff --git a/src/utils/context/reducers/AuthReducer.js b/src/utils/context/reducers/AuthReducer.js new file mode 100644 index 0000000..41a4db4 --- /dev/null +++ b/src/utils/context/reducers/AuthReducer.js @@ -0,0 +1,21 @@ +import Types from "../actions/Types" + +export const initAuthState = { +} + +const AuthReducer = (state, action) => { + if (action.type === Types.AUTH_LOGIN) { + let loggedInUser = action.payload//get user object + return { ...loggedInUser, logged_in: true } + } else if (action.type === Types.AUTH_SIGNUP) { + let newUser = action.payload//get user object + return { ...newUser, logged_in: true } + } else if (action.type === Types.AUTH_LOGOUT) { + return { ...initAuthState } + } else if (action.type === Types.AUTH_STATE) { + return { ...state, logged_in: action.payload } + } else { + return state + } +} +export default AuthReducer \ No newline at end of file diff --git a/src/utils/context/reducers/ListReducer.js b/src/utils/context/reducers/ListReducer.js new file mode 100644 index 0000000..c959d7a --- /dev/null +++ b/src/utils/context/reducers/ListReducer.js @@ -0,0 +1,31 @@ + +import Types from '../actions/Types'; +export const initListState = []; + +const ListReducer = (state, action) => { + switch (action.type) { + case Types.GET_DATA: + return [...action.payload];//return an array + case Types.ADD_DATA: + return [action.payload, ...state];//return array with new object + case Types.UPDATE_DATA: + state = state.map(itm => { + const id_field = action.payload.id_field + if (itm[id_field] === action.payload.obj[id_field]) + return action.payload.obj; + else + return itm; + }); + return state;//return array with updated object + case Types.DELETE_DATA: + state = state.filter(itm => { + return itm?._id !== action.payload//id + }); + console.log(state.length) + return state;//return array with updated object + default: + return state;//default arry + } + +} +export default ListReducer; \ No newline at end of file diff --git a/src/utils/helpers/AxiosHelper.js b/src/utils/helpers/AxiosHelper.js new file mode 100644 index 0000000..3ad0417 --- /dev/null +++ b/src/utils/helpers/AxiosHelper.js @@ -0,0 +1,115 @@ +import axios from "axios"; +import Define from "./Define"; +import Response from './Response'; + +const AxiosHelper = { + + getSource: () => { + return axios.CancelToken.source() + }, //return token to cancel the request + + //get the object + getData: (url, source) => { + return new Promise((resolve, reject) => { + axios + .get(`${url}`, { + cancelToken: source.token, + }) + .then((res) => { + const { error, message, data } = res.data; + if (error === false) { + //no error + resolve( + Response(true, "success", message, Define.BT_SUCCESS, data) + ); + } else { + resolve( + Response(false, "failed", message, Define.BT_DANGER) + ); + } + }) + .catch((e) => { + if (axios.isCancel(e)) { + resolve( + Response(false, "failed", "canceled the request", Define.BT_DANGER) + ); + } else { + resolve( + Response(false, "failed", e.message, Define.BT_DANGER) + ); + } + }); + }); + },//get end + + + addData: (url, newdata) => { + return new Promise((resolve, reject) => { + axios + .post(url, newdata) + .then((res) => { + const { error, message, data } = res.data; + if (error === false) { + //no error + resolve( + Response(true, "success", message, Define.BT_SUCCESS, data) + ); + } else { + //error + resolve( + Response(false, "failed", message, Define.BT_DANGER) + ); + } + }) + .catch((e) => { + resolve( + Response(false, "failed", e.message, Define.BT_DANGER) + ); + }); + }); + }, //end add data + + updateData: (url, updateData) => { + return new Promise((resolve, reject) => { + axios.put(url, updateData).then((res) => { + const { error, message, data } = res.data + if (error === false) { + resolve(Response(true, "update succes", message, Define.BT_SUCCESS, data)); + } else { + resolve( + Response(false, "failed", message, Define.BT_DANGER) + ); + } + }).catch((e) => { + console.error("erroe: ", e) + resolve( + Response(false, "failed", e.message, Define.BT_DANGER) + ); + }) + }); + },//end update data + + deleteData: (url) => { + return new Promise((resolve, reject) => { + axios.delete(url).then((res) => { + const { error, message, data } = res.data + if (error === false) { + resolve(Response(true, "delete succes", message, Define.BT_SUCCESS, {})); + } else { + resolve( + Response(false, "failed", message, Define.BT_DANGER) + ); + } + }).catch((e) => { + console.error("erroe: ", e) + resolve( + Response(false, "failed", e.message, Define.BT_DANGER) + ); + }) + }); + },//end update data + + + +} +export default AxiosHelper \ No newline at end of file diff --git a/src/utils/helpers/Define.js b/src/utils/helpers/Define.js new file mode 100644 index 0000000..87e866b --- /dev/null +++ b/src/utils/helpers/Define.js @@ -0,0 +1,19 @@ +const Define = { + API_BASE_URL: "https://password-manager-api-27.herokuapp.com/", + //time format + FORMAT_DATE: "DD-MM-YYYY", + //bootstrap color + BT_PRIMARY: "primary", + BT_DANGER: "danger", + BT_SUCCESS: "success", + BT_WARNING: "warning", + BT_INFO: "info", + //define + NOT_SET: "NOT_SET", + //local storage + C_USER: "AUTH_C_USER", + AUTH_PASS: "AUTH_PASS", + CONTACTS: "CONTACTS", + +} +export default Define \ No newline at end of file diff --git a/src/utils/helpers/Helper.js b/src/utils/helpers/Helper.js new file mode 100644 index 0000000..0385dd2 --- /dev/null +++ b/src/utils/helpers/Helper.js @@ -0,0 +1,90 @@ +import Theme from './Theme'; +import { + ToastAndroid, + Platform, + AlertIOS, +} from 'react-native'; +import AsyncStorage from '@react-native-async-storage/async-storage'; +import Define from './Define'; +import CryptoES from 'crypto-es'; + +const Helper = { + validateField: (...arr) => { + const n_arr = arr.filter(itm => { + if (itm && itm !== null && itm !== undefined) { + return true + } + }) + if (n_arr.length === arr.length) { + return true;//valid all field + } else { + return false;//invalid some field + } + },//validateField + + validateObject: (input) => {//{key:value} + + const n_arr = Object.entries(input).filter((value) => { + if (value[1] && value[1] !== null && value[1] !== undefined && value[1] !== "") {//they are valid so ignore them + return false + } + return true + }) + //[["name", null], ["email", "milon@g.com"]] + //[["name", null]]-> key:value[0] + return n_arr + },//validateObject + + Toast: (msg) => { + if (Platform.OS === 'android') { + ToastAndroid.show(msg, ToastAndroid.SHORT) + } else { + AlertIOS.alert(msg); + } + },//Toast + onChange: ({ name, value, setInput, setError }) => { + setInput((input) => ({ ...input, [name]: value })) + //check error & remove error + if (value !== "") { + setError((pre) => ({ ...pre, [name]: "" })) + } else { + setError((pre) => ({ ...pre, [name]: "This field is required" })) + } + }, + ramdomColor: () => { + const colors = ["#CD5C5C", "#FFA07A", "#40E0D0", Theme.COLOR_TEST, "#ffcc33", "#9FE2BF", "#6495ED", "#808000", "#9f8b80", Theme.COLOR_PRIMARY, "#2baabf"] + const max = colors.length + const random = Math.round(Math.random() * max) + return colors[random] + }, + getUser: async () => { + const u = JSON.parse(await AsyncStorage.getItem(Define.C_USER)) + return u + }, + getUserID: async () => { + const u = JSON.parse(await AsyncStorage.getItem(Define.C_USER)) + //console.log(u?._id) + return u?._id + }, + getPass: async () => { + const u = await AsyncStorage.getItem(Define.AUTH_PASS) + return u + }, + encryptPass: async (pass) => { + const plainPassAsKey = await AsyncStorage.getItem(Define.AUTH_PASS) + const hash = CryptoES.SHA256(plainPassAsKey).toString(); + const encrypted = CryptoES.AES.encrypt(pass, hash); + return encrypted.toString(); + // const decrypted = CryptoES.AES.decrypt(encrypted.toString(), key256Bits.toString()); + // return decrypted.toString(CryptoES.enc.Utf8) + }, + decryptPass: async (encrypted_str) => { + const plainPassAsKey = await AsyncStorage.getItem(Define.AUTH_PASS) + const hash = CryptoES.SHA256(plainPassAsKey).toString(); + + const decrypted = CryptoES.AES.decrypt(encrypted_str, hash); + return decrypted.toString(CryptoES.enc.Utf8) + } +} + +export default Helper \ No newline at end of file diff --git a/src/utils/helpers/Response.js b/src/utils/helpers/Response.js new file mode 100644 index 0000000..861fb46 --- /dev/null +++ b/src/utils/helpers/Response.js @@ -0,0 +1,4 @@ +const Response = (success, title, desc, type, object = {}) => { + return { success: success, title: title, desc: desc, type: type, object: object } +}//Response(false/true, "message title" , "message description", "danger-bootstrap color") +export default Response; \ No newline at end of file diff --git a/src/utils/helpers/Theme.js b/src/utils/helpers/Theme.js new file mode 100644 index 0000000..b2d1299 --- /dev/null +++ b/src/utils/helpers/Theme.js @@ -0,0 +1,23 @@ +const Theme2 = { + COLOR_PRIMARY: "#314e52",//deep green + COLOR_SECONDARY: "#f7f6e7",//light yellow + COLOR_ACCENT: "#f2a154",//orange + COLOR_BG: "#edeef7",//off white + COLOR_GRAY: "#909090",//gray + COLOR_WHITE: "#fff", + COLOR_BLACK: "#000", + COLOR_DANGER: "red", + COLOR_TEST: "#29c074", +} +const Theme = { + COLOR_PRIMARY: "#f2a154",// + COLOR_ACCENT: "#f2a154",// + COLOR_BG: "#1F2933",// + COLOR_GRAY: "#787A91",// + COLOR_WHITE: "#000", + COLOR_BLACK: "#fff", + COLOR_DANGER: "red", + STATUS_BAR: "light", + STATUS_BAR_ALT: "dark" +} +export default Theme \ No newline at end of file diff --git a/src/utils/helpers/URL.js b/src/utils/helpers/URL.js new file mode 100644 index 0000000..88f2a43 --- /dev/null +++ b/src/utils/helpers/URL.js @@ -0,0 +1,12 @@ +const URL = { + HOME_NAV: "Home", + SIGN_IN: "Sign In", + SIGN_UP: "Sign Up", + HOME_SCREEN: "Home Screen", + CONTACT_DETAIL: "Contact Details", + CREATE_PASSWORD: "Create Password", + FAV_PASSWORD: "Favourite Password", + CREATE_CATEGORY: "Create Category", + ABOUT: "About", +} +export default URL \ No newline at end of file diff --git a/src/utils/helpers/hooks/useAsyncstorage.js b/src/utils/helpers/hooks/useAsyncstorage.js new file mode 100644 index 0000000..ef0e76e --- /dev/null +++ b/src/utils/helpers/hooks/useAsyncstorage.js @@ -0,0 +1,36 @@ +import React, { useState } from 'react' +//import { AsyncStorage } from '@react-native-community/async-storage'; +import AsyncStorage from '@react-native-async-storage/async-storage' +/** + * const [user,setUser]=useAsyncStorage('user',{}) + * const [user,setUser]=useAsyncStorage('user',()=>{}) + */ + +const PREFIX = "contacts-" +export default function useAsyncStorage(key, initValue) { + + const prefixed_key = PREFIX + key + + const [user, setUser] = useState(async () => { + if (await AsyncStorage.getItem(prefixed_key) != null) { + console.log("not null"); + return JSON.parse(await AsyncStorage.getItem(prefixed_key)) + } + if (typeof initValue === 'function') { + console.log("function"); + return initValue() + } else { + console.log("not function"); + return initValue + } + }) + + useEffect(() => { + const load = async () => { + await AsyncStorage.setItem(prefixed_key, JSON.stringify(initValue)) + } + load() + }, [prefixed_key, state]) + + return [user, setUser] +} diff --git a/src/utils/helpers/hooks/useLocalstorage.js b/src/utils/helpers/hooks/useLocalstorage.js new file mode 100644 index 0000000..ca9bb51 --- /dev/null +++ b/src/utils/helpers/hooks/useLocalstorage.js @@ -0,0 +1,31 @@ +import React, { useState } from 'react' +/** + * const [user,setUser]=useLocalStorage('user',{}) + * const [user,setUser]=useLocalStorage('user',()=>{}) + */ + +const PREFIX = "contacts-" +export default function useLocalstorage(key, initValue) { + + const prefixed_key = PREFIX + key + + const [user, setUser] = useState(() => { + if (localStorage.getItem(prefixed_key) != null) { + console.log("not null"); + return JSON.parse(localStorage.getItem(prefixed_key)) + } + if (typeof initValue === 'function') { + console.log("function"); + return initValue() + } else { + console.log("not function"); + return initValue + } + }) + + useEffect(() => { + localStorage.setItem(prefixed_key, JSON.stringify(initValue)) + }, [prefixed_key, state]) + + return [user, setUser] +} diff --git a/step.follow.md b/step.follow.md new file mode 100644 index 0000000..37784dd --- /dev/null +++ b/step.follow.md @@ -0,0 +1,101 @@ +# go to android folder + +>> step-1: create local.properties file + +``` + sdk.dir=C\:\\Users\\milon\\AppData\\Local\\Android\\Sdk +``` + +>> step-2: for emulator you need to use android studio(run the android project first time) + +
+ +>> step-3: usb debuging on psyiscal device + +``` +Ensure that both + + Developer Options -> USB debugging + Developer Options -> USB debugging (Security settings) + +``` + +>> step-4: create folder structur + +``` +/root-> + /src-> + app.js + /assets + /components-> + /layouts + /navigation + /screens + /utils + /context + /actions + /reducers + MainContext.js + /helpers + /hooks + useLocalStorage.js + Define.js + Helper.js + Response.js + URL.js + +``` + + +>> step-5: setup react navigation navigation + +* Instalation + +```` + npm install @react-navigation/native + npm install react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view + +```` +* setting native navigation in IOS +``` + npx pod-install ios +``` + +* import gesture at the top of your entry file, such as index.js or App.js and wrap everything in NavigationContainer +``` +import 'react-native-gesture-handler'; +import * as React from 'react'; +import { NavigationContainer } from '@react-navigation/native'; + +export default function App() { + return ( + {/* Rest of your app code */} + ); +} + + +``` + +## note : you may got error: try to clear the cache,or run on android studio + +``` +npx react-native start --reset-cache +``` + +* Create Navigator + * install stack navigator + * install drawer navigator +``` +npm i @react-navigation/stack +npm i @react-navigation/drawer +``` + +for cookie enable + +``` +npm i @react-native-community/cookies + +npx react-native link @react-native-community/cookies + +Add “import CookieManager from '@react-native-community/cookies’;” to App.js +``` \ No newline at end of file