diff --git a/public/translations/de.json b/public/translations/de.json index aa65ce17..b06de27b 100644 --- a/public/translations/de.json +++ b/public/translations/de.json @@ -471,6 +471,7 @@ "incident_detected": "Ein Sicherheitsvorfall wurde entdeckt! Bitte überprüfe deine Logs!" }, "server": { + "boxineDisabled": "Boxine/Toniecloud ist derzeit deaktiviert. Aktiviere die Einstellung 'Cloud enabled', um die Verbindung zu Boxine/Toniecloud wiederherzustellen.", "boxineStatusOffline": "Nicht mit Boxine/Toniecloud verbunden", "boxineStatusOnline": "Mit Boxine/Toniecloud verbunden", "teddycloudStatusOffline": "TeddyCloud Server offline", diff --git a/public/translations/en.json b/public/translations/en.json index 4234e366..22f1cb7e 100644 --- a/public/translations/en.json +++ b/public/translations/en.json @@ -471,6 +471,7 @@ "incident_detected": "Security Incident Detected! Please check your logs!" }, "server": { + "boxineDisabled": "Boxine/Toniecloud is currently disabled. Enable setting 'Cloud enabled' to reconnect to Boxine/Toniecloud.", "boxineStatusOffline": "Not connected to Boxine/Toniecloud", "boxineStatusOnline": "Connected to Boxine/Toniecloud", "teddycloudStatusOffline": "Teddycloud server offline", diff --git a/public/translations/es.json b/public/translations/es.json index 95cab9c8..729e74f1 100644 --- a/public/translations/es.json +++ b/public/translations/es.json @@ -471,6 +471,7 @@ "incident_detected": "¡Incidente de seguridad detectado! ¡Por favor, revisa tus registros!" }, "server": { + "boxineDisabled": "Boxine/Toniecloud está actualmente desactivado. Activa la opción 'Cloud enabled' para volver a conectarte a Boxine/Toniecloud.", "boxineStatusOffline": "No conectado a Boxine/Toniecloud", "boxineStatusOnline": "Conectado a Boxine/Toniecloud", "teddycloudStatusOffline": "Servidor de Teddycloud desconectado", diff --git a/public/translations/fr.json b/public/translations/fr.json index 825723ce..5c20bf4b 100644 --- a/public/translations/fr.json +++ b/public/translations/fr.json @@ -471,6 +471,7 @@ "incident_detected": "Incident de sécurité détecté ! Veuillez vérifier vos journaux !" }, "server": { + "boxineDisabled": "Boxine/Toniecloud est actuellement désactivé. Active le paramètre 'Cloud enabled' pour te reconnecter à Boxine/Toniecloud.", "boxineStatusOffline": "Non connecté à Boxine/Toniecloud", "boxineStatusOnline": "Connecté à Boxine/Toniecloud", "teddycloudStatusOffline": "Serveur TeddyCloud hors ligne", diff --git a/src/App.tsx b/src/App.tsx index 706bdd70..e4870bfc 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -39,6 +39,7 @@ import { SystemSoundsPage } from "./pages/tonies/SystemSoundsPage"; import { TonieAudioPlaylistsPage } from "./pages/tonies/TonieAudioPlaylistsPage"; import { ToniesPage } from "./pages/tonies/ToniesPage"; import { detectColorScheme } from "./utils/browserUtils"; +import { TeddyCloudProvider } from "./utils/TeddyCloudContext"; function App() { const { defaultAlgorithm, darkAlgorithm } = theme; @@ -101,68 +102,76 @@ function App() { }, }} > -
- - - - - - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } - /> - } /> - } - /> - } - /> - } /> - } - /> - } - /> - } /> - } /> - } /> - } /> - } /> - } /> - } - /> - } /> - } /> - } /> - } /> - - - - - - -
+ +
+ + + + + + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } + /> + } + /> + } + /> + } + /> + } /> + } + /> + } + /> + } /> + } /> + } /> + } /> + } /> + } /> + } + /> + } + /> + } /> + } /> + } /> + + + + + + +
+
); } diff --git a/src/components/form/CertificatesDragAndDrop.tsx b/src/components/form/CertificatesDragAndDrop.tsx index ecda8921..e0996ff1 100644 --- a/src/components/form/CertificatesDragAndDrop.tsx +++ b/src/components/form/CertificatesDragAndDrop.tsx @@ -4,11 +4,13 @@ import { InboxOutlined } from "@ant-design/icons"; import { ApiUploadCertPostRequest, TeddyCloudApi } from "../../api"; import { defaultAPIConfig } from "../../config/defaultApiConfig"; +import { useTeddyCloud } from "../../utils/TeddyCloudContext"; const api = new TeddyCloudApi(defaultAPIConfig()); export const CertificateDragNDrop: React.FC<{ overlay?: string }> = ({ overlay }) => { const { t } = useTranslation(); + const { setFetchCloudStatus } = useTeddyCloud(); const handleUpload = async (file: UploadFile) => { const formData = new FormData(); @@ -35,6 +37,7 @@ export const CertificateDragNDrop: React.FC<{ overlay?: string }> = ({ overlay } filename: file.name, }) ); + setFetchCloudStatus((prev) => !prev); } catch (err) { message.error( t("settings.certificates.uploadFailed", { diff --git a/src/components/form/SwitchField.tsx b/src/components/form/SwitchField.tsx index 95596e77..b2466845 100644 --- a/src/components/form/SwitchField.tsx +++ b/src/components/form/SwitchField.tsx @@ -6,6 +6,7 @@ import { useField } from "formik"; import { TeddyCloudApi } from "../../api"; import { defaultAPIConfig } from "../../config/defaultApiConfig"; +import { useTeddyCloud } from "../../utils/TeddyCloudContext"; type SwitchFieldProps = { name: string; @@ -21,6 +22,7 @@ type SwitchFieldProps = { const SwitchField = (props: SwitchFieldProps & SwitchProps) => { const { t } = useTranslation(); + const { setFetchCloudStatus } = useTeddyCloud(); const { name, label, valueConverter, description, overlayed: initialOverlayed, overlayId, ...switchProps } = props; const [field, meta, { setValue }] = useField(name!); const [overlayed, setOverlayed] = useState(initialOverlayed); @@ -97,6 +99,11 @@ const SwitchField = (props: SwitchFieldProps & SwitchProps) => { triggerWriteConfig(); message.success(t("settings.saved")); }) + .then(() => { + if (name === "cloud.enabled") { + setFetchCloudStatus((prev) => !prev); + } + }) .catch((e) => { message.error("Error while saving config to file."); }); diff --git a/src/components/header/ServerStatus.tsx b/src/components/header/ServerStatus.tsx index 52ee19c4..939f1995 100644 --- a/src/components/header/ServerStatus.tsx +++ b/src/components/header/ServerStatus.tsx @@ -1,21 +1,35 @@ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { Space, Tag, Tooltip } from "antd"; -import { CheckCircleOutlined, CloseCircleOutlined } from "@ant-design/icons"; +import { CheckCircleOutlined, CloseCircleOutlined, LockOutlined } from "@ant-design/icons"; import { defaultAPIConfig } from "../../config/defaultApiConfig"; -import { BoxineApi, BoxineForcedApi } from "../../api"; +import { BoxineApi, BoxineForcedApi, TeddyCloudApi } from "../../api"; import { HiddenDesktop, HiddenMobile } from "../StyledComponents"; +import { useTeddyCloud } from "../../utils/TeddyCloudContext"; const api = new BoxineApi(defaultAPIConfig()); const api2 = new BoxineForcedApi(defaultAPIConfig()); +const apiTC = new TeddyCloudApi(defaultAPIConfig()); export const ServerStatus = () => { const { t } = useTranslation(); + const { fetchCloudStatus } = useTeddyCloud(); const [boxineStatus, setBoxineStatus] = useState(false); + const [boxineEnabledStatus, setBoxineEnabledStatus] = useState(true); const [teddyStatus, setTeddyStatus] = useState(false); + const fetchBoxineEnabledStatus = async () => { + try { + const cloudEnabled = await apiTC.apiGetTeddyCloudSettingRaw("cloud.enabled"); + setBoxineEnabledStatus((await cloudEnabled.text()) === "true"); + } catch (err) { + console.log("Something went wrong getting cloud.enabled."); + setBoxineEnabledStatus(false); + } + }; + const fetchTime = async () => { try { const timeRequest = (await api.v1TimeGet()) as String; @@ -25,27 +39,58 @@ export const ServerStatus = () => { } catch (e) { setTeddyStatus(false); } - try { - const timeRequest2 = (await api2.reverseV1TimeGet()) as String; - if (timeRequest2.length === 10) { - setBoxineStatus(true); + if (boxineEnabledStatus) { + try { + const timeRequest2 = (await api2.reverseV1TimeGet()) as String; + + if (timeRequest2.length === 10) { + setBoxineStatus(true); + } + } catch (e) { + setBoxineStatus(false); } - } catch (e) { - setBoxineStatus(false); } }; useEffect(() => { + fetchBoxineEnabledStatus(); fetchTime(); }, []); + useEffect(() => { + fetchBoxineEnabledStatus(); + fetchTime(); + }, [fetchCloudStatus]); + + useEffect(() => { + fetchTime(); + }, [boxineEnabledStatus]); + return ( - + : } - color={boxineStatus ? "#87d068" : "#f50"} + icon={ + boxineEnabledStatus ? ( + boxineStatus ? ( + + ) : ( + + ) + ) : ( + + ) + } + color={boxineEnabledStatus ? (boxineStatus ? "#87d068" : "#f50") : "#faad14"} bordered={false} style={{ cursor: "help", color: "#001529" }} > diff --git a/src/pages/tonieboxes/boxsetup/esp32/ESP32BoxFlashingPage.tsx b/src/pages/tonieboxes/boxsetup/esp32/ESP32BoxFlashingPage.tsx index 405d2eff..018ba21a 100644 --- a/src/pages/tonieboxes/boxsetup/esp32/ESP32BoxFlashingPage.tsx +++ b/src/pages/tonieboxes/boxsetup/esp32/ESP32BoxFlashingPage.tsx @@ -33,6 +33,7 @@ import ConfirmationDialog from "../../../../components/utils/ConfirmationDialog" import AvailableBoxesModal, { connectESP32Explanation } from "../../../../components/tonieboxes/boxSetup/CommonContent"; import DotAnimation from "../../../../components/utils/DotAnimation"; import { isWebSerialSupported } from "../../../../utils/checkWebSerialSupport"; +import { useTeddyCloud } from "../../../../utils/TeddyCloudContext"; const api = new TeddyCloudApi(defaultAPIConfig()); @@ -72,6 +73,7 @@ const { Step } = Steps; export const ESP32BoxFlashingPage = () => { const { t } = useTranslation(); + const { setFetchCloudStatus } = useTeddyCloud(); const navigate = useNavigate(); const currentLanguage = i18n.language; @@ -863,6 +865,7 @@ export const ESP32BoxFlashingPage = () => { file: state.filename, }) ); + setFetchCloudStatus((prev) => !prev); } else if (!response.ok && response.status === 409) { hideLoading(); const errorMessage = await response.text(); diff --git a/src/utils/TeddyCloudContext.tsx b/src/utils/TeddyCloudContext.tsx new file mode 100644 index 00000000..61be9f71 --- /dev/null +++ b/src/utils/TeddyCloudContext.tsx @@ -0,0 +1,29 @@ +import { createContext, useContext, useState, ReactNode, Dispatch, SetStateAction } from "react"; + +interface TeddyCloudContextType { + fetchCloudStatus: boolean; + setFetchCloudStatus: Dispatch>; +} + +const TeddyCloudContext = createContext({ + fetchCloudStatus: false, + setFetchCloudStatus: () => {}, +}); + +interface TeddyCloudProviderProps { + children: ReactNode; +} + +export function TeddyCloudProvider({ children }: TeddyCloudProviderProps) { + const [fetchCloudStatus, setFetchCloudStatus] = useState(false); + + return ( + + {children} + + ); +} + +export function useTeddyCloud() { + return useContext(TeddyCloudContext); +}