├── starter-files ├── backend │ ├── .dockerignore │ ├── .gitignore │ ├── src │ │ ├── models │ │ │ ├── User.js │ │ │ ├── Registration.js │ │ │ └── Event.js │ │ ├── config │ │ │ ├── verifyToken.js │ │ │ ├── upload.js │ │ │ └── s3Upload.js │ │ ├── controllers │ │ │ ├── ApprovalController.js │ │ │ ├── RejectionController.js │ │ │ ├── LoginController.js │ │ │ ├── UserController.js │ │ │ ├── DashboardController.js │ │ │ ├── EventController.js │ │ │ └── RegistrationController.js │ │ ├── server.js │ │ └── routes.js │ ├── docker-compose.yml │ ├── Dockerfile │ ├── .eslintrc.json │ └── package.json └── mobile-app │ ├── assets │ ├── icon.png │ ├── splash.png │ ├── favicon.png │ ├── background.jpg │ └── adaptive-icon.png │ ├── babel.config.js │ ├── .gitignore │ ├── App.js │ ├── app.json │ └── package.json ├── episode-0-settup └── mobile-app │ ├── assets │ ├── icon.png │ ├── favicon.png │ ├── splash.png │ ├── background.jpg │ └── adaptive-icon.png │ ├── babel.config.js │ ├── .gitignore │ ├── pages │ ├── Register.js │ └── Login.js │ ├── app.json │ ├── package.json │ └── App.js ├── episode-1-login-register └── mobile-app │ ├── assets │ ├── icon.png │ ├── favicon.png │ ├── splash.png │ ├── background.jpg │ └── adaptive-icon.png │ ├── babel.config.js │ ├── .gitignore │ ├── app.json │ ├── package.json │ ├── pages │ ├── Dashboard.js │ ├── Login.js │ └── Register.js │ └── App.js ├── episode-2-start-dashboard └── mobile-app │ ├── assets │ ├── icon.png │ ├── splash.png │ ├── favicon.png │ ├── background.jpg │ └── adaptive-icon.png │ ├── babel.config.js │ ├── .gitignore │ ├── app.json │ ├── package.json │ ├── App.js │ └── pages │ ├── Dashboard.js │ ├── Login.js │ └── Register.js ├── episode-5-dashboard-filter └── mobile-app │ ├── assets │ ├── icon.png │ ├── favicon.png │ ├── splash.png │ ├── background.jpg │ └── adaptive-icon.png │ ├── babel.config.js │ ├── .gitignore │ ├── app.json │ ├── hooks │ └── isLoggedIn.js │ ├── package.json │ ├── App.js │ ├── pages │ ├── Login.js │ ├── Register.js │ └── Dashboard.js │ └── components │ └── ModalComponent.js ├── episode-4-fetch-created-events └── mobile-app │ ├── assets │ ├── icon.png │ ├── splash.png │ ├── favicon.png │ ├── background.jpg │ └── adaptive-icon.png │ ├── babel.config.js │ ├── .gitignore │ ├── app.json │ ├── hooks │ └── isLoggedIn.js │ ├── package.json │ ├── App.js │ ├── pages │ ├── Login.js │ ├── Dashboard.js │ └── Register.js │ └── components │ └── ModalComponent.js ├── episode-3-add-create-event-page └── mobile-app │ ├── assets │ ├── icon.png │ ├── favicon.png │ ├── splash.png │ ├── background.jpg │ └── adaptive-icon.png │ ├── babel.config.js │ ├── .gitignore │ ├── app.json │ ├── hooks │ └── isLoggedIn.js │ ├── package.json │ ├── App.js │ ├── pages │ ├── Login.js │ ├── Dashboard.js │ └── Register.js │ └── components │ └── ModalComponent.js └── README.md /starter-files/backend/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log -------------------------------------------------------------------------------- /starter-files/backend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | data 4 | files -------------------------------------------------------------------------------- /starter-files/mobile-app/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/starter-files/mobile-app/assets/icon.png -------------------------------------------------------------------------------- /starter-files/mobile-app/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/starter-files/mobile-app/assets/splash.png -------------------------------------------------------------------------------- /episode-0-settup/mobile-app/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-0-settup/mobile-app/assets/icon.png -------------------------------------------------------------------------------- /starter-files/mobile-app/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/starter-files/mobile-app/assets/favicon.png -------------------------------------------------------------------------------- /episode-0-settup/mobile-app/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-0-settup/mobile-app/assets/favicon.png -------------------------------------------------------------------------------- /episode-0-settup/mobile-app/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-0-settup/mobile-app/assets/splash.png -------------------------------------------------------------------------------- /starter-files/mobile-app/assets/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/starter-files/mobile-app/assets/background.jpg -------------------------------------------------------------------------------- /episode-0-settup/mobile-app/assets/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-0-settup/mobile-app/assets/background.jpg -------------------------------------------------------------------------------- /starter-files/mobile-app/assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/starter-files/mobile-app/assets/adaptive-icon.png -------------------------------------------------------------------------------- /episode-0-settup/mobile-app/assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-0-settup/mobile-app/assets/adaptive-icon.png -------------------------------------------------------------------------------- /episode-1-login-register/mobile-app/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-1-login-register/mobile-app/assets/icon.png -------------------------------------------------------------------------------- /episode-2-start-dashboard/mobile-app/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-2-start-dashboard/mobile-app/assets/icon.png -------------------------------------------------------------------------------- /starter-files/mobile-app/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /episode-0-settup/mobile-app/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /episode-1-login-register/mobile-app/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-1-login-register/mobile-app/assets/favicon.png -------------------------------------------------------------------------------- /episode-1-login-register/mobile-app/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-1-login-register/mobile-app/assets/splash.png -------------------------------------------------------------------------------- /episode-2-start-dashboard/mobile-app/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-2-start-dashboard/mobile-app/assets/splash.png -------------------------------------------------------------------------------- /episode-5-dashboard-filter/mobile-app/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-5-dashboard-filter/mobile-app/assets/icon.png -------------------------------------------------------------------------------- /episode-1-login-register/mobile-app/assets/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-1-login-register/mobile-app/assets/background.jpg -------------------------------------------------------------------------------- /episode-1-login-register/mobile-app/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /episode-2-start-dashboard/mobile-app/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-2-start-dashboard/mobile-app/assets/favicon.png -------------------------------------------------------------------------------- /episode-2-start-dashboard/mobile-app/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /episode-4-fetch-created-events/mobile-app/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-4-fetch-created-events/mobile-app/assets/icon.png -------------------------------------------------------------------------------- /episode-5-dashboard-filter/mobile-app/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-5-dashboard-filter/mobile-app/assets/favicon.png -------------------------------------------------------------------------------- /episode-5-dashboard-filter/mobile-app/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-5-dashboard-filter/mobile-app/assets/splash.png -------------------------------------------------------------------------------- /episode-2-start-dashboard/mobile-app/assets/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-2-start-dashboard/mobile-app/assets/background.jpg -------------------------------------------------------------------------------- /episode-3-add-create-event-page/mobile-app/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-3-add-create-event-page/mobile-app/assets/icon.png -------------------------------------------------------------------------------- /episode-4-fetch-created-events/mobile-app/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-4-fetch-created-events/mobile-app/assets/splash.png -------------------------------------------------------------------------------- /episode-4-fetch-created-events/mobile-app/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /episode-5-dashboard-filter/mobile-app/assets/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-5-dashboard-filter/mobile-app/assets/background.jpg -------------------------------------------------------------------------------- /episode-5-dashboard-filter/mobile-app/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /episode-1-login-register/mobile-app/assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-1-login-register/mobile-app/assets/adaptive-icon.png -------------------------------------------------------------------------------- /episode-2-start-dashboard/mobile-app/assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-2-start-dashboard/mobile-app/assets/adaptive-icon.png -------------------------------------------------------------------------------- /episode-3-add-create-event-page/mobile-app/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-3-add-create-event-page/mobile-app/assets/favicon.png -------------------------------------------------------------------------------- /episode-3-add-create-event-page/mobile-app/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-3-add-create-event-page/mobile-app/assets/splash.png -------------------------------------------------------------------------------- /episode-3-add-create-event-page/mobile-app/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /episode-4-fetch-created-events/mobile-app/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-4-fetch-created-events/mobile-app/assets/favicon.png -------------------------------------------------------------------------------- /episode-5-dashboard-filter/mobile-app/assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-5-dashboard-filter/mobile-app/assets/adaptive-icon.png -------------------------------------------------------------------------------- /episode-3-add-create-event-page/mobile-app/assets/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-3-add-create-event-page/mobile-app/assets/background.jpg -------------------------------------------------------------------------------- /episode-4-fetch-created-events/mobile-app/assets/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-4-fetch-created-events/mobile-app/assets/background.jpg -------------------------------------------------------------------------------- /episode-3-add-create-event-page/mobile-app/assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-3-add-create-event-page/mobile-app/assets/adaptive-icon.png -------------------------------------------------------------------------------- /episode-4-fetch-created-events/mobile-app/assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanrauwers/react-native-bootcamp/HEAD/episode-4-fetch-created-events/mobile-app/assets/adaptive-icon.png -------------------------------------------------------------------------------- /starter-files/mobile-app/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo/* 3 | .expo-shared 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | 13 | # macOS 14 | .DS_Store 15 | -------------------------------------------------------------------------------- /episode-0-settup/mobile-app/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo/* 3 | .expo-shared 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | 13 | # macOS 14 | .DS_Store 15 | -------------------------------------------------------------------------------- /episode-1-login-register/mobile-app/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo/* 3 | .expo-shared 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | 13 | # macOS 14 | .DS_Store 15 | -------------------------------------------------------------------------------- /episode-2-start-dashboard/mobile-app/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo/* 3 | .expo-shared 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | 13 | # macOS 14 | .DS_Store 15 | -------------------------------------------------------------------------------- /episode-5-dashboard-filter/mobile-app/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo/* 3 | .expo-shared 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | 13 | # macOS 14 | .DS_Store 15 | -------------------------------------------------------------------------------- /episode-3-add-create-event-page/mobile-app/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo/* 3 | .expo-shared 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | 13 | # macOS 14 | .DS_Store 15 | -------------------------------------------------------------------------------- /episode-4-fetch-created-events/mobile-app/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo/* 3 | .expo-shared 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | 13 | # macOS 14 | .DS_Store 15 | -------------------------------------------------------------------------------- /episode-0-settup/mobile-app/pages/Register.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { View, Text } from 'react-native' 3 | 4 | const Register = () => { 5 | return ( 6 | 7 | Register PAGE 8 | 9 | ) 10 | } 11 | 12 | export default Register; -------------------------------------------------------------------------------- /starter-files/backend/src/models/User.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | 3 | const UserSchema = new mongoose.Schema({ 4 | firstName: String, 5 | lastName: String, 6 | password: String, 7 | email: String, 8 | }) 9 | 10 | module.exports = mongoose.model('User', UserSchema) 11 | -------------------------------------------------------------------------------- /starter-files/backend/src/config/verifyToken.js: -------------------------------------------------------------------------------- 1 | function verifyToken(req, res, next) { 2 | const bearerToken = req.header('user') 3 | if (typeof bearerToken !== 'undefined') { 4 | req.token = bearerToken 5 | next() 6 | } else { 7 | res.sendStatus(401) 8 | } 9 | } 10 | 11 | module.exports = verifyToken -------------------------------------------------------------------------------- /starter-files/backend/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | node-api: 4 | container_name: backend 5 | image: react-native-bootcamp/backend:1.0.0 6 | restart: always 7 | build: . 8 | ports: 9 | - "8080:8080" 10 | links: 11 | - mongo 12 | mongo: 13 | container_name: mongo 14 | image: mongo 15 | volumes: 16 | - ./data:/data/db 17 | ports: 18 | - "27017:27017" 19 | -------------------------------------------------------------------------------- /starter-files/backend/src/config/upload.js: -------------------------------------------------------------------------------- 1 | const multer = require('multer') 2 | const path = require('path') 3 | 4 | module.exports = { 5 | storage: multer.diskStorage({ 6 | destination: path.resolve(__dirname, '..', '..', 'files'), 7 | filename: (req, file, cb) => { 8 | const ext = path.extname(file.originalname) 9 | const name = path.basename(file.originalname, ext) 10 | 11 | cb(null, `${name.replace(/\s/g, '')}-${Date.now()}${ext}`) 12 | } 13 | }) 14 | } -------------------------------------------------------------------------------- /starter-files/backend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14 2 | 3 | # Create app directory 4 | WORKDIR /usr/src/app 5 | 6 | # Install app dependencies 7 | # A wildcard is used to ensure both package.json AND package-lock.json are copied 8 | # where available (npm@5+) 9 | COPY package*.json ./ 10 | 11 | # RUN npm install 12 | # If you are building your code for production 13 | RUN npm ci --only=production 14 | 15 | # Bundle app source 16 | COPY . . 17 | 18 | EXPOSE 8080 19 | CMD [ "node", "src/server.js" ] -------------------------------------------------------------------------------- /starter-files/backend/src/models/Registration.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | 3 | const RegistrationSchema = new mongoose.Schema({ 4 | date: () => Date.now(), 5 | approved: Boolean, 6 | owner: String, 7 | eventTitle: String, 8 | eventPrice: String, 9 | userEmail:String, 10 | eventDate:String, 11 | user: { 12 | type: mongoose.Schema.Types.ObjectId, 13 | ref: 'User' 14 | }, 15 | event: { 16 | type: mongoose.Schema.Types.ObjectId, 17 | ref: 'Event' 18 | } 19 | }) 20 | 21 | module.exports = mongoose.model('Registration', RegistrationSchema) -------------------------------------------------------------------------------- /starter-files/backend/src/controllers/ApprovalController.js: -------------------------------------------------------------------------------- 1 | const Registration = require('../models/Registration') 2 | const jwt = require('jsonwebtoken') 3 | 4 | module.exports = { 5 | approval(req, res) { 6 | jwt.verify(req.token, 'secret', async (err, authData) => { 7 | if (err) { 8 | res.sendStatus(401) 9 | } else { 10 | const { registration_id } = req.params 11 | 12 | try { 13 | const registration = await Registration.findById(registration_id); 14 | 15 | if (registration) { 16 | registration.approved = true 17 | await registration.save() 18 | 19 | return res.json(registration) 20 | } 21 | 22 | } catch (error) { 23 | return res.status(400).json(error) 24 | } 25 | } 26 | }) 27 | } 28 | } -------------------------------------------------------------------------------- /starter-files/backend/src/controllers/RejectionController.js: -------------------------------------------------------------------------------- 1 | const Registration = require('../models/Registration') 2 | const jwt = require('jsonwebtoken') 3 | 4 | module.exports = { 5 | rejection(req, res) { 6 | jwt.verify(req.token, 'secret', async (err, authData) => { 7 | if (err) { 8 | res.sendStatus(401) 9 | } else { 10 | const { registration_id } = req.params 11 | 12 | try { 13 | const registration = await Registration.findById(registration_id); 14 | 15 | if (registration) { 16 | registration.approved = false 17 | await registration.save() 18 | 19 | return res.json(registration) 20 | } 21 | 22 | } catch (error) { 23 | return res.status(400).json(error) 24 | } 25 | } 26 | }) 27 | } 28 | } -------------------------------------------------------------------------------- /starter-files/mobile-app/App.js: -------------------------------------------------------------------------------- 1 | import { StatusBar } from 'expo-status-bar'; 2 | import React from 'react'; 3 | import { StyleSheet, Text, View } from 'react-native'; 4 | 5 | export default function App() { 6 | return ( 7 | 8 | Hello World 9 | Thats fine! 10 | 11 | 12 | ); 13 | } 14 | 15 | const styles = StyleSheet.create({ 16 | container: { 17 | flex: 1, 18 | backgroundColor: '#fff', 19 | alignItems: 'center', 20 | justifyContent: 'center', 21 | }, 22 | btnContainer: { 23 | position: 'absolute', 24 | top: 50 25 | }, 26 | text: { 27 | color: 'green' 28 | } 29 | }); 30 | -------------------------------------------------------------------------------- /starter-files/backend/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "commonjs": true, 5 | "browser": false, 6 | "es6": true 7 | }, 8 | "extends": "eslint:recommended", 9 | "globals": { 10 | "Atomics": "readonly", 11 | "SharedArrayBuffer": "readonly" 12 | }, 13 | "parserOptions": { 14 | "ecmaVersion": 11 15 | }, 16 | "rules": { 17 | "indent": [ 18 | "error", 19 | "tab" 20 | ], 21 | "linebreak-style": [ 22 | "error", 23 | "unix" 24 | ], 25 | "quotes": [ 26 | "error", 27 | "single" 28 | ], 29 | "semi": [ 30 | "error", 31 | "never" 32 | ] 33 | } 34 | } -------------------------------------------------------------------------------- /starter-files/mobile-app/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "mobile-app", 4 | "slug": "mobile-app", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "splash": { 9 | "image": "./assets/splash.png", 10 | "resizeMode": "contain", 11 | "backgroundColor": "#ffffff" 12 | }, 13 | "updates": { 14 | "fallbackToCacheTimeout": 0 15 | }, 16 | "assetBundlePatterns": [ 17 | "**/*" 18 | ], 19 | "ios": { 20 | "supportsTablet": true 21 | }, 22 | "android": { 23 | "adaptiveIcon": { 24 | "foregroundImage": "./assets/adaptive-icon.png", 25 | "backgroundColor": "#FFFFFF" 26 | } 27 | }, 28 | "web": { 29 | "favicon": "./assets/favicon.png" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /episode-0-settup/mobile-app/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "mobile-app", 4 | "slug": "mobile-app", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "splash": { 9 | "image": "./assets/splash.png", 10 | "resizeMode": "contain", 11 | "backgroundColor": "#ffffff" 12 | }, 13 | "updates": { 14 | "fallbackToCacheTimeout": 0 15 | }, 16 | "assetBundlePatterns": [ 17 | "**/*" 18 | ], 19 | "ios": { 20 | "supportsTablet": true 21 | }, 22 | "android": { 23 | "adaptiveIcon": { 24 | "foregroundImage": "./assets/adaptive-icon.png", 25 | "backgroundColor": "#FFFFFF" 26 | } 27 | }, 28 | "web": { 29 | "favicon": "./assets/favicon.png" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /episode-1-login-register/mobile-app/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "mobile-app", 4 | "slug": "mobile-app", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "splash": { 9 | "image": "./assets/splash.png", 10 | "resizeMode": "contain", 11 | "backgroundColor": "#ffffff" 12 | }, 13 | "updates": { 14 | "fallbackToCacheTimeout": 0 15 | }, 16 | "assetBundlePatterns": [ 17 | "**/*" 18 | ], 19 | "ios": { 20 | "supportsTablet": true 21 | }, 22 | "android": { 23 | "adaptiveIcon": { 24 | "foregroundImage": "./assets/adaptive-icon.png", 25 | "backgroundColor": "#FFFFFF" 26 | } 27 | }, 28 | "web": { 29 | "favicon": "./assets/favicon.png" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /episode-2-start-dashboard/mobile-app/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "mobile-app", 4 | "slug": "mobile-app", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "splash": { 9 | "image": "./assets/splash.png", 10 | "resizeMode": "contain", 11 | "backgroundColor": "#ffffff" 12 | }, 13 | "updates": { 14 | "fallbackToCacheTimeout": 0 15 | }, 16 | "assetBundlePatterns": [ 17 | "**/*" 18 | ], 19 | "ios": { 20 | "supportsTablet": true 21 | }, 22 | "android": { 23 | "adaptiveIcon": { 24 | "foregroundImage": "./assets/adaptive-icon.png", 25 | "backgroundColor": "#FFFFFF" 26 | } 27 | }, 28 | "web": { 29 | "favicon": "./assets/favicon.png" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /episode-4-fetch-created-events/mobile-app/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "mobile-app", 4 | "slug": "mobile-app", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "splash": { 9 | "image": "./assets/splash.png", 10 | "resizeMode": "contain", 11 | "backgroundColor": "#ffffff" 12 | }, 13 | "updates": { 14 | "fallbackToCacheTimeout": 0 15 | }, 16 | "assetBundlePatterns": [ 17 | "**/*" 18 | ], 19 | "ios": { 20 | "supportsTablet": true 21 | }, 22 | "android": { 23 | "adaptiveIcon": { 24 | "foregroundImage": "./assets/adaptive-icon.png", 25 | "backgroundColor": "#FFFFFF" 26 | } 27 | }, 28 | "web": { 29 | "favicon": "./assets/favicon.png" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /episode-5-dashboard-filter/mobile-app/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "mobile-app", 4 | "slug": "mobile-app", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "splash": { 9 | "image": "./assets/splash.png", 10 | "resizeMode": "contain", 11 | "backgroundColor": "#ffffff" 12 | }, 13 | "updates": { 14 | "fallbackToCacheTimeout": 0 15 | }, 16 | "assetBundlePatterns": [ 17 | "**/*" 18 | ], 19 | "ios": { 20 | "supportsTablet": true 21 | }, 22 | "android": { 23 | "adaptiveIcon": { 24 | "foregroundImage": "./assets/adaptive-icon.png", 25 | "backgroundColor": "#FFFFFF" 26 | } 27 | }, 28 | "web": { 29 | "favicon": "./assets/favicon.png" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /episode-3-add-create-event-page/mobile-app/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "mobile-app", 4 | "slug": "mobile-app", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "splash": { 9 | "image": "./assets/splash.png", 10 | "resizeMode": "contain", 11 | "backgroundColor": "#ffffff" 12 | }, 13 | "updates": { 14 | "fallbackToCacheTimeout": 0 15 | }, 16 | "assetBundlePatterns": [ 17 | "**/*" 18 | ], 19 | "ios": { 20 | "supportsTablet": true 21 | }, 22 | "android": { 23 | "adaptiveIcon": { 24 | "foregroundImage": "./assets/adaptive-icon.png", 25 | "backgroundColor": "#FFFFFF" 26 | } 27 | }, 28 | "web": { 29 | "favicon": "./assets/favicon.png" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /starter-files/backend/src/config/s3Upload.js: -------------------------------------------------------------------------------- 1 | const aws = require('aws-sdk') 2 | const multer = require('multer') 3 | const multersS3 = require('multer-s3') 4 | const path = require('path') 5 | 6 | const s3 = new aws.S3({ 7 | accessKeyId: process.env.ACCESS_KEY_ID, 8 | secretAccessKey: process.env.SECRET_ACCESS_KEY 9 | }) 10 | 11 | module.exports = multer({ 12 | storage: multersS3({ 13 | s3: s3, 14 | bucket: "react-native-bootcamp-public", 15 | metadata: function (req, file, cb) { 16 | cb(null, { fieldName: file.fieldname }) 17 | }, 18 | key: function (req, file, cb) { 19 | const ext = path.extname(file.originalname) 20 | const name = path.basename(file.originalname) 21 | 22 | cb(null, `${name.replace(/\s/g, '')}-${Date.now()}${ext}`) 23 | } 24 | }) 25 | }) -------------------------------------------------------------------------------- /starter-files/backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "lint": "eslint ./src --fix", 8 | "start": "node src/server.js", 9 | "dev": "nodemon src/server.js", 10 | "build-docker-image": "docker build . -t react-native-bootcamp/backend:1.0.0", 11 | "run-docker-image": "docker-compose up" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "dependencies": { 17 | "aws-sdk": "^2.698.0", 18 | "bcrypt": "^5.0.0", 19 | "cors": "^2.8.5", 20 | "dotenv": "^8.2.0", 21 | "express": "^4.17.1", 22 | "jsonwebtoken": "^8.5.1", 23 | "mongoose": "5.11.15", 24 | "multer": "^1.4.2", 25 | "multer-s3": "^2.9.0", 26 | "socket.io": "^3.1.2" 27 | }, 28 | "devDependencies": { 29 | "eslint": "^7.0.0", 30 | "nodemon": "^2.0.3" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /starter-files/mobile-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "node_modules/expo/AppEntry.js", 3 | "scripts": { 4 | "start": "expo start", 5 | "android": "expo start --android", 6 | "ios": "expo start --ios", 7 | "web": "expo start --web", 8 | "eject": "expo eject" 9 | }, 10 | "dependencies": { 11 | "@react-native-community/masked-view": "0.1.10", 12 | "@react-navigation/native": "^5.9.2", 13 | "@react-navigation/stack": "^5.14.2", 14 | "expo": "~40.0.0", 15 | "expo-status-bar": "~1.0.3", 16 | "react": "16.13.1", 17 | "react-dom": "16.13.1", 18 | "react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz", 19 | "react-native-gesture-handler": "~1.8.0", 20 | "react-native-reanimated": "~1.13.0", 21 | "react-native-safe-area-context": "3.1.9", 22 | "react-native-screens": "~2.15.2", 23 | "react-native-web": "~0.13.12" 24 | }, 25 | "devDependencies": { 26 | "@babel/core": "~7.9.0" 27 | }, 28 | "private": true 29 | } 30 | -------------------------------------------------------------------------------- /episode-0-settup/mobile-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "node_modules/expo/AppEntry.js", 3 | "scripts": { 4 | "start": "expo start", 5 | "android": "expo start --android", 6 | "ios": "expo start --ios", 7 | "web": "expo start --web", 8 | "eject": "expo eject" 9 | }, 10 | "dependencies": { 11 | "@react-native-community/masked-view": "0.1.10", 12 | "@react-navigation/native": "^5.9.2", 13 | "@react-navigation/stack": "^5.14.2", 14 | "expo": "~40.0.0", 15 | "expo-status-bar": "~1.0.3", 16 | "react": "16.13.1", 17 | "react-dom": "16.13.1", 18 | "react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz", 19 | "react-native-gesture-handler": "~1.8.0", 20 | "react-native-reanimated": "~1.13.0", 21 | "react-native-safe-area-context": "3.1.9", 22 | "react-native-screens": "~2.15.2", 23 | "react-native-web": "~0.13.12" 24 | }, 25 | "devDependencies": { 26 | "@babel/core": "~7.9.0" 27 | }, 28 | "private": true 29 | } 30 | -------------------------------------------------------------------------------- /episode-1-login-register/mobile-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "node_modules/expo/AppEntry.js", 3 | "scripts": { 4 | "start": "expo start", 5 | "android": "expo start --android", 6 | "ios": "expo start --ios", 7 | "web": "expo start --web", 8 | "eject": "expo eject" 9 | }, 10 | "dependencies": { 11 | "@react-native-community/masked-view": "0.1.10", 12 | "@react-navigation/native": "^5.9.2", 13 | "@react-navigation/stack": "^5.14.2", 14 | "expo": "~40.0.0", 15 | "expo-status-bar": "~1.0.3", 16 | "react": "16.13.1", 17 | "react-dom": "16.13.1", 18 | "react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz", 19 | "react-native-gesture-handler": "~1.8.0", 20 | "react-native-reanimated": "~1.13.0", 21 | "react-native-safe-area-context": "3.1.9", 22 | "react-native-screens": "~2.15.2", 23 | "react-native-web": "~0.13.12" 24 | }, 25 | "devDependencies": { 26 | "@babel/core": "~7.9.0" 27 | }, 28 | "private": true 29 | } 30 | -------------------------------------------------------------------------------- /episode-2-start-dashboard/mobile-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "node_modules/expo/AppEntry.js", 3 | "scripts": { 4 | "start": "expo start", 5 | "android": "expo start --android", 6 | "ios": "expo start --ios", 7 | "web": "expo start --web", 8 | "eject": "expo eject" 9 | }, 10 | "dependencies": { 11 | "@react-native-community/masked-view": "0.1.10", 12 | "@react-navigation/native": "^5.9.2", 13 | "@react-navigation/stack": "^5.14.2", 14 | "expo": "~40.0.0", 15 | "expo-status-bar": "~1.0.3", 16 | "react": "16.13.1", 17 | "react-dom": "16.13.1", 18 | "react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz", 19 | "react-native-gesture-handler": "~1.8.0", 20 | "react-native-reanimated": "~1.13.0", 21 | "react-native-safe-area-context": "3.1.9", 22 | "react-native-screens": "~2.15.2", 23 | "react-native-web": "~0.13.12" 24 | }, 25 | "devDependencies": { 26 | "@babel/core": "~7.9.0" 27 | }, 28 | "private": true 29 | } 30 | -------------------------------------------------------------------------------- /episode-5-dashboard-filter/mobile-app/hooks/isLoggedIn.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import AsyncStorage from '@react-native-async-storage/async-storage'; 3 | 4 | const isLoggedIn = (navigation) => { 5 | const [user, setUser] = useState(null) 6 | const [user_id, setUserId] = useState(null) 7 | 8 | useEffect(() => { 9 | const getData = async () => { 10 | try { 11 | const user = await AsyncStorage.getItem('user') 12 | const user_id = await AsyncStorage.getItem('user_id') 13 | if (user !== null && user_id !== null) { 14 | setUser(user) 15 | setUserId(user_id) 16 | } 17 | } catch (e) { 18 | console.log('🚀 -------------------------------------------------') 19 | console.log('🚀 ~ file: Dashboard.js ~ line 44 ~ getData ~ e', e) 20 | console.log('🚀 -------------------------------------------------') 21 | } 22 | } 23 | getData(); 24 | }, [user, user_id]) 25 | 26 | return [user, user_id]; 27 | }; 28 | 29 | export default isLoggedIn; 30 | 31 | -------------------------------------------------------------------------------- /episode-3-add-create-event-page/mobile-app/hooks/isLoggedIn.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import AsyncStorage from '@react-native-async-storage/async-storage'; 3 | 4 | const isLoggedIn = (navigation) => { 5 | const [user, setUser] = useState(null) 6 | const [user_id, setUserId] = useState(null) 7 | 8 | useEffect(() => { 9 | const getData = async () => { 10 | try { 11 | const user = await AsyncStorage.getItem('user') 12 | const user_id = await AsyncStorage.getItem('user_id') 13 | if (user !== null && user_id !== null) { 14 | setUser(user) 15 | setUserId(user_id) 16 | } 17 | } catch (e) { 18 | console.log('🚀 -------------------------------------------------') 19 | console.log('🚀 ~ file: Dashboard.js ~ line 44 ~ getData ~ e', e) 20 | console.log('🚀 -------------------------------------------------') 21 | } 22 | } 23 | getData(); 24 | }, [user, user_id]) 25 | 26 | return [user, user_id]; 27 | }; 28 | 29 | export default isLoggedIn; 30 | 31 | -------------------------------------------------------------------------------- /episode-4-fetch-created-events/mobile-app/hooks/isLoggedIn.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import AsyncStorage from '@react-native-async-storage/async-storage'; 3 | 4 | const isLoggedIn = (navigation) => { 5 | const [user, setUser] = useState(null) 6 | const [user_id, setUserId] = useState(null) 7 | 8 | useEffect(() => { 9 | const getData = async () => { 10 | try { 11 | const user = await AsyncStorage.getItem('user') 12 | const user_id = await AsyncStorage.getItem('user_id') 13 | if (user !== null && user_id !== null) { 14 | setUser(user) 15 | setUserId(user_id) 16 | } 17 | } catch (e) { 18 | console.log('🚀 -------------------------------------------------') 19 | console.log('🚀 ~ file: Dashboard.js ~ line 44 ~ getData ~ e', e) 20 | console.log('🚀 -------------------------------------------------') 21 | } 22 | } 23 | getData(); 24 | }, [user, user_id]) 25 | 26 | return [user, user_id]; 27 | }; 28 | 29 | export default isLoggedIn; 30 | 31 | -------------------------------------------------------------------------------- /episode-0-settup/mobile-app/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StatusBar } from 'expo-status-bar'; 3 | import { StyleSheet, Text, View } from 'react-native'; 4 | 5 | import { NavigationContainer } from '@react-navigation/native'; 6 | import { createStackNavigator } from '@react-navigation/stack'; 7 | 8 | import Login from './pages/Login'; 9 | import Register from './pages/Register'; 10 | 11 | export default function App() { 12 | const Stack = createStackNavigator() 13 | 14 | return ( 15 | 16 | 19 | 20 | 21 | 22 | 23 | ); 24 | } 25 | 26 | const styles = StyleSheet.create({ 27 | container: { 28 | flex: 1, 29 | backgroundColor: '#fff', 30 | alignItems: 'center', 31 | justifyContent: 'center', 32 | }, 33 | btnContainer: { 34 | position: 'absolute', 35 | bottom: 50 36 | }, 37 | text: { 38 | color: 'black' 39 | } 40 | }); 41 | -------------------------------------------------------------------------------- /episode-0-settup/mobile-app/pages/Login.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { View, Text, TouchableOpacity, StyleSheet, ImageBackground } from 'react-native' 3 | import { color } from 'react-native-reanimated' 4 | 5 | const bgImage = require('../assets/background.jpg') 6 | 7 | const Login = ({ navigation }) => { 8 | return ( 9 | 10 | 11 | Sport's App 12 | navigation.navigate('Register')}> 13 | Go to Routes PAGE 14 | 15 | 16 | 17 | ) 18 | } 19 | 20 | const styles = StyleSheet.create({ 21 | container: { 22 | flex: 1, 23 | justifyContent: "center", 24 | alignItems: 'center' 25 | }, 26 | image: { 27 | flex: 1, 28 | width: "100%", 29 | height: "100%", 30 | justifyContent: "center", 31 | alignItems: 'center' 32 | }, 33 | title: { 34 | fontSize: 32, 35 | marginBottom: 8, 36 | fontWeight: "bold", 37 | color: "#f04a5b" 38 | } 39 | 40 | 41 | }) 42 | 43 | 44 | export default Login -------------------------------------------------------------------------------- /episode-1-login-register/mobile-app/pages/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { View, Text, TouchableOpacity, StyleSheet, ImageBackground } from 'react-native' 3 | import { color } from 'react-native-reanimated' 4 | 5 | const bgImage = require('../assets/background.jpg') 6 | 7 | const DashBoard = ({ navigation }) => { 8 | return ( 9 | 10 | 11 | Sport's App - DashBoard 12 | navigation.navigate('Login')}> 13 | Logout 14 | 15 | 16 | 17 | ) 18 | } 19 | 20 | 21 | const styles = StyleSheet.create({ 22 | container: { 23 | flex: 1, 24 | justifyContent: "center", 25 | alignItems: 'center' 26 | }, 27 | image: { 28 | flex: 1, 29 | width: "100%", 30 | height: "100%", 31 | justifyContent: "center", 32 | alignItems: 'center' 33 | }, 34 | title: { 35 | fontSize: 32, 36 | marginBottom: 8, 37 | fontWeight: "bold", 38 | color: "#f04a5b" 39 | } 40 | }) 41 | 42 | 43 | export default DashBoard -------------------------------------------------------------------------------- /starter-files/backend/src/models/Event.js: -------------------------------------------------------------------------------- 1 | // reverting API to do not use S3 2 | // const mongoose = require('mongoose') 3 | 4 | // const EventSchema = new mongoose.Schema({ 5 | // title: String, 6 | // description: String, 7 | // price: Number, 8 | // thumbnail: String, 9 | // sport: String, 10 | // date: Date, 11 | // user: { 12 | // type: mongoose.Schema.Types.ObjectId, 13 | // ref: 'User' 14 | // } 15 | // }, { 16 | // toJSON: { 17 | // virtuals: true 18 | // } 19 | // }) 20 | 21 | // EventSchema.virtual('thumbnail_url').get(function () { return this.thumbnail }) 22 | 23 | // module.exports = mongoose.model('Event', EventSchema) 24 | 25 | 26 | const mongoose = require('mongoose') 27 | 28 | const EventSchema = new mongoose.Schema({ 29 | title: String, 30 | description: String, 31 | price: Number, 32 | thumbnail: String, 33 | sport: String, 34 | date: Date, 35 | user: { 36 | type: mongoose.Schema.Types.ObjectId, 37 | ref: 'User' 38 | } 39 | }, { 40 | toJSON: { 41 | virtuals: true 42 | } 43 | }) 44 | 45 | EventSchema.virtual('thumbnail_url').get(function () { return `http://localhost:8080/files/${this.thumbnail}` }) 46 | 47 | module.exports = mongoose.model('Event', EventSchema) 48 | -------------------------------------------------------------------------------- /episode-3-add-create-event-page/mobile-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "node_modules/expo/AppEntry.js", 3 | "scripts": { 4 | "start": "expo start", 5 | "android": "expo start --android", 6 | "ios": "expo start --ios", 7 | "web": "expo start --web", 8 | "eject": "expo eject" 9 | }, 10 | "dependencies": { 11 | "@react-native-async-storage/async-storage": "^1.13.4", 12 | "@react-native-community/masked-view": "0.1.10", 13 | "@react-native-picker/picker": "^1.9.11", 14 | "@react-navigation/native": "^5.9.2", 15 | "@react-navigation/stack": "^5.14.2", 16 | "expo": "~40.0.0", 17 | "expo-image-picker": "~9.2.0", 18 | "expo-status-bar": "~1.0.3", 19 | "react": "16.13.1", 20 | "react-dom": "16.13.1", 21 | "react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz", 22 | "react-native-action-button": "^2.8.5", 23 | "react-native-gesture-handler": "~1.8.0", 24 | "react-native-reanimated": "~1.13.0", 25 | "react-native-safe-area-context": "3.1.9", 26 | "react-native-screens": "~2.15.2", 27 | "react-native-web": "~0.13.12" 28 | }, 29 | "devDependencies": { 30 | "@babel/core": "~7.9.0" 31 | }, 32 | "private": true 33 | } 34 | -------------------------------------------------------------------------------- /episode-1-login-register/mobile-app/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StatusBar } from 'expo-status-bar'; 3 | import { StyleSheet, Text, View } from 'react-native'; 4 | 5 | import { NavigationContainer } from '@react-navigation/native'; 6 | import { createStackNavigator } from '@react-navigation/stack'; 7 | 8 | import Login from './pages/Login'; 9 | import Register from './pages/Register'; 10 | import Dashboard from './pages/Dashboard' 11 | 12 | export default function App() { 13 | const Stack = createStackNavigator() 14 | 15 | return ( 16 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | ); 26 | } 27 | 28 | const styles = StyleSheet.create({ 29 | container: { 30 | flex: 1, 31 | backgroundColor: '#fff', 32 | alignItems: 'center', 33 | justifyContent: 'center', 34 | }, 35 | btnContainer: { 36 | position: 'absolute', 37 | bottom: 50 38 | }, 39 | text: { 40 | color: 'black' 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /episode-2-start-dashboard/mobile-app/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StatusBar } from 'expo-status-bar'; 3 | import { StyleSheet, Text, View } from 'react-native'; 4 | 5 | import { NavigationContainer } from '@react-navigation/native'; 6 | import { createStackNavigator } from '@react-navigation/stack'; 7 | 8 | import Login from './pages/Login'; 9 | import Register from './pages/Register'; 10 | import Dashboard from './pages/Dashboard' 11 | 12 | export default function App() { 13 | const Stack = createStackNavigator() 14 | 15 | return ( 16 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | ); 26 | } 27 | 28 | const styles = StyleSheet.create({ 29 | container: { 30 | flex: 1, 31 | backgroundColor: '#fff', 32 | alignItems: 'center', 33 | justifyContent: 'center', 34 | }, 35 | btnContainer: { 36 | position: 'absolute', 37 | bottom: 50 38 | }, 39 | text: { 40 | color: 'black' 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /episode-5-dashboard-filter/mobile-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "node_modules/expo/AppEntry.js", 3 | "scripts": { 4 | "start": "expo start", 5 | "android": "expo start --android", 6 | "ios": "expo start --ios", 7 | "web": "expo start --web", 8 | "eject": "expo eject" 9 | }, 10 | "dependencies": { 11 | "@react-native-async-storage/async-storage": "^1.13.4", 12 | "@react-native-community/datetimepicker": "3.0.4", 13 | "@react-native-community/masked-view": "0.1.10", 14 | "@react-native-picker/picker": "^1.9.11", 15 | "@react-navigation/native": "^5.9.2", 16 | "@react-navigation/stack": "^5.14.2", 17 | "expo": "~40.0.0", 18 | "expo-image-picker": "~9.2.0", 19 | "expo-status-bar": "~1.0.3", 20 | "react": "16.13.1", 21 | "react-dom": "16.13.1", 22 | "react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz", 23 | "react-native-action-button": "^2.8.5", 24 | "react-native-gesture-handler": "~1.8.0", 25 | "react-native-reanimated": "~1.13.0", 26 | "react-native-safe-area-context": "3.1.9", 27 | "react-native-screens": "~2.15.2", 28 | "react-native-web": "~0.13.12" 29 | }, 30 | "devDependencies": { 31 | "@babel/core": "~7.9.0" 32 | }, 33 | "private": true 34 | } 35 | -------------------------------------------------------------------------------- /episode-4-fetch-created-events/mobile-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "node_modules/expo/AppEntry.js", 3 | "scripts": { 4 | "start": "expo start", 5 | "android": "expo start --android", 6 | "ios": "expo start --ios", 7 | "web": "expo start --web", 8 | "eject": "expo eject" 9 | }, 10 | "dependencies": { 11 | "@react-native-async-storage/async-storage": "^1.13.4", 12 | "@react-native-community/datetimepicker": "3.0.4", 13 | "@react-native-community/masked-view": "0.1.10", 14 | "@react-native-picker/picker": "^1.9.11", 15 | "@react-navigation/native": "^5.9.2", 16 | "@react-navigation/stack": "^5.14.2", 17 | "expo": "~40.0.0", 18 | "expo-image-picker": "~9.2.0", 19 | "expo-status-bar": "~1.0.3", 20 | "react": "16.13.1", 21 | "react-dom": "16.13.1", 22 | "react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz", 23 | "react-native-action-button": "^2.8.5", 24 | "react-native-gesture-handler": "~1.8.0", 25 | "react-native-reanimated": "~1.13.0", 26 | "react-native-safe-area-context": "3.1.9", 27 | "react-native-screens": "~2.15.2", 28 | "react-native-web": "~0.13.12" 29 | }, 30 | "devDependencies": { 31 | "@babel/core": "~7.9.0" 32 | }, 33 | "private": true 34 | } 35 | -------------------------------------------------------------------------------- /starter-files/backend/src/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const mongoose = require('mongoose') 3 | const cors = require('cors') 4 | const routes = require('./routes') 5 | const path = require('path') 6 | const http = require('http') 7 | const PORT = process.env.PORT || 8080 8 | const app = express() 9 | const server = http.Server(app) 10 | const io = require("socket.io")(server, { 11 | cors: { 12 | origin: "*", 13 | methods: ["GET", "POST", "DELETE"] 14 | } 15 | }); 16 | 17 | try { 18 | mongoose.connect('mongodb://mongo:27017/docker-node-mongo', { 19 | useNewUrlParser: true, 20 | useUnifiedTopology: true, 21 | }) 22 | console.log('MongoDb connected successfully!') 23 | } catch (error) { 24 | console.log(`mongodb Connection error - ${error}`) 25 | } 26 | 27 | const connectUsers = {}; 28 | io.on('connection', socket => { 29 | const { user } = socket.handshake.query 30 | connectUsers[user] = socket.id 31 | }) 32 | 33 | app.use((req, res, next) => { 34 | req.io = io 35 | req.connectUsers = connectUsers 36 | return next() 37 | }) 38 | 39 | app.use(cors()) 40 | app.use(express.json()) 41 | app.use('/files', express.static(path.resolve(__dirname, '..', 'files'))) 42 | app.use(routes) 43 | 44 | server.listen(PORT, () => { 45 | console.log(`Listening on ${PORT}`) 46 | }) 47 | -------------------------------------------------------------------------------- /episode-5-dashboard-filter/mobile-app/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StyleSheet, LogBox } from 'react-native'; 3 | import { NavigationContainer } from '@react-navigation/native'; 4 | import { createStackNavigator } from '@react-navigation/stack'; 5 | 6 | import Login from './pages/Login'; 7 | import Register from './pages/Register'; 8 | import Dashboard from './pages/Dashboard' 9 | 10 | 11 | LogBox.ignoreLogs(['Warning: ...']); // Ignore log notification by message 12 | LogBox.ignoreAllLogs(); 13 | 14 | 15 | export default function App() { 16 | const Stack = createStackNavigator() 17 | 18 | return ( 19 | 20 | 23 | 24 | 25 | 26 | 27 | 28 | ); 29 | } 30 | 31 | const styles = StyleSheet.create({ 32 | container: { 33 | flex: 1, 34 | backgroundColor: '#fff', 35 | alignItems: 'center', 36 | justifyContent: 'center', 37 | }, 38 | btnContainer: { 39 | position: 'absolute', 40 | bottom: 50 41 | }, 42 | text: { 43 | color: 'black' 44 | } 45 | }); 46 | -------------------------------------------------------------------------------- /episode-3-add-create-event-page/mobile-app/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StyleSheet, LogBox } from 'react-native'; 3 | import { NavigationContainer } from '@react-navigation/native'; 4 | import { createStackNavigator } from '@react-navigation/stack'; 5 | 6 | import Login from './pages/Login'; 7 | import Register from './pages/Register'; 8 | import Dashboard from './pages/Dashboard' 9 | 10 | 11 | LogBox.ignoreLogs(['Warning: ...']); // Ignore log notification by message 12 | LogBox.ignoreAllLogs(); 13 | 14 | 15 | export default function App() { 16 | const Stack = createStackNavigator() 17 | 18 | return ( 19 | 20 | 23 | 24 | 25 | 26 | 27 | 28 | ); 29 | } 30 | 31 | const styles = StyleSheet.create({ 32 | container: { 33 | flex: 1, 34 | backgroundColor: '#fff', 35 | alignItems: 'center', 36 | justifyContent: 'center', 37 | }, 38 | btnContainer: { 39 | position: 'absolute', 40 | bottom: 50 41 | }, 42 | text: { 43 | color: 'black' 44 | } 45 | }); 46 | -------------------------------------------------------------------------------- /episode-4-fetch-created-events/mobile-app/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StyleSheet, LogBox } from 'react-native'; 3 | import { NavigationContainer } from '@react-navigation/native'; 4 | import { createStackNavigator } from '@react-navigation/stack'; 5 | 6 | import Login from './pages/Login'; 7 | import Register from './pages/Register'; 8 | import Dashboard from './pages/Dashboard' 9 | 10 | 11 | LogBox.ignoreLogs(['Warning: ...']); // Ignore log notification by message 12 | LogBox.ignoreAllLogs(); 13 | 14 | 15 | export default function App() { 16 | const Stack = createStackNavigator() 17 | 18 | return ( 19 | 20 | 23 | 24 | 25 | 26 | 27 | 28 | ); 29 | } 30 | 31 | const styles = StyleSheet.create({ 32 | container: { 33 | flex: 1, 34 | backgroundColor: '#fff', 35 | alignItems: 'center', 36 | justifyContent: 'center', 37 | }, 38 | btnContainer: { 39 | position: 'absolute', 40 | bottom: 50 41 | }, 42 | text: { 43 | color: 'black' 44 | } 45 | }); 46 | -------------------------------------------------------------------------------- /starter-files/backend/src/controllers/LoginController.js: -------------------------------------------------------------------------------- 1 | const bcrypt = require('bcrypt') 2 | const User = require('../models/User') 3 | const jwt = require('jsonwebtoken') 4 | 5 | module.exports = { 6 | async store(req, res) { 7 | try { 8 | const { email, password } = req.body 9 | 10 | if (!email || !password) { 11 | return res.status(200).json({ message: 'Required field missing!' }) 12 | } 13 | 14 | const user = await User.findOne({ email }) 15 | if (!user) { 16 | return res.status(200).json({ message: 'User not found! Do you want to register instead?' }) 17 | } 18 | 19 | if (user && await bcrypt.compare(password, user.password)) { 20 | const userResponse = { 21 | _id: user._id, 22 | email: user.email, 23 | firstName: user.firstName, 24 | lastName: user.lastName 25 | } 26 | console.log('🚀 ---------------------------------------------------------------------------') 27 | console.log('🚀 ~ file: LoginController.js ~ line 26 ~ store ~ userResponse', userResponse) 28 | console.log('🚀 ---------------------------------------------------------------------------') 29 | 30 | return jwt.sign({ user: userResponse }, 'secret', (err, token) => { 31 | return res.json({ 32 | user: token, 33 | user_id: userResponse._id 34 | }) 35 | }) 36 | 37 | } else { 38 | return res.status(200).json({ message: 'Email or Password does not match!' }) 39 | } 40 | 41 | 42 | } catch (error) { 43 | throw Error(`Error while Authenticating a User ${error}`) 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /starter-files/backend/src/controllers/UserController.js: -------------------------------------------------------------------------------- 1 | const bcrypt = require('bcrypt') 2 | const User = require('../models/User') 3 | const jwt = require('jsonwebtoken') 4 | 5 | module.exports = { 6 | async createUser(req, res) { 7 | try { 8 | const { email, firstName, lastName, password } = req.body 9 | const existentUser = await User.findOne({ email }) 10 | 11 | if (!existentUser) { 12 | const hashPassword = await bcrypt.hash(password, 10) 13 | const userResponse = await User.create({ 14 | email, 15 | firstName, 16 | lastName, 17 | password: hashPassword, 18 | }) 19 | console.log('🚀 -------------------------------------------------------------------------------') 20 | console.log('🚀 ~ file: UserController.js ~ line 19 ~ createUser ~ userResponse', userResponse) 21 | console.log('🚀 -------------------------------------------------------------------------------') 22 | 23 | return jwt.sign({ user: userResponse }, 'secret', (err, token) => { 24 | return res.json({ 25 | user: token, 26 | user_id: userResponse._id 27 | }) 28 | }) 29 | } else { 30 | return res.status(400).json({ 31 | message: 32 | 'email already exist! do you want to login instead? ', 33 | }) 34 | } 35 | } catch (err) { 36 | throw Error(`Error while Registering new user : ${err}`) 37 | } 38 | }, 39 | 40 | async getUserById(req, res) { 41 | const { userId } = req.params 42 | 43 | try { 44 | const user = await User.findById(userId) 45 | return res.json(user) 46 | } catch (error) { 47 | return res.status(400).json({ 48 | message: 49 | 'User ID does not exist, do you want to register instead?', 50 | }) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # REACT NATIVE BOOTCAMP 2021 - (For Beginners) 2 | 3 | This is a series video were we going to build a React Native app (frontend) for our Sport's App built on [the Mern Bootcamp.]('https://github.com/jeanrauwers/mern-course-bootcamp') 4 | 5 | We will build a Sports Web App for creating sports events and the app will include: 6 | 7 | * User registration and authentication (maybe add facebook/gmail login if the series becomes popular) 8 | * Hashing password for security 9 | * Session control 10 | * CRUD operations (create, read, update and delete) using provided API 11 | * Search for events using filters (Running, Cycling or Swimming) 12 | * Sign UP for event in order to participate 13 | * Upload images to the S3 bucket 14 | * Push notification using web sockets 15 | 16 | ### Milestones of the project 17 | 18 | Series get more than 1000 Stars I will create a Next.js Bootcamp!
19 | 20 | So please share this video with your friends. 21 | 22 | ## Need help? 23 | [We have a discord group](https://discord.gg/7bsz7U5) 24 | 25 | ## Videos on Youtube : 26 | 27 | * [React-Native Bootcamp demo](https://youtu.be/Dnrc8W4jb-E) - The Mobile App demo can be found here. 28 | * [React-Native Botcamp episode 0](https://youtu.be/s1Y3HkVh4lk) - Installing dependencies and starting coding. 29 | * [React-Native Botcamp episode 1](https://youtu.be/Eg_W_TRkAeQ) - Creating Login and Register pages. 30 | * [React-Native Botcamp episode 2](https://youtu.be/ZoX3otWqM-Y) - Creating Dashboard page and display first event. 31 | * [React-Native Botcamp episode 3 - A](https://youtu.be/IdKzvEH4Znk) - Add Events modal and form. 32 | * [React-Native Botcamp episode 3 - B](https://youtu.be/j7Pfss5rr8w) - Finish the input fields for the modal and add the picker-image. 33 | * [React-Native Botcamp episode 4](https://youtu.be/DfM8ego4rlU) - Finalize modal details to post new event to server. 34 | * [React-Native Botcamp episode 5](https://youtu.be/QEwZlVBlhuQ) - Post and Get Event from Dashboard page and filter by sport. 35 | 36 | 37 | 38 | ## Want to know more about my motivation for this project? 39 | [Read my blog post](http://italktech.io/mern-coding-bootcamp/) 40 | -------------------------------------------------------------------------------- /starter-files/backend/src/routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const verifyToken = require('./config/verifyToken') 3 | const multer = require('multer') 4 | 5 | const UserController = require('./controllers/UserController') 6 | const EventController = require('./controllers/EventController') 7 | const DashboardController = require('./controllers/DashboardController') 8 | const LoginController = require('./controllers/LoginController') 9 | const RegistrationController = require('./controllers/RegistrationController') 10 | const ApprovalController = require('./controllers/ApprovalController') 11 | const RejectionController = require('./controllers/RejectionController') 12 | // const uploadToS3 = require('./config/s3Upload'); removing s3Upload support 13 | const uploadConfig = require('./config/upload') 14 | const routes = express.Router() 15 | const upload = multer(uploadConfig) 16 | 17 | routes.get('/api/status', (req, res) => { 18 | res.send({ status: 200 }) 19 | }) 20 | 21 | //Registration 22 | routes.post('/api/registration/:eventId', verifyToken, RegistrationController.create) 23 | routes.get('/api/registration', verifyToken, RegistrationController.getMyRegistrations) 24 | routes.get('/api/registration/:registration_id', RegistrationController.getRegistration) 25 | routes.post('/api/registration/:registration_id/approvals', verifyToken, ApprovalController.approval) 26 | routes.post('/api/registration/:registration_id/rejections', verifyToken, RejectionController.rejection) 27 | 28 | //Login 29 | routes.post('/api/login', LoginController.store) 30 | 31 | //Dashboard 32 | routes.get('/api/dashboard/:sport', verifyToken, DashboardController.getAllEvents) 33 | routes.get('/api/dashboard', verifyToken, DashboardController.getAllEvents) 34 | routes.get('/api/user/events', verifyToken, DashboardController.getEventsByUserId) 35 | routes.get('/api/event/:eventId', verifyToken, DashboardController.getEventById) 36 | 37 | //Events 38 | // routes.post('/api/event', verifyToken, uploadToS3.single('thumbnail'), EventController.createEvent) removes s3 support 39 | routes.post('/api/event', verifyToken, upload.single('thumbnail'), EventController.createEvent) 40 | routes.delete('/api/event/:eventId', verifyToken, EventController.delete) 41 | 42 | //User 43 | routes.post('/api/user/register', UserController.createUser) 44 | routes.get('/api/user/:userId', UserController.getUserById) 45 | 46 | module.exports = routes -------------------------------------------------------------------------------- /starter-files/backend/src/controllers/DashboardController.js: -------------------------------------------------------------------------------- 1 | const Event = require('../models/Event') 2 | const jwt = require('jsonwebtoken') 3 | 4 | module.exports = { 5 | getEventById(req, res) { 6 | jwt.verify(req.token, 'secret', async (err, authData) => { 7 | if (err) { 8 | res.sendStatus(401) 9 | } else { 10 | const { eventId } = req.params 11 | try { 12 | const event = await Event.findById(eventId) 13 | if (event) { 14 | console.log('🚀 ------------------------------------------------------------------------') 15 | console.log('🚀 ~ file: DashboardController.js ~ line 34 ~ jwt.verify ~ events', event) 16 | console.log('🚀 ------------------------------------------------------------------------') 17 | return res.json({ authData: authData, events: event }) 18 | } else { 19 | return res.json({ message: 'EventId does not exist!' }) 20 | } 21 | } catch (error) { 22 | return res.status(400).json({ message: error }) 23 | } 24 | } 25 | 26 | }) 27 | }, 28 | getAllEvents(req, res) { 29 | jwt.verify(req.token, 'secret', async (err, authData) => { 30 | if (err) { 31 | res.sendStatus(401) 32 | } else { 33 | const { sport } = req.params 34 | const query = sport ? { sport } : {} 35 | 36 | try { 37 | const events = await Event.find(query) 38 | 39 | if (events) { 40 | console.log('🚀 ------------------------------------------------------------------------') 41 | console.log('🚀 ~ file: DashboardController.js ~ line 34 ~ jwt.verify ~ events', events) 42 | console.log('🚀 ------------------------------------------------------------------------') 43 | return res.json({ authData, events }) 44 | } else { 45 | return res.json({ message: 'We do have any events yet' }) 46 | } 47 | } catch (error) { 48 | return res.status(400).json({ message: error }) 49 | } 50 | 51 | } 52 | }) 53 | }, 54 | 55 | getEventsByUserId(req, res) { 56 | jwt.verify(req.token, 'secret', async (err, authData) => { 57 | if (err) { 58 | res.sendStatus(401) 59 | } else { 60 | 61 | const { user_id } = req.headers 62 | 63 | try { 64 | const events = await Event.find({ user: authData.user._id }) 65 | 66 | if (events) { 67 | return res.json({ authData, events }) 68 | } else { 69 | return res.status(400).json({ message: `We do have any events with the user_id ${user_id}` }) 70 | } 71 | } catch (error) { 72 | return res.status(400).json({ message: error }) 73 | } 74 | } 75 | }) 76 | } 77 | } -------------------------------------------------------------------------------- /starter-files/backend/src/controllers/EventController.js: -------------------------------------------------------------------------------- 1 | const Event = require('../models/Event') 2 | const User = require('../models/User') 3 | const jwt = require('jsonwebtoken') 4 | 5 | 6 | 7 | 8 | module.exports = { 9 | // reverting upload to S3 10 | // createEvent(req, res) { 11 | // jwt.verify(req.token, 'secret', async (err, authData) => { 12 | // if (err) { 13 | // res.statusCode(401) 14 | // } else { 15 | // const { title, description, price, sport, date } = req.body 16 | // const { location } = req.file 17 | 18 | // const user = await User.findById(authData.user._id) 19 | 20 | // if (!user) { 21 | // return res.status(400).json({ message: 'User does not exist!' }) 22 | // } 23 | 24 | // try { 25 | // const event = await Event.create({ 26 | // title, 27 | // description, 28 | // sport, 29 | // price: parseFloat(price), 30 | // user: authData.user._id, 31 | // thumbnail: location, 32 | // date 33 | // }) 34 | 35 | // return res.json(event) 36 | // } catch (error) { 37 | // return res.status(400).json({ message: error }) 38 | // } 39 | // } 40 | // }) 41 | 42 | // }, 43 | createEvent(req, res) { 44 | jwt.verify(req.token, 'secret', async (err, authData) => { 45 | if (err) { 46 | res.statusCode(401) 47 | } else { 48 | const { title, description, price, sport, date } = req.body 49 | const { filename } = req.file 50 | 51 | try { 52 | const user = await User.findById(authData.user._id) 53 | 54 | if (!user) { 55 | return res.status(400).json({ message: 'User does not exist!' }) 56 | } 57 | 58 | const event = await Event.create({ 59 | title, 60 | description, 61 | sport, 62 | price: parseFloat(price), 63 | user: authData.user._id, 64 | thumbnail: filename, 65 | date 66 | }) 67 | console.log('🚀 ------------------------------------------------------------------') 68 | console.log('🚀 ~ file: EventController.js ~ line 67 ~ jwt.verify ~ event', event) 69 | console.log('🚀 ------------------------------------------------------------------') 70 | 71 | return res.json(event) 72 | } catch (error) { 73 | return res.status(400).json({ message: error }) 74 | } 75 | } 76 | }) 77 | }, 78 | 79 | delete(req, res) { 80 | jwt.verify(req.token, 'secret', async (err) => { 81 | if (err) { 82 | res.statusCode(401) 83 | } else { 84 | const { eventId } = req.params 85 | try { 86 | await Event.findByIdAndDelete(eventId) 87 | return res.status(204).send() 88 | 89 | } catch (error) { 90 | return res.status(400).json({ message: 'We do have any event with the ID' }) 91 | } 92 | } 93 | }) 94 | } 95 | } -------------------------------------------------------------------------------- /starter-files/backend/src/controllers/RegistrationController.js: -------------------------------------------------------------------------------- 1 | const Registration = require('../models/Registration') 2 | const jwt = require('jsonwebtoken') 3 | 4 | module.exports = { 5 | create(req, res) { 6 | jwt.verify(req.token, 'secret', async (err, authData) => { 7 | if (err) { 8 | res.sendStatus(401) 9 | } else { 10 | const user_id = authData.user._id 11 | const { eventId } = req.params 12 | 13 | const registration = await Registration.create({ 14 | user: user_id, 15 | event: eventId 16 | }) 17 | 18 | await registration 19 | .populate('event') 20 | .populate('user', '-password') 21 | .execPopulate() 22 | 23 | registration.owner = registration.event.user 24 | registration.eventTitle = registration.event.title 25 | registration.eventPrice = registration.event.price 26 | registration.eventDate = registration.event.date 27 | registration.userEmail = registration.user.email 28 | registration.save() 29 | 30 | console.log(registration) 31 | 32 | const ownerSocket = req.connectUsers[registration.event.user] 33 | 34 | console.log('🚀 ---------------------------------------------------------------------------------------') 35 | console.log('🚀 ~ file: RegistrationController.js ~ line 42 ~ jwt.verify ~ registration', registration) 36 | console.log('🚀 ---------------------------------------------------------------------------------------') 37 | 38 | if (ownerSocket) { 39 | req.io.to(ownerSocket).emit('registration_request', registration) 40 | } 41 | 42 | return res.json(registration) 43 | } 44 | 45 | }) 46 | }, 47 | 48 | async getRegistration(req, res) { 49 | const { registration_id } = req.params 50 | try { 51 | const registration = await Registration.findById(registration_id) 52 | await registration 53 | .populate('event') 54 | .populate('user', '-password') 55 | .execPopulate() 56 | 57 | return res.json(registration) 58 | } catch (error) { 59 | return res.status(400).json({ message: 'Registration not found' }) 60 | } 61 | 62 | }, 63 | 64 | getMyRegistrations(req, res) { 65 | jwt.verify(req.token, 'secret', async (err, authData) => { 66 | if (err) { 67 | res.sendStatus(401) 68 | } else { 69 | 70 | try { 71 | const registrationsArr = await Registration.find({ "owner": authData.user._id }) 72 | if (registrationsArr) { 73 | console.log('🚀 -----------------------------------------------------------------------------------------------') 74 | console.log('🚀 ~ file: RegistrationController.js ~ line 72 ~ jwt.verify ~ registrationsArr', registrationsArr) 75 | console.log('🚀 -----------------------------------------------------------------------------------------------') 76 | return res.json(registrationsArr); 77 | } 78 | 79 | } catch (error) { 80 | console.log(error) 81 | } 82 | } 83 | }) 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /episode-2-start-dashboard/mobile-app/pages/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { View, Text, TouchableOpacity, StyleSheet, ImageBackground, SafeAreaView, FlatList, Image } from 'react-native' 3 | 4 | const bgImage = require('../assets/background.jpg') 5 | 6 | const DashBoard = ({ navigation }) => { 7 | const [events, setEvents] = useState([{ 8 | _id: 'idblah', 9 | title: 'London 5K running', 10 | sport: 'Running', 11 | description: "The best 5K event in London", 12 | price: "39.00", 13 | thumbnail_url: 'https://admin.concern.org.uk/sites/default/files/styles/hero_desktop/public/media/images/2019-06/London%20Marathon%20-%20Jenny%20Flynn.jpg/' 14 | }, 15 | { 16 | _id: 'idbah', 17 | title: 'London 5K running', 18 | sport: 'Running', 19 | description: "The best 5K event in London", 20 | price: "39.00", 21 | thumbnail_url: 'https://admin.concern.org.uk/sites/default/files/styles/hero_desktop/public/media/images/2019-06/London%20Marathon%20-%20Jenny%20Flynn.jpg/' 22 | }, 23 | { 24 | _id: 'idbla', 25 | title: 'London 5K running', 26 | sport: 'Running', 27 | description: "The best 5K event in London", 28 | price: "39.00", 29 | thumbnail_url: 'https://admin.concern.org.uk/sites/default/files/styles/hero_desktop/public/media/images/2019-06/London%20Marathon%20-%20Jenny%20Flynn.jpg/' 30 | }]) 31 | 32 | return ( 33 | 34 | 35 | 36 | DashBoard 37 | event._id} 42 | renderItem={({ item }) => { 43 | 44 | console.log('🚀 ---------------------------------------------------------') 45 | console.log('🚀 ~ file: Dashboard.js ~ line 51 ~ DashBoard ~ item', item) 46 | console.log('🚀 ---------------------------------------------------------') 47 | 48 | return ( 49 | 53 | Title: {item.title} 54 | Sport: {item.sport} 55 | Price: {'$' + item.price} 56 | Description: {item.description} 57 | console.log('Register')}> 58 | Register 59 | 60 | ) 61 | }}> 62 | 63 | 64 | navigation.navigate('Login')}> 65 | Logout 66 | 67 | 68 | 69 | 70 | ) 71 | } 72 | 73 | 74 | const styles = StyleSheet.create({ 75 | container: { 76 | flex: 1, 77 | }, 78 | image: { 79 | flex: 1, 80 | width: "100%", 81 | height: "100%", 82 | justifyContent: "center", 83 | alignItems: 'center' 84 | }, 85 | list: { 86 | width: "100%", 87 | paddingHorizontal: 20 88 | }, 89 | listItem: { 90 | padding: 8, 91 | backgroundColor: "#FFFF", 92 | marginVertical: 8, 93 | opacity: 0.9 94 | }, 95 | thumbnail: { 96 | width: 'auto', 97 | height: 150, 98 | marginBottom: 8 99 | }, 100 | eventTitle: { 101 | fontSize: 20, 102 | marginBottom: 8, 103 | fontWeight: "bold", 104 | color: "#444", 105 | marginBottom: 15, 106 | }, 107 | sport: { 108 | fontSize: 16, 109 | color: "#444", 110 | }, 111 | price: { 112 | fontSize: 16, 113 | color: '#999', 114 | marginTop: 5, 115 | fontWeight: 'bold' 116 | }, 117 | description: { 118 | fontSize: 16, 119 | color: "#444", 120 | }, 121 | boldText: { 122 | fontSize: 13, 123 | fontWeight: 'bold', 124 | color: "#444" 125 | }, 126 | primaryBtn: { 127 | height: 42, 128 | backgroundColor: '#007bff', 129 | justifyContent: 'center', 130 | alignItems: 'center', 131 | borderRadius: 4, 132 | marginTop: 20 133 | }, 134 | }) 135 | 136 | 137 | export default DashBoard -------------------------------------------------------------------------------- /episode-2-start-dashboard/mobile-app/pages/Login.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { View, Text, StyleSheet, ImageBackground, TouchableOpacity, TextInput } from 'react-native' 3 | 4 | const bgImage = require('../assets/background.jpg') 5 | 6 | const Login = ({ navigation }) => { 7 | 8 | const [email, setEmail] = useState(null) 9 | const [password, setPassword] = useState(null) 10 | 11 | 12 | const submitHandler = async () => { 13 | try { 14 | const response = await fetch('http://localhost:8080/api/login', { 15 | method: "POST", 16 | body: JSON.stringify({ email, password }), 17 | headers: { 'Content-Type': 'application/json' } 18 | }) 19 | 20 | const responseJson = await response.json() 21 | 22 | if (responseJson.user && responseJson.user_id) { 23 | // Save token in mobile db 24 | 25 | //route the user to Dashboard 26 | navigation.navigate('Dashboard') 27 | } 28 | console.log('🚀 ----------------------------------------------------------------------------') 29 | console.log('🚀 ~ file: Register.js ~ line 24 ~ submitHandler ~ responseJson', responseJson) 30 | console.log('🚀 ----------------------------------------------------------------------------') 31 | 32 | } catch (error) { 33 | console.log('🚀 --------------------------------------------------------------') 34 | console.log('🚀 ~ file: Register.js ~ line 19 ~ submitHandler ~ error', error) 35 | console.log('🚀 --------------------------------------------------------------') 36 | 37 | } 38 | 39 | } 40 | 41 | const registerInsteadHandler = () => { 42 | setEmail(null) 43 | setPassword(null) 44 | navigation.navigate('Register') 45 | } 46 | 47 | return ( 48 | 49 | 50 | Sport's App 51 | 52 | Email: 53 | setEmail(text)} 60 | autoCapitalize="none" /> 61 | 62 | Password: 63 | setPassword(text)} 70 | autoCapitalize="none" /> 71 | 72 | Login 73 | 74 | 75 | Register Instead 76 | 77 | 78 | 79 | 80 | ) 81 | } 82 | 83 | const styles = StyleSheet.create({ 84 | container: { 85 | flex: 1, 86 | justifyContent: "center", 87 | alignItems: 'center' 88 | }, 89 | image: { 90 | flex: 1, 91 | width: "100%", 92 | height: "100%", 93 | justifyContent: "center", 94 | alignItems: 'center' 95 | }, 96 | title: { 97 | fontSize: 32, 98 | marginBottom: 8, 99 | fontWeight: "bold", 100 | color: "#f04a5b" 101 | }, 102 | form: { 103 | alignSelf: "stretch", 104 | paddingHorizontal: 30, 105 | marginTop: 30 106 | }, 107 | label: { 108 | fontSize: 16, 109 | color: '#ffffff', 110 | fontWeight: "bold", 111 | marginBottom: 8 112 | }, 113 | input: { 114 | borderWidth: 1, 115 | borderColor: '#ffffff', 116 | paddingHorizontal: 10, 117 | fontSize: 16, 118 | color: '#ffffff', 119 | fontWeight: "400", 120 | height: 44, 121 | marginBottom: 30, 122 | borderRadius: 4, 123 | backgroundColor: 'rgba(0,0,0,0.4)' 124 | }, 125 | primaryBtn: { 126 | height: 42, 127 | backgroundColor: '#007bff', 128 | justifyContent: 'center', 129 | alignItems: 'center', 130 | borderRadius: 4, 131 | marginTop: 20 132 | }, 133 | secondaryBtn: { 134 | height: 42, 135 | backgroundColor: '#f04a5b', 136 | justifyContent: 'center', 137 | alignItems: 'center', 138 | borderRadius: 4, 139 | marginTop: 20 140 | } 141 | }) 142 | 143 | 144 | export default Login -------------------------------------------------------------------------------- /episode-1-login-register/mobile-app/pages/Login.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { View, Text, StyleSheet, ImageBackground, TouchableOpacity, TextInput } from 'react-native' 3 | 4 | const bgImage = require('../assets/background.jpg') 5 | 6 | const Login = ({ navigation }) => { 7 | 8 | const [email, setEmail] = useState(null) 9 | const [password, setPassword] = useState(null) 10 | 11 | 12 | const submitHandler = async () => { 13 | try { 14 | const response = await fetch('http://localhost:8080/api/login', { 15 | method: "POST", 16 | body: JSON.stringify({ email, password }), 17 | headers: { 'Content-Type': 'application/json' } 18 | }) 19 | 20 | const responseJson = await response.json() 21 | 22 | if (responseJson.user && responseJson.user_id) { 23 | // Save token in mobile db 24 | 25 | //route the user to Dashboard 26 | navigation.navigate('Dashboard') 27 | } 28 | console.log('🚀 ----------------------------------------------------------------------------') 29 | console.log('🚀 ~ file: Register.js ~ line 24 ~ submitHandler ~ responseJson', responseJson) 30 | console.log('🚀 ----------------------------------------------------------------------------') 31 | 32 | } catch (error) { 33 | console.log('🚀 --------------------------------------------------------------') 34 | console.log('🚀 ~ file: Register.js ~ line 19 ~ submitHandler ~ error', error) 35 | console.log('🚀 --------------------------------------------------------------') 36 | 37 | } 38 | 39 | } 40 | 41 | const registerInsteadHandler = () => { 42 | setEmail(null) 43 | setPassword(null) 44 | navigation.navigate('Register') 45 | } 46 | 47 | return ( 48 | 49 | 50 | Sport's App 51 | 52 | Email: 53 | setEmail(text)} 60 | autoCapitalize="none" /> 61 | 62 | Password: 63 | setPassword(text)} 72 | autoCapitalize="none" /> 73 | 74 | Login 75 | 76 | 77 | Register Instead 78 | 79 | 80 | 81 | 82 | ) 83 | } 84 | 85 | const styles = StyleSheet.create({ 86 | container: { 87 | flex: 1, 88 | justifyContent: "center", 89 | alignItems: 'center' 90 | }, 91 | image: { 92 | flex: 1, 93 | width: "100%", 94 | height: "100%", 95 | justifyContent: "center", 96 | alignItems: 'center' 97 | }, 98 | title: { 99 | fontSize: 32, 100 | marginBottom: 8, 101 | fontWeight: "bold", 102 | color: "#f04a5b" 103 | }, 104 | form: { 105 | alignSelf: "stretch", 106 | paddingHorizontal: 30, 107 | marginTop: 30 108 | }, 109 | label: { 110 | fontSize: 16, 111 | color: '#ffffff', 112 | fontWeight: "bold", 113 | marginBottom: 8 114 | }, 115 | input: { 116 | borderWidth: 1, 117 | borderColor: '#ffffff', 118 | paddingHorizontal: 10, 119 | fontSize: 16, 120 | color: '#ffffff', 121 | fontWeight: "400", 122 | height: 44, 123 | marginBottom: 30, 124 | borderRadius: 4, 125 | backgroundColor: 'rgba(0,0,0,0.4)' 126 | }, 127 | primaryBtn: { 128 | height: 42, 129 | backgroundColor: '#007bff', 130 | justifyContent: 'center', 131 | alignItems: 'center', 132 | borderRadius: 4, 133 | marginTop: 20 134 | }, 135 | secondaryBtn: { 136 | height: 42, 137 | backgroundColor: '#f04a5b', 138 | justifyContent: 'center', 139 | alignItems: 'center', 140 | borderRadius: 4, 141 | marginTop: 20 142 | } 143 | }) 144 | 145 | 146 | export default Login -------------------------------------------------------------------------------- /episode-5-dashboard-filter/mobile-app/pages/Login.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { View, Text, StyleSheet, ImageBackground, TouchableOpacity, TextInput } from 'react-native' 3 | import AsyncStorage from '@react-native-async-storage/async-storage'; 4 | import isLoggedIn from '../hooks/isLoggedIn'; 5 | 6 | const bgImage = require('../assets/background.jpg'); 7 | 8 | const Login = ({ navigation }) => { 9 | const [user, user_id] = isLoggedIn(); 10 | const [email, setEmail] = useState(null) 11 | const [password, setPassword] = useState(null) 12 | 13 | useEffect(() => { 14 | if (user !== null && user_id !== null) navigation.navigate('Dashboard'); 15 | }, []) 16 | 17 | const submitHandler = async () => { 18 | try { 19 | const response = await fetch('http://localhost:8080/api/login', { 20 | method: "POST", 21 | body: JSON.stringify({ email, password }), 22 | headers: { 'Content-Type': 'application/json' } 23 | }) 24 | 25 | const { user, user_id } = await response.json() 26 | 27 | if (user && user_id) { 28 | await AsyncStorage.setItem('user', user); 29 | await AsyncStorage.setItem('user_id', user_id); 30 | navigation.navigate('Dashboard'); 31 | } 32 | } catch (error) { 33 | console.log('🚀 --------------------------------------------------------------') 34 | console.log('🚀 ~ file: Register.js ~ line 19 ~ submitHandler ~ error', error) 35 | console.log('🚀 --------------------------------------------------------------') 36 | 37 | } 38 | } 39 | 40 | const registerInsteadHandler = () => { 41 | setEmail(null) 42 | setPassword(null) 43 | navigation.navigate('Register') 44 | } 45 | 46 | 47 | useEffect(() => { 48 | if (user && user_id) navigation.navigate('Dashboard') 49 | }, [user, user_id]) 50 | 51 | return ( 52 | 53 | 54 | Sport's App 55 | 56 | Email: 57 | setEmail(text)} 64 | autoCapitalize="none" /> 65 | 66 | Password: 67 | setPassword(text)} 74 | autoCapitalize="none" /> 75 | 76 | Login 77 | 78 | 79 | Register Instead 80 | 81 | 82 | 83 | 84 | ) 85 | } 86 | 87 | const styles = StyleSheet.create({ 88 | container: { 89 | flex: 1, 90 | justifyContent: "center", 91 | alignItems: 'center' 92 | }, 93 | image: { 94 | flex: 1, 95 | width: "100%", 96 | height: "100%", 97 | justifyContent: "center", 98 | alignItems: 'center' 99 | }, 100 | title: { 101 | fontSize: 32, 102 | marginBottom: 8, 103 | fontWeight: "bold", 104 | color: "#f04a5b" 105 | }, 106 | form: { 107 | alignSelf: "stretch", 108 | paddingHorizontal: 30, 109 | marginTop: 30 110 | }, 111 | label: { 112 | fontSize: 16, 113 | color: '#ffffff', 114 | fontWeight: "bold", 115 | marginBottom: 8 116 | }, 117 | input: { 118 | borderWidth: 1, 119 | borderColor: '#ffffff', 120 | paddingHorizontal: 10, 121 | fontSize: 16, 122 | color: '#ffffff', 123 | fontWeight: "400", 124 | height: 44, 125 | marginBottom: 30, 126 | borderRadius: 4, 127 | backgroundColor: 'rgba(0,0,0,0.4)' 128 | }, 129 | primaryBtn: { 130 | height: 42, 131 | backgroundColor: '#007bff', 132 | justifyContent: 'center', 133 | alignItems: 'center', 134 | borderRadius: 4, 135 | marginTop: 20 136 | }, 137 | secondaryBtn: { 138 | height: 42, 139 | backgroundColor: '#f04a5b', 140 | justifyContent: 'center', 141 | alignItems: 'center', 142 | borderRadius: 4, 143 | marginTop: 20 144 | } 145 | }) 146 | 147 | 148 | export default Login -------------------------------------------------------------------------------- /episode-3-add-create-event-page/mobile-app/pages/Login.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { View, Text, StyleSheet, ImageBackground, TouchableOpacity, TextInput } from 'react-native' 3 | import AsyncStorage from '@react-native-async-storage/async-storage'; 4 | import isLoggedIn from '../hooks/isLoggedIn'; 5 | 6 | const bgImage = require('../assets/background.jpg'); 7 | 8 | const Login = ({ navigation }) => { 9 | const [user, user_id] = isLoggedIn(); 10 | const [email, setEmail] = useState(null) 11 | const [password, setPassword] = useState(null) 12 | 13 | useEffect(() => { 14 | if (user !== null && user_id !== null) navigation.navigate('Dashboard'); 15 | }, []) 16 | 17 | const submitHandler = async () => { 18 | try { 19 | const response = await fetch('http://localhost:8080/api/login', { 20 | method: "POST", 21 | body: JSON.stringify({ email, password }), 22 | headers: { 'Content-Type': 'application/json' } 23 | }) 24 | 25 | const { user, user_id } = await response.json() 26 | 27 | if (user && user_id) { 28 | await AsyncStorage.setItem('user', user); 29 | await AsyncStorage.setItem('user_id', user_id); 30 | navigation.navigate('Dashboard'); 31 | } 32 | } catch (error) { 33 | console.log('🚀 --------------------------------------------------------------') 34 | console.log('🚀 ~ file: Register.js ~ line 19 ~ submitHandler ~ error', error) 35 | console.log('🚀 --------------------------------------------------------------') 36 | 37 | } 38 | } 39 | 40 | const registerInsteadHandler = () => { 41 | setEmail(null) 42 | setPassword(null) 43 | navigation.navigate('Register') 44 | } 45 | 46 | 47 | useEffect(() => { 48 | if (user && user_id) navigation.navigate('Dashboard') 49 | }, [user, user_id]) 50 | 51 | return ( 52 | 53 | 54 | Sport's App 55 | 56 | Email: 57 | setEmail(text)} 64 | autoCapitalize="none" /> 65 | 66 | Password: 67 | setPassword(text)} 74 | autoCapitalize="none" /> 75 | 76 | Login 77 | 78 | 79 | Register Instead 80 | 81 | 82 | 83 | 84 | ) 85 | } 86 | 87 | const styles = StyleSheet.create({ 88 | container: { 89 | flex: 1, 90 | justifyContent: "center", 91 | alignItems: 'center' 92 | }, 93 | image: { 94 | flex: 1, 95 | width: "100%", 96 | height: "100%", 97 | justifyContent: "center", 98 | alignItems: 'center' 99 | }, 100 | title: { 101 | fontSize: 32, 102 | marginBottom: 8, 103 | fontWeight: "bold", 104 | color: "#f04a5b" 105 | }, 106 | form: { 107 | alignSelf: "stretch", 108 | paddingHorizontal: 30, 109 | marginTop: 30 110 | }, 111 | label: { 112 | fontSize: 16, 113 | color: '#ffffff', 114 | fontWeight: "bold", 115 | marginBottom: 8 116 | }, 117 | input: { 118 | borderWidth: 1, 119 | borderColor: '#ffffff', 120 | paddingHorizontal: 10, 121 | fontSize: 16, 122 | color: '#ffffff', 123 | fontWeight: "400", 124 | height: 44, 125 | marginBottom: 30, 126 | borderRadius: 4, 127 | backgroundColor: 'rgba(0,0,0,0.4)' 128 | }, 129 | primaryBtn: { 130 | height: 42, 131 | backgroundColor: '#007bff', 132 | justifyContent: 'center', 133 | alignItems: 'center', 134 | borderRadius: 4, 135 | marginTop: 20 136 | }, 137 | secondaryBtn: { 138 | height: 42, 139 | backgroundColor: '#f04a5b', 140 | justifyContent: 'center', 141 | alignItems: 'center', 142 | borderRadius: 4, 143 | marginTop: 20 144 | } 145 | }) 146 | 147 | 148 | export default Login -------------------------------------------------------------------------------- /episode-4-fetch-created-events/mobile-app/pages/Login.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { View, Text, StyleSheet, ImageBackground, TouchableOpacity, TextInput } from 'react-native' 3 | import AsyncStorage from '@react-native-async-storage/async-storage'; 4 | import isLoggedIn from '../hooks/isLoggedIn'; 5 | 6 | const bgImage = require('../assets/background.jpg'); 7 | 8 | const Login = ({ navigation }) => { 9 | const [user, user_id] = isLoggedIn(); 10 | const [email, setEmail] = useState(null) 11 | const [password, setPassword] = useState(null) 12 | 13 | useEffect(() => { 14 | if (user !== null && user_id !== null) navigation.navigate('Dashboard'); 15 | }, []) 16 | 17 | const submitHandler = async () => { 18 | try { 19 | const response = await fetch('http://localhost:8080/api/login', { 20 | method: "POST", 21 | body: JSON.stringify({ email, password }), 22 | headers: { 'Content-Type': 'application/json' } 23 | }) 24 | 25 | const { user, user_id } = await response.json() 26 | 27 | if (user && user_id) { 28 | await AsyncStorage.setItem('user', user); 29 | await AsyncStorage.setItem('user_id', user_id); 30 | navigation.navigate('Dashboard'); 31 | } 32 | } catch (error) { 33 | console.log('🚀 --------------------------------------------------------------') 34 | console.log('🚀 ~ file: Register.js ~ line 19 ~ submitHandler ~ error', error) 35 | console.log('🚀 --------------------------------------------------------------') 36 | 37 | } 38 | } 39 | 40 | const registerInsteadHandler = () => { 41 | setEmail(null) 42 | setPassword(null) 43 | navigation.navigate('Register') 44 | } 45 | 46 | 47 | useEffect(() => { 48 | if (user && user_id) navigation.navigate('Dashboard') 49 | }, [user, user_id]) 50 | 51 | return ( 52 | 53 | 54 | Sport's App 55 | 56 | Email: 57 | setEmail(text)} 64 | autoCapitalize="none" /> 65 | 66 | Password: 67 | setPassword(text)} 74 | autoCapitalize="none" /> 75 | 76 | Login 77 | 78 | 79 | Register Instead 80 | 81 | 82 | 83 | 84 | ) 85 | } 86 | 87 | const styles = StyleSheet.create({ 88 | container: { 89 | flex: 1, 90 | justifyContent: "center", 91 | alignItems: 'center' 92 | }, 93 | image: { 94 | flex: 1, 95 | width: "100%", 96 | height: "100%", 97 | justifyContent: "center", 98 | alignItems: 'center' 99 | }, 100 | title: { 101 | fontSize: 32, 102 | marginBottom: 8, 103 | fontWeight: "bold", 104 | color: "#f04a5b" 105 | }, 106 | form: { 107 | alignSelf: "stretch", 108 | paddingHorizontal: 30, 109 | marginTop: 30 110 | }, 111 | label: { 112 | fontSize: 16, 113 | color: '#ffffff', 114 | fontWeight: "bold", 115 | marginBottom: 8 116 | }, 117 | input: { 118 | borderWidth: 1, 119 | borderColor: '#ffffff', 120 | paddingHorizontal: 10, 121 | fontSize: 16, 122 | color: '#ffffff', 123 | fontWeight: "400", 124 | height: 44, 125 | marginBottom: 30, 126 | borderRadius: 4, 127 | backgroundColor: 'rgba(0,0,0,0.4)' 128 | }, 129 | primaryBtn: { 130 | height: 42, 131 | backgroundColor: '#007bff', 132 | justifyContent: 'center', 133 | alignItems: 'center', 134 | borderRadius: 4, 135 | marginTop: 20 136 | }, 137 | secondaryBtn: { 138 | height: 42, 139 | backgroundColor: '#f04a5b', 140 | justifyContent: 'center', 141 | alignItems: 'center', 142 | borderRadius: 4, 143 | marginTop: 20 144 | } 145 | }) 146 | 147 | 148 | export default Login -------------------------------------------------------------------------------- /episode-4-fetch-created-events/mobile-app/pages/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { View, Text, TouchableOpacity, StyleSheet, ImageBackground, SafeAreaView, FlatList, Image } from 'react-native' 3 | import isLoggedIn from '../hooks/isLoggedIn'; 4 | import AsyncStorage from '@react-native-async-storage/async-storage'; 5 | import ActionButton from 'react-native-action-button'; 6 | import ModalComponent from '../components/ModalComponent' 7 | 8 | const bgImage = require('../assets/background.jpg'); 9 | import { FontAwesome5, MaterialCommunityIcons, Ionicons } from '@expo/vector-icons'; 10 | 11 | const DashBoard = ({ navigation }) => { 12 | const [user, user_id] = isLoggedIn({ navigation }); 13 | const [modalIsVisible, setModalIsVisible] = useState(false); 14 | const [events, setEvents] = useState([]) 15 | 16 | useEffect(() => { 17 | loadEvents() 18 | }, []) 19 | 20 | const loadEvents = async () => { 21 | const response = await fetch(`http://localhost:8080/api/dashboard`, { 22 | method: 'GET', 23 | headers: { user: user } 24 | }) 25 | 26 | const jsonResponse = await response.json() 27 | setEvents(jsonResponse.events) 28 | } 29 | 30 | const logoutHandler = async () => { 31 | await AsyncStorage.removeItem('user'); 32 | await AsyncStorage.removeItem('user_id'); 33 | navigation.navigate('Login') 34 | } 35 | 36 | return ( 37 | 38 | 39 | 40 | DashBoard 41 | event._id} 46 | renderItem={({ item }) => { 47 | return ( 48 | 52 | Title: {item.title} 53 | Sport: {item.sport} 54 | Price: {'$' + item.price} 55 | Description: {item.description} 56 | console.log('Register')}> 57 | Register 58 | 59 | ) 60 | }}> 61 | 62 | 63 | 64 | 65 | Logout 66 | 67 | 68 | 69 | setModalIsVisible(true)}> 70 | 71 | 72 | 73 | 74 | 75 | 76 | ) 77 | } 78 | 79 | 80 | const styles = StyleSheet.create({ 81 | container: { 82 | flex: 1, 83 | }, 84 | image: { 85 | flex: 1, 86 | width: "100%", 87 | height: "100%", 88 | justifyContent: "center", 89 | alignItems: 'center' 90 | }, 91 | list: { 92 | width: "100%", 93 | paddingHorizontal: 20 94 | }, 95 | listItem: { 96 | padding: 8, 97 | backgroundColor: "#FFFF", 98 | marginVertical: 8, 99 | opacity: 0.9 100 | }, 101 | thumbnail: { 102 | width: 'auto', 103 | height: 150, 104 | marginBottom: 8 105 | }, 106 | eventTitle: { 107 | fontSize: 20, 108 | marginBottom: 8, 109 | fontWeight: "bold", 110 | color: "#444", 111 | marginBottom: 15, 112 | }, 113 | sport: { 114 | fontSize: 16, 115 | color: "#444", 116 | }, 117 | price: { 118 | fontSize: 16, 119 | color: '#999', 120 | marginTop: 5, 121 | fontWeight: 'bold' 122 | }, 123 | description: { 124 | fontSize: 16, 125 | color: "#444", 126 | }, 127 | boldText: { 128 | fontSize: 13, 129 | fontWeight: 'bold', 130 | color: "#444" 131 | }, 132 | primaryBtn: { 133 | height: 42, 134 | backgroundColor: '#007bff', 135 | justifyContent: 'center', 136 | alignItems: 'center', 137 | borderRadius: 4, 138 | marginTop: 20 139 | }, 140 | actionButton: { 141 | fontSize: 20, 142 | height: 22, 143 | color: 'white' 144 | } 145 | }) 146 | 147 | 148 | export default DashBoard; -------------------------------------------------------------------------------- /episode-2-start-dashboard/mobile-app/pages/Register.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { View, Text, StyleSheet, TextInput, ImageBackground, TouchableOpacity } from 'react-native' 3 | 4 | const bgImage = require('../assets/background.jpg') 5 | 6 | const Register = ({ navigation }) => { 7 | const [firstName, setName] = useState(null) 8 | const [lastName, setLastName] = useState(null) 9 | const [email, setEmail] = useState(null) 10 | const [password, setPassword] = useState(null) 11 | 12 | 13 | const submitHandler = async () => { 14 | console.log(firstName, lastName, email, password) 15 | 16 | try { 17 | const response = await fetch('http://localhost:8080/api/user/register', { 18 | method: "POST", 19 | body: JSON.stringify({ firstName, lastName, email, password }), 20 | headers: { 'Content-Type': 'application/json' } 21 | }) 22 | 23 | const responseJson = await response.json() 24 | console.log('🚀 ----------------------------------------------------------------------------') 25 | console.log('🚀 ~ file: Register.js ~ line 24 ~ submitHandler ~ responseJson', responseJson) 26 | console.log('🚀 ----------------------------------------------------------------------------') 27 | 28 | } catch (error) { 29 | console.log('🚀 --------------------------------------------------------------') 30 | console.log('🚀 ~ file: Register.js ~ line 19 ~ submitHandler ~ error', error) 31 | console.log('🚀 --------------------------------------------------------------') 32 | 33 | } 34 | 35 | } 36 | 37 | const cancelHandler = () => { 38 | setName(null) 39 | setLastName(null) 40 | setEmail(null) 41 | setPassword(null) 42 | navigation.navigate('Login') 43 | } 44 | 45 | return ( 46 | 47 | 48 | Sport's App 49 | 50 | First Name: 51 | setName(text)} 58 | /> 59 | Last Name: 60 | setLastName(text)} 67 | /> 68 | Email: 69 | setEmail(text)} 76 | autoCapitalize="none" /> 77 | 78 | Password: 79 | setPassword(text)} 86 | autoCapitalize="none" /> 87 | 88 | Register 89 | 90 | 91 | Cancel 92 | 93 | 94 | 95 | 96 | 97 | ) 98 | } 99 | 100 | 101 | const styles = StyleSheet.create({ 102 | container: { 103 | flex: 1, 104 | justifyContent: "center", 105 | alignItems: 'center' 106 | }, 107 | image: { 108 | flex: 1, 109 | width: "100%", 110 | height: "100%", 111 | justifyContent: "center", 112 | alignItems: 'center' 113 | }, 114 | title: { 115 | fontSize: 32, 116 | marginBottom: 8, 117 | fontWeight: "bold", 118 | color: "#f04a5b" 119 | }, 120 | form: { 121 | alignSelf: "stretch", 122 | paddingHorizontal: 30, 123 | marginTop: 30 124 | }, 125 | label: { 126 | fontSize: 16, 127 | color: '#ffffff', 128 | fontWeight: "bold", 129 | marginBottom: 8 130 | }, 131 | input: { 132 | borderWidth: 1, 133 | borderColor: '#ffffff', 134 | paddingHorizontal: 10, 135 | fontSize: 16, 136 | color: '#ffffff', 137 | fontWeight: "400", 138 | height: 44, 139 | marginBottom: 30, 140 | borderRadius: 4, 141 | backgroundColor: 'rgba(0,0,0,0.4)' 142 | }, 143 | primaryBtn: { 144 | height: 42, 145 | backgroundColor: '#007bff', 146 | justifyContent: 'center', 147 | alignItems: 'center', 148 | borderRadius: 4, 149 | marginTop: 20 150 | }, 151 | secondaryBtn: { 152 | height: 42, 153 | backgroundColor: '#f04a5b', 154 | justifyContent: 'center', 155 | alignItems: 'center', 156 | borderRadius: 4, 157 | marginTop: 20 158 | } 159 | 160 | 161 | }) 162 | 163 | 164 | export default Register; -------------------------------------------------------------------------------- /episode-1-login-register/mobile-app/pages/Register.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { View, Text, StyleSheet, TextInput, ImageBackground, TouchableOpacity } from 'react-native' 3 | 4 | const bgImage = require('../assets/background.jpg') 5 | 6 | const Register = ({ navigation }) => { 7 | const [firstName, setName] = useState(null) 8 | const [lastName, setLastName] = useState(null) 9 | const [email, setEmail] = useState(null) 10 | const [password, setPassword] = useState(null) 11 | 12 | 13 | const submitHandler = async () => { 14 | console.log(firstName, lastName, email, password) 15 | 16 | try { 17 | const response = await fetch('http://localhost:8080/api/user/register', { 18 | method: "POST", 19 | body: JSON.stringify({ firstName, lastName, email, password }), 20 | headers: { 'Content-Type': 'application/json' } 21 | }) 22 | 23 | const responseJson = await response.json() 24 | console.log('🚀 ----------------------------------------------------------------------------') 25 | console.log('🚀 ~ file: Register.js ~ line 24 ~ submitHandler ~ responseJson', responseJson) 26 | console.log('🚀 ----------------------------------------------------------------------------') 27 | 28 | } catch (error) { 29 | console.log('🚀 --------------------------------------------------------------') 30 | console.log('🚀 ~ file: Register.js ~ line 19 ~ submitHandler ~ error', error) 31 | console.log('🚀 --------------------------------------------------------------') 32 | 33 | } 34 | 35 | } 36 | 37 | const cancelHandler = () => { 38 | setName(null) 39 | setLastName(null) 40 | setEmail(null) 41 | setPassword(null) 42 | navigation.navigate('Login') 43 | } 44 | 45 | return ( 46 | 47 | 48 | Sport's App 49 | 50 | First Name: 51 | setName(text)} 58 | /> 59 | Last Name: 60 | setLastName(text)} 67 | /> 68 | Email: 69 | setEmail(text)} 76 | autoCapitalize="none" /> 77 | 78 | Password: 79 | setPassword(text)} 88 | autoCapitalize="none" /> 89 | 90 | Register 91 | 92 | 93 | Cancel 94 | 95 | 96 | 97 | 98 | 99 | ) 100 | } 101 | 102 | 103 | const styles = StyleSheet.create({ 104 | container: { 105 | flex: 1, 106 | justifyContent: "center", 107 | alignItems: 'center' 108 | }, 109 | image: { 110 | flex: 1, 111 | width: "100%", 112 | height: "100%", 113 | justifyContent: "center", 114 | alignItems: 'center' 115 | }, 116 | title: { 117 | fontSize: 32, 118 | marginBottom: 8, 119 | fontWeight: "bold", 120 | color: "#f04a5b" 121 | }, 122 | form: { 123 | alignSelf: "stretch", 124 | paddingHorizontal: 30, 125 | marginTop: 30 126 | }, 127 | label: { 128 | fontSize: 16, 129 | color: '#ffffff', 130 | fontWeight: "bold", 131 | marginBottom: 8 132 | }, 133 | input: { 134 | borderWidth: 1, 135 | borderColor: '#ffffff', 136 | paddingHorizontal: 10, 137 | fontSize: 16, 138 | color: '#ffffff', 139 | fontWeight: "400", 140 | height: 44, 141 | marginBottom: 30, 142 | borderRadius: 4, 143 | backgroundColor: 'rgba(0,0,0,0.4)' 144 | }, 145 | primaryBtn: { 146 | height: 42, 147 | backgroundColor: '#007bff', 148 | justifyContent: 'center', 149 | alignItems: 'center', 150 | borderRadius: 4, 151 | marginTop: 20 152 | }, 153 | secondaryBtn: { 154 | height: 42, 155 | backgroundColor: '#f04a5b', 156 | justifyContent: 'center', 157 | alignItems: 'center', 158 | borderRadius: 4, 159 | marginTop: 20 160 | } 161 | 162 | 163 | }) 164 | 165 | 166 | export default Register; -------------------------------------------------------------------------------- /episode-3-add-create-event-page/mobile-app/pages/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { View, Text, TouchableOpacity, StyleSheet, ImageBackground, SafeAreaView, FlatList, Image } from 'react-native' 3 | import isLoggedIn from '../hooks/isLoggedIn'; 4 | import AsyncStorage from '@react-native-async-storage/async-storage'; 5 | import ActionButton from 'react-native-action-button'; 6 | import ModalComponent from '../components/ModalComponent' 7 | 8 | const bgImage = require('../assets/background.jpg'); 9 | import { FontAwesome5, MaterialCommunityIcons, Ionicons } from '@expo/vector-icons'; 10 | 11 | const DashBoard = ({ navigation }) => { 12 | const [user, user_id] = isLoggedIn({ navigation }); 13 | const [modalIsVisible, setModalIsVisible] = useState(false); 14 | const [events, setEvents] = useState([{ 15 | _id: 'idblah', 16 | title: 'London 5K running', 17 | sport: 'Running', 18 | description: "The best 5K event in London", 19 | price: "39.00", 20 | thumbnail_url: 'https://admin.concern.org.uk/sites/default/files/styles/hero_desktop/public/media/images/2019-06/London%20Marathon%20-%20Jenny%20Flynn.jpg/' 21 | }, 22 | { 23 | _id: 'idbah', 24 | title: 'London 5K running', 25 | sport: 'Running', 26 | description: "The best 5K event in London", 27 | price: "39.00", 28 | thumbnail_url: 'https://admin.concern.org.uk/sites/default/files/styles/hero_desktop/public/media/images/2019-06/London%20Marathon%20-%20Jenny%20Flynn.jpg/' 29 | }, 30 | { 31 | _id: 'idbla', 32 | title: 'London 5K running', 33 | sport: 'Running', 34 | description: "The best 5K event in London", 35 | price: "39.00", 36 | thumbnail_url: 'https://admin.concern.org.uk/sites/default/files/styles/hero_desktop/public/media/images/2019-06/London%20Marathon%20-%20Jenny%20Flynn.jpg/' 37 | }]) 38 | 39 | const logoutHandler = async () => { 40 | await AsyncStorage.removeItem('user'); 41 | await AsyncStorage.removeItem('user_id'); 42 | navigation.navigate('Login') 43 | } 44 | 45 | return ( 46 | 47 | 48 | 49 | DashBoard 50 | event._id} 55 | renderItem={({ item }) => { 56 | return ( 57 | 61 | Title: {item.title} 62 | Sport: {item.sport} 63 | Price: {'$' + item.price} 64 | Description: {item.description} 65 | console.log('Register')}> 66 | Register 67 | 68 | ) 69 | }}> 70 | 71 | 72 | 73 | 74 | Logout 75 | 76 | 77 | 78 | setModalIsVisible(true)}> 79 | 80 | 81 | 82 | 83 | 84 | 85 | ) 86 | } 87 | 88 | 89 | const styles = StyleSheet.create({ 90 | container: { 91 | flex: 1, 92 | }, 93 | image: { 94 | flex: 1, 95 | width: "100%", 96 | height: "100%", 97 | justifyContent: "center", 98 | alignItems: 'center' 99 | }, 100 | list: { 101 | width: "100%", 102 | paddingHorizontal: 20 103 | }, 104 | listItem: { 105 | padding: 8, 106 | backgroundColor: "#FFFF", 107 | marginVertical: 8, 108 | opacity: 0.9 109 | }, 110 | thumbnail: { 111 | width: 'auto', 112 | height: 150, 113 | marginBottom: 8 114 | }, 115 | eventTitle: { 116 | fontSize: 20, 117 | marginBottom: 8, 118 | fontWeight: "bold", 119 | color: "#444", 120 | marginBottom: 15, 121 | }, 122 | sport: { 123 | fontSize: 16, 124 | color: "#444", 125 | }, 126 | price: { 127 | fontSize: 16, 128 | color: '#999', 129 | marginTop: 5, 130 | fontWeight: 'bold' 131 | }, 132 | description: { 133 | fontSize: 16, 134 | color: "#444", 135 | }, 136 | boldText: { 137 | fontSize: 13, 138 | fontWeight: 'bold', 139 | color: "#444" 140 | }, 141 | primaryBtn: { 142 | height: 42, 143 | backgroundColor: '#007bff', 144 | justifyContent: 'center', 145 | alignItems: 'center', 146 | borderRadius: 4, 147 | marginTop: 20 148 | }, 149 | actionButton: { 150 | fontSize: 20, 151 | height: 22, 152 | color: 'white' 153 | } 154 | }) 155 | 156 | 157 | export default DashBoard; -------------------------------------------------------------------------------- /episode-5-dashboard-filter/mobile-app/pages/Register.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { View, Text, StyleSheet, TextInput, ImageBackground, TouchableOpacity } from 'react-native'; 3 | import AsyncStorage from '@react-native-async-storage/async-storage'; 4 | import isLoggedIn from '../hooks/isLoggedIn'; 5 | 6 | const bgImage = require('../assets/background.jpg') 7 | 8 | const Register = ({ navigation }) => { 9 | const [user, user_id] = isLoggedIn(); 10 | const [firstName, setName] = useState(null) 11 | const [lastName, setLastName] = useState(null) 12 | const [email, setEmail] = useState(null) 13 | const [password, setPassword] = useState(null) 14 | 15 | useEffect(() => { 16 | if (user !== null && user_id !== null) navigation.navigate('Dashboard') 17 | }, []) 18 | 19 | const submitHandler = async () => { 20 | try { 21 | const response = await fetch('http://localhost:8080/api/user/register', { 22 | method: "POST", 23 | body: JSON.stringify({ firstName, lastName, email, password }), 24 | headers: { 'Content-Type': 'application/json' } 25 | }) 26 | 27 | const { user, user_id } = await response.json() 28 | if (user && user_id) { 29 | await AsyncStorage.setItem('user', user); 30 | await AsyncStorage.setItem('user_id', user_id); 31 | navigation.navigate('Dashboard'); 32 | } 33 | } catch (error) { 34 | console.log('🚀 --------------------------------------------------------------') 35 | console.log('🚀 ~ file: Register.js ~ line 19 ~ submitHandler ~ error', error) 36 | console.log('🚀 --------------------------------------------------------------') 37 | } 38 | 39 | } 40 | 41 | const cancelHandler = () => { 42 | setName(null) 43 | setLastName(null) 44 | setEmail(null) 45 | setPassword(null) 46 | navigation.navigate('Login') 47 | } 48 | 49 | return ( 50 | 51 | 52 | Sport's App 53 | 54 | First Name: 55 | setName(text)} 62 | /> 63 | Last Name: 64 | setLastName(text)} 71 | /> 72 | Email: 73 | setEmail(text)} 80 | autoCapitalize="none" /> 81 | 82 | Password: 83 | setPassword(text)} 90 | autoCapitalize="none" /> 91 | 92 | Register 93 | 94 | 95 | Cancel 96 | 97 | 98 | 99 | 100 | 101 | ) 102 | } 103 | 104 | 105 | const styles = StyleSheet.create({ 106 | container: { 107 | flex: 1, 108 | justifyContent: "center", 109 | alignItems: 'center' 110 | }, 111 | image: { 112 | flex: 1, 113 | width: "100%", 114 | height: "100%", 115 | justifyContent: "center", 116 | alignItems: 'center' 117 | }, 118 | title: { 119 | fontSize: 32, 120 | marginBottom: 8, 121 | fontWeight: "bold", 122 | color: "#f04a5b" 123 | }, 124 | form: { 125 | alignSelf: "stretch", 126 | paddingHorizontal: 30, 127 | marginTop: 30 128 | }, 129 | label: { 130 | fontSize: 16, 131 | color: '#ffffff', 132 | fontWeight: "bold", 133 | marginBottom: 8 134 | }, 135 | input: { 136 | borderWidth: 1, 137 | borderColor: '#ffffff', 138 | paddingHorizontal: 10, 139 | fontSize: 16, 140 | color: '#ffffff', 141 | fontWeight: "400", 142 | height: 44, 143 | marginBottom: 30, 144 | borderRadius: 4, 145 | backgroundColor: 'rgba(0,0,0,0.4)' 146 | }, 147 | primaryBtn: { 148 | height: 42, 149 | backgroundColor: '#007bff', 150 | justifyContent: 'center', 151 | alignItems: 'center', 152 | borderRadius: 4, 153 | marginTop: 20 154 | }, 155 | secondaryBtn: { 156 | height: 42, 157 | backgroundColor: '#f04a5b', 158 | justifyContent: 'center', 159 | alignItems: 'center', 160 | borderRadius: 4, 161 | marginTop: 20 162 | } 163 | 164 | 165 | }) 166 | 167 | 168 | export default Register; -------------------------------------------------------------------------------- /episode-3-add-create-event-page/mobile-app/pages/Register.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { View, Text, StyleSheet, TextInput, ImageBackground, TouchableOpacity } from 'react-native'; 3 | import AsyncStorage from '@react-native-async-storage/async-storage'; 4 | import isLoggedIn from '../hooks/isLoggedIn'; 5 | 6 | const bgImage = require('../assets/background.jpg') 7 | 8 | const Register = ({ navigation }) => { 9 | const [user, user_id] = isLoggedIn(); 10 | const [firstName, setName] = useState(null) 11 | const [lastName, setLastName] = useState(null) 12 | const [email, setEmail] = useState(null) 13 | const [password, setPassword] = useState(null) 14 | 15 | useEffect(() => { 16 | if (user !== null && user_id !== null) navigation.navigate('Dashboard') 17 | }, []) 18 | 19 | const submitHandler = async () => { 20 | try { 21 | const response = await fetch('http://localhost:8080/api/user/register', { 22 | method: "POST", 23 | body: JSON.stringify({ firstName, lastName, email, password }), 24 | headers: { 'Content-Type': 'application/json' } 25 | }) 26 | 27 | const { user, user_id } = await response.json() 28 | if (user && user_id) { 29 | await AsyncStorage.setItem('user', user); 30 | await AsyncStorage.setItem('user_id', user_id); 31 | navigation.navigate('Dashboard'); 32 | } 33 | } catch (error) { 34 | console.log('🚀 --------------------------------------------------------------') 35 | console.log('🚀 ~ file: Register.js ~ line 19 ~ submitHandler ~ error', error) 36 | console.log('🚀 --------------------------------------------------------------') 37 | } 38 | 39 | } 40 | 41 | const cancelHandler = () => { 42 | setName(null) 43 | setLastName(null) 44 | setEmail(null) 45 | setPassword(null) 46 | navigation.navigate('Login') 47 | } 48 | 49 | return ( 50 | 51 | 52 | Sport's App 53 | 54 | First Name: 55 | setName(text)} 62 | /> 63 | Last Name: 64 | setLastName(text)} 71 | /> 72 | Email: 73 | setEmail(text)} 80 | autoCapitalize="none" /> 81 | 82 | Password: 83 | setPassword(text)} 90 | autoCapitalize="none" /> 91 | 92 | Register 93 | 94 | 95 | Cancel 96 | 97 | 98 | 99 | 100 | 101 | ) 102 | } 103 | 104 | 105 | const styles = StyleSheet.create({ 106 | container: { 107 | flex: 1, 108 | justifyContent: "center", 109 | alignItems: 'center' 110 | }, 111 | image: { 112 | flex: 1, 113 | width: "100%", 114 | height: "100%", 115 | justifyContent: "center", 116 | alignItems: 'center' 117 | }, 118 | title: { 119 | fontSize: 32, 120 | marginBottom: 8, 121 | fontWeight: "bold", 122 | color: "#f04a5b" 123 | }, 124 | form: { 125 | alignSelf: "stretch", 126 | paddingHorizontal: 30, 127 | marginTop: 30 128 | }, 129 | label: { 130 | fontSize: 16, 131 | color: '#ffffff', 132 | fontWeight: "bold", 133 | marginBottom: 8 134 | }, 135 | input: { 136 | borderWidth: 1, 137 | borderColor: '#ffffff', 138 | paddingHorizontal: 10, 139 | fontSize: 16, 140 | color: '#ffffff', 141 | fontWeight: "400", 142 | height: 44, 143 | marginBottom: 30, 144 | borderRadius: 4, 145 | backgroundColor: 'rgba(0,0,0,0.4)' 146 | }, 147 | primaryBtn: { 148 | height: 42, 149 | backgroundColor: '#007bff', 150 | justifyContent: 'center', 151 | alignItems: 'center', 152 | borderRadius: 4, 153 | marginTop: 20 154 | }, 155 | secondaryBtn: { 156 | height: 42, 157 | backgroundColor: '#f04a5b', 158 | justifyContent: 'center', 159 | alignItems: 'center', 160 | borderRadius: 4, 161 | marginTop: 20 162 | } 163 | 164 | 165 | }) 166 | 167 | 168 | export default Register; -------------------------------------------------------------------------------- /episode-4-fetch-created-events/mobile-app/pages/Register.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { View, Text, StyleSheet, TextInput, ImageBackground, TouchableOpacity } from 'react-native'; 3 | import AsyncStorage from '@react-native-async-storage/async-storage'; 4 | import isLoggedIn from '../hooks/isLoggedIn'; 5 | 6 | const bgImage = require('../assets/background.jpg') 7 | 8 | const Register = ({ navigation }) => { 9 | const [user, user_id] = isLoggedIn(); 10 | const [firstName, setName] = useState(null) 11 | const [lastName, setLastName] = useState(null) 12 | const [email, setEmail] = useState(null) 13 | const [password, setPassword] = useState(null) 14 | 15 | useEffect(() => { 16 | if (user !== null && user_id !== null) navigation.navigate('Dashboard') 17 | }, []) 18 | 19 | const submitHandler = async () => { 20 | try { 21 | const response = await fetch('http://localhost:8080/api/user/register', { 22 | method: "POST", 23 | body: JSON.stringify({ firstName, lastName, email, password }), 24 | headers: { 'Content-Type': 'application/json' } 25 | }) 26 | 27 | const { user, user_id } = await response.json() 28 | if (user && user_id) { 29 | await AsyncStorage.setItem('user', user); 30 | await AsyncStorage.setItem('user_id', user_id); 31 | navigation.navigate('Dashboard'); 32 | } 33 | } catch (error) { 34 | console.log('🚀 --------------------------------------------------------------') 35 | console.log('🚀 ~ file: Register.js ~ line 19 ~ submitHandler ~ error', error) 36 | console.log('🚀 --------------------------------------------------------------') 37 | } 38 | 39 | } 40 | 41 | const cancelHandler = () => { 42 | setName(null) 43 | setLastName(null) 44 | setEmail(null) 45 | setPassword(null) 46 | navigation.navigate('Login') 47 | } 48 | 49 | return ( 50 | 51 | 52 | Sport's App 53 | 54 | First Name: 55 | setName(text)} 62 | /> 63 | Last Name: 64 | setLastName(text)} 71 | /> 72 | Email: 73 | setEmail(text)} 80 | autoCapitalize="none" /> 81 | 82 | Password: 83 | setPassword(text)} 90 | autoCapitalize="none" /> 91 | 92 | Register 93 | 94 | 95 | Cancel 96 | 97 | 98 | 99 | 100 | 101 | ) 102 | } 103 | 104 | 105 | const styles = StyleSheet.create({ 106 | container: { 107 | flex: 1, 108 | justifyContent: "center", 109 | alignItems: 'center' 110 | }, 111 | image: { 112 | flex: 1, 113 | width: "100%", 114 | height: "100%", 115 | justifyContent: "center", 116 | alignItems: 'center' 117 | }, 118 | title: { 119 | fontSize: 32, 120 | marginBottom: 8, 121 | fontWeight: "bold", 122 | color: "#f04a5b" 123 | }, 124 | form: { 125 | alignSelf: "stretch", 126 | paddingHorizontal: 30, 127 | marginTop: 30 128 | }, 129 | label: { 130 | fontSize: 16, 131 | color: '#ffffff', 132 | fontWeight: "bold", 133 | marginBottom: 8 134 | }, 135 | input: { 136 | borderWidth: 1, 137 | borderColor: '#ffffff', 138 | paddingHorizontal: 10, 139 | fontSize: 16, 140 | color: '#ffffff', 141 | fontWeight: "400", 142 | height: 44, 143 | marginBottom: 30, 144 | borderRadius: 4, 145 | backgroundColor: 'rgba(0,0,0,0.4)' 146 | }, 147 | primaryBtn: { 148 | height: 42, 149 | backgroundColor: '#007bff', 150 | justifyContent: 'center', 151 | alignItems: 'center', 152 | borderRadius: 4, 153 | marginTop: 20 154 | }, 155 | secondaryBtn: { 156 | height: 42, 157 | backgroundColor: '#f04a5b', 158 | justifyContent: 'center', 159 | alignItems: 'center', 160 | borderRadius: 4, 161 | marginTop: 20 162 | } 163 | 164 | 165 | }) 166 | 167 | 168 | export default Register; -------------------------------------------------------------------------------- /episode-3-add-create-event-page/mobile-app/components/ModalComponent.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { View, Text, StyleSheet, ImageBackground, TouchableOpacity, TextInput, Modal, Dimensions, Image } from 'react-native'; 3 | import { Picker } from '@react-native-picker/picker' 4 | 5 | const widowWidth = Dimensions.get('window').width; 6 | const windowHeight = Dimensions.get('window').height; 7 | 8 | import * as ImagePicker from 'expo-image-picker'; 9 | 10 | 11 | const ModalComponent = ({ isVisible, setIsVisible, user }) => { 12 | const [eventTitle, setEventTitle] = useState(null) 13 | const [eventDescription, setEventDescription] = useState(null) 14 | const [eventPrice, setEventPrice] = useState(null) 15 | const [eventSport, setEventSport] = useState('Running') 16 | const [image, setImage] = useState(null) 17 | 18 | 19 | const pickImage = async () => { 20 | const result = await ImagePicker.launchImageLibraryAsync({ 21 | mediaTypes: ImagePicker.MediaTypeOptions.All, 22 | allowsEditing: true, 23 | aspect: [4, 3], 24 | quality: 1, 25 | }); 26 | 27 | console.log(result); 28 | 29 | if (!result.cancelled) { 30 | setImage(result.uri); 31 | } 32 | }; 33 | 34 | return ( 35 | { 40 | Alert.alert("Modal has been closed."); 41 | setIsVisible(!setIsVisible); 42 | }} 43 | > 44 | 45 | 46 | Create a New Event 47 | 48 | 49 | 53 | Pick an Image! 54 | 55 | {image && } 56 | 57 | 58 | Event Title: 59 | console.log(text)} /> 60 | Event Description: 61 | console.log(text)} /> 62 | Event Price: 63 | console.log(text)} /> 64 | 65 | 66 | Sport: {eventSport} 67 | setEventSport(value)}> 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | setIsVisible(!isVisible)} 80 | > 81 | Hide Modal 82 | 83 | 84 | 85 | 86 | ) 87 | 88 | }; 89 | 90 | 91 | const styles = StyleSheet.create({ 92 | centeredView: { 93 | flex: 1, 94 | justifyContent: "center", 95 | alignItems: "center", 96 | marginTop: 22 97 | }, 98 | modalView: { 99 | margin: 20, 100 | height: windowHeight, 101 | width: widowWidth, 102 | backgroundColor: "white", 103 | borderRadius: 20, 104 | padding: 35, 105 | alignItems: "center", 106 | shadowColor: "#000", 107 | shadowOffset: { 108 | width: 0, 109 | height: 2 110 | }, 111 | shadowOpacity: 0.25, 112 | shadowRadius: 4, 113 | elevation: 5 114 | }, 115 | button: { 116 | borderRadius: 20, 117 | padding: 10, 118 | elevation: 2 119 | }, 120 | buttonOpen: { 121 | backgroundColor: "#F194FF", 122 | }, 123 | buttonClose: { 124 | backgroundColor: "#2196F3", 125 | }, 126 | textStyle: { 127 | color: "white", 128 | fontWeight: "bold", 129 | textAlign: "center" 130 | }, 131 | modalText: { 132 | marginBottom: 15, 133 | textAlign: "center" 134 | }, 135 | form: { 136 | alignSelf: "stretch", 137 | paddingHorizontal: 30, 138 | marginTop: 30 139 | }, 140 | label: { 141 | fontSize: 16, 142 | color: 'black', 143 | fontWeight: "bold", 144 | marginBottom: 8 145 | }, 146 | input: { 147 | borderWidth: 1, 148 | borderColor: '#007bff', 149 | paddingHorizontal: 10, 150 | fontSize: 16, 151 | color: '#000000', 152 | fontWeight: "400", 153 | height: 44, 154 | shadowColor: '#000000', 155 | marginBottom: 30, 156 | borderRadius: 4 157 | }, 158 | primaryBtn: { 159 | height: 42, 160 | backgroundColor: '#007bff', 161 | justifyContent: 'center', 162 | alignItems: 'center', 163 | borderRadius: 4, 164 | marginTop: 20 165 | }, 166 | secondaryBtn: { 167 | height: 42, 168 | backgroundColor: '#f04a5b', 169 | justifyContent: 'center', 170 | alignItems: 'center', 171 | borderRadius: 4, 172 | marginTop: 20 173 | } 174 | }); 175 | 176 | export default ModalComponent; -------------------------------------------------------------------------------- /episode-5-dashboard-filter/mobile-app/pages/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { View, Text, TouchableOpacity, StyleSheet, ImageBackground, SafeAreaView, FlatList, Image } from 'react-native' 3 | import isLoggedIn from '../hooks/isLoggedIn'; 4 | import AsyncStorage from '@react-native-async-storage/async-storage'; 5 | import ActionButton from 'react-native-action-button'; 6 | import ModalComponent from '../components/ModalComponent' 7 | 8 | const bgImage = require('../assets/background.jpg'); 9 | import { FontAwesome5, MaterialCommunityIcons, Ionicons, MaterialIcons } from '@expo/vector-icons'; 10 | 11 | const DashBoard = ({ navigation }) => { 12 | const [user, user_id] = isLoggedIn({ navigation }); 13 | const [modalIsVisible, setModalIsVisible] = useState(false); 14 | const [events, setEvents] = useState([]) 15 | 16 | useEffect(() => { 17 | loadEvents('') 18 | }, [user]) 19 | 20 | const loadEvents = async (query = '') => { 21 | const response = await fetch(`http://localhost:8080/api/dashboard/${query}`, { 22 | method: 'GET', 23 | headers: { user: user } 24 | }) 25 | 26 | const jsonResponse = await response.json() 27 | setEvents(jsonResponse.events) 28 | } 29 | 30 | const logoutHandler = async () => { 31 | await AsyncStorage.removeItem('user'); 32 | await AsyncStorage.removeItem('user_id'); 33 | navigation.navigate('Login') 34 | } 35 | 36 | const deleteEventHandler = async (eventId) => { 37 | await fetch(`http://localhost:8080/api/event/${eventId}`, { 38 | method: 'DELETE', 39 | headers: { user: user } 40 | }) 41 | 42 | loadEvents() 43 | } 44 | 45 | return ( 46 | 47 | 48 | 49 | DashBoard 50 | event._id} 55 | renderItem={({ item }) => { 56 | return ( 57 | 61 | Title: {item.title} 62 | Sport: {item.sport} 63 | Price: {'$' + item.price} 64 | Description: {item.description} 65 | { item.user === user_id ? deleteEventHandler(item._id)}> 66 | 67 | : null} 68 | console.log('Register')}> 69 | Register 70 | 71 | ) 72 | }}> 73 | 74 | 75 | 76 | 77 | Logout 78 | 79 | 80 | 81 | setModalIsVisible(true)}> 82 | 83 | 84 | loadEvents()}> 85 | 86 | 87 | loadEvents('Running')}> 88 | 89 | 90 | loadEvents('Cycling')}> 91 | 92 | 93 | loadEvents('Swimming')}> 94 | 95 | 96 | 97 | 98 | 99 | ) 100 | } 101 | 102 | 103 | const styles = StyleSheet.create({ 104 | container: { 105 | flex: 1, 106 | }, 107 | image: { 108 | flex: 1, 109 | width: "100%", 110 | height: "100%", 111 | justifyContent: "center", 112 | alignItems: 'center' 113 | }, 114 | list: { 115 | width: "100%", 116 | paddingHorizontal: 20 117 | }, 118 | listItem: { 119 | padding: 8, 120 | backgroundColor: "#FFFF", 121 | marginVertical: 8, 122 | opacity: 0.9 123 | }, 124 | thumbnail: { 125 | width: 'auto', 126 | height: 150, 127 | marginBottom: 8 128 | }, 129 | eventTitle: { 130 | fontSize: 20, 131 | marginBottom: 8, 132 | fontWeight: "bold", 133 | color: "#444", 134 | marginBottom: 15, 135 | }, 136 | sport: { 137 | fontSize: 16, 138 | color: "#444", 139 | }, 140 | price: { 141 | fontSize: 16, 142 | color: '#999', 143 | marginTop: 5, 144 | fontWeight: 'bold' 145 | }, 146 | description: { 147 | fontSize: 16, 148 | color: "#444", 149 | }, 150 | boldText: { 151 | fontSize: 13, 152 | fontWeight: 'bold', 153 | color: "#444" 154 | }, 155 | primaryBtn: { 156 | height: 42, 157 | backgroundColor: '#007bff', 158 | justifyContent: 'center', 159 | alignItems: 'center', 160 | borderRadius: 4, 161 | marginTop: 20 162 | }, 163 | actionButton: { 164 | fontSize: 20, 165 | height: 22, 166 | color: 'white' 167 | }, 168 | deleteBtn: { 169 | position: 'relative', 170 | backgroundColor: '#ffff', 171 | width: 36, 172 | left: '92%', 173 | bottom: '73%' 174 | } 175 | }) 176 | 177 | 178 | export default DashBoard; -------------------------------------------------------------------------------- /episode-4-fetch-created-events/mobile-app/components/ModalComponent.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { View, Text, StyleSheet, TouchableOpacity, TextInput, Modal, Dimensions, Image } from 'react-native'; 3 | 4 | import { MaterialIcons } from '@expo/vector-icons'; 5 | 6 | const widowWidth = Dimensions.get('window').width; 7 | const windowHeight = Dimensions.get('window').height; 8 | 9 | import * as ImagePicker from 'expo-image-picker'; 10 | import DateTimePicker from '@react-native-community/datetimepicker'; 11 | import { Picker } from '@react-native-picker/picker' 12 | 13 | 14 | const ModalComponent = ({ isVisible, setIsVisible, user, loadEvents }) => { 15 | const [eventTitle, setEventTitle] = useState(null) 16 | const [eventDescription, setEventDescription] = useState(null) 17 | const [eventPrice, setEventPrice] = useState(null) 18 | const [eventSport, setEventSport] = useState('Running') 19 | const [eventDate, setEventDate] = useState(new Date()) 20 | const [image, setImage] = useState(null) 21 | 22 | const pickImage = async () => { 23 | const result = await ImagePicker.launchImageLibraryAsync({ 24 | mediaTypes: ImagePicker.MediaTypeOptions.All, 25 | allowsEditing: true, 26 | aspect: [4, 3], 27 | quality: 1, 28 | }); 29 | 30 | console.log(result); 31 | 32 | if (!result.cancelled) { 33 | setImage(result.uri); 34 | } 35 | }; 36 | 37 | const submitEventHandler = async () => { 38 | const localUri = image; 39 | const filename = localUri.split('/').pop() 40 | const match = /\.(\w+)$/.exec(filename) 41 | const type = match ? `image/${match[1]}` : `image`; 42 | 43 | const data = new FormData() 44 | 45 | data.append('thumbnail', { uri: localUri, name: filename, type }) 46 | data.append('title', eventTitle) 47 | data.append('description', eventDescription) 48 | data.append('price', eventPrice) 49 | data.append('date', eventDate) 50 | data.append('sport', eventSport) 51 | 52 | try { 53 | await fetch(`http://localhost:8080/api/event`, { 54 | method: 'POST', 55 | body: data, 56 | headers: { user: user } 57 | }) 58 | loadEvents() 59 | cancelEventHandler() 60 | } catch (error) { 61 | console.log('🚀 -------------------------------------------------------------------------') 62 | console.log('🚀 ~ file: ModalComponent.js ~ line 60 ~ submitEventHandler ~ error', error) 63 | console.log('🚀 -------------------------------------------------------------------------') 64 | 65 | } 66 | } 67 | 68 | const onChange = (event, selectedDate) => { 69 | const currentDate = selectedDate || eventDate; 70 | setEventDate(currentDate); 71 | }; 72 | 73 | const cancelEventHandler = () => { 74 | setIsVisible(!isVisible) 75 | setImage(null) 76 | setEventTitle(null) 77 | setEventDescription(null) 78 | setEventDate(new Date()) 79 | setEventSport('Running') 80 | } 81 | return ( 82 | { 87 | Alert.alert("Modal has been closed."); 88 | setIsVisible(!setIsVisible); 89 | }} 90 | > 91 | 92 | 93 | 94 | 95 | 96 | {image ? 97 | 98 | : 99 | 103 | 104 | } 105 | 106 | 107 | Event Title: 108 | setEventTitle(text)} /> 109 | Event Description: 110 | setEventDescription(text)} /> 111 | Event Price: 112 | setEventPrice(text)} /> 113 | 114 | 115 | Event Date: 116 | 121 | 122 | 123 | Sport: {eventSport} 124 | setEventSport(value)}> 125 | 126 | 127 | 128 | 129 | 130 | 131 | 135 | Submit Event 136 | 137 | 141 | Cancel 142 | 143 | 144 | 145 | 146 | ) 147 | 148 | }; 149 | 150 | 151 | const styles = StyleSheet.create({ 152 | centeredView: { 153 | flex: 1, 154 | justifyContent: "center", 155 | alignItems: "center" 156 | }, 157 | modalView: { 158 | margin: 20, 159 | height: windowHeight, 160 | width: widowWidth, 161 | backgroundColor: "white", 162 | borderRadius: 20, 163 | padding: 25, 164 | alignItems: "center", 165 | shadowColor: "#000", 166 | shadowOffset: { 167 | width: 0, 168 | height: 2 169 | }, 170 | shadowOpacity: 0.25, 171 | shadowRadius: 4, 172 | elevation: 5 173 | }, 174 | textStyle: { 175 | color: "white", 176 | fontWeight: "bold", 177 | textAlign: "center" 178 | }, 179 | form: { 180 | alignSelf: "stretch", 181 | marginTop: 16 182 | }, 183 | label: { 184 | fontSize: 16, 185 | color: 'black', 186 | fontWeight: "bold", 187 | marginBottom: 8 188 | }, 189 | input: { 190 | borderWidth: 1, 191 | borderColor: '#007bff', 192 | paddingHorizontal: 10, 193 | fontSize: 16, 194 | color: '#000000', 195 | fontWeight: "400", 196 | height: 44, 197 | shadowColor: '#000000', 198 | marginBottom: 15, 199 | borderRadius: 4 200 | }, 201 | primaryBtn: { 202 | height: 42, 203 | width: '100%', 204 | backgroundColor: '#007bff', 205 | justifyContent: 'center', 206 | alignItems: 'center', 207 | borderRadius: 4, 208 | marginTop: 10 209 | }, 210 | secondaryBtn: { 211 | height: 42, 212 | width: '100%', 213 | backgroundColor: '#f04a5b', 214 | justifyContent: 'center', 215 | alignItems: 'center', 216 | borderRadius: 4, 217 | marginTop: 10 218 | }, 219 | addImage: { 220 | justifyContent: 'center', 221 | alignItems: 'center', 222 | marginBottom: 28, 223 | width: '100%', 224 | height: 150 225 | }, 226 | loadedImage: { 227 | width: '100%', 228 | height: 150, 229 | justifyContent: 'center', 230 | alignItems: 'center', 231 | }, 232 | sportPicker: { 233 | 234 | } 235 | }); 236 | 237 | export default ModalComponent; -------------------------------------------------------------------------------- /episode-5-dashboard-filter/mobile-app/components/ModalComponent.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { View, Text, StyleSheet, TouchableOpacity, TextInput, Modal, Dimensions, Image } from 'react-native'; 3 | 4 | import { MaterialIcons } from '@expo/vector-icons'; 5 | 6 | const widowWidth = Dimensions.get('window').width; 7 | const windowHeight = Dimensions.get('window').height; 8 | 9 | import * as ImagePicker from 'expo-image-picker'; 10 | import DateTimePicker from '@react-native-community/datetimepicker'; 11 | import { Picker } from '@react-native-picker/picker' 12 | 13 | 14 | const ModalComponent = ({ isVisible, setIsVisible, user, loadEvents }) => { 15 | const [eventTitle, setEventTitle] = useState(null) 16 | const [eventDescription, setEventDescription] = useState(null) 17 | const [eventPrice, setEventPrice] = useState(null) 18 | const [eventSport, setEventSport] = useState('Running') 19 | const [eventDate, setEventDate] = useState(new Date()) 20 | const [image, setImage] = useState(null) 21 | 22 | const pickImage = async () => { 23 | const result = await ImagePicker.launchImageLibraryAsync({ 24 | mediaTypes: ImagePicker.MediaTypeOptions.All, 25 | allowsEditing: true, 26 | aspect: [4, 3], 27 | quality: 1, 28 | }); 29 | 30 | console.log(result); 31 | 32 | if (!result.cancelled) { 33 | setImage(result.uri); 34 | } 35 | }; 36 | 37 | const submitEventHandler = async () => { 38 | const localUri = image; 39 | const filename = localUri.split('/').pop() 40 | const match = /\.(\w+)$/.exec(filename) 41 | const type = match ? `image/${match[1]}` : `image`; 42 | 43 | const data = new FormData() 44 | 45 | data.append('thumbnail', { uri: localUri, name: filename, type }) 46 | data.append('title', eventTitle) 47 | data.append('description', eventDescription) 48 | data.append('price', eventPrice) 49 | data.append('date', eventDate) 50 | data.append('sport', eventSport) 51 | 52 | try { 53 | await fetch(`http://localhost:8080/api/event`, { 54 | method: 'POST', 55 | body: data, 56 | headers: { user: user } 57 | }) 58 | loadEvents() 59 | cancelEventHandler() 60 | } catch (error) { 61 | console.log('🚀 -------------------------------------------------------------------------') 62 | console.log('🚀 ~ file: ModalComponent.js ~ line 60 ~ submitEventHandler ~ error', error) 63 | console.log('🚀 -------------------------------------------------------------------------') 64 | 65 | } 66 | } 67 | 68 | const onChange = (event, selectedDate) => { 69 | const currentDate = selectedDate || eventDate; 70 | setEventDate(currentDate); 71 | }; 72 | 73 | const cancelEventHandler = () => { 74 | setIsVisible(!isVisible) 75 | setImage(null) 76 | setEventTitle(null) 77 | setEventDescription(null) 78 | setEventDate(new Date()) 79 | setEventSport('Running') 80 | } 81 | return ( 82 | { 87 | Alert.alert("Modal has been closed."); 88 | setIsVisible(!setIsVisible); 89 | }} 90 | > 91 | 92 | 93 | 94 | 95 | 96 | {image ? 97 | 98 | : 99 | 103 | 104 | } 105 | 106 | 107 | Event Title: 108 | setEventTitle(text)} /> 109 | Event Description: 110 | setEventDescription(text)} /> 111 | Event Price: 112 | setEventPrice(text)} /> 113 | 114 | 115 | Event Date: 116 | 121 | 122 | 123 | Sport: {eventSport} 124 | setEventSport(value)}> 125 | 126 | 127 | 128 | 129 | 130 | 131 | 135 | Submit Event 136 | 137 | 141 | Cancel 142 | 143 | 144 | 145 | 146 | ) 147 | 148 | }; 149 | 150 | 151 | const styles = StyleSheet.create({ 152 | centeredView: { 153 | flex: 1, 154 | justifyContent: "center", 155 | alignItems: "center" 156 | }, 157 | modalView: { 158 | margin: 20, 159 | height: windowHeight, 160 | width: widowWidth, 161 | backgroundColor: "white", 162 | borderRadius: 20, 163 | padding: 25, 164 | alignItems: "center", 165 | shadowColor: "#000", 166 | shadowOffset: { 167 | width: 0, 168 | height: 2 169 | }, 170 | shadowOpacity: 0.25, 171 | shadowRadius: 4, 172 | elevation: 5 173 | }, 174 | textStyle: { 175 | color: "white", 176 | fontWeight: "bold", 177 | textAlign: "center" 178 | }, 179 | form: { 180 | alignSelf: "stretch", 181 | marginTop: 16 182 | }, 183 | label: { 184 | fontSize: 16, 185 | color: 'black', 186 | fontWeight: "bold", 187 | marginBottom: 8 188 | }, 189 | input: { 190 | borderWidth: 1, 191 | borderColor: '#007bff', 192 | paddingHorizontal: 10, 193 | fontSize: 16, 194 | color: '#000000', 195 | fontWeight: "400", 196 | height: 44, 197 | shadowColor: '#000000', 198 | marginBottom: 15, 199 | borderRadius: 4 200 | }, 201 | primaryBtn: { 202 | height: 42, 203 | width: '100%', 204 | backgroundColor: '#007bff', 205 | justifyContent: 'center', 206 | alignItems: 'center', 207 | borderRadius: 4, 208 | marginTop: 10 209 | }, 210 | secondaryBtn: { 211 | height: 42, 212 | width: '100%', 213 | backgroundColor: '#f04a5b', 214 | justifyContent: 'center', 215 | alignItems: 'center', 216 | borderRadius: 4, 217 | marginTop: 10 218 | }, 219 | addImage: { 220 | justifyContent: 'center', 221 | alignItems: 'center', 222 | marginBottom: 28, 223 | width: '100%', 224 | height: 150 225 | }, 226 | loadedImage: { 227 | width: '100%', 228 | height: 150, 229 | justifyContent: 'center', 230 | alignItems: 'center', 231 | }, 232 | sportPicker: { 233 | 234 | } 235 | }); 236 | 237 | export default ModalComponent; --------------------------------------------------------------------------------