Skip to content

Commit

Permalink
Add NotificationContextProvider for publishing and subscribing notifi…
Browse files Browse the repository at this point in the history
…cations.
  • Loading branch information
junhaoliao committed Sep 30, 2024
1 parent d185624 commit 61f4b6e
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 57 deletions.
24 changes: 19 additions & 5 deletions new-log-viewer/src/App.tsx
Original file line number Diff line number Diff line change
@@ -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";


/**
Expand All @@ -10,11 +16,19 @@ import UrlContextProvider from "./contexts/UrlContextProvider";
*/
const App = () => {
return (
<UrlContextProvider>
<StateContextProvider>
<Layout/>
</StateContextProvider>
</UrlContextProvider>
<CssVarsProvider
defaultMode={CONFIG_DEFAULT[CONFIG_KEY.THEME]}
modeStorageKey={CONFIG_KEY.THEME}
theme={APP_THEME}
>
<NotificationContextProvider>
<UrlContextProvider>
<StateContextProvider>
<Layout/>
</StateContextProvider>
</UrlContextProvider>
</NotificationContextProvider>
</CssVarsProvider>
);
};

Expand Down
25 changes: 7 additions & 18 deletions new-log-viewer/src/components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -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";


/**
Expand All @@ -16,19 +11,13 @@ import APP_THEME from "./theme";
*/
const Layout = () => {
return (
<CssVarsProvider
defaultMode={CONFIG_DEFAULT[CONFIG_KEY.THEME]}
modeStorageKey={CONFIG_KEY.THEME}
theme={APP_THEME}
>
<div className={"layout"}>
<MenuBar/>
<DropFileContainer>
<Editor/>
</DropFileContainer>
<StatusBar/>
</div>
</CssVarsProvider>
<div className={"layout"}>
<MenuBar/>
<DropFileContainer>
<Editor/>
</DropFileContainer>
<StatusBar/>
</div>
);
};

Expand Down
4 changes: 3 additions & 1 deletion new-log-viewer/src/components/StatusBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -26,6 +27,7 @@ const handleCopyLinkButtonClick = () => {
* @return
*/
const StatusBar = () => {
const {statusMessage} = useContext(NotificationContext);
const {numEvents} = useContext(StateContext);
const {logEventNum} = useContext(UrlContext);

Expand All @@ -35,7 +37,7 @@ const StatusBar = () => {
className={"status-message"}
level={"body-sm"}
>
Status message
{statusMessage}
</Typography>
<Button
size={"sm"}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import React, {forwardRef} from "react";
import React, {
forwardRef,
useContext,
} from "react";

import {
Button,
Expand All @@ -19,12 +22,14 @@ import DarkModeIcon from "@mui/icons-material/DarkMode";
import LightModeIcon from "@mui/icons-material/LightMode";
import SettingsBrightnessIcon from "@mui/icons-material/SettingsBrightness";

import {NotificationContext} from "../../../contexts/NotificationContextProvider";
import {Nullable} from "../../../typings/common";
import {
CONFIG_KEY,
LOCAL_STORAGE_KEY,
THEME_NAME,
} from "../../../typings/config";
import {LOG_LEVEL} from "../../../typings/logs";
import {
getConfig,
setConfig,
Expand Down Expand Up @@ -74,38 +79,39 @@ const handleConfigFormReset = (ev: React.FormEvent) => {
};

/**
* Handles the submit event for the configuration form.
* Generates a handler for the submit event for the configuration form.
*
* @param ev
* @return the generated handler.
*/
const handleConfigFormSubmit = (ev: React.FormEvent) => {
ev.preventDefault();
const formData = new FormData(ev.target as HTMLFormElement);
const getFormDataValue = (key: string) => formData.get(key) as string;
const useHandleConfigFormSubmit = () => {
const {postPopup} = useContext(NotificationContext);

return (ev: React.FormEvent) => {
ev.preventDefault();
const formData = new FormData(ev.target as HTMLFormElement);
const getFormDataValue = (key: string) => formData.get(key) as string;

const formatString = getFormDataValue(LOCAL_STORAGE_KEY.DECODER_OPTIONS_FORMAT_STRING);
const logLevelKey = getFormDataValue(LOCAL_STORAGE_KEY.DECODER_OPTIONS_LOG_LEVEL_KEY);
const timestampKey = getFormDataValue(LOCAL_STORAGE_KEY.DECODER_OPTIONS_TIMESTAMP_KEY);
const pageSize = getFormDataValue(LOCAL_STORAGE_KEY.PAGE_SIZE);
const formatString = getFormDataValue(LOCAL_STORAGE_KEY.DECODER_OPTIONS_FORMAT_STRING);
const logLevelKey = getFormDataValue(LOCAL_STORAGE_KEY.DECODER_OPTIONS_LOG_LEVEL_KEY);
const timestampKey = getFormDataValue(LOCAL_STORAGE_KEY.DECODER_OPTIONS_TIMESTAMP_KEY);
const pageSize = getFormDataValue(LOCAL_STORAGE_KEY.PAGE_SIZE);

let error: Nullable<string> = null;
error ||= setConfig({
key: CONFIG_KEY.DECODER_OPTIONS,
value: {formatString, logLevelKey, timestampKey},
});
error ||= setConfig({
key: CONFIG_KEY.PAGE_SIZE,
value: Number(pageSize),
});
let error: Nullable<string> = null;
error ||= setConfig({
key: CONFIG_KEY.DECODER_OPTIONS,
value: {formatString, logLevelKey, timestampKey},
});
error ||= setConfig({
key: CONFIG_KEY.PAGE_SIZE,
value: Number(pageSize),
});

if (null !== error) {
// eslint-disable-next-line no-warning-comments
// TODO: Show an error pop-up once NotificationProvider is implemented.
// eslint-disable-next-line no-alert
window.alert(error);
} else {
window.location.reload();
}
if (null !== error) {
postPopup(LOG_LEVEL.ERROR, error, "Unable to apply config.");
} else {
window.location.reload();
}
};
};

/**
Expand All @@ -121,7 +127,7 @@ const SettingsDialog = forwardRef<HTMLFormElement>((_, ref) => {
ref={ref}
tabIndex={-1}
onReset={handleConfigFormReset}
onSubmit={handleConfigFormSubmit}
onSubmit={useHandleConfigFormSubmit()}
>
<ModalDialog
minWidth={"md"}
Expand Down
134 changes: 134 additions & 0 deletions new-log-viewer/src/contexts/NotificationContextProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import React, {
createContext,
useCallback,
useRef,
useState,
} from "react";

import {
Box,
ModalClose,
Snackbar,
Stack,
Typography,
} from "@mui/joy";

import {Nullable} from "../typings/common";
import {LOG_LEVEL} from "../typings/logs";


interface NotificationContextType {
statusMessage: string,

postPopup: (level: LOG_LEVEL, message: string, title?: string) => void,
postStatus: (level: LOG_LEVEL, message: string) => void,
}

const NotificationContext = createContext<NotificationContextType>({} as NotificationContextType);

interface NotificationContextProvider {
children: React.ReactNode;
}

/**
*

Check warning on line 34 in new-log-viewer/src/contexts/NotificationContextProvider.tsx

View workflow job for this annotation

GitHub Actions / lint-check

No empty blocks
*/
const NOTIFICATION_DEFAULT: Readonly<NotificationContextType> = Object.freeze({
statusMessage: "",

postPopup: () => null,
postStatus: () => null,
});

interface PopupNotification {
level: LOG_LEVEL,
message: string,
title: string
}

/**

Check warning on line 49 in new-log-viewer/src/contexts/NotificationContextProvider.tsx

View workflow job for this annotation

GitHub Actions / lint-check

Missing JSDoc block description

Check warning on line 49 in new-log-viewer/src/contexts/NotificationContextProvider.tsx

View workflow job for this annotation

GitHub Actions / lint-check

Missing JSDoc @return declaration
*
* @param props
* @param props.children
*/
const NotificationContextProvider = ({children}: NotificationContextProvider) => {
const [popupNotification, setPopupNotification] = useState<Nullable<PopupNotification>>(null);
const [statusMessage, setStatusMessage] = useState<string>(NOTIFICATION_DEFAULT.statusMessage);
const popupNotificationTimeoutRef = useRef<Nullable<ReturnType<typeof setTimeout>>>(null);
const statusMsgTimeoutRef = useRef<Nullable<ReturnType<typeof setTimeout>>>(null);

const postPopup = useCallback((level: LOG_LEVEL, message: string, title: string = "") => {
if (null !== popupNotificationTimeoutRef.current) {
clearTimeout(popupNotificationTimeoutRef.current);
}
setPopupNotification(null);
setPopupNotification({
level: level,
message: message,
title: "" === title ?
LOG_LEVEL[level] :
title,
});

if (LOG_LEVEL.INFO >= level) {
popupNotificationTimeoutRef.current = setTimeout(() => {
setPopupNotification(null);
}, 5000);

Check failure on line 76 in new-log-viewer/src/contexts/NotificationContextProvider.tsx

View workflow job for this annotation

GitHub Actions / lint-check

No magic number: 5000
}
}, []);

const postStatus = useCallback((level: LOG_LEVEL, message: string) => {
if (null !== statusMsgTimeoutRef.current) {
clearTimeout(statusMsgTimeoutRef.current);
}
setStatusMessage(message);

if (LOG_LEVEL.INFO >= level) {
statusMsgTimeoutRef.current = setTimeout(() => {
setStatusMessage(NOTIFICATION_DEFAULT.statusMessage);
}, 5000);

Check failure on line 89 in new-log-viewer/src/contexts/NotificationContextProvider.tsx

View workflow job for this annotation

GitHub Actions / lint-check

No magic number: 5000
}
}, []);


return (
<NotificationContext.Provider
value={{
statusMessage: statusMessage,
postPopup: postPopup,
postStatus: postStatus,
}}
>
{children}
{null !== popupNotification && <Snackbar
open={true}
sx={{right: "14px", bottom: "32px"}}
color={popupNotification.level >= LOG_LEVEL.ERROR ?
"danger" :
"primary"}
>

Check failure on line 109 in new-log-viewer/src/contexts/NotificationContextProvider.tsx

View workflow job for this annotation

GitHub Actions / lint-check

The closing bracket must be aligned with the opening tag (expected column 44)
<Stack>
<Box>
<Typography
level={"title-sm"}
color={popupNotification.level >= LOG_LEVEL.ERROR ?
"danger" :
"primary"}
>
{popupNotification.title}
</Typography>
<ModalClose
size={"sm"}
onClick={() => { setPopupNotification(null); }}/>
</Box>
<Typography level={"body-sm"}>
{popupNotification.message}
</Typography>
</Stack>
</Snackbar>}

Check failure on line 128 in new-log-viewer/src/contexts/NotificationContextProvider.tsx

View workflow job for this annotation

GitHub Actions / lint-check

Expected closing tag to match indentation of opening
</NotificationContext.Provider>
);
};

export {NotificationContext};
export default NotificationContextProvider;
7 changes: 3 additions & 4 deletions new-log-viewer/src/contexts/StateContextProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
clamp,
getChunkNum,
} from "../utils/math";
import {NotificationContext} from "./NotificationContextProvider";
import {
updateWindowUrlHashParams,
updateWindowUrlSearchParams,
Expand Down Expand Up @@ -139,6 +140,7 @@ const workerPostReq = <T extends WORKER_REQ_CODE>(
*/
// eslint-disable-next-line max-lines-per-function
const StateContextProvider = ({children}: StateContextProviderProps) => {

Check failure on line 142 in new-log-viewer/src/contexts/StateContextProvider.tsx

View workflow job for this annotation

GitHub Actions / lint-check

Arrow function has too many statements (21). Maximum allowed is 20
const {postStatus} = useContext(NotificationContext);
const {filePath, logEventNum} = useContext(UrlContext);

// States
Expand Down Expand Up @@ -172,10 +174,7 @@ const StateContextProvider = ({children}: StateContextProviderProps) => {
setNumEvents(args.numEvents);
break;
case WORKER_RESP_CODE.NOTIFICATION:
// eslint-disable-next-line no-warning-comments
// TODO: notifications should be shown in the UI when the NotificationProvider
// is added
console.error(args.logLevel, args.message);
postStatus(args.logLevel, args.message);
break;
case WORKER_RESP_CODE.PAGE_DATA: {
setLogData(args.logs);
Expand Down

0 comments on commit 61f4b6e

Please sign in to comment.