initializes the base for the firmware wizard

PWA based on ReactJs. Showcases the basic functionalities required
for the Firmware Selector.
Proof of Concept for the wizard with selection for vendor, model
and variants.

Signed-off-by: Sudhanshu Gautam <me@sudhanshug.com>
Initial commit from Create React App
This commit is contained in:
Sudhanshu Gautam 2019-06-26 02:26:43 +05:30
commit 8f68dc328e
20 changed files with 12167 additions and 0 deletions

38
src/App.js Normal file
View file

@ -0,0 +1,38 @@
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import './App.scss';
import { createMuiTheme } from '@material-ui/core/styles';
import { ThemeProvider } from '@material-ui/styles';
import Header from './components/header.js'
import Home from './containers/home/home';
import NotFound from './containers/not-found/not-found';
const theme = createMuiTheme({
palette: {
primary: {
main: '#3F51B5',
},
secondary: {
main: '#009688',
},
},
});
function App() {
return (
<ThemeProvider theme={theme}>
<div className="App">
<Header></Header>
<Router>
<Switch>
<Route exact path="/" component={Home}></Route>
<Route default component={NotFound}></Route>
</Switch>
</Router>
</div>
</ThemeProvider>
);
}
export default App;

33
src/App.scss Normal file
View file

@ -0,0 +1,33 @@
.App {
text-align: center;
}
.App-logo {
animation: App-logo-spin infinite 20s linear;
height: 40vmin;
pointer-events: none;
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

9
src/App.test.js Normal file
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);
});

25
src/components/header.js Normal file
View file

@ -0,0 +1,25 @@
import React from "react";
import AppBar from '@material-ui/core/AppBar';
import Typography from '@material-ui/core/Typography';
import Button from '@material-ui/core/Button';
import LanguageIcon from '@material-ui/icons/Language';
import { Toolbar } from '@material-ui/core';
class Header extends React.Component {
render() {
return (
<AppBar position="static">
<Toolbar>
<Typography edge="start" variant="h6">OpenWrt Firmware Selector Wizard</Typography>
<div style={{flexGrow: 1}}></div>
<Button color="secondary" variant="contained">
Change Language &nbsp;
<LanguageIcon />
</Button>
</Toolbar>
</AppBar>
);
}
}
export default Header;

233
src/containers/home/home.js Normal file
View file

@ -0,0 +1,233 @@
import React from "react";
import { Container, Paper, Typography, Grid, Button } from "@material-ui/core";
import './home.scss';
import Select from 'react-select';
import data from '../../data.json';
class Home extends React.Component {
devices = [];
state = {
selectedOption: null,
variants: [],
models: [],
showDeviceData: false,
device: null,
vendor: null,
model: null,
variant: null
};
vendorExistsInDevices(vendor) {
var exists = false;
var existIndex = -1;
this.devices.forEach((d, i) => {
if (d["label"] === vendor) {
exists = true;
existIndex = i;
}
});
return {
exists,
existIndex
};
}
modelExistsInDevices(vendorIndex, model) {
var exists = false;
var existIndex = -1;
this.devices[vendorIndex]["value"].forEach((d, i) => {
if (d["label"] === model) {
exists = true;
existIndex = i;
}
});
return {
exists,
existIndex
};
}
variantExistsInDevices(vendorIndex, modelIndex, variant) {
var exists = false;
var existIndex = -1;
this.devices[vendorIndex]["value"][modelIndex]["value"].forEach((d, i) => {
if (d["label"] === variant) {
exists = true;
existIndex = i;
}
});
return {
exists,
existIndex
};
}
componentDidMount() {
Object.keys(data["devices"]).forEach((device_id) => {
var vendor = "";
var variant = "";
var device_data = data["devices"][device_id];
if ("vendor" in device_data) {
vendor = device_data["vendor"];
}
var model = device_data["model"];
if ("variant" in device_data) {
variant = device_data["variant"];
}
var vendorExists = this.vendorExistsInDevices(vendor);
if (vendorExists.exists) {
var modelExists = this.modelExistsInDevices(vendorExists.existIndex, model);
if (modelExists.exists) {
var variantExists = this.variantExistsInDevices(vendorExists.existIndex, modelExists.existIndex, variant);
if (!variantExists.existIndex) {
this.devices[vendorExists.existIndex]["value"][modelExists.existIndex]["value"].push({
"label": variant,
"value": device_data
});
}
} else {
this.devices[vendorExists.existIndex]["value"].push({
"label": model,
"value": [{
"label": variant,
"value": device_data
}]
});
}
} else {
this.devices.push({
"label": vendor,
"value": [{
"label": model,
"value": [{
"label": variant,
"value": device_data
}]
}]
});
}
});
}
changeVendor = (v) => {
this.setState({
vendor: v,
model: null,
variant: null,
});
}
changeModel = (v) => {
this.setState({
model: v,
variant: v.value[0],
});
}
changeVariant = (v) => {
this.setState({
variant: v,
});
}
findDevice = () => {
try {
var device = this.state.variant.value;
this.setState({
device: device,
showDeviceData: true,
});
} catch (error) {
this.setState({
device: {},
showDeviceData: false,
});
}
}
render() {
return (
<Container className="home-container">
<Paper>
<Typography variant="h5">Download OpenWrt firmware for your device!</Typography>
<Typography>Please use the input below to download firmware for your device!</Typography>
<br />
<Grid container spacing={2}>
<Grid item xs={4}>
<Select
onChange={this.changeVendor}
options={this.devices}
value={this.state.vendor}
/>
</Grid>
{
this.state.vendor === null ? '' : (
<Grid item xs={4}>
<Select
onChange={this.changeModel}
options={this.state.vendor.value}
value={this.state.model}
/>
</Grid>
)
}
{
this.state.model === null ? '' : (
(this.state.model.value.length === 1 && this.state.model.value[0].label === '') ? '' : (
<Grid item xs={4}>
<Select
onChange={this.changeVariant}
options={this.state.model.value}
value={this.state.variant}
/>
</Grid>
)
)
}
</Grid>
<br />
<Button
color="primary"
variant="contained"
onClick={this.findDevice.bind(this)}
>
Submit
</Button>
<br />
{this.state.showDeviceData ? (
<table className="device-table">
<tbody>
<tr>
<td>Model</td>
<td>{this.state.device.model}</td>
</tr>
<tr>
<td>Vendor</td>
<td>{this.state.device.vendor}</td>
</tr>
{
this.state.device.variant === null || this.state.device.variant === '' ? '' : (
<tr>
<td>Variant</td>
<td>{this.state.device.variant}</td>
</tr>
)
}
{
this.state.device.images.map((image, i) => {
return <tr key={i}>
<td>{image.type}</td>
<td><a href={"http://downloads.openwrt.org/snapshots/targets/" + this.state.device.target + "/" + this.state.device.subtarget + "/" + image.name}>{image.name}</a></td>
</tr>;
})
}
</tbody>
</table>
) : ''}
</Paper>
</Container>
);
}
}
export default Home;

View file

@ -0,0 +1,22 @@
.home-container {
margin-top: 30px;
.MuiPaper-root {
padding: 30px;
text-align: left;
.MuiTypography-h4 {
font-weight: bold;
margin-bottom: 15px;
}
}
}
.device-table {
margin-top: 20px;
width: 100%;
td {
padding: 20px 30px;
}
tr:nth-child(odd) {
background-color: rgba(104, 74, 238, 0.07);
}
}

View file

@ -0,0 +1,26 @@
import React from "react";
import { Typography, Paper, Container } from "@material-ui/core";
import { makeStyles } from '@material-ui/core/styles';
const page404Styles = makeStyles(theme => ({
root: {
padding: theme.spacing(3, 2),
},
}));
export default function NotFound() {
var classes = page404Styles();
return (
<Container style={{marginTop: '50px'}}>
<Paper className={classes.root} elevation={3}>
<Typography variant="h5" component="h3">
404 Page Not Found
</Typography>
<Typography component="p">
Please head to the home.
</Typography>
</Paper>
</Container>
);
}

258
src/data.json Normal file
View file

@ -0,0 +1,258 @@
{
"release_version": "SNAPSHOT",
"release_commit": "r10241+1-865e25e049",
"devices": {
"tplink_archer-c5-v1": {
"device_id": "tplink_archer-c5-v1",
"supported_devices": ["tplink,archer-c5-v1", "archer-c5"],
"vendor": "TP-Link",
"model": "Archer C5",
"variant": "v1",
"target": "ath79",
"subtarget": "generic",
"images": [{
"type": "sysupgrade",
"name": "openwrt-ath79-generic-tplink_archer-c5-v1-squashfs-sysupgrade.bin",
"hash": "92e699c555d8b17423105f5d5fcf3faa6b9edd66f90480372aa5da69b0be5576"
},
{
"type": "factory",
"name": "openwrt-ath79-generic-tplink_archer-c5-v1-squashfs-factory.bin",
"hash": "e42a929f742e6ac141e277f676b86608ed8468afa3479f0f03a94138ff96ad42"
}
]
},
"tplink_archer-c7-v2": {
"device_id": "tplink_archer-c7-v2",
"supported_devices": ["tplink,archer-c7-v2", "archer-c7"],
"vendor": "TP-Link",
"model": "Archer C7",
"variant": "v2",
"target": "ath79",
"subtarget": "generic",
"images": [{
"type": "sysupgrade",
"name": "openwrt-ath79-generic-tplink_archer-c7-v2-squashfs-sysupgrade.bin",
"hash": "a9b49a62bbc5dbef0df90cb365d552a366760b8468c5feaed60d3c1845ab8d29"
},
{
"type": "factory",
"name": "openwrt-ath79-generic-tplink_archer-c7-v2-squashfs-factory.bin",
"hash": "6bea6065b621d5b34736c1394b08721bbe4efa932068bd4b724e9905d6dcb7d9"
},
{
"type": "factory-us",
"name": "openwrt-ath79-generic-tplink_archer-c7-v2-squashfs-factory-us.bin",
"hash": "20e8fc84bb315508874e3feb1734e315c003eed2a1294dcc6ab63050692126c5"
},
{
"type": "factory-eu",
"name": "openwrt-ath79-generic-tplink_archer-c7-v2-squashfs-factory-eu.bin",
"hash": "e7ec8fd5a563f0832531063de52e0c207fbedc305d452e6914328d36a9a364e6"
}
]
},
"tplink_tl-wdr4300": {
"device_id": "tplink_tl-wdr4300",
"supported_devices": ["tplink,tl-wdr4300", "tl-wdr4300"],
"vendor": "TP-Link",
"model": "TL-WDR4300",
"variant": "",
"target": "ath79",
"subtarget": "generic",
"images": [{
"type": "sysupgrade",
"name": "openwrt-ath79-generic-tplink_tl-wdr4300-squashfs-sysupgrade.bin",
"hash": "45b4f33e7f0c357dc61166032bf40891458ee9941e443daf896ae9b825639770"
},
{
"type": "factory",
"name": "openwrt-ath79-generic-tplink_tl-wdr4300-squashfs-factory.bin",
"hash": "21c0d11b7343f233f0e35dbf306c1269a0fcd602b0d26dbb5938c80e71e86a27"
}
]
},
"tplink_archer-c7-v4": {
"device_id": "tplink_archer-c7-v4",
"supported_devices": ["tplink,archer-c7-v4", "archer-c7-v4"],
"vendor": "TP-Link",
"model": "Archer C7",
"variant": "v4",
"target": "ath79",
"subtarget": "generic",
"images": [{
"type": "sysupgrade",
"name": "openwrt-ath79-generic-tplink_archer-c7-v4-squashfs-sysupgrade.bin",
"hash": "a0c9ea31608f35bcf88bd5090b721263d6f65396ecc23edaecbfb5ffae007a33"
},
{
"type": "factory",
"name": "openwrt-ath79-generic-tplink_archer-c7-v4-squashfs-factory.bin",
"hash": "7b44e8942a5ce5ceee04ac02e5cd7582c6e13653c8ce83adc5a60efca93bf25e"
}
]
},
"tplink_archer-c59-v1": {
"device_id": "tplink_archer-c59-v1",
"supported_devices": ["tplink,archer-c59-v1", "archer-c59-v1"],
"vendor": "TP-Link",
"model": "Archer C59",
"variant": "v1",
"target": "ath79",
"subtarget": "generic",
"images": [{
"type": "sysupgrade",
"name": "openwrt-ath79-generic-tplink_archer-c59-v1-squashfs-sysupgrade.bin",
"hash": "e0e6612dfbb961c62877cc347476d699e7f4b5aeca0355c468ac5716f3696b94"
},
{
"type": "factory",
"name": "openwrt-ath79-generic-tplink_archer-c59-v1-squashfs-factory.bin",
"hash": "99134ef23c69d055f45e20cfa638a287fc5182bec0825128d9cb0458471727d7"
}
]
},
"tplink_archer-c2-v3": {
"device_id": "tplink_archer-c2-v3",
"supported_devices": ["tplink,archer-c2-v3"],
"vendor": "TP-Link",
"model": "Archer C2",
"variant": "v3",
"target": "ath79",
"subtarget": "generic",
"images": [{
"type": "sysupgrade",
"name": "openwrt-ath79-generic-tplink_archer-c2-v3-squashfs-sysupgrade.bin",
"hash": "fd213ce0adaf8ba6434862a208a520887f8d26c6d2a8145acce15d0e243b4578"
},
{
"type": "factory",
"name": "openwrt-ath79-generic-tplink_archer-c2-v3-squashfs-factory.bin",
"hash": "034a015d3fa00be116055ee11b2a2e7bbf8ac8bf4f26273dda49f2dcb959e3ff"
}
]
},
"tplink_archer-c6-v2": {
"device_id": "tplink_archer-c6-v2",
"supported_devices": ["tplink,archer-c6-v2"],
"vendor": "TP-Link",
"model": "Archer C6",
"variant": "v2",
"target": "ath79",
"subtarget": "generic",
"images": [{
"type": "factory",
"name": "openwrt-ath79-generic-tplink_archer-c6-v2-squashfs-factory.bin",
"hash": "793473938124b55c9808e3d755fadf331dbc555e3e8d48506755b28a49260d2e"
},
{
"type": "sysupgrade",
"name": "openwrt-ath79-generic-tplink_archer-c6-v2-squashfs-sysupgrade.bin",
"hash": "ec130a2f5d3d3db5f883274584823e6018bb1d8f8f0c7177a0853cd70bc75186"
}
]
},
"tplink_archer-c7-v5": {
"device_id": "tplink_archer-c7-v5",
"supported_devices": ["tplink,archer-c7-v5", "archer-c7-v5"],
"vendor": "TP-Link",
"model": "Archer C7",
"variant": "v5",
"target": "ath79",
"subtarget": "generic",
"images": [{
"type": "sysupgrade",
"name": "openwrt-ath79-generic-tplink_archer-c7-v5-squashfs-sysupgrade.bin",
"hash": "cf673e3af5f9e985f30a3adf2c7f96e5210b036a4cc43a080c9f5c023e1bdd84"
},
{
"type": "factory",
"name": "openwrt-ath79-generic-tplink_archer-c7-v5-squashfs-factory.bin",
"hash": "523a5fe3a8616ecd52d5a5117b49f9f114c496f720fc7b8fc97c4f82bf077822"
}
]
},
"tplink_archer-c7-v1": {
"device_id": "tplink_archer-c7-v1",
"supported_devices": ["tplink,archer-c7-v1", "archer-c7"],
"vendor": "TP-Link",
"model": "Archer C7",
"variant": "v1",
"target": "ath79",
"subtarget": "generic",
"images": [{
"type": "sysupgrade",
"name": "openwrt-ath79-generic-tplink_archer-c7-v1-squashfs-sysupgrade.bin",
"hash": "13728d071e4c357ca3cbcad6064b26e0caec422191ddbc0cf109c5021bc0ed47"
},
{
"type": "factory",
"name": "openwrt-ath79-generic-tplink_archer-c7-v1-squashfs-factory.bin",
"hash": "8096c5a5decfa8af6e7195a49d71fbb3da02d1a014a553974bcd6dc59b64778c"
}
]
},
"tplink_archer-c58-v1": {
"device_id": "tplink_archer-c58-v1",
"supported_devices": ["tplink,archer-c58-v1", "archer-c58-v1"],
"vendor": "TP-Link",
"model": "Archer C58",
"variant": "v1",
"target": "ath79",
"subtarget": "generic",
"images": [{
"type": "factory",
"name": "openwrt-ath79-generic-tplink_archer-c58-v1-squashfs-factory.bin",
"hash": "a740142322a9c6e814b7c42398d47004c15db9f2aec9b913e8d9ad34e18a80ae"
},
{
"type": "sysupgrade",
"name": "openwrt-ath79-generic-tplink_archer-c58-v1-squashfs-sysupgrade.bin",
"hash": "cb36b50b646137ff05ed23ed7fbaabffeb032d0b5da214a3586d7b086088c17a"
}
]
},
"tplink_tl-wdr3600": {
"device_id": "tplink_tl-wdr3600",
"supported_devices": ["tplink,tl-wdr3600", "tl-wdr4300"],
"vendor": "TP-Link",
"model": "TL-WDR3600",
"variant": "",
"target": "ath79",
"subtarget": "generic",
"images": [{
"type": "sysupgrade",
"name": "openwrt-ath79-generic-tplink_tl-wdr3600-squashfs-sysupgrade.bin",
"hash": "fbbcbd9e1174026c001d002149a641db8ebeb459892eff442c975d9043bf2b64"
},
{
"type": "factory",
"name": "openwrt-ath79-generic-tplink_tl-wdr3600-squashfs-factory.bin",
"hash": "2d7120d2f4593d6e0a8d5d8164666aaa54c51bcbeef6f9c8c2ab1f2a75e7b728"
}
]
},
"tplink_tl-wdr4900-v2": {
"device_id": "tplink_tl-wdr4900-v2",
"supported_devices": ["tplink,tl-wdr4900-v2"],
"vendor": "TP-Link",
"model": "TL-WDR4900",
"variant": "v2",
"target": "ath79",
"subtarget": "generic",
"images": [{
"type": "factory",
"name": "openwrt-ath79-generic-tplink_tl-wdr4900-v2-squashfs-factory.bin",
"hash": "f4b096db93eb4cc504d2b98972c8a638b14a811eb0643c1fdac40d1ca0e7ae33"
},
{
"type": "sysupgrade",
"name": "openwrt-ath79-generic-tplink_tl-wdr4900-v2-squashfs-sysupgrade.bin",
"hash": "d5286e54feca1a9ce1c2d4b5d6b05b027796b755a00550629741cb9318106d75"
}
]
}
}
}

31
src/i18n.js Normal file
View file

@ -0,0 +1,31 @@
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import Backend from 'i18next-xhr-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
// not like to use this?
// have a look at the Quick start guide
// for passing in lng and translations on init
i18n
// load translation using xhr -> see /public/locales
// learn more: https://github.com/i18next/i18next-xhr-backend
.use(Backend)
// detect user language
// learn more: https://github.com/i18next/i18next-browser-languageDetector
.use(LanguageDetector)
// pass the i18n instance to react-i18next.
.use(initReactI18next)
// init i18next
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
fallbackLng: 'en',
debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
}
});
export default i18n;

14
src/index.css Normal file
View file

@ -0,0 +1,14 @@
body {
margin: 0;
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;
background-color: #F5F5F5;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}

14
src/index.js Normal file
View file

@ -0,0 +1,14 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import './i18n';
ReactDOM.render(<App />, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

135
src/serviceWorker.js Normal file
View file

@ -0,0 +1,135 @@
// This optional code is used to register a service worker.
// register() is not called by default.
// 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 subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.
// To learn more about the benefits of this model and instructions on how to
// opt-in, read https://bit.ly/CRA-PWA
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 function register(config) {
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.href);
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/facebook/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. Let's check if a service worker still exists or not.
checkValidServiceWorker(swUrl, config);
// 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://bit.ly/CRA-PWA'
);
});
} else {
// Is not localhost. Just register service worker
registerValidSW(swUrl, config);
}
});
}
}
function registerValidSW(swUrl, config) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
'New content is available and will be used when all ' +
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
);
// Execute callback
if (config && config.onUpdate) {
config.onUpdate(registration);
}
} 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.');
// Execute callback
if (config && config.onSuccess) {
config.onSuccess(registration);
}
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl, config) {
// 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.
const contentType = response.headers.get('content-type');
if (
response.status === 404 ||
(contentType != null && contentType.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, config);
}
})
.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();
});
}
}