From 14a3114afc3d33aabfb2578c868b7ec279809a4b Mon Sep 17 00:00:00 2001 From: Dave Marco Date: Fri, 22 Nov 2024 19:15:20 +0000 Subject: [PATCH 1/9] first draft --- .../Sidebar/SidebarTabs/index.tsx | 5 ++-- src/components/PopUps/PopUpMessageBox.tsx | 13 +++++++- src/components/PopUps/index.css | 4 +++ src/contexts/StateContextProvider.tsx | 30 +++++++++++++++++-- .../formatters/YscopeFormatter/index.ts | 6 ++++ src/typings/notifications.ts | 9 ++++++ src/utils/config.ts | 9 +++--- 7 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/components/CentralContainer/Sidebar/SidebarTabs/index.tsx b/src/components/CentralContainer/Sidebar/SidebarTabs/index.tsx index 2e7a0596..90b3c93e 100644 --- a/src/components/CentralContainer/Sidebar/SidebarTabs/index.tsx +++ b/src/components/CentralContainer/Sidebar/SidebarTabs/index.tsx @@ -1,6 +1,6 @@ import { forwardRef, - useState, + useContext, } from "react"; import { @@ -13,6 +13,7 @@ import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; import SearchIcon from "@mui/icons-material/Search"; import SettingsOutlinedIcon from "@mui/icons-material/SettingsOutlined"; +import {StateContext} from "../../../../contexts/StateContextProvider"; import {TAB_NAME} from "../../../../typings/tab"; import SettingsModal from "../../../modals/SettingsModal"; import FileInfoTabPanel from "./FileInfoTabPanel"; @@ -51,7 +52,7 @@ const SidebarTabs = forwardRef(( }, tabListRef ) => { - const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false); + const {isSettingsModalOpen, setIsSettingsModalOpen} = useContext(StateContext); const handleSettingsModalClose = () => { setIsSettingsModalOpen(false); diff --git a/src/components/PopUps/PopUpMessageBox.tsx b/src/components/PopUps/PopUpMessageBox.tsx index 7ae9f20b..fb8ce224 100644 --- a/src/components/PopUps/PopUpMessageBox.tsx +++ b/src/components/PopUps/PopUpMessageBox.tsx @@ -8,6 +8,7 @@ import { import { Alert, Box, + Button, CircularProgress, IconButton, Typography, @@ -38,7 +39,7 @@ interface PopUpMessageProps { * @return */ const PopUpMessageBox = ({message}: PopUpMessageProps) => { - const {id, level, message: messageStr, title, timeoutMillis} = message; + const {id, level, message: messageStr, title, timeoutMillis, button} = message; const {handlePopUpMessageClose} = useContext(NotificationContext); const [percentRemaining, setPercentRemaining] = useState(100); @@ -113,6 +114,16 @@ const PopUpMessageBox = ({message}: PopUpMessageProps) => { {messageStr} + {button && ( + + )} ); diff --git a/src/components/PopUps/index.css b/src/components/PopUps/index.css index be9612fd..acb475bf 100644 --- a/src/components/PopUps/index.css +++ b/src/components/PopUps/index.css @@ -30,6 +30,10 @@ width: 300px; } +.pop-up-message-box-callback-button { + margin-top: 10px !important; +} + .pop-up-message-box-title-container { display: flex; align-items: center; diff --git a/src/contexts/StateContextProvider.tsx b/src/contexts/StateContextProvider.tsx index 09d5eda3..4ff33555 100644 --- a/src/contexts/StateContextProvider.tsx +++ b/src/contexts/StateContextProvider.tsx @@ -14,7 +14,10 @@ import LogExportManager, { } from "../services/LogExportManager"; import {Nullable} from "../typings/common"; import {CONFIG_KEY} from "../typings/config"; -import {LogLevelFilter} from "../typings/logs"; +import { + LOG_LEVEL, + LogLevelFilter, +} from "../typings/logs"; import {DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS} from "../typings/notifications"; import {UI_STATE} from "../typings/states"; import {SEARCH_PARAM_NAMES} from "../typings/url"; @@ -36,6 +39,7 @@ import { NavigationAction, } from "../utils/actions"; import { + CONFIG_DEFAULT, EXPORT_LOGS_CHUNK_SIZE, getConfig, } from "../utils/config"; @@ -56,8 +60,9 @@ import { interface StateContextType { beginLineNumToLogEventNum: BeginLineNumToLogEventNumMap, - fileName: string, exportProgress: Nullable, + fileName: string, + isSettingsModalOpen: boolean, uiState: UI_STATE, logData: string, numEvents: number, @@ -70,6 +75,7 @@ interface StateContextType { exportLogs: () => void, loadFile: (fileSrc: FileSrcType, cursor: CursorType) => void, loadPageByAction: (navAction: NavigationAction) => void, + setIsSettingsModalOpen: (isOpen: boolean) => void, setLogLevelFilter: (newLogLevelFilter: LogLevelFilter) => void, startQuery: (queryString: string, isRegex: boolean, isCaseSensitive: boolean) => void, } @@ -82,6 +88,7 @@ const STATE_DEFAULT: Readonly = Object.freeze({ beginLineNumToLogEventNum: new Map(), exportProgress: null, fileName: "", + isSettingsModalOpen: false, logData: "No file is open.", numEvents: 0, numPages: 0, @@ -94,6 +101,7 @@ const STATE_DEFAULT: Readonly = Object.freeze({ exportLogs: () => null, loadFile: () => null, loadPageByAction: () => null, + setIsSettingsModalOpen: () => null, setLogLevelFilter: () => null, startQuery: () => null, }); @@ -236,6 +244,8 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { // States const [exportProgress, setExportProgress] = useState>(STATE_DEFAULT.exportProgress); + const [isSettingsModalOpen, setIsSettingsModalOpen] = + useState(STATE_DEFAULT.isSettingsModalOpen); const [fileName, setFileName] = useState(STATE_DEFAULT.fileName); const [logData, setLogData] = useState(STATE_DEFAULT.logData); const [numEvents, setNumEvents] = useState(STATE_DEFAULT.numEvents); @@ -274,6 +284,20 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { setFileName(args.fileName); setNumEvents(args.numEvents); setOnDiskFileSizeInBytes(args.onDiskFileSizeInBytes); + if (getConfig(CONFIG_KEY.DECODER_OPTIONS).formatString === + CONFIG_DEFAULT[CONFIG_KEY.DECODER_OPTIONS].formatString) { + postPopUp({ + button: { + title: "Open Settings", + callback: () => { setIsSettingsModalOpen(true); }, + }, + level: LOG_LEVEL.INFO, + message: "Open settings to configure a format" + + " string to improve readability of structured logs", + timeoutMillis: 2 * DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS, + title: "Configure Format String", + }); + } break; case WORKER_RESP_CODE.NOTIFICATION: postPopUp({ @@ -510,6 +534,7 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { beginLineNumToLogEventNum: beginLineNumToLogEventNumRef.current, exportProgress: exportProgress, fileName: fileName, + isSettingsModalOpen: isSettingsModalOpen, logData: logData, numEvents: numEvents, numPages: numPages, @@ -522,6 +547,7 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { exportLogs: exportLogs, loadFile: loadFile, loadPageByAction: loadPageByAction, + setIsSettingsModalOpen: setIsSettingsModalOpen, setLogLevelFilter: setLogLevelFilter, startQuery: startQuery, }} diff --git a/src/services/formatters/YscopeFormatter/index.ts b/src/services/formatters/YscopeFormatter/index.ts index 9997771f..b01bd437 100644 --- a/src/services/formatters/YscopeFormatter/index.ts +++ b/src/services/formatters/YscopeFormatter/index.ts @@ -10,6 +10,7 @@ import { import {LogEvent} from "../../../typings/logs"; import { getFormattedField, + jsonValueToString, removeEscapeCharacters, replaceDoubleBacklash, splitFieldPlaceholder, @@ -37,6 +38,11 @@ class YscopeFormatter implements Formatter { } formatLogEvent (logEvent: LogEvent): string { + // Empty format string is special case where formatter returns all fields as JSON. + if ("" === this.#processedFormatString) { + return jsonValueToString(logEvent.fields); + } + const formattedLogFragments: string[] = []; let lastIndex = 0; diff --git a/src/typings/notifications.ts b/src/typings/notifications.ts index f67fc892..0ce479c9 100644 --- a/src/typings/notifications.ts +++ b/src/typings/notifications.ts @@ -1,6 +1,14 @@ import {LOG_LEVEL} from "./logs"; +/** + * Optional button and callback function for pop-up. + */ +type PopUpButton = { + title: string, + callback: () => void, +} + /** * Contents of pop-up messages and its associated auto dismiss timeout. */ @@ -9,6 +17,7 @@ interface PopUpMessage { message: string, timeoutMillis: number, title: string, + button?: PopUpButton, } /** diff --git a/src/utils/config.ts b/src/utils/config.ts index 29ba3983..a10ee619 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -19,8 +19,7 @@ const QUERY_CHUNK_SIZE = 10_000; */ const CONFIG_DEFAULT: ConfigMap = Object.freeze({ [CONFIG_KEY.DECODER_OPTIONS]: { - formatString: "{@timestamp:timestamp:YYYY-MM-DD HH\\:mm\\:ss.SSS}" + - " {log\\.level} [{process\\.thread\\.name}] {message}", + formatString: "", logLevelKey: "log.level", timestampKey: "@timestamp", }, @@ -42,8 +41,10 @@ const testConfig = ({key, value}: ConfigUpdate): Nullable => { let result = null; switch (key) { case CONFIG_KEY.DECODER_OPTIONS: - if (Object.values(value).includes("")) { - result = "Decoder options cannot be empty."; + if ("" === value.timestampKey) { + result = "Timestamp key cannot be empty."; + } else if ("" === value.logLevelKey) { + result = "Log level key cannot be empty."; } break; case CONFIG_KEY.INITIAL_TAB_NAME: From 5c1d573f1f091f0ecafb89f1f8811a7574812a1b Mon Sep 17 00:00:00 2001 From: Dave Marco Date: Fri, 22 Nov 2024 19:19:00 +0000 Subject: [PATCH 2/9] small change --- src/components/PopUps/PopUpMessageBox.tsx | 2 +- src/typings/notifications.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/PopUps/PopUpMessageBox.tsx b/src/components/PopUps/PopUpMessageBox.tsx index fb8ce224..eddbe9ee 100644 --- a/src/components/PopUps/PopUpMessageBox.tsx +++ b/src/components/PopUps/PopUpMessageBox.tsx @@ -39,7 +39,7 @@ interface PopUpMessageProps { * @return */ const PopUpMessageBox = ({message}: PopUpMessageProps) => { - const {id, level, message: messageStr, title, timeoutMillis, button} = message; + const {id, level, button, message: messageStr, title, timeoutMillis} = message; const {handlePopUpMessageClose} = useContext(NotificationContext); const [percentRemaining, setPercentRemaining] = useState(100); diff --git a/src/typings/notifications.ts b/src/typings/notifications.ts index 0ce479c9..edc1bc02 100644 --- a/src/typings/notifications.ts +++ b/src/typings/notifications.ts @@ -2,7 +2,7 @@ import {LOG_LEVEL} from "./logs"; /** - * Optional button and callback function for pop-up. + * Optional button for pop-up. */ type PopUpButton = { title: string, From 1942e04a6a3cd936f992c13de89f1df6da933483 Mon Sep 17 00:00:00 2001 From: Dave Marco Date: Fri, 22 Nov 2024 19:34:10 +0000 Subject: [PATCH 3/9] small changes --- src/components/modals/SettingsModal/SettingsDialog.tsx | 3 ++- src/contexts/StateContextProvider.tsx | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/modals/SettingsModal/SettingsDialog.tsx b/src/components/modals/SettingsModal/SettingsDialog.tsx index b119d7f0..005ed0d8 100644 --- a/src/components/modals/SettingsModal/SettingsDialog.tsx +++ b/src/components/modals/SettingsModal/SettingsDialog.tsx @@ -38,7 +38,8 @@ const CONFIG_FORM_FIELDS = [ \`{[:[:]]}\`, where \`field-name\` is required, while \`formatter-name\` and \`formatter-options\` are optional. For example, the following placeholder would format a timestamp field with name \`@timestamp\`: - \`{@timestamp:timestamp:YYYY-MM-DD HH\\:mm\\:ss.SSS}\`.`, + \`{@timestamp:timestamp:YYYY-MM-DD HH\\:mm\\:ss.SSS}\`. The default setting is + an empty string which will print all fields formatted as JSON.`, initialValue: getConfig(CONFIG_KEY.DECODER_OPTIONS).formatString, label: "Decoder: Format string", name: LOCAL_STORAGE_KEY.DECODER_OPTIONS_FORMAT_STRING, diff --git a/src/contexts/StateContextProvider.tsx b/src/contexts/StateContextProvider.tsx index 4ff33555..c74a6b01 100644 --- a/src/contexts/StateContextProvider.tsx +++ b/src/contexts/StateContextProvider.tsx @@ -292,10 +292,10 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { callback: () => { setIsSettingsModalOpen(true); }, }, level: LOG_LEVEL.INFO, - message: "Open settings to configure a format" + - " string to improve readability of structured logs", + message: "Input a custom format string in settings dialog" + + " to improve readability of JSON logs", timeoutMillis: 2 * DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS, - title: "Configure Format String", + title: "Format JSON Logs", }); } break; From 027f00da2307cd6c18404a8d19cff83679d50fac Mon Sep 17 00:00:00 2001 From: davemarco <83603688+davemarco@users.noreply.github.com> Date: Tue, 26 Nov 2024 20:11:57 -0500 Subject: [PATCH 4/9] Update src/contexts/StateContextProvider.tsx Co-authored-by: Junhao Liao --- src/contexts/StateContextProvider.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/contexts/StateContextProvider.tsx b/src/contexts/StateContextProvider.tsx index c74a6b01..ff24bc07 100644 --- a/src/contexts/StateContextProvider.tsx +++ b/src/contexts/StateContextProvider.tsx @@ -292,8 +292,7 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { callback: () => { setIsSettingsModalOpen(true); }, }, level: LOG_LEVEL.INFO, - message: "Input a custom format string in settings dialog" + - " to improve readability of JSON logs", + message: "Adding one can enhance the readability of your structured logs by customizing how fields are displayed.", timeoutMillis: 2 * DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS, title: "Format JSON Logs", }); From f2959ae7dc660c6e7b08223644660042af50552b Mon Sep 17 00:00:00 2001 From: davemarco <83603688+davemarco@users.noreply.github.com> Date: Tue, 26 Nov 2024 20:12:03 -0500 Subject: [PATCH 5/9] Update src/contexts/StateContextProvider.tsx Co-authored-by: Junhao Liao --- src/contexts/StateContextProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/contexts/StateContextProvider.tsx b/src/contexts/StateContextProvider.tsx index ff24bc07..e4ef844d 100644 --- a/src/contexts/StateContextProvider.tsx +++ b/src/contexts/StateContextProvider.tsx @@ -294,7 +294,7 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { level: LOG_LEVEL.INFO, message: "Adding one can enhance the readability of your structured logs by customizing how fields are displayed.", timeoutMillis: 2 * DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS, - title: "Format JSON Logs", + title: "A format string has not been configured", }); } break; From 78f336c439e332c64c68fd9a1c7206efbf1a2cab Mon Sep 17 00:00:00 2001 From: davemarco <83603688+davemarco@users.noreply.github.com> Date: Tue, 26 Nov 2024 20:12:10 -0500 Subject: [PATCH 6/9] Update src/components/PopUps/PopUpMessageBox.tsx Co-authored-by: Junhao Liao --- src/components/PopUps/PopUpMessageBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/PopUps/PopUpMessageBox.tsx b/src/components/PopUps/PopUpMessageBox.tsx index eddbe9ee..8486256a 100644 --- a/src/components/PopUps/PopUpMessageBox.tsx +++ b/src/components/PopUps/PopUpMessageBox.tsx @@ -118,7 +118,7 @@ const PopUpMessageBox = ({message}: PopUpMessageProps) => { )} diff --git a/src/components/PopUps/index.css b/src/components/PopUps/index.css index acb475bf..bd2e714b 100644 --- a/src/components/PopUps/index.css +++ b/src/components/PopUps/index.css @@ -27,13 +27,12 @@ } .pop-up-message-box-alert-layout { + display: flex; + flex-direction: column; + gap: 10px; width: 300px; } -.pop-up-message-box-callback-button { - margin-top: 10px !important; -} - .pop-up-message-box-title-container { display: flex; align-items: center; diff --git a/src/contexts/StateContextProvider.tsx b/src/contexts/StateContextProvider.tsx index e4ef844d..b59d8059 100644 --- a/src/contexts/StateContextProvider.tsx +++ b/src/contexts/StateContextProvider.tsx @@ -8,6 +8,8 @@ import React, { useState, } from "react"; +import SettingsOutlinedIcon from "@mui/icons-material/SettingsOutlined"; + import LogExportManager, { EXPORT_LOG_PROGRESS_VALUE_MAX, EXPORT_LOG_PROGRESS_VALUE_MIN, @@ -18,7 +20,10 @@ import { LOG_LEVEL, LogLevelFilter, } from "../typings/logs"; -import {DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS} from "../typings/notifications"; +import { + DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS, + LONG_AUTO_DISMISS_TIMEOUT_MILLIS, +} from "../typings/notifications"; import {UI_STATE} from "../typings/states"; import {SEARCH_PARAM_NAMES} from "../typings/url"; import { @@ -39,7 +44,6 @@ import { NavigationAction, } from "../utils/actions"; import { - CONFIG_DEFAULT, EXPORT_LOGS_CHUNK_SIZE, getConfig, } from "../utils/config"; @@ -280,23 +284,24 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { } } break; + case WORKER_RESP_CODE.FORMAT_POPUP: + postPopUp({ + level: LOG_LEVEL.INFO, + message: "Adding a format string can enhance the readability of your" + + " structured logs by customizing how fields are displayed.", + primaryAction: { + children: "Settings", + startDecorator: , + onClick: () => { setIsSettingsModalOpen(true); }, + }, + timeoutMillis: LONG_AUTO_DISMISS_TIMEOUT_MILLIS, + title: "A format string has not been configured", + }); + break; case WORKER_RESP_CODE.LOG_FILE_INFO: setFileName(args.fileName); setNumEvents(args.numEvents); setOnDiskFileSizeInBytes(args.onDiskFileSizeInBytes); - if (getConfig(CONFIG_KEY.DECODER_OPTIONS).formatString === - CONFIG_DEFAULT[CONFIG_KEY.DECODER_OPTIONS].formatString) { - postPopUp({ - button: { - title: "Open Settings", - callback: () => { setIsSettingsModalOpen(true); }, - }, - level: LOG_LEVEL.INFO, - message: "Adding one can enhance the readability of your structured logs by customizing how fields are displayed.", - timeoutMillis: 2 * DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS, - title: "A format string has not been configured", - }); - } break; case WORKER_RESP_CODE.NOTIFICATION: postPopUp({ diff --git a/src/services/MainWorker.ts b/src/services/MainWorker.ts index ce58d1f9..d7d3118b 100644 --- a/src/services/MainWorker.ts +++ b/src/services/MainWorker.ts @@ -50,6 +50,14 @@ const onQueryResults = (queryProgress: number, queryResults: QueryResults) => { postResp(WORKER_RESP_CODE.QUERY_RESULT, {progress: queryProgress, results: queryResults}); }; +/** + * Sends a message to the renderer to open a popup which prompts user to replace the default + * format string. + */ +const postFormatPopup = () => { + postResp(WORKER_RESP_CODE.FORMAT_POPUP, null); +}; + // eslint-disable-next-line no-warning-comments // TODO: Break this function up into smaller functions. // eslint-disable-next-line max-lines-per-function,max-statements @@ -149,3 +157,5 @@ onmessage = async (ev: MessageEvent) => { } } }; + +export {postFormatPopup}; diff --git a/src/services/decoders/ClpIrDecoder.ts b/src/services/decoders/ClpIrDecoder.ts index 81383259..305ad740 100644 --- a/src/services/decoders/ClpIrDecoder.ts +++ b/src/services/decoders/ClpIrDecoder.ts @@ -13,6 +13,7 @@ import {Formatter} from "../../typings/formatters"; import {JsonObject} from "../../typings/js"; import {LogLevelFilter} from "../../typings/logs"; import YscopeFormatter from "../formatters/YscopeFormatter"; +import {postFormatPopup} from "../MainWorker"; import { convertToDayjsTimestamp, isJsonObject, @@ -29,7 +30,7 @@ class ClpIrDecoder implements Decoder { readonly #streamType: CLP_IR_STREAM_TYPE; - #formatter: Nullable; + #formatter: Nullable = null; constructor ( streamType: CLP_IR_STREAM_TYPE, @@ -38,9 +39,12 @@ class ClpIrDecoder implements Decoder { ) { this.#streamType = streamType; this.#streamReader = streamReader; - this.#formatter = (streamType === CLP_IR_STREAM_TYPE.STRUCTURED) ? - new YscopeFormatter({formatString: decoderOptions.formatString}) : - null; + if (streamType === CLP_IR_STREAM_TYPE.STRUCTURED) { + this.#formatter = new YscopeFormatter({formatString: decoderOptions.formatString}); + if (0 === decoderOptions.formatString.length) { + postFormatPopup(); + } + } } /** diff --git a/src/services/decoders/JsonlDecoder/index.ts b/src/services/decoders/JsonlDecoder/index.ts index 5405ca00..1ff3f4d7 100644 --- a/src/services/decoders/JsonlDecoder/index.ts +++ b/src/services/decoders/JsonlDecoder/index.ts @@ -17,6 +17,7 @@ import { LogLevelFilter, } from "../../../typings/logs"; import YscopeFormatter from "../../formatters/YscopeFormatter"; +import {postFormatPopup} from "../../MainWorker"; import { convertToDayjsTimestamp, convertToLogLevelValue, @@ -54,6 +55,9 @@ class JsonlDecoder implements Decoder { this.#logLevelKey = decoderOptions.logLevelKey; this.#timestampKey = decoderOptions.timestampKey; this.#formatter = new YscopeFormatter({formatString: decoderOptions.formatString}); + if (0 === decoderOptions.formatString.length) { + postFormatPopup(); + } } getEstimatedNumEvents (): number { diff --git a/src/typings/file.ts b/src/typings/file.ts index 8b6d4af3..89aedb82 100644 --- a/src/typings/file.ts +++ b/src/typings/file.ts @@ -1,3 +1,4 @@ type OnFileOpenCallback = (file: File) => void; + export type {OnFileOpenCallback}; diff --git a/src/typings/formatters.ts b/src/typings/formatters.ts index bd1dc0ce..15352587 100644 --- a/src/typings/formatters.ts +++ b/src/typings/formatters.ts @@ -4,7 +4,7 @@ import {LogEvent} from "./logs"; /** - * @property formatString A Yscope format string. The format string can include field-placeholders + * @property formatString A YScope format string. The format string can include field-placeholders * to insert and format any field of a JSON log event. A field-placeholder uses the following * syntax: * `{[:[:]]}` @@ -49,14 +49,14 @@ interface YscopeFieldFormatter { } /** - * Type for list of currently supported Yscope field formatters. + * Type for list of currently supported YScope field formatters. */ type YscopeFieldFormatterMap = { [key: string]: new (options: Nullable) => YscopeFieldFormatter; }; /** - * Parsed field placeholder from a Yscope format string. + * Parsed field placeholder from a YScope format string. */ type YscopeFieldPlaceholder = { fieldNameKeys: string[], diff --git a/src/typings/notifications.ts b/src/typings/notifications.ts index edc1bc02..506f3607 100644 --- a/src/typings/notifications.ts +++ b/src/typings/notifications.ts @@ -1,14 +1,8 @@ +import {ButtonProps} from "@mui/joy"; + import {LOG_LEVEL} from "./logs"; -/** - * Optional button for pop-up. - */ -type PopUpButton = { - title: string, - callback: () => void, -} - /** * Contents of pop-up messages and its associated auto dismiss timeout. */ @@ -17,7 +11,7 @@ interface PopUpMessage { message: string, timeoutMillis: number, title: string, - button?: PopUpButton, + primaryAction?: ButtonProps, } /** @@ -30,9 +24,15 @@ const DO_NOT_TIMEOUT_VALUE = 0; */ const DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS = 10_000; +/** + * A longer duration in milliseconds after which an automatic dismissal will occur. + */ +const LONG_AUTO_DISMISS_TIMEOUT_MILLIS = 20_000; + export type {PopUpMessage}; export { DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS, DO_NOT_TIMEOUT_VALUE, + LONG_AUTO_DISMISS_TIMEOUT_MILLIS, }; diff --git a/src/typings/worker.ts b/src/typings/worker.ts index 334f1cb0..94d87d9e 100644 --- a/src/typings/worker.ts +++ b/src/typings/worker.ts @@ -77,6 +77,7 @@ enum WORKER_REQ_CODE { enum WORKER_RESP_CODE { CHUNK_DATA = "chunkData", + FORMAT_POPUP = "format_popup", LOG_FILE_INFO = "fileInfo", NOTIFICATION = "notification", PAGE_DATA = "pageData", @@ -122,6 +123,7 @@ type WorkerRespMap = { [WORKER_RESP_CODE.CHUNK_DATA]: { logs: string }, + [WORKER_RESP_CODE.FORMAT_POPUP]: null, [WORKER_RESP_CODE.LOG_FILE_INFO]: { fileName: string, numEvents: number, diff --git a/src/utils/config.ts b/src/utils/config.ts index a10ee619..becc0837 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -41,9 +41,9 @@ const testConfig = ({key, value}: ConfigUpdate): Nullable => { let result = null; switch (key) { case CONFIG_KEY.DECODER_OPTIONS: - if ("" === value.timestampKey) { + if (0 === value.timestampKey.length) { result = "Timestamp key cannot be empty."; - } else if ("" === value.logLevelKey) { + } else if (0 === value.logLevelKey.length) { result = "Log level key cannot be empty."; } break; From 8855636ea8565352bf22aaee108e5206676ff6e3 Mon Sep 17 00:00:00 2001 From: Dave Marco Date: Wed, 27 Nov 2024 03:23:09 +0000 Subject: [PATCH 8/9] small changes --- src/services/MainWorker.ts | 2 +- src/typings/file.ts | 1 - src/typings/worker.ts | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/services/MainWorker.ts b/src/services/MainWorker.ts index d7d3118b..d7ce638d 100644 --- a/src/services/MainWorker.ts +++ b/src/services/MainWorker.ts @@ -51,7 +51,7 @@ const onQueryResults = (queryProgress: number, queryResults: QueryResults) => { }; /** - * Sends a message to the renderer to open a popup which prompts user to replace the default + * Sends a message to the renderer to open a pop-up which prompts user to replace the default * format string. */ const postFormatPopup = () => { diff --git a/src/typings/file.ts b/src/typings/file.ts index 89aedb82..8b6d4af3 100644 --- a/src/typings/file.ts +++ b/src/typings/file.ts @@ -1,4 +1,3 @@ type OnFileOpenCallback = (file: File) => void; - export type {OnFileOpenCallback}; diff --git a/src/typings/worker.ts b/src/typings/worker.ts index 94d87d9e..2ccbe1a5 100644 --- a/src/typings/worker.ts +++ b/src/typings/worker.ts @@ -77,7 +77,7 @@ enum WORKER_REQ_CODE { enum WORKER_RESP_CODE { CHUNK_DATA = "chunkData", - FORMAT_POPUP = "format_popup", + FORMAT_POPUP = "formatPopup", LOG_FILE_INFO = "fileInfo", NOTIFICATION = "notification", PAGE_DATA = "pageData", From 8de0b4ca3a84f835302e266e9d2cb214f6982925 Mon Sep 17 00:00:00 2001 From: Dave Marco Date: Wed, 27 Nov 2024 16:48:37 +0000 Subject: [PATCH 9/9] junhao review --- src/components/PopUps/PopUpMessageBox.tsx | 28 +++++++++++-------- src/components/PopUps/index.css | 7 ++++- .../modals/SettingsModal/SettingsDialog.tsx | 4 +-- src/contexts/StateContextProvider.tsx | 6 ++-- 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/components/PopUps/PopUpMessageBox.tsx b/src/components/PopUps/PopUpMessageBox.tsx index 241bd720..5e24b453 100644 --- a/src/components/PopUps/PopUpMessageBox.tsx +++ b/src/components/PopUps/PopUpMessageBox.tsx @@ -1,4 +1,4 @@ -import { +import React, { useContext, useEffect, useRef, @@ -51,6 +51,11 @@ const PopUpMessageBox = ({message}: PopUpMessageProps) => { handlePopUpMessageClose(id); }; + const handlePrimaryActionClick = (ev: React.MouseEvent) => { + primaryAction?.onClick?.(ev); + handleCloseButtonClick(); + }; + useEffect(() => { if (DO_NOT_TIMEOUT_VALUE === timeoutMillis) { return () => {}; @@ -117,17 +122,16 @@ const PopUpMessageBox = ({message}: PopUpMessageProps) => { {messageStr} {primaryAction && ( - + + + )} diff --git a/src/components/PopUps/index.css b/src/components/PopUps/index.css index bd2e714b..3c2171d4 100644 --- a/src/components/PopUps/index.css +++ b/src/components/PopUps/index.css @@ -30,7 +30,12 @@ display: flex; flex-direction: column; gap: 10px; - width: 300px; + width: 333px; +} + +.pop-up-message-box-actions-container { + display: flex; + justify-content: flex-end; } .pop-up-message-box-title-container { diff --git a/src/components/modals/SettingsModal/SettingsDialog.tsx b/src/components/modals/SettingsModal/SettingsDialog.tsx index 005ed0d8..be49765f 100644 --- a/src/components/modals/SettingsModal/SettingsDialog.tsx +++ b/src/components/modals/SettingsModal/SettingsDialog.tsx @@ -38,8 +38,8 @@ const CONFIG_FORM_FIELDS = [ \`{[:[:]]}\`, where \`field-name\` is required, while \`formatter-name\` and \`formatter-options\` are optional. For example, the following placeholder would format a timestamp field with name \`@timestamp\`: - \`{@timestamp:timestamp:YYYY-MM-DD HH\\:mm\\:ss.SSS}\`. The default setting is - an empty string which will print all fields formatted as JSON.`, + \`{@timestamp:timestamp:YYYY-MM-DD HH\\:mm\\:ss.SSS}\`. Leave format string blank to + display the entire log event formatted as JSON.`, initialValue: getConfig(CONFIG_KEY.DECODER_OPTIONS).formatString, label: "Decoder: Format string", name: LOCAL_STORAGE_KEY.DECODER_OPTIONS_FORMAT_STRING, diff --git a/src/contexts/StateContextProvider.tsx b/src/contexts/StateContextProvider.tsx index b59d8059..87917f71 100644 --- a/src/contexts/StateContextProvider.tsx +++ b/src/contexts/StateContextProvider.tsx @@ -80,7 +80,7 @@ interface StateContextType { loadFile: (fileSrc: FileSrcType, cursor: CursorType) => void, loadPageByAction: (navAction: NavigationAction) => void, setIsSettingsModalOpen: (isOpen: boolean) => void, - setLogLevelFilter: (newLogLevelFilter: LogLevelFilter) => void, + setLogLevelFilter: (filter: LogLevelFilter) => void, startQuery: (queryString: string, isRegex: boolean, isCaseSensitive: boolean) => void, } const StateContext = createContext({} as StateContextType); @@ -446,14 +446,14 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { loadPageByCursor(mainWorkerRef.current, cursor); }, []); - const setLogLevelFilter = useCallback((newLogLevelFilter: LogLevelFilter) => { + const setLogLevelFilter = useCallback((filter: LogLevelFilter) => { if (null === mainWorkerRef.current) { return; } setUiState(UI_STATE.FAST_LOADING); workerPostReq(mainWorkerRef.current, WORKER_REQ_CODE.SET_FILTER, { cursor: {code: CURSOR_CODE.EVENT_NUM, args: {eventNum: logEventNumRef.current ?? 1}}, - logLevelFilter: newLogLevelFilter, + logLevelFilter: filter, }); }, []);