├── .gitignore
├── LICENSE
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── index.html
└── manifest.json
└── src
├── Actions
└── UserActions.js
├── Components
├── ErrorAlert.js
├── FooterFormButton.js
├── ImageField.js
├── InputField.js
├── Loading.js
└── SimpleBox.js
├── Containers
├── AuthenticatedComponent.js
├── CreateAccount.js
├── ListUsers.js
├── LoadingComponent.js
├── Login.js
└── PreviewPicture.js
├── Firebase.js
├── Helpers
└── ReduxFormValidation.js
├── Reducers
├── DbUserReducer.js
├── LoadingReducer.js
├── UserReducer.js
└── index.js
├── Styles
└── App.css
├── index.js
└── registerServiceWorker.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | logs
6 | *.log
7 | npm-debug.log*
8 | yarn-debug.log*
9 | yarn-error.log*
10 |
11 | # Runtime data
12 | pids
13 | *.pid
14 | *.seed
15 | *.pid.lock
16 |
17 | # Directory for instrumented libs generated by jscoverage/JSCover
18 | lib-cov
19 |
20 | # Coverage directory used by tools like istanbul
21 | coverage
22 |
23 | # nyc test coverage
24 | .nyc_output
25 |
26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
27 | .grunt
28 |
29 | # Bower dependency directory (https://bower.io/)
30 | bower_components
31 |
32 | # node-waf configuration
33 | .lock-wscript
34 |
35 | # Compiled binary addons (http://nodejs.org/api/addons.html)
36 | build/Release
37 |
38 | # Dependency directories
39 | node_modules/
40 | jspm_packages/
41 |
42 | # Typescript v1 declaration files
43 | typings/
44 |
45 | # Optional npm cache directory
46 | .npm
47 |
48 | # Optional eslint cache
49 | .eslintcache
50 |
51 | # Optional REPL history
52 | .node_repl_history
53 |
54 | # Output of 'npm pack'
55 | *.tgz
56 |
57 | # Yarn Integrity file
58 | .yarn-integrity
59 |
60 | # dotenv environment variables file
61 | .env
62 |
63 | # testing
64 | /coverage
65 |
66 | # production
67 | /build
68 |
69 | # misc
70 | .DS_Store
71 | .env.local
72 | .env.development.local
73 | .env.test.local
74 | .env.production.local
75 |
76 | npm-debug.log*
77 | yarn-debug.log*
78 | yarn-error.log*
79 |
80 | .idea
81 |
82 | keys/
83 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Taylor Howard
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "messageboard",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "firebase": "^4.2.0",
7 | "lodash": "^4.17.4",
8 | "react": "^15.6.1",
9 | "react-dom": "^15.6.1",
10 | "react-redux": "^5.0.5",
11 | "react-router-dom": "^4.1.2",
12 | "react-scripts": "1.0.10",
13 | "redux": "^3.7.2",
14 | "redux-form": "^7.0.3",
15 | "redux-thunk": "^2.2.0"
16 | },
17 | "scripts": {
18 | "start": "react-scripts start",
19 | "build": "react-scripts build",
20 | "test": "react-scripts test --env=jsdom",
21 | "eject": "react-scripts eject"
22 | },
23 | "devDependencies": {}
24 | }
25 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TaylorRayHoward/FirebaseUserExample/d37351417101560694f266ee8660832fe62c5a87/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
14 | Message Board
15 |
16 |
17 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Message Board",
3 | "name": "Message Board",
4 | "start_url": "./index.html",
5 | "display": "standalone",
6 | "theme_color": "#000000",
7 | "background_color": "#ffffff"
8 | }
9 |
--------------------------------------------------------------------------------
/src/Actions/UserActions.js:
--------------------------------------------------------------------------------
1 | import { auth, database, storage } from '../Firebase';
2 |
3 | export const GET_USER = 'get_user';
4 | export const GET_DB_USERS = 'get_db_users';
5 | export const USER_STATUS = 'user_status';
6 | export const USER_DB_STATUS = 'user_db_status';
7 |
8 | export function getUser() {
9 | return dispatch => {
10 | dispatch({
11 | type: USER_STATUS,
12 | payload: true
13 | });
14 | auth.onAuthStateChanged(user => {
15 | dispatch({
16 | type: GET_USER,
17 | payload: user
18 | });
19 | dispatch({
20 | type: USER_STATUS,
21 | payload: false
22 | });
23 | });
24 | };
25 | }
26 |
27 | export function getDbUsers() {
28 | return dispatch => {
29 | dispatch({
30 | type: USER_DB_STATUS,
31 | payload: true
32 | });
33 | database.ref('users').on('value', db => {
34 | dispatch({
35 | type: GET_DB_USERS,
36 | payload: db.val()
37 | });
38 | dispatch({
39 | type: USER_DB_STATUS,
40 | payload: false
41 | });
42 | });
43 | };
44 | }
45 |
46 | export function login(email, password) {
47 | return dispatch => auth.signInWithEmailAndPassword(email, password);
48 | }
49 |
50 | export function logout() {
51 | return dispatch => auth.signOut();
52 | }
53 |
54 | export function createAccount(data, picture) {
55 | const { fname, lname, email, password, image } = data;
56 | return dispatch => auth.createUserWithEmailAndPassword(email, password).then((user) => {
57 | if (user !== null) {
58 | storage.child(`profile/${picture.name}/${new Date().getTime()}`).put(image[0]).then((snapshot) => {
59 | database.ref('users').child(user.uid).set({
60 | fname,
61 | lname,
62 | picture: snapshot.metadata.downloadURLs[0]
63 | });
64 | });
65 | }
66 | });
67 | }
68 |
--------------------------------------------------------------------------------
/src/Components/ErrorAlert.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const ErrorAlert = (props) => {
4 | return (
5 |
6 | {props.children}
7 |
8 | );
9 | };
10 |
11 | export default ErrorAlert;
--------------------------------------------------------------------------------
/src/Components/FooterFormButton.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import '../Styles/App.css'
3 |
4 | const FooterButton = (props) => {
5 | const { submitLabel, otherLabel, goToLink, history } = props;
6 | return (
7 |
8 |
9 |
12 |
13 | );
14 | };
15 |
16 | export default FooterButton;
17 |
--------------------------------------------------------------------------------
/src/Components/ImageField.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PreviewPicture from '../Containers/PreviewPicture';
3 |
4 | class InputField extends Component {
5 | displayPicture(event) {
6 | let reader = new FileReader();
7 | let file = event.target.files[0];
8 | reader.onloadend = () => {
9 | console.log(file);
10 | this.setState({
11 | picture: file,
12 | pictureUrl: reader.result
13 | });
14 | };
15 | reader.readAsDataURL(file);
16 | }
17 |
18 | constructor(state) {
19 | super(state);
20 | this.state = {
21 | picture: 'Please attach a picture',
22 | pictureUrl: null
23 | };
24 | }
25 |
26 | render() {
27 | const { required, input, label } = this.props;
28 | delete input.value;
29 | return (
30 |
31 |
32 |
33 |
34 | {
38 | this.displayPicture(event);
39 | }}
40 | />
41 |
42 |
43 |
44 |
45 | );
46 | }
47 | }
48 |
49 | export default InputField;
50 |
--------------------------------------------------------------------------------
/src/Components/InputField.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { errStyle } from '../Helpers/ReduxFormValidation';
3 |
4 | function isVowel(char) {
5 | return /^[aeiou]$/.test(char.toLowerCase());
6 | }
7 |
8 | const InputField = (props) => {
9 | const { required, input, meta: { touched, error } } = props;
10 | const isError = touched && error;
11 | return (
12 |
13 |
14 |
15 |
21 | {isError &&
22 |
23 | {error}
24 |
}
25 |
26 |
27 | );
28 | };
29 | export default InputField;
30 |
--------------------------------------------------------------------------------
/src/Components/Loading.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import '../Styles/App.css'
3 |
4 | const Loading = () => {
5 | return (
6 |
7 |
8 | Loading...
9 |
10 | )
11 | };
12 |
13 | export default Loading;
14 |
--------------------------------------------------------------------------------
/src/Components/SimpleBox.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | export default class SimpleBox extends Component {
4 | render() {
5 | const { children, title } = this.props;
6 | return (
7 |
8 |
9 |
10 |
11 |
12 |
{title}
13 |
14 | {children}
15 |
16 |
17 |
18 |
19 | );
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Containers/AuthenticatedComponent.js:
--------------------------------------------------------------------------------
1 | import { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { withRouter } from 'react-router-dom';
4 | import * as React from 'react';
5 |
6 | class AuthenticatedComponent extends Component {
7 |
8 | componentDidUpdate() {
9 | const { userLoading, user } = this.props;
10 | if (userLoading === false && !user) {
11 | this.props.history.push('/Login');
12 | }
13 | }
14 |
15 | render() {
16 | const { user, children, userLoading } = this.props;
17 | return (userLoading === false && user) ? {children}
: null;
18 | }
19 | }
20 |
21 | function mapStateToProps(state) {
22 | return { user: state.user, userLoading: state.loading.user };
23 | }
24 |
25 | export default withRouter(connect(mapStateToProps)(AuthenticatedComponent));
26 |
--------------------------------------------------------------------------------
/src/Containers/CreateAccount.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import InputField from '../Components/InputField';
3 | import FooterFormButton from '../Components/FooterFormButton';
4 | import SimpleBox from '../Components/SimpleBox';
5 | import { createAccount } from '../Actions/UserActions';
6 | import { connect } from 'react-redux';
7 | import { Field, reduxForm } from 'redux-form';
8 | import { email, required } from '../Helpers/ReduxFormValidation';
9 | import ImageField from '../Components/ImageField';
10 |
11 | class CreateAccount extends Component {
12 | constructor(props) {
13 | super(props);
14 | this.state = {
15 | email: '',
16 | password: '',
17 | confirmPassword: '',
18 | error: ''
19 | };
20 | }
21 |
22 | onSubmit(data) {
23 | this.props.createAccount(data, this.state);
24 | }
25 |
26 | render() {
27 | const { handleSubmit } = this.props;
28 | return (
29 |
78 | );
79 | }
80 | }
81 |
82 | let form = reduxForm({
83 | form: 'CreateAccountForm'
84 | })(CreateAccount);
85 |
86 | form = connect(null, { createAccount })(form);
87 |
88 | export default form;
--------------------------------------------------------------------------------
/src/Containers/ListUsers.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import _ from 'lodash';
4 | import SimpleBox from '../Components/SimpleBox';
5 | import { logout } from '../Actions/UserActions';
6 | import PreviewPicture from './PreviewPicture';
7 |
8 | class ListUsers extends Component {
9 | renderUsers() {
10 | const { userData, uid } = this.props;
11 | console.log(userData);
12 | return _.map(_.filter(userData, (user, key) => {
13 | return key !== uid;
14 | }), (user, key) => {
15 | return (
16 |
17 |
18 | {user.fname} {user.lname}
19 |
20 |
21 |
22 | );
23 | });
24 | }
25 |
26 | render() {
27 | const { uid, userData } = this.props;
28 | return (
29 |
30 | {uid &&
31 |
32 |
Welcome {userData[uid].fname} {userData[uid].lname}
}
33 | {this.renderUsers()}
34 |
35 | );
36 | }
37 | }
38 |
39 | function mapStateToProps(state) {
40 | const checkedUser = state.user || {};
41 | return { uid: checkedUser.uid, userData: state.dbUsers };
42 | }
43 |
44 | export default connect(mapStateToProps, { logout })(ListUsers);
--------------------------------------------------------------------------------
/src/Containers/LoadingComponent.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { withRouter } from 'react-router-dom';
4 | import { getUser, getDbUsers } from '../Actions/UserActions';
5 | import Loading from '../Components/Loading';
6 | import { auth } from '../Firebase';
7 |
8 | class LoadingComponent extends Component {
9 | componentWillMount() {
10 | const { userLoading, dbUserLoading } = this.props;
11 | if (userLoading === undefined) {
12 | this.props.getUser();
13 | }
14 | if (dbUserLoading === undefined) {
15 | this.props.getDbUsers();
16 | }
17 | }
18 |
19 | render() {
20 | const { userLoading, dbUserLoading, children } = this.props;
21 | if (userLoading === false && dbUserLoading === false) {
22 | return (
23 |
24 | {children}
25 |
26 | );
27 | }
28 |
29 | else {
30 | return (
31 |
32 | );
33 | }
34 | }
35 | }
36 |
37 | function mapStateToProps(state) {
38 | return {
39 | userLoading: state.loading.user,
40 | dbUserLoading: state.loading.dbUser,
41 | user: state.user
42 | };
43 | }
44 |
45 | export default withRouter(connect(mapStateToProps, { getUser, getDbUsers })(LoadingComponent));
46 |
--------------------------------------------------------------------------------
/src/Containers/Login.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import SimpleBox from '../Components/SimpleBox';
3 | import InputField from '../Components/InputField';
4 | import FooterFormButton from '../Components/FooterFormButton';
5 | import { login, getUser } from '../Actions/UserActions';
6 | import { connect } from 'react-redux';
7 | import ErrorAlert from '../Components/ErrorAlert';
8 | import { Field, reduxForm } from 'redux-form';
9 | import { email, required } from '../Helpers/ReduxFormValidation';
10 |
11 | class Login extends Component {
12 | constructor(props) {
13 | super(props);
14 | this.state = {
15 | error: ''
16 | };
17 | }
18 |
19 | onSubmit(data) {
20 | this.props.login(data.email, data.password).catch((err) => {
21 | this.setState({
22 | error: err
23 | });
24 | }
25 | );
26 | }
27 |
28 | componentWillMount() {
29 | if (this.props.user !== null) {
30 | this.props.history.push('/');
31 | }
32 | }
33 |
34 | componentWillReceiveProps(nextProps) {
35 | if (nextProps.user !== null) {
36 | nextProps.history.push('/');
37 | }
38 | }
39 |
40 | render() {
41 | const { handleSubmit } = this.props;
42 | return (
43 |
71 | );
72 | }
73 | }
74 |
75 | function mapStateToProps(state, ownProps) {
76 | return { user: state.user };
77 | }
78 |
79 | let form = reduxForm({
80 | form: 'LoginForm'
81 | })(Login);
82 |
83 | form = connect(mapStateToProps, { login, getUser })(form);
84 |
85 | export default form;
86 |
--------------------------------------------------------------------------------
/src/Containers/PreviewPicture.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const PreviewPicture = (props) => {
4 | const { picture, pictureUrl } = props;
5 |
6 | if (!pictureUrl) {
7 | return (
8 |
12 | {picture}
13 |
14 | );
15 | }
16 | else {
17 | return (
18 |
19 | );
20 | }
21 | };
22 |
23 | export default PreviewPicture;
24 |
--------------------------------------------------------------------------------
/src/Firebase.js:
--------------------------------------------------------------------------------
1 | import * as firebase from 'firebase';
2 | const config = {
3 | apiKey: "AIzaSyBCw_Px68EFyzY2eZPegF1I1uRMjq_XONw",
4 | authDomain: "acoolmessageboard.firebaseapp.com",
5 | databaseURL: "https://acoolmessageboard.firebaseio.com",
6 | projectId: "acoolmessageboard",
7 | storageBucket: "acoolmessageboard.appspot.com",
8 | messagingSenderId: "410302103395"
9 | };
10 | firebase.initializeApp(config);
11 | export const database = firebase.database();
12 | export const auth = firebase.auth();
13 | export const storage = firebase.storage().ref();
14 |
--------------------------------------------------------------------------------
/src/Helpers/ReduxFormValidation.js:
--------------------------------------------------------------------------------
1 | export const required = value => value ? undefined : 'This field is required';
2 | export const email = value => /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value) ? undefined : 'Invalid email address';
3 |
4 | export const errStyle = {
5 | borderColor: 'red'
6 | };
--------------------------------------------------------------------------------
/src/Reducers/DbUserReducer.js:
--------------------------------------------------------------------------------
1 | import { GET_DB_USERS } from '../Actions/UserActions';
2 |
3 | export default function (state = {}, action) {
4 | switch (action.type) {
5 | case GET_DB_USERS:
6 | return action.payload;
7 | default:
8 | return state;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Reducers/LoadingReducer.js:
--------------------------------------------------------------------------------
1 | import { USER_DB_STATUS, USER_STATUS } from '../Actions/UserActions';
2 |
3 | export default function (state = {}, action) {
4 | switch (action.type) {
5 | case USER_STATUS:
6 | return { ...state, user: action.payload };
7 | case USER_DB_STATUS:
8 | return { ...state, dbUser: action.payload };
9 | default:
10 | return state;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Reducers/UserReducer.js:
--------------------------------------------------------------------------------
1 | import { GET_USER } from '../Actions/UserActions';
2 | export default function (state = {}, action) {
3 | switch (action.type) {
4 | case GET_USER:
5 | return action.payload;
6 | default:
7 | return state;
8 | }
9 | }
--------------------------------------------------------------------------------
/src/Reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import { reducer as formReducer } from 'redux-form'
3 | import UserReducer from './UserReducer';
4 | import LoadingReducer from './LoadingReducer';
5 | import DbUserReducer from './DbUserReducer';
6 |
7 | const rootReducer = combineReducers({
8 | form: formReducer,
9 | user: UserReducer,
10 | loading: LoadingReducer,
11 | dbUsers: DbUserReducer
12 | });
13 |
14 | export default rootReducer;
--------------------------------------------------------------------------------
/src/Styles/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #474647;
3 | }
4 |
5 | .loading {
6 | color: white;
7 | }
8 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import registerServiceWorker from './registerServiceWorker';
4 | import { applyMiddleware, createStore } from 'redux';
5 | import thunk from 'redux-thunk';
6 | import reducers from './Reducers/index';
7 | import { Provider } from 'react-redux';
8 | import { BrowserRouter, Switch, Route } from 'react-router-dom';
9 | import Login from './Containers/Login';
10 | import CreateAccount from './Containers/CreateAccount';
11 | import LoadingComponent from './Containers/LoadingComponent';
12 | import ListUsers from './Containers/ListUsers';
13 | const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
14 |
15 | ReactDOM.render(
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | ,
27 | document.getElementById('root'));
28 | registerServiceWorker();
29 |
--------------------------------------------------------------------------------
/src/registerServiceWorker.js:
--------------------------------------------------------------------------------
1 | // In production, we register a service worker to serve assets from local cache.
2 |
3 | // This lets the app load faster on subsequent visits in production, and gives
4 | // it offline capabilities. However, it also means that developers (and users)
5 | // will only see deployed updates on the "N+1" visit to a page, since previously
6 | // cached resources are updated in the background.
7 |
8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
9 | // This link also includes instructions on opting out of this behavior.
10 |
11 | const isLocalhost = Boolean(
12 | window.location.hostname === 'localhost' ||
13 | // [::1] is the IPv6 localhost address.
14 | window.location.hostname === '[::1]' ||
15 | // 127.0.0.1/8 is considered localhost for IPv4.
16 | window.location.hostname.match(
17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
18 | )
19 | );
20 |
21 | export default function register() {
22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
23 | // The URL constructor is available in all browsers that support SW.
24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
25 | if (publicUrl.origin !== window.location.origin) {
26 | // Our service worker won't work if PUBLIC_URL is on a different origin
27 | // from what our page is served on. This might happen if a CDN is used to
28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
29 | return;
30 | }
31 |
32 | window.addEventListener('load', () => {
33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
34 |
35 | if (!isLocalhost) {
36 | // Is not local host. Just register service worker
37 | registerValidSW(swUrl);
38 | } else {
39 | // This is running on localhost. Lets check if a service worker still exists or not.
40 | checkValidServiceWorker(swUrl);
41 | }
42 | });
43 | }
44 | }
45 |
46 | function registerValidSW(swUrl) {
47 | navigator.serviceWorker
48 | .register(swUrl)
49 | .then(registration => {
50 | registration.onupdatefound = () => {
51 | const installingWorker = registration.installing;
52 | installingWorker.onstatechange = () => {
53 | if (installingWorker.state === 'installed') {
54 | if (navigator.serviceWorker.controller) {
55 | // At this point, the old content will have been purged and
56 | // the fresh content will have been added to the cache.
57 | // It's the perfect time to display a "New content is
58 | // available; please refresh." message in your web app.
59 | console.log('New content is available; please refresh.');
60 | } else {
61 | // At this point, everything has been precached.
62 | // It's the perfect time to display a
63 | // "Content is cached for offline use." message.
64 | console.log('Content is cached for offline use.');
65 | }
66 | }
67 | };
68 | };
69 | })
70 | .catch(error => {
71 | console.error('Error during service worker registration:', error);
72 | });
73 | }
74 |
75 | function checkValidServiceWorker(swUrl) {
76 | // Check if the service worker can be found. If it can't reload the page.
77 | fetch(swUrl)
78 | .then(response => {
79 | // Ensure service worker exists, and that we really are getting a JS file.
80 | if (
81 | response.status === 404 ||
82 | response.headers.get('content-type').indexOf('javascript') === -1
83 | ) {
84 | // No service worker found. Probably a different app. Reload the page.
85 | navigator.serviceWorker.ready.then(registration => {
86 | registration.unregister().then(() => {
87 | window.location.reload();
88 | });
89 | });
90 | } else {
91 | // Service worker found. Proceed as normal.
92 | registerValidSW(swUrl);
93 | }
94 | })
95 | .catch(() => {
96 | console.log(
97 | 'No internet connection found. App is running in offline mode.'
98 | );
99 | });
100 | }
101 |
102 | export function unregister() {
103 | if ('serviceWorker' in navigator) {
104 | navigator.serviceWorker.ready.then(registration => {
105 | registration.unregister();
106 | });
107 | }
108 | }
109 |
--------------------------------------------------------------------------------