diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index e7a8cae..6b571bd 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,12 +1,5 @@ import { useEffect, useReducer, useRef, useState } from 'react'; -import { - Navbar, - Form, - Offcanvas, - Container, - Toast, - ToastContainer -} from 'react-bootstrap'; +import { Navbar, Form, Offcanvas, Container } from 'react-bootstrap'; import { hostname, port, socket } from './socket'; import { DataServiceComponent, @@ -14,6 +7,7 @@ import { } from './components/DataServiceComponent'; import './App.css'; import { getDataServiceJSONValueByPathAndKey } from './utils/nestedObjectUtils'; +import { Notifications } from './components/NotificationsComponent'; type ValueType = boolean | string | number | object; @@ -21,10 +15,10 @@ type State = DataServiceJSON | null; type Action = | { type: 'SET_DATA'; data: DataServiceJSON } | { type: 'UPDATE_ATTRIBUTE'; parent_path: string; name: string; value: ValueType }; -type UpdateNotification = { +type UpdateMessage = { data: { parent_path: string; name: string; value: object }; }; -type ExceptionNotification = { +type ExceptionMessage = { data: { exception: string; type: string }; }; @@ -132,10 +126,14 @@ const App = () => { ); }; + const removeExceptionById = (id: number) => { + setExceptions((prevNotifications) => prevNotifications.filter((n) => n.id !== id)); + }; + const handleCloseSettings = () => setShowSettings(false); const handleShowSettings = () => setShowSettings(true); - function onNotify(value: UpdateNotification) { + function onNotify(value: UpdateMessage) { // Extracting data from the notification const { parent_path, name, value: newValue } = value.data; @@ -170,12 +168,11 @@ const App = () => { setNotifications((prevNotifications) => [newNotification, ...prevNotifications]); } - function onException(value: ExceptionNotification) { + function onException(value: ExceptionMessage) { const currentTime = new Date(); const timeString = currentTime.toISOString().substr(11, 8); const newNotification = { - type: 'exception', id: Math.random(), time: timeString, text: `${value.data.type}: ${value.data.exception}.` @@ -216,59 +213,13 @@ const App = () => { - - {showNotification && - notifications.map((notification) => ( - { - removeNotificationById(notification.id); - }} - onClick={() => { - removeNotificationById(notification.id); - }} - onMouseLeave={() => { - // For exception type notifications, do not dismiss on mouse leave - if (notification.type !== 'exception') { - removeNotificationById(notification.id); - } - }} - show={true} - autohide={true} - delay={2000}> - - Notification - {notification.time} - - {notification.text} - - ))} - {exceptions.map((exception) => ( - // Always render exceptions, regardless of showNotification - { - setExceptions((prevExceptions) => - prevExceptions.filter((e) => e.id !== exception.id) - ); - }} - show={true} - autohide={false}> - - Exception - {exception.time} - - {exception.text} - - ))} - + void; + removeExceptionById: (id: number) => void; +}; + +export const Notifications = React.memo((props: NotificationProps) => { + const { + showNotification, + notifications, + exceptions, + removeExceptionById, + removeNotificationById + } = props; + + return ( + + {showNotification && + notifications.map((notification) => ( + removeNotificationById(notification.id)} + onClick={() => { + removeNotificationById(notification.id); + }} + onMouseLeave={() => { + removeNotificationById(notification.id); + }} + show={true} + autohide={true} + delay={2000}> + + Notification + {notification.time} + + {notification.text} + + ))} + {exceptions.map((exception) => ( + removeExceptionById(exception.id)} + onClick={() => { + removeExceptionById(exception.id); + }} + show={true} + autohide={false}> + + Exception + {exception.time} + + {exception.text} + + ))} + + ); +}); diff --git a/src/pydase/frontend/asset-manifest.json b/src/pydase/frontend/asset-manifest.json index 283d964..a7e2d7b 100644 --- a/src/pydase/frontend/asset-manifest.json +++ b/src/pydase/frontend/asset-manifest.json @@ -1,13 +1,13 @@ { "files": { "main.css": "/static/css/main.d5ec2545.css", - "main.js": "/static/js/main.0683ca07.js", + "main.js": "/static/js/main.0eb7a5df.js", "index.html": "/index.html", "main.d5ec2545.css.map": "/static/css/main.d5ec2545.css.map", - "main.0683ca07.js.map": "/static/js/main.0683ca07.js.map" + "main.0eb7a5df.js.map": "/static/js/main.0eb7a5df.js.map" }, "entrypoints": [ "static/css/main.d5ec2545.css", - "static/js/main.0683ca07.js" + "static/js/main.0eb7a5df.js" ] } \ No newline at end of file diff --git a/src/pydase/frontend/index.html b/src/pydase/frontend/index.html index 6bd048b..b9f8297 100644 --- a/src/pydase/frontend/index.html +++ b/src/pydase/frontend/index.html @@ -1 +1 @@ -pydase App
\ No newline at end of file +pydase App
\ No newline at end of file diff --git a/src/pydase/frontend/static/js/main.0683ca07.js b/src/pydase/frontend/static/js/main.0683ca07.js deleted file mode 100644 index 874f9ac..0000000 --- a/src/pydase/frontend/static/js/main.0683ca07.js +++ /dev/null @@ -1,3 +0,0 @@ -/*! For license information please see main.0683ca07.js.LICENSE.txt */ -!function(){var e={694:function(e,t){var n;!function(){"use strict";var r={}.hasOwnProperty;function a(){for(var e=[],t=0;t