diff --git a/App.tsx b/App.tsx index 9518027..8ab50a5 100644 --- a/App.tsx +++ b/App.tsx @@ -1,6 +1,11 @@ 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 "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"; @@ -8,21 +13,57 @@ import DrawerScreenSettings from "./src/components/DrawerSettings/DrawerScreenSe import Home from "./src/routes/Home/Home"; 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]); return ( - - - - - - - + + + + + + + + + + + + ); } diff --git a/app.json b/app.json index 755f6c0..ca641e0 100644 --- a/app.json +++ b/app.json @@ -1,6 +1,7 @@ { "expo": { "name": "StudE_Frontend", + "scheme": "stude", "slug": "StudE_Frontend", "version": "1.0.0", "orientation": "portrait", diff --git a/package-lock.json b/package-lock.json index 781e9d8..bc946b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,17 +8,25 @@ "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": "^6.1.7", "@react-navigation/native-stack": "^6.9.13", + "@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", "react-native": "0.71.8", "react-native-gesture-handler": "~2.9.0", "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", @@ -1979,6 +1987,21 @@ "node": ">=0.8.0" } }, + "node_modules/@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "optional": true, + "dependencies": { + "@emotion/memoize": "0.7.4" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "optional": true + }, "node_modules/@expo/bunyan": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@expo/bunyan/-/bunyan-4.0.0.tgz", @@ -3544,6 +3567,64 @@ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, + "node_modules/@motionone/animation": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.15.1.tgz", + "integrity": "sha512-mZcJxLjHor+bhcPuIFErMDNyrdb2vJur8lSfMCsuCB4UyV8ILZLvK+t+pg56erv8ud9xQGK/1OGPt10agPrCyQ==", + "dependencies": { + "@motionone/easing": "^10.15.1", + "@motionone/types": "^10.15.1", + "@motionone/utils": "^10.15.1", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/dom": { + "version": "10.12.0", + "resolved": "https://registry.npmjs.org/@motionone/dom/-/dom-10.12.0.tgz", + "integrity": "sha512-UdPTtLMAktHiqV0atOczNYyDd/d8Cf5fFsd1tua03PqTwwCe/6lwhLSQ8a7TbnQ5SN0gm44N1slBfj+ORIhrqw==", + "dependencies": { + "@motionone/animation": "^10.12.0", + "@motionone/generators": "^10.12.0", + "@motionone/types": "^10.12.0", + "@motionone/utils": "^10.12.0", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/easing": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/easing/-/easing-10.15.1.tgz", + "integrity": "sha512-6hIHBSV+ZVehf9dcKZLT7p5PEKHGhDwky2k8RKkmOvUoYP3S+dXsKupyZpqx5apjd9f+php4vXk4LuS+ADsrWw==", + "dependencies": { + "@motionone/utils": "^10.15.1", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/generators": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/generators/-/generators-10.15.1.tgz", + "integrity": "sha512-67HLsvHJbw6cIbLA/o+gsm7h+6D4Sn7AUrB/GPxvujse1cGZ38F5H7DzoH7PhX+sjvtDnt2IhFYF2Zp1QTMKWQ==", + "dependencies": { + "@motionone/types": "^10.15.1", + "@motionone/utils": "^10.15.1", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/types": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/types/-/types-10.15.1.tgz", + "integrity": "sha512-iIUd/EgUsRZGrvW0jqdst8st7zKTzS9EsKkP+6c6n4MPZoQHwiHuVtTQLD6Kp0bsBLhNzKIBlHXponn/SDT4hA==" + }, + "node_modules/@motionone/utils": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/utils/-/utils-10.15.1.tgz", + "integrity": "sha512-p0YncgU+iklvYr/Dq4NobTRdAPv9PveRDUXabPEeOjBLSO/1FNB2phNTZxOxpi1/GZwYpAoECEa0Wam+nsmhSw==", + "dependencies": { + "@motionone/types": "^10.15.1", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3653,6 +3734,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", @@ -4930,7 +5022,6 @@ "version": "6.4.9", "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-6.4.9.tgz", "integrity": "sha512-G9GH7bP9x0qqupxZnkSftnkn4JoXancElTvFc8FVGfEvxnxP+gBo3wqcknyBi7M5Vad4qecsYjCOa9wqsftv9g==", - "peer": true, "dependencies": { "@react-navigation/routers": "^6.1.9", "escape-string-regexp": "^4.0.0", @@ -4947,7 +5038,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "peer": true, "engines": { "node": ">=10" }, @@ -4958,8 +5048,7 @@ "node_modules/@react-navigation/core/node_modules/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==", - "peer": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/@react-navigation/drawer": { "version": "6.6.3", @@ -4995,7 +5084,6 @@ "version": "6.1.7", "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-6.1.7.tgz", "integrity": "sha512-W6E3+AtTombMucCRo6q7vPmluq8hSjS+IxfazJ/SokOe7ChJX7eLvvralIsJkjFj3iWV1KgOSnHxa6hdiFasBw==", - "peer": true, "dependencies": { "@react-navigation/core": "^6.4.9", "escape-string-regexp": "^4.0.0", @@ -5027,7 +5115,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "peer": true, "engines": { "node": ">=10" }, @@ -5039,11 +5126,33 @@ "version": "6.1.9", "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-6.1.9.tgz", "integrity": "sha512-lTM8gSFHSfkJvQkxacGM6VJtBt61ip2XO54aNfswD+KMw6eeZ4oehl7m0me3CR9hnDE4+60iAZR8sAhvCiI3NA==", - "peer": true, "dependencies": { "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 +5206,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 +5244,17 @@ "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/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", "integrity": "sha512-ExsidLLSzYj4cvaQjGnQCk4HFfVT9+EZ9XZsQ8Hsrcn8QNgXtpZ3m9vSIC2MWtx7jHictK6wYhQgGh6ic58oOw==", - "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -5143,14 +5264,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 +5571,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 +6740,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", @@ -7235,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", @@ -7424,8 +7583,7 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "peer": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { "version": "3.3.0", @@ -7526,7 +7684,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -7644,6 +7801,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", @@ -7681,6 +7857,34 @@ "node": ">=0.10.0" } }, + "node_modules/framer-motion": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-6.5.1.tgz", + "integrity": "sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==", + "dependencies": { + "@motionone/dom": "10.12.0", + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "popmotion": "11.0.3", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + }, + "optionalDependencies": { + "@emotion/is-prop-valid": "^0.8.2" + }, + "peerDependencies": { + "react": ">=16.8 || ^17.0.0 || ^18.0.0", + "react-dom": ">=16.8 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/framesync": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.0.1.tgz", + "integrity": "sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/freeport-async": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/freeport-async/-/freeport-async-2.0.0.tgz", @@ -8016,6 +8220,11 @@ "node": ">=8" } }, + "node_modules/hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==" + }, "node_modules/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", @@ -8140,6 +8349,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 +8637,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 +9875,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", @@ -10751,6 +10988,17 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/moti": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/moti/-/moti-0.25.3.tgz", + "integrity": "sha512-n6YR56i+tQdIuiOfspeCCvNR8vAJtPdfclt4998rX6S6kQn+/5DmfBWkOneN63JFTWM/oYnZHwwpF4z4LUXohQ==", + "dependencies": { + "framer-motion": "^6.5.1" + }, + "peerDependencies": { + "react-native-reanimated": "*" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -10818,7 +11066,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -11543,6 +11790,17 @@ "node": ">=4.0.0" } }, + "node_modules/popmotion": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-11.0.3.tgz", + "integrity": "sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==", + "dependencies": { + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + } + }, "node_modules/posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -11659,6 +11917,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", @@ -11694,7 +11957,6 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", - "peer": true, "dependencies": { "decode-uri-component": "^0.2.2", "filter-obj": "^1.1.0", @@ -11808,6 +12070,19 @@ } } }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, "node_modules/react-freeze": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.3.tgz", @@ -11976,6 +12251,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 +12355,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", @@ -12822,7 +13156,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", - "peer": true, "engines": { "node": ">=6" } @@ -13005,7 +13338,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", - "peer": true, "engines": { "node": ">=4" } @@ -13100,6 +13432,15 @@ "resolved": "https://registry.npmjs.org/structured-headers/-/structured-headers-0.4.1.tgz", "integrity": "sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==" }, + "node_modules/style-value-types": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-5.0.0.tgz", + "integrity": "sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==", + "dependencies": { + "hey-listen": "^1.0.8", + "tslib": "^2.1.0" + } + }, "node_modules/sucrase": { "version": "3.32.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz", @@ -13799,7 +14140,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.1.6.tgz", "integrity": "sha512-VO/P91A/PmKH9bcN9a7O3duSuxe6M14ZoYXgA6a8dab8doWNdhiIHzEkX/jFeTTRBsX0Ubk6nG4q2NIjNsj+bg==", - "peer": true, "peerDependencies": { "react": ">=16.8" } diff --git a/package.json b/package.json index 71a8caf..5c2ccd0 100644 --- a/package.json +++ b/package.json @@ -9,17 +9,25 @@ "web": "expo start --web" }, "dependencies": { + "@react-native-async-storage/async-storage": "1.17.11", "@react-navigation/drawer": "^6.6.3", + "@react-navigation/native": "^6.1.7", "@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", + "moti": "^0.25.3", "react": "18.2.0", "react-native": "0.71.8", "react-native-gesture-handler": "~2.9.0", "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", + "expo-linking": "~4.0.1" }, "devDependencies": { "@babel/core": "^7.20.0", diff --git a/src/components/AnimatedContainer/AnimatedContainer.tsx b/src/components/AnimatedContainer/AnimatedContainer.tsx new file mode 100644 index 0000000..838a688 --- /dev/null +++ b/src/components/AnimatedContainer/AnimatedContainer.tsx @@ -0,0 +1,21 @@ +import * as React from "react"; +import { View, Text } from "react-native"; +import styles from "../../styles"; +import { colors } from "../../styles"; +import { MotiView } from "moti"; +export interface props { + children: React.ReactNode; +} + +export default function AnimatedContainer(props: props) { + return ( + + {props.children} + + ); +} diff --git a/src/components/Api/Api.tsx b/src/components/Api/Api.tsx new file mode 100644 index 0000000..97dbec0 --- /dev/null +++ b/src/components/Api/Api.tsx @@ -0,0 +1,125 @@ +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 + +// Token Handling +export async function getAccessToken() { + const accessToken = await AsyncStorage.getItem("access_token"); + return accessToken; +} + +export async function getRefreshToken() { + const refreshToken = await AsyncStorage.getItem("refresh_token"); + return refreshToken; +} + +export async function setAccessToken(access: string) { + await AsyncStorage.setItem("access_token", access); + return true; +} + +export async function setRefreshToken(refresh: string) { + await AsyncStorage.setItem("refresh_token", refresh); + return true; +} + +// User APIs +export function UserRegister(register: RegistrationParams) { + console.log(JSON.stringify(register)); + return instance + .post("/api/v1/accounts/users/", register) + .then(async (response) => { + return [true, response.status]; + }) + .catch((error) => { + return [false, error.response.status, error.response.data]; + }); +} + +export function UserLogin(user: LoginParams) { + return instance + .post("/api/v1/accounts/jwt/create/", user) + .then(async (response) => { + /*console.log( + "Access Token:", + response.data.access, + "\nRefresh Token:", + response.data.refresh + );*/ + setAccessToken(response.data.access); + setRefreshToken(response.data.refresh); + return [true]; + }) + .catch((error) => { + console.log("Login Failed:" + error.response.data); + return [false, error.response.data]; + }); +} + +export async function TokenRefresh() { + const refresh = await getRefreshToken(); + // console.log("Refresh token", refresh); + return instance + .post("/api/v1/accounts/jwt/refresh/", { + refresh: refresh, + }) + .then(async (response) => { + setAccessToken(response.data.access); + /*console.log( + "Token refresh success! New Access Token", + response.data.access + );*/ + return [true]; + }) + .catch((error) => { + console.log("Refresh Failed: " + JSON.stringify(error.response.data)); + return [false, error.response.data]; + }); +} +export async function UserInfo() { + const accessToken = await getAccessToken(); + return instance + .get("/api/v1/accounts/users/me/", { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }) + .then((response) => { + // console.log(JSON.stringify(response.data)); + return response.data; + }) + .catch((error) => { + console.log("User Info Error", error.response.data); + return [false, error.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..6a843c7 --- /dev/null +++ b/src/components/Button/Button.tsx @@ -0,0 +1,23 @@ +import * as React from "react"; +import { 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..21332e3 100644 --- a/src/components/DrawerSettings/CustomDrawerContent.tsx +++ b/src/components/DrawerSettings/CustomDrawerContent.tsx @@ -11,47 +11,112 @@ import HomeIcon from "../../icons/HomeIcon/HomeIcon"; import LoginIcon from "../../icons/LoginIcon/LoginIcon"; import SignupIcon from "../../icons/SignupIcon/SignupIcon"; import DrawerButton from "../Button/DrawerButton"; +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 - - + 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()); + await AsyncStorage.clear(); + navigation.navigate("Home"); + }} + > + + Logout + + + ); + } else { + return ( + + + + Stud-E + + { + navigation.navigate("Home"); + }} + > + + Home + + { + navigation.navigate("Login"); + }} + > + + Login + + { + dispatch(clear()); + navigation.navigate("Register"); + }} + > + + Register + + {/* + Debug buttons for accessing revalidation and activation page + { + navigation.navigate("Revalidation"); + }} + > + Revalidation + + { + navigation.navigate("Activation"); + }} + > + Activation + + */} + + ); + } } 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..ab55ea6 --- /dev/null +++ b/src/components/ParseError/ParseError.tsx @@ -0,0 +1,24 @@ +export default function ParseError(text: string) { + if (text) { + return text + .replaceAll(/[{}()"]/g, " ") + .replaceAll(/,/g, "\n") + .replaceAll("[", "") + .replaceAll("]", "") + .replaceAll(".", ""); + } + return ""; +} + +export function ParseLoginError(text: string) { + if (text) { + return text + .replaceAll(/[{}()"]/g, " ") + .replaceAll(/,/g, "\n") + .replaceAll("[", "") + .replaceAll("]", "") + .replaceAll(".", "") + .replaceAll("non_field_errors", ""); + } + return ""; +} 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..b575579 --- /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: "", + logged_in: false, + }, + }, + reducers: { + login: (state) => { + state.creds.logged_in = true; + }, + setUser: (state, action) => { + state.creds = { + email: action.payload.email, + uid: action.payload.uid, + username: action.payload.username, + full_name: action.payload.full_name, + logged_in: true, + }; + }, + clear: (state) => { + state.creds = { + email: "", + uid: "", + username: "", + full_name: "", + logged_in: false, + }; + }, + }, +}); + +// Action creators are generated for each case reducer function +export const { login, 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 [state, setState] = useState( + "Activating with UID " + uid + " and Token " + token + ); + const [loading, setLoading] = useState(true); + + 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 ( + + + + Activation + + + {state} + {uid + "\n" + token} + + + ); +} diff --git a/src/routes/Home/Home.tsx b/src/routes/Home/Home.tsx index 42192de..944d131 100644 --- a/src/routes/Home/Home.tsx +++ b/src/routes/Home/Home.tsx @@ -1,20 +1,18 @@ import * as React from "react"; import styles from "../../styles"; -import { font_sizes } from "../../styles"; import { View, Text } from "react-native"; +import { useSelector } from "react-redux"; +import { RootState } from "../../features/redux/Store/Store"; +import AnimatedContainer from "../../components/AnimatedContainer/AnimatedContainer"; export default function Home() { + const creds = useSelector((state: RootState) => state.auth.creds); return ( - - Template Homepage - + + Template Homepage + {JSON.stringify(creds)} + ); } diff --git a/src/routes/Login/Login.tsx b/src/routes/Login/Login.tsx index 31510b2..d0b9cc7 100644 --- a/src/routes/Login/Login.tsx +++ b/src/routes/Login/Login.tsx @@ -1,20 +1,105 @@ 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 { useDispatch } from "react-redux"; +import { colors } from "../../styles"; +import { useState } from "react"; +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"; +import AnimatedContainer from "../../components/AnimatedContainer/AnimatedContainer"; export default function Login() { + const navigation = useNavigation(); + const dispatch = useDispatch(); + const [user, setUser] = useState({ + username: "", + password: "", + error: "", + }); return ( - - Template Login Page - + + + + + Student Login + + + + + ): void => { + setUser({ ...user, username: e.nativeEvent.text }); + }} + /> + + + ): void => { + setUser({ ...user, password: e.nativeEvent.text }); + }} + /> + + {user.error} + + + + ); } diff --git a/src/routes/Onboarding/Onboarding.tsx b/src/routes/Onboarding/Onboarding.tsx new file mode 100644 index 0000000..70baf56 --- /dev/null +++ b/src/routes/Onboarding/Onboarding.tsx @@ -0,0 +1,17 @@ +import * as React from "react"; +import styles from "../../styles"; +import { View, Text } from "react-native"; +import { useNavigation } from "@react-navigation/native"; +import { RootDrawerParamList } from "../../interfaces/Interfaces"; +export default function Onboarding() { + const navigation = useNavigation(); + // const dispatch = useDispatch(); + // const creds = useSelector((state: RootState) => state.auth.creds); + return ( + + + Template Onboarding Page + + + ); +} diff --git a/src/routes/Register/Register.tsx b/src/routes/Register/Register.tsx index 155dcd6..f484f11 100644 --- a/src/routes/Register/Register.tsx +++ b/src/routes/Register/Register.tsx @@ -1,20 +1,176 @@ 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 { 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"; +import AnimatedContainer from "../../components/AnimatedContainer/AnimatedContainer"; 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/routes/Revalidation/Revalidation.tsx b/src/routes/Revalidation/Revalidation.tsx new file mode 100644 index 0000000..622cebf --- /dev/null +++ b/src/routes/Revalidation/Revalidation.tsx @@ -0,0 +1,47 @@ +import * as React from "react"; +import styles from "../../styles"; +import { View, Text, ActivityIndicator } from "react-native"; +import { + TokenRefresh, + UserInfo, + setAccessToken, +} from "../../components/Api/Api"; +import { useDispatch } from "react-redux"; +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 [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} + + + ); +} 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;