mirror of
https://git.netzspielplatz.de/docker-multiarch/openwrt-firmware-selector.git
synced 2025-11-08 22:09:25 +00:00
Adds tabbed interface for advanced image building and reformats code
An interface tagged Basic and Advanced is introduced. Basic section shows the default openwrt images with download links. Advanced section show the options to choose which packages to use and an option to build the images is introduced. Reformatting of the code is also done according to standard code styling. Signed-off-by: Sudhanshu Gautam <me@sudhanshug.com>
This commit is contained in:
parent
7cab0bfeb5
commit
40f466153b
9 changed files with 584 additions and 401 deletions
|
|
@ -1,16 +1,16 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<base href="/openwrt-firmware-selector/">
|
<base href="/openwrt-firmware-selector/">
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8"/>
|
||||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
|
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000"/>
|
||||||
<!--
|
<!--
|
||||||
manifest.json provides metadata used when your web app is installed on a
|
manifest.json provides metadata used when your web app is installed on a
|
||||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||||
-->
|
-->
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json"/>
|
||||||
<!--
|
<!--
|
||||||
Notice the use of %PUBLIC_URL% in the tags above.
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
It will be replaced with the URL of the `public` folder during the build.
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
|
@ -21,11 +21,11 @@
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
-->
|
-->
|
||||||
<title>OpenWrt Firmware Selector Wizard </title>
|
<title>OpenWrt Firmware Selector Wizard </title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<!--
|
<!--
|
||||||
This HTML file is a template.
|
This HTML file is a template.
|
||||||
If you open it directly in the browser, you will see an empty page.
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
|
@ -34,6 +34,6 @@
|
||||||
|
|
||||||
To begin the development, run `npm start` or `yarn start`.
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
To create a production bundle, use `npm run build` or `yarn build`.
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
-->
|
-->
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
18
src/App.js
18
src/App.js
|
|
@ -1,10 +1,10 @@
|
||||||
import React, { Suspense } from 'react';
|
import React, {Suspense} from 'react';
|
||||||
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
|
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom';
|
||||||
import './App.scss';
|
import './App.scss';
|
||||||
|
|
||||||
import { createMuiTheme } from '@material-ui/core/styles';
|
import {createMuiTheme} from '@material-ui/core/styles';
|
||||||
import { ThemeProvider } from '@material-ui/styles';
|
import {ThemeProvider} from '@material-ui/styles';
|
||||||
import Header from './components/header.js'
|
import Header from './components/header.js';
|
||||||
import Home from './containers/home/home';
|
import Home from './containers/home/home';
|
||||||
import NotFound from './containers/not-found/not-found';
|
import NotFound from './containers/not-found/not-found';
|
||||||
import LinearProgress from '@material-ui/core/LinearProgress';
|
import LinearProgress from '@material-ui/core/LinearProgress';
|
||||||
|
|
@ -24,14 +24,14 @@ function App() {
|
||||||
return (
|
return (
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<Suspense fallback={
|
<Suspense fallback={
|
||||||
<LinearProgress />
|
<LinearProgress/>
|
||||||
}>
|
}>
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<Header></Header>
|
<Header/>
|
||||||
<Router>
|
<Router>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path="" component={Home}></Route>
|
<Route path="" component={Home}/>
|
||||||
<Route default component={NotFound}></Route>
|
<Route default component={NotFound}/>
|
||||||
</Switch>
|
</Switch>
|
||||||
</Router>
|
</Router>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,25 @@
|
||||||
import React from "react";
|
import React from 'react';
|
||||||
import LanguageIcon from '@material-ui/icons/Language';
|
import LanguageIcon from '@material-ui/icons/Language';
|
||||||
import { Toolbar, Typography, AppBar, Button, Radio, RadioGroup, FormControlLabel, FormControl, FormLabel, Popper, Fade, Paper } from '@material-ui/core';
|
import {
|
||||||
import { useTranslation } from 'react-i18next';
|
AppBar,
|
||||||
|
Button,
|
||||||
|
Fade,
|
||||||
|
FormControl,
|
||||||
|
FormControlLabel,
|
||||||
|
FormLabel,
|
||||||
|
Paper,
|
||||||
|
Popper,
|
||||||
|
Radio,
|
||||||
|
RadioGroup,
|
||||||
|
Toolbar,
|
||||||
|
Typography,
|
||||||
|
} from '@material-ui/core';
|
||||||
|
import {useTranslation} from 'react-i18next';
|
||||||
import i18next from 'i18next';
|
import i18next from 'i18next';
|
||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
|
|
||||||
const { t, i18n } = useTranslation();
|
const {t, i18n} = useTranslation();
|
||||||
|
|
||||||
const [value, setValue] = React.useState('en');
|
const [value, setValue] = React.useState('en');
|
||||||
const [anchorEl, setAnchorEl] = React.useState(null);
|
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||||
|
|
@ -20,7 +33,7 @@ export default function Header() {
|
||||||
const openChangeLanguagePopper = event => {
|
const openChangeLanguagePopper = event => {
|
||||||
setValue(i18next.language.substring(0, 2));
|
setValue(i18next.language.substring(0, 2));
|
||||||
setAnchorEl(anchorEl ? null : event.currentTarget);
|
setAnchorEl(anchorEl ? null : event.currentTarget);
|
||||||
}
|
};
|
||||||
|
|
||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl);
|
||||||
const id = open ? 'simple-popper' : undefined;
|
const id = open ? 'simple-popper' : undefined;
|
||||||
|
|
@ -28,33 +41,37 @@ export default function Header() {
|
||||||
return (
|
return (
|
||||||
<AppBar position="static">
|
<AppBar position="static">
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<Typography edge="start" variant="h6">{t('OpenWrt Firmware Selector Wizard')}</Typography>
|
<Typography edge="start" variant="h6">{t(
|
||||||
<div style={{flexGrow: 1}}></div>
|
'OpenWrt Firmware Selector Wizard')}</Typography>
|
||||||
<Button aria-describedby={id} color="secondary" variant="contained" onClick={openChangeLanguagePopper}>
|
<div style={{flexGrow: 1}} />
|
||||||
|
<Button aria-describedby={id} color="secondary" variant="contained"
|
||||||
|
onClick={openChangeLanguagePopper} href="#">
|
||||||
{t('Change Language')}
|
{t('Change Language')}
|
||||||
<LanguageIcon />
|
<LanguageIcon/>
|
||||||
</Button>
|
</Button>
|
||||||
<Popper
|
<Popper
|
||||||
id={id}
|
id={id}
|
||||||
open={open}
|
open={open}
|
||||||
anchorEl={anchorEl}
|
anchorEl={anchorEl}
|
||||||
transition
|
transition
|
||||||
disablePortal = {true}
|
disablePortal={true}
|
||||||
>
|
>
|
||||||
{({ TransitionProps }) => (
|
{({TransitionProps}) => (
|
||||||
<Fade {...TransitionProps} timeout={350}>
|
<Fade {...TransitionProps} timeout={350}>
|
||||||
<Paper className="language-selector-popper">
|
<Paper className="language-selector-popper">
|
||||||
<FormControl component="fieldset">
|
<FormControl component="fieldset">
|
||||||
<FormLabel component="legend">Change Language</FormLabel>
|
<FormLabel component="legend">Change Language</FormLabel>
|
||||||
<br />
|
<br/>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
aria-label="Language"
|
aria-label="Language"
|
||||||
name="language"
|
name="language"
|
||||||
value={value}
|
value={value}
|
||||||
onChange={changeLanguage}
|
onChange={changeLanguage}
|
||||||
>
|
>
|
||||||
<FormControlLabel value="en" control={<Radio />} label={t('English')} />
|
<FormControlLabel value="en" control={<Radio/>}
|
||||||
<FormControlLabel value="de" control={<Radio />} label={t('German')} />
|
label={t('English')}/>
|
||||||
|
<FormControlLabel value="de" control={<Radio/>}
|
||||||
|
label={t('German')}/>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,38 @@
|
||||||
import React from "react";
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
|
AppBar,
|
||||||
|
Button,
|
||||||
|
Checkbox,
|
||||||
|
CircularProgress,
|
||||||
|
ClickAwayListener,
|
||||||
Container,
|
Container,
|
||||||
Paper,
|
Dialog,
|
||||||
Typography,
|
DialogActions,
|
||||||
InputAdornment,
|
DialogContent,
|
||||||
|
DialogContentText,
|
||||||
|
DialogTitle,
|
||||||
FormControl,
|
FormControl,
|
||||||
TextField,
|
FormControlLabel,
|
||||||
|
FormGroup,
|
||||||
|
Grid,
|
||||||
|
InputAdornment,
|
||||||
List,
|
List,
|
||||||
ListItem,
|
ListItem,
|
||||||
ListItemText,
|
ListItemText,
|
||||||
CircularProgress,
|
Paper,
|
||||||
Button,
|
Tab,
|
||||||
Grid,
|
Tabs,
|
||||||
ClickAwayListener,
|
TextField,
|
||||||
ExpansionPanel,
|
Typography,
|
||||||
ExpansionPanelSummary,
|
} from '@material-ui/core';
|
||||||
ExpansionPanelDetails,
|
import {fade, makeStyles} from '@material-ui/core/styles';
|
||||||
Checkbox,
|
|
||||||
FormGroup,
|
|
||||||
FormControlLabel } from "@material-ui/core";
|
|
||||||
import { fade, makeStyles } from '@material-ui/core/styles';
|
|
||||||
|
|
||||||
import SearchIcon from '@material-ui/icons/Search';
|
import SearchIcon from '@material-ui/icons/Search';
|
||||||
import CloudDownloadIcon from '@material-ui/icons/CloudDownload';
|
import CloudDownloadIcon from '@material-ui/icons/CloudDownload';
|
||||||
import WarningIcon from '@material-ui/icons/Warning';
|
import WarningIcon from '@material-ui/icons/Warning';
|
||||||
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
import BuildIcon from '@material-ui/icons/Build';
|
||||||
import './home.scss';
|
import './home.scss';
|
||||||
import { withTranslation } from 'react-i18next';
|
import {withTranslation} from 'react-i18next';
|
||||||
import FuzzySet from 'fuzzyset.js';
|
import FuzzySet from 'fuzzyset.js';
|
||||||
|
|
||||||
const useStylesSearch = makeStyles(theme => ({
|
const useStylesSearch = makeStyles(theme => ({
|
||||||
|
|
@ -60,17 +66,54 @@ function SearchTextField(props) {
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
InputProps={
|
InputProps={
|
||||||
{ classes,
|
{
|
||||||
|
classes,
|
||||||
endAdornment: (
|
endAdornment: (
|
||||||
<InputAdornment position="start">
|
<InputAdornment position="start">
|
||||||
<SearchIcon className={classes.label} />
|
<SearchIcon className={classes.label}/>
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
)
|
),
|
||||||
}
|
}
|
||||||
} {...props} />
|
} {...props} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function TabContainer({children, dir}) {
|
||||||
|
return (
|
||||||
|
<Typography component="div" dir={dir} style={{padding: '20px 0 0'}}>
|
||||||
|
{children}
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function AlertDialog({open, handleClose, text, title, t}) {
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={open}
|
||||||
|
onClose={() => handleClose(-1)}
|
||||||
|
aria-labelledby="alert-dialog-title"
|
||||||
|
aria-describedby="alert-dialog-description"
|
||||||
|
>
|
||||||
|
<DialogTitle
|
||||||
|
id="alert-dialog-title">{t(title)}</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogContentText id="alert-dialog-description">
|
||||||
|
{t(text)}
|
||||||
|
</DialogContentText>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={() => handleClose(1)} color="primary">
|
||||||
|
{t('Build')} <BuildIcon/>
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => handleClose(0)} color="secondary"
|
||||||
|
variant="contained" autoFocus>
|
||||||
|
{t('Cancel')}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
class Home extends React.Component {
|
class Home extends React.Component {
|
||||||
|
|
||||||
deviceNames = [];
|
deviceNames = [];
|
||||||
|
|
@ -87,17 +130,51 @@ class Home extends React.Component {
|
||||||
query: '',
|
query: '',
|
||||||
downloading: false,
|
downloading: false,
|
||||||
packages: {},
|
packages: {},
|
||||||
|
release_version_number: '',
|
||||||
};
|
};
|
||||||
fuzzySet;
|
fuzzySet;
|
||||||
packages = ["opkg","ip6tables","odhcp6c","base-files","mtd","fstools","kmod-leds-gpio","busybox","wpad-mini","kmod-gpio-button-hotplug","kmod-mt76","logd","swconfig","dnsmasq","dropbear","ppp","netifd","ppp-mod-pppoe","uci","libc","uclient-fetch","kmod-ipt-offload","libgcc","odhcpd-ipv6only","iptables","firewall","luci"];
|
basicInterface = 0;
|
||||||
|
confirmingBuild = false;
|
||||||
|
packages = [
|
||||||
|
'opkg',
|
||||||
|
'ip6tables',
|
||||||
|
'odhcp6c',
|
||||||
|
'base-files',
|
||||||
|
'mtd',
|
||||||
|
'fstools',
|
||||||
|
'kmod-leds-gpio',
|
||||||
|
'busybox',
|
||||||
|
'wpad-mini',
|
||||||
|
'kmod-gpio-button-hotplug',
|
||||||
|
'kmod-mt76',
|
||||||
|
'logd',
|
||||||
|
'swconfig',
|
||||||
|
'dnsmasq',
|
||||||
|
'dropbear',
|
||||||
|
'ppp',
|
||||||
|
'netifd',
|
||||||
|
'ppp-mod-pppoe',
|
||||||
|
'uci',
|
||||||
|
'libc',
|
||||||
|
'uclient-fetch',
|
||||||
|
'kmod-ipt-offload',
|
||||||
|
'libgcc',
|
||||||
|
'odhcpd-ipv6only',
|
||||||
|
'iptables',
|
||||||
|
'firewall',
|
||||||
|
'luci'];
|
||||||
|
|
||||||
getDevicesData = () => fetch('https://chef.libremesh.org/download/json/devices.json').then(res => res.json());
|
getDevicesData = () => fetch(
|
||||||
getDeviceData = (device_id) => fetch('https://chef.libremesh.org/download/json/' + device_id + '.json').then(res => res.json());
|
'https://chef.libremesh.org/download/json/devices.json')
|
||||||
|
.then(res => res.json());
|
||||||
|
getDeviceData = (device_id) => fetch(
|
||||||
|
'https://chef.libremesh.org/download/json/' + device_id + '.json')
|
||||||
|
.then(res => res.json());
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.getDevicesData().then(data => {
|
this.getDevicesData().then(data => {
|
||||||
Object.keys(data['devices']).forEach((device_id) => {
|
Object.keys(data['devices']).forEach((device_id) => {
|
||||||
var device_name = data['devices'][device_id];
|
const device_name = data['devices'][device_id];
|
||||||
this.deviceNames.push(device_name);
|
this.deviceNames.push(device_name);
|
||||||
this.deviceNamesID[device_name] = device_id;
|
this.deviceNamesID[device_name] = device_id;
|
||||||
});
|
});
|
||||||
|
|
@ -105,9 +182,10 @@ class Home extends React.Component {
|
||||||
this.setState({
|
this.setState({
|
||||||
devices: data['devices'],
|
devices: data['devices'],
|
||||||
devicesLoaded: true,
|
devicesLoaded: true,
|
||||||
|
release_version_number: data['version_number'],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
var packages = {};
|
let packages = {};
|
||||||
this.packages.forEach((package_name) => {
|
this.packages.forEach((package_name) => {
|
||||||
packages[package_name] = true;
|
packages[package_name] = true;
|
||||||
});
|
});
|
||||||
|
|
@ -118,7 +196,7 @@ class Home extends React.Component {
|
||||||
|
|
||||||
selectDevice = (device_name) => {
|
selectDevice = (device_name) => {
|
||||||
if (device_name != null) {
|
if (device_name != null) {
|
||||||
var device_id = this.deviceNamesID[device_name];
|
const device_id = this.deviceNamesID[device_name];
|
||||||
this.setState({
|
this.setState({
|
||||||
showDeviceData: true,
|
showDeviceData: true,
|
||||||
showSearch: false,
|
showSearch: false,
|
||||||
|
|
@ -132,7 +210,7 @@ class Home extends React.Component {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
search = (event) => {
|
search = (event) => {
|
||||||
const query = event.target.value;
|
const query = event.target.value;
|
||||||
|
|
@ -142,9 +220,9 @@ class Home extends React.Component {
|
||||||
showSearch: false,
|
showSearch: false,
|
||||||
});
|
});
|
||||||
const deviceNames = this.fuzzySet.get(query, undefined, 0);
|
const deviceNames = this.fuzzySet.get(query, undefined, 0);
|
||||||
var searchResults = [];
|
let searchResults = [];
|
||||||
if (deviceNames != null) {
|
if (deviceNames != null) {
|
||||||
for (var i = 0; i < deviceNames.length && i < 6; i++) {
|
for (let i = 0; i < deviceNames.length && i < 6; i++) {
|
||||||
searchResults.push(deviceNames[i][1]);
|
searchResults.push(deviceNames[i][1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -152,36 +230,50 @@ class Home extends React.Component {
|
||||||
searchResults,
|
searchResults,
|
||||||
showSearch: query.length > 0,
|
showSearch: query.length > 0,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
hideSearchResults = () => {
|
hideSearchResults = () => {
|
||||||
console.log("bahar")
|
|
||||||
this.setState({
|
this.setState({
|
||||||
showSearch: false
|
showSearch: false,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
|
changeInterface = (e, val) => {
|
||||||
|
this.basicInterface = val;
|
||||||
|
};
|
||||||
|
|
||||||
packageSettingChange = (event, package_name) => {
|
packageSettingChange = (event, package_name) => {
|
||||||
var packages = this.state.packages;
|
let packages = this.state.packages;
|
||||||
packages[package_name] = event.target.checked;
|
packages[package_name] = event.target.checked;
|
||||||
this.setState({
|
this.setState({
|
||||||
packages,
|
packages,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
downlodingImageIndicatorShow = (i) => {
|
downloadingImageIndicatorShow = (i) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
downloading: true
|
downloading: true,
|
||||||
});
|
});
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
downloading: false
|
downloading: false,
|
||||||
});
|
});
|
||||||
}, 1000)
|
}, 1000);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
closeConfirmBuildDialog = (v) => {
|
||||||
|
this.confirmingBuild = false;
|
||||||
|
console.log(v);
|
||||||
|
};
|
||||||
|
|
||||||
|
openConfirmBuildDialog = () => {
|
||||||
|
this.confirmingBuild = true;
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const warning432 = this.state.showDeviceData && parseInt((this.state.device['image_size'] || '').slice(0, -1)) <= 4000 && (
|
const warning432 = this.state.showDeviceData &&
|
||||||
|
parseInt(
|
||||||
|
(this.state.device['image_size'] || '').slice(0, -1)) <= 4000 && (
|
||||||
<Paper className="warning-432" elevation={0}>
|
<Paper className="warning-432" elevation={0}>
|
||||||
<Grid
|
<Grid
|
||||||
container
|
container
|
||||||
|
|
@ -190,16 +282,17 @@ class Home extends React.Component {
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
>
|
>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
<WarningIcon className="icon" />
|
<WarningIcon className="icon"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs>
|
<Grid item xs>
|
||||||
Devices with ≤4MB flash and/or ≤32MB ram will work but they will be very limited (usually they can't install or run additional packages) because they have low RAM and flash space. Consider this when choosing a device to buy, or when deciding to flash OpenWrt on your device because it is listed as supported.
|
{this.props.t(
|
||||||
|
'Devices with ≤4MB flash and/or ≤32MB ram will work but they will be very limited (usually they can\'t install or run additional packages) because they have low RAM and flash space. Consider this when choosing a device to buy, or when deciding to flash OpenWrt on your device because it is listed as supported.')}
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
const notLoaded = (
|
const notLoaded = (
|
||||||
<CircularProgress />
|
<CircularProgress/>
|
||||||
);
|
);
|
||||||
const onLoad = (
|
const onLoad = (
|
||||||
<>
|
<>
|
||||||
|
|
@ -207,9 +300,10 @@ class Home extends React.Component {
|
||||||
{this.props.t('Download OpenWrt firmware for your device!')}
|
{this.props.t('Download OpenWrt firmware for your device!')}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography>
|
<Typography>
|
||||||
{this.props.t('Please use the input below to download firmware for your device!')}
|
{this.props.t(
|
||||||
|
'Please use the input below to download firmware for your device!')}
|
||||||
</Typography>
|
</Typography>
|
||||||
<br />
|
<br/>
|
||||||
<ClickAwayListener onClickAway={this.hideSearchResults}>
|
<ClickAwayListener onClickAway={this.hideSearchResults}>
|
||||||
<div className="search-container">
|
<div className="search-container">
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
|
|
@ -237,7 +331,7 @@ class Home extends React.Component {
|
||||||
<div>
|
<div>
|
||||||
{res}
|
{res}
|
||||||
</div>
|
</div>
|
||||||
} />
|
}/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|
@ -247,10 +341,12 @@ class Home extends React.Component {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
(this.state.searchResults.length === 0 && this.state.showSearch) && (
|
(this.state.searchResults.length === 0 &&
|
||||||
|
this.state.showSearch) && (
|
||||||
<Paper elevation={4} className="search-results">
|
<Paper elevation={4} className="search-results">
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<ListItemText primary={this.props.t('No results')}></ListItemText>
|
<ListItemText
|
||||||
|
primary={this.props.t('No results')}/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</Paper>
|
</Paper>
|
||||||
)
|
)
|
||||||
|
|
@ -260,73 +356,56 @@ class Home extends React.Component {
|
||||||
{
|
{
|
||||||
this.state.showDeviceData && !this.state.deviceLoaded && (
|
this.state.showDeviceData && !this.state.deviceLoaded && (
|
||||||
<>
|
<>
|
||||||
<br />
|
<br/>
|
||||||
{ notLoaded }
|
{notLoaded}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
this.state.showDeviceData && this.state.deviceLoaded && (
|
this.state.showDeviceData && this.state.deviceLoaded && (
|
||||||
<>
|
<>
|
||||||
{ warning432 }
|
{warning432}
|
||||||
<br />
|
<br/>
|
||||||
<Grid container className="device-info">
|
<Grid container className="device-info">
|
||||||
<Grid item xs>
|
<Grid item xs>
|
||||||
<b>Name: </b> {this.state.device['title']}
|
<b>{this.props.t(
|
||||||
|
'Name')}: </b> {this.state.device['title']}({this.state.device['target']}/{this.state.device['subtarget']})
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs>
|
<Grid item xs>
|
||||||
<b>Target: </b> {this.state.device['target']}
|
<b>{this.props.t(
|
||||||
</Grid>
|
'Release Version')}: </b> {this.state.release_version_number}
|
||||||
<Grid item xs>
|
|
||||||
<b>Subtarget: </b> {this.state.device['subtarget']}
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs>
|
|
||||||
<b>Image Size: </b> {this.state.device['image_size']}
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<ExpansionPanel elevation={0} className="advanced-settings">
|
<AppBar className="interface-switch-bar" position="relative"
|
||||||
<ExpansionPanelSummary
|
elevation={0}>
|
||||||
expandIcon={<ExpandMoreIcon />}
|
<Tabs value={this.basicInterface}
|
||||||
aria-controls="panel1a-content"
|
onChange={this.changeInterface}>
|
||||||
id="panel1a-header"
|
<Tab className="interface-switch"
|
||||||
>
|
label={this.props.t('Basic')}/>
|
||||||
<Typography>Customize Packages (<WarningIcon className="icon" /> for advanced users)</Typography>
|
<Tab className="interface-switch"
|
||||||
</ExpansionPanelSummary>
|
label={this.props.t('Advanced')}/>
|
||||||
<ExpansionPanelDetails className="options">
|
</Tabs>
|
||||||
<FormGroup row>
|
</AppBar>
|
||||||
{
|
{
|
||||||
this.packages.map((package_name, i) => {
|
this.basicInterface === 0 ? (
|
||||||
return (
|
<TabContainer>
|
||||||
<FormControlLabel
|
|
||||||
key={i}
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
onChange={(event) => this.packageSettingChange(event, package_name)}
|
|
||||||
value={this.state.packages[package_name]}
|
|
||||||
checked={this.state.packages[package_name]}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label={package_name}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</FormGroup>
|
|
||||||
</ExpansionPanelDetails>
|
|
||||||
</ExpansionPanel>
|
|
||||||
{
|
{
|
||||||
this.state.device.images.map((image, i) => {
|
this.state.device.images.map((image, i) => {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
key={i}
|
key={i}
|
||||||
className="download-button"
|
className="download-button"
|
||||||
href={"http://downloads.openwrt.org/snapshots/targets/" + this.state.device.target + "/" + this.state.device.subtarget + "/" + image.name}
|
href={'http://downloads.openwrt.org/snapshots/targets/' +
|
||||||
|
this.state.device.target + '/' +
|
||||||
|
this.state.device.subtarget + '/' +
|
||||||
|
image.name}
|
||||||
color="primary"
|
color="primary"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={() => this.downlodingImageIndicatorShow()}
|
onClick={() => this.downloadingImageIndicatorShow()}
|
||||||
>
|
>
|
||||||
<CloudDownloadIcon className="download-icon" />
|
<CloudDownloadIcon
|
||||||
{ image.type }
|
className="download-icon"/>
|
||||||
|
{image.type}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|
@ -334,7 +413,41 @@ class Home extends React.Component {
|
||||||
|
|
||||||
{
|
{
|
||||||
this.state.downloading && (
|
this.state.downloading && (
|
||||||
<CircularProgress size={20} />
|
<CircularProgress size={20}/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</TabContainer>
|
||||||
|
) : (
|
||||||
|
<TabContainer>
|
||||||
|
<FormGroup row>
|
||||||
|
{
|
||||||
|
this.packages.map((package_name, i) => {
|
||||||
|
return (
|
||||||
|
<FormControlLabel
|
||||||
|
className="package"
|
||||||
|
key={i}
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
onChange={(event) => this.packageSettingChange(
|
||||||
|
event, package_name)}
|
||||||
|
value={this.state.packages[package_name]}
|
||||||
|
checked={this.state.packages[package_name]}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={package_name}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</FormGroup>
|
||||||
|
<br/>
|
||||||
|
<Button variant="outlined" color="primary"
|
||||||
|
onClick={this.openConfirmBuildDialog}>
|
||||||
|
<BuildIcon/>
|
||||||
|
|
||||||
|
{this.props.t('Build')}
|
||||||
|
</Button>
|
||||||
|
</TabContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
</>
|
</>
|
||||||
|
|
@ -345,7 +458,12 @@ class Home extends React.Component {
|
||||||
return (
|
return (
|
||||||
<Container className="home-container">
|
<Container className="home-container">
|
||||||
<Paper className="home-container-paper">
|
<Paper className="home-container-paper">
|
||||||
{ this.state.devicesLoaded ? onLoad : notLoaded }
|
<AlertDialog handleClose={this.closeConfirmBuildDialog}
|
||||||
|
open={this.confirmingBuild}
|
||||||
|
text="Building image requires computation resources, so we would request you to check if this selection is what you want"
|
||||||
|
title="Please confirm that you want to perform this action"
|
||||||
|
t={this.props.t}/>
|
||||||
|
{this.state.devicesLoaded ? onLoad : notLoaded}
|
||||||
</Paper>
|
</Paper>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,61 @@
|
||||||
.home-container {
|
.home-container {
|
||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
|
|
||||||
.home-container-paper {
|
.home-container-paper {
|
||||||
padding: 30px;
|
padding: 30px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|
||||||
.warning-432 {
|
.warning-432 {
|
||||||
background-color: #f0f0f0;
|
background-color: #f0f0f0;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
border: 1px solid #e3e3e3;
|
border: 1px solid #e3e3e3;
|
||||||
color: #666;
|
color: #666;
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
margin: 0 20px 0 10px;
|
margin: 0 20px 0 10px;
|
||||||
color: #F9A825
|
color: #F9A825
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-info {
|
.device-info {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-label {
|
.search-label {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-results {
|
.search-results {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.MuiTypography-h4 {
|
.MuiTypography-h4 {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.download-button {
|
.download-button {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
|
||||||
.download-icon {
|
.download-icon {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.interface-switch-bar {
|
||||||
|
border: 1px solid #e3e3e3;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #fff;
|
||||||
|
z-index: 9;
|
||||||
|
overflow: hidden;
|
||||||
|
.interface-switch {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
.advanced-settings {
|
.advanced-settings {
|
||||||
border: 1px solid #999;
|
border: 1px solid #999;
|
||||||
background-color: #f0f0f0;
|
background-color: #f0f0f0;
|
||||||
|
|
@ -44,14 +63,33 @@
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
vertical-align: text-top;
|
vertical-align: text-top;
|
||||||
}
|
}
|
||||||
|
|
||||||
.options {
|
.options {
|
||||||
padding-top: 30px;
|
padding-top: 30px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.package {
|
||||||
|
padding-right: 15px;
|
||||||
|
padding-left: 5px;
|
||||||
|
border-radius: 30px;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.package:hover {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.package:focus, .package:active {
|
||||||
|
transition: .2s;
|
||||||
|
background-color: darken(#f0f0f0, 15%);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -59,9 +97,11 @@
|
||||||
.device-table {
|
.device-table {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
td {
|
td {
|
||||||
padding: 20px 30px;
|
padding: 20px 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
tr:nth-child(odd) {
|
tr:nth-child(odd) {
|
||||||
background-color: rgba(104, 74, 238, 0.07);
|
background-color: rgba(104, 74, 238, 0.07);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import React from "react";
|
import React from 'react';
|
||||||
import { Typography, Paper, Container } from "@material-ui/core";
|
import {Container, Paper, Typography} from '@material-ui/core';
|
||||||
import { makeStyles } from '@material-ui/core/styles';
|
import {makeStyles} from '@material-ui/core/styles';
|
||||||
|
|
||||||
const page404Styles = makeStyles(theme => ({
|
const page404Styles = makeStyles(theme => ({
|
||||||
root: {
|
root: {
|
||||||
|
|
@ -8,7 +8,6 @@ const page404Styles = makeStyles(theme => ({
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
export default function NotFound() {
|
export default function NotFound() {
|
||||||
var classes = page404Styles();
|
var classes = page404Styles();
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
21
src/i18n.js
21
src/i18n.js
|
|
@ -1,36 +1,27 @@
|
||||||
import i18n from 'i18next';
|
import i18n from 'i18next';
|
||||||
import { initReactI18next } from 'react-i18next';
|
import {initReactI18next} from 'react-i18next';
|
||||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||||
import translationEN from './locales/en.json';
|
import translationEN from './locales/en.json';
|
||||||
import translationDE from './locales/de.json';
|
import translationDE from './locales/de.json';
|
||||||
|
|
||||||
|
|
||||||
const resources = {
|
const resources = {
|
||||||
en: {
|
en: {
|
||||||
translation: translationEN
|
translation: translationEN,
|
||||||
},
|
},
|
||||||
de: {
|
de: {
|
||||||
translation: translationDE
|
translation: translationDE,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
i18n
|
i18n.use(LanguageDetector)
|
||||||
// detect user language
|
|
||||||
// learn more: https://github.com/i18next/i18next-browser-languageDetector
|
|
||||||
.use(LanguageDetector)
|
|
||||||
// pass the i18n instance to react-i18next.
|
|
||||||
.use(initReactI18next)
|
.use(initReactI18next)
|
||||||
// init i18next
|
|
||||||
// for all options read: https://www.i18next.com/overview/configuration-options
|
|
||||||
.init({
|
.init({
|
||||||
resources,
|
resources,
|
||||||
fallbackLng: 'en',
|
fallbackLng: 'en',
|
||||||
debug: true,
|
debug: true,
|
||||||
|
|
||||||
interpolation: {
|
interpolation: {
|
||||||
escapeValue: false, // not needed for react as it escapes by default
|
escapeValue: false, // not needed for react as it escapes by default
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
export default i18n;
|
export default i18n;
|
||||||
|
|
@ -8,6 +8,15 @@
|
||||||
"Model": "Modell",
|
"Model": "Modell",
|
||||||
"Vendor": "Verkäufer",
|
"Vendor": "Verkäufer",
|
||||||
"Variant": "Variante",
|
"Variant": "Variante",
|
||||||
|
"Name": "Name",
|
||||||
|
"Release Version" : "Veröffentlichungsversion",
|
||||||
|
"Basic": "Basic",
|
||||||
|
"Advanced": "Fortgeschritten",
|
||||||
|
"Build": "Bauen",
|
||||||
|
"Cancel": "Abbrechen",
|
||||||
|
"Please confirm that you want to perform this action": "Bitte bestätigen Sie, dass Sie diese Aktion ausführen möchten",
|
||||||
|
"Building image requires computation resources, so we would request you to check if this selection is what you want": "Das Erstellen eines Image erfordert Rechenressourcen. Wir bitten Sie daher, zu prüfen, ob diese Auswahl Ihren Wünschen entspricht",
|
||||||
|
"Devices with ≤4MB flash and/or ≤32MB ram will work but they will be very limited (usually they can't install or run additional packages) because they have low RAM and flash space. Consider this when choosing a device to buy, or when deciding to flash OpenWrt on your device because it is listed as supported.": "Geräte mit ≤4MB Flash und / oder ≤32MB RAM funktionieren, sind jedoch sehr eingeschränkt (normalerweise können sie keine zusätzlichen Pakete installieren oder ausführen), da sie über wenig RAM und Flash-Speicher verfügen. Berücksichtigen Sie dies, wenn Sie ein Gerät zum Kaufen auswählen oder wenn Sie OpenWrt auf Ihrem Gerät flashen, da es als unterstützt aufgeführt ist Zusätzliche Pakete können nicht installiert oder ausgeführt werden, da sie über wenig RAM und wenig Flash-Speicher verfügen. Berücksichtigen Sie dies, wenn Sie ein Gerät zum Kaufen auswählen oder wenn Sie sich entscheiden, OpenWrt auf Ihrem Gerät zu flashen, da es als unterstützt aufgeführt ist.",
|
||||||
"OpenWrt Firmware Selector Wizard": "OpenWrt Firmware Selector Wizard",
|
"OpenWrt Firmware Selector Wizard": "OpenWrt Firmware Selector Wizard",
|
||||||
"Download OpenWrt firmware for your device!": "Laden Sie die OpenWrt-Firmware für Ihr Gerät herunter!",
|
"Download OpenWrt firmware for your device!": "Laden Sie die OpenWrt-Firmware für Ihr Gerät herunter!",
|
||||||
"Please use the input below to download firmware for your device!": "Bitte benutzen Sie den unten stehenden Eingang, um die Firmware für Ihr Gerät herunterzuladen!"
|
"Please use the input below to download firmware for your device!": "Bitte benutzen Sie den unten stehenden Eingang, um die Firmware für Ihr Gerät herunterzuladen!"
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,15 @@
|
||||||
"Model": "Model",
|
"Model": "Model",
|
||||||
"Vendor": "Vendor",
|
"Vendor": "Vendor",
|
||||||
"Variant": "Variant",
|
"Variant": "Variant",
|
||||||
|
"Name": "Name",
|
||||||
|
"Release Version" : "Release Version",
|
||||||
|
"Basic": "Basic",
|
||||||
|
"Advanced": "Advanced",
|
||||||
|
"Build": "Build",
|
||||||
|
"Cancel": "Cancel",
|
||||||
|
"Please confirm that you want to perform this action": "Please confirm that you want to perform this action",
|
||||||
|
"Building image requires computation resources, so we would request you to check if this selection is what you want": "Building image requires computation resources, so we would request you to check if this selection is what you want",
|
||||||
|
"Devices with ≤4MB flash and/or ≤32MB ram will work but they will be very limited (usually they can't install or run additional packages) because they have low RAM and flash space. Consider this when choosing a device to buy, or when deciding to flash OpenWrt on your device because it is listed as supported.": "Devices with ≤4MB flash and/or ≤32MB ram will work but they will be very limited (usually they can't install or run additional packages) because they have low RAM and flash space. Consider this when choosing a device to buy, or when deciding to flash OpenWrt on your device because it is listed as supported.",
|
||||||
"OpenWrt Firmware Selector Wizard": "OpenWrt Firmware Selector Wizard",
|
"OpenWrt Firmware Selector Wizard": "OpenWrt Firmware Selector Wizard",
|
||||||
"Download OpenWrt firmware for your device!": "Download OpenWrt firmware for your device!",
|
"Download OpenWrt firmware for your device!": "Download OpenWrt firmware for your device!",
|
||||||
"Please use the input below to download firmware for your device!": "Please use the input below to download firmware for your device!"
|
"Please use the input below to download firmware for your device!": "Please use the input below to download firmware for your device!"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue