1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-03-23 11:04:49 +00:00

Added V2 from scratch

This commit is contained in:
retspen 2018-09-23 13:17:48 +03:00
parent 5c2232f4e8
commit 6c2925a35d
478 changed files with 21437 additions and 134206 deletions

View file

@ -0,0 +1,28 @@
.App {
text-align: center;
}
.App-logo {
animation: App-logo-spin infinite 20s linear;
height: 80px;
}
.App-header {
background-color: #222;
height: 150px;
padding: 20px;
color: white;
}
.App-title {
font-size: 1.5em;
}
.App-intro {
font-size: large;
}
@keyframes App-logo-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}

19
frontend/admin/src/App.js Normal file
View file

@ -0,0 +1,19 @@
import React, { Component } from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import Dashboard from './layouts/Dashboard';
import Auth from './layouts/Auth';
import './App.css';
export default class App extends Component {
render() {
return (
<Router>
<Switch>
<Route path="/" component={Dashboard} />
<Route path="/profile" component={Dashboard} />
<Route path="/auth" component={Auth} />
</Switch>
</Router>
);
}
}

View file

@ -0,0 +1,9 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});

View file

@ -0,0 +1,64 @@
import React from 'react';
import ListItem from '@material-ui/core/ListItem';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemText from '@material-ui/core/ListItemText';
import ListSubheader from '@material-ui/core/ListSubheader';
import DashboardIcon from '@material-ui/icons/Dashboard';
import PeopleIcon from '@material-ui/icons/People';
import BarChartIcon from '@material-ui/icons/BarChart';
import LayersIcon from '@material-ui/icons/Layers';
import AssignmentIcon from '@material-ui/icons/Assignment';
import {Link} from 'react-router-dom';
export const mainListItems = (
<div>
<ListItem button component={Link} to="/">
<ListItemIcon>
<DashboardIcon />
</ListItemIcon>
<ListItemText primary="Dashboard" />
</ListItem>
<ListItem button component={Link} to="/users">
<ListItemIcon>
<PeopleIcon />
</ListItemIcon>
<ListItemText primary="Users" />
</ListItem>
<ListItem button>
<ListItemIcon>
<BarChartIcon />
</ListItemIcon>
<ListItemText primary="Reports" />
</ListItem>
<ListItem button>
<ListItemIcon>
<LayersIcon />
</ListItemIcon>
<ListItemText primary="Compute" />
</ListItem>
</div>
);
export const secondaryListItems = (
<div>
<ListSubheader inset>Reports</ListSubheader>
<ListItem button>
<ListItemIcon>
<AssignmentIcon />
</ListItemIcon>
<ListItemText primary="Current month" />
</ListItem>
<ListItem button>
<ListItemIcon>
<AssignmentIcon />
</ListItemIcon>
<ListItemText primary="Last quarter" />
</ListItem>
<ListItem button>
<ListItemIcon>
<AssignmentIcon />
</ListItemIcon>
<ListItemText primary="Year-end sale" />
</ListItem>
</div>
);

View file

@ -0,0 +1,5 @@
body {
margin: 0;
padding: 0;
font-family: 'Roboto', sans-serif;
}

View file

@ -0,0 +1,11 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './css/main.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render((
<App />
), document.getElementById('root')
);
registerServiceWorker();

View file

@ -0,0 +1,23 @@
import React from "react";
import {Route, Switch} from "react-router-dom";
import SignInPage from "../pages/auth/SignIn";
import SignUpPage from "../pages/auth/SignUp";
import CssBaseline from "@material-ui/core/CssBaseline/CssBaseline";
function Auth(props) {
return (
<React.Fragment>
<CssBaseline />
<div className="dashboard-layout">
<main>
<Switch>
<Route exact path="/auth/signin" component={SignInPage} />
<Route exact path="/auth/signup" component={SignUpPage} />
</Switch>
</main>
</div>
</React.Fragment>
)
}
export default Auth;

View file

@ -0,0 +1,217 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import {withStyles} from '@material-ui/core/styles';
import CssBaseline from '@material-ui/core/CssBaseline';
import Drawer from '@material-ui/core/Drawer';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import List from '@material-ui/core/List';
import Typography from '@material-ui/core/Typography';
import Divider from '@material-ui/core/Divider';
import IconButton from '@material-ui/core/IconButton';
import Badge from '@material-ui/core/Badge';
import MenuIcon from '@material-ui/icons/Menu';
import ChevronLeftIcon from '@material-ui/icons/ChevronLeft';
import NotificationsIcon from '@material-ui/icons/Notifications';
import {Link, Route, Switch} from "react-router-dom";
import IndexPage from "../pages/Index";
import UsersPage from "../pages/Users";
import {mainListItems, secondaryListItems} from '../components/listItems';
import AccountCircle from '@material-ui/icons/AccountCircle';
import Menu from "@material-ui/core/Menu/Menu";
import MenuItem from "@material-ui/core/MenuItem/MenuItem";
const drawerWidth = 240;
const styles = theme => ({
root: {
display: 'flex',
},
toolbar: {
paddingRight: 24, // keep right padding when drawer closed
},
toolbarIcon: {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
padding: '0 8px',
...theme.mixins.toolbar,
},
appBar: {
zIndex: theme.zIndex.drawer + 1,
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
},
appBarShift: {
marginLeft: drawerWidth,
width: `calc(100% - ${drawerWidth}px)`,
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
},
menuButton: {
marginLeft: 12,
marginRight: 36,
},
menuButtonHidden: {
display: 'none',
},
title: {
flexGrow: 1,
},
drawerPaper: {
position: 'relative',
whiteSpace: 'nowrap',
width: drawerWidth,
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
},
drawerPaperClose: {
overflowX: 'hidden',
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
width: theme.spacing.unit * 7,
[theme.breakpoints.up('sm')]: {
width: theme.spacing.unit * 9,
},
},
appBarSpacer: theme.mixins.toolbar,
content: {
flexGrow: 1,
padding: theme.spacing.unit * 3,
height: '100vh',
overflow: 'auto',
},
chartContainer: {
marginLeft: -22,
},
tableContainer: {
height: 320,
},
});
class Dashboard extends React.Component {
state = {
open: true,
anchorEl: null,
};
handleDrawerOpen = () => {
this.setState({open: true});
};
handleDrawerClose = () => {
this.setState({open: false});
};
handleUserMenu = event => {
this.setState({ anchorEl: event.currentTarget });
};
handleUserMenuClose = () => {
this.setState({ anchorEl: null });
};
render() {
const {classes} = this.props;
const { anchorEl } = this.state;
const userMenuIsOpen = Boolean(anchorEl);
return (
<React.Fragment>
<CssBaseline/>
<div className={classes.root}>
<AppBar
position="absolute"
className={classNames(classes.appBar, this.state.open && classes.appBarShift)}
>
<Toolbar disableGutters={!this.state.open} className={classes.toolbar}>
<IconButton
color="inherit"
aria-label="Open drawer"
onClick={this.handleDrawerOpen}
className={classNames(
classes.menuButton,
this.state.open && classes.menuButtonHidden,
)}
>
<MenuIcon/>
</IconButton>
<Typography variant="title" color="inherit" noWrap className={classes.title}>
Dashboard
</Typography>
<IconButton color="inherit">
<Badge badgeContent={4} color="secondary">
<NotificationsIcon/>
</Badge>
</IconButton>
<IconButton
aria-owns={userMenuIsOpen ? 'menu-appbar' : null}
aria-haspopup="true"
onClick={this.handleUserMenu}
color="inherit"
>
<AccountCircle />
</IconButton>
<Menu
id="menu-appbar"
anchorEl={anchorEl}
anchorOrigin={{
vertical: 'top',
horizontal: 'right',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
open={userMenuIsOpen}
onClose={this.handleClose}
>
<MenuItem component={Link} to="/profile" onClick={this.handleUserMenu}>Profile</MenuItem>
<MenuItem component={Link} to="/auth/signin" onClick={this.handleUserMenu}>Log out</MenuItem>
</Menu>
</Toolbar>
</AppBar>
<Drawer
variant="permanent"
classes={{
paper: classNames(classes.drawerPaper, !this.state.open && classes.drawerPaperClose),
}}
open={this.state.open}
>
<div className={classes.toolbarIcon}>
<IconButton onClick={this.handleDrawerClose}>
<ChevronLeftIcon/>
</IconButton>
</div>
<Divider/>
<List>{mainListItems}</List>
<Divider/>
{/*<List>{secondaryListItems}</List>*/}
</Drawer>
<main className={classes.content}>
<div className={classes.appBarSpacer}/>
<Switch>
<Route exact path="/" component={IndexPage}/>
<Route exact path="/users" component={UsersPage}/>
</Switch>
</main>
</div>
</React.Fragment>
);
}
}
Dashboard.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(Dashboard);

View file

@ -0,0 +1,45 @@
import React from 'react';
import Typography from '@material-ui/core/Typography';
import Paper from '@material-ui/core/Paper';
import CssBaseline from "@material-ui/core/CssBaseline/CssBaseline";
import withStyles from "@material-ui/core/styles/withStyles";
import Grid from '@material-ui/core/Grid';
const styles = theme => ({
title: {
marginBottom: theme.spacing.unit * 3,
},
root: {
flexGrow: 1,
},
paper: {
width: '100%',
overflowX: 'auto',
padding: theme.spacing.unit * 4,
},
});
function Index(props) {
const { classes } = props;
return (
<React.Fragment>
<CssBaseline />
<div>
<Typography className={classes.title} variant="headline">Cluster stats</Typography>
<Grid container spacing={24}>
<Grid item xs={12} md={4}>
<Paper className={classes.paper}>Some graph here</Paper>
</Grid>
<Grid item xs={12} md={4}>
<Paper className={classes.paper}>Some graph here</Paper>
</Grid>
<Grid item xs={12} md={4}>
<Paper className={classes.paper}>Some graph here</Paper>
</Grid>
</Grid>
</div>
</React.Fragment>
)
}
export default withStyles(styles)(Index);

View file

@ -0,0 +1,117 @@
import React from "react";
import Paper from "@material-ui/core/Paper";
import CssBaseline from "@material-ui/core/CssBaseline/CssBaseline";
import Typography from '@material-ui/core/Typography';
import Grid from '@material-ui/core/Grid';
import TextField from '@material-ui/core/TextField';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Checkbox from '@material-ui/core/Checkbox';
import Button from '@material-ui/core/Button';
import withStyles from "@material-ui/core/styles/withStyles";
const styles = theme => ({
layout: {
width: 'auto',
display: 'block',
marginLeft: theme.spacing.unit * 3,
marginRight: theme.spacing.unit * 3,
[theme.breakpoints.up(600 + theme.spacing.unit * 3 * 2)]: {
width: 600,
marginLeft: 'auto',
marginRight: 'auto',
},
},
paper: {
padding: theme.spacing.unit * 4,
},
});
function Profile(props) {
const { classes } = props;
return (
<React.Fragment>
<CssBaseline />
<div className={classes.layout}>
<Paper className={classes.paper}>
<Typography variant="headline">Profile</Typography>
<Grid container spacing={24}>
<Grid item xs={12} sm={6}>
<TextField
required
id="firstName"
name="firstName"
label="First name"
fullWidth
autoComplete="fname"
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
required
id="lastName"
name="lastName"
label="Last name"
fullWidth
autoComplete="lname"
/>
</Grid>
<Grid item xs={12}>
<TextField
required
id="address1"
name="address1"
label="Address line 1"
fullWidth
autoComplete="billing address-line1"
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
required
id="city"
name="city"
label="City"
fullWidth
autoComplete="billing address-level2"
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField id="state" name="state" label="State/Province/Region" fullWidth />
</Grid>
<Grid item xs={12} sm={6}>
<TextField
required
id="zip"
name="zip"
label="Zip / Postal code"
fullWidth
autoComplete="billing postal-code"
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
required
id="country"
name="country"
label="Country"
fullWidth
autoComplete="billing country"
/>
</Grid>
<Grid item xs={12}>
<FormControlLabel
control={<Checkbox color="secondary" name="saveAddress" value="yes" />}
label="Use this address for payment details"
/>
</Grid>
<Grid item xs={12}>
<Button fullWidth variant="contained" color="primary">Update profile</Button>
</Grid>
</Grid>
</Paper>
</div>
</React.Fragment>
)
}
export default withStyles(styles)(Profile);

View file

@ -0,0 +1,74 @@
import React from 'react';
import Typography from '@material-ui/core/Typography';
import Paper from '@material-ui/core/Paper';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import IconButton from '@material-ui/core/IconButton';
import DeleteIcon from '@material-ui/icons/Delete';
import CssBaseline from "@material-ui/core/CssBaseline/CssBaseline";
import withStyles from "@material-ui/core/styles/withStyles";
const styles = theme => ({
title: {
marginBottom: theme.spacing.unit * 2,
},
paper: {
width: '100%',
overflowX: 'auto',
padding: theme.spacing.unit * 4,
},
});
let id = 0;
function createData(name, status) {
id += 1;
return { id, name, status };
}
const rows = [
createData('cloud-test', 'connected'),
createData('cloud-ubuntu-fra3', 'disconnected'),
];
function Index(props) {
const { classes } = props;
return (
<React.Fragment>
<CssBaseline />
<div>
<Typography className={classes.title} variant="headline">Users</Typography>
<Paper className={classes.paper}>
<Table className={classes.table}>
<TableHead>
<TableRow>
<TableCell>Name</TableCell>
<TableCell>Status</TableCell>
<TableCell>Action</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map(row => {
return (
<TableRow key={row.id}>
<TableCell component="th" scope="row">{row.name}</TableCell>
<TableCell>{row.status}</TableCell>
<TableCell>
<IconButton aria-label="Delete">
<DeleteIcon />
</IconButton>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</Paper>
</div>
</React.Fragment>
)
}
export default withStyles(styles)(Index);

View file

@ -0,0 +1,135 @@
import React from 'react';
import PropTypes from 'prop-types';
import Avatar from '@material-ui/core/Avatar';
import Button from '@material-ui/core/Button';
import CssBaseline from '@material-ui/core/CssBaseline';
import FormControl from '@material-ui/core/FormControl';
import Input from '@material-ui/core/Input';
import InputLabel from '@material-ui/core/InputLabel';
import LockIcon from '@material-ui/icons/LockOutlined';
import Paper from '@material-ui/core/Paper';
import Typography from '@material-ui/core/Typography';
import withStyles from '@material-ui/core/styles/withStyles';
import {Link} from "react-router-dom";
const styles = theme => ({
layout: {
width: 'auto',
display: 'block',
marginLeft: theme.spacing.unit * 3,
marginRight: theme.spacing.unit * 3,
[theme.breakpoints.up(400 + theme.spacing.unit * 3 * 2)]: {
width: 400,
marginLeft: 'auto',
marginRight: 'auto',
},
},
paper: {
marginTop: theme.spacing.unit * 8,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px`,
},
avatar: {
margin: theme.spacing.unit,
backgroundColor: theme.palette.secondary.main,
},
form: {
width: '100%',
marginTop: theme.spacing.unit,
},
submit: {
marginTop: theme.spacing.unit * 3,
},
footer: {
marginTop: theme.spacing.unit * 3,
textAlign: 'center',
},
});
class SignIn extends React.Component {
constructor(props) {
super(props);
this.state = {
user: {
name: '',
email: '',
password: '',
}
};
}
/**
* Change the user object.
*
* @param {object} event - the JavaScript event object
*/
handleChange(event) {
const field = event.target.name;
const user = this.state.user;
user[field] = event.target.value;
this.setState({ user });
}
login() {
return fetch('http://localhost:8000/api/v1/rest-auth/registration/', {
method: 'POST',
body: JSON.stringify({
email: this.state.user.email,
name: this.state.user.name,
password: this.state.user.password,
}),
});
}
render() {
const { classes } = this.props;
return (
<React.Fragment>
<CssBaseline />
<div className={classes.layout}>
<Paper className={classes.paper}>
<Avatar className={classes.avatar}>
<LockIcon />
</Avatar>
<Typography variant="headline">Sign In</Typography>
<form className={classes.form}>
<FormControl margin="normal" required fullWidth>
<InputLabel htmlFor="email">Email Address</InputLabel>
<Input id="email" name="email" autoComplete="email" autoFocus />
</FormControl>
<FormControl margin="normal" required fullWidth>
<InputLabel htmlFor="password">Password</InputLabel>
<Input
name="password"
type="password"
id="password"
autoComplete="current-password"
/>
</FormControl>
<Button
type="submit"
fullWidth
variant="raised"
color="primary"
className={classes.submit}
onClick={this.login.bind(this)}
>
Sign In
</Button>
</form>
</Paper>
<footer className={classes.footer}>
<Button size="small" component={Link} to="/auth/signup">Don't have an account? Sign Up</Button>
</footer>
</div>
</React.Fragment>
)
}
}
SignIn.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(SignIn);

View file

@ -0,0 +1,150 @@
import React from 'react';
import {Link} from 'react-router-dom';
import PropTypes from 'prop-types';
import Avatar from '@material-ui/core/Avatar';
import Button from '@material-ui/core/Button';
import CssBaseline from '@material-ui/core/CssBaseline';
import FormControl from '@material-ui/core/FormControl';
import Input from '@material-ui/core/Input';
import InputLabel from '@material-ui/core/InputLabel';
import LockIcon from '@material-ui/icons/LockOutlined';
import Paper from '@material-ui/core/Paper';
import Typography from '@material-ui/core/Typography';
import withStyles from '@material-ui/core/styles/withStyles';
const styles = theme => ({
layout: {
width: 'auto',
display: 'block',
marginLeft: theme.spacing.unit * 3,
marginRight: theme.spacing.unit * 3,
[theme.breakpoints.up(400 + theme.spacing.unit * 3 * 2)]: {
width: 400,
marginLeft: 'auto',
marginRight: 'auto',
},
},
paper: {
marginTop: theme.spacing.unit * 8,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px`,
},
avatar: {
margin: theme.spacing.unit,
backgroundColor: theme.palette.secondary.main,
},
form: {
width: '100%',
marginTop: theme.spacing.unit,
},
submit: {
marginTop: theme.spacing.unit * 3,
},
footer: {
marginTop: theme.spacing.unit * 3,
textAlign: 'center',
},
});
class SignIn extends React.Component {
constructor(props) {
super(props);
this.state = {
user: {
email: '',
password: '',
}
};
this.submitForm = this.submitForm.bind(this);
this.handleChange = this.handleChange.bind(this);
}
/**
* Change the user object.
*
* @param {object} event - the JavaScript event object
*/
handleChange(event) {
const field = event.target.name;
const user = this.state.user;
user[field] = event.target.value;
this.setState({ user });
}
/**
* Process the form.
*
* @param {object} event - the JavaScript event object
*/
submitForm(event) {
// prevent default action. in this case, action is the form submission event
event.preventDefault();
fetch('http://localhost:8000/api/v1/rest-auth/registration/', {
credentials: 'same-origin',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
method: 'POST',
body: JSON.stringify(this.state.user),
})
.then(response=> response.json())
.then((response) => {
console.log(response);
});
}
render() {
const { classes } = this.props;
return (
<React.Fragment>
<CssBaseline />
<div className={classes.layout}>
<Paper className={classes.paper}>
<Avatar className={classes.avatar}>
<LockIcon />
</Avatar>
<Typography variant="headline">Sign Up</Typography>
<form action="/" className={classes.form} onSubmit={this.submitForm}>
<FormControl margin="normal" required fullWidth>
<InputLabel htmlFor="email">Email Address</InputLabel>
<Input onChange={this.handleChange} id="email" name="email" autoComplete="email" autoFocus />
</FormControl>
<FormControl margin="normal" required fullWidth>
<InputLabel htmlFor="password">Password</InputLabel>
<Input
onChange={this.handleChange}
name="password"
type="password"
id="password"
autoComplete="current-password"
/>
</FormControl>
<Button
type="submit"
fullWidth
variant="raised"
color="primary"
className={classes.submit}
>
Sign Up
</Button>
</form>
</Paper>
<footer className={classes.footer}>
<Button size="small" component={Link} to="/auth/signin">Already have an account? Sign In</Button>
</footer>
</div>
</React.Fragment>
)
}
}
SignIn.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(SignIn);

View file

@ -0,0 +1,117 @@
// In production, we register a service worker to serve assets from local cache.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on the "N+1" visit to a page, since previously
// cached resources are updated in the background.
// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
// This link also includes instructions on opting out of this behavior.
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
export default function register() {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Lets check if a service worker still exists or not.
checkValidServiceWorker(swUrl);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://goo.gl/SC7cgQ'
);
});
} else {
// Is not local host. Just register service worker
registerValidSW(swUrl);
}
});
}
}
function registerValidSW(swUrl) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the old content will have been purged and
// the fresh content will have been added to the cache.
// It's the perfect time to display a "New content is
// available; please refresh." message in your web app.
console.log('New content is available; please refresh.');
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl)
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
if (
response.status === 404 ||
response.headers.get('content-type').indexOf('javascript') === -1
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl);
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
});
}
}