From 12eaeadf12d4b61b9843fa65679ffae41bc78239 Mon Sep 17 00:00:00 2001 From: L4168 <L4168@student.jamk.fi> Date: Sun, 2 Jun 2019 09:37:45 +0300 Subject: [PATCH] Added login-demo with Dockerfile --- .dockerignore | 2 + Dockerfile | 14 ++++ package-lock.json | 66 ++++++++++++++++ package.json | 4 +- src/App.css | 137 ++++++++++++++++++++++++++++++++- src/App.js | 68 +++------------- src/LoginForm.js | 100 ------------------------ src/components/Header.js | 100 ++++++++++++++++++++++++ src/components/LoginForm.js | 117 ++++++++++++++++++++++++++++ src/components/RegisterForm.js | 133 ++++++++++++++++++++++++++++++++ src/components/UserMap.js | 34 ++++---- src/index.css | 6 +- src/index.js | 8 +- 13 files changed, 603 insertions(+), 186 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile delete mode 100644 src/LoginForm.js create mode 100644 src/components/Header.js create mode 100644 src/components/LoginForm.js create mode 100644 src/components/RegisterForm.js diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..5171c54 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +node_modules +npm-debug.log \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3ef2135 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +FROM node:10.15.3-alpine as build +WORKDIR /usr/src/app +ENV PATH /usr/src/app/node_modules/.bin:$PATH +COPY package*.json ./ +RUN npm install +RUN npm install react-scripts@3.0.1 -g +COPY . . +RUN npm run build + +# production environment +FROM nginx:1.16.0-alpine +COPY --from=build /usr/src/app/build /usr/share/nginx/html +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] diff --git a/package-lock.json b/package-lock.json index e4b02e8..6139953 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1310,6 +1310,20 @@ "@babel/types": "^7.3.0" } }, + "@types/cookie": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz", + "integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow==" + }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "@types/istanbul-lib-coverage": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", @@ -1337,11 +1351,30 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.3.tgz", "integrity": "sha512-zkOxCS/fA+3SsdA+9Yun0iANxzhQRiNwTvJSr6N95JhuJ/x27z9G2URx1Jpt3zYFfCGUXZGL5UDxt5eyLE7wgw==" }, + "@types/object-assign": { + "version": "4.0.30", + "resolved": "https://registry.npmjs.org/@types/object-assign/-/object-assign-4.0.30.tgz", + "integrity": "sha1-iUk3HVqZ9Dge4PHfCpt6GH4H5lI=" + }, + "@types/prop-types": { + "version": "15.7.1", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.1.tgz", + "integrity": "sha512-CFzn9idOEpHrgdw8JsoTkaDDyRWk1jrzIV8djzcgpq0y9tG4B4lFT+Nxh52DVpDXV+n4+NPNv7M1Dj5uMp6XFg==" + }, "@types/q": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz", "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==" }, + "@types/react": { + "version": "16.8.19", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.19.tgz", + "integrity": "sha512-QzEzjrd1zFzY9cDlbIiFvdr+YUmefuuRYrPxmkwG0UQv5XF35gFIi7a95m1bNVcFU0VimxSZ5QVGSiBmlggQXQ==", + "requires": { + "@types/prop-types": "*", + "csstype": "^2.2.0" + } + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -3881,6 +3914,11 @@ "cssom": "0.3.x" } }, + "csstype": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.5.tgz", + "integrity": "sha512-JsTaiksRsel5n7XwqPAfB0l3TFKdpjW/kgAELf9vrb5adGA7UCPLajKK5s3nFrcFm3Rkyp/Qkgl73ENc1UY3cA==" + }, "cyclist": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", @@ -9994,6 +10032,16 @@ "whatwg-fetch": "3.0.0" } }, + "react-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-4.0.0.tgz", + "integrity": "sha512-om/HB4MBHt4k+moR8Mb5h1kmKxcmOxK2U6aaQZ8Y+f+igICcE5bpng7yCiAo3kKN0btFpzvQ70XnpONOC0xkdA==", + "requires": { + "@types/hoist-non-react-statics": "^3.0.1", + "hoist-non-react-statics": "^3.0.0", + "universal-cookie": "^4.0.0" + } + }, "react-dev-utils": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-9.0.1.tgz", @@ -11881,6 +11929,24 @@ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz", "integrity": "sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ==" }, + "universal-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-4.0.0.tgz", + "integrity": "sha512-6JVx+3oGPjslGqFhQ8YSIBHmYTx8HbyAEH++2/b6SKNXsbsdQ7lU7wRG2bYcRB5JVCz8GYgQ+Ixew91hn3Dy9w==", + "requires": { + "@types/cookie": "^0.3.1", + "@types/object-assign": "^4.0.30", + "cookie": "^0.3.1", + "object-assign": "^4.1.0" + }, + "dependencies": { + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + } + } + }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", diff --git a/package.json b/package.json index ec811f0..fd4f1ef 100644 --- a/package.json +++ b/package.json @@ -5,9 +5,11 @@ "dependencies": { "leaflet": "^1.5.1", "react": "^16.8.6", + "react-cookie": "^4.0.0", "react-dom": "^16.8.6", "react-leaflet": "^2.3.0", - "react-scripts": "3.0.1" + "react-scripts": "3.0.1", + "universal-cookie": "^4.0.0" }, "scripts": { "start": "react-scripts start", diff --git a/src/App.css b/src/App.css index eda5bc7..8541116 100644 --- a/src/App.css +++ b/src/App.css @@ -1,4 +1,139 @@ +body { + margin: 0; + padding: 0; +} + +.hidden { + display: none; +} + +/* UserMap */ .map { + position: absolute; + margin-top: 50px; + height: 95vh; + width: 100vw; +} + +/* Header */ +div.header { + position: absolute; + top: 0; + height: 50px; + width: 100%; + background: white; + z-index: 1000; +} + +div.header button { + transition-property: background-color; + transition-duration: 0.5s; + transition-timing-function: ease; + background-color: #279fd9; + color: white; + font-weight: bold; + border: 0px; + padding: 0.5em; + margin: 0em 1em; + width: 10%; + height: 100%; + font-size: 18px; + float: right; +} + +div.header button:hover { + background-color: darkblue; + cursor: pointer; +} + +/* Login&RegisterForm */ +div.fade-main { + position: absolute; + top: 0; + z-index: 1000; height: 100vh; width: 100vw; -} \ No newline at end of file + margin: auto; + text-align: center; + background-color: rgba(0, 0, 0, 0.85); +} + +div.sticky { + position: fixed; + top: 0px; + right: 0px; + height: 100px; + width: 100px; + margin-right: 150px; +} + +.close { + position: fixed; + color: #f1f1f1; + height: 100px; + font-size: 100px; + font-weight: bold; + transition: transform 0.4s ease-in-out; + line-height: 70%; +} + +.close:hover, +.close:focus { + color: #bbb; + text-decoration: none; + cursor: pointer; + transform: rotateZ(90deg); +} + +div.login { + height: 60vh; + width: 40vw; + margin: auto; + margin-top: 5%; + padding-top: 25px; +} + +div.login h1 { + color: rgb(0, 200, 255); + margin-bottom: 0.5em; + font-size: 3em; +} + +div.login h2 { + color: rgb(255, 0, 0); + margin-top: 2em; + font-size: 1.5em; +} + +div.login input { + background: none; + border: none; + outline: none; + width: 60%; + padding: 12px 20px; + margin-bottom: 1em; + display: inline-block; + box-sizing: border-box; + font-size: 180%; + color: white; + border-bottom: 3px solid rgb(0, 200, 255); +} + +div.login button { + transition-property: background-color; + transition-duration: 0.5s; + transition-timing-function: ease; + background-color: #279fd9; + color: white; + font-weight: bold; + border: 0px; + padding: 0.5em; + margin: 0em 2em; + width: 20%; + font-size: 20px; +} + +div.login button:hover { + background-color: darkblue; + cursor: pointer; +} diff --git a/src/App.js b/src/App.js index 02a6ba3..4ee8619 100644 --- a/src/App.js +++ b/src/App.js @@ -1,9 +1,8 @@ import React, { Component } from 'react'; -import styles from './App.css'; - -import UserMap from './components/UserMap.js' -import {LoginForm} from './LoginForm'; +import styles from './App.css'; +import UserMap from './components/UserMap'; +import Header from './components/Header'; class App extends Component { constructor() { @@ -12,68 +11,19 @@ class App extends Component { this.state = { lat: 62.2416479, lng: 25.7597186, - zoom: 13, - logged: false, - name: '' - } - - this.login = this.login.bind(this); - } - - componentDidMount(){ - // log in if cookie with username is found - let name = getCookie('username'); - if(name !== ''){ - this.login({ - logged: true, - name: name - }); - } + zoom: 13 + }; } - login(loginInfo){ - if(loginInfo.logged !== this.state.logged){ - this.setState({ - logged: loginInfo.logged, - name: loginInfo.name - }); - document.cookie = 'username='+loginInfo.name+';path=/;'; - } - else{ - console.log('Wrong info'); - } - } - - render(){ - const initialPosition = [this.state.lat, this.state.lng]; + render() { + const initialPosition = [this.state.lat, this.state.lng]; return ( <div> - <p>{this.state.logged ? 'Logged in: ' + this.state.name : ''}</p> - {!this.state.logged && <LoginForm onSubmit={this.login}/>} - {this.state.logged && <button onClick={() => this.login({ - logged: false, - name: '' - })}>Logout</button>} - <UserMap position={initialPosition} zoom={this.state.zoom}/> + <UserMap position={initialPosition} zoom={this.state.zoom} />, + <Header /> </div> ); } } -function getCookie(cname){ - let name = cname + "="; - let decodedCookie = decodeURIComponent(document.cookie); - let ca = decodedCookie.split(";"); - for (let i = 0; i < ca.length; i++) { - let c = ca[i]; - while (c.charAt(0) === " ") { - c = c.substring(1); - } - if(c.indexOf(name) === 0){ - return c.substring(name.length, c.length); - } - } - return ""; -} - export default App; diff --git a/src/LoginForm.js b/src/LoginForm.js deleted file mode 100644 index 05962f7..0000000 --- a/src/LoginForm.js +++ /dev/null @@ -1,100 +0,0 @@ -import React from "react"; - -//TODO: remove this when not needed -const user = { - name: "user", - password: "password" -} - -const style = { - backgroundColor: 'red', - maxHeight: '100%', - maxWidth: '350px' -} - -export class LoginForm extends React.Component{ - constructor(props){ - super(props); - this.handleLogin = this.handleLogin.bind(this); - } - - handleLogin(e){ - const name = e.target.elements.name.value; - const password = e.target.elements.password.value; - - e.preventDefault(); - - // Don't send user info to the server if used hardcoded info - if(name === user.name && password === user.password){ - this.props.onSubmit({ - logged: true, - name: name - }); - }else{ - this.props.onSubmit({ - logged: false, - name: "" - }); - - - // This did NOT work !!!! - - // (async() =>{ - // const asd = await fetch('http://172.20.2.143:3000/user/login',{ - // method: 'POST', - // headers: { - // 'Accept': 'application/json', - // 'Content-Type': 'application/json', - // }, - // credentials: 'same-origin', - // body: JSON.stringify({ - // name: name, - // password: password - // }) - // }); - // const content = await asd; - // console.log(content); - // })(); - - // This worked :) - // Send login info to the server - fetch("http://172.20.2.143:3000/user/login",{ - method: 'POST', - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - name: name, - password: password - }) - }) - .then(res => res.json()) - .then((result) => { - console.log(result); - }, - // Note: it's important to handle errors here - // instead of a catch() block so that we don't swallow - // exceptions from actual bugs in components. - (error) => { - console.log(error); - } - ) - } - } - - render(){ - return( - <form onSubmit={this.handleLogin} style={style}> - <label>LOGIN</label> - <br></br> - <label>Name:</label> - <input name="name"/> - <br></br> - <label>Password:</label> - <input type="password" name="password"/> - <button type="submit">Submit</button> - </form> - ); - } -} \ No newline at end of file diff --git a/src/components/Header.js b/src/components/Header.js new file mode 100644 index 0000000..4406f82 --- /dev/null +++ b/src/components/Header.js @@ -0,0 +1,100 @@ +import React from 'react'; + +import LoginForm from './LoginForm'; +import RegisterForm from './RegisterForm'; + +class Header extends React.Component { + state = { + login: false, + register: false, + username: null, + token: null + }; + + // toggles the login/register view + toggleView = view => { + this.setState(prevState => { + return { + [view]: view === 'login' ? !prevState.login : !prevState.register + }; + }); + }; + + handleState = data => { + sessionStorage.setItem('name', data.name); + sessionStorage.setItem('token', data.token); + this.setState({ username: data.name, token: data.token }); + }; + + handleLogout = () => { + this.setState({ username: null, token: null }); + sessionStorage.removeItem('token'); + }; + + // verifies the token (if it exists) on element mount with backend server + componentDidMount() { + let token = sessionStorage.getItem('token'); + if (token) { + fetch('http://localhost:5000/user/verify', { + headers: { + Authorization: 'Bearer ' + token + } + }) + .then(res => res.json()) + .then( + result => { + // if token is still valid, login user + if (result === true) { + this.setState({ + username: sessionStorage.getItem('name'), + token: token + }); + // logout user if token has expired / is invalid + } else { + this.handleLogout(); + } + }, + error => { + console.log(error); + } + ); + } + } + + render() { + return ( + <div> + <div className='header'> + {!this.state.username && ( + <button onClick={() => this.toggleView('register')}> + register + </button> + )} + {!this.state.username && ( + <button onClick={() => this.toggleView('login')}>login</button> + )} + {this.state.username && ( + <button onClick={this.handleLogout}>logout</button> + )} + {this.state.username && <button>{this.state.username}</button>} + </div> + {this.state.register && ( + <RegisterForm + view='register' + handleState={this.handleState} + toggleView={this.toggleView} + /> + )} + {this.state.login && ( + <LoginForm + view='login' + handleState={this.handleState} + toggleView={this.toggleView} + /> + )} + </div> + ); + } +} + +export default Header; diff --git a/src/components/LoginForm.js b/src/components/LoginForm.js new file mode 100644 index 0000000..7893bb8 --- /dev/null +++ b/src/components/LoginForm.js @@ -0,0 +1,117 @@ +import React from 'react'; + +export class LoginForm extends React.Component { + constructor(props) { + super(props); + + this.state = { + errorMsg: '', + username: '', + password: '' + }; + } + + handleError = error => { + this.setState({ errorMsg: error }); + }; + + handleChange = e => { + const { name, value } = e.target; + this.setState({ [name]: value }); + }; + + // show/hide this form + handleView = e => { + this.props.toggleView(this.props.view); + }; + + // remove login view with ESC + handleEsc = e => { + if (e.keyCode === 27) { + this.handleView(); + } + }; + + handleLogin = e => { + const name = this.state.username; + const password = this.state.password; + e.preventDefault(); + + // Send login info to the server + fetch('http://localhost:5000/user/login', { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + name: name, + password: password + }) + }) + .then(res => res.json()) + .then( + result => { + if (result.name) { + this.props.handleState(result); + this.handleView(); + } else { + this.handleError(result.errorResponse.message); + } + }, + // Note: it's important to handle errors here + // instead of a catch() block so that we don't swallow + // exceptions from actual bugs in components. + error => { + console.log(error); + } + ); + }; + + componentDidMount() { + document.addEventListener('keyup', this.handleEsc); + } + + componentWillUnmount() { + document.removeEventListener('keyup', this.handleEsc); + } + + render() { + return ( + <div className='fade-main'> + <div className='sticky'> + <span className='close' onClick={this.handleView}> + × + </span> + </div> + <div className='login'> + <form onSubmit={this.handleLogin}> + <h1>demo login</h1> + <br /> + <input + placeholder='Enter Username' + name='username' + value={this.state.username} + onChange={this.handleChange} + required + /> + <br /> + <input + placeholder='Enter password' + type='password' + name='password' + value={this.state.password} + onChange={this.handleChange} + required + /> + <br /> + <button type='submit'>login</button> + <h2>{this.state.errorMsg}</h2> + </form> + </div> + </div> + ); + } +} + +export default LoginForm; diff --git a/src/components/RegisterForm.js b/src/components/RegisterForm.js new file mode 100644 index 0000000..f0ead02 --- /dev/null +++ b/src/components/RegisterForm.js @@ -0,0 +1,133 @@ +import React from 'react'; + +export class RegisterForm extends React.Component { + constructor(props) { + super(props); + + this.state = { + errorMsg: '', + username: '', + password: '', + password2: '' + }; + } + + // shows error messages associated with registering + handleError = error => { + this.setState({ errorMsg: error }); + }; + + // updates state with input values + handleChange = e => { + const { name, value } = e.target; + this.setState({ [name]: value }); + }; + + // show/hide this form + handleView = e => { + this.props.toggleView(this.props.view); + }; + + // remove register view with ESC + handleEsc = e => { + if (e.keyCode === 27) { + this.handleView(); + } + }; + + handleRegister = e => { + const name = this.state.username; + const password = this.state.password; + e.preventDefault(); + + if (this.state.password !== this.state.password2) { + this.handleError('Passwords do not match'); + } else { + // Send register info to the server + fetch('http://localhost:5000/user/register', { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + name: name, + password: password + }) + }) + .then(res => res.json()) + .then( + result => { + if (result.name) { + this.props.handleState(result); + this.handleView(); + } else { + this.handleError(result.errorResponse.message); + } + }, + // Note: it's important to handle errors here + // instead of a catch() block so that we don't swallow + // exceptions from actual bugs in components. + error => { + console.log(error); + } + ); + } + }; + + componentDidMount() { + document.addEventListener('keyup', this.handleEsc); + } + + componentWillUnmount() { + document.removeEventListener('keyup', this.handleEsc); + } + + render() { + return ( + <div className='fade-main'> + <div className='sticky'> + <span className='close' onClick={this.handleView}> + × + </span> + </div> + <div className='login'> + <form onSubmit={this.handleRegister}> + <h1>register new user</h1> + <br /> + <input + placeholder='Enter Username' + name='username' + value={this.state.username} + onChange={this.handleChange} + required + /> + <br /> + <input + placeholder='Enter password' + type='password' + name='password' + value={this.state.password} + onChange={this.handleChange} + required + /> + <br /> + <input + placeholder='Verify password' + type='password' + name='password2' + value={this.state.password2} + onChange={this.handleChange} + required + /> + <br /> + <button type='submit'>register</button> + <h2>{this.state.errorMsg}</h2> + </form> + </div> + </div> + ); + } +} + +export default RegisterForm; diff --git a/src/components/UserMap.js b/src/components/UserMap.js index 1ac5bcb..3f7e559 100644 --- a/src/components/UserMap.js +++ b/src/components/UserMap.js @@ -1,22 +1,22 @@ -import React, {Component} from 'react'; -import { Map, TileLayer, Marker, Popup } from 'react-leaflet' +import React, { Component } from 'react'; +import { Map, TileLayer, Marker, Popup } from 'react-leaflet'; -class UserMap extends Component{ - render(){ +class UserMap extends Component { + render() { return ( - <Map className="map" center={this.props.position} zoom={this.props.zoom}> - <TileLayer - attribution='Maanmittauslaitoksen kartta' - url=" https://tiles.kartat.kapsi.fi/taustakartta/{z}/{x}/{y}.jpg" - /> - <Marker position={this.props.position}> - <Popup> - Se on perjantai, my dudes <br /> - </Popup> - </Marker> - </Map>) + <Map className='map' center={this.props.position} zoom={this.props.zoom}> + <TileLayer + attribution='Maanmittauslaitoksen kartta' + url=' https://tiles.kartat.kapsi.fi/taustakartta/{z}/{x}/{y}.jpg' + /> + <Marker position={this.props.position}> + <Popup> + Se on perjantai, my dudes <br /> + </Popup> + </Marker> + </Map> + ); } } - -export default UserMap; \ No newline at end of file +export default UserMap; diff --git a/src/index.css b/src/index.css index 4a1df4d..ec2585e 100644 --- a/src/index.css +++ b/src/index.css @@ -1,13 +1,13 @@ body { margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", - "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; } diff --git a/src/index.js b/src/index.js index b22efa5..0b8e652 100644 --- a/src/index.js +++ b/src/index.js @@ -11,13 +11,11 @@ import * as serviceWorker from './serviceWorker'; // make the default marker work with react (dunno if this is a weird hack) delete L.Icon.Default.prototype._getIconUrl; L.Icon.Default.mergeOptions({ - iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'), - iconUrl: require('leaflet/dist/images/marker-icon.png'), - shadowUrl: require('leaflet/dist/images/marker-shadow.png') + iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'), + iconUrl: require('leaflet/dist/images/marker-icon.png'), + shadowUrl: require('leaflet/dist/images/marker-shadow.png') }); - - ReactDOM.render(<App />, document.getElementById('root')); // If you want your app to work offline and load faster, you can change -- GitLab