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