mirror of
https://git.netzspielplatz.de/docker-multiarch/openwrt-firmware-selector.git
synced 2025-11-09 00:09:35 +00:00
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:
commit
8f68dc328e
20 changed files with 12167 additions and 0 deletions
38
src/App.js
Normal file
38
src/App.js
Normal 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
33
src/App.scss
Normal 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
9
src/App.test.js
Normal 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
25
src/components/header.js
Normal 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
|
||||
<LanguageIcon />
|
||||
</Button>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Header;
|
||||
233
src/containers/home/home.js
Normal file
233
src/containers/home/home.js
Normal 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;
|
||||
22
src/containers/home/home.scss
Normal file
22
src/containers/home/home.scss
Normal 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);
|
||||
}
|
||||
}
|
||||
26
src/containers/not-found/not-found.js
Normal file
26
src/containers/not-found/not-found.js
Normal 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
258
src/data.json
Normal 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
31
src/i18n.js
Normal 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
14
src/index.css
Normal 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
14
src/index.js
Normal 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
135
src/serviceWorker.js
Normal 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();
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue