import "./styles.sass";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {
faArrowDown,
faArrowUp,
faCircleNotch,
faClock,
faExclamationTriangle,
faKey,
faPen,
faServer,
faTableTennisPaddleBall,
faTrash
} from "@fortawesome/free-solid-svg-icons";
import React, {useContext, useEffect, useState} from "react";
import {NodeContext} from "@/common/contexts/Node";
import {useAlert} from "@/common/contexts/Alert";
import {ToastNotificationContext} from "@/common/contexts/ToastNotification";
import {baseRequest, patchRequest} from "@/common/utils/RequestUtil";
import {t} from "i18next";
import {Trans} from "react-i18next";
import {getIconBySpeed} from "@/common/utils/TestUtil";
import {ConfigContext} from "@/common/contexts/Config";
import {PreferencesContext} from "@/common/contexts/Preferences";
import {convertSpeed, getSpeedUnit} from "@/common/utils/FormatUtil";
import {useNavigate} from "react-router-dom";
import ContextMenu from "@/common/components/ContextMenu";
export const NodeContainer = (node) => {
const updateNodes = useContext(NodeContext)[1];
const updateCurrentNode = useContext(NodeContext)[3];
const reloadConfig = useContext(ConfigContext)[1];
const [preferences] = useContext(PreferencesContext);
const speedUnit = getSpeedUnit(preferences);
const updateToast = useContext(ToastNotificationContext);
const alert = useAlert();
const [nodeData, setNodeData] = useState(null);
const [nodeError, setNodeError] = useState(undefined);
const [contextMenu, setContextMenu] = useState(null);
const navigate = useNavigate();
const prefix = node.currentNode ? "" : "/nodes/" + node.id;
const updateData = async () => {
const testRequest = await baseRequest(prefix + "/speedtests?limit=1");
if (testRequest.status === 401) return setNodeError("PASSWORD_CHANGED");
if (!testRequest.ok) return setNodeError("SERVER_NOT_REACHABLE");
const tests = await testRequest.json();
if (tests.length < 0) return setNodeError("SERVER_NOT_REACHABLE");
const configRequest = await baseRequest(prefix + "/config");
if (configRequest.status === 401) return setNodeError("PASSWORD_CHANGED");
if (!configRequest.ok) return setNodeError("SERVER_NOT_REACHABLE");
const config = await configRequest.json();
if (config.viewMode) return setNodeError("PASSWORD_CHANGED");
if (tests[0] === undefined) return setNodeData({pending: true});
setNodeError(undefined);
setNodeData({
ping: tests[0]?.ping,
download: Math.round(tests[0]?.download),
upload: Math.round(tests[0]?.upload),
pingIcon: getIconBySpeed(tests[0]?.ping, config.ping, false),
downloadIcon: getIconBySpeed(tests[0]?.download, config.download, true),
uploadIcon: getIconBySpeed(tests[0]?.upload, config.upload, true)
});
}
const updatePassword = async (wrong = false) => {
const result = await alert.openInput(t("nodes.password_outdated"), {
inputType: "password",
description: wrong ?
{t("dialog.password.wrong")} : t("nodes.update_password"),
placeholder: t("dialog.password.placeholder"),
buttonText: t("dialog.update")
});
if (result) {
const res = await (await baseRequest(`/nodes/${node.id}/password`, "PATCH", {password: result})).json();
if (res.type === "PASSWORD_UPDATED") {
updateData().catch(() => setNodeError("SERVER_NOT_REACHABLE"));
updateToast(t("nodes.password_updated"), "green", faKey);
} else {
updatePassword(true);
}
}
};
useEffect(() => {
updateData().catch(() => setNodeError("SERVER_NOT_REACHABLE"));
const interval = setInterval(() => updateData().catch(() => setNodeError("SERVER_NOT_REACHABLE")), 10000);
return () => clearInterval(interval);
}, []);
const switchNode = () => {
if (nodeError || !nodeData) {
if (nodeError === "PASSWORD_CHANGED") updatePassword();
return;
}
navigate("/");
updateCurrentNode(node.id);
reloadConfig();
}
const onContext = async (event) => {
event.preventDefault();
event.stopPropagation();
if (node.currentNode) return;
setContextMenu({x: event.clientX, y: event.clientY});
};
const closeContextMenu = () => setContextMenu(null);
const handleRename = async () => {
const newName = await alert.openInput(t("nodes.rename.title"), {
placeholder: t("nodes.rename.placeholder"),
buttonText: t("dialog.save"),
value: node.name
});
if (newName && newName !== node.name) {
await patchRequest(`/nodes/${node.id}/name`, {name: newName});
updateToast(t("nodes.rename.success"), "green", faPen);
updateNodes();
}
};
const handleDelete = async () => {
const confirmed = await alert.openConfirm(
t("nodes.delete.title"),
{node.url.replace(/(^\w+:|^)\/\//, '')}