From 61f4b6e4ee0759b26b05cf2fd63e6c9392a6d927 Mon Sep 17 00:00:00 2001 From: Junhao Liao Date: Mon, 30 Sep 2024 00:26:36 -0400 Subject: [PATCH 01/45] Add NotificationContextProvider for publishing and subscribing notifications. --- new-log-viewer/src/App.tsx | 24 +++- new-log-viewer/src/components/Layout.tsx | 25 +--- .../src/components/StatusBar/index.tsx | 4 +- .../modals/SettingsModal/SettingsDialog.tsx | 64 +++++---- .../contexts/NotificationContextProvider.tsx | 134 ++++++++++++++++++ .../src/contexts/StateContextProvider.tsx | 7 +- 6 files changed, 201 insertions(+), 57 deletions(-) create mode 100644 new-log-viewer/src/contexts/NotificationContextProvider.tsx diff --git a/new-log-viewer/src/App.tsx b/new-log-viewer/src/App.tsx index afaaa4b3..1f894a33 100644 --- a/new-log-viewer/src/App.tsx +++ b/new-log-viewer/src/App.tsx @@ -1,6 +1,12 @@ +import {CssVarsProvider} from "@mui/joy/styles"; + import Layout from "./components/Layout"; +import APP_THEME from "./components/theme"; +import NotificationContextProvider from "./contexts/NotificationContextProvider"; import StateContextProvider from "./contexts/StateContextProvider"; import UrlContextProvider from "./contexts/UrlContextProvider"; +import {CONFIG_KEY} from "./typings/config"; +import {CONFIG_DEFAULT} from "./utils/config"; /** @@ -10,11 +16,19 @@ import UrlContextProvider from "./contexts/UrlContextProvider"; */ const App = () => { return ( - - - - - + + + + + + + + + ); }; diff --git a/new-log-viewer/src/components/Layout.tsx b/new-log-viewer/src/components/Layout.tsx index c0169495..90fc07c8 100644 --- a/new-log-viewer/src/components/Layout.tsx +++ b/new-log-viewer/src/components/Layout.tsx @@ -1,12 +1,7 @@ -import {CssVarsProvider} from "@mui/joy/styles"; - -import {CONFIG_KEY} from "../typings/config"; -import {CONFIG_DEFAULT} from "../utils/config"; import DropFileContainer from "./DropFileContainer"; import Editor from "./Editor"; import MenuBar from "./MenuBar"; import StatusBar from "./StatusBar"; -import APP_THEME from "./theme"; /** @@ -16,19 +11,13 @@ import APP_THEME from "./theme"; */ const Layout = () => { return ( - -
- - - - - -
-
+
+ + + + + +
); }; diff --git a/new-log-viewer/src/components/StatusBar/index.tsx b/new-log-viewer/src/components/StatusBar/index.tsx index 1d8598f2..5741720a 100644 --- a/new-log-viewer/src/components/StatusBar/index.tsx +++ b/new-log-viewer/src/components/StatusBar/index.tsx @@ -4,6 +4,7 @@ import Button from "@mui/joy/Button"; import Sheet from "@mui/joy/Sheet"; import Typography from "@mui/joy/Typography"; +import {NotificationContext} from "../../contexts/NotificationContextProvider"; import {StateContext} from "../../contexts/StateContextProvider"; import { copyPermalinkToClipboard, @@ -26,6 +27,7 @@ const handleCopyLinkButtonClick = () => { * @return */ const StatusBar = () => { + const {statusMessage} = useContext(NotificationContext); const {numEvents} = useContext(StateContext); const {logEventNum} = useContext(UrlContext); @@ -35,7 +37,7 @@ const StatusBar = () => { className={"status-message"} level={"body-sm"} > - Status message + {statusMessage} - - - + {CONFIG_FORM_FIELDS.map((field, index) => ( diff --git a/new-log-viewer/src/components/modals/SettingsModal/ThemeSwitchToggle.tsx b/new-log-viewer/src/components/modals/SettingsModal/ThemeSwitchToggle.tsx new file mode 100644 index 00000000..fbc90c45 --- /dev/null +++ b/new-log-viewer/src/components/modals/SettingsModal/ThemeSwitchToggle.tsx @@ -0,0 +1,53 @@ +import { + Button, + ToggleButtonGroup, + useColorScheme, +} from "@mui/joy"; +import type {Mode} from "@mui/system/cssVars/useCurrentColorScheme"; + +import DarkModeIcon from "@mui/icons-material/DarkMode"; +import LightModeIcon from "@mui/icons-material/LightMode"; +import SettingsBrightnessIcon from "@mui/icons-material/SettingsBrightness"; + +import {THEME_NAME} from "../../../typings/config"; + + +/** + * Renders a toggle button group for theme selection. + * + * @return + */ +const ThemeSwitchToggle = () => { + const {setMode, mode} = useColorScheme(); + + return ( + { + setMode(newValue as Mode); + }} + > + + + + + ); +}; + +export default ThemeSwitchToggle; From 3a633864c7ebce669061c17733537a762283b7ea Mon Sep 17 00:00:00 2001 From: Junhao Liao Date: Sat, 12 Oct 2024 03:17:33 +0800 Subject: [PATCH 19/45] Move handleConfigFormSubmit generator inside SettingsDialog so that `postPopup` can be directly used from the context. --- .../modals/SettingsModal/SettingsDialog.tsx | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/new-log-viewer/src/components/modals/SettingsModal/SettingsDialog.tsx b/new-log-viewer/src/components/modals/SettingsModal/SettingsDialog.tsx index 66ed7c01..6337e517 100644 --- a/new-log-viewer/src/components/modals/SettingsModal/SettingsDialog.tsx +++ b/new-log-viewer/src/components/modals/SettingsModal/SettingsDialog.tsx @@ -1,5 +1,6 @@ import React, { forwardRef, + useCallback, useContext, } from "react"; @@ -75,14 +76,14 @@ const handleConfigFormReset = (ev: React.FormEvent) => { }; /** - * Generates a handler for the submit event for the configuration form. + * Renders a settings dialog for configurations. * - * @return the generated handler. + * @return */ -const useHandleConfigFormSubmit = () => { +const SettingsDialog = forwardRef((_, ref) => { const {postPopup} = useContext(NotificationContext); - return (ev: React.FormEvent) => { + const handleConfigFormSubmit = useCallback((ev: React.FormEvent) => { ev.preventDefault(); const formData = new FormData(ev.target as HTMLFormElement); const getFormDataValue = (key: string) => formData.get(key) as string; @@ -107,16 +108,7 @@ const useHandleConfigFormSubmit = () => { } else { window.location.reload(); } - }; -}; - -/** - * Renders a settings dialog for configurations. - * - * @return - */ -const SettingsDialog = forwardRef((_, ref) => { - const handleConfigFormSubmit = useHandleConfigFormSubmit(); + }, [postPopup]); return ( Date: Sat, 12 Oct 2024 03:21:10 +0800 Subject: [PATCH 20/45] Move CssVarsProvider back into Layout. --- new-log-viewer/src/App.tsx | 25 +++++++----------------- new-log-viewer/src/components/Layout.tsx | 13 ++++++++++-- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/new-log-viewer/src/App.tsx b/new-log-viewer/src/App.tsx index 1f894a33..3e00df4f 100644 --- a/new-log-viewer/src/App.tsx +++ b/new-log-viewer/src/App.tsx @@ -1,12 +1,7 @@ -import {CssVarsProvider} from "@mui/joy/styles"; - import Layout from "./components/Layout"; -import APP_THEME from "./components/theme"; import NotificationContextProvider from "./contexts/NotificationContextProvider"; import StateContextProvider from "./contexts/StateContextProvider"; import UrlContextProvider from "./contexts/UrlContextProvider"; -import {CONFIG_KEY} from "./typings/config"; -import {CONFIG_DEFAULT} from "./utils/config"; /** @@ -16,19 +11,13 @@ import {CONFIG_DEFAULT} from "./utils/config"; */ const App = () => { return ( - - - - - - - - - + + + + + + + ); }; diff --git a/new-log-viewer/src/components/Layout.tsx b/new-log-viewer/src/components/Layout.tsx index 665c0a51..1a57e7f2 100644 --- a/new-log-viewer/src/components/Layout.tsx +++ b/new-log-viewer/src/components/Layout.tsx @@ -1,6 +1,11 @@ +import {CssVarsProvider} from "@mui/joy"; + +import {CONFIG_KEY} from "../typings/config"; +import {CONFIG_DEFAULT} from "../utils/config"; import CentralContainer from "./CentralContainer"; import MenuBar from "./MenuBar"; import StatusBar from "./StatusBar"; +import APP_THEME from "./theme"; /** @@ -10,11 +15,15 @@ import StatusBar from "./StatusBar"; */ const Layout = () => { return ( - <> + - + ); }; From 729fac6ac7ca7be3e7d054c28c485e9e14d27150 Mon Sep 17 00:00:00 2001 From: Junhao Liao Date: Sat, 12 Oct 2024 03:30:49 +0800 Subject: [PATCH 21/45] Separate concerns by moving `PopUpMessagesContainer` from `NotificationContextProvider` to `Layout`. --- new-log-viewer/src/components/Layout.tsx | 9 +++ .../NotificationContextProvider/index.tsx | 55 ++++++++++++------- 2 files changed, 43 insertions(+), 21 deletions(-) diff --git a/new-log-viewer/src/components/Layout.tsx b/new-log-viewer/src/components/Layout.tsx index 1a57e7f2..e2364f46 100644 --- a/new-log-viewer/src/components/Layout.tsx +++ b/new-log-viewer/src/components/Layout.tsx @@ -1,5 +1,9 @@ +import {useContext} from "react"; + import {CssVarsProvider} from "@mui/joy"; +import {NotificationContext} from "../contexts/NotificationContextProvider"; +import PopUpMessagesContainer from "../contexts/NotificationContextProvider/PopUpMessagesContainer"; import {CONFIG_KEY} from "../typings/config"; import {CONFIG_DEFAULT} from "../utils/config"; import CentralContainer from "./CentralContainer"; @@ -14,6 +18,8 @@ import APP_THEME from "./theme"; * @return */ const Layout = () => { + const {popupMessages, onPopupMessagesChange} = useContext(NotificationContext); + return ( { + ); }; diff --git a/new-log-viewer/src/contexts/NotificationContextProvider/index.tsx b/new-log-viewer/src/contexts/NotificationContextProvider/index.tsx index d2bec538..2a7520d3 100644 --- a/new-log-viewer/src/contexts/NotificationContextProvider/index.tsx +++ b/new-log-viewer/src/contexts/NotificationContextProvider/index.tsx @@ -6,22 +6,21 @@ import React, { import {Nullable} from "../../typings/common"; import {LOG_LEVEL} from "../../typings/logs"; -import PopUpMessagesContainer from "./PopUpMessagesContainer"; import "./index.css"; -/** - * The default duration in milliseconds after which an automatic dismissal will occur. - */ -const DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS = 10_000; - -/** - * A value that indicates that a pop-up message should not be automatically dismissed. - */ -const DO_NOT_TIMEOUT_VALUE = null; +interface PopupMessage { + level: LOG_LEVEL, + message: string, + title: string, + timeoutMillis: Nullable, +} interface NotificationContextType { + popupMessages: PopupMessage[], + + onPopupMessagesChange: (callback: (value: PopupMessage[]) => PopupMessage[]) => void, postPopup: ( level: LOG_LEVEL, message: string, @@ -32,17 +31,30 @@ interface NotificationContextType { const NotificationContext = createContext({} as NotificationContextType); +/** + * Default values of the Notification context value object. + */ +const NOTIFICATION_DEFAULT: Readonly = Object.freeze({ + popupMessages: [], + + onPopupMessagesChange: () => {}, + postPopup: () => {}, +}); + +/** + * The default duration in milliseconds after which an automatic dismissal will occur. + */ +const DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS = 10_000; + +/** + * A value that indicates that a pop-up message should not be automatically dismissed. + */ +const DO_NOT_TIMEOUT_VALUE = null; + interface NotificationContextProviderProps { children: React.ReactNode; } -interface PopupMessage { - level: LOG_LEVEL, - message: string, - title: string, - timeoutMillis: Nullable, -} - /** * Provides notification management for the application. This provider must be at the outermost * layer of subscriber / publisher components to ensure they can receive / publish notifications. @@ -52,7 +64,9 @@ interface PopupMessage { * @return */ const NotificationContextProvider = ({children}: NotificationContextProviderProps) => { - const [popupMessages, setPopupMessages] = useState([]); + const [popupMessages, setPopupMessages] = useState( + NOTIFICATION_DEFAULT.popupMessages + ); const postPopup = useCallback(( level: LOG_LEVEL, @@ -78,13 +92,12 @@ const NotificationContextProvider = ({children}: NotificationContextProviderProp return ( {children} - ); }; From 93ab288329578843477a3ee3b24e5c97110ff6ec Mon Sep 17 00:00:00 2001 From: Junhao Liao Date: Sat, 12 Oct 2024 03:33:56 +0800 Subject: [PATCH 22/45] Move PopUpMessagesContainer and related components to new structure for improved organization. --- new-log-viewer/src/components/Layout.tsx | 2 +- .../PopUpMessagesContainer}/PopUpMessageBox.tsx | 4 ++-- .../PopUpMessagesContainer}/index.css | 0 .../PopUpMessagesContainer/index.tsx} | 4 +++- .../index.tsx => NotificationContextProvider.tsx} | 6 ++---- 5 files changed, 8 insertions(+), 8 deletions(-) rename new-log-viewer/src/{contexts/NotificationContextProvider => components/PopUpMessagesContainer}/PopUpMessageBox.tsx (97%) rename new-log-viewer/src/{contexts/NotificationContextProvider => components/PopUpMessagesContainer}/index.css (100%) rename new-log-viewer/src/{contexts/NotificationContextProvider/PopUpMessagesContainer.tsx => components/PopUpMessagesContainer/index.tsx} (92%) rename new-log-viewer/src/contexts/{NotificationContextProvider/index.tsx => NotificationContextProvider.tsx} (95%) diff --git a/new-log-viewer/src/components/Layout.tsx b/new-log-viewer/src/components/Layout.tsx index e2364f46..a08dd6de 100644 --- a/new-log-viewer/src/components/Layout.tsx +++ b/new-log-viewer/src/components/Layout.tsx @@ -3,11 +3,11 @@ import {useContext} from "react"; import {CssVarsProvider} from "@mui/joy"; import {NotificationContext} from "../contexts/NotificationContextProvider"; -import PopUpMessagesContainer from "../contexts/NotificationContextProvider/PopUpMessagesContainer"; import {CONFIG_KEY} from "../typings/config"; import {CONFIG_DEFAULT} from "../utils/config"; import CentralContainer from "./CentralContainer"; import MenuBar from "./MenuBar"; +import PopUpMessagesContainer from "./PopUpMessagesContainer"; import StatusBar from "./StatusBar"; import APP_THEME from "./theme"; diff --git a/new-log-viewer/src/contexts/NotificationContextProvider/PopUpMessageBox.tsx b/new-log-viewer/src/components/PopUpMessagesContainer/PopUpMessageBox.tsx similarity index 97% rename from new-log-viewer/src/contexts/NotificationContextProvider/PopUpMessageBox.tsx rename to new-log-viewer/src/components/PopUpMessagesContainer/PopUpMessageBox.tsx index 66e91705..f66ae2d8 100644 --- a/new-log-viewer/src/contexts/NotificationContextProvider/PopUpMessageBox.tsx +++ b/new-log-viewer/src/components/PopUpMessagesContainer/PopUpMessageBox.tsx @@ -12,11 +12,11 @@ import { import CloseIcon from "@mui/icons-material/Close"; -import {LOG_LEVEL} from "../../typings/logs"; import { DO_NOT_TIMEOUT_VALUE, PopupMessage, -} from "./index"; +} from "../../contexts/NotificationContextProvider"; +import {LOG_LEVEL} from "../../typings/logs"; interface PopUpMessageProps { diff --git a/new-log-viewer/src/contexts/NotificationContextProvider/index.css b/new-log-viewer/src/components/PopUpMessagesContainer/index.css similarity index 100% rename from new-log-viewer/src/contexts/NotificationContextProvider/index.css rename to new-log-viewer/src/components/PopUpMessagesContainer/index.css diff --git a/new-log-viewer/src/contexts/NotificationContextProvider/PopUpMessagesContainer.tsx b/new-log-viewer/src/components/PopUpMessagesContainer/index.tsx similarity index 92% rename from new-log-viewer/src/contexts/NotificationContextProvider/PopUpMessagesContainer.tsx rename to new-log-viewer/src/components/PopUpMessagesContainer/index.tsx index 86fc8071..d3584a2b 100644 --- a/new-log-viewer/src/contexts/NotificationContextProvider/PopUpMessagesContainer.tsx +++ b/new-log-viewer/src/components/PopUpMessagesContainer/index.tsx @@ -3,9 +3,11 @@ import { Stack, } from "@mui/joy"; -import {PopupMessage} from "./index"; +import {PopupMessage} from "../../contexts/NotificationContextProvider"; import PopUpMessageBox from "./PopUpMessageBox"; +import "./index.css"; + interface PopUpMessagesContainerProps { popupMessages: PopupMessage[], diff --git a/new-log-viewer/src/contexts/NotificationContextProvider/index.tsx b/new-log-viewer/src/contexts/NotificationContextProvider.tsx similarity index 95% rename from new-log-viewer/src/contexts/NotificationContextProvider/index.tsx rename to new-log-viewer/src/contexts/NotificationContextProvider.tsx index 2a7520d3..7de11d86 100644 --- a/new-log-viewer/src/contexts/NotificationContextProvider/index.tsx +++ b/new-log-viewer/src/contexts/NotificationContextProvider.tsx @@ -4,10 +4,8 @@ import React, { useState, } from "react"; -import {Nullable} from "../../typings/common"; -import {LOG_LEVEL} from "../../typings/logs"; - -import "./index.css"; +import {Nullable} from "../typings/common"; +import {LOG_LEVEL} from "../typings/logs"; interface PopupMessage { From 2c92af5b5961ccdfdee67f40c869d2dc51ec29d3 Mon Sep 17 00:00:00 2001 From: Junhao Liao Date: Sat, 12 Oct 2024 03:36:15 +0800 Subject: [PATCH 23/45] Change the semicolon to a comma in the children property within the NotificationContextProviderProps interface for consistency. --- new-log-viewer/src/contexts/NotificationContextProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/new-log-viewer/src/contexts/NotificationContextProvider.tsx b/new-log-viewer/src/contexts/NotificationContextProvider.tsx index 7de11d86..73f18151 100644 --- a/new-log-viewer/src/contexts/NotificationContextProvider.tsx +++ b/new-log-viewer/src/contexts/NotificationContextProvider.tsx @@ -50,7 +50,7 @@ const DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS = 10_000; const DO_NOT_TIMEOUT_VALUE = null; interface NotificationContextProviderProps { - children: React.ReactNode; + children: React.ReactNode, } /** From 4c51ff20a7e437d417f3c7b19a99fac8ba8df2e5 Mon Sep 17 00:00:00 2001 From: Junhao Liao Date: Sat, 12 Oct 2024 03:41:01 +0800 Subject: [PATCH 24/45] Refactor postPopup API to accept a single object parameter. --- .../modals/SettingsModal/SettingsDialog.tsx | 7 ++++++- .../contexts/NotificationContextProvider.tsx | 18 ++++-------------- .../src/contexts/StateContextProvider.tsx | 12 ++++++------ 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/new-log-viewer/src/components/modals/SettingsModal/SettingsDialog.tsx b/new-log-viewer/src/components/modals/SettingsModal/SettingsDialog.tsx index 6337e517..21410415 100644 --- a/new-log-viewer/src/components/modals/SettingsModal/SettingsDialog.tsx +++ b/new-log-viewer/src/components/modals/SettingsModal/SettingsDialog.tsx @@ -104,7 +104,12 @@ const SettingsDialog = forwardRef((_, ref) => { }); if (null !== error) { - postPopup(LOG_LEVEL.ERROR, error, "Unable to apply config.", DO_NOT_TIMEOUT_VALUE); + postPopup({ + level: LOG_LEVEL.ERROR, + message: error, + timeoutMillis: DO_NOT_TIMEOUT_VALUE, + title: "Unable to apply config.", + }); } else { window.location.reload(); } diff --git a/new-log-viewer/src/contexts/NotificationContextProvider.tsx b/new-log-viewer/src/contexts/NotificationContextProvider.tsx index 73f18151..f70a151e 100644 --- a/new-log-viewer/src/contexts/NotificationContextProvider.tsx +++ b/new-log-viewer/src/contexts/NotificationContextProvider.tsx @@ -11,20 +11,15 @@ import {LOG_LEVEL} from "../typings/logs"; interface PopupMessage { level: LOG_LEVEL, message: string, - title: string, timeoutMillis: Nullable, + title: string, } interface NotificationContextType { popupMessages: PopupMessage[], onPopupMessagesChange: (callback: (value: PopupMessage[]) => PopupMessage[]) => void, - postPopup: ( - level: LOG_LEVEL, - message: string, - title: string, - timeoutMillis: Nullable - ) => void, + postPopup: (message: PopupMessage) => void, } const NotificationContext = createContext({} as NotificationContextType); @@ -66,19 +61,14 @@ const NotificationContextProvider = ({children}: NotificationContextProviderProp NOTIFICATION_DEFAULT.popupMessages ); - const postPopup = useCallback(( - level: LOG_LEVEL, - message: string, - title: string, - timeoutMillis: Nullable - ) => { + const postPopup = useCallback(({level, message, timeoutMillis, title}:PopupMessage) => { const newMessage = { level: level, message: message, + timeoutMillis: timeoutMillis, title: "" === title ? LOG_LEVEL[level] : title, - timeoutMillis: timeoutMillis, }; setPopupMessages((v) => ([ diff --git a/new-log-viewer/src/contexts/StateContextProvider.tsx b/new-log-viewer/src/contexts/StateContextProvider.tsx index eba644ae..41efe4b9 100644 --- a/new-log-viewer/src/contexts/StateContextProvider.tsx +++ b/new-log-viewer/src/contexts/StateContextProvider.tsx @@ -266,12 +266,12 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { setOnDiskFileSizeInBytes(args.onDiskFileSizeInBytes); break; case WORKER_RESP_CODE.NOTIFICATION: - postPopup( - args.logLevel, - args.message, - "Action failed", - DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS - ); + postPopup({ + level: args.logLevel, + message: args.message, + timeoutMillis: DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS, + title: "Action failed", + }); break; case WORKER_RESP_CODE.PAGE_DATA: { setLogData(args.logs); From f4d191a7e17e8ee09c41781a4f0300a8634d0203 Mon Sep 17 00:00:00 2001 From: Dave Marco Date: Fri, 11 Oct 2024 20:56:40 +0000 Subject: [PATCH 25/45] take advantage of useContext, export the close method directly --- new-log-viewer/src/components/Layout.tsx | 9 +- .../src/components/MenuBar/PageNumInput.tsx | 1 + .../PopUpMessageBox.tsx | 88 ------------------- .../PopUpMessagesContainer/index.css | 27 ------ .../PopUpMessagesContainer/index.tsx | 46 ---------- .../contexts/NotificationContextProvider.tsx | 22 +++-- 6 files changed, 16 insertions(+), 177 deletions(-) delete mode 100644 new-log-viewer/src/components/PopUpMessagesContainer/PopUpMessageBox.tsx delete mode 100644 new-log-viewer/src/components/PopUpMessagesContainer/index.css delete mode 100644 new-log-viewer/src/components/PopUpMessagesContainer/index.tsx diff --git a/new-log-viewer/src/components/Layout.tsx b/new-log-viewer/src/components/Layout.tsx index a08dd6de..7e87ac61 100644 --- a/new-log-viewer/src/components/Layout.tsx +++ b/new-log-viewer/src/components/Layout.tsx @@ -1,13 +1,11 @@ -import {useContext} from "react"; import {CssVarsProvider} from "@mui/joy"; -import {NotificationContext} from "../contexts/NotificationContextProvider"; import {CONFIG_KEY} from "../typings/config"; import {CONFIG_DEFAULT} from "../utils/config"; import CentralContainer from "./CentralContainer"; import MenuBar from "./MenuBar"; -import PopUpMessagesContainer from "./PopUpMessagesContainer"; +import PopUpMessages from "./PopUpMessages"; import StatusBar from "./StatusBar"; import APP_THEME from "./theme"; @@ -18,7 +16,6 @@ import APP_THEME from "./theme"; * @return */ const Layout = () => { - const {popupMessages, onPopupMessagesChange} = useContext(NotificationContext); return ( { - + ); }; diff --git a/new-log-viewer/src/components/MenuBar/PageNumInput.tsx b/new-log-viewer/src/components/MenuBar/PageNumInput.tsx index cf37059a..11e92f96 100644 --- a/new-log-viewer/src/components/MenuBar/PageNumInput.tsx +++ b/new-log-viewer/src/components/MenuBar/PageNumInput.tsx @@ -86,6 +86,7 @@ const PageNumInput = () => { {"/ "} {numPages} diff --git a/new-log-viewer/src/components/PopUpMessagesContainer/PopUpMessageBox.tsx b/new-log-viewer/src/components/PopUpMessagesContainer/PopUpMessageBox.tsx deleted file mode 100644 index f66ae2d8..00000000 --- a/new-log-viewer/src/components/PopUpMessagesContainer/PopUpMessageBox.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { - useCallback, - useEffect, -} from "react"; - -import { - Alert, - Box, - IconButton, - Typography, -} from "@mui/joy"; - -import CloseIcon from "@mui/icons-material/Close"; - -import { - DO_NOT_TIMEOUT_VALUE, - PopupMessage, -} from "../../contexts/NotificationContextProvider"; -import {LOG_LEVEL} from "../../typings/logs"; - - -interface PopUpMessageProps { - message: PopupMessage, - onPopupMessagesChange: (callback: (value: PopupMessage[]) => PopupMessage[]) => void, -} - -/** - * Display a pop-up message in an alert box. - * - * @param props - * @param props.message - * @param props.onPopupMessagesChange - * @return - */ -const PopUpMessageBox = ({message, onPopupMessagesChange}: PopUpMessageProps) => { - const color = message.level >= LOG_LEVEL.ERROR ? - "danger" : - "primary"; - - const handlePopUpMessageClose = useCallback(() => { - onPopupMessagesChange((v) => v.filter((m) => m !== message)); - }, [ - message, - onPopupMessagesChange, - ]); - - useEffect(() => { - if (DO_NOT_TIMEOUT_VALUE !== message.timeoutMillis) { - setTimeout(handlePopUpMessageClose, message.timeoutMillis); - } - }, [ - handlePopUpMessageClose, - message.timeoutMillis, - ]); - - return ( - -
- - - {message.title} - - - - - - - {message.message} - -
-
- ); -}; - -export default PopUpMessageBox; diff --git a/new-log-viewer/src/components/PopUpMessagesContainer/index.css b/new-log-viewer/src/components/PopUpMessagesContainer/index.css deleted file mode 100644 index d7365d49..00000000 --- a/new-log-viewer/src/components/PopUpMessagesContainer/index.css +++ /dev/null @@ -1,27 +0,0 @@ -.pop-up-messages-container-snackbar { - right: 14px !important; - bottom: var(--ylv-status-bar-height) !important; - - padding: 0 !important; - - background: transparent !important; - border: none !important; - box-shadow: none !important; -} - -.pop-up-message-box-alert { - padding-inline: 18px !important; -} - -.pop-up-message-box-alert-layout { - width: 300px; -} - -.pop-up-message-box-title-container { - display: flex; - align-items: center; -} - -.pop-up-message-box-title-text { - flex-grow: 1; -} diff --git a/new-log-viewer/src/components/PopUpMessagesContainer/index.tsx b/new-log-viewer/src/components/PopUpMessagesContainer/index.tsx deleted file mode 100644 index d3584a2b..00000000 --- a/new-log-viewer/src/components/PopUpMessagesContainer/index.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { - Snackbar, - Stack, -} from "@mui/joy"; - -import {PopupMessage} from "../../contexts/NotificationContextProvider"; -import PopUpMessageBox from "./PopUpMessageBox"; - -import "./index.css"; - - -interface PopUpMessagesContainerProps { - popupMessages: PopupMessage[], - onPopupMessagesChange: (callback: (value: PopupMessage[]) => PopupMessage[]) => void -} - -/** - * Display a container for pop-up messages that appears at the bottom-right of the screen. - * - * @param props - * @param props.popupMessages - * @param props.onPopupMessagesChange - * @return - */ -const PopUpMessagesContainer = ({ - popupMessages, - onPopupMessagesChange, -}: PopUpMessagesContainerProps) => { - return ( - - - {popupMessages.map((message, index) => ( - - ))} - - - ); -}; - -export default PopUpMessagesContainer; diff --git a/new-log-viewer/src/contexts/NotificationContextProvider.tsx b/new-log-viewer/src/contexts/NotificationContextProvider.tsx index f70a151e..6749c97c 100644 --- a/new-log-viewer/src/contexts/NotificationContextProvider.tsx +++ b/new-log-viewer/src/contexts/NotificationContextProvider.tsx @@ -16,9 +16,9 @@ interface PopupMessage { } interface NotificationContextType { - popupMessages: PopupMessage[], + popUpMessages: PopupMessage[], - onPopupMessagesChange: (callback: (value: PopupMessage[]) => PopupMessage[]) => void, + handlePopUpMessageClose: (message: PopupMessage) => void; postPopup: (message: PopupMessage) => void, } @@ -28,9 +28,9 @@ const NotificationContext = createContext({} as Notific * Default values of the Notification context value object. */ const NOTIFICATION_DEFAULT: Readonly = Object.freeze({ - popupMessages: [], + popUpMessages: [], - onPopupMessagesChange: () => {}, + handlePopUpMessageClose: () => {}, postPopup: () => {}, }); @@ -57,8 +57,8 @@ interface NotificationContextProviderProps { * @return */ const NotificationContextProvider = ({children}: NotificationContextProviderProps) => { - const [popupMessages, setPopupMessages] = useState( - NOTIFICATION_DEFAULT.popupMessages + const [popUpMessages, setPopUpMessages] = useState( + NOTIFICATION_DEFAULT.popUpMessages ); const postPopup = useCallback(({level, message, timeoutMillis, title}:PopupMessage) => { @@ -71,17 +71,21 @@ const NotificationContextProvider = ({children}: NotificationContextProviderProp title, }; - setPopupMessages((v) => ([ + setPopUpMessages((v) => ([ ...v, newMessage, ])); }, []); + const handlePopUpMessageClose = useCallback((message: PopupMessage) => { + setPopUpMessages((v) => v.filter((m) => m !== message)); + }, []); + return ( From fe5714425303214ae8331341a01e84ef931e1735 Mon Sep 17 00:00:00 2001 From: Dave Marco Date: Fri, 11 Oct 2024 21:14:25 +0000 Subject: [PATCH 26/45] rename things --- .../src/components/Popups/PopupMessageBox.tsx | 86 +++++++++++++++++++ .../src/components/Popups/index.css | 27 ++++++ .../src/components/Popups/index.tsx | 37 ++++++++ 3 files changed, 150 insertions(+) create mode 100644 new-log-viewer/src/components/Popups/PopupMessageBox.tsx create mode 100644 new-log-viewer/src/components/Popups/index.css create mode 100644 new-log-viewer/src/components/Popups/index.tsx diff --git a/new-log-viewer/src/components/Popups/PopupMessageBox.tsx b/new-log-viewer/src/components/Popups/PopupMessageBox.tsx new file mode 100644 index 00000000..5ced6331 --- /dev/null +++ b/new-log-viewer/src/components/Popups/PopupMessageBox.tsx @@ -0,0 +1,86 @@ +import { + useContext, + useEffect, +} from "react"; + +import { + Alert, + Box, + IconButton, + Typography, +} from "@mui/joy"; + +import CloseIcon from "@mui/icons-material/Close"; + +import { + DO_NOT_TIMEOUT_VALUE, + NotificationContext, + PopupMessage, +} from "../../contexts/NotificationContextProvider"; +import {LOG_LEVEL} from "../../typings/logs"; + + +interface PopupMessageProps { + message: PopupMessage, +} + +/** + * Display a pop-up message in an alert box. + * + * @param props + * @param props.message + * @return + */ +const PopupMessageBox = ({message}: PopupMessageProps) => { + const {handlePopupMessageClose} = useContext(NotificationContext); + const color = message.level >= LOG_LEVEL.ERROR ? + "danger" : + "primary"; + + useEffect(() => { + if (DO_NOT_TIMEOUT_VALUE !== message.timeoutMillis) { + setTimeout( + () => { + handlePopupMessageClose(message); + }, + message.timeoutMillis + ); + } + }, [ + message, + handlePopupMessageClose, + ]); + + return ( + +
+ + + {message.title} + + { handlePopupMessageClose(message); }} + > + + + + + {message.message} + +
+
+ ); +}; + +export default PopupMessageBox; diff --git a/new-log-viewer/src/components/Popups/index.css b/new-log-viewer/src/components/Popups/index.css new file mode 100644 index 00000000..b5688ee1 --- /dev/null +++ b/new-log-viewer/src/components/Popups/index.css @@ -0,0 +1,27 @@ +.pop-up-messages-container-snackbar { + right: 14px !important; + bottom: var(--ylv-status-bar-height) !important; + + padding: 10px !important; + + background: transparent !important; + border: none !important; + box-shadow: none !important; +} + +.pop-up-message-box-alert { + padding-inline: 18px !important; +} + +.pop-up-message-box-alert-layout { + width: 300px; +} + +.pop-up-message-box-title-container { + display: flex; + align-items: center; +} + +.pop-up-message-box-title-text { + flex-grow: 1; +} diff --git a/new-log-viewer/src/components/Popups/index.tsx b/new-log-viewer/src/components/Popups/index.tsx new file mode 100644 index 00000000..36c4a547 --- /dev/null +++ b/new-log-viewer/src/components/Popups/index.tsx @@ -0,0 +1,37 @@ +import {useContext} from "react"; + +import { + Snackbar, + Stack, +} from "@mui/joy"; + +import {NotificationContext} from "../../contexts/NotificationContextProvider"; +import PopupMessageBox from "./PopupMessageBox"; + +import "./index.css"; + + +/** + * Displays popups. + * + * @return + */ +const Popups = () => { + const {popupMessages} = useContext(NotificationContext); + return ( + + + {popupMessages.map((message, index) => ( + + ))} + + + ); +}; + +export default Popups; From d8d997df665f76c86f9aea9e54af31704a37d4eb Mon Sep 17 00:00:00 2001 From: Dave Marco Date: Fri, 11 Oct 2024 21:15:29 +0000 Subject: [PATCH 27/45] rename things --- new-log-viewer/src/components/Layout.tsx | 6 ++--- .../src/components/MenuBar/PageNumInput.tsx | 1 - .../contexts/NotificationContextProvider.tsx | 22 +++++++++---------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/new-log-viewer/src/components/Layout.tsx b/new-log-viewer/src/components/Layout.tsx index 7e87ac61..d3a529fb 100644 --- a/new-log-viewer/src/components/Layout.tsx +++ b/new-log-viewer/src/components/Layout.tsx @@ -1,11 +1,10 @@ - import {CssVarsProvider} from "@mui/joy"; import {CONFIG_KEY} from "../typings/config"; import {CONFIG_DEFAULT} from "../utils/config"; import CentralContainer from "./CentralContainer"; import MenuBar from "./MenuBar"; -import PopUpMessages from "./PopUpMessages"; +import Popups from "./Popups"; import StatusBar from "./StatusBar"; import APP_THEME from "./theme"; @@ -16,7 +15,6 @@ import APP_THEME from "./theme"; * @return */ const Layout = () => { - return ( { - + ); }; diff --git a/new-log-viewer/src/components/MenuBar/PageNumInput.tsx b/new-log-viewer/src/components/MenuBar/PageNumInput.tsx index 11e92f96..cf37059a 100644 --- a/new-log-viewer/src/components/MenuBar/PageNumInput.tsx +++ b/new-log-viewer/src/components/MenuBar/PageNumInput.tsx @@ -86,7 +86,6 @@ const PageNumInput = () => { {"/ "} {numPages} diff --git a/new-log-viewer/src/contexts/NotificationContextProvider.tsx b/new-log-viewer/src/contexts/NotificationContextProvider.tsx index 6749c97c..170bd9bf 100644 --- a/new-log-viewer/src/contexts/NotificationContextProvider.tsx +++ b/new-log-viewer/src/contexts/NotificationContextProvider.tsx @@ -16,9 +16,9 @@ interface PopupMessage { } interface NotificationContextType { - popUpMessages: PopupMessage[], + popupMessages: PopupMessage[], - handlePopUpMessageClose: (message: PopupMessage) => void; + handlePopupMessageClose: (message: PopupMessage) => void; postPopup: (message: PopupMessage) => void, } @@ -28,9 +28,9 @@ const NotificationContext = createContext({} as Notific * Default values of the Notification context value object. */ const NOTIFICATION_DEFAULT: Readonly = Object.freeze({ - popUpMessages: [], + popupMessages: [], - handlePopUpMessageClose: () => {}, + handlePopupMessageClose: () => {}, postPopup: () => {}, }); @@ -57,8 +57,8 @@ interface NotificationContextProviderProps { * @return */ const NotificationContextProvider = ({children}: NotificationContextProviderProps) => { - const [popUpMessages, setPopUpMessages] = useState( - NOTIFICATION_DEFAULT.popUpMessages + const [popupMessages, setPopupMessages] = useState( + NOTIFICATION_DEFAULT.popupMessages ); const postPopup = useCallback(({level, message, timeoutMillis, title}:PopupMessage) => { @@ -71,21 +71,21 @@ const NotificationContextProvider = ({children}: NotificationContextProviderProp title, }; - setPopUpMessages((v) => ([ + setPopupMessages((v) => ([ ...v, newMessage, ])); }, []); - const handlePopUpMessageClose = useCallback((message: PopupMessage) => { - setPopUpMessages((v) => v.filter((m) => m !== message)); + const handlePopupMessageClose = useCallback((message: PopupMessage) => { + setPopupMessages((v) => v.filter((m) => m !== message)); }, []); return ( From 4704393539086151c3ea463322afcc482b965889 Mon Sep 17 00:00:00 2001 From: Dave Marco Date: Fri, 11 Oct 2024 21:29:41 +0000 Subject: [PATCH 28/45] small cleanup --- .../src/components/Popups/PopupMessageBox.tsx | 11 +++++------ .../src/contexts/NotificationContextProvider.tsx | 3 ++- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/new-log-viewer/src/components/Popups/PopupMessageBox.tsx b/new-log-viewer/src/components/Popups/PopupMessageBox.tsx index 5ced6331..5e40f135 100644 --- a/new-log-viewer/src/components/Popups/PopupMessageBox.tsx +++ b/new-log-viewer/src/components/Popups/PopupMessageBox.tsx @@ -33,18 +33,17 @@ interface PopupMessageProps { */ const PopupMessageBox = ({message}: PopupMessageProps) => { const {handlePopupMessageClose} = useContext(NotificationContext); + const color = message.level >= LOG_LEVEL.ERROR ? "danger" : "primary"; + useEffect(() => { if (DO_NOT_TIMEOUT_VALUE !== message.timeoutMillis) { - setTimeout( - () => { - handlePopupMessageClose(message); - }, - message.timeoutMillis - ); + setTimeout(() => { + handlePopupMessageClose(message); + }, message.timeoutMillis); } }, [ message, diff --git a/new-log-viewer/src/contexts/NotificationContextProvider.tsx b/new-log-viewer/src/contexts/NotificationContextProvider.tsx index 170bd9bf..435231cd 100644 --- a/new-log-viewer/src/contexts/NotificationContextProvider.tsx +++ b/new-log-viewer/src/contexts/NotificationContextProvider.tsx @@ -78,7 +78,8 @@ const NotificationContextProvider = ({children}: NotificationContextProviderProp }, []); const handlePopupMessageClose = useCallback((message: PopupMessage) => { - setPopupMessages((v) => v.filter((m) => m !== message)); + // Keep everything but except input message. + setPopupMessages((popups) => popups.filter((m) => m !== message)); }, []); return ( From 7881cb57cc7e25dc1cc98d0cd675e20061ac0f9b Mon Sep 17 00:00:00 2001 From: Junhao Liao Date: Sat, 12 Oct 2024 22:06:32 +0800 Subject: [PATCH 29/45] Add generic type `WithId`. --- new-log-viewer/src/typings/common.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/new-log-viewer/src/typings/common.ts b/new-log-viewer/src/typings/common.ts index 01000ce1..cd6acf17 100644 --- a/new-log-viewer/src/typings/common.ts +++ b/new-log-viewer/src/typings/common.ts @@ -4,7 +4,10 @@ type NullableProperties = { [P in keyof T]: Nullable; }; +type WithId = T & { id: number }; + export type { Nullable, NullableProperties, + WithId, }; From ddbb5779cfaaf4622a6eb7b6a397a02ef4978ecb Mon Sep 17 00:00:00 2001 From: Junhao Liao Date: Sat, 12 Oct 2024 22:07:48 +0800 Subject: [PATCH 30/45] Add field `id` to the pop-up messages and use that as keys in rendering ``s and also as IDs when removing messages from the queue. --- .../src/components/Popups/index.tsx | 4 +-- .../contexts/NotificationContextProvider.tsx | 29 ++++++++++--------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/new-log-viewer/src/components/Popups/index.tsx b/new-log-viewer/src/components/Popups/index.tsx index 36c4a547..34a293ef 100644 --- a/new-log-viewer/src/components/Popups/index.tsx +++ b/new-log-viewer/src/components/Popups/index.tsx @@ -24,9 +24,9 @@ const Popups = () => { open={0 < popupMessages.length} > - {popupMessages.map((message, index) => ( + {popupMessages.map((message) => ( ))} diff --git a/new-log-viewer/src/contexts/NotificationContextProvider.tsx b/new-log-viewer/src/contexts/NotificationContextProvider.tsx index 435231cd..1fb0444a 100644 --- a/new-log-viewer/src/contexts/NotificationContextProvider.tsx +++ b/new-log-viewer/src/contexts/NotificationContextProvider.tsx @@ -1,10 +1,14 @@ import React, { createContext, useCallback, + useRef, useState, } from "react"; -import {Nullable} from "../typings/common"; +import { + Nullable, + WithId, +} from "../typings/common"; import {LOG_LEVEL} from "../typings/logs"; @@ -16,9 +20,9 @@ interface PopupMessage { } interface NotificationContextType { - popupMessages: PopupMessage[], + popupMessages: WithId[], - handlePopupMessageClose: (message: PopupMessage) => void; + handlePopupMessageClose: (messageId: number) => void; postPopup: (message: PopupMessage) => void, } @@ -57,29 +61,28 @@ interface NotificationContextProviderProps { * @return */ const NotificationContextProvider = ({children}: NotificationContextProviderProps) => { - const [popupMessages, setPopupMessages] = useState( + const [popupMessages, setPopupMessages] = useState[]>( NOTIFICATION_DEFAULT.popupMessages ); + const nextPopUpMessageIdRef = useRef(0); - const postPopup = useCallback(({level, message, timeoutMillis, title}:PopupMessage) => { + const postPopup = useCallback((message:PopupMessage) => { const newMessage = { - level: level, - message: message, - timeoutMillis: timeoutMillis, - title: "" === title ? - LOG_LEVEL[level] : - title, + id: nextPopUpMessageIdRef.current, + ...message, }; + nextPopUpMessageIdRef.current++; + setPopupMessages((v) => ([ ...v, newMessage, ])); }, []); - const handlePopupMessageClose = useCallback((message: PopupMessage) => { + const handlePopupMessageClose = useCallback((messageId: number) => { // Keep everything but except input message. - setPopupMessages((popups) => popups.filter((m) => m !== message)); + setPopupMessages((popups) => popups.filter((m) => m.id !== messageId)); }, []); return ( From 4635798be518ce673bf51ff402cc0d30bc32a289 Mon Sep 17 00:00:00 2001 From: Junhao Liao Date: Sat, 12 Oct 2024 22:08:52 +0800 Subject: [PATCH 31/45] Add circular progress to PopupMessageBox for visualization of the timeout timer. --- .../src/components/Popups/PopupMessageBox.tsx | 66 +++++++++++++++---- .../src/components/Popups/index.css | 7 ++ 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/new-log-viewer/src/components/Popups/PopupMessageBox.tsx b/new-log-viewer/src/components/Popups/PopupMessageBox.tsx index 5e40f135..d31f6255 100644 --- a/new-log-viewer/src/components/Popups/PopupMessageBox.tsx +++ b/new-log-viewer/src/components/Popups/PopupMessageBox.tsx @@ -1,11 +1,14 @@ import { useContext, useEffect, + useRef, + useState, } from "react"; import { Alert, Box, + CircularProgress, IconButton, Typography, } from "@mui/joy"; @@ -17,11 +20,17 @@ import { NotificationContext, PopupMessage, } from "../../contexts/NotificationContextProvider"; +import { + Nullable, + WithId, +} from "../../typings/common"; import {LOG_LEVEL} from "../../typings/logs"; +const AUTO_DISMISS_PERCENT_UPDATE_INTERVAL_MILLIS = 50; + interface PopupMessageProps { - message: PopupMessage, + message: WithId, } /** @@ -33,23 +42,44 @@ interface PopupMessageProps { */ const PopupMessageBox = ({message}: PopupMessageProps) => { const {handlePopupMessageClose} = useContext(NotificationContext); + const [timeoutPercent, setTimeoutPercent] = useState(0); + const startTimeMillisRef = useRef(Date.now()); - const color = message.level >= LOG_LEVEL.ERROR ? - "danger" : - "primary"; - + const handleCloseButtonClick = () => { + handlePopupMessageClose(message.id); + }; useEffect(() => { - if (DO_NOT_TIMEOUT_VALUE !== message.timeoutMillis) { - setTimeout(() => { - handlePopupMessageClose(message); - }, message.timeoutMillis); + const {timeoutMillis} = message; + let timeoutId: Nullable> = null; + let intervalId: Nullable> = null; + if (DO_NOT_TIMEOUT_VALUE !== timeoutMillis) { + timeoutId = setTimeout(() => { + handlePopupMessageClose(message.id); + }, timeoutMillis); + intervalId = setInterval(() => { + const fraction = (Date.now() - startTimeMillisRef.current) / timeoutMillis; + setTimeoutPercent(100 - (100 * fraction)); + }, AUTO_DISMISS_PERCENT_UPDATE_INTERVAL_MILLIS); } + + return () => { + if (null !== timeoutId) { + clearTimeout(timeoutId); + } + if (null !== intervalId) { + clearInterval(intervalId); + } + }; }, [ message, handlePopupMessageClose, ]); + const color = message.level >= LOG_LEVEL.ERROR ? + "danger" : + "primary"; + return ( { > {message.title} - { handlePopupMessageClose(message); }} + thickness={2} + value={timeoutPercent} > - - + + + + {message.message} diff --git a/new-log-viewer/src/components/Popups/index.css b/new-log-viewer/src/components/Popups/index.css index b5688ee1..184cb3a7 100644 --- a/new-log-viewer/src/components/Popups/index.css +++ b/new-log-viewer/src/components/Popups/index.css @@ -25,3 +25,10 @@ .pop-up-message-box-title-text { flex-grow: 1; } + +.pop-up-message-box-close-button { + /* stylelint-disable-next-line custom-property-pattern */ + --IconButton-size: 18px !important; + + border-radius: 18px !important; +} From d81e25827c5a32bed2da000f90682ffea8997768 Mon Sep 17 00:00:00 2001 From: Junhao Liao Date: Thu, 17 Oct 2024 18:04:30 -0400 Subject: [PATCH 32/45] Update Popups to auto-scroll on new messages. --- .../src/components/Popups/index.css | 14 +++++++++- .../src/components/Popups/index.tsx | 26 +++++++++++++++++-- .../contexts/NotificationContextProvider.tsx | 2 +- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/new-log-viewer/src/components/Popups/index.css b/new-log-viewer/src/components/Popups/index.css index 184cb3a7..e3e4770a 100644 --- a/new-log-viewer/src/components/Popups/index.css +++ b/new-log-viewer/src/components/Popups/index.css @@ -1,15 +1,27 @@ .pop-up-messages-container-snackbar { + /* Disable pointer events on the transparent container to allow components underneath to be + accessed. */ + pointer-events: none; + right: 14px !important; bottom: var(--ylv-status-bar-height) !important; - padding: 10px !important; + padding: 0 !important; background: transparent !important; border: none !important; box-shadow: none !important; } +.pop-up-messages-container-stack { + overflow-y: auto; + height: calc(100vh - var(--ylv-status-bar-height) - var(--ylv-menu-bar-height)); +} + .pop-up-message-box-alert { + /* Restore pointer events on the pou-up messages. See above `pointer-events: none` in + `.pop-up-messages-container-snackbar`. */ + pointer-events: initial; padding-inline: 18px !important; } diff --git a/new-log-viewer/src/components/Popups/index.tsx b/new-log-viewer/src/components/Popups/index.tsx index 34a293ef..9e19eb03 100644 --- a/new-log-viewer/src/components/Popups/index.tsx +++ b/new-log-viewer/src/components/Popups/index.tsx @@ -1,4 +1,8 @@ -import {useContext} from "react"; +import { + useContext, + useEffect, + useRef, +} from "react"; import { Snackbar, @@ -18,12 +22,30 @@ import "./index.css"; */ const Popups = () => { const {popupMessages} = useContext(NotificationContext); + const containerStackRef = useRef(null); + + // On `popupMessages` update, scroll to the very top of the container. + useEffect(() => { + if (null === containerStackRef.current) { + // The component is mounted yet. + return; + } + + // The negative sign is necessary because the Stack is in "column-reverse" direction. + containerStackRef.current.scrollTo({top: -containerStackRef.current.scrollHeight}); + }, [popupMessages]); + return ( - + {popupMessages.map((message) => ( = Object.freeze({ /** * The default duration in milliseconds after which an automatic dismissal will occur. */ -const DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS = 10_000; +const DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS = 10_0000; /** * A value that indicates that a pop-up message should not be automatically dismissed. From 62409b0dece2101b553f2c13b622f568da4e0b86 Mon Sep 17 00:00:00 2001 From: Junhao Liao Date: Thu, 17 Oct 2024 18:28:00 -0400 Subject: [PATCH 33/45] Get new messages appear at the bottom and get rid of the auto-scrolling-to-top behaviour. --- .../src/components/Popups/index.tsx | 19 +------------------ .../contexts/NotificationContextProvider.tsx | 4 ++-- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/new-log-viewer/src/components/Popups/index.tsx b/new-log-viewer/src/components/Popups/index.tsx index 9e19eb03..240abcdb 100644 --- a/new-log-viewer/src/components/Popups/index.tsx +++ b/new-log-viewer/src/components/Popups/index.tsx @@ -1,8 +1,4 @@ -import { - useContext, - useEffect, - useRef, -} from "react"; +import {useContext} from "react"; import { Snackbar, @@ -22,18 +18,6 @@ import "./index.css"; */ const Popups = () => { const {popupMessages} = useContext(NotificationContext); - const containerStackRef = useRef(null); - - // On `popupMessages` update, scroll to the very top of the container. - useEffect(() => { - if (null === containerStackRef.current) { - // The component is mounted yet. - return; - } - - // The negative sign is necessary because the Stack is in "column-reverse" direction. - containerStackRef.current.scrollTo({top: -containerStackRef.current.scrollHeight}); - }, [popupMessages]); return ( { className={"pop-up-messages-container-stack"} direction={"column-reverse"} gap={1} - ref={containerStackRef} > {popupMessages.map((message) => ( = Object.freeze({ /** * The default duration in milliseconds after which an automatic dismissal will occur. */ -const DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS = 10_0000; +const DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS = 10_000; /** * A value that indicates that a pop-up message should not be automatically dismissed. @@ -75,8 +75,8 @@ const NotificationContextProvider = ({children}: NotificationContextProviderProp nextPopUpMessageIdRef.current++; setPopupMessages((v) => ([ - ...v, newMessage, + ...v, ])); }, []); From 7b71ac424a63e73187cde209ac402f55d87dae6f Mon Sep 17 00:00:00 2001 From: Junhao Liao Date: Thu, 17 Oct 2024 18:30:43 -0400 Subject: [PATCH 34/45] Hide the scrollbar while keeping the container scrollable. --- new-log-viewer/src/components/Popups/index.css | 1 + 1 file changed, 1 insertion(+) diff --git a/new-log-viewer/src/components/Popups/index.css b/new-log-viewer/src/components/Popups/index.css index e3e4770a..99a2ffa2 100644 --- a/new-log-viewer/src/components/Popups/index.css +++ b/new-log-viewer/src/components/Popups/index.css @@ -14,6 +14,7 @@ } .pop-up-messages-container-stack { + scrollbar-width: none; overflow-y: auto; height: calc(100vh - var(--ylv-status-bar-height) - var(--ylv-menu-bar-height)); } From 779d93b652d76e8fc8e72d6a9d9b433b673ce0af Mon Sep 17 00:00:00 2001 From: Junhao Liao Date: Thu, 17 Oct 2024 18:46:21 -0400 Subject: [PATCH 35/45] Move PopupMessage type into separate file. --- .../src/contexts/NotificationContextProvider.tsx | 16 +++------------- new-log-viewer/src/typings/notifications.ts | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 13 deletions(-) create mode 100644 new-log-viewer/src/typings/notifications.ts diff --git a/new-log-viewer/src/contexts/NotificationContextProvider.tsx b/new-log-viewer/src/contexts/NotificationContextProvider.tsx index d7f58257..40375ee4 100644 --- a/new-log-viewer/src/contexts/NotificationContextProvider.tsx +++ b/new-log-viewer/src/contexts/NotificationContextProvider.tsx @@ -5,19 +5,9 @@ import React, { useState, } from "react"; -import { - Nullable, - WithId, -} from "../typings/common"; -import {LOG_LEVEL} from "../typings/logs"; - - -interface PopupMessage { - level: LOG_LEVEL, - message: string, - timeoutMillis: Nullable, - title: string, -} +import {WithId} from "../typings/common"; +import {PopupMessage} from "../typings/notifications"; + interface NotificationContextType { popupMessages: WithId[], diff --git a/new-log-viewer/src/typings/notifications.ts b/new-log-viewer/src/typings/notifications.ts new file mode 100644 index 00000000..477638e3 --- /dev/null +++ b/new-log-viewer/src/typings/notifications.ts @@ -0,0 +1,15 @@ +import {Nullable} from "./common"; +import {LOG_LEVEL} from "./logs"; + + +/** + * Contents of popup messages and its associated auto dismiss timeout. + */ +interface PopupMessage { + level: LOG_LEVEL, + message: string, + timeoutMillis: Nullable, + title: string, +} + +export type {PopupMessage}; From 143a4bb8547f37034718720d30a5ea395e1a6bd6 Mon Sep 17 00:00:00 2001 From: Junhao Liao Date: Thu, 17 Oct 2024 18:50:54 -0400 Subject: [PATCH 36/45] Use counter approach in calculating timer percentage - Apply suggestions from code review Co-authored-by: davemarco <83603688+davemarco@users.noreply.github.com> --- .../src/components/Popups/PopupMessageBox.tsx | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/new-log-viewer/src/components/Popups/PopupMessageBox.tsx b/new-log-viewer/src/components/Popups/PopupMessageBox.tsx index d31f6255..0bccfa7b 100644 --- a/new-log-viewer/src/components/Popups/PopupMessageBox.tsx +++ b/new-log-viewer/src/components/Popups/PopupMessageBox.tsx @@ -42,34 +42,38 @@ interface PopupMessageProps { */ const PopupMessageBox = ({message}: PopupMessageProps) => { const {handlePopupMessageClose} = useContext(NotificationContext); - const [timeoutPercent, setTimeoutPercent] = useState(0); - const startTimeMillisRef = useRef(Date.now()); + const [intervalCount, setIntervalCount] = useState(0); + + const {timeoutMillis} = message; + let percentRemaining: number = 100; + + if (timeoutMillis) { + // If timeout is not 0 or null. + const totalIntervals = + Math.ceil(timeoutMillis / AUTO_DISMISS_PERCENT_UPDATE_INTERVAL_MILLIS); + + percentRemaining = 100 - (100 * (intervalCount / totalIntervals)); + } const handleCloseButtonClick = () => { handlePopupMessageClose(message.id); }; useEffect(() => { - const {timeoutMillis} = message; - let timeoutId: Nullable> = null; - let intervalId: Nullable> = null; - if (DO_NOT_TIMEOUT_VALUE !== timeoutMillis) { - timeoutId = setTimeout(() => { - handlePopupMessageClose(message.id); - }, timeoutMillis); - intervalId = setInterval(() => { - const fraction = (Date.now() - startTimeMillisRef.current) / timeoutMillis; - setTimeoutPercent(100 - (100 * fraction)); - }, AUTO_DISMISS_PERCENT_UPDATE_INTERVAL_MILLIS); + if (DO_NOT_TIMEOUT_VALUE === message.timeoutMillis) { + return () => {}; } + const timeoutId = setTimeout(() => { + handlePopupMessageClose(message.id); + }, message.timeoutMillis); + + const intervalId = setInterval(() => { + setIntervalCount((c) => c + 1); + }, AUTO_DISMISS_PERCENT_UPDATE_INTERVAL_MILLIS); return () => { - if (null !== timeoutId) { - clearTimeout(timeoutId); - } - if (null !== intervalId) { - clearInterval(intervalId); - } + clearTimeout(timeoutId); + clearInterval(intervalId); }; }, [ message, From 2dec93bb2ab4f363b5b380ac927a0d82ade8a0df Mon Sep 17 00:00:00 2001 From: Junhao Liao Date: Thu, 17 Oct 2024 19:03:33 -0400 Subject: [PATCH 37/45] Move PopupMessage-related constants into typings/notifications.ts; change DO_NOT_TIMEOUT_VALUE=null->0. --- .../src/components/Popups/PopupMessageBox.tsx | 46 +++++++------------ .../modals/SettingsModal/SettingsDialog.tsx | 6 +-- .../contexts/NotificationContextProvider.tsx | 15 +----- .../src/contexts/StateContextProvider.tsx | 6 +-- new-log-viewer/src/typings/notifications.ts | 18 +++++++- 5 files changed, 38 insertions(+), 53 deletions(-) diff --git a/new-log-viewer/src/components/Popups/PopupMessageBox.tsx b/new-log-viewer/src/components/Popups/PopupMessageBox.tsx index 0bccfa7b..9cf5d1ed 100644 --- a/new-log-viewer/src/components/Popups/PopupMessageBox.tsx +++ b/new-log-viewer/src/components/Popups/PopupMessageBox.tsx @@ -1,7 +1,6 @@ import { useContext, useEffect, - useRef, useState, } from "react"; @@ -16,15 +15,12 @@ import { import CloseIcon from "@mui/icons-material/Close"; import { - DO_NOT_TIMEOUT_VALUE, NotificationContext, PopupMessage, } from "../../contexts/NotificationContextProvider"; -import { - Nullable, - WithId, -} from "../../typings/common"; +import {WithId} from "../../typings/common"; import {LOG_LEVEL} from "../../typings/logs"; +import {DO_NOT_TIMEOUT_VALUE} from "../../typings/notifications"; const AUTO_DISMISS_PERCENT_UPDATE_INTERVAL_MILLIS = 50; @@ -41,49 +37,41 @@ interface PopupMessageProps { * @return */ const PopupMessageBox = ({message}: PopupMessageProps) => { + const {id, level, message: messageStr, title, timeoutMillis} = message; + const {handlePopupMessageClose} = useContext(NotificationContext); const [intervalCount, setIntervalCount] = useState(0); - const {timeoutMillis} = message; - let percentRemaining: number = 100; - - if (timeoutMillis) { - // If timeout is not 0 or null. - const totalIntervals = - Math.ceil(timeoutMillis / AUTO_DISMISS_PERCENT_UPDATE_INTERVAL_MILLIS); - - percentRemaining = 100 - (100 * (intervalCount / totalIntervals)); - } - const handleCloseButtonClick = () => { - handlePopupMessageClose(message.id); + handlePopupMessageClose(id); }; useEffect(() => { - if (DO_NOT_TIMEOUT_VALUE === message.timeoutMillis) { + if (DO_NOT_TIMEOUT_VALUE === timeoutMillis) { return () => {}; } - const timeoutId = setTimeout(() => { - handlePopupMessageClose(message.id); - }, message.timeoutMillis); - const intervalId = setInterval(() => { setIntervalCount((c) => c + 1); }, AUTO_DISMISS_PERCENT_UPDATE_INTERVAL_MILLIS); return () => { - clearTimeout(timeoutId); clearInterval(intervalId); }; }, [ - message, + timeoutMillis, handlePopupMessageClose, ]); - const color = message.level >= LOG_LEVEL.ERROR ? + const color = level >= LOG_LEVEL.ERROR ? "danger" : "primary"; + let percentRemaining = 100; + if (DO_NOT_TIMEOUT_VALUE !== timeoutMillis) { + const totalIntervals = timeoutMillis / AUTO_DISMISS_PERCENT_UPDATE_INTERVAL_MILLIS; + percentRemaining = 100 - (100 * (intervalCount / totalIntervals)); + } + return ( { color={color} level={"title-md"} > - {message.title} + {title} { - {message.message} + {messageStr} diff --git a/new-log-viewer/src/components/modals/SettingsModal/SettingsDialog.tsx b/new-log-viewer/src/components/modals/SettingsModal/SettingsDialog.tsx index 21410415..2b98d1f8 100644 --- a/new-log-viewer/src/components/modals/SettingsModal/SettingsDialog.tsx +++ b/new-log-viewer/src/components/modals/SettingsModal/SettingsDialog.tsx @@ -16,16 +16,14 @@ import FormHelperText from "@mui/joy/FormHelperText"; import FormLabel from "@mui/joy/FormLabel/FormLabel"; import Input from "@mui/joy/Input"; -import { - DO_NOT_TIMEOUT_VALUE, - NotificationContext, -} from "../../../contexts/NotificationContextProvider"; +import {NotificationContext} from "../../../contexts/NotificationContextProvider"; import {Nullable} from "../../../typings/common"; import { CONFIG_KEY, LOCAL_STORAGE_KEY, } from "../../../typings/config"; import {LOG_LEVEL} from "../../../typings/logs"; +import {DO_NOT_TIMEOUT_VALUE} from "../../../typings/notifications"; import { getConfig, setConfig, diff --git a/new-log-viewer/src/contexts/NotificationContextProvider.tsx b/new-log-viewer/src/contexts/NotificationContextProvider.tsx index 40375ee4..922269ed 100644 --- a/new-log-viewer/src/contexts/NotificationContextProvider.tsx +++ b/new-log-viewer/src/contexts/NotificationContextProvider.tsx @@ -28,15 +28,6 @@ const NOTIFICATION_DEFAULT: Readonly = Object.freeze({ postPopup: () => {}, }); -/** - * The default duration in milliseconds after which an automatic dismissal will occur. - */ -const DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS = 10_000; - -/** - * A value that indicates that a pop-up message should not be automatically dismissed. - */ -const DO_NOT_TIMEOUT_VALUE = null; interface NotificationContextProviderProps { children: React.ReactNode, @@ -88,10 +79,6 @@ const NotificationContextProvider = ({children}: NotificationContextProviderProp ); }; -export { - DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS, - DO_NOT_TIMEOUT_VALUE, - NotificationContext, -}; +export {NotificationContext}; export type {PopupMessage}; export default NotificationContextProvider; diff --git a/new-log-viewer/src/contexts/StateContextProvider.tsx b/new-log-viewer/src/contexts/StateContextProvider.tsx index 41efe4b9..46fd239a 100644 --- a/new-log-viewer/src/contexts/StateContextProvider.tsx +++ b/new-log-viewer/src/contexts/StateContextProvider.tsx @@ -12,6 +12,7 @@ import LogExportManager, {EXPORT_LOG_PROGRESS_VALUE_MIN} from "../services/LogEx import {Nullable} from "../typings/common"; import {CONFIG_KEY} from "../typings/config"; import {LogLevelFilter} from "../typings/logs"; +import {DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS} from "../typings/notifications"; import {SEARCH_PARAM_NAMES} from "../typings/url"; import { BeginLineNumToLogEventNumMap, @@ -37,10 +38,7 @@ import { isWithinBounds, } from "../utils/data"; import {clamp} from "../utils/math"; -import { - DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS, - NotificationContext, -} from "./NotificationContextProvider"; +import {NotificationContext} from "./NotificationContextProvider"; import { updateWindowUrlHashParams, updateWindowUrlSearchParams, diff --git a/new-log-viewer/src/typings/notifications.ts b/new-log-viewer/src/typings/notifications.ts index 477638e3..ab7a58d7 100644 --- a/new-log-viewer/src/typings/notifications.ts +++ b/new-log-viewer/src/typings/notifications.ts @@ -1,4 +1,3 @@ -import {Nullable} from "./common"; import {LOG_LEVEL} from "./logs"; @@ -8,8 +7,23 @@ import {LOG_LEVEL} from "./logs"; interface PopupMessage { level: LOG_LEVEL, message: string, - timeoutMillis: Nullable, + timeoutMillis: number, title: string, } +/** + * A value that indicates that a pop-up message should not be automatically dismissed. + */ +const DO_NOT_TIMEOUT_VALUE = 0; + +/** + * The default duration in milliseconds after which an automatic dismissal will occur. + */ +const DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS = 10_000; + + export type {PopupMessage}; +export { + DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS, + DO_NOT_TIMEOUT_VALUE, +}; From 38cafbdeb24813dc8e1159b5dd7358bc98edc9a4 Mon Sep 17 00:00:00 2001 From: Junhao Liao Date: Thu, 17 Oct 2024 19:12:51 -0400 Subject: [PATCH 38/45] Add back the auto-dismiss behaviour. --- new-log-viewer/src/components/Popups/PopupMessageBox.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/new-log-viewer/src/components/Popups/PopupMessageBox.tsx b/new-log-viewer/src/components/Popups/PopupMessageBox.tsx index 9cf5d1ed..b75605fd 100644 --- a/new-log-viewer/src/components/Popups/PopupMessageBox.tsx +++ b/new-log-viewer/src/components/Popups/PopupMessageBox.tsx @@ -70,6 +70,11 @@ const PopupMessageBox = ({message}: PopupMessageProps) => { if (DO_NOT_TIMEOUT_VALUE !== timeoutMillis) { const totalIntervals = timeoutMillis / AUTO_DISMISS_PERCENT_UPDATE_INTERVAL_MILLIS; percentRemaining = 100 - (100 * (intervalCount / totalIntervals)); + if (0 >= percentRemaining) { + setTimeout(() => { + handlePopupMessageClose(id); + }, 0); + } } return ( From 189be812fb430d3abb1ac124e7351b67a7f5a7c9 Mon Sep 17 00:00:00 2001 From: Junhao Liao Date: Thu, 17 Oct 2024 19:43:52 -0400 Subject: [PATCH 39/45] Rename 'popup' references to 'popUp' across components. --- new-log-viewer/src/components/Layout.tsx | 4 +-- ...opupMessageBox.tsx => PopUpMessageBox.tsx} | 18 +++++----- .../src/components/Popups/index.tsx | 16 ++++----- .../modals/SettingsModal/SettingsDialog.tsx | 6 ++-- .../contexts/NotificationContextProvider.tsx | 34 +++++++++---------- .../src/contexts/StateContextProvider.tsx | 6 ++-- new-log-viewer/src/typings/notifications.ts | 6 ++-- 7 files changed, 45 insertions(+), 45 deletions(-) rename new-log-viewer/src/components/Popups/{PopupMessageBox.tsx => PopUpMessageBox.tsx} (89%) diff --git a/new-log-viewer/src/components/Layout.tsx b/new-log-viewer/src/components/Layout.tsx index d3a529fb..9e6335ee 100644 --- a/new-log-viewer/src/components/Layout.tsx +++ b/new-log-viewer/src/components/Layout.tsx @@ -4,7 +4,7 @@ import {CONFIG_KEY} from "../typings/config"; import {CONFIG_DEFAULT} from "../utils/config"; import CentralContainer from "./CentralContainer"; import MenuBar from "./MenuBar"; -import Popups from "./Popups"; +import PopUps from "./PopUps"; import StatusBar from "./StatusBar"; import APP_THEME from "./theme"; @@ -24,7 +24,7 @@ const Layout = () => { - + ); }; diff --git a/new-log-viewer/src/components/Popups/PopupMessageBox.tsx b/new-log-viewer/src/components/Popups/PopUpMessageBox.tsx similarity index 89% rename from new-log-viewer/src/components/Popups/PopupMessageBox.tsx rename to new-log-viewer/src/components/Popups/PopUpMessageBox.tsx index b75605fd..6aa50ff7 100644 --- a/new-log-viewer/src/components/Popups/PopupMessageBox.tsx +++ b/new-log-viewer/src/components/Popups/PopUpMessageBox.tsx @@ -16,7 +16,7 @@ import CloseIcon from "@mui/icons-material/Close"; import { NotificationContext, - PopupMessage, + PopUpMessage, } from "../../contexts/NotificationContextProvider"; import {WithId} from "../../typings/common"; import {LOG_LEVEL} from "../../typings/logs"; @@ -25,8 +25,8 @@ import {DO_NOT_TIMEOUT_VALUE} from "../../typings/notifications"; const AUTO_DISMISS_PERCENT_UPDATE_INTERVAL_MILLIS = 50; -interface PopupMessageProps { - message: WithId, +interface PopUpMessageProps { + message: WithId, } /** @@ -36,14 +36,14 @@ interface PopupMessageProps { * @param props.message * @return */ -const PopupMessageBox = ({message}: PopupMessageProps) => { +const PopUpMessageBox = ({message}: PopUpMessageProps) => { const {id, level, message: messageStr, title, timeoutMillis} = message; - const {handlePopupMessageClose} = useContext(NotificationContext); + const {handlePopUpMessageClose} = useContext(NotificationContext); const [intervalCount, setIntervalCount] = useState(0); const handleCloseButtonClick = () => { - handlePopupMessageClose(id); + handlePopUpMessageClose(id); }; useEffect(() => { @@ -59,7 +59,7 @@ const PopupMessageBox = ({message}: PopupMessageProps) => { }; }, [ timeoutMillis, - handlePopupMessageClose, + handlePopUpMessageClose, ]); const color = level >= LOG_LEVEL.ERROR ? @@ -72,7 +72,7 @@ const PopupMessageBox = ({message}: PopupMessageProps) => { percentRemaining = 100 - (100 * (intervalCount / totalIntervals)); if (0 >= percentRemaining) { setTimeout(() => { - handlePopupMessageClose(id); + handlePopUpMessageClose(id); }, 0); } } @@ -117,4 +117,4 @@ const PopupMessageBox = ({message}: PopupMessageProps) => { ); }; -export default PopupMessageBox; +export default PopUpMessageBox; diff --git a/new-log-viewer/src/components/Popups/index.tsx b/new-log-viewer/src/components/Popups/index.tsx index 240abcdb..c146a20f 100644 --- a/new-log-viewer/src/components/Popups/index.tsx +++ b/new-log-viewer/src/components/Popups/index.tsx @@ -6,31 +6,31 @@ import { } from "@mui/joy"; import {NotificationContext} from "../../contexts/NotificationContextProvider"; -import PopupMessageBox from "./PopupMessageBox"; +import PopUpMessageBox from "./PopUpMessageBox"; import "./index.css"; /** - * Displays popups. + * Displays pop-ups in a transparent container positioned on the right side of the viewport. * * @return */ -const Popups = () => { - const {popupMessages} = useContext(NotificationContext); +const PopUps = () => { + const {popUpMessages} = useContext(NotificationContext); return ( - {popupMessages.map((message) => ( - ( + ))} @@ -39,4 +39,4 @@ const Popups = () => { ); }; -export default Popups; +export default PopUps; diff --git a/new-log-viewer/src/components/modals/SettingsModal/SettingsDialog.tsx b/new-log-viewer/src/components/modals/SettingsModal/SettingsDialog.tsx index 2b98d1f8..c3ac316d 100644 --- a/new-log-viewer/src/components/modals/SettingsModal/SettingsDialog.tsx +++ b/new-log-viewer/src/components/modals/SettingsModal/SettingsDialog.tsx @@ -79,7 +79,7 @@ const handleConfigFormReset = (ev: React.FormEvent) => { * @return */ const SettingsDialog = forwardRef((_, ref) => { - const {postPopup} = useContext(NotificationContext); + const {postPopUp} = useContext(NotificationContext); const handleConfigFormSubmit = useCallback((ev: React.FormEvent) => { ev.preventDefault(); @@ -102,7 +102,7 @@ const SettingsDialog = forwardRef((_, ref) => { }); if (null !== error) { - postPopup({ + postPopUp({ level: LOG_LEVEL.ERROR, message: error, timeoutMillis: DO_NOT_TIMEOUT_VALUE, @@ -111,7 +111,7 @@ const SettingsDialog = forwardRef((_, ref) => { } else { window.location.reload(); } - }, [postPopup]); + }, [postPopUp]); return ( [], + popUpMessages: WithId[], - handlePopupMessageClose: (messageId: number) => void; - postPopup: (message: PopupMessage) => void, + handlePopUpMessageClose: (messageId: number) => void; + postPopUp: (message: PopUpMessage) => void, } const NotificationContext = createContext({} as NotificationContextType); @@ -22,10 +22,10 @@ const NotificationContext = createContext({} as Notific * Default values of the Notification context value object. */ const NOTIFICATION_DEFAULT: Readonly = Object.freeze({ - popupMessages: [], + popUpMessages: [], - handlePopupMessageClose: () => {}, - postPopup: () => {}, + handlePopUpMessageClose: () => {}, + postPopUp: () => {}, }); @@ -42,12 +42,12 @@ interface NotificationContextProviderProps { * @return */ const NotificationContextProvider = ({children}: NotificationContextProviderProps) => { - const [popupMessages, setPopupMessages] = useState[]>( - NOTIFICATION_DEFAULT.popupMessages + const [popUpMessages, setPopUpMessages] = useState[]>( + NOTIFICATION_DEFAULT.popUpMessages ); const nextPopUpMessageIdRef = useRef(0); - const postPopup = useCallback((message:PopupMessage) => { + const postPopUp = useCallback((message:PopUpMessage) => { const newMessage = { id: nextPopUpMessageIdRef.current, ...message, @@ -55,23 +55,23 @@ const NotificationContextProvider = ({children}: NotificationContextProviderProp nextPopUpMessageIdRef.current++; - setPopupMessages((v) => ([ + setPopUpMessages((v) => ([ newMessage, ...v, ])); }, []); - const handlePopupMessageClose = useCallback((messageId: number) => { + const handlePopUpMessageClose = useCallback((messageId: number) => { // Keep everything but except input message. - setPopupMessages((popups) => popups.filter((m) => m.id !== messageId)); + setPopUpMessages((v) => v.filter((m) => m.id !== messageId)); }, []); return ( {children} @@ -80,5 +80,5 @@ const NotificationContextProvider = ({children}: NotificationContextProviderProp }; export {NotificationContext}; -export type {PopupMessage}; +export type {PopUpMessage}; export default NotificationContextProvider; diff --git a/new-log-viewer/src/contexts/StateContextProvider.tsx b/new-log-viewer/src/contexts/StateContextProvider.tsx index 46fd239a..9d745470 100644 --- a/new-log-viewer/src/contexts/StateContextProvider.tsx +++ b/new-log-viewer/src/contexts/StateContextProvider.tsx @@ -225,7 +225,7 @@ const updateUrlIfEventOnPage = ( */ // eslint-disable-next-line max-lines-per-function, max-statements const StateContextProvider = ({children}: StateContextProviderProps) => { - const {postPopup} = useContext(NotificationContext); + const {postPopUp} = useContext(NotificationContext); const {filePath, logEventNum} = useContext(UrlContext); // States @@ -264,7 +264,7 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { setOnDiskFileSizeInBytes(args.onDiskFileSizeInBytes); break; case WORKER_RESP_CODE.NOTIFICATION: - postPopup({ + postPopUp({ level: args.logLevel, message: args.message, timeoutMillis: DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS, @@ -285,7 +285,7 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { console.error(`Unexpected ev.data: ${JSON.stringify(ev.data)}`); break; } - }, [postPopup]); + }, [postPopUp]); const exportLogs = useCallback(() => { if (null === mainWorkerRef.current) { diff --git a/new-log-viewer/src/typings/notifications.ts b/new-log-viewer/src/typings/notifications.ts index ab7a58d7..f67fc892 100644 --- a/new-log-viewer/src/typings/notifications.ts +++ b/new-log-viewer/src/typings/notifications.ts @@ -2,9 +2,9 @@ import {LOG_LEVEL} from "./logs"; /** - * Contents of popup messages and its associated auto dismiss timeout. + * Contents of pop-up messages and its associated auto dismiss timeout. */ -interface PopupMessage { +interface PopUpMessage { level: LOG_LEVEL, message: string, timeoutMillis: number, @@ -22,7 +22,7 @@ const DO_NOT_TIMEOUT_VALUE = 0; const DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS = 10_000; -export type {PopupMessage}; +export type {PopUpMessage}; export { DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS, DO_NOT_TIMEOUT_VALUE, From c81d38c6212b73b6429ef6bea1eeef6fc6346cb5 Mon Sep 17 00:00:00 2001 From: Junhao Liao Date: Thu, 17 Oct 2024 20:20:04 -0400 Subject: [PATCH 40/45] more - Rename 'popup' references to 'popUp' across components. --- .../src/components/{Popups => PopUps}/PopUpMessageBox.tsx | 0 new-log-viewer/src/components/{Popups => PopUps}/index.css | 0 new-log-viewer/src/components/{Popups => PopUps}/index.tsx | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename new-log-viewer/src/components/{Popups => PopUps}/PopUpMessageBox.tsx (100%) rename new-log-viewer/src/components/{Popups => PopUps}/index.css (100%) rename new-log-viewer/src/components/{Popups => PopUps}/index.tsx (100%) diff --git a/new-log-viewer/src/components/Popups/PopUpMessageBox.tsx b/new-log-viewer/src/components/PopUps/PopUpMessageBox.tsx similarity index 100% rename from new-log-viewer/src/components/Popups/PopUpMessageBox.tsx rename to new-log-viewer/src/components/PopUps/PopUpMessageBox.tsx diff --git a/new-log-viewer/src/components/Popups/index.css b/new-log-viewer/src/components/PopUps/index.css similarity index 100% rename from new-log-viewer/src/components/Popups/index.css rename to new-log-viewer/src/components/PopUps/index.css diff --git a/new-log-viewer/src/components/Popups/index.tsx b/new-log-viewer/src/components/PopUps/index.tsx similarity index 100% rename from new-log-viewer/src/components/Popups/index.tsx rename to new-log-viewer/src/components/PopUps/index.tsx From ac0ee35377fb633b688a7a421142c6fcc4382f38 Mon Sep 17 00:00:00 2001 From: Junhao Liao Date: Fri, 18 Oct 2024 12:06:20 -0400 Subject: [PATCH 41/45] Refactor timeout logic in PopUpMessageBox. --- .../src/components/PopUps/PopUpMessageBox.tsx | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/new-log-viewer/src/components/PopUps/PopUpMessageBox.tsx b/new-log-viewer/src/components/PopUps/PopUpMessageBox.tsx index 6aa50ff7..1a91fd18 100644 --- a/new-log-viewer/src/components/PopUps/PopUpMessageBox.tsx +++ b/new-log-viewer/src/components/PopUps/PopUpMessageBox.tsx @@ -1,6 +1,7 @@ import { useContext, useEffect, + useRef, useState, } from "react"; @@ -40,7 +41,8 @@ const PopUpMessageBox = ({message}: PopUpMessageProps) => { const {id, level, message: messageStr, title, timeoutMillis} = message; const {handlePopUpMessageClose} = useContext(NotificationContext); - const [intervalCount, setIntervalCount] = useState(0); + const [percentRemaining, setPercentRemaining] = useState(100); + const intervalCountRef = useRef(0); const handleCloseButtonClick = () => { handlePopUpMessageClose(id); @@ -50,8 +52,15 @@ const PopUpMessageBox = ({message}: PopUpMessageProps) => { if (DO_NOT_TIMEOUT_VALUE === timeoutMillis) { return () => {}; } + + const totalIntervals = timeoutMillis / AUTO_DISMISS_PERCENT_UPDATE_INTERVAL_MILLIS; const intervalId = setInterval(() => { - setIntervalCount((c) => c + 1); + intervalCountRef.current++; + const newPercentRemaining = 100 - (100 * (intervalCountRef.current / totalIntervals)); + if (0 >= newPercentRemaining) { + handlePopUpMessageClose(id); + } + setPercentRemaining(newPercentRemaining); }, AUTO_DISMISS_PERCENT_UPDATE_INTERVAL_MILLIS); return () => { @@ -60,23 +69,13 @@ const PopUpMessageBox = ({message}: PopUpMessageProps) => { }, [ timeoutMillis, handlePopUpMessageClose, + id, ]); const color = level >= LOG_LEVEL.ERROR ? "danger" : "primary"; - let percentRemaining = 100; - if (DO_NOT_TIMEOUT_VALUE !== timeoutMillis) { - const totalIntervals = timeoutMillis / AUTO_DISMISS_PERCENT_UPDATE_INTERVAL_MILLIS; - percentRemaining = 100 - (100 * (intervalCount / totalIntervals)); - if (0 >= percentRemaining) { - setTimeout(() => { - handlePopUpMessageClose(id); - }, 0); - } - } - return ( Date: Fri, 18 Oct 2024 12:07:30 -0400 Subject: [PATCH 42/45] Fix typo - Apply suggestions from code review Co-authored-by: davemarco <83603688+davemarco@users.noreply.github.com> --- new-log-viewer/src/components/PopUps/index.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/new-log-viewer/src/components/PopUps/index.css b/new-log-viewer/src/components/PopUps/index.css index 99a2ffa2..be9612fd 100644 --- a/new-log-viewer/src/components/PopUps/index.css +++ b/new-log-viewer/src/components/PopUps/index.css @@ -20,7 +20,7 @@ } .pop-up-message-box-alert { - /* Restore pointer events on the pou-up messages. See above `pointer-events: none` in + /* Restore pointer events on the pop-up messages. See above `pointer-events: none` in `.pop-up-messages-container-snackbar`. */ pointer-events: initial; padding-inline: 18px !important; From de99ef327b4ad6539ddd2666e017bc9dbb111528 Mon Sep 17 00:00:00 2001 From: Junhao Liao Date: Fri, 18 Oct 2024 13:07:19 -0400 Subject: [PATCH 43/45] Fix auto-dismiss calculation by rounding up intervals. --- new-log-viewer/src/components/PopUps/PopUpMessageBox.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/new-log-viewer/src/components/PopUps/PopUpMessageBox.tsx b/new-log-viewer/src/components/PopUps/PopUpMessageBox.tsx index 1a91fd18..5d22080e 100644 --- a/new-log-viewer/src/components/PopUps/PopUpMessageBox.tsx +++ b/new-log-viewer/src/components/PopUps/PopUpMessageBox.tsx @@ -53,7 +53,9 @@ const PopUpMessageBox = ({message}: PopUpMessageProps) => { return () => {}; } - const totalIntervals = timeoutMillis / AUTO_DISMISS_PERCENT_UPDATE_INTERVAL_MILLIS; + const totalIntervals = Math.ceil( + timeoutMillis / AUTO_DISMISS_PERCENT_UPDATE_INTERVAL_MILLIS + ); const intervalId = setInterval(() => { intervalCountRef.current++; const newPercentRemaining = 100 - (100 * (intervalCountRef.current / totalIntervals)); From 6980c7a479c80eb29fc0a2ea5d6567d44e44c156 Mon Sep 17 00:00:00 2001 From: Junhao Liao Date: Fri, 18 Oct 2024 15:14:03 -0400 Subject: [PATCH 44/45] Increase progress ring thickness from 2 to 3 - Apply suggestions from code review Co-authored-by: Henry8192 <50559854+Henry8192@users.noreply.github.com> --- new-log-viewer/src/components/PopUps/PopUpMessageBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/new-log-viewer/src/components/PopUps/PopUpMessageBox.tsx b/new-log-viewer/src/components/PopUps/PopUpMessageBox.tsx index 5d22080e..7ae9f20b 100644 --- a/new-log-viewer/src/components/PopUps/PopUpMessageBox.tsx +++ b/new-log-viewer/src/components/PopUps/PopUpMessageBox.tsx @@ -97,7 +97,7 @@ const PopUpMessageBox = ({message}: PopUpMessageProps) => { color={color} determinate={true} size={"sm"} - thickness={2} + thickness={3} value={percentRemaining} > Date: Fri, 18 Oct 2024 15:24:05 -0400 Subject: [PATCH 45/45] Remove redundant template type specification - Apply suggestions from code review Co-authored-by: Henry8192 <50559854+Henry8192@users.noreply.github.com> --- new-log-viewer/src/contexts/NotificationContextProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/new-log-viewer/src/contexts/NotificationContextProvider.tsx b/new-log-viewer/src/contexts/NotificationContextProvider.tsx index 3b08dc19..01ed7bdf 100644 --- a/new-log-viewer/src/contexts/NotificationContextProvider.tsx +++ b/new-log-viewer/src/contexts/NotificationContextProvider.tsx @@ -16,7 +16,7 @@ interface NotificationContextType { postPopUp: (message: PopUpMessage) => void, } -const NotificationContext = createContext({} as NotificationContextType); +const NotificationContext = createContext({} as NotificationContextType); /** * Default values of the Notification context value object.