1import {useContext, useEffect, useState} from "react";
2import {faArrowDown, faArrowUp, faClockRotateLeft, faPingPongPaddleBall, faWaveSquare} from "@fortawesome/free-solid-svg-icons";
3import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
4import {generateRelativeTime} from "./utils";
5import {StatusContext} from "@/common/contexts/Status";
6import {useAlert} from "@/common/contexts/Alert";
7import {SpeedtestContext} from "@/common/contexts/Speedtests";
8import {ConfigContext} from "@/common/contexts/Config";
9import BorderAnimation from "@/common/components/BorderAnimation";
10import "./styles.sass";
11import {getIconBySpeed} from "@/common/utils/TestUtil";
12import {downloadInfo, jitterInfo, latestTestInfo, pingInfo, uploadInfo} from "@/pages/Home/components/LatestTest/utils/dialogs";
13import {t} from "i18next";
14import {PreferencesContext} from "@/common/contexts/Preferences";
15import {convertSpeed, getSpeedUnit} from "@/common/utils/FormatUtil";
16
17const LoadingValue = ({ children }) => {
18 return (
19 <span className="loading-value">{children}</span>
20 );
21};
22
23const LatestTestComponent = () => {
24 const status = useContext(StatusContext)[0];
25 const [latest, setLatest] = useState(null);
26 const [latestTestTime, setLatestTestTime] = useState("N/A");
27 const alert = useAlert();
28 const {speedtests} = useContext(SpeedtestContext);
29 const config = useContext(ConfigContext)[0];
30 const [preferences] = useContext(PreferencesContext);
31 const speedUnit = getSpeedUnit(preferences);
32
33 useEffect(() => {
34 setLatest(speedtests.length !== 0 ? speedtests[0] : {ping: "N/A", download: "N/A", upload: "N/A"});
35 }, [speedtests]);
36
37 useEffect(() => {
38 if (latest) setLatestTestTime(generateRelativeTime(latest.created));
39 const interval = setInterval(() => setLatestTestTime(generateRelativeTime(latest ? latest.created : 0)), 1000);
40 return () => clearInterval(interval);
41 }, [latest]);
42
43 if (Object.entries(config).length === 0) return (<></>);
44 if (latest === null) return (<></>);
45
46 const getAreaClass = () => {
47 if (status.running) return "analyse-area test-running";
48 if (status.paused) return "analyse-area tests-paused";
49 return "analyse-area pulse";
50 };
51
52 const showPingInfo = () => {
53 const info = pingInfo();
54 alert.openAlert(info.title, info.description, {buttonText: info.buttonText});
55 };
56
57 const showJitterInfo = () => {
58 const info = jitterInfo();
59 alert.openAlert(info.title, info.description, {buttonText: info.buttonText});
60 };
61
62 const showDownloadInfo = () => {
63 const info = downloadInfo();
64 alert.openAlert(info.title, info.description, {buttonText: info.buttonText});
65 };
66
67 const showUploadInfo = () => {
68 const info = uploadInfo();
69 alert.openAlert(info.title, info.description, {buttonText: info.buttonText});
70 };
71
72 const showLatestTestInfo = () => {
73 const info = latestTestInfo(latest, preferences);
74 alert.openAlert(info.title, info.description, {buttonText: info.buttonText});
75 };
76
77 return (
78 <div className={getAreaClass()}>
79 {status.running && <BorderAnimation />}
80 <div className="inner-container">
81 <div className="container-header">
82 <FontAwesomeIcon onClick={showPingInfo} icon={faPingPongPaddleBall}
83 className={"container-icon help-icon icon-" + getIconBySpeed(latest.ping, config.ping, false)}/>
84 <h2 className="container-text">{t("latest.ping")}<span
85 className="container-subtext">{t("latest.ping_unit")}</span></h2>
86 </div>
87 <div className="container-main">
88 <h2>
89 {status.running ? (
90 <LoadingValue>{latest.ping === -1 ? "N/A" : latest.ping}</LoadingValue>
91 ) : (
92 latest.ping === -1 ? "N/A" : latest.ping
93 )}
94 {latest.jitter !== null && latest.jitter !== undefined && (
95 <span className="jitter-suffix" onClick={showJitterInfo} title={t("latest.jitter")}>
96 <FontAwesomeIcon icon={faWaveSquare} className="jitter-icon" />{latest.jitter}
97 </span>
98 )}
99 </h2>
100 </div>
101 </div>
102
103 <div className="inner-container">
104 <div className="container-header">
105 <FontAwesomeIcon onClick={showDownloadInfo} icon={faArrowDown}
106 className={"container-icon help-icon icon-" + getIconBySpeed(latest.download, config.download, true)}/>
107 <h2 className="container-text">{t("latest.down")}<span
108 className="container-subtext">{speedUnit}</span></h2>
109 </div>
110 <div className="container-main">
111 <h2>
112 {status.running ? (
113 <LoadingValue>{latest.download === -1 ? "N/A" : convertSpeed(latest.download, preferences)}</LoadingValue>
114 ) : (
115 latest.download === -1 ? "N/A" : convertSpeed(latest.download, preferences)
116 )}
117 </h2>
118 </div>
119 </div>
120
121 <div className="mobile-break"></div>
122
123 <div className="inner-container">
124 <div className="container-header">
125 <FontAwesomeIcon onClick={showUploadInfo} icon={faArrowUp}
126 className={"container-icon help-icon icon-" + getIconBySpeed(latest.upload, config.upload, true)}/>
127 <h2 className="container-text">{t("latest.up")}<span
128 className="container-subtext">{speedUnit}</span></h2>
129 </div>
130 <div className="container-main">
131 <h2>
132 {status.running ? (
133 <LoadingValue>{latest.upload === -1 ? "N/A" : convertSpeed(latest.upload, preferences)}</LoadingValue>
134 ) : (
135 latest.upload === -1 ? "N/A" : convertSpeed(latest.upload, preferences)
136 )}
137 </h2>
138 </div>
139 </div>
140
141 <div className="inner-container">
142 <div className="container-header">
143 <FontAwesomeIcon onClick={showLatestTestInfo} icon={faClockRotateLeft}
144 className="container-icon icon-blue help-icon"/>
145 <h2 className="container-text">{t("latest.latest")}<span
146 className="container-subtext">{t("latest.before")}</span></h2>
147 </div>
148 <div className="container-main">
149 <h2>
150 {status.running ? (
151 <LoadingValue>{latestTestTime}</LoadingValue>
152 ) : (
153 latestTestTime
154 )}
155 </h2>
156 </div>
157 </div>
158 </div>
159 )
160}
161
162export default LatestTestComponent;