├── 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;
--------------------------------------------------------------------------------