forked from fr/deguapp
		
	Added: fully working signin/signup system with routing
This commit is contained in:
		| @@ -11,7 +11,7 @@ services: | ||||
|     image: mongo-express | ||||
|     restart: always | ||||
|     ports: | ||||
|       - 8081:8081 | ||||
|       - 8091:8081 | ||||
|     environment: | ||||
|       ME_CONFIG_MONGODB_ADMINUSERNAME: root | ||||
|       ME_CONFIG_MONGODB_ADMINPASSWORD: root | ||||
|   | ||||
| @@ -23,6 +23,7 @@ | ||||
| 		"bcrypt": "^5.1.1", | ||||
| 		"colors": "1.4.0", | ||||
| 		"cookie-parser": "^1.4.6", | ||||
| 		"cors": "^2.8.5", | ||||
| 		"dotenv": "^16.4.5", | ||||
| 		"express": "^4.19.2", | ||||
| 		"fs-extra": "^10.0.0", | ||||
| @@ -41,6 +42,7 @@ | ||||
| 		"@types/bcrypt": "^5.0.2", | ||||
| 		"@types/chai": "^4.2.22", | ||||
| 		"@types/cookie-parser": "^1.4.7", | ||||
| 		"@types/cors": "^2.8.17", | ||||
| 		"@types/express": "^4.17.21", | ||||
| 		"@types/fs-extra": "^9.0.13", | ||||
| 		"@types/inquirer": "^8.1.3", | ||||
|   | ||||
| @@ -1,29 +1,29 @@ | ||||
| import express from "express"; | ||||
| import morgan from "morgan"; | ||||
| import path from 'path' | ||||
| //import cors from 'cors' | ||||
| import cors from 'cors' | ||||
| import cookieParser from 'cookie-parser' | ||||
| import { router as routes } from "./routes"; | ||||
| //import { router as middlewares } from './middlewares' | ||||
| //import env from './config/environment' | ||||
| import env from './config/environment' | ||||
|  | ||||
| //export let corsWhitelist: Array<string> | ||||
| //if (env.CORS_WHITELIST != 'undefined') { | ||||
| //	corsWhitelist = [...['http://localhost:8080', 'http://localhost:6040'], ...env.CORS_WHITELIST.split(';')] | ||||
| //} else { | ||||
| //	corsWhitelist = ['http://localhost:8080', 'http://localhost:6040'] | ||||
| //} | ||||
| //const corsOptions = { | ||||
| //	origin: function (origin: any, callback: any) { | ||||
| //		if (!origin || corsWhitelist.indexOf(origin) !== -1) { | ||||
| //			callback(null, true) | ||||
| //		} else { | ||||
| //			callback(new Error('Not allowed by CORS')) | ||||
| //		} | ||||
| //	}, | ||||
| //	optionsSuccessStatus: 200, | ||||
| //	credentials: true | ||||
| //} | ||||
| export let corsWhitelist: Array<string> | ||||
| if (env.CORS_WHITELIST != 'undefined') { | ||||
| 	corsWhitelist = [...['http://localhost:8080', 'http://localhost:6040'], ...env.CORS_WHITELIST.split(';')] | ||||
| } else { | ||||
| 	corsWhitelist = ['http://localhost:8080', 'http://localhost:6040'] | ||||
| } | ||||
| const corsOptions = { | ||||
| 	origin: function (origin: any, callback: any) { | ||||
| 		if (!origin || corsWhitelist.indexOf(origin) !== -1) { | ||||
| 			callback(null, true) | ||||
| 		} else { | ||||
| 			callback(new Error('Not allowed by CORS')) | ||||
| 		} | ||||
| 	}, | ||||
| 	optionsSuccessStatus: 200, | ||||
| 	credentials: true | ||||
| } | ||||
|  | ||||
| export const app = express(); | ||||
|  | ||||
| @@ -31,7 +31,7 @@ export const app = express(); | ||||
| //app.use(middlewares) | ||||
| //app.set('view engine', 'ejs') | ||||
| //app.set('views', path.join(__dirname, 'views')) | ||||
| //app.use(cors(corsOptions)) | ||||
| app.use(cors(corsOptions)) | ||||
| app.use(morgan("dev")); | ||||
| app.use(express.urlencoded({ extended: true })); | ||||
| app.use(express.json()); | ||||
|   | ||||
| @@ -52,7 +52,7 @@ export async function signin_post(req: Request, res: Response) { | ||||
| 			res.cookie('jwt', token, { httpOnly: true, maxAge: maxAge * 1000 }); | ||||
| 			res.cookie('auth', true, { httpOnly: false, maxAge: maxAge * 1000 }); | ||||
|  | ||||
| 			res.json(Log.info(200, 'user is logged in')); | ||||
| 			res.json(Log.info(200, 'user is logged in', {jwt: token})); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
|   | ||||
| @@ -18,6 +18,7 @@ router.get('/', docsController.docs_get); | ||||
|  | ||||
| router.post("/auth/signup",validate(AuthVal.signup) , authController.signup_post); | ||||
| router.post("/auth/signin",validate(AuthVal.signin) , authController.signin_post); | ||||
| router.options("/auth/signin",validate(AuthVal.signin) , authController.signin_post); | ||||
| router.post("/auth/logout", requireAuth, authController.logout_post); | ||||
| router.get("/auth/status", requireAuth, authController.status_get); | ||||
|  | ||||
|   | ||||
| @@ -2,5 +2,5 @@ import * as shell from 'shelljs'; | ||||
|  | ||||
| // Copy all the view templates | ||||
| //shell.cp('-R', 'src/views', 'dist/') | ||||
| //shell.cp('-R', 'src/public', 'dist/'); | ||||
| shell.cp('-R', 'src/public', 'dist/'); | ||||
| shell.cp('-u', 'src/.env', 'dist/'); | ||||
| @@ -2,6 +2,7 @@ | ||||
|   "expo": { | ||||
|     "name": "deguapp", | ||||
|     "slug": "deguapp", | ||||
|     "scheme": "deguapp", | ||||
|     "version": "1.0.0", | ||||
|     "orientation": "portrait", | ||||
|     "icon": "./assets/icon.png", | ||||
| @@ -25,6 +26,9 @@ | ||||
|     }, | ||||
|     "web": { | ||||
|       "favicon": "./assets/favicon.png" | ||||
|     } | ||||
|     }, | ||||
|     "plugins": [ | ||||
|       "expo-router" | ||||
|     ] | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										22
									
								
								frontend/app/(app)/_layout.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								frontend/app/(app)/_layout.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| import { Redirect, Stack } from "expo-router"; | ||||
|  | ||||
| import { useAuth } from "../context/AuthContext"; | ||||
| import { View, Text } from "react-native"; | ||||
|  | ||||
| export default function AppLayout() { | ||||
|   const { authState } = useAuth(); | ||||
|  | ||||
|   if (authState.authenticated === null) { | ||||
|     // micro loading co neni skoro videt ale get the fuck out se uz neloguje | ||||
|     return ( | ||||
|       <View> | ||||
|         <Text>Loading...</Text> | ||||
|       </View> | ||||
|     ); | ||||
|   } | ||||
|   if (!authState.authenticated) { | ||||
|     console.log("get the fuck out"); | ||||
|     return <Redirect href="/login" />; | ||||
|   } | ||||
|   return <Stack />; | ||||
| } | ||||
							
								
								
									
										21
									
								
								frontend/app/(app)/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								frontend/app/(app)/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| import { Text, View } from "react-native"; | ||||
|  | ||||
| import { useAuth } from "../context/AuthContext"; | ||||
|  | ||||
| export default function Index() { | ||||
|   const { onLogout } = useAuth(); | ||||
|   const user = "debil" | ||||
|   return ( | ||||
|     <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}> | ||||
|       <Text>Welcome {user}</Text> | ||||
|       <Text | ||||
|         onPress={() => { | ||||
|           // The `app/(app)/_layout.tsx` will redirect to the sign-in screen. | ||||
|           onLogout(); | ||||
|         }} | ||||
|       > | ||||
|         Sign Out | ||||
|       </Text> | ||||
|     </View> | ||||
|   ); | ||||
| } | ||||
							
								
								
									
										10
									
								
								frontend/app/_layout.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								frontend/app/_layout.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| import { Slot } from "expo-router"; | ||||
| import { AuthProvider } from "./context/AuthContext"; | ||||
|  | ||||
| export default function Root() { | ||||
|   return ( | ||||
|     <AuthProvider> | ||||
|       <Slot /> | ||||
|     </AuthProvider> | ||||
|   ); | ||||
| } | ||||
							
								
								
									
										103
									
								
								frontend/app/context/AuthContext.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								frontend/app/context/AuthContext.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | ||||
| import { createContext, useContext, useEffect, useState } from "react"; | ||||
| import axios from "axios"; | ||||
| import storageUtil from "./storage"; | ||||
|  | ||||
| const TOKEN_KEY = "my-jwt"; | ||||
| export const API_URL = "http://10.69.1.137:6060/api/v1"; | ||||
| const AuthContext = createContext(null); | ||||
|  | ||||
| export function useAuth() { | ||||
|   const authContext = useContext(AuthContext); | ||||
|   if (authContext === undefined) { | ||||
|     throw new Error("Context is outside of provider"); | ||||
|   } | ||||
|   return authContext; | ||||
| } | ||||
|  | ||||
| export function AuthProvider({ children }) { | ||||
|   const [authState, setAuthState] = useState({ | ||||
|     token: null, | ||||
|     authenticated: null, | ||||
|   }); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     // tohle se zavola jen poprve pri startu appky | ||||
|     async function loadToken() { | ||||
|       const token = await storageUtil.getItem(TOKEN_KEY); | ||||
|       console.log(`stored: ${token}`); | ||||
|  | ||||
|       if (token) { | ||||
|         axios.defaults.headers.common["Authorization"] = `Bearer ${token}`; | ||||
|  | ||||
|         setAuthState({ | ||||
|           token: token, | ||||
|           authenticated: true, | ||||
|         }); | ||||
|  | ||||
|         return; | ||||
|       } | ||||
|       setAuthState({ | ||||
|         authenticated: false, | ||||
|         token: null, | ||||
|       }); | ||||
|     } | ||||
|     loadToken(); | ||||
|   }, []); | ||||
|  | ||||
|   async function register(username, email, password) { | ||||
|     try { | ||||
|       const res = await axios.post(`${API_URL}/auth/signup`, { | ||||
|         username, | ||||
|         email, | ||||
|         password, | ||||
|       }); | ||||
|       return res | ||||
|     } catch (err) { | ||||
|       return { error: true, msg: err.response.data}; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   async function login(email, password) { | ||||
|     try { | ||||
|       const res = await axios.post(`${API_URL}/auth/signin`, { | ||||
|         email, | ||||
|         password, | ||||
|       }); | ||||
|  | ||||
|       setAuthState({ | ||||
|         token: res.data.data.jwt, | ||||
|         authenticated: true, | ||||
|       }); | ||||
|  | ||||
|       //axios.defaults.headers.common[ | ||||
|       //  "Authorization" | ||||
|       //] = `Bearer ${res.data.data.jwt}`; | ||||
|  | ||||
|       await storageUtil.setItem(TOKEN_KEY, res.data.data.jwt); | ||||
|  | ||||
|       return res | ||||
|     } catch (err) { | ||||
|       return { error: true, msg: err.res }; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   async function logout() { | ||||
|     await storageUtil.delItem(TOKEN_KEY); | ||||
|  | ||||
|     axios.defaults.headers.common["Authorization"] = ""; | ||||
|  | ||||
|     setAuthState({ | ||||
|       token: null, | ||||
|       authenticated: false, | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   const value = { | ||||
|     onSignin: register, | ||||
|     onLogin: login, | ||||
|     onLogout: logout, | ||||
|     authState, | ||||
|   }; | ||||
|  | ||||
|   return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>; | ||||
| } | ||||
							
								
								
									
										34
									
								
								frontend/app/context/storage.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								frontend/app/context/storage.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| import * as SecureStore from "expo-secure-store"; | ||||
| import { Platform } from "react-native"; | ||||
| import AsyncStorage from "@react-native-async-storage/async-storage"; | ||||
|  | ||||
| const storageUtil = { | ||||
|   setItem: async (k, v) => { | ||||
|     if (Platform.OS === "web") { | ||||
|       // web | ||||
|       await AsyncStorage.setItem(k, v); | ||||
|     } else { | ||||
|       // mobile | ||||
|       await SecureStore.setItemAsync(k, v.toString()); // v must be string, | ||||
|     } | ||||
|   }, | ||||
|   getItem: async (k) => { | ||||
|     if (Platform.OS === "web") { | ||||
|       // web | ||||
|       return await AsyncStorage.getItem(k); | ||||
|     } else { | ||||
|       // mobile | ||||
|       return await SecureStore.getItemAsync(k); | ||||
|     } | ||||
|   }, | ||||
|   delItem: async (k) => { | ||||
|     if (Platform.OS === "web") { | ||||
|       // web | ||||
|       await AsyncStorage.removeItem(k); | ||||
|     } else { | ||||
|       // mobile | ||||
|       await SecureStore.deleteItemAsync(k); | ||||
|     } | ||||
|   }, | ||||
| }; | ||||
| export default storageUtil; | ||||
							
								
								
									
										6
									
								
								frontend/app/hooks/useIsAutheticated.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								frontend/app/hooks/useIsAutheticated.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| import { useAuth } from "../context/AuthContext"; | ||||
|  | ||||
| export function useIsAutheticated() { | ||||
|   const { authState } = useAuth(); | ||||
|   return authState.authenticated | ||||
| } | ||||
							
								
								
									
										9
									
								
								frontend/app/index.js.old
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								frontend/app/index.js.old
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| import { Redirect, Link } from "expo-router"; | ||||
| import { Text, View } from "react-native"; | ||||
| import { AuthProvider } from "./context/AuthContext"; | ||||
|  | ||||
| function HomePage() { | ||||
|   return <Redirect href="/login" />; | ||||
| } | ||||
|  | ||||
| export default HomePage; | ||||
| @@ -1,15 +1,26 @@ | ||||
| import { | ||||
|   StyleSheet, | ||||
|   TouchableOpacity, | ||||
|   TextInput, | ||||
|   View, | ||||
|   Text, | ||||
|   Image, | ||||
| } from "react-native"; | ||||
| import { StyleSheet, TextInput, View, Text, Image } from "react-native"; | ||||
| import { useEffect, useState } from "react"; | ||||
| import { router } from "expo-router"; | ||||
| import Button from "../components/Button"; | ||||
| import { colors } from "../components/style"; | ||||
| 
 | ||||
| export function LoginForm() { | ||||
| import { useAuth } from "./context/AuthContext"; | ||||
| 
 | ||||
| function LoginPage() { | ||||
|   const [pass, setPass] = useState(""); | ||||
|   const [email, setEmail] = useState(""); | ||||
|   const { onLogin, authState } = useAuth(); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (authState.authenticated) { | ||||
|       router.replace("/"); | ||||
|     } | ||||
|   }, [authState.authenticated]); | ||||
| 
 | ||||
|   function login() { | ||||
|     onLogin(email, pass); | ||||
|   } | ||||
| 
 | ||||
|   return ( | ||||
|     <View style={styles.container}> | ||||
|       <View style={styles.header}> | ||||
| @@ -19,7 +30,6 @@ export function LoginForm() { | ||||
|         /> | ||||
|         <Text style={styles.h1}>Please Log In</Text> | ||||
|       </View> | ||||
| 
 | ||||
|       <View style={styles.form}> | ||||
|         <TextInput | ||||
|           style={styles.input} | ||||
| @@ -30,6 +40,8 @@ export function LoginForm() { | ||||
|           keyboardType="email-address" | ||||
|           placeholderTextColor={"#aaaaaa"} | ||||
|           returnKeyType="next" | ||||
|           value={email} | ||||
|           onChangeText={(text) => setEmail(text)} | ||||
|         /> | ||||
|         <TextInput | ||||
|           style={styles.input} | ||||
| @@ -37,19 +49,21 @@ export function LoginForm() { | ||||
|           placeholder="Enter your password" | ||||
|           placeholderTextColor={"#aaaaaa"} | ||||
|           returnKeyType="done" | ||||
|           value={pass} | ||||
|           onChangeText={(text) => setPass(text)} | ||||
|         /> | ||||
|         <View style={styles.btnContainer}> | ||||
|           <Button | ||||
|             style={styles.button} | ||||
|             title="Sign Up" | ||||
|             color={colors.charcoal} | ||||
|             onPress={() => alert("Signed In")} | ||||
|             onPress={() => router.replace("/signup")} | ||||
|           /> | ||||
|           <Button | ||||
|             style={styles.button} | ||||
|             title="Log In" | ||||
|             color={colors.gold} | ||||
|             onPress={() => alert("Logged In")} | ||||
|             onPress={login} | ||||
|           /> | ||||
|         </View> | ||||
|       </View> | ||||
| @@ -92,9 +106,12 @@ const styles = StyleSheet.create({ | ||||
|     borderWidth: 1, | ||||
|     borderRadius: 5, | ||||
|     padding: 10, | ||||
|     color: "#fff", | ||||
|   }, | ||||
|   btnContainer: { | ||||
|     flexDirection: "row", | ||||
|     gap: 5, | ||||
|   }, | ||||
| }); | ||||
| 
 | ||||
| export default LoginPage; | ||||
							
								
								
									
										148
									
								
								frontend/app/signup.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								frontend/app/signup.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,148 @@ | ||||
| import { StyleSheet, TextInput, View, Text, Image } from "react-native"; | ||||
| import { useState } from "react"; | ||||
| import Button from "../components/Button"; | ||||
| import { colors } from "../components/style"; | ||||
| import { Link, router } from "expo-router"; | ||||
|  | ||||
| import { useAuth } from "./context/AuthContext"; | ||||
|  | ||||
| function SignupPage() { | ||||
|   const [pass1, setPass1] = useState(""); | ||||
|   const [pass2, setPass2] = useState(""); | ||||
|   const [email, setEmail] = useState(""); | ||||
|   const [username, setUsername] = useState(""); | ||||
|   const { onSignin } = useAuth(); | ||||
|  | ||||
|   async function signin() { | ||||
|     if (pass1 == pass2) { | ||||
|       const res = await onSignin(username, email, pass1); | ||||
|       if (res.error) { | ||||
|         if(res.msg.message == "validation error") { | ||||
|           alert(res.msg.data.message); | ||||
|         } else { | ||||
|           alert(res.msg.message) | ||||
|         } | ||||
|       } | ||||
|       if (!res.error) { | ||||
|         alert("You have been successfully registered. Please Log In"); | ||||
|         router.replace("/login"); | ||||
|       } | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     alert("Passwords are not same!"); | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <View style={styles.container}> | ||||
|       <View style={styles.header}> | ||||
|         <Image | ||||
|           source={require("../assets/deguapp_logo.png")} | ||||
|           style={styles.logo} | ||||
|         /> | ||||
|         <Text style={styles.h1}>Please Sign Up</Text> | ||||
|       </View> | ||||
|  | ||||
|       <View style={styles.form}> | ||||
|         <TextInput | ||||
|           style={styles.input} | ||||
|           placeholder="Enter your username" | ||||
|           placeholderTextColor={"#aaaaaa"} | ||||
|           returnKeyType="done" | ||||
|           value={username} | ||||
|           onChangeText={(username) => setUsername(username)} | ||||
|         /> | ||||
|         <TextInput | ||||
|           style={styles.input} | ||||
|           placeholder="Enter your email" | ||||
|           autoCapitalize="none" | ||||
|           autoCompleteType="email" | ||||
|           textContentType="emailAddress" | ||||
|           keyboardType="email-address" | ||||
|           placeholderTextColor={"#aaaaaa"} | ||||
|           returnKeyType="next" | ||||
|           value={email} | ||||
|           onChangeText={(email) => setEmail(email)} | ||||
|         /> | ||||
|         <TextInput | ||||
|           style={styles.input} | ||||
|           secureTextEntry={true} | ||||
|           placeholder="Enter your password" | ||||
|           placeholderTextColor={"#aaaaaa"} | ||||
|           returnKeyType="done" | ||||
|           value={pass1} | ||||
|           onChangeText={(pass1) => setPass1(pass1)} | ||||
|         /> | ||||
|         <TextInput | ||||
|           style={styles.input} | ||||
|           secureTextEntry={true} | ||||
|           placeholder="Enter your password" | ||||
|           placeholderTextColor={"#aaaaaa"} | ||||
|           returnKeyType="done" | ||||
|           value={pass2} | ||||
|           onChangeText={(pass2) => setPass2(pass2)} | ||||
|         /> | ||||
|         <Button | ||||
|           style={styles.button} | ||||
|           title="Sign Up" | ||||
|           color={colors.gold} | ||||
|           onPress={signin} | ||||
|         /> | ||||
|         <Link href="/login" style={styles.a}> | ||||
|           Already have an account? Log In! | ||||
|         </Link> | ||||
|       </View> | ||||
|     </View> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| const styles = StyleSheet.create({ | ||||
|   container: { | ||||
|     width: "100%", | ||||
|     height: "100%", | ||||
|     backgroundColor: colors.dark, | ||||
|   }, | ||||
|   form: { | ||||
|     flex: 1, | ||||
|     alignItems: "center", | ||||
|     paddingTop: "10%", | ||||
|     width: "100%", | ||||
|     gap: 15, | ||||
|   }, | ||||
|   h1: { | ||||
|     color: "#FFF", | ||||
|     fontSize: 30, | ||||
|     textAlign: "center", | ||||
|     paddingTop: "20%", | ||||
|   }, | ||||
|   a: { | ||||
|     color: "#FFF", | ||||
|     fontSize: 12, | ||||
|     fontStyle: "italic", | ||||
|     textDecorationLine: "underline", | ||||
|   }, | ||||
|   logo: { | ||||
|     width: "80%", | ||||
|     resizeMode: "contain", | ||||
|   }, | ||||
|   header: { | ||||
|     width: "100%", | ||||
|     alignItems: "center", | ||||
|     paddingTop: "20%", | ||||
|   }, | ||||
|   input: { | ||||
|     height: "auto", | ||||
|     width: "60%", | ||||
|     borderColor: "gray", | ||||
|     borderWidth: 1, | ||||
|     borderRadius: 5, | ||||
|     padding: 10, | ||||
|     color: "#fff", | ||||
|   }, | ||||
|   btnContainer: { | ||||
|     flexDirection: "row", | ||||
|     gap: 5, | ||||
|   }, | ||||
| }); | ||||
|  | ||||
| export default SignupPage; | ||||
							
								
								
									
										
											BIN
										
									
								
								frontend/assets/deguapp_logo_v2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								frontend/assets/deguapp_logo_v2.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 79 KiB | 
							
								
								
									
										772
									
								
								frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										772
									
								
								frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,7 +1,7 @@ | ||||
| { | ||||
|   "name": "deguapp", | ||||
|   "version": "1.0.0", | ||||
|   "main": "node_modules/expo/AppEntry.js", | ||||
|   "main": "expo-router/entry", | ||||
|   "scripts": { | ||||
|     "start": "expo start", | ||||
|     "android": "expo start --android", | ||||
| @@ -9,13 +9,22 @@ | ||||
|     "web": "expo start --web" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@expo/metro-runtime": "~3.1.3", | ||||
|     "@react-native-async-storage/async-storage": "^1.23.1", | ||||
|     "@types/react": "~18.2.45", | ||||
|     "axios": "^1.6.8", | ||||
|     "expo": "~50.0.17", | ||||
|     "expo-constants": "~15.4.6", | ||||
|     "expo-linking": "~6.2.2", | ||||
|     "expo-router": "~3.4.10", | ||||
|     "expo-secure-store": "^12.8.1", | ||||
|     "expo-status-bar": "~1.11.1", | ||||
|     "react": "18.2.0", | ||||
|     "react-native": "0.73.6", | ||||
|     "react-native-web": "~0.19.6", | ||||
|     "react-dom": "18.2.0", | ||||
|     "@expo/metro-runtime": "~3.1.3" | ||||
|     "react-native": "0.73.6", | ||||
|     "react-native-safe-area-context": "4.8.2", | ||||
|     "react-native-screens": "~3.29.0", | ||||
|     "react-native-web": "~0.19.6" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@babel/core": "^7.20.0" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user