diff --git a/package-lock.json b/package-lock.json index 4fe9d9799..ce3a8b68a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -119,6 +119,7 @@ "react-error-overlay": "^6.1.0", "react-final-form": "^6.5.9", "react-flexible-workbench": "^5.6.1", + "react-hot-toast": "^2.6.0", "react-hotkeys": "^2.0.0", "react-i18next": "^12.2.0", "react-is": "^18.3.1", @@ -16015,6 +16016,38 @@ } } }, + "node_modules/react-hot-toast": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.6.0.tgz", + "integrity": "sha512-bH+2EBMZ4sdyou/DPrfgIouFpcRLCJ+HoCA32UoAYHn6T3Ur5yfcDCeSr5mwldl6pFOsiocmrXMuoCJ1vV8bWg==", + "license": "MIT", + "dependencies": { + "csstype": "^3.1.3", + "goober": "^2.1.16" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/react-hot-toast/node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/react-hot-toast/node_modules/goober": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.16.tgz", + "integrity": "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==", + "license": "MIT", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, "node_modules/react-hotkeys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/react-hotkeys/-/react-hotkeys-2.0.0.tgz", diff --git a/package.json b/package.json index d0599a0bd..20c39d5eb 100644 --- a/package.json +++ b/package.json @@ -133,6 +133,7 @@ "react-error-overlay": "^6.1.0", "react-final-form": "^6.5.9", "react-flexible-workbench": "^5.6.1", + "react-hot-toast": "^2.6.0", "react-hotkeys": "^2.0.0", "react-i18next": "^12.2.0", "react-is": "^18.3.1", diff --git a/src/app.jsx b/src/app.jsx index 9b759c838..b0f8a065a 100644 --- a/src/app.jsx +++ b/src/app.jsx @@ -5,7 +5,8 @@ import React from 'react'; import { ErrorBoundary } from 'react-error-boundary'; import { WorkbenchView } from 'react-flexible-workbench'; import { connect, Provider as StoreProvider } from 'react-redux'; -import { ToastProvider } from 'react-toast-notifications'; +// import { ToastProvider } from 'react-toast-notifications'; +import { Toaster } from 'react-hot-toast'; import { PersistGate } from 'redux-persist/es/integration/react'; import loadable from '@loadable/component'; @@ -35,12 +36,11 @@ import SafetyDialog from './features/safety/SafetyDialog'; import SavedLocationEditorDialog from './features/saved-locations/SavedLocationEditorDialog'; import ShowConfiguratorDialog from './features/show-configurator/ShowConfiguratorDialog'; import Sidebar from './features/sidebar/Sidebar'; -import ToastNotificationManager from './features/snackbar/ToastNotificationManager'; +// import ToastNotificationManager from './features/snackbar/ToastNotificationManager'; import UAVDetailsDialog from './features/uavs/UAVDetailsDialog'; import UploadDialog from './features/upload/UploadDialog'; import VersionCheckDialog from './features/version-check/VersionCheckDialog'; -import { SNACKBAR_TRANSITION_DURATION } from './features/snackbar/constants'; import { isWorkbenchLayoutFixed, shouldSidebarBeShown, @@ -193,13 +193,24 @@ const App = ({ onFirstRender }) => ( - - - + */} + + {config.tour && } diff --git a/src/components/ServerConnectionManager.jsx b/src/components/ServerConnectionManager.jsx index c1c8b6ed4..2dbb9c98b 100644 --- a/src/components/ServerConnectionManager.jsx +++ b/src/components/ServerConnectionManager.jsx @@ -50,7 +50,7 @@ import { clearStartTimeAndMethod, synchronizeShowSettings, } from '~/features/show/slice'; -import { showError, showNotification } from '~/features/snackbar/actions'; +import { showError, showNotification } from '~/features/snackbar/ToastNotificationManager'; import { MessageSemantics } from '~/features/snackbar/types'; import { clearWeatherData } from '~/features/weather/slice'; import i18n from '~/i18n'; diff --git a/src/components/map/buttons/FitAllFeaturesButton.jsx b/src/components/map/buttons/FitAllFeaturesButton.jsx index 374e67341..a2ef319d2 100644 --- a/src/components/map/buttons/FitAllFeaturesButton.jsx +++ b/src/components/map/buttons/FitAllFeaturesButton.jsx @@ -14,7 +14,7 @@ import IconButton from '@material-ui/core/IconButton'; import ActionAllOut from '@material-ui/icons/AllOut'; import { TooltipWithContainerFromContext as Tooltip } from '~/containerContext'; -import { showError } from '~/features/snackbar/actions'; +import { showError } from '~/features/snackbar/ToastNotificationManager'; import { isUavId } from '~/model/identifiers'; import { fitAllFeaturesSignal, diff --git a/src/components/map/layers/features.jsx b/src/components/map/layers/features.jsx index 8bef19fab..b8526758d 100644 --- a/src/components/map/layers/features.jsx +++ b/src/components/map/layers/features.jsx @@ -28,7 +28,7 @@ import { suggestedColorForFeature, } from '~/features/map-features/selectors-style-suggestions'; import { getGeofencePolygonId } from '~/features/mission/selectors'; -import { showError } from '~/features/snackbar/actions'; +import { showError } from '~/features/snackbar/ToastNotificationManager'; import { FeatureType, LabelStyle } from '~/model/features'; import { featureIdToGlobalId } from '~/model/identifiers'; import { mapViewCoordinateFromLonLat, measureFeature } from '~/utils/geography'; diff --git a/src/components/map/layers/tile-server/connected.js b/src/components/map/layers/tile-server/connected.js index 1b370575e..83bb42874 100644 --- a/src/components/map/layers/tile-server/connected.js +++ b/src/components/map/layers/tile-server/connected.js @@ -1,7 +1,7 @@ import { connect } from 'react-redux'; import { setLayerParametersById } from '~/features/map/layers'; -import { showNotification } from '~/features/snackbar/actions'; +import { showNotification } from '~/features/snackbar/ToastNotificationManager'; import { TileServerLayerSettings as TileServerLayerSettingsPresentation } from './presentation'; diff --git a/src/components/uavs/UAVStatusSummary.jsx b/src/components/uavs/UAVStatusSummary.jsx index 82fe9d987..b450637f8 100644 --- a/src/components/uavs/UAVStatusSummary.jsx +++ b/src/components/uavs/UAVStatusSummary.jsx @@ -13,7 +13,7 @@ import LazyTooltip from '@skybrush/mui-components/lib/LazyTooltip'; import StatusLight from '@skybrush/mui-components/lib/StatusLight'; import { Status } from '~/components/semantics'; -import { showNotification } from '~/features/snackbar/actions'; +import { showNotification } from '~/features/snackbar/ToastNotificationManager'; import { MessageSemantics } from '~/features/snackbar/types'; import { getUAVIdToStateMapping, diff --git a/src/features/hotkeys/actions.js b/src/features/hotkeys/actions.js index 0fade79d3..44bc271bf 100644 --- a/src/features/hotkeys/actions.js +++ b/src/features/hotkeys/actions.js @@ -8,7 +8,7 @@ import { getMissionMapping, isMappingEditable, } from '~/features/mission/selectors'; -import { showNotification } from '~/features/snackbar/actions'; +import { showNotification } from '~/features/snackbar/ToastNotificationManager'; import { setSelectedUAVIds } from '~/features/uavs/actions'; import { getUAVById } from '~/features/uavs/selectors'; import { scrollUAVListItemIntoView } from '~/utils/navigation'; diff --git a/src/features/light-control/actions.js b/src/features/light-control/actions.js index 4c21463ef..5f5010017 100644 --- a/src/features/light-control/actions.js +++ b/src/features/light-control/actions.js @@ -6,7 +6,7 @@ import { } from './selectors'; import { setColor, setLightControlActive } from './slice'; -import { showNotification } from '~/features/snackbar/actions'; +import { showNotification } from '~/features/snackbar/ToastNotificationManager'; import { MessageSemantics } from '~/features/snackbar/types'; import messageHub from '~/message-hub'; diff --git a/src/features/measurement/actions.js b/src/features/measurement/actions.js index 224562c67..ca2a5354f 100644 --- a/src/features/measurement/actions.js +++ b/src/features/measurement/actions.js @@ -15,7 +15,7 @@ import { } from './slice'; import { setFlatEarthCoordinateSystemOrigin } from '~/features/map/origin'; -import { showNotification } from '~/features/snackbar/actions'; +import { showNotification } from '~/features/snackbar/ToastNotificationManager'; import { getPreferredCoordinateFormatter } from '~/selectors/formatting'; /** diff --git a/src/features/mission/actions.js b/src/features/mission/actions.js index 29af67343..640a0af14 100644 --- a/src/features/mission/actions.js +++ b/src/features/mission/actions.js @@ -33,7 +33,7 @@ import { showError, showNotification, showSuccess, -} from '~/features/snackbar/actions'; +} from '~/features/snackbar/ToastNotificationManager'; import { MessageSemantics } from '~/features/snackbar/types'; import { selectSingleUAVUnlessAmbiguous } from '~/features/uavs/actions'; import { diff --git a/src/features/rtk/actions.js b/src/features/rtk/actions.js index 33c711f60..b3c5c0f1c 100644 --- a/src/features/rtk/actions.js +++ b/src/features/rtk/actions.js @@ -1,5 +1,5 @@ import copy from 'copy-to-clipboard'; -import { showNotification } from '~/features/snackbar/actions'; +import { showNotification } from '~/features/snackbar/ToastNotificationManager'; import { MessageSemantics } from '~/features/snackbar/types'; import messageHub from '~/message-hub'; diff --git a/src/features/safety/actions.ts b/src/features/safety/actions.ts index 600893651..dd7feec8b 100644 --- a/src/features/safety/actions.ts +++ b/src/features/safety/actions.ts @@ -8,7 +8,7 @@ import { getMissionType, } from '~/features/mission/selectors'; import { setGeofencePolygonId } from '~/features/mission/slice'; -import { showNotification } from '~/features/snackbar/actions'; +import { showNotification } from '~/features/snackbar/ToastNotificationManager'; import { MessageSemantics } from '~/features/snackbar/types'; import { type Feature, FeatureType } from '~/model/features'; import { type AppThunk } from '~/store/reducers'; diff --git a/src/features/servers/saga.js b/src/features/servers/saga.js index 4e5743cc1..ddccd277d 100644 --- a/src/features/servers/saga.js +++ b/src/features/servers/saga.js @@ -25,7 +25,7 @@ import { } from './slice'; import { showAuthenticationDialog } from '~/features/servers/actions'; -import { showError, showNotification } from '~/features/snackbar/actions'; +import { showError, showNotification } from '~/features/snackbar/ToastNotificationManager'; import messageHub from '~/message-hub'; import { isAuthenticationDialogOpen } from '~/selectors/dialogs'; diff --git a/src/features/session/actions.ts b/src/features/session/actions.ts index 6c3afeb07..3149a67be 100644 --- a/src/features/session/actions.ts +++ b/src/features/session/actions.ts @@ -1,4 +1,4 @@ -import { showNotification } from '~/features/snackbar/actions'; +import { showNotification } from '~/features/snackbar/ToastNotificationManager'; import type { AppDispatch, RootState } from '~/store/reducers'; import { setBroadcast, setDeveloperMode } from './slice'; diff --git a/src/features/show-configurator/actions.ts b/src/features/show-configurator/actions.ts index 19c5449eb..df0b23be3 100644 --- a/src/features/show-configurator/actions.ts +++ b/src/features/show-configurator/actions.ts @@ -8,7 +8,7 @@ import { findAssignmentInDistanceMatrix } from '~/algorithms/matching'; import type { TransformFeaturesInteractionEvent } from '~/components/map/interactions/TransformFeatures'; import { errorToString } from '~/error-handling'; import { getBase64ShowBlob } from '~/features/show/selectors'; -import { showError } from '~/features/snackbar/actions'; +import { showError } from '~/features/snackbar/ToastNotificationManager'; import { getAllValidUAVPositions } from '~/features/uavs/selectors'; import messageHub from '~/message-hub'; import { type GPSPosition } from '~/model/geography'; diff --git a/src/features/show/actions.js b/src/features/show/actions.js index 5325749f5..06e127eaf 100644 --- a/src/features/show/actions.js +++ b/src/features/show/actions.js @@ -19,7 +19,7 @@ import { updateLandingPositions, updateTakeoffHeadings, } from '~/features/mission/slice'; -import { showNotification } from '~/features/snackbar/actions'; +import { showNotification } from '~/features/snackbar/ToastNotificationManager'; import { MessageSemantics } from '~/features/snackbar/types'; import { getActiveUAVIds, diff --git a/src/features/snackbar/ToastNotificationManager.jsx b/src/features/snackbar/ToastNotificationManager.jsx index a383133bc..9e87b37b4 100644 --- a/src/features/snackbar/ToastNotificationManager.jsx +++ b/src/features/snackbar/ToastNotificationManager.jsx @@ -1,141 +1,57 @@ -/** - * @file The global snackbar of the main window. - */ - -import PropTypes from 'prop-types'; -import React from 'react'; -import { connect } from 'react-redux'; -import { useToasts } from 'react-toast-notifications'; - -import Box from '@material-ui/core/Box'; -import Button from '@material-ui/core/Button'; -import { styled } from '@material-ui/core/styles'; - -import { useSignal } from '~/hooks'; - +// src/features/snackbar/toast.jsx +import { toast } from 'react-hot-toast'; import { MessageSemantics } from './types'; -import { SNACKBAR_TRANSITION_DURATION } from './constants'; -import snackbarSignal from './signal'; - -const semanticsToAppearance = { - [MessageSemantics.INFO]: 'info', - [MessageSemantics.SUCCESS]: 'success', - [MessageSemantics.WARNING]: 'warning', - [MessageSemantics.ERROR]: 'error', -}; - -const ToastNotificationButton = styled(Button)({ - border: '1px solid currentColor', - color: 'inherit', -}); - -/** - * Function that creates a React content node to show for the given notification. - */ -const createContentNode = ({ buttons, message, header }, dispatch) => { - let result = message; - - if (Array.isArray(buttons) && buttons.length > 0) { - const buttonComponents = buttons.map( - ({ action, label, ...rest }, index) => ( - dispatch(action)} - {...rest} - > - {label} - - ) - ); - result = ( - - {result} - {buttonComponents} - - ); - } - - if (header && header.length > 0) { - result = ( -
-
- {header} +// توابع داخلی بدون dispatch +function notify(n) { + const content = ( +
+ {n.header && {n.header}} +
{n.message}
+ {Array.isArray(n.buttons) && n.buttons.length > 0 && ( +
+ {n.buttons.map((b, idx) => ( + + ))}
- {result} -
- ); - } - - return result; -}; - -/** - * Presentation component for the global snackbar of the main window. - * - * @returns {Object} the rendered snackbar component - */ -const ToastNotificationManager = ({ dispatch }) => { - const { addToast, toastStack, removeToast } = useToasts(); - - useSignal( - snackbarSignal, - ({ - buttons, - header, - message, - permanent = false, - semantics = MessageSemantics.DEFAULT, - timeout, - topic, - }) => { - const match = topic && toastStack.find((t) => t?.topic === topic); - - const content = createContentNode({ buttons, message, header }, dispatch); - const options = { - appearance: semanticsToAppearance[semantics] || 'info', - autoDismiss: !permanent, - // We don't want to override the default, and `undefined` is not ignored - // internally, so we only pass the parameter, if it's actually present - ...(timeout && { autoDismissTimeout: timeout }), - topic, - }; - - if (match) { - // If a previous notification with the same topic exists, remove it and - // delay the showing of the next one until it has finished disappearing - removeToast(match.id); - if (content) { - setTimeout(() => { - addToast(content, options); - }, SNACKBAR_TRANSITION_DURATION); - } - } else { - if (content) { - addToast(content, options); - } - } - } + )} +
); - return null; + const options = { + duration: n.permanent ? Infinity : n.timeout || 5000, + style: { borderRadius: '8px', background: '#333', color: '#fff' }, + position: 'top-right', + }; + + const toastFn = { + [MessageSemantics.INFO]: toast, + [MessageSemantics.SUCCESS]: toast.success, + [MessageSemantics.WARNING]: toast, + [MessageSemantics.ERROR]: toast.error, + }[n.semantics || MessageSemantics.INFO] || toast; + + toastFn(content, options); +} + +// نسخه Thunk برای dispatch +export const showNotification = (notification) => { + return () => { + const n = typeof notification === 'string' ? { message: notification } : notification; + notify(n); + }; }; -ToastNotificationManager.propTypes = { - dispatch: PropTypes.func.isRequired, +export const showError = (message) => { + return () => { + notify({ message, semantics: MessageSemantics.ERROR }); + }; }; -/** - * Global snackbar at the bottom of the main window. - */ -export default connect()(ToastNotificationManager); -// We only need `connect` to get `dispatch` as a prop. +export const showSuccess = (message) => { + return () => { + notify({ message, semantics: MessageSemantics.SUCCESS }); + }; +}; diff --git a/src/features/snackbar/actions.ts b/src/features/snackbar/actions.ts deleted file mode 100644 index ead0a242f..000000000 --- a/src/features/snackbar/actions.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { sendSnackbarSignal } from './signal'; -import { MessageSemantics, type Notification } from './types'; - -import type { AppThunk } from '~/store/reducers'; - -export function showNotification( - notification: string | Notification -): AppThunk { - return () => { - sendSnackbarSignal( - typeof notification === 'string' - ? { message: notification } - : notification - ); - }; -} - -export function showError(message: string): AppThunk { - return showNotification({ - message, - semantics: MessageSemantics.ERROR, - }); -} - -export function showSuccess(message: string): AppThunk { - return showNotification({ - message, - semantics: MessageSemantics.SUCCESS, - }); -} diff --git a/src/features/snackbar/signal.ts b/src/features/snackbar/signal.ts deleted file mode 100644 index 95228a1bf..000000000 --- a/src/features/snackbar/signal.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MiniSignal } from 'mini-signals'; - -import type { Notification } from './types'; - -const snackbarSignal = new MiniSignal<[string | Notification]>(); - -export const sendSnackbarSignal = ( - notification: string | Notification -): void => { - snackbarSignal.dispatch(notification); -}; - -export default snackbarSignal; diff --git a/src/features/three-d/actions.js b/src/features/three-d/actions.js index 0053a2252..d29c5f17e 100644 --- a/src/features/three-d/actions.js +++ b/src/features/three-d/actions.js @@ -1,5 +1,5 @@ import AFrame from '@skybrush/aframe-components'; -import { showError } from '~/features/snackbar/actions'; +import { showError } from '~/features/snackbar/ToastNotificationManager'; import { getActiveUAVIds } from '~/features/uavs/selectors'; import { getDroneFlockDOMNode } from './refs'; diff --git a/src/features/uav-control/actions.js b/src/features/uav-control/actions.js index 5d6fbce1c..124ddee1d 100644 --- a/src/features/uav-control/actions.js +++ b/src/features/uav-control/actions.js @@ -1,6 +1,6 @@ import meanBy from 'lodash-es/meanBy'; -import { showError } from '~/features/snackbar/actions'; +import { showError } from '~/features/snackbar/ToastNotificationManager'; import { getCurrentGPSPositionByUavId, getSelectedUAVIds, diff --git a/src/features/uavs/UAVLogsPanel.jsx b/src/features/uavs/UAVLogsPanel.jsx index 20ab95d91..24333f486 100644 --- a/src/features/uavs/UAVLogsPanel.jsx +++ b/src/features/uavs/UAVLogsPanel.jsx @@ -21,7 +21,7 @@ import LargeProgressIndicator from '@skybrush/mui-components/lib/LargeProgressIn import StatusLight from '@skybrush/mui-components/lib/StatusLight'; import { listOf } from '~/components/helpers/lists'; -import { showNotification } from '~/features/snackbar/actions'; +import { showNotification } from '~/features/snackbar/ToastNotificationManager'; import { MessageSemantics } from '~/features/snackbar/types'; import { getLogDownloadState, diff --git a/src/message-hub.js b/src/message-hub.js index d6163f9fd..fea93257e 100644 --- a/src/message-hub.js +++ b/src/message-hub.js @@ -21,7 +21,7 @@ import { import { handleObjectDeletionMessage } from './model/objects'; import { batchAddInboundMessages } from './features/messages/slice'; -import { showError, showNotification } from './features/snackbar/actions'; +import { showError, showNotification } from './features/snackbar/ToastNotificationManager'; import { semanticsFromSeverity } from './features/snackbar/utils'; import flock from './flock'; diff --git a/src/utils/messaging.js b/src/utils/messaging.js index f0b79d86b..774d2bd08 100644 --- a/src/utils/messaging.js +++ b/src/utils/messaging.js @@ -11,7 +11,7 @@ import values from 'lodash-es/values'; import { showConfirmationDialog } from '~/features/prompt/actions'; import { UAV_SIGNAL_DURATION } from '~/features/settings/constants'; import { shouldConfirmUAVOperation } from '~/features/settings/selectors'; -import { showNotification } from '~/features/snackbar/actions'; +import { showNotification } from '~/features/snackbar/ToastNotificationManager'; import { MessageSemantics } from '~/features/snackbar/types'; import messageHub from '~/message-hub'; import store from '~/store'; diff --git a/src/views/features/FeaturePanelToolbar.jsx b/src/views/features/FeaturePanelToolbar.jsx index 142b87f93..51484d1e6 100644 --- a/src/views/features/FeaturePanelToolbar.jsx +++ b/src/views/features/FeaturePanelToolbar.jsx @@ -12,7 +12,7 @@ import FolderOpen from '@material-ui/icons/FolderOpen'; import FileButton from '~/components/FileButton'; import { addFeatureWithName } from '~/features/map-features/actions'; -import { showError, showSuccess } from '~/features/snackbar/actions'; +import { showError, showSuccess } from '~/features/snackbar/ToastNotificationManager'; import { createFeaturesFromOpenLayers } from '~/model/openlayers'; import { readFileAsArrayBuffer } from '~/utils/files'; diff --git a/src/views/map/layers/geojson.jsx b/src/views/map/layers/geojson.jsx index 4d9acd5ac..f3ef8c0dc 100644 --- a/src/views/map/layers/geojson.jsx +++ b/src/views/map/layers/geojson.jsx @@ -15,7 +15,7 @@ import TextField from '@material-ui/core/TextField'; import Colors from '~/components/colors'; import PopupColorPicker from '~/components/PopupColorPicker'; import { setLayerParametersById } from '~/features/map/layers'; -import { showNotification } from '~/features/snackbar/actions'; +import { showNotification } from '~/features/snackbar/ToastNotificationManager'; import { parseColor } from '~/utils/coloring'; import { convertSimpleStyleToOLStyle } from '~/utils/simplestyle'; diff --git a/src/views/show-control/OutdoorEnvironmentEditor.jsx b/src/views/show-control/OutdoorEnvironmentEditor.jsx index ffc02c33c..31a76a287 100644 --- a/src/views/show-control/OutdoorEnvironmentEditor.jsx +++ b/src/views/show-control/OutdoorEnvironmentEditor.jsx @@ -43,7 +43,7 @@ import { getOutdoorShowOrientation, getOutdoorShowTakeoffHeadingSpecification, } from '~/features/show/selectors'; -import { showNotification } from '~/features/snackbar/actions'; +import { showNotification } from '~/features/snackbar/ToastNotificationManager'; import { MessageSemantics } from '~/features/snackbar/types'; import { getAverageHeadingOfActiveUAVs } from '~/features/uavs/selectors'; import i18n from '~/i18n'; diff --git a/src/views/show-control/ShowConfiguratorButton.tsx b/src/views/show-control/ShowConfiguratorButton.tsx index 27b4f2133..394cf1082 100644 --- a/src/views/show-control/ShowConfiguratorButton.tsx +++ b/src/views/show-control/ShowConfiguratorButton.tsx @@ -25,7 +25,7 @@ import { hasLoadedShowFile, } from '~/features/show/selectors'; import { getSetupStageStatuses } from '~/features/show/stages'; -import { showError } from '~/features/snackbar/actions'; +import { showError } from '~/features/snackbar/ToastNotificationManager'; import { type PreparedI18nKey, tt } from '~/i18n'; import Pro from '~/icons/Pro'; import {