From 26b3db25f08811df3b7a91a4c1d9899c273f85cc Mon Sep 17 00:00:00 2001 From: Keannu Bernasol Date: Mon, 3 Jul 2023 21:22:31 +0800 Subject: [PATCH 01/15] Added functional registration --- App.tsx | 26 ++- package-lock.json | 199 +++++++++++++++++- package.json | 7 +- src/components/Api/Api.tsx | 73 +++++++ src/components/Button/Button.tsx | 23 ++ src/components/Button/DrawerButton.tsx | 6 +- .../DrawerSettings/CustomDrawerContent.tsx | 8 +- .../DrawerSettings/DrawerScreenSettings.tsx | 2 +- src/components/IsNumber/IsNumber.tsx | 8 + src/components/ParseError/ParseError.tsx | 9 + src/features/redux/Store/Store.tsx | 14 ++ .../redux/slices/AuthSlice/AuthSlice.tsx | 42 ++++ src/icons/AddIcon/AddIcon.tsx | 3 +- src/icons/AppIcon/AppIcon.tsx | 3 +- src/icons/HomeIcon/HomeIcon.tsx | 3 +- src/icons/LoginIcon/LoginIcon.tsx | 3 +- src/icons/LogoutIcon/LogoutIcon.tsx | 3 +- src/icons/SignupIcon/SignupIcon.tsx | 3 +- src/icons/UserIcon/UserIcon.tsx | 3 +- src/interfaces/Interfaces.tsx | 7 + src/routes/Login/Login.tsx | 89 +++++++- src/routes/Register/Register.tsx | 175 ++++++++++++++- src/styles.tsx | 27 ++- 23 files changed, 679 insertions(+), 57 deletions(-) create mode 100644 src/components/Api/Api.tsx create mode 100644 src/components/Button/Button.tsx create mode 100644 src/components/IsNumber/IsNumber.tsx create mode 100644 src/components/ParseError/ParseError.tsx create mode 100644 src/features/redux/Store/Store.tsx create mode 100644 src/features/redux/slices/AuthSlice/AuthSlice.tsx diff --git a/App.tsx b/App.tsx index 9518027..3ec14e5 100644 --- a/App.tsx +++ b/App.tsx @@ -1,6 +1,8 @@ import "react-native-gesture-handler"; import { NavigationContainer } from "@react-navigation/native"; import { createDrawerNavigator } from "@react-navigation/drawer"; +import { Provider } from "react-redux"; +import store from "./src/features/redux/Store/Store"; import CustomDrawerContent from "./src/components/DrawerSettings/CustomDrawerContent"; import DrawerScreenSettings from "./src/components/DrawerSettings/DrawerScreenSettings"; @@ -13,16 +15,18 @@ const Drawer = createDrawerNavigator(); export default function App() { return ( - - - - - - - + + + + + + + + + ); } diff --git a/package-lock.json b/package-lock.json index 781e9d8..68ea4c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8 +8,11 @@ "name": "stude_frontend", "version": "1.0.0", "dependencies": { + "@react-native-async-storage/async-storage": "1.17.11", "@react-navigation/drawer": "^6.6.3", "@react-navigation/native-stack": "^6.9.13", + "@reduxjs/toolkit": "^1.9.5", + "axios": "^1.4.0", "expo": "~48.0.18", "expo-status-bar": "~1.4.4", "react": "18.2.0", @@ -18,7 +21,9 @@ "react-native-reanimated": "~2.14.4", "react-native-safe-area-context": "4.5.0", "react-native-screens": "~3.20.0", - "react-native-svg": "13.4.0" + "react-native-svg": "13.4.0", + "react-redux": "^8.1.1", + "redux": "^4.2.1" }, "devDependencies": { "@babel/core": "^7.20.0", @@ -3653,6 +3658,17 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@react-native-async-storage/async-storage": { + "version": "1.17.11", + "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.17.11.tgz", + "integrity": "sha512-bzs45n5HNcDq6mxXnSsOHysZWn1SbbebNxldBXCQs8dSvF8Aor9KCdpm+TpnnGweK3R6diqsT8lFhX77VX0NFw==", + "dependencies": { + "merge-options": "^3.0.4" + }, + "peerDependencies": { + "react-native": "^0.0.0-0 || 0.60 - 0.71 || 1000.0.0" + } + }, "node_modules/@react-native-community/cli": { "version": "10.2.2", "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-10.2.2.tgz", @@ -5044,6 +5060,29 @@ "nanoid": "^3.1.23" } }, + "node_modules/@reduxjs/toolkit": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz", + "integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==", + "dependencies": { + "immer": "^9.0.21", + "redux": "^4.2.1", + "redux-thunk": "^2.4.2", + "reselect": "^4.1.8" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18", + "react-redux": "^7.2.1 || ^8.0.2" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, "node_modules/@segment/loosely-validate-event": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@segment/loosely-validate-event/-/loosely-validate-event-2.0.0.tgz", @@ -5097,6 +5136,15 @@ "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.41.tgz", "integrity": "sha512-ewXv/ceBaJprikMcxCmWU1FKyMAQ2X7a9Gtmzw8fcg2kIePI1crERDM818W+XYrxqdBBOdlf2rm137bU+BltCA==" }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", @@ -5126,14 +5174,12 @@ "node_modules/@types/prop-types": { "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "dev": true + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, "node_modules/@types/react": { "version": "18.0.38", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.38.tgz", "integrity": "sha512-ExsidLLSzYj4cvaQjGnQCk4HFfVT9+EZ9XZsQ8Hsrcn8QNgXtpZ3m9vSIC2MWtx7jHictK6wYhQgGh6ic58oOw==", - "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -5143,14 +5189,18 @@ "node_modules/@types/scheduler": { "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", - "dev": true + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" }, "node_modules/@types/stack-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, "node_modules/@types/yargs": { "version": "15.0.15", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.15.tgz", @@ -5446,6 +5496,29 @@ "node": ">= 4.5.0" } }, + "node_modules/axios": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/babel-core": { "version": "7.0.0-bridge.0", "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", @@ -6592,8 +6665,7 @@ "node_modules/csstype": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", - "dev": true + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, "node_modules/dag-map": { "version": "1.0.2", @@ -7644,6 +7716,25 @@ "node": ">=0.4.0" } }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/fontfaceobserver": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/fontfaceobserver/-/fontfaceobserver-2.3.0.tgz", @@ -8140,6 +8231,15 @@ "node": ">=4.0" } }, + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/import-fresh": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", @@ -8419,6 +8519,14 @@ "node": ">=8" } }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -9649,6 +9757,17 @@ "resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz", "integrity": "sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA==" }, + "node_modules/merge-options": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz", + "integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==", + "dependencies": { + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -11659,6 +11778,11 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -11976,6 +12100,49 @@ "async-limiter": "~1.0.0" } }, + "node_modules/react-redux": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.1.tgz", + "integrity": "sha512-5W0QaKtEhj+3bC0Nj0NkqkhIv8gLADH/2kYFMTHxCVqQILiWzLv6MaLuV5wJU3BQEdHKzTfcvPN0WMS6SC1oyA==", + "dependencies": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + }, + "peerDependencies": { + "@types/react": "^16.8 || ^17.0 || ^18.0", + "@types/react-dom": "^16.8 || ^17.0 || ^18.0", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0", + "react-native": ">=0.59", + "redux": "^4 || ^5.0.0-beta.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-redux/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, "node_modules/react-refresh": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.4.3.tgz", @@ -12037,6 +12204,22 @@ "node": ">=0.10.0" } }, + "node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, + "node_modules/redux-thunk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", + "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "peerDependencies": { + "redux": "^4" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", diff --git a/package.json b/package.json index 71a8caf..3065f35 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,8 @@ "dependencies": { "@react-navigation/drawer": "^6.6.3", "@react-navigation/native-stack": "^6.9.13", + "@reduxjs/toolkit": "^1.9.5", + "axios": "^1.4.0", "expo": "~48.0.18", "expo-status-bar": "~1.4.4", "react": "18.2.0", @@ -19,7 +21,10 @@ "react-native-reanimated": "~2.14.4", "react-native-safe-area-context": "4.5.0", "react-native-screens": "~3.20.0", - "react-native-svg": "13.4.0" + "react-native-svg": "13.4.0", + "react-redux": "^8.1.1", + "redux": "^4.2.1", + "@react-native-async-storage/async-storage": "1.17.11" }, "devDependencies": { "@babel/core": "^7.20.0", diff --git a/src/components/Api/Api.tsx b/src/components/Api/Api.tsx new file mode 100644 index 0000000..5056087 --- /dev/null +++ b/src/components/Api/Api.tsx @@ -0,0 +1,73 @@ +import axios from "axios"; +import AsyncStorage from "@react-native-async-storage/async-storage"; +import { + ActivationParams, + LoginParams, + RegistrationParams, +} from "../../interfaces/Interfaces"; + +let debug = true; +export let backendURL = ""; +if (debug) { + backendURL = "http://10.0.10.8:8000"; +} else { + backendURL = "https://keannu125.pythonanywhere.com"; +} + +const instance = axios.create({ + baseURL: backendURL, + timeout: 1000, +}); + +// App APIs + +// User APIs + +export function UserRegister(register: RegistrationParams) { + console.log(JSON.stringify(register)); + return instance + .post("/api/v1/accounts/users/", register) + .then(async (response) => { + return [response.status]; + }) + .catch((error) => { + return [error.response.status, error.response.data]; + }); +} + +export function UserLogin(user: LoginParams) { + return instance + .post("/api/v1/accounts/token/login/", user) + .then(async (response) => { + AsyncStorage.setItem("token", JSON.stringify(response.data.auth_token)); + return true; + }) + .catch((error) => { + console.log("Login Failed: " + error); + return false; + }); +} + +export async function UserInfo() { + const token = JSON.parse((await AsyncStorage.getItem("token")) || "{}"); + return instance + .get("/api/v1/accounts/users/me/", { + headers: { + Authorization: "Token " + token, + }, + }) + .then((response) => { + return response.data; + }); +} + +export function UserActivate(activation: ActivationParams) { + return instance + .post("/api/v1/accounts/users/activation/", activation) + .then(async (response) => { + return true; + }) + .catch((error) => { + return false; + }); +} diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx new file mode 100644 index 0000000..86ba814 --- /dev/null +++ b/src/components/Button/Button.tsx @@ -0,0 +1,23 @@ +import * as React from "react"; +import { Text, Pressable, GestureResponderEvent } from "react-native"; +import styles from "../../styles"; + +export interface props { + children: React.ReactNode; + onPress: (event: GestureResponderEvent) => void; + color: string; +} + +export default function Button(props: props) { + return ( + + {props.children} + + ); +} diff --git a/src/components/Button/DrawerButton.tsx b/src/components/Button/DrawerButton.tsx index 9fe64ad..5708d6e 100644 --- a/src/components/Button/DrawerButton.tsx +++ b/src/components/Button/DrawerButton.tsx @@ -14,7 +14,11 @@ export default function DrawerButton(props: props) { onPress={props.onPress} style={{ ...styles.button_template, - ...{ backgroundColor: props.color, width: "95%" }, + ...{ + backgroundColor: props.color, + width: "95%", + justifyContent: "flex-start", + }, }} > {props.children} diff --git a/src/components/DrawerSettings/CustomDrawerContent.tsx b/src/components/DrawerSettings/CustomDrawerContent.tsx index d890c35..12483f5 100644 --- a/src/components/DrawerSettings/CustomDrawerContent.tsx +++ b/src/components/DrawerSettings/CustomDrawerContent.tsx @@ -22,7 +22,7 @@ export default function CustomDrawerContent(props: {}) { ...{ justifyContent: "center" }, }} > - + Stud-E - + Home - + Login - + Register diff --git a/src/components/DrawerSettings/DrawerScreenSettings.tsx b/src/components/DrawerSettings/DrawerScreenSettings.tsx index 3e4679f..e71292c 100644 --- a/src/components/DrawerSettings/DrawerScreenSettings.tsx +++ b/src/components/DrawerSettings/DrawerScreenSettings.tsx @@ -24,7 +24,7 @@ const DrawerScreenSettings: DrawerNavigationOptions = { - + ), }; diff --git a/src/components/IsNumber/IsNumber.tsx b/src/components/IsNumber/IsNumber.tsx new file mode 100644 index 0000000..70262f6 --- /dev/null +++ b/src/components/IsNumber/IsNumber.tsx @@ -0,0 +1,8 @@ +export default function IsNumber(number: string) { + const re = /^[0-9\b]+$/; + if (re.test(number)) { + return true; + } else { + return false; + } +} diff --git a/src/components/ParseError/ParseError.tsx b/src/components/ParseError/ParseError.tsx new file mode 100644 index 0000000..6a39e77 --- /dev/null +++ b/src/components/ParseError/ParseError.tsx @@ -0,0 +1,9 @@ +export default function ParseError(text: string) { + return text + .replaceAll(/[{}()"]/g, " ") + .replaceAll(/,/g, "\n") + .replaceAll(":", "") + .replaceAll("[", "") + .replaceAll("]", "") + .replaceAll(".", ""); +} diff --git a/src/features/redux/Store/Store.tsx b/src/features/redux/Store/Store.tsx new file mode 100644 index 0000000..55a6590 --- /dev/null +++ b/src/features/redux/Store/Store.tsx @@ -0,0 +1,14 @@ +import { configureStore } from "@reduxjs/toolkit"; +import AuthReducer from "../slices/AuthSlice/AuthSlice"; + +const store = configureStore({ + reducer: { + auth: AuthReducer, + }, +}); + +export default store; + +// Infer the `RootState` and `AppDispatch` types from the store itself +export type RootState = ReturnType; +export type AppDispatch = typeof store.dispatch; diff --git a/src/features/redux/slices/AuthSlice/AuthSlice.tsx b/src/features/redux/slices/AuthSlice/AuthSlice.tsx new file mode 100644 index 0000000..578f20c --- /dev/null +++ b/src/features/redux/slices/AuthSlice/AuthSlice.tsx @@ -0,0 +1,42 @@ +import { createSlice } from "@reduxjs/toolkit"; + +export const AuthSlice = createSlice({ + name: "Auth", + initialState: { + creds: { + email: "", + uid: "", + username: "", + full_name: "", + token: "", + }, + }, + reducers: { + setToken: (state, action) => { + state.creds.token = action.payload.token; + }, + setUser: (state, action) => { + state.creds = { + email: action.payload.email, + uid: action.payload.uid, + username: action.payload.username, + full_name: action.payload.full_name, + token: action.payload.token, + }; + }, + clear: (state) => { + state.creds = { + email: "", + uid: "", + username: "", + full_name: "", + token: "", + }; + }, + }, +}); + +// Action creators are generated for each case reducer function +export const { setToken, setUser, clear } = AuthSlice.actions; + +export default AuthSlice.reducer; diff --git a/src/icons/AddIcon/AddIcon.tsx b/src/icons/AddIcon/AddIcon.tsx index b6ae27a..058f470 100644 --- a/src/icons/AddIcon/AddIcon.tsx +++ b/src/icons/AddIcon/AddIcon.tsx @@ -2,6 +2,7 @@ import * as React from "react"; import { IconProps } from "../../interfaces/Interfaces"; import { Svg, Path } from "react-native-svg"; +import { colors } from "../../styles"; export default function AddIcon(props: IconProps) { return ( @@ -11,7 +12,7 @@ export default function AddIcon(props: IconProps) { height={props.size} viewBox="0 0 24 24" strokeWidth="2" - stroke={props.color} + stroke={colors.icon_color} fill="none" strokeLinecap="round" strokeLinejoin="round" diff --git a/src/icons/AppIcon/AppIcon.tsx b/src/icons/AppIcon/AppIcon.tsx index 41f11e7..8901270 100644 --- a/src/icons/AppIcon/AppIcon.tsx +++ b/src/icons/AppIcon/AppIcon.tsx @@ -2,12 +2,13 @@ import * as React from "react"; import { IconProps } from "../../interfaces/Interfaces"; import { Svg, Path } from "react-native-svg"; +import { colors } from "../../styles"; export default function AppIcon(props: IconProps) { return ( <> (); + // const dispatch = useDispatch(); + // const creds = useSelector((state: RootState) => state.auth.creds); + const [user, setUser] = useState({ + email: "", + password: "", + error: "", + }); return ( - - Template Login Page - + + + + + Student Login + + + + + ): void => { + setUser({ ...user, email: e.nativeEvent.text }); + }} + /> + + + ): void => { + setUser({ ...user, password: e.nativeEvent.text }); + }} + /> + + + + ); } diff --git a/src/routes/Register/Register.tsx b/src/routes/Register/Register.tsx index 155dcd6..d88b3b0 100644 --- a/src/routes/Register/Register.tsx +++ b/src/routes/Register/Register.tsx @@ -1,20 +1,173 @@ import * as React from "react"; import styles from "../../styles"; -import { font_sizes } from "../../styles"; -import { View, Text } from "react-native"; +import { + View, + Text, + TextInput, + NativeSyntheticEvent, + TextInputChangeEventData, +} from "react-native"; +import { useSelector, useDispatch } from "react-redux"; +import { colors } from "../../styles"; +import { useState } from "react"; +import Button from "../../components/Button/Button"; +import { useNavigation } from "@react-navigation/native"; +import { RootDrawerParamList } from "../../interfaces/Interfaces"; +import SignupIcon from "../../icons/SignupIcon/SignupIcon"; +import { UserRegister } from "../../components/Api/Api"; +import IsNumber from "../../components/IsNumber/IsNumber"; +import ParseError from "../../components/ParseError/ParseError"; export default function Register() { + const navigation = useNavigation(); + // const dispatch = useDispatch(); + // const creds = useSelector((state: RootState) => state.auth.creds); + const [user, setUser] = useState({ + first_name: "", + last_name: "", + student_id_number: "", + username: "", + email: "", + password: "", + feedback: "", + }); return ( - - Template Register Page - + + + + + Student Signup + + + + ): void => { + setUser({ ...user, first_name: e.nativeEvent.text }); + }} + /> + + + ): void => { + setUser({ ...user, last_name: e.nativeEvent.text }); + }} + /> + + + ): void => { + if (IsNumber(e.nativeEvent.text)) { + setUser({ ...user, student_id_number: e.nativeEvent.text }); + } else if (!e.nativeEvent.text) { + setUser({ ...user, student_id_number: "" }); + } + }} + /> + + + + ): void => { + setUser({ ...user, username: e.nativeEvent.text }); + }} + /> + + + ): void => { + setUser({ ...user, email: e.nativeEvent.text }); + }} + /> + + + ): void => { + setUser({ ...user, password: e.nativeEvent.text }); + }} + /> + + {user.feedback} + + + ); } diff --git a/src/styles.tsx b/src/styles.tsx index fda9474..7d80160 100644 --- a/src/styles.tsx +++ b/src/styles.tsx @@ -8,6 +8,8 @@ export const colors = { blue_2: "#77ACC3", blue_3: "#1B5D79", text_default: "white", + text_error: "#e32d1e", + text_success: "green", icon_color: "white", }; @@ -25,6 +27,19 @@ const styles = StyleSheet.create({ height: "100%", width: "100%", }, + container: { + marginTop: "5%", + width: "92%", + borderRadius: 15, + backgroundColor: colors.blue_2, + alignItems: "center", + alignSelf: "center", + paddingTop: 32, + paddingBottom: 32, + justifyContent: "flex-start", + display: "flex", + flexDirection: "column", + }, flex_row: { display: "flex", flexDirection: "row", @@ -66,15 +81,23 @@ const styles = StyleSheet.create({ textAlign: "center", }, button_template: { - justifyContent: "flex-start", + justifyContent: "center", alignSelf: "center", alignItems: "center", display: "flex", flexDirection: "row", - margin: 8, + marginVertical: 4, + marginHorizontal: 8, padding: 8, borderRadius: 16, }, + text_input: { + color: colors.text_default, + backgroundColor: colors.blue_1, + width: "50%", + padding: 10, + borderRadius: 8, + }, }); export default styles; From a634f07f2b4f55d6c766ae2c163b1c7290d514c4 Mon Sep 17 00:00:00 2001 From: Keannu Bernasol Date: Mon, 3 Jul 2023 22:11:04 +0800 Subject: [PATCH 02/15] Added login functionality --- src/components/Api/Api.tsx | 8 +++--- src/components/ParseError/ParseError.tsx | 11 +++++++- src/routes/Login/Login.tsx | 33 ++++++++++++++++++++---- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/src/components/Api/Api.tsx b/src/components/Api/Api.tsx index 5056087..797499a 100644 --- a/src/components/Api/Api.tsx +++ b/src/components/Api/Api.tsx @@ -40,11 +40,13 @@ export function UserLogin(user: LoginParams) { .post("/api/v1/accounts/token/login/", user) .then(async (response) => { AsyncStorage.setItem("token", JSON.stringify(response.data.auth_token)); - return true; + return [true]; }) .catch((error) => { - console.log("Login Failed: " + error); - return false; + console.log( + "Login Failed: " + error.response.status + " " + error.response.data + ); + return [false, error.response.data]; }); } diff --git a/src/components/ParseError/ParseError.tsx b/src/components/ParseError/ParseError.tsx index 6a39e77..defb5fd 100644 --- a/src/components/ParseError/ParseError.tsx +++ b/src/components/ParseError/ParseError.tsx @@ -2,8 +2,17 @@ export default function ParseError(text: string) { return text .replaceAll(/[{}()"]/g, " ") .replaceAll(/,/g, "\n") - .replaceAll(":", "") .replaceAll("[", "") .replaceAll("]", "") .replaceAll(".", ""); } + +export function ParseLoginError(text: string) { + return text + .replaceAll(/[{}()"]/g, " ") + .replaceAll(/,/g, "\n") + .replaceAll("[", "") + .replaceAll("]", "") + .replaceAll(".", "") + .replaceAll("non_field_errors", ""); +} diff --git a/src/routes/Login/Login.tsx b/src/routes/Login/Login.tsx index 0b5e4fe..4d4510e 100644 --- a/src/routes/Login/Login.tsx +++ b/src/routes/Login/Login.tsx @@ -19,13 +19,15 @@ import LoginIcon from "../../icons/LoginIcon/LoginIcon"; import Button from "../../components/Button/Button"; import { useNavigation } from "@react-navigation/native"; import { RootDrawerParamList } from "../../interfaces/Interfaces"; +import { UserLogin } from "../../components/Api/Api"; +import { ParseLoginError } from "../../components/ParseError/ParseError"; export default function Login() { const navigation = useNavigation(); // const dispatch = useDispatch(); // const creds = useSelector((state: RootState) => state.auth.creds); const [user, setUser] = useState({ - email: "", + username: "", password: "", error: "", }); @@ -49,13 +51,14 @@ export default function Login() { ): void => { - setUser({ ...user, email: e.nativeEvent.text }); + setUser({ ...user, username: e.nativeEvent.text }); }} /> @@ -71,8 +74,28 @@ export default function Login() { setUser({ ...user, password: e.nativeEvent.text }); }} /> + + {user.error} - - + ); } diff --git a/src/routes/Revalidation/Revalidation.tsx b/src/routes/Revalidation/Revalidation.tsx new file mode 100644 index 0000000..46e8da6 --- /dev/null +++ b/src/routes/Revalidation/Revalidation.tsx @@ -0,0 +1,51 @@ +import * as React from "react"; +import styles from "../../styles"; +import { View, Text, ActivityIndicator } from "react-native"; +import Button from "../../components/Button/Button"; +import { TokenRefresh, UserInfo, UserLogin } from "../../components/Api/Api"; +import axios from "axios"; +import { useDispatch, useSelector } from "react-redux"; +import { RootState } from "../../features/redux/Store/Store"; +import AsyncStorage from "@react-native-async-storage/async-storage"; + +import { MotiView } from "moti"; +import { colors } from "../../styles"; +import { useEffect, useState } from "react"; +import { useNavigation } from "@react-navigation/native"; +import { RootDrawerParamList } from "../../interfaces/Interfaces"; +import { setUser } from "../../features/redux/slices/AuthSlice/AuthSlice"; +import AnimatedContainer from "../../components/AnimatedContainer/AnimatedContainer"; + +export default function Revalidation() { + const dispatch = useDispatch(); + const navigation = useNavigation(); + const creds = useSelector((state: RootState) => state.auth.creds); + console.log(JSON.stringify(creds)); + const [state, setState] = useState("Checking for existing session"); + useEffect(() => { + setState("Previous session found"); + TokenRefresh().then(async (response) => { + if (response[0]) { + await dispatch(setUser(await UserInfo())); + setTimeout(() => { + navigation.navigate("Home"); + }, 700); + } else { + setState("Session expired"); + setTimeout(() => { + navigation.navigate("Login"); + }, 700); + } + }); + }, []); + + return ( + + + + + {state} + + + ); +} From fb7b77efb704773be48062a49355c9af3fd9a759 Mon Sep 17 00:00:00 2001 From: Keannu Bernasol Date: Tue, 4 Jul 2023 14:26:49 +0800 Subject: [PATCH 06/15] Removed unused imports and commented out debug logs --- src/components/Api/Api.tsx | 2 +- src/routes/Home/Home.tsx | 4 ---- src/routes/Register/Register.tsx | 1 - src/routes/Revalidation/Revalidation.tsx | 12 ++---------- 4 files changed, 3 insertions(+), 16 deletions(-) diff --git a/src/components/Api/Api.tsx b/src/components/Api/Api.tsx index 927e20e..f6c5975 100644 --- a/src/components/Api/Api.tsx +++ b/src/components/Api/Api.tsx @@ -108,7 +108,7 @@ export async function UserInfo() { }, }) .then((response) => { - console.log(JSON.stringify(response.data)); + // console.log(JSON.stringify(response.data)); return response.data; }) .catch((error) => { diff --git a/src/routes/Home/Home.tsx b/src/routes/Home/Home.tsx index 7fe3a1c..944d131 100644 --- a/src/routes/Home/Home.tsx +++ b/src/routes/Home/Home.tsx @@ -3,10 +3,6 @@ import styles from "../../styles"; import { View, Text } from "react-native"; import { useSelector } from "react-redux"; import { RootState } from "../../features/redux/Store/Store"; -import Button from "../../components/Button/Button"; -import { UserLogin } from "../../components/Api/Api"; -import { colors } from "../../styles"; -import axios from "axios"; import AnimatedContainer from "../../components/AnimatedContainer/AnimatedContainer"; export default function Home() { diff --git a/src/routes/Register/Register.tsx b/src/routes/Register/Register.tsx index c893513..8e0a567 100644 --- a/src/routes/Register/Register.tsx +++ b/src/routes/Register/Register.tsx @@ -7,7 +7,6 @@ import { NativeSyntheticEvent, TextInputChangeEventData, } from "react-native"; -import { useSelector, useDispatch } from "react-redux"; import { colors } from "../../styles"; import { useState } from "react"; import Button from "../../components/Button/Button"; diff --git a/src/routes/Revalidation/Revalidation.tsx b/src/routes/Revalidation/Revalidation.tsx index 46e8da6..7117573 100644 --- a/src/routes/Revalidation/Revalidation.tsx +++ b/src/routes/Revalidation/Revalidation.tsx @@ -1,14 +1,8 @@ import * as React from "react"; import styles from "../../styles"; import { View, Text, ActivityIndicator } from "react-native"; -import Button from "../../components/Button/Button"; -import { TokenRefresh, UserInfo, UserLogin } from "../../components/Api/Api"; -import axios from "axios"; -import { useDispatch, useSelector } from "react-redux"; -import { RootState } from "../../features/redux/Store/Store"; -import AsyncStorage from "@react-native-async-storage/async-storage"; - -import { MotiView } from "moti"; +import { TokenRefresh, UserInfo } from "../../components/Api/Api"; +import { useDispatch } from "react-redux"; import { colors } from "../../styles"; import { useEffect, useState } from "react"; import { useNavigation } from "@react-navigation/native"; @@ -19,8 +13,6 @@ import AnimatedContainer from "../../components/AnimatedContainer/AnimatedContai export default function Revalidation() { const dispatch = useDispatch(); const navigation = useNavigation(); - const creds = useSelector((state: RootState) => state.auth.creds); - console.log(JSON.stringify(creds)); const [state, setState] = useState("Checking for existing session"); useEffect(() => { setState("Previous session found"); From 4a00dd1817ea59961273419935d13a55fe80e584 Mon Sep 17 00:00:00 2001 From: Keannu Bernasol Date: Tue, 4 Jul 2023 14:34:31 +0800 Subject: [PATCH 07/15] Fixed registration page --- src/components/Api/Api.tsx | 4 ++-- src/routes/Register/Register.tsx | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/components/Api/Api.tsx b/src/components/Api/Api.tsx index f6c5975..5d2e6dd 100644 --- a/src/components/Api/Api.tsx +++ b/src/components/Api/Api.tsx @@ -48,10 +48,10 @@ export function UserRegister(register: RegistrationParams) { return instance .post("/api/v1/accounts/users/", register) .then(async (response) => { - return [response.status]; + return [true, response.status]; }) .catch((error) => { - return [error.response.status, error.response.data]; + return [false, error.response.status, error.response.data]; }); } diff --git a/src/routes/Register/Register.tsx b/src/routes/Register/Register.tsx index 8e0a567..f484f11 100644 --- a/src/routes/Register/Register.tsx +++ b/src/routes/Register/Register.tsx @@ -141,12 +141,7 @@ export default function Register() { last_name: user.last_name, }).then((result) => { console.log(result); - try { - setUser({ - ...user, - feedback: ParseError(JSON.stringify(result[1])), - }); - } catch { + if (result[0]) { setUser({ ...user, first_name: "", @@ -158,6 +153,14 @@ export default function Register() { feedback: "Success! An email has been sent to activate your account", }); + setTimeout(() => { + navigation.navigate("Login"); + }, 10000); + } else { + setUser({ + ...user, + feedback: ParseError(JSON.stringify(result[2])), + }); } }); { From e52aca41cc81b72d6238916e0678b08d682c016f Mon Sep 17 00:00:00 2001 From: Keannu Bernasol Date: Tue, 4 Jul 2023 16:08:24 +0800 Subject: [PATCH 08/15] Added initial activation page --- App.tsx | 39 ++++++++++++++++++- app.json | 1 + package-lock.json | 18 +++++++++ package.json | 3 +- .../DrawerSettings/CustomDrawerContent.tsx | 9 +++++ src/routes/Activation/Activation.tsx | 28 +++++++++++++ 6 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 src/routes/Activation/Activation.tsx diff --git a/App.tsx b/App.tsx index f9b1928..485ed7f 100644 --- a/App.tsx +++ b/App.tsx @@ -5,6 +5,7 @@ import { Provider } from "react-redux"; import store from "./src/features/redux/Store/Store"; import "react-native-reanimated"; import "react-native-gesture-handler"; +import * as Linking from "expo-linking"; import CustomDrawerContent from "./src/components/DrawerSettings/CustomDrawerContent"; import DrawerScreenSettings from "./src/components/DrawerSettings/DrawerScreenSettings"; @@ -14,13 +15,48 @@ import Login from "./src/routes/Login/Login"; import Register from "./src/routes/Register/Register"; import Onboarding from "./src/routes/Onboarding/Onboarding"; import Revalidation from "./src/routes/Revalidation/Revalidation"; +import Activation from "./src/routes/Activation/Activation"; +import { useState, useEffect } from "react"; const Drawer = createDrawerNavigator(); +const linking = { + prefixes: [Linking.makeUrl("/")], + config: { + screens: { + Home: "home", + Login: "login", + Register: "register", + Onboarding: "onboarding", + Revalidation: "revalidation", + Activation: "activation/:uid?/:token?", + NotFound: "*", + }, + }, +}; + export default function App() { + const [initialRoute, setInitialRoute] = useState(null); + useEffect(() => { + async function getInitialURL() { + const url = await Linking.getInitialURL(); + if (url) { + setInitialRoute(url); + } + } + if (!initialRoute) { + getInitialURL(); + } + }, [initialRoute]); + + if (!initialRoute) { + console.log("heh"); + return null; + } + return ( - + + diff --git a/app.json b/app.json index 755f6c0..a14231f 100644 --- a/app.json +++ b/app.json @@ -1,6 +1,7 @@ { "expo": { "name": "StudE_Frontend", + "scheme": "StudE_Frontend", "slug": "StudE_Frontend", "version": "1.0.0", "orientation": "portrait", diff --git a/package-lock.json b/package-lock.json index 4cd68d2..bc946b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@reduxjs/toolkit": "^1.9.5", "axios": "^1.4.0", "expo": "~48.0.18", + "expo-linking": "~4.0.1", "expo-status-bar": "~1.4.4", "moti": "^0.25.3", "react": "18.2.0", @@ -5245,6 +5246,11 @@ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + }, "node_modules/@types/react": { "version": "18.0.38", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.38.tgz", @@ -7376,6 +7382,18 @@ "expo": "*" } }, + "node_modules/expo-linking": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-4.0.1.tgz", + "integrity": "sha512-geRMmKLhtaCigptRzOGW8ZZJKGl47gyu0KFtQOtGf26ELxyt/AietgQjyzF24i7cVD08TnWUJDHgWZkpRHTa7g==", + "dependencies": { + "@types/qs": "^6.9.7", + "expo-constants": "~14.2.0", + "invariant": "^2.2.4", + "qs": "^6.11.0", + "url-parse": "^1.5.9" + } + }, "node_modules/expo-modules-autolinking": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-1.2.0.tgz", diff --git a/package.json b/package.json index 6cb9bcf..5c2ccd0 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "react-native-screens": "~3.20.0", "react-native-svg": "13.4.0", "react-redux": "^8.1.1", - "redux": "^4.2.1" + "redux": "^4.2.1", + "expo-linking": "~4.0.1" }, "devDependencies": { "@babel/core": "^7.20.0", diff --git a/src/components/DrawerSettings/CustomDrawerContent.tsx b/src/components/DrawerSettings/CustomDrawerContent.tsx index 5b9f694..579717b 100644 --- a/src/components/DrawerSettings/CustomDrawerContent.tsx +++ b/src/components/DrawerSettings/CustomDrawerContent.tsx @@ -62,6 +62,15 @@ export default function CustomDrawerContent(props: {}) { Revalidation + { + navigation.navigate("Activation"); + }} + > + + Activation + ); } diff --git a/src/routes/Activation/Activation.tsx b/src/routes/Activation/Activation.tsx new file mode 100644 index 0000000..562b241 --- /dev/null +++ b/src/routes/Activation/Activation.tsx @@ -0,0 +1,28 @@ +import * as React from "react"; +import styles, { colors } from "../../styles"; +import { View, Text, ActivityIndicator } from "react-native"; +import AnimatedContainer from "../../components/AnimatedContainer/AnimatedContainer"; +import { useRoute } from "@react-navigation/native"; + +interface ActivationRouteParams { + uid?: string; + token?: string; +} + +export default function Activation() { + const route = useRoute(); + const { uid, token } = route.params as ActivationRouteParams; + return ( + + + Activation + + + + Activating {uid ? `with UID: ${uid}` : ""}{" "} + {token ? `and Token: ${token}` : ""} + + + + ); +} From 92b8ce0e4e86975d97778f87fa1bbe8b6ca9f023 Mon Sep 17 00:00:00 2001 From: Keannu Bernasol Date: Tue, 4 Jul 2023 16:22:37 +0800 Subject: [PATCH 09/15] Added working activation page --- src/routes/Activation/Activation.tsx | 44 ++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/src/routes/Activation/Activation.tsx b/src/routes/Activation/Activation.tsx index 562b241..d9d7b9d 100644 --- a/src/routes/Activation/Activation.tsx +++ b/src/routes/Activation/Activation.tsx @@ -3,6 +3,8 @@ import styles, { colors } from "../../styles"; import { View, Text, ActivityIndicator } from "react-native"; import AnimatedContainer from "../../components/AnimatedContainer/AnimatedContainer"; import { useRoute } from "@react-navigation/native"; +import { useEffect, useState } from "react"; +import { UserActivate } from "../../components/Api/Api"; interface ActivationRouteParams { uid?: string; @@ -11,17 +13,47 @@ interface ActivationRouteParams { export default function Activation() { const route = useRoute(); - const { uid, token } = route.params as ActivationRouteParams; + const { uid, token } = (route.params as ActivationRouteParams) || ""; + + const [state, setState] = useState( + "Activating with UID " + uid + " and Token " + token + ); + const [loading, setLoading] = useState(true); + async function activate() { + if (await UserActivate({ uid: String(uid), token: String(token) })) { + setTimeout(() => { + setState("Activation successful!"); + }, 1000); + } else { + setTimeout(() => { + setState("Activation unsuccessful\nPlease contact support"); + }, 1000); + } + setLoading(false); + } + useEffect(() => { + activate(); + }, []); return ( + Activation - - - Activating {uid ? `with UID: ${uid}` : ""}{" "} - {token ? `and Token: ${token}` : ""} - + + {state} ); From a0271158364a91bcabced3caf9fa3401571532f7 Mon Sep 17 00:00:00 2001 From: Keannu Bernasol Date: Tue, 4 Jul 2023 16:41:29 +0800 Subject: [PATCH 10/15] Polished revalidation and login. Opted to not store tokens anymore in state but only in asyncstorage --- src/components/Api/Api.tsx | 2 +- .../DrawerSettings/CustomDrawerContent.tsx | 3 ++- .../redux/slices/AuthSlice/AuthSlice.tsx | 16 ++++++---------- src/routes/Login/Login.tsx | 7 ------- src/routes/Revalidation/Revalidation.tsx | 6 +++++- 5 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/components/Api/Api.tsx b/src/components/Api/Api.tsx index 5d2e6dd..91af5ea 100644 --- a/src/components/Api/Api.tsx +++ b/src/components/Api/Api.tsx @@ -92,7 +92,7 @@ export async function TokenRefresh() { "Token refresh success! New Access Token", response.data.access );*/ - return [true, getAccessToken()]; + return [true, JSON.stringify(response.data.access)]; }) .catch((error) => { console.log("Refresh Failed: " + JSON.stringify(error.response.data)); diff --git a/src/components/DrawerSettings/CustomDrawerContent.tsx b/src/components/DrawerSettings/CustomDrawerContent.tsx index 579717b..de521b6 100644 --- a/src/components/DrawerSettings/CustomDrawerContent.tsx +++ b/src/components/DrawerSettings/CustomDrawerContent.tsx @@ -53,7 +53,7 @@ export default function CustomDrawerContent(props: {}) { Register - { navigation.navigate("Revalidation"); @@ -71,6 +71,7 @@ export default function CustomDrawerContent(props: {}) { Activation + */} ); } diff --git a/src/features/redux/slices/AuthSlice/AuthSlice.tsx b/src/features/redux/slices/AuthSlice/AuthSlice.tsx index 39fa0e2..b575579 100644 --- a/src/features/redux/slices/AuthSlice/AuthSlice.tsx +++ b/src/features/redux/slices/AuthSlice/AuthSlice.tsx @@ -8,14 +8,12 @@ export const AuthSlice = createSlice({ uid: "", username: "", full_name: "", - refresh_token: "", - access_token: "", + logged_in: false, }, }, reducers: { - setToken: (state, action) => { - state.creds.access_token = action.payload.access_token; - state.creds.refresh_token = action.payload.refresh_token; + login: (state) => { + state.creds.logged_in = true; }, setUser: (state, action) => { state.creds = { @@ -23,8 +21,7 @@ export const AuthSlice = createSlice({ uid: action.payload.uid, username: action.payload.username, full_name: action.payload.full_name, - access_token: action.payload.access_token, - refresh_token: action.payload.refresh_token, + logged_in: true, }; }, clear: (state) => { @@ -33,14 +30,13 @@ export const AuthSlice = createSlice({ uid: "", username: "", full_name: "", - refresh_token: "", - access_token: "", + logged_in: false, }; }, }, }); // Action creators are generated for each case reducer function -export const { setToken, setUser, clear } = AuthSlice.actions; +export const { login, setUser, clear } = AuthSlice.actions; export default AuthSlice.reducer; diff --git a/src/routes/Login/Login.tsx b/src/routes/Login/Login.tsx index 28c3ef4..2e2873d 100644 --- a/src/routes/Login/Login.tsx +++ b/src/routes/Login/Login.tsx @@ -8,7 +8,6 @@ import { TextInputChangeEventData, } from "react-native"; import { useDispatch } from "react-redux"; -import { setToken } from "../../features/redux/slices/AuthSlice/AuthSlice"; import { colors } from "../../styles"; import { useState } from "react"; import LoginIcon from "../../icons/LoginIcon/LoginIcon"; @@ -87,12 +86,6 @@ export default function Login() { "\nRefresh Token:", result[2] );*/ - dispatch( - setToken({ - access_token: result[1], - refresh_token: result[2], - }) - ); navigation.navigate("Onboarding"); } else { setUser({ diff --git a/src/routes/Revalidation/Revalidation.tsx b/src/routes/Revalidation/Revalidation.tsx index 7117573..622cebf 100644 --- a/src/routes/Revalidation/Revalidation.tsx +++ b/src/routes/Revalidation/Revalidation.tsx @@ -1,7 +1,11 @@ import * as React from "react"; import styles from "../../styles"; import { View, Text, ActivityIndicator } from "react-native"; -import { TokenRefresh, UserInfo } from "../../components/Api/Api"; +import { + TokenRefresh, + UserInfo, + setAccessToken, +} from "../../components/Api/Api"; import { useDispatch } from "react-redux"; import { colors } from "../../styles"; import { useEffect, useState } from "react"; From c4f241f799de0919b4d138306c3c6920ae652924 Mon Sep 17 00:00:00 2001 From: Keannu Bernasol Date: Tue, 4 Jul 2023 17:00:22 +0800 Subject: [PATCH 11/15] Clean up login since state no longer tracks tokens --- src/components/Api/Api.tsx | 8 ++------ src/routes/Login/Login.tsx | 6 ------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/components/Api/Api.tsx b/src/components/Api/Api.tsx index 91af5ea..0032997 100644 --- a/src/components/Api/Api.tsx +++ b/src/components/Api/Api.tsx @@ -67,11 +67,7 @@ export function UserLogin(user: LoginParams) { ); setAccessToken(response.data.access); setRefreshToken(response.data.refresh); - return [ - true, - JSON.stringify(response.data.access), - JSON.stringify(response.data.refresh), - ]; + return [true]; }) .catch((error) => { console.log("Login Failed:" + error.response.data); @@ -92,7 +88,7 @@ export async function TokenRefresh() { "Token refresh success! New Access Token", response.data.access );*/ - return [true, JSON.stringify(response.data.access)]; + return [true]; }) .catch((error) => { console.log("Refresh Failed: " + JSON.stringify(error.response.data)); diff --git a/src/routes/Login/Login.tsx b/src/routes/Login/Login.tsx index 2e2873d..d0b9cc7 100644 --- a/src/routes/Login/Login.tsx +++ b/src/routes/Login/Login.tsx @@ -80,12 +80,6 @@ export default function Login() { }).then((result) => { if (result[0]) { setUser({ ...user, username: "", password: "", error: "" }); - /*console.log( - "Access Token:", - result[1], - "\nRefresh Token:", - result[2] - );*/ navigation.navigate("Onboarding"); } else { setUser({ From 9cdb4f1ba2690f1a9f80f5bbe87a4325505c4522 Mon Sep 17 00:00:00 2001 From: Keannu Bernasol Date: Tue, 4 Jul 2023 17:44:49 +0800 Subject: [PATCH 12/15] Fixed app.tsx showing blank when not specifying url and made drawer responsive to login state --- App.tsx | 2 - .../DrawerSettings/CustomDrawerContent.tsx | 162 +++++++++++------- 2 files changed, 103 insertions(+), 61 deletions(-) diff --git a/App.tsx b/App.tsx index 485ed7f..f762b1c 100644 --- a/App.tsx +++ b/App.tsx @@ -50,8 +50,6 @@ export default function App() { }, [initialRoute]); if (!initialRoute) { - console.log("heh"); - return null; } return ( diff --git a/src/components/DrawerSettings/CustomDrawerContent.tsx b/src/components/DrawerSettings/CustomDrawerContent.tsx index de521b6..39be406 100644 --- a/src/components/DrawerSettings/CustomDrawerContent.tsx +++ b/src/components/DrawerSettings/CustomDrawerContent.tsx @@ -11,67 +11,111 @@ import HomeIcon from "../../icons/HomeIcon/HomeIcon"; import LoginIcon from "../../icons/LoginIcon/LoginIcon"; import SignupIcon from "../../icons/SignupIcon/SignupIcon"; import DrawerButton from "../Button/DrawerButton"; -import AddIcon from "../../icons/AddIcon/AddIcon"; +import { useDispatch, useSelector } from "react-redux"; +import { RootState } from "../../features/redux/Store/Store"; +import LogoutIcon from "../../icons/LogoutIcon/LogoutIcon"; +import { clear } from "../../features/redux/slices/AuthSlice/AuthSlice"; +import AsyncStorage from "@react-native-async-storage/async-storage"; export default function CustomDrawerContent(props: {}) { const navigation = useNavigation(); - return ( - - - - Stud-E - - { - navigation.navigate("Home"); - }} - > - - Home - - { - navigation.navigate("Login"); - }} - > - - Login - - { - navigation.navigate("Register"); - }} - > - - Register - - {/* { - navigation.navigate("Revalidation"); - }} - > - - Revalidation - - { - navigation.navigate("Activation"); - }} - > - - Activation - - */} - + const logged_in = useSelector( + (state: RootState) => state.auth.creds.logged_in ); + const dispatch = useDispatch(); + if (logged_in) { + return ( + + + + Stud-E + + { + navigation.navigate("Home"); + }} + > + + Home + + { + dispatch(await clear()); + AsyncStorage.clear(); + }} + > + + Logout + + + ); + } else { + return ( + + + + Stud-E + + { + navigation.navigate("Home"); + }} + > + + Home + + { + navigation.navigate("Login"); + }} + > + + Login + + { + dispatch(clear()); + navigation.navigate("Login"); + }} + > + + Register + + {/* + Debug buttons for accessing revalidation and activation page + { + navigation.navigate("Revalidation"); + }} + > + Revalidation + + { + navigation.navigate("Activation"); + }} + > + Activation + + */} + + ); + } } From 13a24ef87abd750a9e3aa3a85ac42f18f750d28e Mon Sep 17 00:00:00 2001 From: Keannu Bernasol Date: Tue, 4 Jul 2023 17:48:35 +0800 Subject: [PATCH 13/15] Cleaned up App.tsx and changed url scheme in app.json --- App.tsx | 4 ---- app.json | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/App.tsx b/App.tsx index f762b1c..8ab50a5 100644 --- a/App.tsx +++ b/App.tsx @@ -48,10 +48,6 @@ export default function App() { getInitialURL(); } }, [initialRoute]); - - if (!initialRoute) { - } - return ( diff --git a/app.json b/app.json index a14231f..ca641e0 100644 --- a/app.json +++ b/app.json @@ -1,7 +1,7 @@ { "expo": { "name": "StudE_Frontend", - "scheme": "StudE_Frontend", + "scheme": "stude", "slug": "StudE_Frontend", "version": "1.0.0", "orientation": "portrait", From 877da5123ae52a8e1cfdd3c21d7d942a07d979ac Mon Sep 17 00:00:00 2001 From: Keannu Bernasol Date: Tue, 4 Jul 2023 19:37:55 +0800 Subject: [PATCH 14/15] Improved drawer logout and activation page --- .../DrawerSettings/CustomDrawerContent.tsx | 5 ++- src/routes/Activation/Activation.tsx | 41 ++++++++++++------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/components/DrawerSettings/CustomDrawerContent.tsx b/src/components/DrawerSettings/CustomDrawerContent.tsx index 39be406..21332e3 100644 --- a/src/components/DrawerSettings/CustomDrawerContent.tsx +++ b/src/components/DrawerSettings/CustomDrawerContent.tsx @@ -48,7 +48,8 @@ export default function CustomDrawerContent(props: {}) { color={colors.blue_2} onPress={async () => { dispatch(await clear()); - AsyncStorage.clear(); + await AsyncStorage.clear(); + navigation.navigate("Home"); }} > @@ -90,7 +91,7 @@ export default function CustomDrawerContent(props: {}) { color={colors.blue_2} onPress={() => { dispatch(clear()); - navigation.navigate("Login"); + navigation.navigate("Register"); }} > diff --git a/src/routes/Activation/Activation.tsx b/src/routes/Activation/Activation.tsx index d9d7b9d..dce7f24 100644 --- a/src/routes/Activation/Activation.tsx +++ b/src/routes/Activation/Activation.tsx @@ -2,9 +2,10 @@ import * as React from "react"; import styles, { colors } from "../../styles"; import { View, Text, ActivityIndicator } from "react-native"; import AnimatedContainer from "../../components/AnimatedContainer/AnimatedContainer"; -import { useRoute } from "@react-navigation/native"; +import { useNavigation, useRoute } from "@react-navigation/native"; import { useEffect, useState } from "react"; import { UserActivate } from "../../components/Api/Api"; +import { RootDrawerParamList } from "../../interfaces/Interfaces"; interface ActivationRouteParams { uid?: string; @@ -14,26 +15,35 @@ interface ActivationRouteParams { export default function Activation() { const route = useRoute(); const { uid, token } = (route.params as ActivationRouteParams) || ""; - + const navigation = useNavigation(); const [state, setState] = useState( "Activating with UID " + uid + " and Token " + token ); const [loading, setLoading] = useState(true); - async function activate() { - if (await UserActivate({ uid: String(uid), token: String(token) })) { - setTimeout(() => { - setState("Activation successful!"); - }, 1000); - } else { - setTimeout(() => { - setState("Activation unsuccessful\nPlease contact support"); - }, 1000); - } - setLoading(false); - } + useEffect(() => { + async function activate() { + let result = await UserActivate({ + uid: String(uid), + token: String(token), + }); + if (result) { + setTimeout(() => { + setState("Activation successful!"); + }, 1000); + setTimeout(() => { + navigation.navigate("Login"); + }, 2000); + } else { + setTimeout(() => { + setState("Activation unsuccessful\nPlease contact support"); + }, 1000); + } + setLoading(false); + } activate(); - }, []); + }, [uid, token]); + return ( @@ -54,6 +64,7 @@ export default function Activation() { color={colors.blue_1} /> {state} + {uid + "\n" + token} ); From e40f32d327fd3fb5d24fdf82db94b65c72ef1206 Mon Sep 17 00:00:00 2001 From: Keannu Bernasol Date: Tue, 4 Jul 2023 19:40:25 +0800 Subject: [PATCH 15/15] Commented out debug logs in api --- src/components/Api/Api.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Api/Api.tsx b/src/components/Api/Api.tsx index 0032997..97dbec0 100644 --- a/src/components/Api/Api.tsx +++ b/src/components/Api/Api.tsx @@ -59,12 +59,12 @@ export function UserLogin(user: LoginParams) { return instance .post("/api/v1/accounts/jwt/create/", user) .then(async (response) => { - console.log( + /*console.log( "Access Token:", response.data.access, "\nRefresh Token:", response.data.refresh - ); + );*/ setAccessToken(response.data.access); setRefreshToken(response.data.refresh); return [true];