mirror of
https://github.com/gnmyt/myspeed.git
synced 2026-02-10 23:58:38 -06:00
Migrate Tooltips and OptimalValuesDialog to new system
This commit is contained in:
@@ -10,7 +10,7 @@ import {
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import DropdownComponent from "../Dropdown/DropdownComponent";
|
||||
import { InputDialogContext } from "@/common/contexts/InputDialog";
|
||||
import { useAlert } from "@/common/contexts/Alert";
|
||||
import { StatusContext } from "@/common/contexts/Status";
|
||||
import { SpeedtestContext } from "@/common/contexts/Speedtests";
|
||||
import { jsonRequest, postRequest } from "@/common/utils/RequestUtil";
|
||||
@@ -23,6 +23,7 @@ import { Trans } from "react-i18next";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import Pagination from "./components/Pagination";
|
||||
import AboutDialog from "@/common/components/AboutDialog";
|
||||
import Tooltip from "@/common/components/Tooltip";
|
||||
|
||||
const HeaderComponent = () => {
|
||||
const findNode = useContext(NodeContext)[4];
|
||||
@@ -30,7 +31,7 @@ const HeaderComponent = () => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
const [setDialog] = useContext(InputDialogContext);
|
||||
const alert = useAlert();
|
||||
const [icon, setIcon] = useState(faGear);
|
||||
const [status, updateStatus, setRunning] = useContext(StatusContext);
|
||||
const {updateTests} = useContext(SpeedtestContext);
|
||||
@@ -44,35 +45,38 @@ const HeaderComponent = () => {
|
||||
setIcon(isDropdownOpen ? faGear : faClose);
|
||||
}
|
||||
|
||||
const showDemoDialog = () => setDialog({
|
||||
title: t("preview.title"),
|
||||
description: <Trans components={{ Link: <a href={WEB_URL + "/install"} target="_blank" /> }}>preview.description</Trans>,
|
||||
buttonText: t("dialog.okay")
|
||||
});
|
||||
const showDemoDialog = () => alert.openAlert(
|
||||
t("preview.title"),
|
||||
<Trans components={{ Link: <a href={WEB_URL + "/install"} target="_blank" /> }}>preview.description</Trans>,
|
||||
{ buttonText: t("dialog.okay") }
|
||||
);
|
||||
|
||||
const showPasswordDialog = () => setDialog({
|
||||
title: t("header.admin_login"),
|
||||
placeholder: t("dialog.password.placeholder"),
|
||||
description: localStorage.getItem("password") ? <span className="icon-red">{t("dialog.password.wrong")}</span> : "",
|
||||
type: "password",
|
||||
buttonText: t("dialog.login"),
|
||||
onSuccess: (value) => {
|
||||
localStorage.setItem("password", value);
|
||||
const showPasswordDialog = async () => {
|
||||
const result = await alert.openInput(t("header.admin_login"), {
|
||||
placeholder: t("dialog.password.placeholder"),
|
||||
description: localStorage.getItem("password") ? <span className="icon-red">{t("dialog.password.wrong")}</span> : "",
|
||||
inputType: "password",
|
||||
buttonText: t("dialog.login")
|
||||
});
|
||||
|
||||
if (result) {
|
||||
localStorage.setItem("password", result);
|
||||
reloadConfig();
|
||||
checkConfig().then((config) => config?.viewMode ? showPasswordDialog() : false).catch(() => showPasswordDialog());
|
||||
},
|
||||
onClose: () => {
|
||||
const newConfig = await checkConfig().catch(() => null);
|
||||
if (newConfig?.viewMode) {
|
||||
showPasswordDialog();
|
||||
}
|
||||
} else {
|
||||
localStorage.removeItem("password");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const startSpeedtest = async () => {
|
||||
await updateStatus();
|
||||
if (status.paused) return setDialog({
|
||||
title: t("failed"),
|
||||
description: t("header.paused"),
|
||||
buttonText: t("dialog.okay")
|
||||
});
|
||||
if (status.paused) {
|
||||
alert.openAlert(t("failed"), t("header.paused"), { buttonText: t("dialog.okay") });
|
||||
return;
|
||||
}
|
||||
|
||||
if (status.running) return;
|
||||
|
||||
@@ -101,7 +105,7 @@ const HeaderComponent = () => {
|
||||
|
||||
return (
|
||||
<header>
|
||||
{showAboutDialog && <AboutDialog onClose={() => setShowAboutDialog(false)}/>}
|
||||
<AboutDialog open={showAboutDialog} onClose={() => setShowAboutDialog(false)}/>
|
||||
<div className="header-main">
|
||||
<div className="header-left">
|
||||
{config.viewMode && <h2>{t("header.title")}</h2>}
|
||||
@@ -115,38 +119,43 @@ const HeaderComponent = () => {
|
||||
<div className="header-right">
|
||||
{updateAvailable ?
|
||||
<div><FontAwesomeIcon icon={faCircleArrowUp} className="header-icon icon-orange update-icon"
|
||||
onClick={() => setDialog({
|
||||
title: t("header.new_update"),
|
||||
buttonText: t("dialog.okay"),
|
||||
description: updateInfo(updateAvailable)
|
||||
})} /></div> : <></>}
|
||||
onClick={() => alert.openAlert(
|
||||
t("header.new_update"),
|
||||
updateInfo(updateAvailable),
|
||||
{ buttonText: t("dialog.okay") }
|
||||
)} /></div> : <></>}
|
||||
|
||||
{!(status.paused || config.viewMode) ? <div className="tooltip-element tooltip-bottom">
|
||||
<FontAwesomeIcon icon={faGaugeHigh}
|
||||
className={"header-icon " + (status.running ? "test-running" : "")}
|
||||
onClick={startSpeedtest} />
|
||||
<span className="tooltip">{t("header." + (status.running ? "running_tooltip" : "start_tooltip"))}</span>
|
||||
</div> : <></>}
|
||||
{!(status.paused || config.viewMode) ?
|
||||
<Tooltip content={t("header." + (status.running ? "running_tooltip" : "start_tooltip"))} position="bottom">
|
||||
<FontAwesomeIcon icon={faGaugeHigh}
|
||||
className={"header-icon " + (status.running ? "test-running" : "")}
|
||||
onClick={startSpeedtest} />
|
||||
</Tooltip>
|
||||
: <></>}
|
||||
|
||||
{(config.viewMode ? <div className="tooltip-element tooltip-bottom">
|
||||
<FontAwesomeIcon icon={faLock} className={"header-icon"} onClick={showPasswordDialog} />
|
||||
<span className="tooltip">{t("header.admin_login")}</span>
|
||||
</div> : <></>)}
|
||||
{config.viewMode ?
|
||||
<Tooltip content={t("header.admin_login")} position="bottom">
|
||||
<FontAwesomeIcon icon={faLock} className={"header-icon"} onClick={showPasswordDialog} />
|
||||
</Tooltip>
|
||||
: <></>}
|
||||
|
||||
{(config.previewMode ? <div className="tooltip-element tooltip-bottom">
|
||||
<FontAwesomeIcon icon={faDownload} className={"header-icon"} onClick={openDownloadPage} />
|
||||
<span className="tooltip">{t("header.download")}</span>
|
||||
</div> : <></>)}
|
||||
{config.previewMode ?
|
||||
<Tooltip content={t("header.download")} position="bottom">
|
||||
<FontAwesomeIcon icon={faDownload} className={"header-icon"} onClick={openDownloadPage} />
|
||||
</Tooltip>
|
||||
: <></>}
|
||||
|
||||
{!config.viewMode && <div className="tooltip-element tooltip-bottom">
|
||||
<FontAwesomeIcon icon={faServer} className="header-icon" onClick={() => navigate("/nodes")} />
|
||||
<span className="tooltip">{t("header.servers")}</span>
|
||||
</div>}
|
||||
{!config.viewMode &&
|
||||
<Tooltip content={t("header.servers")} position="bottom">
|
||||
<FontAwesomeIcon icon={faServer} className="header-icon" onClick={() => navigate("/nodes")} />
|
||||
</Tooltip>
|
||||
}
|
||||
|
||||
<div className="tooltip-element tooltip-bottom" id="open-header">
|
||||
<FontAwesomeIcon icon={icon} className="header-icon" onClick={switchDropdown} />
|
||||
<span className="tooltip">{t("dropdown.settings")}</span>
|
||||
</div>
|
||||
<Tooltip content={t("dropdown.settings")} position="bottom">
|
||||
<div id="open-header">
|
||||
<FontAwesomeIcon icon={icon} className="header-icon" onClick={switchDropdown} />
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<DropdownComponent isOpen={isDropdownOpen} switchDropdown={switchDropdown} />
|
||||
|
||||
@@ -15,9 +15,7 @@ header
|
||||
.header-right
|
||||
display: flex
|
||||
justify-content: flex-end
|
||||
|
||||
.header-right svg
|
||||
margin-left: 15px
|
||||
gap: 15px
|
||||
|
||||
.header-main *
|
||||
font-size: 24pt
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
import {DialogContext, DialogProvider} from "@/common/contexts/Dialog";
|
||||
import {Dialog, DialogHeader, DialogBody, DialogFooter} from "@/common/contexts/Dialog";
|
||||
import {t} from "i18next";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import {faArrowDown, faArrowUp, faCheck, faClose, faExclamationTriangle, faTableTennis, faWandMagicSparkles} from "@fortawesome/free-solid-svg-icons";
|
||||
import {faArrowDown, faArrowUp, faCheck, faExclamationTriangle, faTableTennis, faWandMagicSparkles} from "@fortawesome/free-solid-svg-icons";
|
||||
import "./styles.sass";
|
||||
import React, {useContext, useEffect, useState} from "react";
|
||||
import {jsonRequest, patchRequest} from "@/common/utils/RequestUtil";
|
||||
import {ConfigContext} from "@/common/contexts/Config";
|
||||
import {ToastNotificationContext} from "@/common/contexts/ToastNotification";
|
||||
|
||||
export const Dialog = () => {
|
||||
const close = useContext(DialogContext);
|
||||
export const OptimalValuesDialog = ({open, onClose}) => {
|
||||
const [config, reloadConfig] = useContext(ConfigContext);
|
||||
const updateToast = useContext(ToastNotificationContext);
|
||||
|
||||
const [ping, setPing] = useState(config.ping || "");
|
||||
const [download, setDownload] = useState(config.download || "");
|
||||
const [upload, setUpload] = useState(config.upload || "");
|
||||
const [recommendations, setRecommendations] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
jsonRequest("/recommendations").then((result) => {
|
||||
if (!result.message) setRecommendations(result);
|
||||
}).catch(() => {});
|
||||
}, []);
|
||||
}, [open]);
|
||||
|
||||
const applyRecommendations = () => {
|
||||
if (recommendations) {
|
||||
@@ -32,17 +31,15 @@ export const Dialog = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const update = async () => {
|
||||
const update = async (close) => {
|
||||
if ((ping && /[^0-9.]/.test(ping)) || (download && /[^0-9.]/.test(download)) || (upload && /[^0-9.]/.test(upload))) {
|
||||
updateToast(t("dropdown.invalid"), "red", faExclamationTriangle);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (ping !== config.ping) await patchRequest("/config/ping", {value: ping});
|
||||
if (download !== config.download) await patchRequest("/config/download", {value: download});
|
||||
if (upload !== config.upload) await patchRequest("/config/upload", {value: upload});
|
||||
|
||||
reloadConfig();
|
||||
updateToast(t("dropdown.changes_applied"), "green", faCheck);
|
||||
close();
|
||||
@@ -52,65 +49,60 @@ export const Dialog = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="dialog-header">
|
||||
<h4 className="dialog-text">{t("optimal_values.title")}</h4>
|
||||
<FontAwesomeIcon icon={faClose} className="dialog-text dialog-icon" onClick={() => close()}/>
|
||||
</div>
|
||||
<div className="optimal-values-content">
|
||||
<div className="optimal-values-speeds">
|
||||
<div className="optimal-values-speed">
|
||||
<div className="optimal-values-speed-header">
|
||||
<FontAwesomeIcon icon={faTableTennis}/>
|
||||
<div className="optimal-values-speed-text">
|
||||
<h2>{t("latest.ping")}</h2>
|
||||
<p>{t("welcome.ms")}</p>
|
||||
<Dialog open={open} onClose={onClose} className="optimal-values-dialog">
|
||||
{({close}) => (
|
||||
<>
|
||||
<DialogHeader onClose={close}>{t("optimal_values.title")}</DialogHeader>
|
||||
<DialogBody>
|
||||
<div className="optimal-values-content">
|
||||
<div className="optimal-values-speeds">
|
||||
<div className="optimal-values-speed">
|
||||
<div className="optimal-values-speed-header">
|
||||
<FontAwesomeIcon icon={faTableTennis}/>
|
||||
<div className="optimal-values-speed-text">
|
||||
<h2>{t("latest.ping")}</h2>
|
||||
<p>{t("welcome.ms")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<input type="number" placeholder={recommendations?.ping || ""} className="dialog-input"
|
||||
value={ping} onChange={(e) => setPing(e.target.value)}/>
|
||||
</div>
|
||||
<div className="optimal-values-speed">
|
||||
<div className="optimal-values-speed-header">
|
||||
<FontAwesomeIcon icon={faArrowDown}/>
|
||||
<div className="optimal-values-speed-text">
|
||||
<h2>{t("latest.down")}</h2>
|
||||
<p>{t("welcome.mbps")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<input type="number" placeholder={recommendations?.download || ""} className="dialog-input"
|
||||
value={download} onChange={(e) => setDownload(e.target.value)}/>
|
||||
</div>
|
||||
<div className="optimal-values-speed">
|
||||
<div className="optimal-values-speed-header">
|
||||
<FontAwesomeIcon icon={faArrowUp}/>
|
||||
<div className="optimal-values-speed-text">
|
||||
<h2>{t("latest.up")}</h2>
|
||||
<p>{t("welcome.mbps")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<input type="number" placeholder={recommendations?.upload || ""} className="dialog-input"
|
||||
value={upload} onChange={(e) => setUpload(e.target.value)}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="number" placeholder={recommendations?.ping || ""} className="dialog-input"
|
||||
value={ping} onChange={(e) => setPing(e.target.value)}/>
|
||||
</div>
|
||||
<div className="optimal-values-speed">
|
||||
<div className="optimal-values-speed-header">
|
||||
<FontAwesomeIcon icon={faArrowDown}/>
|
||||
<div className="optimal-values-speed-text">
|
||||
<h2>{t("latest.down")}</h2>
|
||||
<p>{t("welcome.mbps")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<input type="number" placeholder={recommendations?.download || ""} className="dialog-input"
|
||||
value={download} onChange={(e) => setDownload(e.target.value)}/>
|
||||
</div>
|
||||
<div className="optimal-values-speed">
|
||||
<div className="optimal-values-speed-header">
|
||||
<FontAwesomeIcon icon={faArrowUp}/>
|
||||
<div className="optimal-values-speed-text">
|
||||
<h2>{t("latest.up")}</h2>
|
||||
<p>{t("welcome.mbps")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<input type="number" placeholder={recommendations?.upload || ""} className="dialog-input"
|
||||
value={upload} onChange={(e) => setUpload(e.target.value)}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="dialog-buttons">
|
||||
{recommendations && (
|
||||
<button className="dialog-btn" onClick={applyRecommendations}>
|
||||
<FontAwesomeIcon icon={faWandMagicSparkles}/>
|
||||
<span>{t("optimal_values.use_recommended")}</span>
|
||||
</button>
|
||||
)}
|
||||
<button className="dialog-btn" onClick={update}>{t("dialog.update")}</button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const OptimalValuesDialog = (props) => {
|
||||
return (
|
||||
<DialogProvider close={props.onClose}>
|
||||
<Dialog/>
|
||||
</DialogProvider>
|
||||
</DialogBody>
|
||||
<DialogFooter>
|
||||
{recommendations && (
|
||||
<button className="dialog-btn" onClick={applyRecommendations}>
|
||||
<FontAwesomeIcon icon={faWandMagicSparkles}/>
|
||||
<span>{t("optimal_values.use_recommended")}</span>
|
||||
</button>
|
||||
)}
|
||||
<button className="dialog-btn" onClick={() => update(close)}>{t("dialog.update")}</button>
|
||||
</DialogFooter>
|
||||
</>
|
||||
)}
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user