Skip to content

Commit

Permalink
Merge pull request #681 from gnmyt/updates/storage-management
Browse files Browse the repository at this point in the history
💾 Storage Management hinzugefügt
  • Loading branch information
gnmyt authored May 20, 2024
2 parents 851d4bf + d4bd2de commit 45772f8
Show file tree
Hide file tree
Showing 24 changed files with 638 additions and 93 deletions.
38 changes: 31 additions & 7 deletions client/public/assets/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
"dropdown": {
"settings": "Settings",
"changes_applied": "Your changes have been saved.",
"language_changed": "The language has been changed.",
"provider_changed": "The provider has been changed.",
"view_changed": "The view has been changed.",
"changes_unsaved": "Your changes were not applied. Check your input.",
"invalid": "Input invalid",
Expand All @@ -55,7 +57,7 @@
"password": "Change password",
"cron": "Set frequency",
"time": "Set period",
"export": "Export tests",
"storage": "Manage storage",
"pause_tests": "Pause tests",
"resume_tests": "Resume tests",
"language": "Change language",
Expand All @@ -78,10 +80,6 @@
"rare": "Rarely (every 3 hours)",
"really_rare": "Very rarely (every 6 hours)"
},
"export": {
"json": "JSON file",
"csv": "CSV file"
},
"level": {
"no_access": "No Access",
"read_access": "Read-only Access"
Expand All @@ -107,7 +105,6 @@
"cron_rules": "Cron rule",
"cron_next_test": "Next Test:",
"time_title": "Show tests of the last ...",
"export_title": "Export speedtests",
"download": "Download",
"pause_title": "Pause speedtests for...",
"hours": "Hours",
Expand All @@ -130,6 +127,33 @@
"description": "This feature is still in beta. If you find any bugs, please report them <Link>here</Link>."
}
},
"storage": {
"speedtests": "Speedtests",
"configuration": "Configuration",
"stored_tests": "Stored tests",
"tests": "Tests",
"export_tests": "Export tests",
"tests_exported": "The tests have been exported",
"csv": "CSV",
"json": "JSON",
"import_tests": "Import tests",
"tests_imported": "The tests have been imported",
"import_error": "An error occurred while importing the tests",
"export": "Export",
"import": "Import",
"clear_history": "Clear history",
"history_cleared": "The history has been cleared",
"delete": "Delete",
"confirm_delete": "Yes, delete",
"export_settings": "Export settings",
"import_settings": "Import settings",
"factory_reset": "Factory reset",
"factory_reset_completed": "The factory reset has been completed",
"reset": "Reset",
"confirm_reset": "Yes, reset",
"settings_exported": "The settings have been exported",
"settings_imported": "The settings have been imported"
},
"latest": {
"ping": "Ping",
"ping_unit": "ms",
Expand All @@ -140,7 +164,7 @@
"before": "before"
},
"info": {
"credits": "<Link>MySpeed</Link> is provided by GNMYT and uses the <CLILink>Speedtest CLI</CLILink> from Ookla.",
"credits": "<MSpeed>MySpeed</MSpeed> is a open source project provided by GNMYT. Leave a star on <Github>GitHub</Github> or <Donate>donate</Donate> to support the project.",
"recommendations_error": "You have to do at least 10 tests to get an average. It doesn't matter if the tests were done manually or automatically.",
"recommendations_info": "Based on the last 10 tests, it was found that the optimal ping was <Bold>{{ping}} ms</Bold>, the download at <Bold>{{down}} Mbps</Bold> and the upload at <Bold>{{up}} Mbps</Bold>. It is best to orientate yourself on your internet contract and only adopt it if it matches that.",
"update": "An update to version {{version}} is available. See <Changes>the changes</Changes> and <DLLink>download the update</DLLink>.",
Expand Down
23 changes: 7 additions & 16 deletions client/src/common/components/Dropdown/DropdownComponent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
faCircleNodes,
faClock,
faClose,
faFileExport,
faChartSimple,
faGear,
faGlobeEurope,
Expand All @@ -18,15 +17,15 @@ import {
faPlay,
faWandMagicSparkles,
faCheck,
faExclamationTriangle, faSliders
faExclamationTriangle, faSliders, faHardDrive
} from "@fortawesome/free-solid-svg-icons";
import {ConfigContext} from "@/common/contexts/Config";
import {StatusContext} from "@/common/contexts/Status";
import {InputDialogContext} from "@/common/contexts/InputDialog";
import {SpeedtestContext} from "@/common/contexts/Speedtests";
import {baseRequest, downloadRequest, jsonRequest, patchRequest, postRequest} from "@/common/utils/RequestUtil";
import {baseRequest, jsonRequest, patchRequest, postRequest} from "@/common/utils/RequestUtil";
import {creditsInfo, recommendationsInfo} from "@/common/components/Dropdown/utils/infos";
import {exportOptions, levelOptions, selectOptions, timeOptions} from "@/common/components/Dropdown/utils/options";
import {levelOptions, selectOptions, timeOptions} from "@/common/components/Dropdown/utils/options";
import {parseCron, stringifyCron} from "@/common/components/Dropdown/utils/utils";
import {t} from "i18next";
import ViewDialog from "@/common/components/ViewDialog";
Expand All @@ -36,6 +35,7 @@ import {NodeContext} from "@/common/contexts/Node";
import {IntegrationDialog} from "@/common/components/IntegrationDialog";
import LanguageDialog from "@/common/components/LanguageDialog";
import ProviderDialog from "@/common/components/ProviderDialog";
import StorageDialog from "@/common/components/StorageDialog";

let icon;

Expand Down Expand Up @@ -66,6 +66,7 @@ function DropdownComponent() {
const [showIntegrationDialog, setShowIntegrationDialog] = useState(false);
const [showLanguageDialog, setShowLanguageDialog] = useState(false);
const [showProviderDialog, setShowProviderDialog] = useState(false);
const [showStorageDialog, setShowStorageDialog] = useState(false);
const ref = useRef();

useEffect(() => {
Expand Down Expand Up @@ -192,17 +193,6 @@ function DropdownComponent() {
});
}

function exportDialog() {
setDialog({
select: true,
title: t("update.export_title"),
buttonText: t("update.download"),
value: "json",
selectOptions: exportOptions(),
onSuccess: value => downloadRequest("/export/" + value)
});
}

const togglePause = () => {
if (!status.paused) {
setDialog({
Expand All @@ -228,9 +218,9 @@ function DropdownComponent() {
{run: recommendedSettings, icon: faWandMagicSparkles, text: t("dropdown.recommendations")},
{hr: true, key: 1},
{run: () => setShowProviderDialog(true), icon: faSliders, text: t("dropdown.change_provider")},
{run: () => setShowStorageDialog(true), icon: faHardDrive, text: t("dropdown.storage")},
{run: updatePassword, icon: faKey, text: t("dropdown.password"), previewHidden: true},
{run: updateCron, icon: faClock, text: t("dropdown.cron")},
{run: exportDialog, icon: faFileExport, text: t("dropdown.export")},
{run: togglePause, icon: status.paused ? faPlay : faPause, text: t("dropdown." + (status.paused ? "resume_tests" : "pause_tests"))},
{run: () => setShowIntegrationDialog(true), icon: faCircleNodes, text: t("dropdown.integrations")},
{hr: true, key: 2},
Expand All @@ -247,6 +237,7 @@ function DropdownComponent() {
{showIntegrationDialog && <IntegrationDialog onClose={() => setShowIntegrationDialog(false)}/>}
{showLanguageDialog && <LanguageDialog onClose={() => setShowLanguageDialog(false)}/>}
{showProviderDialog && <ProviderDialog onClose={() => setShowProviderDialog(false)}/>}
{showStorageDialog && <StorageDialog onClose={() => setShowStorageDialog(false)}/>}
<div className="dropdown dropdown-invisible" id="dropdown" ref={ref}>
<div className="dropdown-content">
<h2>{t("dropdown.settings")}</h2>
Expand Down
8 changes: 4 additions & 4 deletions client/src/common/components/Dropdown/utils/infos.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {WEB_URL} from "@/index";
import {DONATION_URL, PROJECT_URL, WEB_URL} from "@/index";
import {Trans} from "react-i18next";
const CLI_URL = "https://www.speedtest.net/apps/cli";

export const creditsInfo = () => <Trans components={{Link: <a href={WEB_URL} target="_blank" />,
CLILink: <a href={CLI_URL} target="_blank"/>}}>info.credits</Trans>

export const creditsInfo = () => <Trans components={{MSpeed: <a href={WEB_URL} target="_blank" />,
Github: <a href={PROJECT_URL} target="_blank" />, Donate: <a href={DONATION_URL} target="_blank" />}}>info.credits</Trans>

export const recommendationsInfo = (ping, down, up) => <Trans components={{Bold: <span className="dialog-value" />}}
values={{ping, down, up}}>info.recommendations_info</Trans>
5 changes: 0 additions & 5 deletions client/src/common/components/Dropdown/utils/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,4 @@ export const selectOptions = () => ({
"0 * * * *": t("options.cron.default"),
"0 0,3,6,9,12,15,18,21 * * *": t("options.cron.rare"),
"0 0,6,12,18 * * *": t("options.cron.really_rare")
});

export const exportOptions = () => ({
json: t("options.export.json"),
csv: t("options.export.csv")
});
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import {DialogContext, DialogProvider} from "@/common/contexts/Dialog";
import {t, changeLanguage} from "i18next";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faClose} from "@fortawesome/free-solid-svg-icons";
import {faClose, faGlobe} from "@fortawesome/free-solid-svg-icons";
import "./styles.sass";
import {languages} from "@/i18n";
import {useContext, useState} from "react";
import {ToastNotificationContext} from "@/common/contexts/ToastNotification";

export const Dialog = () => {
const [selectedLanguage, setSelectedLanguage] = useState(localStorage.getItem("language") || "en");
const updateToast = useContext(ToastNotificationContext);
const close = useContext(DialogContext);

const updateLanguage = () => {
changeLanguage(selectedLanguage);
updateToast(t('dropdown.language_changed'), "green", faGlobe);
close();
}

Expand Down
2 changes: 2 additions & 0 deletions client/src/common/components/LanguageDialog/styles.sass
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
cursor: pointer
transition: background-color 0.3s
border-radius: 0.5rem
border: 2px solid $darker-gray

&:hover
background-color: $darker-gray
Expand All @@ -37,6 +38,7 @@
.language-selected
background-color: $light-gray
color: $white
border: 2px solid $light-gray

&:hover
background-color: $light-gray
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {DialogContext, DialogProvider} from "@/common/contexts/Dialog";
import {t} from "i18next";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faClose} from "@fortawesome/free-solid-svg-icons";
import {faCheck, faClose} from "@fortawesome/free-solid-svg-icons";
import "./styles.sass";
import React, {useContext, useEffect, useState} from "react";
import OoklaImage from "./assets/img/ookla.webp";
Expand All @@ -10,6 +10,7 @@ import CloudflareImage from "./assets/img/cloudflare.webp";
import {jsonRequest, patchRequest} from "@/common/utils/RequestUtil";
import {Trans} from "react-i18next";
import {ConfigContext} from "@/common/contexts/Config";
import {ToastNotificationContext} from "@/common/contexts/ToastNotification";

export const providers = [
{id: "ookla", name: "Ookla", image: OoklaImage},
Expand All @@ -21,6 +22,7 @@ export const providers = [
export const Dialog = () => {
const close = useContext(DialogContext);
const [config, reloadConfig] = useContext(ConfigContext);
const updateToast = useContext(ToastNotificationContext);
const [provider, setProvider] = useState(config.provider || "ookla");

const [licenseAccepted, setLicenseAccepted] = useState(false);
Expand Down Expand Up @@ -61,6 +63,7 @@ export const Dialog = () => {
}

reloadConfig();
updateToast(t('dropdown.provider_changed'), "green", faCheck);

close();
}
Expand Down
69 changes: 69 additions & 0 deletions client/src/common/components/StorageDialog/StorageDialog.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import "./styles.sass";
import React, {useContext, useEffect, useState} from "react";
import {DialogContext, DialogProvider} from "@/common/contexts/Dialog";
import {t} from "i18next";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faClose, faDatabase, faGauge, faScrewdriverWrench} from "@fortawesome/free-solid-svg-icons";
import Speedtests from "./tabs/Speedtests";
import Configuration from "./tabs/Configuration";
import {jsonRequest} from "@/common/utils/RequestUtil";

const Dialog = () => {
const close = useContext(DialogContext);
const [storageSize, setStorageSize] = useState({size: 0, testCount: 0});

const [currentTab, setCurrentTab] = useState(1);

useEffect(() => {
jsonRequest("/storage").then((res) => {
setStorageSize(res);
});
}, []);

return (
<>
<div className="dialog-header">
<h4 className="dialog-text">{t("dropdown.storage")}</h4>
<FontAwesomeIcon icon={faClose} className="dialog-text dialog-icon" onClick={() => close()}/>
</div>
<div className="storage-dialog">
<div className="storage-options">
<div className="storage-top">
<div className={"storage-tab" + (1 === currentTab ? " storage-item-active" : "")}
onClick={() => setCurrentTab(1)}>
<FontAwesomeIcon icon={faGauge}/>
<p>{t("storage.speedtests")}</p>
</div>
<div className={"storage-tab" + (2 === currentTab ? " storage-item-active" : "")}
onClick={() => setCurrentTab(2)}>
<FontAwesomeIcon icon={faScrewdriverWrench}/>
<p>{t("storage.configuration")}</p>
</div>

</div>
<div className="storage-bottom">
<div className="storage-tab reset-cursor">
<FontAwesomeIcon icon={faDatabase}/>
<p>{Math.round(storageSize.size / 1024)} KB</p>
</div>
</div>
</div>

<div className="storage-manager">
{currentTab === 1 && <Speedtests tests={storageSize.testCount}/>}
{currentTab === 2 && <Configuration/>}
</div>


</div>
</>
);
}

export const StorageDialog = ({onClose}) => {
return (
<DialogProvider close={onClose}>
<Dialog/>
</DialogProvider>
)
}
1 change: 1 addition & 0 deletions client/src/common/components/StorageDialog/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {StorageDialog as default} from "./StorageDialog";
Loading

0 comments on commit 45772f8

Please sign in to comment.