diff --git a/.gitignore b/.gitignore index a4d5706dd..22e24ea0a 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,5 @@ build_wrapper_output_directory/ # entity dump results dump_entities.csv dump_entities.xls* + +react-toastify/ \ No newline at end of file diff --git a/interface/package.json b/interface/package.json index 4d8f6f314..d19e380d4 100644 --- a/interface/package.json +++ b/interface/package.json @@ -38,12 +38,12 @@ "jwt-decode": "^3.1.2", "lodash-es": "^4.17.21", "mime-types": "^2.1.35", - "notistack": "2.0.8", "react": "latest", "react-dom": "latest", "react-dropzone": "^14.2.3", "react-icons": "^4.8.0", "react-router-dom": "^6.8.2", + "react-toastify": "^9.1.1", "sockette": "^2.0.6", "typesafe-i18n": "^5.24.2", "typescript": "^4.9.5" diff --git a/interface/src/App.tsx b/interface/src/App.tsx index 2556fd06c..553c5045d 100644 --- a/interface/src/App.tsx +++ b/interface/src/App.tsx @@ -1,8 +1,7 @@ -import { FC, createRef, useEffect, useState, RefObject } from 'react'; -import { SnackbarProvider } from 'notistack'; +import { FC, useEffect, useState } from 'react'; -import { IconButton } from '@mui/material'; -import CloseIcon from '@mui/icons-material/Close'; +import { ToastContainer } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.minimal.css'; import CustomTheme from 'CustomTheme'; import AppRouting from 'AppRouting'; @@ -15,12 +14,6 @@ import { loadLocaleAsync } from 'i18n/i18n-util.async'; const detectedLocale = detectLocale(localStorageDetector); const App: FC = () => { - const notistackRef: RefObject = createRef(); - - const onClickDismiss = (key: string | number | undefined) => () => { - notistackRef.current.closeSnackbar(key); - }; - const [wasLoaded, setWasLoaded] = useState(false); useEffect(() => { @@ -32,18 +25,19 @@ const App: FC = () => { return ( - ( - - - - )} - > - - + + ); diff --git a/interface/src/AppRouting.tsx b/interface/src/AppRouting.tsx index 472b6d9ee..0fbde8ea2 100644 --- a/interface/src/AppRouting.tsx +++ b/interface/src/AppRouting.tsx @@ -2,7 +2,7 @@ import { FC, useContext, useEffect } from 'react'; import { Route, Routes, Navigate, useLocation } from 'react-router-dom'; -import { useSnackbar, VariantType } from 'notistack'; +import { toast } from 'react-toastify'; import { useI18nContext } from 'i18n/i18n-react'; @@ -14,17 +14,17 @@ import AuthenticatedRouting from 'AuthenticatedRouting'; interface SecurityRedirectProps { message: string; - variant?: VariantType; + // variant?: VariantType; signOut?: boolean; } -const RootRedirect: FC = ({ message, variant, signOut }) => { +const RootRedirect: FC = ({ message, signOut }) => { const authenticationContext = useContext(AuthenticationContext); - const { enqueueSnackbar } = useSnackbar(); useEffect(() => { signOut && authenticationContext.signOut(false); - enqueueSnackbar(message, { variant }); - }, [message, variant, signOut, authenticationContext, enqueueSnackbar]); + // TODO toast variant + toast.error(message); + }, [message, signOut, authenticationContext]); return ; }; @@ -50,7 +50,7 @@ const AppRouting: FC = () => { } /> - } /> + } /> { const authenticationContext = useContext(AuthenticationContext); - const { enqueueSnackbar } = useSnackbar(); const [signInRequest, setSignInRequest] = useState({ username: '', @@ -61,10 +60,10 @@ const SignIn: FC = () => { } catch (error) { if (error.response) { if (error.response?.status === 401) { - enqueueSnackbar(LL.INVALID_LOGIN(), { variant: 'warning' }); + toast.warn(LL.INVALID_LOGIN()); } } else { - enqueueSnackbar(extractErrorMessage(error, LL.ERROR()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.ERROR())); } setProcessing(false); } diff --git a/interface/src/components/upload/useFileUpload.ts b/interface/src/components/upload/useFileUpload.ts index bf6d80a3f..afa41aaa7 100644 --- a/interface/src/components/upload/useFileUpload.ts +++ b/interface/src/components/upload/useFileUpload.ts @@ -1,6 +1,6 @@ import { useCallback, useEffect, useState } from 'react'; import axios, { AxiosPromise, CancelTokenSource, AxiosProgressEvent } from 'axios'; -import { useSnackbar } from 'notistack'; +import { toast } from 'react-toastify'; import { extractErrorMessage } from 'utils'; import { FileUploadConfig } from 'api/endpoints'; @@ -14,7 +14,6 @@ interface MediaUploadOptions { const useFileUpload = ({ upload }: MediaUploadOptions) => { const { LL } = useI18nContext(); - const { enqueueSnackbar } = useSnackbar(); const [uploading, setUploading] = useState(false); const [md5, setMd5] = useState(''); const [uploadProgress, setUploadProgress] = useState(); @@ -49,17 +48,17 @@ const useFileUpload = ({ upload }: MediaUploadOptions) => { }); resetUploadingStates(); if (response.status === 200) { - enqueueSnackbar(LL.UPLOAD() + ' ' + LL.SUCCESSFUL(), { variant: 'success' }); + toast.success(LL.UPLOAD() + ' ' + LL.SUCCESSFUL()); } else if (response.status === 201) { setMd5(String(response.data)); - enqueueSnackbar(LL.UPLOAD() + ' MD5 ' + LL.SUCCESSFUL(), { variant: 'success' }); + toast.success(LL.UPLOAD() + ' MD5 ' + LL.SUCCESSFUL()); } } catch (error) { if (axios.isCancel(error)) { - enqueueSnackbar(LL.UPLOAD() + ' ' + LL.ABORTED(), { variant: 'warning' }); + toast.warning(LL.UPLOAD() + ' ' + LL.ABORTED()); } else { resetUploadingStates(); - enqueueSnackbar(extractErrorMessage(error, LL.UPLOAD() + ' ' + LL.FAILED()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.UPLOAD() + ' ' + LL.FAILED())); } } }; diff --git a/interface/src/contexts/authentication/Authentication.tsx b/interface/src/contexts/authentication/Authentication.tsx index 426e81312..d997356e4 100644 --- a/interface/src/contexts/authentication/Authentication.tsx +++ b/interface/src/contexts/authentication/Authentication.tsx @@ -1,5 +1,5 @@ import { FC, useCallback, useEffect, useState } from 'react'; -import { useSnackbar } from 'notistack'; +import { toast } from 'react-toastify'; import { useNavigate } from 'react-router-dom'; import { useI18nContext } from 'i18n/i18n-react'; @@ -15,7 +15,6 @@ const Authentication: FC = ({ children }) => { const { LL } = useI18nContext(); const navigate = useNavigate(); - const { enqueueSnackbar } = useSnackbar(); const [initialized, setInitialized] = useState(false); const [me, setMe] = useState(); @@ -25,7 +24,7 @@ const Authentication: FC = ({ children }) => { AuthenticationApi.getStorage().setItem(ACCESS_TOKEN, accessToken); const decodedMe = AuthenticationApi.decodeMeJWT(accessToken); setMe(decodedMe); - enqueueSnackbar(LL.LOGGED_IN({ name: decodedMe.username }), { variant: 'success' }); + toast.success(LL.LOGGED_IN({ name: decodedMe.username })); } catch (error) { setMe(undefined); throw new Error('Failed to parse JWT'); diff --git a/interface/src/framework/network/NetworkSettingsForm.tsx b/interface/src/framework/network/NetworkSettingsForm.tsx index 59ab5afaf..ad980f34a 100644 --- a/interface/src/framework/network/NetworkSettingsForm.tsx +++ b/interface/src/framework/network/NetworkSettingsForm.tsx @@ -1,5 +1,5 @@ import { FC, useContext, useEffect, useState } from 'react'; -import { useSnackbar } from 'notistack'; +import { toast } from 'react-toastify'; import { Avatar, @@ -48,7 +48,6 @@ import RestartMonitor from '../system/RestartMonitor'; const WiFiSettingsForm: FC = () => { const { LL } = useI18nContext(); - const { enqueueSnackbar } = useSnackbar(); const { selectedNetwork, deselectNetwork } = useContext(WiFiConnectionContext); @@ -118,7 +117,7 @@ const WiFiSettingsForm: FC = () => { await EMSESP.restart(); setRestarting(true); } catch (error) { - enqueueSnackbar(LL.PROBLEM_UPDATING(), { variant: 'error' }); + toast.error(LL.PROBLEM_UPDATING()); } }; diff --git a/interface/src/framework/network/WiFiNetworkScanner.tsx b/interface/src/framework/network/WiFiNetworkScanner.tsx index bfbb12c9e..61bb39aed 100644 --- a/interface/src/framework/network/WiFiNetworkScanner.tsx +++ b/interface/src/framework/network/WiFiNetworkScanner.tsx @@ -1,5 +1,5 @@ import { useEffect, FC, useState, useCallback, useRef } from 'react'; -import { useSnackbar } from 'notistack'; +import { toast } from 'react-toastify'; import { Button } from '@mui/material'; import PermScanWifiIcon from '@mui/icons-material/PermScanWifi'; @@ -24,20 +24,15 @@ const compareNetworks = (network1: WiFiNetwork, network2: WiFiNetwork) => { const WiFiNetworkScanner: FC = () => { const { LL } = useI18nContext(); - const { enqueueSnackbar } = useSnackbar(); - const pollCount = useRef(0); const [networkList, setNetworkList] = useState(); const [errorMessage, setErrorMessage] = useState(); - const finishedWithError = useCallback( - (message: string) => { - enqueueSnackbar(message, { variant: 'error' }); - setNetworkList(undefined); - setErrorMessage(message); - }, - [enqueueSnackbar] - ); + const finishedWithError = useCallback((message: string) => { + toast.error(message); + setNetworkList(undefined); + setErrorMessage(message); + }, []); const pollNetworkList = useCallback(async () => { try { diff --git a/interface/src/framework/ntp/NTPStatusForm.tsx b/interface/src/framework/ntp/NTPStatusForm.tsx index 54793125f..9f2c42b0a 100644 --- a/interface/src/framework/ntp/NTPStatusForm.tsx +++ b/interface/src/framework/ntp/NTPStatusForm.tsx @@ -1,5 +1,5 @@ import { FC, useContext, useState } from 'react'; -import { useSnackbar } from 'notistack'; +import { toast } from 'react-toastify'; import { Avatar, @@ -55,7 +55,6 @@ const NTPStatusForm: FC = () => { const [localTime, setLocalTime] = useState(''); const [settingTime, setSettingTime] = useState(false); const [processing, setProcessing] = useState(false); - const { enqueueSnackbar } = useSnackbar(); const { me } = useContext(AuthenticatedContext); const { LL } = useI18nContext(); @@ -88,11 +87,11 @@ const NTPStatusForm: FC = () => { await NTPApi.updateTime({ local_time: formatLocalDateTime(new Date(localTime)) }); - enqueueSnackbar(LL.TIME_SET(), { variant: 'success' }); + toast.success(LL.TIME_SET()); setSettingTime(false); loadData(); } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_UPDATING())); } finally { setProcessing(false); } diff --git a/interface/src/framework/security/GenerateToken.tsx b/interface/src/framework/security/GenerateToken.tsx index ccbc0e5e2..2b0c0e9fd 100644 --- a/interface/src/framework/security/GenerateToken.tsx +++ b/interface/src/framework/security/GenerateToken.tsx @@ -14,7 +14,7 @@ import { import CloseIcon from '@mui/icons-material/Close'; import { extractErrorMessage } from 'utils'; -import { useSnackbar } from 'notistack'; +import { toast } from 'react-toastify'; import { MessageBox } from 'components'; import * as SecurityApi from 'api/security'; import { Token } from 'types'; @@ -32,15 +32,13 @@ const GenerateToken: FC = ({ username, onClose }) => { const { LL } = useI18nContext(); - const { enqueueSnackbar } = useSnackbar(); - const getToken = useCallback(async () => { try { setToken((await SecurityApi.generateToken(username)).data); } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_UPDATING())); } - }, [username, enqueueSnackbar, LL]); + }, [username, LL]); useEffect(() => { if (open) { diff --git a/interface/src/framework/system/GeneralFileUpload.tsx b/interface/src/framework/system/GeneralFileUpload.tsx index 14e284c8c..51c8a48b2 100644 --- a/interface/src/framework/system/GeneralFileUpload.tsx +++ b/interface/src/framework/system/GeneralFileUpload.tsx @@ -9,7 +9,7 @@ import { SingleUpload, useFileUpload } from 'components'; import DownloadIcon from '@mui/icons-material/GetApp'; -import { useSnackbar } from 'notistack'; +import { toast } from 'react-toastify'; import { extractErrorMessage } from 'utils'; @@ -24,8 +24,6 @@ interface UploadFileProps { const GeneralFileUpload: FC = ({ uploadGeneralFile }) => { const [uploadFile, cancelUpload, uploading, uploadProgress, md5] = useFileUpload({ upload: uploadGeneralFile }); - const { enqueueSnackbar } = useSnackbar(); - const { LL } = useI18nContext(); const saveFile = (json: any, endpoint: string) => { @@ -40,19 +38,18 @@ const GeneralFileUpload: FC = ({ uploadGeneralFile }) => { document.body.appendChild(a); a.click(); document.body.removeChild(a); - enqueueSnackbar(LL.DOWNLOAD_SUCCESSFUL(), { variant: 'info' }); + toast.info(LL.DOWNLOAD_SUCCESSFUL()); }; const downloadSettings = async () => { try { const response = await EMSESP.getSettings(); if (response.status !== 200) { - enqueueSnackbar(LL.PROBLEM_LOADING(), { variant: 'error' }); - } else { + toast.error(LL.PROBLEM_LOADING()); saveFile(response.data, 'settings'); } } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_LOADING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_LOADING())); } }; @@ -60,12 +57,12 @@ const GeneralFileUpload: FC = ({ uploadGeneralFile }) => { try { const response = await EMSESP.getCustomizations(); if (response.status !== 200) { - enqueueSnackbar(LL.PROBLEM_LOADING(), { variant: 'error' }); + toast.error(LL.PROBLEM_LOADING()); } else { saveFile(response.data, 'customizations'); } } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_LOADING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_LOADING())); } }; @@ -73,12 +70,11 @@ const GeneralFileUpload: FC = ({ uploadGeneralFile }) => { try { const response = await EMSESP.readSchedule(); if (response.status !== 200) { - enqueueSnackbar(LL.PROBLEM_LOADING(), { variant: 'error' }); - } else { + toast.error(LL.PROBLEM_LOADING()); saveFile(response.data, 'schedule'); } } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_LOADING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_LOADING())); } }; diff --git a/interface/src/framework/system/SystemLog.tsx b/interface/src/framework/system/SystemLog.tsx index 5b2aad646..b79b02248 100644 --- a/interface/src/framework/system/SystemLog.tsx +++ b/interface/src/framework/system/SystemLog.tsx @@ -12,7 +12,7 @@ import { updateValue, useRest, extractErrorMessage } from 'utils'; import DownloadIcon from '@mui/icons-material/GetApp'; -import { useSnackbar } from 'notistack'; +import { toast } from 'react-toastify'; import { EVENT_SOURCE_ROOT } from 'api/endpoints'; @@ -93,8 +93,6 @@ const SystemLog: FC = () => { const updateFormValue = updateValue(setData); - const { enqueueSnackbar } = useSnackbar(); - const reloadPage = () => { window.location.reload(); }; @@ -108,10 +106,10 @@ const SystemLog: FC = () => { compact: data.compact }); if (response.status !== 200) { - enqueueSnackbar(LL.PROBLEM_UPDATING(), { variant: 'error' }); + toast.error(LL.PROBLEM_UPDATING()); } } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_UPDATING())); } } }; diff --git a/interface/src/framework/system/SystemStatusForm.tsx b/interface/src/framework/system/SystemStatusForm.tsx index 453db573a..f0d078da9 100644 --- a/interface/src/framework/system/SystemStatusForm.tsx +++ b/interface/src/framework/system/SystemStatusForm.tsx @@ -1,5 +1,5 @@ import { FC, useContext, useState, useEffect } from 'react'; -import { useSnackbar } from 'notistack'; +import { toast } from 'react-toastify'; import { Avatar, Box, @@ -61,7 +61,6 @@ const SystemStatusForm: FC = () => { const [confirmRestart, setConfirmRestart] = useState(false); const [confirmFactoryReset, setConfirmFactoryReset] = useState(false); const [processing, setProcessing] = useState(false); - const { enqueueSnackbar } = useSnackbar(); const [showingVersion, setShowingVersion] = useState(false); const [latestVersion, setLatestVersion] = useState(); const [latestDevVersion, setLatestDevVersion] = useState(); @@ -91,7 +90,7 @@ const SystemStatusForm: FC = () => { setRestarting(true); } } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_LOADING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_LOADING())); } finally { setConfirmRestart(false); setProcessing(false); @@ -104,7 +103,7 @@ const SystemStatusForm: FC = () => { await SystemApi.partition(); setRestarting(true); } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_LOADING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_LOADING())); } finally { setConfirmRestart(false); setProcessing(false); @@ -211,7 +210,7 @@ const SystemStatusForm: FC = () => { await SystemApi.factoryReset(); setRestarting(true); } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_UPDATING())); } finally { setConfirmFactoryReset(false); setProcessing(false); diff --git a/interface/src/project/DashboardData.tsx b/interface/src/project/DashboardData.tsx index c20887e62..b7bfdd84b 100644 --- a/interface/src/project/DashboardData.tsx +++ b/interface/src/project/DashboardData.tsx @@ -20,7 +20,7 @@ import { Checkbox } from '@mui/material'; -import { useSnackbar } from 'notistack'; +import { toast } from 'react-toastify'; import { useTheme } from '@table-library/react-table-library/theme'; import { useSort, SortToggleType } from '@table-library/react-table-library/sort'; @@ -78,8 +78,6 @@ const DashboardData: FC = () => { const { LL } = useI18nContext(); - const { enqueueSnackbar } = useSnackbar(); - const [coreData, setCoreData] = useState({ connected: true, devices: [], @@ -363,9 +361,9 @@ const DashboardData: FC = () => { try { setCoreData((await EMSESP.readCoreData()).data); } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_LOADING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_LOADING())); } - }, [enqueueSnackbar, LL]); + }, [LL]); useEffect(() => { fetchCoreData(); @@ -384,7 +382,7 @@ const DashboardData: FC = () => { try { setDeviceData((await EMSESP.readDeviceData({ id: unique_id })).data); } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_LOADING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_LOADING())); } }; @@ -392,7 +390,7 @@ const DashboardData: FC = () => { try { setSensorData((await EMSESP.readSensorData()).data); } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_LOADING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_LOADING())); } }; @@ -468,15 +466,15 @@ const DashboardData: FC = () => { devicevalue: deviceValue }); if (response.status === 204) { - enqueueSnackbar(LL.WRITE_CMD_FAILED(), { variant: 'error' }); + toast.error(LL.WRITE_CMD_FAILED()); } else if (response.status === 403) { - enqueueSnackbar(LL.ACCESS_DENIED(), { variant: 'error' }); + toast.error(LL.ACCESS_DENIED()); } else { - enqueueSnackbar(LL.WRITE_CMD_SENT(), { variant: 'success' }); + toast.success(LL.WRITE_CMD_SENT()); } setDeviceValue(undefined); } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_UPDATING())); } finally { refreshData(); setDeviceValue(undefined); @@ -562,15 +560,15 @@ const DashboardData: FC = () => { offset: sensor.o }); if (response.status === 204) { - enqueueSnackbar(LL.UPLOAD_OF(LL.SENSOR()) + ' ' + LL.FAILED(), { variant: 'error' }); + toast.error(LL.UPLOAD_OF(LL.SENSOR()) + ' ' + LL.FAILED()); } else if (response.status === 403) { - enqueueSnackbar(LL.ACCESS_DENIED(), { variant: 'error' }); + toast.error(LL.ACCESS_DENIED()); } else { - enqueueSnackbar(LL.UPDATED_OF(LL.SENSOR()), { variant: 'success' }); + toast.success(LL.UPDATED_OF(LL.SENSOR())); } setSensor(undefined); } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_UPDATING())); } finally { setSensor(undefined); fetchSensorData(); @@ -991,14 +989,14 @@ const DashboardData: FC = () => { }); if (response.status === 204) { - enqueueSnackbar(LL.DELETION_OF(LL.ANALOG_SENSOR()) + ' ' + LL.FAILED(), { variant: 'error' }); + toast.error(LL.DELETION_OF(LL.ANALOG_SENSOR()) + ' ' + LL.FAILED()); } else if (response.status === 403) { - enqueueSnackbar(LL.ACCESS_DENIED(), { variant: 'error' }); + toast.error(LL.ACCESS_DENIED()); } else { - enqueueSnackbar(LL.REMOVED_OF(LL.ANALOG_SENSOR()), { variant: 'success' }); + toast.success(LL.REMOVED_OF(LL.ANALOG_SENSOR())); } } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_UPDATING())); } finally { setAnalog(undefined); fetchSensorData(); @@ -1019,14 +1017,14 @@ const DashboardData: FC = () => { }); if (response.status === 204) { - enqueueSnackbar(LL.UPDATE_OF(LL.ANALOG_SENSOR()) + ' ' + LL.FAILED(), { variant: 'error' }); + toast.error(LL.UPDATE_OF(LL.ANALOG_SENSOR()) + ' ' + LL.FAILED()); } else if (response.status === 403) { - enqueueSnackbar(LL.ACCESS_DENIED(), { variant: 'error' }); + toast.error(LL.ACCESS_DENIED()); } else { - enqueueSnackbar(LL.UPDATED_OF(LL.ANALOG_SENSOR()), { variant: 'success' }); + toast.success(LL.UPDATED_OF(LL.ANALOG_SENSOR())); } } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_UPDATING())); } finally { setAnalog(undefined); fetchSensorData(); diff --git a/interface/src/project/DashboardStatus.tsx b/interface/src/project/DashboardStatus.tsx index 87b22537b..30c053e7d 100644 --- a/interface/src/project/DashboardStatus.tsx +++ b/interface/src/project/DashboardStatus.tsx @@ -1,5 +1,5 @@ import { FC, useState, useContext, useEffect } from 'react'; -import { useSnackbar } from 'notistack'; +import { toast } from 'react-toastify'; import { Avatar, Button, @@ -74,7 +74,6 @@ const DashboardStatus: FC = () => { const theme = useTheme(); const [confirmScan, setConfirmScan] = useState(false); - const { enqueueSnackbar } = useSnackbar(); const { me } = useContext(AuthenticatedContext); @@ -146,9 +145,9 @@ const DashboardStatus: FC = () => { const scan = async () => { try { await EMSESP.scanDevices(); - enqueueSnackbar(LL.SCANNING() + '...', { variant: 'info' }); + toast.info(LL.SCANNING() + '...'); } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_UPDATING())); } finally { setConfirmScan(false); } diff --git a/interface/src/project/HelpInformation.tsx b/interface/src/project/HelpInformation.tsx index e9bfd97fd..4b0c6288b 100644 --- a/interface/src/project/HelpInformation.tsx +++ b/interface/src/project/HelpInformation.tsx @@ -4,7 +4,7 @@ import { Typography, Button, Box, List, ListItem, ListItemText, Link, ListItemAv import { SectionContent } from 'components'; -import { useSnackbar } from 'notistack'; +import { toast } from 'react-toastify'; import CommentIcon from '@mui/icons-material/CommentTwoTone'; import MenuBookIcon from '@mui/icons-material/MenuBookTwoTone'; @@ -21,8 +21,6 @@ import * as EMSESP from './api'; const HelpInformation: FC = () => { const { LL } = useI18nContext(); - const { enqueueSnackbar } = useSnackbar(); - const saveFile = (json: any, endpoint: string) => { const a = document.createElement('a'); const filename = 'emsesp_' + endpoint + '.txt'; @@ -35,7 +33,7 @@ const HelpInformation: FC = () => { document.body.appendChild(a); a.click(); document.body.removeChild(a); - enqueueSnackbar(LL.DOWNLOAD_SUCCESSFUL(), { variant: 'info' }); + toast.info(LL.DOWNLOAD_SUCCESSFUL()); }; const callAPI = async (endpoint: string) => { @@ -46,12 +44,12 @@ const HelpInformation: FC = () => { id: 0 }); if (response.status !== 200) { - enqueueSnackbar(LL.PROBLEM_LOADING(), { variant: 'error' }); + toast.error(LL.PROBLEM_LOADING()); } else { saveFile(response.data, endpoint); } } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_LOADING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_LOADING())); } }; diff --git a/interface/src/project/SettingsApplication.tsx b/interface/src/project/SettingsApplication.tsx index 40063dc1b..3f24ff9c5 100644 --- a/interface/src/project/SettingsApplication.tsx +++ b/interface/src/project/SettingsApplication.tsx @@ -1,7 +1,7 @@ import { FC, useState } from 'react'; import { ValidateFieldsError } from 'async-validator'; -import { useSnackbar } from 'notistack'; +import { toast } from 'react-toastify'; import { Box, Button, Checkbox, MenuItem, Grid, Typography, Divider, InputAdornment } from '@mui/material'; @@ -58,8 +58,6 @@ const SettingsApplication: FC = () => { const { LL } = useI18nContext(); - const { enqueueSnackbar } = useSnackbar(); - const updateFormValue = updateValueDirty(origData, dirtyFlags, setDirtyFlags, setData); const [fieldErrors, setFieldErrors] = useState(); @@ -85,7 +83,7 @@ const SettingsApplication: FC = () => { }); } } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_UPDATING())); } finally { setProcessingBoard(false); } @@ -124,7 +122,7 @@ const SettingsApplication: FC = () => { await EMSESP.restart(); setRestarting(true); } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_UPDATING())); } }; diff --git a/interface/src/project/SettingsCustomization.tsx b/interface/src/project/SettingsCustomization.tsx index 796e46a6a..ff05ac1b4 100644 --- a/interface/src/project/SettingsCustomization.tsx +++ b/interface/src/project/SettingsCustomization.tsx @@ -22,7 +22,7 @@ import { import { useTheme } from '@table-library/react-table-library/theme'; import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table'; -import { useSnackbar } from 'notistack'; +import { toast } from 'react-toastify'; import WarningIcon from '@mui/icons-material/Warning'; import CancelIcon from '@mui/icons-material/Cancel'; @@ -51,7 +51,6 @@ export const APIURL = window.location.origin + '/api/'; const SettingsCustomization: FC = () => { const { LL } = useI18nContext(); - const { enqueueSnackbar } = useSnackbar(); const emptyDeviceEntity = { id: '', v: 0, n: '', cn: '', m: 0, w: false }; @@ -257,9 +256,9 @@ const SettingsCustomization: FC = () => { const resetCustomization = async () => { try { await EMSESP.resetCustomizations(); - enqueueSnackbar(LL.CUSTOMIZATIONS_RESTART(), { variant: 'info' }); + toast.info(LL.CUSTOMIZATIONS_RESTART()); } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_UPDATING())); } finally { setConfirmReset(false); } @@ -296,7 +295,7 @@ const SettingsCustomization: FC = () => { await EMSESP.restart(); setRestarting(true); } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_UPDATING())); } }; @@ -307,7 +306,7 @@ const SettingsCustomization: FC = () => { // check size in bytes to match buffer in CPP, which is 2048 const bytes = new TextEncoder().encode(JSON.stringify(masked_entities)).length; if (bytes > 2000) { - enqueueSnackbar(LL.CUSTOMIZATIONS_FULL(), { variant: 'warning' }); + toast.warning(LL.CUSTOMIZATIONS_FULL()); return; } @@ -317,14 +316,14 @@ const SettingsCustomization: FC = () => { entity_ids: masked_entities }); if (response.status === 200) { - enqueueSnackbar(LL.CUSTOMIZATIONS_SAVED(), { variant: 'success' }); + toast.success(LL.CUSTOMIZATIONS_SAVED()); } else if (response.status === 201) { setRestartNeeded(true); } else { - enqueueSnackbar(LL.PROBLEM_UPDATING(), { variant: 'error' }); + toast.error(LL.PROBLEM_UPDATING()); } } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_UPDATING())); } setOriginalSettings(deviceEntities); } diff --git a/interface/src/project/SettingsScheduler.tsx b/interface/src/project/SettingsScheduler.tsx index bf2bff71a..c37f34ced 100644 --- a/interface/src/project/SettingsScheduler.tsx +++ b/interface/src/project/SettingsScheduler.tsx @@ -21,7 +21,7 @@ import { import { useTheme } from '@table-library/react-table-library/theme'; import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table'; -import { useSnackbar } from 'notistack'; +import { toast } from 'react-toastify'; import RemoveIcon from '@mui/icons-material/RemoveCircleOutline'; import WarningIcon from '@mui/icons-material/Warning'; @@ -66,8 +66,6 @@ function makeid() { const SettingsScheduler: FC = () => { const { LL, locale } = useI18nContext(); - const { enqueueSnackbar } = useSnackbar(); - const [numChanges, setNumChanges] = useState(0); const blocker = useBlocker(numChanges !== 0); @@ -263,12 +261,12 @@ const SettingsScheduler: FC = () => { }) }); if (response.status === 200) { - enqueueSnackbar(LL.SCHEDULE_SAVED(), { variant: 'success' }); + toast.success(LL.SCHEDULE_SAVED()); } else { - enqueueSnackbar(LL.PROBLEM_UPDATING(), { variant: 'error' }); + toast.error(LL.PROBLEM_UPDATING()); } } catch (error) { - enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' }); + toast.error(extractErrorMessage(error, LL.PROBLEM_UPDATING())); } setOriginalSchedule(schedule); } diff --git a/interface/src/utils/useRest.ts b/interface/src/utils/useRest.ts index ed86b7142..f57d20ec0 100644 --- a/interface/src/utils/useRest.ts +++ b/interface/src/utils/useRest.ts @@ -1,5 +1,6 @@ import { useCallback, useEffect, useState } from 'react'; -import { useSnackbar } from 'notistack'; +import { toast } from 'react-toastify'; + import { AxiosPromise } from 'axios'; import { extractErrorMessage } from '.'; @@ -16,8 +17,6 @@ export interface RestRequestOptions { export const useRest = ({ read, update }: RestRequestOptions) => { const { LL } = useI18nContext(); - const { enqueueSnackbar } = useSnackbar(); - const [data, setData] = useState(); const [saving, setSaving] = useState(false); const [errorMessage, setErrorMessage] = useState(); @@ -38,10 +37,10 @@ export const useRest = ({ read, update }: RestRequestOptions) => { setOrigData(fetch_data); } catch (error) { const message = extractErrorMessage(error, LL.PROBLEM_LOADING()); - enqueueSnackbar(message, { variant: 'error' }); + toast.error(message); setErrorMessage(message); } - }, [read, enqueueSnackbar, LL]); + }, [read, LL]); const save = useCallback( async (toSave: D) => { @@ -58,18 +57,18 @@ export const useRest = ({ read, update }: RestRequestOptions) => { if (response.status === 202) { setRestartNeeded(true); } else { - enqueueSnackbar(LL.UPDATED_OF(LL.SETTINGS_OF('')), { variant: 'success' }); + toast.success(LL.UPDATED_OF(LL.SETTINGS_OF(''))); } } catch (error) { const message = extractErrorMessage(error, LL.PROBLEM_UPDATING()); - enqueueSnackbar(message, { variant: 'error' }); + toast.error(message); setErrorMessage(message); } finally { setSaving(false); setDirtyFlags([]); } }, - [update, enqueueSnackbar, LL] + [update, LL] ); const saveData = () => data && save(data); diff --git a/interface/yarn.lock b/interface/yarn.lock index cb1f4362e..77b40d8c5 100644 --- a/interface/yarn.lock +++ b/interface/yarn.lock @@ -1812,7 +1812,6 @@ __metadata: lodash-es: ^4.17.21 mime-types: ^2.1.35 nodemon: ^2.0.21 - notistack: 2.0.8 npm-run-all: ^4.1.5 prettier: ^2.8.4 react: latest @@ -1820,6 +1819,7 @@ __metadata: react-dropzone: ^14.2.3 react-icons: ^4.8.0 react-router-dom: ^6.8.2 + react-toastify: ^9.1.1 rollup-plugin-visualizer: ^5.9.0 sockette: ^2.0.6 typesafe-i18n: ^5.24.2 @@ -2355,7 +2355,7 @@ __metadata: languageName: node linkType: hard -"clsx@npm:^1.1.0, clsx@npm:^1.2.1": +"clsx@npm:^1.1.1, clsx@npm:^1.2.1": version: 1.2.1 resolution: "clsx@npm:1.2.1" checksum: 30befca8019b2eb7dbad38cff6266cf543091dae2825c856a62a8ccf2c3ab9c2907c4d12b288b73101196767f66812365400a227581484a05f968b0307cfaf12 @@ -4700,27 +4700,6 @@ __metadata: languageName: node linkType: hard -"notistack@npm:2.0.8": - version: 2.0.8 - resolution: "notistack@npm:2.0.8" - dependencies: - clsx: ^1.1.0 - hoist-non-react-statics: ^3.3.0 - peerDependencies: - "@emotion/react": ^11.4.1 - "@emotion/styled": ^11.3.0 - "@mui/material": ^5.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - "@emotion/react": - optional: true - "@emotion/styled": - optional: true - checksum: f95952ea7209840f6f05a65e74f0fb4e7a1bb321753b2955ae6052f2205d47a6a3c50a42b72db11eac2fc491c4105b4687bed89e79287399530973fa133a5380 - languageName: node - linkType: hard - "npm-run-all@npm:^4.1.5": version: 4.1.5 resolution: "npm-run-all@npm:4.1.5" @@ -5208,6 +5187,18 @@ __metadata: languageName: node linkType: hard +"react-toastify@npm:^9.1.1": + version: 9.1.1 + resolution: "react-toastify@npm:9.1.1" + dependencies: + clsx: ^1.1.1 + peerDependencies: + react: ">=16" + react-dom: ">=16" + checksum: 2039255539961a9b4d77b2656f120b20abe46cb0c699a7f3c0af23b4ef669d9c4d24dae6b8f4954b5efd83edf6d6e23614a29e94e9ee0d2647741fba9ba2db85 + languageName: node + linkType: hard + "react-transition-group@npm:^4.4.5": version: 4.4.5 resolution: "react-transition-group@npm:4.4.5"