add ASU compatibility

This commit is contained in:
Sudhanshu Gautam 2021-03-21 18:40:44 +05:30
parent a0aedf1563
commit 7cdb5206af
22 changed files with 2282 additions and 1427 deletions

View file

@ -1,42 +1,31 @@
module.exports = { module.exports = {
extends: [ parser: '@typescript-eslint/parser', // Specifies the ESLint parserx
'airbnb-typescript', plugins: ['react-hooks', 'prettier'],
'airbnb/hooks', settings: {
'plugin:@typescript-eslint/recommended', react: {
'plugin:jest/recommended', version: 'detect', // Tells eslint-plugin-react to automatically detect the version of React to use
'prettier',
'prettier/react',
'prettier/@typescript-eslint',
'plugin:prettier/recommended',
],
plugins: ['react', '@typescript-eslint', 'jest'],
env: {
browser: true,
es6: true,
jest: true,
},
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly',
},
root: true,
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true,
}, },
ecmaVersion: 2018,
sourceType: 'module',
project: './tsconfig.json',
}, },
extends: [
'plugin:react/recommended',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended', // Make sure this is always the last configuration in the extends array.
'plugin:jest/recommended',
],
rules: { rules: {
'linebreak-style': 'off', // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
// Consider turning on eventually... lots of warnings though.
'max-len': ['error', 100],
'@typescript-eslint/explicit-module-boundary-types': 'off',
'react/react-in-jsx-scope': 'off',
// This rule is redundent when using arrow functions and we should only use arrow functions
'react/prop-types': 'off', 'react/prop-types': 'off',
'import/no-extraneous-dependencies': ['error', { devDependencies: true }],
'prettier/prettier': [ 'prettier/prettier': [
'error', 'error',
{ {
endOfLine: 'auto', trailingComma: 'all',
arrowParens: 'always',
printWidth: 100,
}, },
], ],
}, },

View file

@ -13,12 +13,14 @@
"i18next": "^17.0.4", "i18next": "^17.0.4",
"i18next-browser-languagedetector": "^3.0.1", "i18next-browser-languagedetector": "^3.0.1",
"lodash": "^4.17.20", "lodash": "^4.17.20",
"luxon": "^1.26.0",
"match-sorter": "^6.1.0", "match-sorter": "^6.1.0",
"react": "^16.8.6", "react": "^16.8.6",
"react-dom": "^16.8.6", "react-dom": "^16.8.6",
"react-i18next": "^10.11.2", "react-i18next": "^10.11.2",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-scripts": "4.0.1", "react-scripts": "4.0.1",
"react-svg": "^12.1.0",
"react-use": "^15.3.8", "react-use": "^15.3.8",
"web-vitals": "^1.0.1" "web-vitals": "^1.0.1"
}, },
@ -50,6 +52,7 @@
"@testing-library/react": "^11.2.3", "@testing-library/react": "^11.2.3",
"@types/jest": "^26.0.20", "@types/jest": "^26.0.20",
"@types/lodash": "^4.14.167", "@types/lodash": "^4.14.167",
"@types/luxon": "^1.26.2",
"@types/react": "^17.0.0", "@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0", "@types/react-dom": "^17.0.0",
"@types/react-router-dom": "^5.1.7", "@types/react-router-dom": "^5.1.7",
@ -57,15 +60,10 @@
"@typescript-eslint/parser": "^4.12.0", "@typescript-eslint/parser": "^4.12.0",
"axios-mock-adapter": "^1.19.0", "axios-mock-adapter": "^1.19.0",
"eslint": "7.2.0", "eslint": "7.2.0",
"eslint-config-airbnb": "18.2.1",
"eslint-config-airbnb-typescript": "^12.0.0",
"eslint-config-prettier": "^7.1.0", "eslint-config-prettier": "^7.1.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jest": "^24.1.3", "eslint-plugin-jest": "^24.1.3",
"eslint-plugin-jsx-a11y": "^6.4.1", "eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-prettier": "^3.3.1", "eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-react": "^7.21.5",
"eslint-plugin-react-hooks": "4.0.0",
"husky": "^3.0.5", "husky": "^3.0.5",
"jest": "^26.6.3", "jest": "^26.6.3",
"prettier": "^2.2.1", "prettier": "^2.2.1",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Before After
Before After

View file

@ -46,6 +46,18 @@
} }
.header { .header {
.logo {
height: 40px;
display: inline-block;
vertical-align: middle;
margin-right: 10px;
& svg {
height: 100%;
width: auto;
}
}
.title-desktop { .title-desktop {
@media all and (max-width: 820px) { @media all and (max-width: 820px) {
display: none; display: none;
@ -74,7 +86,17 @@
} }
} }
@media all and (min-width: 800px) {
.mobile {
display: none !important;
}
}
@media all and (max-width: 800px) { @media all and (max-width: 800px) {
.desktop {
display: none !important;
}
.report-problem-container { .report-problem-container {
font-size: 12px; font-size: 12px;
padding: 10px; padding: 10px;

View file

@ -13,12 +13,47 @@ import Footer from './components/Footer';
const theme = createMuiTheme({ const theme = createMuiTheme({
palette: { palette: {
primary: { primary: {
main: '#3F51B5', main: '#00B5E2',
contrastText: '#ffffff',
}, },
secondary: { secondary: {
main: '#009688', main: '#212322',
}, },
}, },
typography: {
h1: {
fontWeight: 600,
},
h2: {
fontWeight: 600,
},
h3: {
fontWeight: 600,
},
h4: {
fontWeight: 600,
},
h5: {
fontWeight: 600,
},
h6: {
fontWeight: 600,
},
fontFamily: [
'Open Sans',
'-apple-system',
'BlinkMacSystemFont',
'Segoe UI',
'Roboto',
'Oxygen',
'Ubuntu',
'Cantarell',
'Fira Sans',
'Droid Sans',
'Helvetica Neue',
,
].join(','),
},
}); });
const App: FunctionComponent = () => { const App: FunctionComponent = () => {
@ -30,7 +65,7 @@ const App: FunctionComponent = () => {
<Router> <Router>
<Switch> <Switch>
<Route path="" exact component={Home} /> <Route path="" exact component={Home} />
<Route default component={NotFound} /> <Route component={NotFound} />
</Switch> </Switch>
</Router> </Router>
<Footer /> <Footer />

View file

@ -5,7 +5,7 @@
import React from 'react'; import React from 'react';
import { fireEvent, render, RenderResult, waitFor } from '@testing-library/react'; import { fireEvent, render, RenderResult, waitFor } from '@testing-library/react';
import Header from './Header'; import Header from '.';
const mockChangeLanguage = jest.fn(); const mockChangeLanguage = jest.fn();

View file

@ -11,9 +11,11 @@ import {
Typography, Typography,
} from '@material-ui/core'; } from '@material-ui/core';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { ReactSVG } from 'react-svg';
import locales from '../locales'; import logo from '../../images/logo-white.svg';
import '../App.scss'; import locales from '../../locales';
import '../../App.scss';
const Header: FunctionComponent = () => { const Header: FunctionComponent = () => {
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
@ -32,8 +34,10 @@ const Header: FunctionComponent = () => {
return ( return (
<AppBar position="sticky" className="header"> <AppBar position="sticky" className="header">
<Toolbar> <Toolbar>
<Typography variant="h6">{t('tr-title')}</Typography> <ReactSVG src={logo} className="logo" />
<span className="title-mobile">{t('tr-title')}</span> <Typography variant="h5" component="div">
{t('tr-title')}
</Typography>
<div style={{ flexGrow: 1 }} /> <div style={{ flexGrow: 1 }} />
<Box position="relative"> <Box position="relative">
<Button <Button

View file

@ -5,10 +5,10 @@ const config = {
show_help: true, show_help: true,
// Path to overview.json file or URL to the ASU API // Path to overview.json file or URL to the ASU API
versions: { '19.07.5': 'data/19.07.5', SNAPSHOT: 'data/SNAPSHOT' }, versions: { '19.07.7': 'data/19.07.7', SNAPSHOT: 'data/SNAPSHOT' },
// Pre-selected version (optional) // Pre-selected version (optional)
default_version: '19.07.5', default_version: '19.07.7',
// Image download URL (optional) // Image download URL (optional)
image_url: 'https://downloads.openwrt.org/releases/{version}/targets/{target}', image_url: 'https://downloads.openwrt.org/releases/{version}/targets/{target}',
@ -18,7 +18,7 @@ const config = {
// Build custom images (optional) // Build custom images (optional)
// See https://github.com/aparcar/asu // See https://github.com/aparcar/asu
// asu_url: 'https://chef.libremesh.org' asu_url: 'https://chef.libremesh.org',
}; };
export default config; export default config;

View file

@ -2,8 +2,16 @@ import React, { FunctionComponent, ReactNode, useCallback, useEffect, useState }
import { import {
Box, Box,
Button, Button,
Chip,
CircularProgress, CircularProgress,
FormControl,
Grid,
IconButton,
Input,
InputAdornment,
InputLabel,
Link, Link,
makeStyles,
Table, Table,
TableBody, TableBody,
TableCell, TableCell,
@ -12,15 +20,26 @@ import {
TableRow, TableRow,
Typography, Typography,
} from '@material-ui/core'; } from '@material-ui/core';
import { Launch, CloudDownload } from '@material-ui/icons'; import { Launch, CloudDownload, Add } from '@material-ui/icons';
import Axios from 'axios'; import axios from 'axios';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { DateTime } from 'luxon';
import { ProfilesEntity } from '../../../types/overview'; import { ProfilesEntity } from '../../../types/overview';
import { Profile, TitlesEntity } from '../../../types/profile'; import { Profile, TitlesEntity } from '../../../types/profile';
import config from '../../../config'; import config from '../../../config';
import { getTitle } from '../utils/title'; import { getTitle } from '../utils/title';
import asu from '../../../utils/asu';
import { GetBuildResponse } from '../../../types/asu';
const useStyles = makeStyles(() => ({
chip: {
'&:focus': {
border: `2px solid #000`,
},
},
}));
type Props = { type Props = {
selectedVersion: string; selectedVersion: string;
@ -30,7 +49,15 @@ type Props = {
const profilesData: { [key: string]: Profile } = {}; const profilesData: { [key: string]: Profile } = {};
const ProfileDetails: FunctionComponent<Props> = ({ selectedVersion, selectedProfile }) => { const ProfileDetails: FunctionComponent<Props> = ({ selectedVersion, selectedProfile }) => {
const [profile, setProfileData] = useState<Profile>(); const classes = useStyles();
const [profile, setProfile] = useState<Profile>();
const [showAddPackages, setShowAddPackages] = useState(false);
const [customPackages, setCustomPackages] = useState<Set<string>>(new Set());
const [customPackagesInputValue, setCustomPackagesInputValue] = useState<string>('');
const [showPackageExistsError, setShowPackageExistsError] = useState(false);
const [buildStatus, setBuildStatus] = useState<React.ReactNode>();
const [buildResponse, setBuildResponse] = useState<GetBuildResponse>();
const [buildError, setBuildError] = useState<string>();
const { t } = useTranslation(); const { t } = useTranslation();
const getHelpKey = (type: string) => { const getHelpKey = (type: string) => {
@ -56,12 +83,18 @@ const ProfileDetails: FunctionComponent<Props> = ({ selectedVersion, selectedPro
return 'other-help'; return 'other-help';
}; };
const preExistingPackages = (_profile = profile) =>
Array.from(
new Set([...(_profile?.default_packages || []), ...(_profile?.device_packages || [])])
);
const getProfileData = useCallback(async () => { const getProfileData = useCallback(async () => {
let profileData = profilesData[selectedProfile.id]; let profileData = profilesData[selectedProfile.id];
if (!profileData) { if (!profileData) {
const response = await Axios.get<Profile>( const response = await axios.get<Profile>(
`${process.env.PUBLIC_URL}/data/${selectedVersion}/${selectedProfile.target}/${selectedProfile.id}.json` `${process.env.PUBLIC_URL}/data/${selectedVersion}` +
`/${selectedProfile.target}/${selectedProfile.id}.json`
); );
profileData = response.data; profileData = response.data;
profilesData[selectedProfile.id] = profileData; profilesData[selectedProfile.id] = profileData;
@ -73,7 +106,10 @@ const ProfileDetails: FunctionComponent<Props> = ({ selectedVersion, selectedPro
useEffect(() => { useEffect(() => {
let mounted = true; let mounted = true;
getProfileData().then((_profileData) => { getProfileData().then((_profileData) => {
if (mounted && !isEqual(profile, _profileData)) setProfileData(_profileData); if (mounted && !isEqual(profile, _profileData)) {
setProfile(_profileData);
setCustomPackages(new Set(preExistingPackages(_profileData)));
}
}); });
return () => { return () => {
@ -81,14 +117,64 @@ const ProfileDetails: FunctionComponent<Props> = ({ selectedVersion, selectedPro
}; };
}, [selectedVersion, selectedProfile, getProfileData, profile]); }, [selectedVersion, selectedProfile, getProfileData, profile]);
const toggleAddPackages = () => {
if (!profile) return;
setShowAddPackages(!showAddPackages);
setCustomPackages(new Set(preExistingPackages()));
};
const appendAddPackageInput = (
e?: React.KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
if (((e && e.key === 'Enter') || !e) && customPackagesInputValue) {
setCustomPackages((prev) => {
setShowPackageExistsError(false);
if (
!profile?.device_packages?.includes(customPackagesInputValue) &&
!profile?.default_packages?.includes(customPackagesInputValue)
) {
return new Set(prev.add(customPackagesInputValue));
}
setShowPackageExistsError(true);
return prev;
});
setCustomPackagesInputValue('');
}
};
const onBuildStatusChange = (status: string) => {
setBuildStatus(status);
};
if (!profile) return <CircularProgress />; if (!profile) return <CircularProgress />;
const buildAt = new Date(profile.build_at); const buildCustomImage = async () => {
setBuildStatus('Please wait...');
try {
const response = await asu.build(
Array.from(customPackages.values()),
profile.id,
profile.version_number,
onBuildStatusChange
);
setBuildResponse(response);
} catch (e) {
console.log(e);
setBuildError(e.response.data.message);
}
setBuildStatus(undefined);
};
const buildAt = DateTime.fromFormat(profile.build_at, 'yyyy-MM-dd TT').toLocaleString(
DateTime.DATETIME_MED
);
const hasAbilityToBuildCustomPackages = Object.keys(config).includes('asu_url');
const canBuild = !isEqual(Array.from(customPackages.values()), preExistingPackages());
return ( return (
<> <>
<Box paddingTop={3} paddingBottom={2}> <Box paddingTop={3} paddingBottom={2}>
<Typography variant="h6" component="h1" align="left"> <Typography variant="h5" component="h3" align="left">
{t('tr-version-build')} {t('tr-version-build')}
</Typography> </Typography>
</Box> </Box>
@ -150,7 +236,7 @@ const ProfileDetails: FunctionComponent<Props> = ({ selectedVersion, selectedPro
</Table> </Table>
</TableContainer> </TableContainer>
<Box paddingTop={3} paddingBottom={2}> <Box paddingTop={3} paddingBottom={2}>
<Typography variant="h6" component="h1" align="left"> <Typography variant="h5" component="h3" align="left">
{t('tr-downloads')} {t('tr-downloads')}
</Typography> </Typography>
</Box> </Box>
@ -188,6 +274,182 @@ const ProfileDetails: FunctionComponent<Props> = ({ selectedVersion, selectedPro
</TableBody> </TableBody>
</Table> </Table>
</TableContainer> </TableContainer>
<Box paddingTop={3} paddingBottom={2}>
<Typography variant="h5" component="h3" align="left">
{t('Packages')}
</Typography>
</Box>
{profile.default_packages && profile.default_packages.length > 0 && (
<Box mb={2}>
<Typography variant="h6" align="left">
{t('Default Packages')}
</Typography>
{profile.default_packages?.join(', ')}
</Box>
)}
{profile.device_packages && profile.device_packages.length > 0 && (
<Box mb={2}>
<Typography variant="h6" align="left">
{t('Device Packages')}
</Typography>
{profile.device_packages.join(', ')}
</Box>
)}
{hasAbilityToBuildCustomPackages && (
<Box>
{!showAddPackages && (
<Button variant="outlined" size="small" onClick={toggleAddPackages}>
customize packages
</Button>
)}
{showAddPackages && (
<>
<Typography variant="h6" align="left">
{t('Customize Packages')}
<Box display="inline-block" ml={2}>
<Link href="https://openwrt.org/packages/table/start" target="_blank">
<Typography variant="caption">find packages index on this page</Typography>
</Link>
</Box>
</Typography>
<br />
{Array.from(customPackages.values()).map((p) => {
const isDefaultPackage = profile.default_packages?.includes(p);
return (
<Box key={p} pr={1} pb={1} display="inline-block">
<Chip
size="small"
label={p}
color={isDefaultPackage ? 'default' : 'primary'}
className={classes.chip}
onDelete={
isDefaultPackage
? undefined
: () =>
setCustomPackages((prev) => {
const newSet = new Set(Array.from(prev.values()));
newSet.delete(p);
return newSet;
})
}
/>
</Box>
);
})}
<br />
<FormControl size="small">
<InputLabel style={{ fontSize: '0.9em' }}>Custom Package Name</InputLabel>
<Input
value={customPackagesInputValue}
onChange={(e) => e && setCustomPackagesInputValue(e.currentTarget.value)}
onKeyUp={appendAddPackageInput}
endAdornment={
<InputAdornment position="end">
<IconButton size="small" onClick={() => appendAddPackageInput()}>
<Add />
</IconButton>
</InputAdornment>
}
/>
</FormControl>
{showPackageExistsError && (
<Box pt={2}>
<Typography color="error" variant="caption" component="div">
This profile already includes this package. Please try a diffrent one
</Typography>
</Box>
)}
<Box mt={3}>
{!buildStatus && (
<Button
variant="contained"
color="primary"
disabled={!canBuild}
onClick={buildCustomImage}
>
build customized image
</Button>
)}
{buildStatus && (
<>
<Grid
container
alignContent="center"
direction="row"
alignItems="center"
spacing={2}
>
{typeof buildStatus === 'string' && (
<Grid item>
<CircularProgress />
</Grid>
)}
<Grid item>{buildStatus}</Grid>
</Grid>
</>
)}
</Box>
</>
)}
{buildError && <Typography color="error">{buildError}</Typography>}
{buildResponse && (
<Box mt={3}>
<Typography variant="h5">Built Image:</Typography>
<Table>
<TableBody>
<TableRow>
<TableCell>Build At</TableCell>
<TableCell id="title">
{DateTime.fromFormat(
buildResponse.build_at.substr(0, 25),
'ccc, dd MMM yyyy TT',
{
zone: buildResponse.build_at.substr(26),
setZone: true,
}
).toLocaleString(DateTime.DATETIME_MED)}
</TableCell>
</TableRow>
</TableBody>
</Table>
<br />
<br />
<Table>
<TableHead>
<TableRow>
<TableCell>Download link</TableCell>
<TableCell>Help Text</TableCell>
</TableRow>
</TableHead>
<TableBody>
{buildResponse.images?.map((i) => {
const downloadURL = `${config.image_url
.replace('{target}', profile.target)
.replace('{version}', profile.version_number)}/${i.name}`;
return (
<TableRow key={downloadURL + i.type}>
<TableCell>
<Link href={downloadURL} target="_blank" data-testid="download_link">
<Button endIcon={<CloudDownload />} variant="contained" color="primary">
{i.type}
</Button>
</Link>
</TableCell>
<TableCell>
<Box p={1}>
<Typography>{t(`tr-${getHelpKey(i.type)}`)}</Typography>
<Typography variant="caption">sha256sum: {i.sha256}</Typography>
</Box>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</Box>
)}
</Box>
)}
</> </>
); );
}; };

View file

@ -30,9 +30,8 @@ const ProfileSearch: FunctionComponent<Props> = ({ selectedVersion, onProfileCha
toggleWorking(true); toggleWorking(true);
if (!overview) { if (!overview) {
const response = await Axios.get<Overview>( const overviewPath = `${process.env.PUBLIC_URL}/data/${selectedVersion}/overview.json`;
`${process.env.PUBLIC_URL}/data/${selectedVersion}/overview.json` const response = await Axios.get<Overview>(overviewPath);
);
overview = response.data; overview = response.data;
overviewData[selectedVersion] = overview; overviewData[selectedVersion] = overview;
} }

394
src/images/logo-white.svg Normal file
View file

@ -0,0 +1,394 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="529.16669mm"
height="162.23152mm"
viewBox="0 0 529.16669 162.23152"
version="1.1"
id="svg1356"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
sodipodi:docname="logo_full_white.svg">
<defs
id="defs1350">
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath594">
<path
d="M 0,0 H 792 V 612 H 0 Z"
id="path592" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath622">
<path
d="M 0,0 H 792 V 612 H 0 Z"
id="path620" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath646">
<path
d="M 0,0 H 792 V 612 H 0 Z"
id="path644" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath670">
<path
d="M 0,0 H 792 V 612 H 0 Z"
id="path668" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath690">
<path
d="M 0,0 H 792 V 612 H 0 Z"
id="path688" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath710">
<path
d="M 0,0 H 792 V 612 H 0 Z"
id="path708" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath730">
<path
d="M 0,0 H 792 V 612 H 0 Z"
id="path728" />
</clipPath>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.18520099"
inkscape:cx="514.14383"
inkscape:cy="22.780654"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1533"
inkscape:window-height="991"
inkscape:window-x="26"
inkscape:window-y="23"
inkscape:window-maximized="0" />
<metadata
id="metadata1353">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-139.40571,-167.43852)">
<g
id="g588"
transform="matrix(3.2848066,0,0,-3.2848066,-1224.4529,899.56869)">
<g
id="g590"
clip-path="url(#clipPath594)">
<g
id="g596"
transform="translate(476.7686,191.1094)">
<path
d="m 0,0 c 0,-5.89 -4.604,-10.521 -10.493,-10.521 -5.889,0 -10.493,4.631 -10.493,10.521 0,5.916 4.604,10.627 10.493,10.627 C -4.604,10.627 0,5.916 0,0 m -3.667,0 c 0,4.095 -2.998,7.2 -6.826,7.2 -3.828,0 -6.826,-3.105 -6.826,-7.2 0,-4.095 2.998,-7.121 6.826,-7.121 3.828,0 6.826,3.025 6.826,7.121"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path598" />
</g>
<g
id="g600"
transform="translate(492.605,187.6562)">
<path
d="m 0,0 c 0,-4.203 -3.158,-7.067 -6.559,-7.067 -1.82,0 -3.292,0.536 -4.416,1.445 v -8.539 h -3.534 V 6.692 h 3.535 V 5.621 C -9.85,6.558 -8.378,7.093 -6.558,7.093 -3.158,7.093 0,4.203 0,0 m -3.427,0 c 0,2.462 -1.606,4.015 -3.747,4.015 -1.9,0 -3.801,-1.553 -3.801,-4.015 0,-2.463 1.901,-3.987 3.801,-3.987 2.142,-0.002 3.747,1.524 3.747,3.987"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path602" />
</g>
<g
id="g604"
transform="translate(507.4006,186.7197)">
<path
d="m 0,0 h -10.172 c 0.348,-1.928 1.713,-3.105 3.346,-3.105 1.044,0 2.355,0.133 3.319,1.766 l 3.159,-0.669 c -1.178,-2.784 -3.56,-4.122 -6.479,-4.122 -3.774,0 -6.852,2.864 -6.852,7.067 0,4.202 3.078,7.092 6.906,7.092 3.56,0 6.638,-2.758 6.772,-6.826 z m -10.065,2.489 h 6.453 c -0.455,1.714 -1.74,2.463 -3.159,2.463 -1.34,0 -2.866,-0.803 -3.294,-2.463"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path606" />
</g>
<g
id="g608"
transform="translate(521.8176,189.1284)">
<path
d="m 0,0 v -8.138 h -3.533 v 7.388 c 0,1.874 -1.071,3.132 -2.704,3.132 -2.007,0 -3.319,-1.338 -3.319,-4.604 v -5.916 h -3.533 V 5.22 h 3.532 V 4.015 c 1.044,1.044 2.436,1.606 4.203,1.606 C -2.141,5.621 0,3.319 0,0"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path610" />
</g>
</g>
</g>
<g
id="g612"
transform="matrix(3.2848066,0,0,-3.2848066,560.99821,305.04931)">
<path
d="M 0,0 H -2.837 L -7.816,13.197 -12.822,0 h -2.81 l -6.666,20.344 h 3.8 l 4.471,-13.438 5.113,13.438 h 2.168 l 5.113,-13.438 4.47,13.438 h 3.827 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path614" />
</g>
<g
id="g616"
transform="matrix(3.2848066,0,0,-3.2848066,-1224.4529,899.56869)">
<g
id="g618"
clip-path="url(#clipPath622)">
<g
id="g624"
transform="translate(558.9077,194.4282)">
<path
d="m 0,0 -0.187,-3.399 h -0.777 c -3.265,0 -4.765,-2.008 -4.765,-5.729 v -4.31 H -9.262 V -0.08 h 3.533 v -2.329 c 0.991,1.553 2.543,2.516 4.792,2.516 0.348,0 0.616,0 0.937,-0.107"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path626" />
</g>
<g
id="g628"
transform="translate(566.4221,185.916)">
<path
d="m 0,0 c 0,-1.312 0.616,-2.035 1.687,-2.035 0.615,0 1.525,0.269 2.223,0.643 l 1.017,-2.944 c -1.392,-0.75 -2.355,-0.991 -3.479,-0.991 -3.186,0 -4.979,1.821 -4.979,5.06 v 5.809 h -2.865 v 2.89 h 2.864 v 4.845 l 3.533,1.071 V 8.432 H 5.168 V 5.542 H 0 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path630" />
</g>
</g>
</g>
<g
id="g632"
transform="matrix(3.2848066,0,0,-3.2848066,383.96624,329.4222)">
<path
d="M 0,0 H -0.795 L -1.542,2.05 -2.293,0 h -0.791 l -1.219,3.781 h 1.015 l 0.632,-2.039 0.791,2.039 h 0.647 L -0.432,1.742 0.205,3.781 H 1.22 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path634" />
</g>
<g
id="g636"
transform="matrix(3.2848066,0,0,-3.2848066,400.41254,326.72602)">
<path
d="M 0,0 H 0.781 V -0.821 H -1.737 V 0 h 0.781 V 2.135 H -1.737 V 2.96 H 0.781 V 2.135 H 0 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path638" />
</g>
<g
id="g640"
transform="matrix(3.2848066,0,0,-3.2848066,-1224.4529,899.56869)">
<g
id="g642"
clip-path="url(#clipPath646)">
<g
id="g648"
transform="translate(500.3777,173.5708)">
<path
d="M 0,0 -0.697,0.821 H -1.442 V 0 h -0.956 v 3.781 h 1.762 C 0.359,3.781 0.961,3.194 0.961,2.319 0.961,1.756 0.707,1.303 0.269,1.05 L 1.199,0 Z m -0.672,1.642 c 0.398,0 0.726,0.269 0.726,0.677 0,0.398 -0.328,0.643 -0.726,0.643 h -0.77 V 1.644 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path650" />
</g>
</g>
</g>
<g
id="g652"
transform="matrix(3.2848066,0,0,-3.2848066,439.08752,329.4222)">
<path
d="M 0,0 H -2.886 V 3.781 H -0.03 V 2.96 h -1.9 V 2.313 h 1.87 V 1.493 H -1.93 V 0.821 h 1.931 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path654" />
</g>
<g
id="g656"
transform="matrix(3.2848066,0,0,-3.2848066,456.59196,329.4222)">
<path
d="m 0,0 h -2.791 v 3.781 h 0.955 V 0.821 H 0 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path658" />
</g>
<g
id="g660"
transform="matrix(3.2848066,0,0,-3.2848066,474.36148,329.4222)">
<path
d="M 0,0 H -2.886 V 3.781 H -0.03 V 2.96 H -1.931 V 2.313 H -0.06 V 1.493 H -1.931 V 0.821 H 0 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path662" />
</g>
<g
id="g664"
transform="matrix(3.2848066,0,0,-3.2848066,-1224.4529,899.56869)">
<g
id="g666"
clip-path="url(#clipPath670)">
<g
id="g672"
transform="translate(522.7458,176.3374)">
<path
d="m 0,0 -0.935,-0.189 c -0.08,0.383 -0.339,0.492 -0.563,0.492 -0.223,0 -0.408,-0.114 -0.408,-0.324 0,-0.148 0.08,-0.243 0.244,-0.293 l 0.737,-0.234 c 0.621,-0.203 0.96,-0.492 0.96,-1.084 0,-0.851 -0.746,-1.21 -1.488,-1.21 -0.821,0 -1.488,0.433 -1.582,1.155 l 0.98,0.199 c 0.079,-0.373 0.303,-0.533 0.632,-0.533 0.279,0 0.448,0.129 0.448,0.329 0,0.149 -0.08,0.254 -0.289,0.308 l -0.701,0.204 c -0.573,0.164 -0.961,0.458 -0.961,1.07 0,0.771 0.606,1.194 1.433,1.194 C -0.707,1.084 -0.125,0.707 0,0"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path674" />
</g>
<g
id="g676"
transform="translate(527.7451,176.3374)">
<path
d="m 0,0 -0.936,-0.189 c -0.079,0.383 -0.338,0.492 -0.562,0.492 -0.223,0 -0.408,-0.114 -0.408,-0.324 0,-0.148 0.08,-0.243 0.244,-0.293 l 0.737,-0.234 c 0.621,-0.203 0.96,-0.492 0.96,-1.084 0,-0.851 -0.746,-1.21 -1.488,-1.21 -0.821,0 -1.488,0.433 -1.582,1.155 l 0.98,0.199 c 0.079,-0.373 0.303,-0.533 0.632,-0.533 0.279,0 0.448,0.129 0.448,0.329 0,0.149 -0.08,0.254 -0.289,0.308 l -0.702,0.205 c -0.572,0.164 -0.96,0.458 -0.96,1.07 0,0.771 0.606,1.194 1.433,1.194 C -0.707,1.084 -0.125,0.707 0,0"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path678" />
</g>
</g>
</g>
<g
id="g680"
transform="matrix(3.2848066,0,0,-3.2848066,531.81272,321.82281)">
<path
d="M 0,0 H 1.776 V -0.821 H 0 V -2.313 H -0.956 V 1.468 H 1.906 V 0.647 H 0 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path682" />
</g>
<g
id="g684"
transform="matrix(3.2848066,0,0,-3.2848066,-1224.4529,899.56869)">
<g
id="g686"
clip-path="url(#clipPath690)">
<g
id="g692"
transform="translate(541.5046,173.5708)">
<path
d="M 0,0 -0.696,0.821 H -1.443 V 0 h -0.955 v 3.781 h 1.761 C 0.358,3.781 0.96,3.194 0.96,2.319 0.96,1.756 0.707,1.303 0.269,1.05 L 1.199,0 Z m -0.672,1.642 c 0.399,0 0.727,0.269 0.727,0.677 0,0.398 -0.328,0.643 -0.727,0.643 H -1.443 V 1.644 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path694" />
</g>
</g>
</g>
<g
id="g696"
transform="matrix(3.2848066,0,0,-3.2848066,574.18343,329.4222)">
<path
d="M 0,0 H -2.886 V 3.781 H -0.03 V 2.96 H -1.931 V 2.313 H -0.06 V 1.493 H -1.931 V 0.821 H 0 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path698" />
</g>
<g
id="g700"
transform="matrix(3.2848066,0,0,-3.2848066,591.99727,329.4222)">
<path
d="M 0,0 H -2.886 V 3.781 H -0.03 V 2.96 h -1.9 V 2.313 h 1.871 V 1.493 H -1.93 V 0.821 h 1.931 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path702" />
</g>
<g
id="g704"
transform="matrix(3.2848066,0,0,-3.2848066,-1224.4529,899.56869)">
<g
id="g706"
clip-path="url(#clipPath710)">
<g
id="g712"
transform="translate(557.21,177.3521)">
<path
d="M 0,0 C 1.204,0 2,-0.757 2,-1.891 2,-3.024 1.205,-3.781 0,-3.781 H -1.687 V 0 Z m -0.01,-2.96 c 0.652,0 1.05,0.428 1.05,1.069 0,0.642 -0.398,1.07 -1.05,1.07 H -0.73 V -2.96 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path714" />
</g>
<g
id="g716"
transform="translate(565.2275,175.4565)">
<path
d="m 0,0 c 0,-1.104 -0.906,-1.96 -2.02,-1.96 -1.115,0 -2.025,0.856 -2.025,1.96 0,1.104 0.905,1.965 2.025,1.965 C -0.901,1.965 0,1.1 0,0 m -0.96,0 c 0,0.622 -0.468,1.104 -1.061,1.104 -0.592,0 -1.06,-0.482 -1.06,-1.104 0,-0.622 0.468,-1.104 1.06,-1.104 0.593,0 1.061,0.477 1.061,1.104 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path718" />
</g>
</g>
</g>
<g
id="g720"
transform="matrix(3.2848066,0,0,-3.2848066,653.10448,329.4222)">
<path
d="M 0,0 H -0.935 V 1.796 L -2.05,0.262 H -2.339 L -3.453,1.794 V -0.001 H -4.389 V 3.78 h 0.613 L -2.194,1.556 -0.612,3.78 H 0 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path722" />
</g>
<g
id="g724"
transform="matrix(3.2848066,0,0,-3.2848066,-1224.4529,899.56869)">
<g
id="g726"
clip-path="url(#clipPath730)">
<g
id="g732"
transform="translate(574.8005,200.6079)">
<path
d="M 0,0 -0.985,1.069 H -1.391 V 0 h -0.453 v 2.53 h 0.945 c 0.531,0 0.852,-0.303 0.852,-0.729 C -0.039,1.51 -0.215,1.247 -0.486,1.142 L 0.566,0 Z m -0.899,1.478 c 0.266,0 0.403,0.14 0.403,0.323 0,0.182 -0.139,0.318 -0.403,0.318 H -1.391 V 1.477 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path734" />
</g>
<g
id="g736"
transform="translate(573.9438,199.3813)">
<path
d="m 0,0 c -1.301,0 -2.355,1.054 -2.355,2.354 -10e-4,1.301 1.053,2.355 2.354,2.355 1.3,0.001 2.355,-1.053 2.355,-2.354 v 0 C 2.353,1.055 1.3,0.002 0,0 m 0,4.375 c -1.116,0 -2.021,-0.904 -2.021,-2.02 0,-1.116 0.905,-2.021 2.021,-2.021 1.116,0 2.021,0.905 2.021,2.021 C 2.02,3.471 1.116,4.375 0,4.376 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path738" />
</g>
<g
id="g740"
transform="translate(434.1643,199.1333)">
<path
d="m 0,0 c -1.705,0 -3.087,-1.382 -3.087,-3.087 0,-1.705 1.382,-3.086 3.087,-3.086 1.705,0 3.087,1.381 3.087,3.086 C 3.084,-1.383 1.704,-0.002 0,0"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path742" />
</g>
<g
id="g744"
transform="translate(415.2021,215.0088)">
<path
d="m 0,0 3.213,-3.213 c 4.032,4.032 9.606,6.52 15.749,6.52 6.143,0 11.718,-2.489 15.75,-6.52 L 37.925,0 C 33.074,4.851 26.365,7.875 18.962,7.875 11.56,7.875 4.851,4.851 0,0"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path746" />
</g>
<g
id="g748"
transform="translate(420.998,209.2129)">
<path
d="m 0,0 3.213,-3.213 c 2.551,2.552 6.079,4.127 9.952,4.127 3.873,0 7.404,-1.575 9.955,-4.127 L 26.333,0 C 22.963,3.371 18.301,5.482 13.167,5.482 8.033,5.482 3.371,3.372 0,0"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path750" />
</g>
<g
id="g752"
transform="translate(426.7622,203.4482)">
<path
d="m 0,0 3.213,-3.213 c 2.315,2.31 6.063,2.31 8.379,0 L 14.804,0 C 12.842,1.963 10.178,3.063 7.402,3.055 4.536,3.055 1.92,1.89 0,0"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path754" />
</g>
<g
id="g756"
transform="translate(445.5669,204.3306)">
<path
d="m 0,0 c 1.701,-2.331 2.709,-5.197 2.709,-8.284 0,-7.78 -6.331,-14.112 -14.112,-14.112 -7.78,0 -14.111,6.332 -14.111,14.112 0,3.087 1.008,5.953 2.709,8.284 l -3.244,3.245 c -2.52,-3.182 -4.001,-7.182 -4.001,-11.529 0,-10.269 8.379,-18.647 18.647,-18.647 10.269,0 18.648,8.378 18.648,18.647 0,4.347 -1.512,8.347 -4.001,11.529 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path758" />
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -1,14 +1,14 @@
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600;700&display=swap');
body { body {
margin: 0; margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
sans-serif;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
background-color: #f5f5f5; background-color: #f5f5f5;
} }
code { code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
monospace;
} }

View file

@ -1,9 +1,10 @@
/* eslint-disable max-len */
/* exported translations */ /* exported translations */
const translations = { const translations = {
ca: { ca: {
'tr-load': 'Descarregueu el microprogramari OpenWrt per al vostre dispositiu', 'tr-load': 'Descarregueu el microprogramari OpenWrt per al vostre dispositiu',
'tr-title': 'Selector de microprogramari OpenWrt', 'tr-title': 'Selector de microprogramari',
'tr-message': 'tr-message':
'Introduïu el nom o el model del vostre dispositiu i seleccioneu la versió estable (per defecte) o la darrera imatge compilada ("snapshot")', 'Introduïu el nom o el model del vostre dispositiu i seleccioneu la versió estable (per defecte) o la darrera imatge compilada ("snapshot")',
'tr-version-build': 'Compilació', 'tr-version-build': 'Compilació',
@ -35,7 +36,7 @@ const translations = {
'tr-server-link': 'Files', 'tr-server-link': 'Files',
'tr-notfound': 'No model found!', 'tr-notfound': 'No model found!',
'tr-load': 'Download OpenWrt Firmware for your Device', 'tr-load': 'Download OpenWrt Firmware for your Device',
'tr-title': 'OpenWrt Firmware Selector', 'tr-title': 'Firmware Selector',
'tr-message': 'tr-message':
'Type the name or model of your device, then select the recommended build or some other.', 'Type the name or model of your device, then select the recommended build or some other.',
'tr-version-build': 'About this build', 'tr-version-build': 'About this build',
@ -65,7 +66,7 @@ const translations = {
es: { es: {
'tr-notfound': '¡Modelo no encontrado!', 'tr-notfound': '¡Modelo no encontrado!',
'tr-load': 'Descargue el firmware OpenWrt para su dispositivo', 'tr-load': 'Descargue el firmware OpenWrt para su dispositivo',
'tr-title': 'Selector de firmware OpenWrt', 'tr-title': 'Selector de firmware',
'tr-message': 'tr-message':
'Escriba el nombre o modelo de su dispositivo, luego seleccione la versión recomendada o alguna otra.', 'Escriba el nombre o modelo de su dispositivo, luego seleccione la versión recomendada o alguna otra.',
'tr-version-build': 'Acerca de esta compilación', 'tr-version-build': 'Acerca de esta compilación',
@ -95,7 +96,7 @@ const translations = {
}, },
no: { no: {
'tr-load': 'Last ned OpenWrt fastvare for din enhet!', 'tr-load': 'Last ned OpenWrt fastvare for din enhet!',
'tr-title': 'OpenWrt fastvare utvelger', 'tr-title': 'fastvare utvelger',
'tr-message': 'Bruk feltene nedenfor for å laste ned fastvare til enheten din!', 'tr-message': 'Bruk feltene nedenfor for å laste ned fastvare til enheten din!',
'tr-version-build': 'Sammensetning', 'tr-version-build': 'Sammensetning',
'tr-custom-build': 'Tilpasset sammensetning', 'tr-custom-build': 'Tilpasset sammensetning',
@ -126,7 +127,7 @@ const translations = {
'tr-server-link': 'Dateien', 'tr-server-link': 'Dateien',
'tr-notfound': 'Kein Model gefunden!', 'tr-notfound': 'Kein Model gefunden!',
'tr-load': 'Lade die OpenWrt Firmware für dein Gerät!', 'tr-load': 'Lade die OpenWrt Firmware für dein Gerät!',
'tr-title': 'OpenWrt Firmware Selector', 'tr-title': 'Firmware Selector',
'tr-message': 'Bitte benutze die Eingabe um die passende Firmware zu finden!', 'tr-message': 'Bitte benutze die Eingabe um die passende Firmware zu finden!',
'tr-version-build': 'Release Build', 'tr-version-build': 'Release Build',
'tr-custom-build': 'Custom Build', 'tr-custom-build': 'Custom Build',
@ -185,7 +186,7 @@ const translations = {
}, },
it: { it: {
'tr-load': 'Scarica il firmware OpenWrt per il tuo dispositivo!', 'tr-load': 'Scarica il firmware OpenWrt per il tuo dispositivo!',
'tr-title': 'OpenWrt Firmware Selector', 'tr-title': 'Firmware Selector',
'tr-message': 'Usa la casella sottostante per scaricare il firmware per il tuo dispositivo!', 'tr-message': 'Usa la casella sottostante per scaricare il firmware per il tuo dispositivo!',
'tr-version-build': 'Build', 'tr-version-build': 'Build',
'tr-custom-build': 'Custom Build', 'tr-custom-build': 'Custom Build',
@ -216,7 +217,7 @@ const translations = {
'tr-server-link': 'Pliki', 'tr-server-link': 'Pliki',
'tr-notfound': 'Nie znaleziono modelu!', 'tr-notfound': 'Nie znaleziono modelu!',
'tr-load': 'Pobieranie oprogramowania OpenWrt', 'tr-load': 'Pobieranie oprogramowania OpenWrt',
'tr-title': 'OpenWrt Firmware Selector', 'tr-title': 'Firmware Selector',
'tr-message': 'tr-message':
'Wprowadź nazwę lub model swojego urządzenia, a następnie wybierz wersję zalecaną lub inną.', 'Wprowadź nazwę lub model swojego urządzenia, a następnie wybierz wersję zalecaną lub inną.',
'tr-version-build': 'Informacje o obrazie', 'tr-version-build': 'Informacje o obrazie',
@ -246,7 +247,7 @@ const translations = {
}, },
tr: { tr: {
'tr-load': 'Cihazınız için OpenWrt yazılımını indirin!', 'tr-load': 'Cihazınız için OpenWrt yazılımını indirin!',
'tr-title': 'OpenWrt Yazılım Seçicisi', 'tr-title': 'Yazılım Seçicisi',
'tr-message': 'tr-message':
'Cihazınızın adını/modelini girin, ardından Stabil sürümü(varsayılan) veya nightly "snapshot" imajini seçin.', 'Cihazınızın adını/modelini girin, ardından Stabil sürümü(varsayılan) veya nightly "snapshot" imajini seçin.',
'tr-version-build': 'Sürüm', 'tr-version-build': 'Sürüm',

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -1 +1,12 @@
/// <reference types="react-scripts" /> /// <reference types="react-scripts" />
declare global {
namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'development' | 'production';
PUBLIC_URL: string;
}
}
}
export {};

34
src/types/asu.ts Normal file
View file

@ -0,0 +1,34 @@
export interface GetBuildResponse {
arch_packages: string;
bin_dir: string;
build_at: string;
default_packages?: string[];
device_packages?: string[];
enqueued_at: string;
id: string;
image_prefix: string;
images?: ImagesEntity[];
manifest: Record<string, string>;
metadata_version: number;
request_hash: string;
stderr: string;
stdout: string;
supported_devices?: string[];
target: string;
titles?: TitlesEntity[];
version_code: string;
version_number: string;
status?: 'queued' | 'started';
queue_position?: number;
}
export interface ImagesEntity {
filesystem: string;
name: string;
sha256: string;
type: string;
}
export interface TitlesEntity {
title: string;
}

17
src/utils/api.ts Normal file
View file

@ -0,0 +1,17 @@
import axios from 'axios';
import config from '../config';
import { GetBuildResponse } from '../types/asu';
const asu = {
buildNew: (packages: string[], profile: string, version: string) =>
axios.post<GetBuildResponse>(`${config.asu_url}/api/build`, {
version,
profile,
packages,
diff_packages: true,
}),
checkBuild: (request_hash: string) =>
axios.get<GetBuildResponse>(`${config.asu_url}/api/build/${request_hash}`),
};
export default { asu };

31
src/utils/asu.ts Normal file
View file

@ -0,0 +1,31 @@
import api from './api';
import { sleep } from './common';
const build = async (
packages: string[],
profile: string,
version: string,
buildStatusCallback: (status: string) => void
) => {
const buildResponse = await api.asu.buildNew(packages, profile, version);
if (buildResponse.status === 202) {
buildStatusCallback(`#${buildResponse} in queue`);
}
while (true) {
const checkBuildResponse = await api.asu.checkBuild(buildResponse.data.request_hash);
if (checkBuildResponse.status === 200) {
return checkBuildResponse.data;
} else if (checkBuildResponse.status === 202) {
if (checkBuildResponse.data.status === 'queued') {
buildStatusCallback(`#${buildResponse} in queue`);
} else if (checkBuildResponse.data.status === 'started') {
buildStatusCallback(`Building the image`);
}
}
await sleep(5000);
}
};
export default { build };

3
src/utils/common.ts Normal file
View file

@ -0,0 +1,3 @@
export function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

2764
yarn.lock

File diff suppressed because it is too large Load diff