Skip to content

Commit

Permalink
Merge pull request #51 from gnmyt/updates/optimizations
Browse files Browse the repository at this point in the history
🚀 Optimierungen am Code
  • Loading branch information
gnmyt authored Aug 30, 2022
2 parents e72b172 + 5c22fed commit eefe42d
Show file tree
Hide file tree
Showing 33 changed files with 6,695 additions and 1,705 deletions.
1 change: 1 addition & 0 deletions client/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;700;900&display=swap" rel="stylesheet">
<link rel="apple-touch-icon" href="/logo192.png" />
<link rel="manifest" href="/manifest.json" />
<title>MySpeed</title>
</head>
Expand Down
7,469 changes: 6,197 additions & 1,272 deletions client/package-lock.json

Large diffs are not rendered by default.

15 changes: 8 additions & 7 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,19 @@
"preview": "vite preview"
},
"dependencies": {
"@fortawesome/fontawesome-free-solid": "^5.0.13",
"@fortawesome/fontawesome-svg-core": "^6.1.2",
"@fortawesome/free-solid-svg-icons": "^6.1.2",
"@fortawesome/fontawesome-svg-core": "^6.2.0",
"@fortawesome/free-solid-svg-icons": "^6.2.0",
"@fortawesome/react-fontawesome": "^0.2.0",
"@vitejs/plugin-react": "^2.0.1",
"cron-parser": "^4.6.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"sass": "^1.54.5",
"@vitejs/plugin-react": "^2.0.1",
"vite": "^3.0.9"
"sass": "^1.54.6",
"vite": "^3.0.9",
"vite-plugin-pwa": "^0.12.3"
},
"devDependencies": {
"@types/react": "^18.0.17",
"@types/react": "^18.0.18",
"@types/react-dom": "^18.0.6"
}
}
Binary file added client/public/logo192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 8 additions & 1 deletion client/public/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192",
"purpose": "maskable"
},
{
"src": "logo.png",
"type": "image/png",
Expand All @@ -16,5 +22,6 @@
],
"start_url": ".",
"display": "standalone",
"theme_color": "#232835"
"theme_color": "#232835",
"background_color": "#232835"
}
6 changes: 5 additions & 1 deletion client/src/App.sass
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,8 @@ hr
transform: scale(1.6)
filter: blur(5px)
100%
opacity: 1
opacity: 1

@media (max-width: 730px)
::-webkit-scrollbar
width: 5px
380 changes: 142 additions & 238 deletions client/src/common/components/Dropdown/DropdownComponent.jsx

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions client/src/common/components/Dropdown/utils/infos.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export const healthChecksInfo = <>MySpeed verwendet <a href="https://healthchecks.io/" target="_blank">Healthchecks</a>,
um dich zu benachrichtigen, wenn dein Internet ausfällt. Um dies zu aktivieren, setze deine Ping URL in das Textfeld
ein. Mehr dazu <a href="https://myspeed.gnmyt.dev/instructions/settings/" target="_blank">hier</a></>;

export const creditsInfo = <><a href="https://github.com/gnmyt/myspeed" target="_blank"
rel="noreferrer">MySpeed</a> wird von GNMYT bereitgestellt und verwendet die <a
href="https://www.speedtest.net/apps/cli" target="_blank" rel="noreferrer">Speedtest-CLI</a> von Ookla.</>;

export const recommendationsError = <>Du musst mindestens 10 Tests machen, damit ein Durchschnitt ermittelt werden kann.
Ob die Tests manuell oder automatisch durchgeführt wurden ist egal.</>;

export const recommendationsInfo = (ping, download, upload) => <>Anhand der letzten 10 Testergebnisse wurde
festgestellt, dass der optimale Ping bei <span className="dialog-value">{ping} ms</span>, der Download bei <span
className="dialog-value">{download} Mbit/s </span>und der Upload bei <span
className="dialog-value">{upload} Mbit/s</span> liegt. <br/>Orientiere dich am besten an deinem Internetvertrag
und übernehme es nur, wenn es mit dem übereinstimmt.</>;
19 changes: 19 additions & 0 deletions client/src/common/components/Dropdown/utils/options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const timeOptions = {
1: "24 Stunden (Standard)",
2: "2 Tage (Insgesamt)",
3: "7 Tage (Durchschnitt)",
4: "30 Tage (Durchschnitt)"
};

export const selectOptions = {
"* * * * *": "Durchgehend (jede Minute)",
"0,30 * * * *": "Sehr häufig (alle 30 Minuten)",
"0 * * * *": "Standard (jede Stunde)",
"0 0,3,6,9,12,15,18,21 * * *": "Selten (alle 3 Stunden)",
"0 0,6,12,18 * * *": "Sehr selten (alle 6 Stunden)"
}

export const exportOptions = {
json: "JSON-Datei",
csv: "CSV-Datei"
}
19 changes: 19 additions & 0 deletions client/src/common/components/Dropdown/utils/utils.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {parseExpression} from "cron-parser";

// Parses cron as a locale string
export const parseCron = (cron) => {
try {
return parseExpression(cron).next().toDate().toLocaleString()
} catch (e) {
return <span className="icon-orange">Eingabe ungültig</span>;
}
}

// Fixes the cron provided by the user
export const stringifyCron = (cron) => {
try {
return parseExpression(cron).stringify();
} catch (e) {
return null;
}
}
58 changes: 21 additions & 37 deletions client/src/common/components/Header/HeaderComponent.jsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
import "./styles.sass";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faCircleExclamation, faGaugeHigh, faGear} from "@fortawesome/free-solid-svg-icons";
import {faCircleArrowUp, faGaugeHigh, faGear} from "@fortawesome/free-solid-svg-icons";
import {useContext, useEffect, useState} from "react";
import DropdownComponent, {toggleDropdown} from "../Dropdown/DropdownComponent";
import {DialogContext} from "@/common/contexts/Dialog";
import {StatusContext} from "@/common/contexts/Status";
import {SpeedtestContext} from "@/common/contexts/Speedtests";

import {jsonRequest, postRequest} from "@/common/utils/RequestUtil";
import {updateInfo} from "@/common/components/Header/utils/infos";

function HeaderComponent() {

const [setDialog] = useContext(DialogContext);
const [icon, setIcon] = useState(faGear);
const [status, updateStatus] = useContext(StatusContext);
const updateTests = useContext(SpeedtestContext)[1];
const [updateAvailable, setUpdateAvailable] = useState("");

const headers = localStorage.getItem("password") ? {password: localStorage.getItem("password")} : {}
headers['content-type'] = 'application/json'

function switchDropdown() {
toggleDropdown(setIcon);
}
Expand All @@ -39,23 +36,18 @@ function HeaderComponent() {

setDialog({speedtest: true});

fetch("/api/speedtests/run", {headers, method: "POST"}).then(() => {
updateTests();
updateStatus();
setDialog();
});
postRequest("/speedtests/run").then(updateTests).then(updateStatus).then(setDialog);
}

useEffect(() => {
fetch("/api/info/version", {headers})
.then(res => res.json())
.then(version => {
if (version.remote.localeCompare(version.local, undefined,
{numeric: true, sensitivity: 'base'}) === 1)
setUpdateAvailable(version.remote);
});
async function updateVersion() {
const version = await jsonRequest("/info/version");

if (version.remote.localeCompare(version.local, undefined, {numeric: true, sensitivity: 'base'}) === 1)
setUpdateAvailable(version.remote);
}

// eslint-disable-next-line react-hooks/exhaustive-deps
updateVersion();
}, []);

return (
Expand All @@ -64,38 +56,30 @@ function HeaderComponent() {
<h2>Netzwerkanalyse</h2>
<div className="header-right">
{updateAvailable ?
<div><FontAwesomeIcon icon={faCircleExclamation} className="header-icon icon-orange"
<div><FontAwesomeIcon icon={faCircleArrowUp} className="header-icon icon-orange update-icon"
onClick={() => setDialog({
title: "Update verfügbar",
buttonText: "Okay",
description: <>Ein Update auf die Version {updateAvailable} ist
verfügbar. Sieh dir <a target="_blank"
href="https://github.com/gnmyt/myspeed/releases/latest"
rel="noreferrer">die Änderungen
an</a> und <a target="_blank"
href="https://myspeed.gnmyt.dev/setup/linux/"
rel="noreferrer">lade dir das Update
herunter</a>.</>
})}/></div>
: <></>}
<div className="tooltip-element tooltip-bottom">
description: updateInfo(updateAvailable)
})}/></div> : <></>}

{!status.paused ? <div className="tooltip-element tooltip-bottom">
<FontAwesomeIcon icon={faGaugeHigh}
className={"header-icon " + (status.running || status.paused ? "icon-red" : "")}
className={"header-icon " + (status.running ? "test-running" : "")}
onClick={startSpeedtest}/>
<span className="tooltip">Speedtest starten</span>
</div>
<span className="tooltip">{status.running ? "Speedtest läuft" : "Speedtest starten"}</span>
</div> : <></>}


<div className="tooltip-element tooltip-bottom">
<div className="tooltip-element tooltip-bottom" id="open-header">
<FontAwesomeIcon icon={icon} className="header-icon" onClick={switchDropdown}/>
<span className="tooltip">Einstellungen</span>
</div>

</div>
</div>
<DropdownComponent/>
</header>
)

}

export default HeaderComponent;
36 changes: 25 additions & 11 deletions client/src/common/components/Header/styles.sass
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,30 @@
transform: scale(1.1)
color: $green

.icon-red:hover
color: $red-hover
.test-running
animation: pulse 2s infinite
border-radius: 150px
color: $green

.test-running:hover
color: $green-hover

.update-icon
animation: pulse-update 2s infinite
border-radius: 150px

.update-icon:hover
color: $orange-hover

@keyframes pulse-update
0%
box-shadow: 0 0 0 0 $orange
70%
box-shadow: 0 0 0 10px #CCA92C00
100%
box-shadow: 0 0 0 0 #CCA92C00

@media (max-width: 425px)
@media (max-width: 460px)
.header-main
justify-content: center
.header-main div
Expand All @@ -48,14 +68,8 @@
height: 20px


@media (max-width: 280px)
.header-main
justify-content: center
@media (max-width: 360px)
.header-main h2
margin-left: 0
font-size: 16pt
text-overflow: ellipsis
overflow: hidden
.header-icon
width: 16px
height: 16px
overflow: hidden
5 changes: 5 additions & 0 deletions client/src/common/components/Header/utils/infos.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const updateInfo = (update) => <>Ein Update auf die Version {update} ist verfügbar. Sieh dir <a target="_blank"
href="https://github.com/gnmyt/myspeed/releases/latest"
rel="noreferrer">die
Änderungen an</a> und <a target="_blank" href="https://myspeed.gnmyt.dev/setup/linux/" rel="noreferrer">lade dir das
Update herunter</a>.</>;
31 changes: 8 additions & 23 deletions client/src/common/contexts/Config/ConfigContext.jsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,25 @@
import React, {useState, createContext, useEffect, useContext} from "react";
import {DialogContext} from "../Dialog";
import {request} from "@/common/utils/RequestUtil";
import {passwordRequiredDialog} from "@/common/contexts/Config/dialog";

export const ConfigContext = createContext();
export const ConfigContext = createContext({});

export const ConfigProvider = (props) => {

const [config, setConfig] = useState({});
const [setDialog] = useContext(DialogContext);

const reloadConfig = () => {
let passwordHeaders = localStorage.getItem("password") ? {password: localStorage.getItem("password")} : {}
fetch("/api/config", {headers: passwordHeaders})
.then(res => {
if (!res.ok) throw new Error();
return res;
request("/config").then(res => {
if (!res.ok) throw "No connection to server";
return res.json();
})
.then(res => res.json())
.then(result => setConfig(result))
.catch(() => setDialog({
title: "Passwort erforderlich",
placeholder: "Dein Passwort",
description: localStorage.getItem("password") ? <span className="icon-red">Das von dir eingegebene Passwort ist falsch</span> : "",
type: "password",
buttonText: "Fertig",
onClose: () => window.location.reload(),
onSuccess: (value) => {
localStorage.setItem("password", value);
window.location.reload();
}
}));
.catch(() => setDialog(passwordRequiredDialog));
}

useEffect(() => {
reloadConfig();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(reloadConfig, []);

return (
<ConfigContext.Provider value={[config, reloadConfig]}>
Expand Down
12 changes: 12 additions & 0 deletions client/src/common/contexts/Config/dialog.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const passwordRequiredDialog = {
title: "Passwort erforderlich",
placeholder: "Dein Passwort",
description: localStorage.getItem("password") ? <span className="icon-red">Das von dir eingegebene Passwort ist falsch</span> : "",
type: "password",
buttonText: "Fertig",
onClose: () => window.location.reload(),
onSuccess: (value) => {
localStorage.setItem("password", value);
window.location.reload();
}
}
Loading

0 comments on commit eefe42d

Please sign in to comment.