diff --git a/src-ui/app/App.jsx b/src-ui/app/App.jsx index 896cbb34..4374f7a9 100644 --- a/src-ui/app/App.jsx +++ b/src-ui/app/App.jsx @@ -1,13 +1,27 @@ -import { useEffect, useRef } from "react"; import { useTranslation } from "react-i18next"; -import { useStartPython } from "@logics/useStartPython"; +import { + useWindow, +} from "@logics_common"; + +// import React from "react"; + +import { + StartPythonController, + UiLanguageController, + ConfigPageCloseTriggerController, + UiSizeController, + FontFamilyController, + TransparencyController, +} from "./_app_controllers/index.js"; + import { WindowTitleBar } from "./window_title_bar/WindowTitleBar"; import { MainPage } from "./main_page/MainPage"; import { ConfigPage } from "./config_page/ConfigPage"; import { SplashComponent } from "./splash_component/SplashComponent"; import { UpdatingComponent } from "./updating_component/UpdatingComponent"; import { ModalController } from "./modal_controller/ModalController"; +import { SnackbarController } from "./snackbar_controller/SnackbarController"; import styles from "./App.module.scss"; import { useIsBackendReady, useIsSoftwareUpdating } from "@logics_common"; @@ -18,9 +32,9 @@ export const App = () => { return (
- + - + @@ -45,160 +59,11 @@ const Contents = () => { +
: } ); -}; - -import { - useWindow, - useVolume, - useIsOpenedConfigPage, -} from "@logics_common"; - -import { - useUiLanguage, - useUiScaling, - useSelectedFontFamily, - useTransparency, -} from "@logics_configs"; - -import { - useMainFunction, -} from "@logics_main"; - -const StartPythonFacadeComponent = () => { - const { asyncStartPython } = useStartPython(); - const hasRunRef = useRef(false); - const { asyncFetchFonts } = useAsyncFetchFonts(); - - useEffect(() => { - if (!hasRunRef.current) { - asyncStartPython().then(() => { - startFeedingToWatchDog(); - asyncFetchFonts(); - }).catch((err) => { - console.error(err); - }); - } - return () => hasRunRef.current = true; - }, []); - - return null; -}; - -const UiLanguageController = () => { - const { currentUiLanguage } = useUiLanguage(); - const { i18n } = useTranslation(); - - useEffect(() => { - i18n.changeLanguage(currentUiLanguage.data); - }, [currentUiLanguage.data]); - return null; -}; - -import { useStore_MainFunctionsStateMemory } from "@store"; - -const ConfigPageCloseTrigger = () => { - const { currentIsOpenedConfigPage } = useIsOpenedConfigPage(); - const { currentMainFunctionsStateMemory, updateMainFunctionsStateMemory} = useStore_MainFunctionsStateMemory(); - const { - currentTranscriptionSendStatus, - setTranscriptionSend, - currentTranscriptionReceiveStatus, - setTranscriptionReceive, - } = useMainFunction(); - const { - currentMicThresholdCheckStatus, - volumeCheckStop_Mic, - currentSpeakerThresholdCheckStatus, - volumeCheckStop_Speaker, - } = useVolume(); - - - const memorizeLatestMainFunctionsState = () => { - updateMainFunctionsStateMemory({ - transcription_send: currentTranscriptionSendStatus.data, - transcription_receive: currentTranscriptionReceiveStatus.data, - }); - }; - - const restoreMainFunctionState = () => { - if (currentMainFunctionsStateMemory.data.transcription_send === true) setTranscriptionSend(true); - if (currentMainFunctionsStateMemory.data.transcription_receive === true) setTranscriptionReceive(true); - }; - - useEffect(() => { - if (currentIsOpenedConfigPage.data === true) { // When config page is opened. - memorizeLatestMainFunctionsState(); - if (currentTranscriptionSendStatus.data === true) setTranscriptionSend(false); - if (currentTranscriptionReceiveStatus.data === true) setTranscriptionReceive(false); - } else if (currentIsOpenedConfigPage.data === false) { // When config page is closed. - if (currentMicThresholdCheckStatus.data === true) volumeCheckStop_Mic(); - if (currentSpeakerThresholdCheckStatus.data === true) volumeCheckStop_Speaker(); - restoreMainFunctionState(); - } - }, [currentIsOpenedConfigPage.data]); - return null; -}; - -import React from "react"; -const UiSizeController = () => { - const { currentUiScaling } = useUiScaling(); - const font_size = 62.5 * currentUiScaling.data / 100; - - useEffect(() => { - document.documentElement.style.setProperty("font-size", `${font_size}%`); - }, [currentUiScaling.data]); - - return null; -}; - - -const FontFamilyController = () => { - const { currentSelectedFontFamily } = useSelectedFontFamily(); - useEffect(() => { - document.documentElement.style.setProperty("--font_family", currentSelectedFontFamily.data); - }, [currentSelectedFontFamily.data]); - - return null; -}; - -import { useStore_SelectableFontFamilyList } from "@store"; -import { arrayToObject } from "@utils"; - -import { invoke } from "@tauri-apps/api/tauri"; -const useAsyncFetchFonts = () => { - const { updateSelectableFontFamilyList } = useStore_SelectableFontFamilyList(); - const asyncFetchFonts = async () => { - try { - let fonts = await invoke("get_font_list"); - fonts = fonts.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" })); - updateSelectableFontFamilyList(arrayToObject(fonts)); - } catch (error) { - console.error("Error fetching fonts:", error); - } - }; - return { asyncFetchFonts }; -}; - - -const TransparencyController = () => { - const { currentTransparency } = useTransparency(); - useEffect(() => { - document.documentElement.style.setProperty("opacity", `${currentTransparency.data / 100}`); - }, [currentTransparency.data]); - - return null; -}; - -import { useStdoutToPython } from "@logics/useStdoutToPython"; -const startFeedingToWatchDog = () => { - const { asyncStdoutToPython } = useStdoutToPython(); - setInterval(() => { - asyncStdoutToPython("/run/feed_watchdog"); - }, 20000); // 20000ミリ秒 = 20秒 }; \ No newline at end of file diff --git a/src-ui/app/_app_controllers/ConfigPageCloseTriggerController.jsx b/src-ui/app/_app_controllers/ConfigPageCloseTriggerController.jsx new file mode 100644 index 00000000..7705aecc --- /dev/null +++ b/src-ui/app/_app_controllers/ConfigPageCloseTriggerController.jsx @@ -0,0 +1,55 @@ +import { useEffect } from "react"; + +import { + useVolume, + useIsOpenedConfigPage, +} from "@logics_common"; + +import { + useMainFunction, +} from "@logics_main"; + +import { useStore_MainFunctionsStateMemory } from "@store"; + +export const ConfigPageCloseTriggerController = () => { + const { currentIsOpenedConfigPage } = useIsOpenedConfigPage(); + const { currentMainFunctionsStateMemory, updateMainFunctionsStateMemory} = useStore_MainFunctionsStateMemory(); + const { + currentTranscriptionSendStatus, + setTranscriptionSend, + currentTranscriptionReceiveStatus, + setTranscriptionReceive, + } = useMainFunction(); + const { + currentMicThresholdCheckStatus, + volumeCheckStop_Mic, + currentSpeakerThresholdCheckStatus, + volumeCheckStop_Speaker, + } = useVolume(); + + + const memorizeLatestMainFunctionsState = () => { + updateMainFunctionsStateMemory({ + transcription_send: currentTranscriptionSendStatus.data, + transcription_receive: currentTranscriptionReceiveStatus.data, + }); + }; + + const restoreMainFunctionState = () => { + if (currentMainFunctionsStateMemory.data.transcription_send === true) setTranscriptionSend(true); + if (currentMainFunctionsStateMemory.data.transcription_receive === true) setTranscriptionReceive(true); + }; + + useEffect(() => { + if (currentIsOpenedConfigPage.data === true) { // When config page is opened. + memorizeLatestMainFunctionsState(); + if (currentTranscriptionSendStatus.data === true) setTranscriptionSend(false); + if (currentTranscriptionReceiveStatus.data === true) setTranscriptionReceive(false); + } else if (currentIsOpenedConfigPage.data === false) { // When config page is closed. + if (currentMicThresholdCheckStatus.data === true) volumeCheckStop_Mic(); + if (currentSpeakerThresholdCheckStatus.data === true) volumeCheckStop_Speaker(); + restoreMainFunctionState(); + } + }, [currentIsOpenedConfigPage.data]); + return null; +}; \ No newline at end of file diff --git a/src-ui/app/_app_controllers/FontFamilyController.jsx b/src-ui/app/_app_controllers/FontFamilyController.jsx new file mode 100644 index 00000000..f7110d1d --- /dev/null +++ b/src-ui/app/_app_controllers/FontFamilyController.jsx @@ -0,0 +1,11 @@ +import { useEffect } from "react"; +import { useSelectedFontFamily } from "@logics_configs"; + +export const FontFamilyController = () => { + const { currentSelectedFontFamily } = useSelectedFontFamily(); + useEffect(() => { + document.documentElement.style.setProperty("--font_family", currentSelectedFontFamily.data); + }, [currentSelectedFontFamily.data]); + + return null; +}; diff --git a/src-ui/app/_app_controllers/StartPythonController.jsx b/src-ui/app/_app_controllers/StartPythonController.jsx new file mode 100644 index 00000000..62bf0b28 --- /dev/null +++ b/src-ui/app/_app_controllers/StartPythonController.jsx @@ -0,0 +1,48 @@ +import { invoke } from "@tauri-apps/api/tauri"; +import { useEffect, useRef } from "react"; +import { useStartPython } from "@logics/useStartPython"; +import { useStdoutToPython } from "@logics/useStdoutToPython"; + +import { useStore_SelectableFontFamilyList } from "@store"; +import { arrayToObject } from "@utils"; + +export const StartPythonController = () => { + const { asyncStartPython } = useStartPython(); + const hasRunRef = useRef(false); + const { asyncFetchFonts } = useAsyncFetchFonts(); + + useEffect(() => { + if (!hasRunRef.current) { + asyncStartPython().then(() => { + startFeedingToWatchDogController(); + asyncFetchFonts(); + }).catch((err) => { + console.error(err); + }); + } + return () => hasRunRef.current = true; + }, []); + + return null; +}; + +const useAsyncFetchFonts = () => { + const { updateSelectableFontFamilyList } = useStore_SelectableFontFamilyList(); + const asyncFetchFonts = async () => { + try { + let fonts = await invoke("get_font_list"); + fonts = fonts.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" })); + updateSelectableFontFamilyList(arrayToObject(fonts)); + } catch (error) { + console.error("Error fetching fonts:", error); + } + }; + return { asyncFetchFonts }; +}; + +const startFeedingToWatchDogController = () => { + const { asyncStdoutToPython } = useStdoutToPython(); + setInterval(() => { + asyncStdoutToPython("/run/feed_watchdog"); + }, 20000); // 20000ミリ秒 = 20秒 +}; \ No newline at end of file diff --git a/src-ui/app/_app_controllers/TransparencyController.jsx b/src-ui/app/_app_controllers/TransparencyController.jsx new file mode 100644 index 00000000..46982413 --- /dev/null +++ b/src-ui/app/_app_controllers/TransparencyController.jsx @@ -0,0 +1,11 @@ +import { useEffect } from "react"; +import { useTransparency } from "@logics_configs"; + +export const TransparencyController = () => { + const { currentTransparency } = useTransparency(); + useEffect(() => { + document.documentElement.style.setProperty("opacity", `${currentTransparency.data / 100}`); + }, [currentTransparency.data]); + + return null; +}; \ No newline at end of file diff --git a/src-ui/app/_app_controllers/UiLanguageController.jsx b/src-ui/app/_app_controllers/UiLanguageController.jsx new file mode 100644 index 00000000..5b45c503 --- /dev/null +++ b/src-ui/app/_app_controllers/UiLanguageController.jsx @@ -0,0 +1,14 @@ +import { useEffect } from "react"; + +import { useTranslation } from "react-i18next"; +import { useUiLanguage } from "@logics_configs"; + +export const UiLanguageController = () => { + const { currentUiLanguage } = useUiLanguage(); + const { i18n } = useTranslation(); + + useEffect(() => { + i18n.changeLanguage(currentUiLanguage.data); + }, [currentUiLanguage.data]); + return null; +}; \ No newline at end of file diff --git a/src-ui/app/_app_controllers/UiSizeController.jsx b/src-ui/app/_app_controllers/UiSizeController.jsx new file mode 100644 index 00000000..8b970c0f --- /dev/null +++ b/src-ui/app/_app_controllers/UiSizeController.jsx @@ -0,0 +1,13 @@ +import { useEffect } from "react"; +import { useUiScaling } from "@logics_configs"; + +export const UiSizeController = () => { + const { currentUiScaling } = useUiScaling(); + const font_size = 62.5 * currentUiScaling.data / 100; + + useEffect(() => { + document.documentElement.style.setProperty("font-size", `${font_size}%`); + }, [currentUiScaling.data]); + + return null; +}; \ No newline at end of file diff --git a/src-ui/app/_app_controllers/index.js b/src-ui/app/_app_controllers/index.js new file mode 100644 index 00000000..f91d40ac --- /dev/null +++ b/src-ui/app/_app_controllers/index.js @@ -0,0 +1,6 @@ +export { StartPythonController } from "./StartPythonController"; +export { UiLanguageController } from "./UiLanguageController"; +export { ConfigPageCloseTriggerController } from "./ConfigPageCloseTriggerController"; +export { UiSizeController } from "./UiSizeController"; +export { FontFamilyController } from "./FontFamilyController"; +export { TransparencyController } from "./TransparencyController"; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_entry/_Entry.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_entry/_Entry.jsx index dc6f4309..e8bba371 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_entry/_Entry.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_entry/_Entry.jsx @@ -1,3 +1,4 @@ +import clsx from "clsx"; import React, { useRef, forwardRef, useImperativeHandle } from "react"; import styles from "./_Entry.module.scss"; @@ -9,6 +10,9 @@ const _Entry = forwardRef((props, ref) => { inputRef.current.focus(); } })); + const input_class_names = clsx(styles.entry_input_area, { + [styles.is_disabled]: props.is_disabled + }); return (
@@ -18,7 +22,7 @@ const _Entry = forwardRef((props, ref) => { > props.onChange(e)} /> diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_entry/_Entry.module.scss b/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_entry/_Entry.module.scss index dc2ac749..c9744de6 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_entry/_Entry.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_entry/_Entry.module.scss @@ -16,4 +16,9 @@ height: 100%; font-size: 1.4rem; resize: none; + color: var(--dark_basic_text_color); + &.is_disabled { + color: var(--dark_500_color); + pointer-events: none; + } } \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/deepl_auth_key/DeeplAuthKey.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/deepl_auth_key/DeeplAuthKey.jsx index 0a1dea91..42401b7a 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/deepl_auth_key/DeeplAuthKey.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/_components/deepl_auth_key/DeeplAuthKey.jsx @@ -1,9 +1,11 @@ import styles from "./DeeplAuthKey.module.scss"; import { useTranslation } from "react-i18next"; import clsx from "clsx"; +import CircularProgress from "@mui/material/CircularProgress"; import ExternalLink from "@images/external_link.svg?react"; import { _Entry } from "../_atoms/_entry/_Entry"; import { useState, useRef } from "react"; +import { useEffect } from "react"; export const DeeplAuthKey = (props) => { const { t } = useTranslation(); @@ -22,11 +24,28 @@ export const DeeplAuthKey = (props) => { props.saveFunction(); }; + useEffect(() => { + if (props.variable === "" || props.variable === null) { + seIsEditable(true); + } + }, [props.variable]); + + const is_disabled = props.state === "pending"; + + const save_button_class_names = clsx(styles.save_button, { + [styles.is_disabled]: is_disabled + }); + return (
- <_Entry ref={entryRef} width="30rem" onChange={onchangeEntryAuthKey} ui_variable={props.variable}/> - + <_Entry ref={entryRef} width="30rem" onChange={onchangeEntryAuthKey} ui_variable={props.variable} is_disabled={is_disabled}/> + {is_editable ? null : diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/deepl_auth_key/DeeplAuthKey.module.scss b/src-ui/app/config_page/setting_section/setting_box/_components/deepl_auth_key/DeeplAuthKey.module.scss index 6e5a3411..575ec8bc 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/deepl_auth_key/DeeplAuthKey.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/_components/deepl_auth_key/DeeplAuthKey.module.scss @@ -44,17 +44,25 @@ .save_button { padding: 0.8rem 1.2rem; background-color: var(--primary_600_color); - color: var(--dark_basic_text_color); - font-size: 1.4rem; border-radius: 0.4rem; text-align: center; flex-shrink: 0; + min-width: 5.4rem; &:hover { background-color: var(--primary_500_color); } &:active { background-color: var(--primary_700_color); } + &.is_disabled { + pointer-events: none; + background-color: var(--primary_800_color); + } +} + +.save_button_label { + color: var(--dark_basic_text_color); + font-size: 1.4rem; } .open_webpage_button_wrapper { diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/threshold_component/threshold_entry/ThresholdEntry.module.scss b/src-ui/app/config_page/setting_section/setting_box/_components/threshold_component/threshold_entry/ThresholdEntry.module.scss index d2378d03..929f8975 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/threshold_component/threshold_entry/ThresholdEntry.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/_components/threshold_component/threshold_entry/ThresholdEntry.module.scss @@ -1,4 +1,5 @@ .container { + } .entry_wrapper { diff --git a/src-ui/app/config_page/setting_section/setting_box/translation/Translation.jsx b/src-ui/app/config_page/setting_section/setting_box/translation/Translation.jsx index e0f02565..95ebdb54 100644 --- a/src-ui/app/config_page/setting_section/setting_box/translation/Translation.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/translation/Translation.jsx @@ -98,9 +98,9 @@ const CTranslation2ComputeDevice_Box = () => { }; const DeeplAuthKey_Box = () => { - const [input_value, seInputValue] = useState(""); const { t } = useTranslation(); const { currentDeepLAuthKey, setDeepLAuthKey, deleteDeepLAuthKey } = useDeepLAuthKey(); + const [input_value, seInputValue] = useState(currentDeepLAuthKey.data); const onChangeFunction = (value) => { seInputValue(value); @@ -124,6 +124,7 @@ const DeeplAuthKey_Box = () => { {translator: t("main_page.translator")} )} variable={input_value} + state={currentDeepLAuthKey.state} onChangeFunction={onChangeFunction} saveFunction={saveFunction} /> @@ -169,4 +170,4 @@ const findKeyByDeviceValue = (devices, target_value) => { } } return null; -}; +}; \ No newline at end of file diff --git a/src-ui/app/main_page/main_section/message_container/MessageContainer.jsx b/src-ui/app/main_page/main_section/message_container/MessageContainer.jsx index cad18079..dbc5c100 100644 --- a/src-ui/app/main_page/main_section/message_container/MessageContainer.jsx +++ b/src-ui/app/main_page/main_section/message_container/MessageContainer.jsx @@ -14,7 +14,7 @@ export const MessageContainer = () => { const { currentUiScaling } = useUiScaling(); const [is_hovered, setIsHovered] = useState(false); const [message_box_height_in_rem, setMessageBoxHeightInRem] = useState(10); - const FONT_SIZE_STANDARD = 10 * currentUiScaling.data / 100; // 10px = 1rem + const FONT_SIZE_STANDARD = 10 * currentUiScaling.data / 100; // 10px = 1rem const { currentIsAppliedInitMessageBoxHeight, updateIsAppliedInitMessageBoxHeight } = useStore_IsAppliedInitMessageBoxHeight(); const container_ref = useRef(null); @@ -23,9 +23,10 @@ export const MessageContainer = () => { const asyncSetMessageBoxHeightInRem = async (data) => { const minimized = await appWindow.isMinimized(); - if (minimized === true) return; // don't save while the window is minimized. + if (minimized) return; // don't save while the window is minimized. setMessageBoxHeightInRem(data); }; + const calculateMessageBoxRatioAndHeight = () => { if (!currentIsAppliedInitMessageBoxHeight.data) { asyncSetMessageInputBoxRatio(currentMessageInputBoxRatio.data); @@ -33,7 +34,7 @@ export const MessageContainer = () => { return; } - if (log_box_ref.current && message_box_wrapper_ref.current) { + if (log_box_ref.current && message_box_wrapper_ref.current && container_ref.current) { const container_height = container_ref.current.offsetHeight; const container_padding_bottom = parseFloat(window.getComputedStyle(container_ref.current).paddingBottom); const total_height = container_height - container_padding_bottom; @@ -43,6 +44,8 @@ export const MessageContainer = () => { asyncSetMessageInputBoxRatio(message_box_ratio); asyncSetMessageBoxHeightInRem(convertRatioToRem(message_box_ratio)); + } else { + console.warn("References not ready for calculation"); } }; @@ -53,24 +56,26 @@ export const MessageContainer = () => { }); useEffect(() => { - // Note: I thought the part "1.4" is message box bottom padding + (message box separator height/2) - // but it should be fixed at 1.4. Idk why, tho. asyncSetMessageBoxHeightInRem((position / FONT_SIZE_STANDARD) - 1.4); }, [position]); - useEffect(() => { asyncSetMessageBoxHeightInRem(convertRatioToRem(currentMessageInputBoxRatio.data)); }, [currentMessageInputBoxRatio.data]); const convertRatioToRem = (ratio) => { + if (!container_ref.current) return 0; const container_height = container_ref.current.offsetHeight; const container_padding_bottom = parseFloat(window.getComputedStyle(container_ref.current).paddingBottom); const total_height = container_height - container_padding_bottom; - if (total_height === 0) return 0; - return ((ratio / 100) * total_height / FONT_SIZE_STANDARD); + return total_height === 0 ? 0 : ((ratio / 100) * total_height / FONT_SIZE_STANDARD); }; + useEffect(() => { + calculateMessageBoxRatioAndHeight(); + updateIsAppliedInitMessageBoxHeight(true); // Ensure this happens after initial calculation + }, []); + useEffect(() => { let resizeTimeout; @@ -86,10 +91,6 @@ export const MessageContainer = () => { }; }, []); - useEffect(() => { - updateIsAppliedInitMessageBoxHeight(true); - }, []); - return (
{ onMouseLeave={() => setIsHovered(false)} > - +
{ ); }; -const Separator = ({ onDragStart, ...props }) => { - return ( -
- -
- ); -}; +const Separator = ({ onDragStart, ...props }) => ( +
+ +
+); diff --git a/src-ui/app/snackbar_controller/SnackbarController.jsx b/src-ui/app/snackbar_controller/SnackbarController.jsx new file mode 100644 index 00000000..dcc8f54a --- /dev/null +++ b/src-ui/app/snackbar_controller/SnackbarController.jsx @@ -0,0 +1,39 @@ +import { clsx } from "clsx"; +import Snackbar from "@mui/material/Snackbar"; +import Slide from "@mui/material/Slide"; + +import styles from "./SnackbarController.module.scss"; +import { useNotificationStatus } from "@logics_common"; + +export const SnackbarController = () => { + const { currentNotificationStatus, closeNotification } = useNotificationStatus(); + + const handleClose = (event, reason) => { + closeNotification(event, reason); + }; + + const snackbar_classname = clsx(styles.snackbar_content, { + [styles.is_success]: currentNotificationStatus.data.status === "success", + [styles.is_error]: currentNotificationStatus.data.status === "error", + }); + + return ( +
+ +
+

{currentNotificationStatus.data.message}

+
+
+
+ ); +}; + +const SlideTransition = (props) => { + return ; +}; diff --git a/src-ui/app/snackbar_controller/SnackbarController.module.scss b/src-ui/app/snackbar_controller/SnackbarController.module.scss new file mode 100644 index 00000000..2db44fa9 --- /dev/null +++ b/src-ui/app/snackbar_controller/SnackbarController.module.scss @@ -0,0 +1,16 @@ +.snackbar_content { + width: 100%; + height: 100%; + padding: 2rem; + color: #fff; + &.is_success { + background-color: #368777; + } + &.is_error { + background-color: #bb4448; + } +} + +.snackbar_message { + font-size: 1.4rem; +} \ No newline at end of file diff --git a/src-ui/logics/common/index.js b/src-ui/logics/common/index.js index 8f1cd737..e63b28db 100644 --- a/src-ui/logics/common/index.js +++ b/src-ui/logics/common/index.js @@ -5,6 +5,7 @@ export { useWindow } from "./useWindow"; export { useIsOpenedConfigPage } from "./useIsOpenedConfigPage"; export { useIsSoftwareUpdateAvailable } from "./useIsSoftwareUpdateAvailable"; export { useIsSoftwareUpdating } from "./useIsSoftwareUpdating"; +export { useNotificationStatus } from "./useNotificationStatus"; export { useOpenFolder } from "./useOpenFolder"; export { useMessage } from "./useMessage"; export { useUpdateSoftware } from "./useUpdateSoftware"; diff --git a/src-ui/logics/common/useNotificationStatus.js b/src-ui/logics/common/useNotificationStatus.js new file mode 100644 index 00000000..f36c0c45 --- /dev/null +++ b/src-ui/logics/common/useNotificationStatus.js @@ -0,0 +1,42 @@ +import { useStore_NotificationStatus } from "@store"; + +export const useNotificationStatus = () => { + const { currentNotificationStatus, updateNotificationStatus } = useStore_NotificationStatus(); + + const generateRandomKey = () => Math.random(); + + const showNotification_Error = (message) => { + updateNotificationStatus({ + status: "error", + is_open: true, + key: generateRandomKey(), + message: message, + }); + }; + + const showNotification_Success = (message) => { + updateNotificationStatus({ + status: "success", + is_open: true, + key: generateRandomKey(), + message: message, + }); + }; + + const closeNotification = (event, reason) => { + if (reason === "clickaway") return; + updateNotificationStatus((prev) => ({ + ...prev.data, + is_open: false, + })); + }; + + return { + currentNotificationStatus, + updateNotificationStatus, + + showNotification_Error, + showNotification_Success, + closeNotification, + }; +}; \ No newline at end of file diff --git a/src-ui/logics/useReceiveRoutes.js b/src-ui/logics/useReceiveRoutes.js index 748b5061..0c09da4f 100644 --- a/src-ui/logics/useReceiveRoutes.js +++ b/src-ui/logics/useReceiveRoutes.js @@ -2,6 +2,9 @@ import { translator_status } from "@ui_configs"; import { arrayToObject } from "@utils"; import { + useNotificationStatus, + + useComputeMode, useInitProgress, useIsBackendReady, @@ -167,6 +170,10 @@ export const useReceiveRoutes = () => { const { updateOscIpAddress } = useOscIpAddress(); const { updateOscPort } = useOscPort(); + + + const { showNotification_Success, showNotification_Error } = useNotificationStatus(); + const routes = { // Common "/run/feed_watchdog": () => {}, @@ -494,6 +501,7 @@ export const useReceiveRoutes = () => { const error_route = error_routes[parsed_data.endpoint]; (error_route) ? error_route(parsed_data.result.data) : console.error(`Invalid endpoint: ${parsed_data.endpoint}\nresult: ${JSON.stringify(parsed_data.result)}`); console.error(`status 400: ${JSON.stringify(parsed_data.result)}`); + showNotification_Error(parsed_data.result.message); break; case 348: diff --git a/src-ui/store.js b/src-ui/store.js index cf63e236..878911cc 100644 --- a/src-ui/store.js +++ b/src-ui/store.js @@ -115,6 +115,12 @@ export const { atomInstance: Atom_IsSoftwareUpdateAvailable, useHook: useStore_I export const { atomInstance: Atom_InitProgress, useHook: useStore_InitProgress } = createAtomWithHook(0, "InitProgress"); export const { atomInstance: Atom_IsBreakPoint, useHook: useStore_IsBreakPoint } = createAtomWithHook(false, "IsBreakPoint"); export const { atomInstance: Atom_IsSoftwareUpdating, useHook: useStore_IsSoftwareUpdating } = createAtomWithHook(false, "IsSoftwareUpdating"); +export const { atomInstance: Atom_NotificationStatus, useHook: useStore_NotificationStatus } = createAtomWithHook({ + status: "", + is_open: false, + key: 0, + message: "", +}, "NotificationStatus"); // Main Page // Functions