UNPKG

@adaptabletools/adaptable

Version:

Powerful AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements

963 lines 77.3 kB
import * as Redux from 'redux'; import * as PluginsRedux from '../ActionsReducers/PluginsRedux'; import * as PopupRedux from '../ActionsReducers/PopupRedux'; import { PROGRESS_INDICATOR_HIDE, PROGRESS_INDICATOR_SHOW } from '../ActionsReducers/PopupRedux'; import { createEngine as createEngineLocal } from './AdaptableReduxLocalStorageEngine'; import { mergeReducer } from './AdaptableReduxMerger'; import { isAdaptableCellChangedAlert, isAdaptableRowChangedAlert, } from '../../AdaptableState/Common/AdaptableAlert'; import { closeAgChartDefinitionIfOpen, closeExternalChartDefinitionIfOpen, } from '../../Utilities/Helpers/ChartHelper'; import { refreshScheduledAlertJobs, refreshScheduledReportJobs, } from '../../Utilities/Helpers/Scheduling/ScheduledJobsMiddlewareHelper'; import { getRuleAlertProperties, isScheduledAlertDefinition, } from '../../Utilities/Helpers/Scheduling/ScheduledAlertHelper'; import { ERROR_LAYOUT } from '../../Utilities/Constants/GeneralConstants'; import * as ModuleConstants from '../../Utilities/Constants/ModuleConstants'; import Emitter from '../../Utilities/Emitter'; import { StringExtensions } from '../../Utilities/Extensions/StringExtensions'; import { PreviewHelper } from '../../Utilities/Helpers/PreviewHelper'; import { ObjectFactory } from '../../Utilities/ObjectFactory'; import { showToast } from '../../View/Components/Popups/AdaptableToaster'; import * as AlertRedux from '../ActionsReducers/AlertRedux'; import * as ApplicationRedux from '../ActionsReducers/ApplicationRedux'; import * as BulkUpdateRedux from '../ActionsReducers/BulkUpdateRedux'; import * as CalculatedColumnRedux from '../ActionsReducers/CalculatedColumnRedux'; import * as ChartingRedux from '../ActionsReducers/ChartingRedux'; import * as CommentsRedux from '../ActionsReducers/CommentsRedux'; import * as CustomSortRedux from '../ActionsReducers/CustomSortRedux'; import * as DashboardRedux from '../ActionsReducers/DashboardRedux'; import * as ExportRedux from '../ActionsReducers/ExportRedux'; import * as FlashingCellRedux from '../ActionsReducers/FlashingCellRedux'; import * as FormatColumnRedux from '../ActionsReducers/FormatColumnRedux'; import * as FreeTextColumnRedux from '../ActionsReducers/FreeTextColumnRedux'; import * as LayoutRedux from '../ActionsReducers/LayoutRedux'; import * as NamedQueryRedux from '../ActionsReducers/NamedQueryRedux'; import * as NoteRedux from '../ActionsReducers/NoteRedux'; import * as PlusMinusRedux from '../ActionsReducers/PlusMinusRedux'; import * as QuickSearchRedux from '../ActionsReducers/QuickSearchRedux'; import * as ShortcutRedux from '../ActionsReducers/ShortcutRedux'; import * as SmartEditRedux from '../ActionsReducers/SmartEditRedux'; import * as StatusBarRedux from '../ActionsReducers/StatusBarRedux'; import * as StyledColumnRedux from '../ActionsReducers/StyledColumnRedux'; import * as InternalRedux from '../ActionsReducers/InternalRedux'; import * as TeamSharingRedux from '../ActionsReducers/TeamSharingRedux'; import * as ThemeRedux from '../ActionsReducers/ThemeRedux'; import * as ToolPanelRedux from '../ActionsReducers/ToolPanelRedux'; import * as UserInterfaceRedux from '../ActionsReducers/UserInterfaceRedux'; import { areAdaptableProfileTracksEnabled, getMarker } from '../../devTools'; import { isAdaptableSharedEntity, isCustomSharedEntity, } from '../../AdaptableState/TeamSharingState'; import { buildAdaptableStateFunctionConfig } from './buildAdaptableStateFunctionConfig'; const INIT_STATE = 'INIT_STATE'; const LOAD_STATE = 'LOAD_STATE'; const NON_PERSIST_ACTIONS = { '@@INIT': true, '@@redux/init': true, [LOAD_STATE]: true, [INIT_STATE]: true, [PROGRESS_INDICATOR_SHOW]: true, [PROGRESS_INDICATOR_HIDE]: true, }; const NON_PERSISTENT_STORE_KEYS = [ 'Internal', 'Popup', 'Comment', 'Plugins', ]; export const getPersistableState = (state) => { NON_PERSISTENT_STORE_KEYS.forEach((key) => { delete state[key]; }); return state; }; export const InitState = () => ({ type: INIT_STATE, }); export const LoadState = (State) => ({ type: LOAD_STATE, State, }); export class AdaptableStore { TheStore; Load; emitter; beforeEmitter; storageEngine; currentStorageState; previousStorageState; loadStorageInProgress = false; loadStateOnStartup = true; on = (eventName, callback) => { return this.emitter.on(eventName, callback); }; onAny = (callback) => { return this.emitter.onAny(callback); }; onBeforeAny = (callback) => { return this.beforeEmitter.onAny(callback); }; emit = (eventName, data) => { return this.emitter.emit(eventName, data); }; constructor(adaptable) { let rootReducerObject = { Popup: PopupRedux.PopupReducer, Internal: InternalRedux.InternalReducer, Plugins: PluginsRedux.PluginsReducer, Comment: CommentsRedux.CommentsReducer, Alert: AlertRedux.AlertReducer, Application: ApplicationRedux.ApplicationReducer, CalculatedColumn: CalculatedColumnRedux.CalculatedColumnReducer, Charting: ChartingRedux.ChartingReducer, CustomSort: CustomSortRedux.CustomSortReducer, Dashboard: DashboardRedux.DashboardReducer, Export: ExportRedux.ExportReducer, FlashingCell: FlashingCellRedux.FlashingCellReducer, FormatColumn: FormatColumnRedux.FormatColumnReducer, FreeTextColumn: FreeTextColumnRedux.FreeTextColumnReducer, Layout: LayoutRedux.LayoutReducer, NamedQuery: NamedQueryRedux.NamedQueryReducer, Note: NoteRedux.NoteReducer, PlusMinus: PlusMinusRedux.PlusMinusReducer, QuickSearch: QuickSearchRedux.QuickSearchReducer, Shortcut: ShortcutRedux.ShortcutReducer, StatusBar: StatusBarRedux.StatusBarReducer, StyledColumn: StyledColumnRedux.StyledColumnReducer, TeamSharing: TeamSharingRedux.TeamSharingReducer, Theme: ThemeRedux.ThemeReducer, ToolPanel: ToolPanelRedux.ToolPanelReducer, UserInterface: UserInterfaceRedux.UserInterfaceReducer, }; adaptable.forPlugins((plugin) => { if (plugin.rootReducer) { rootReducerObject = { ...rootReducerObject, ...plugin.rootReducer(rootReducerObject), }; } }); const initialRootReducer = Redux.combineReducers(rootReducerObject); const rootReducerWithResetManagement = (state, action) => { switch (action.type) { case LOAD_STATE: const { State } = action; Object.keys(State).forEach((key) => { state[key] = State[key]; }); break; } return initialRootReducer(state, action); }; let storageEngine; this.emitter = new Emitter(); this.beforeEmitter = new Emitter(); storageEngine = createEngineLocal({ adaptableId: adaptable.adaptableOptions.adaptableId, adaptableStateKey: adaptable.adaptableOptions.adaptableStateKey, userName: adaptable.adaptableOptions.userName, initialState: adaptable.adaptableOptions.initialState, loadState: adaptable.adaptableOptions.stateOptions.loadState, persistState: adaptable.adaptableOptions.stateOptions.persistState, debounceStateDelay: adaptable.adaptableOptions.stateOptions.debounceStateDelay, }); const didPersistentStateChange = (state, newState) => { return Object.keys(newState).some((key) => { if (NON_PERSISTENT_STORE_KEYS.includes(key)) { return false; } return state?.[key] !== newState?.[key]; }); }; let rootReducer = mergeReducer(rootReducerWithResetManagement, LOAD_STATE); const composeEnhancers = (x) => x; const persistedReducer = (state, action) => { if (adaptable.isDestroyed) { return state; } const init = state === undefined; const newState = rootReducer(state, action); const emitterArg = { action, state, newState }; if (!NON_PERSIST_ACTIONS[action.type]) { this.emitter.emit(action.type, emitterArg); } const finalState = emitterArg.newState; const shouldPersist = state !== finalState && didPersistentStateChange(state, finalState) && !state?.Internal?.License?.disablePersistence && !NON_PERSIST_ACTIONS[action.type] && !init && !this.loadStorageInProgress; if (shouldPersist) { let storageState = { ...finalState }; storageState = getPersistableState(storageState); this.currentStorageState = storageState; this.previousStorageState = { ...state }; NON_PERSISTENT_STORE_KEYS.forEach((key) => { delete this.previousStorageState[key]; }); storageEngine.save(adaptable, storageState, action.type); } return finalState; }; const beforeEmitterMiddleware = (middlewareAPI) => (next) => (action) => { if (!NON_PERSIST_ACTIONS[action.type]) { const wasProgressActive = middlewareAPI.getState().Popup?.ProgressIndicator?.active; this.beforeEmitter.emitSync(action.type, { action, state: middlewareAPI.getState(), }); const isProgressActive = middlewareAPI.getState().Popup?.ProgressIndicator?.active; if (!wasProgressActive && isProgressActive) { requestAnimationFrame(() => { requestAnimationFrame(() => { next(action); }); }); return action; } } return next(action); }; const devToolsActionMarkerMiddleware = createDevToolsActionMarkerMiddleware(adaptable); const pluginsMiddleware = []; adaptable.forPlugins((plugin) => { if (plugin.reduxMiddleware) { pluginsMiddleware.push(plugin.reduxMiddleware(adaptable)); } }); const middlewares = [ beforeEmitterMiddleware, devToolsActionMarkerMiddleware, adaptableMiddleware(adaptable), ...pluginsMiddleware, ].filter(Boolean); this.TheStore = Redux.createStore(persistedReducer, composeEnhancers(Redux.applyMiddleware(...middlewares))); this.storageEngine = storageEngine; } destroy() { this.emitter?.clearListeners(); this.emitter = null; this.beforeEmitter?.clearListeners(); this.beforeEmitter = null; } getCurrentStorageState() { return this.currentStorageState; } getPreviousStorageState() { return this.previousStorageState; } saveStateNow(adaptable, state) { const newState = state ?? this.getCurrentStorageState(); if (newState) { return this.storageEngine.saveNow(adaptable, newState, 'API_CALL_SAVE_STATE'); } return Promise.resolve(true); } loadStore = (config) => { const { adaptable, adaptableStateKey, initialState, postLoadHook } = config; const postProcessState = postLoadHook ?? ((state) => state); this.storageEngine.setStateKey(adaptableStateKey); this.loadStorageInProgress = true; return (this.Load = this.storageEngine .load(adaptable, initialState) .then((storedState) => { if (storedState && this.loadStateOnStartup) { this.TheStore.dispatch(LoadState(postProcessState(adaptable.adaptableOptions.stateOptions.applyState(storedState, buildAdaptableStateFunctionConfig(adaptable))))); } }) .then(() => { this.TheStore.dispatch(InitState()); this.loadStorageInProgress = false; }, (e) => { adaptable.api.consoleError('Failed to load saved Adaptable state.', e); this.TheStore.dispatch(InitState()); this.loadStorageInProgress = false; this.TheStore.dispatch(PopupRedux.PopupShowAlert({ alertType: 'generic', header: 'Configuration', message: 'Failed to load your configuration: ' + e, alertDefinition: ObjectFactory.CreateInternalAlertDefinitionForMessages('Error'), })); })); }; } function createDevToolsActionMarkerMiddleware(adaptable) { const adaptableId = adaptable?.adaptableOptions?.adaptableId; if (!adaptableId) { return null; } if (!areAdaptableProfileTracksEnabled(adaptableId)) { return null; } return (middlewareAPI) => (next) => (action) => { if (!isTrackableReduxAction(action)) { return next(action); } const previousState = middlewareAPI.getState(); const marker = getMarker(adaptableId).track.Redux.label.Action.start(); try { return next(action); } finally { const nextState = middlewareAPI.getState(); const changedKeys = detectChangedAdaptableStateKeys(previousState, nextState); const details = buildDevToolsActionDetails(action, changedKeys); marker.end({ label: action.type, tooltip: changedKeys?.length ? `State changes: ${changedKeys.join(', ')}` : undefined, details: details.length ? details : undefined, }); } }; } const MAX_MARKER_DETAILS = 6; function buildDevToolsActionDetails(action, changedKeys) { const details = []; if (changedKeys && changedKeys.length > 0) { const truncatedKeys = changedKeys.slice(0, MAX_MARKER_DETAILS); const suffix = changedKeys.length > truncatedKeys.length ? ` +${changedKeys.length - truncatedKeys.length} more` : ''; details.push({ name: 'State changes', value: `${truncatedKeys.join(', ')}${suffix}`, }); } if (isActionWithPayload(action)) { const entries = Object.entries(action).filter(([key]) => key !== 'type'); for (const [key, value] of entries) { if (details.length >= MAX_MARKER_DETAILS) { break; } details.push({ name: `action.${key}`, value: coerceMarkerDetailValue(value), }); } } return details; } function detectChangedAdaptableStateKeys(previousState, nextState) { if (!previousState || !nextState || previousState === nextState) { return undefined; } const previous = previousState; const next = nextState; const keys = new Set([...Object.keys(previous), ...Object.keys(next)]); const changed = []; keys.forEach((key) => { if (previous[key] !== next[key]) { changed.push(key); } }); return changed.length > 0 ? changed : undefined; } function isTrackableReduxAction(action) { return !!action && typeof action.type === 'string'; } function isActionWithPayload(action) { return !!action && typeof action === 'object'; } function coerceMarkerDetailValue(value) { if (value === null) { return 'null'; } if (value === undefined) { return 'undefined'; } const valueType = typeof value; if (valueType === 'string' || valueType === 'number' || valueType === 'boolean') { return value; } if (valueType === 'function') { return '[function]'; } try { const json = JSON.stringify(value); if (!json) { return '[empty]'; } const MAX_LENGTH = 200; return json.length > MAX_LENGTH ? `${json.slice(0, MAX_LENGTH - 3)}...` : json; } catch (err) { return '[unserializable]'; } } const adaptableMiddleware = (adaptable) => (function(middlewareAPI) { return function (next) { return function (action) { switch (action.type) { case NamedQueryRedux.NAMED_QUERY_DELETE: { const actionTyped = action; if (!adaptable.api.namedQueryApi.internalApi.validateDeletedNamedQuery(actionTyped.namedQuery.Name)) { return; } const ret = next(action); return ret; } case NamedQueryRedux.NAMED_QUERY_EDIT: { const actionTyped = action; const editedNamedQuery = actionTyped.namedQuery; const previousNamedQueryName = middlewareAPI .getState() .NamedQuery.NamedQueries.find((namedQuery) => namedQuery.Uuid === editedNamedQuery.Uuid)?.Name ?? ''; if (editedNamedQuery.Name !== previousNamedQueryName) { if (!adaptable.api.namedQueryApi.internalApi.validateRenamedNamedQuery(previousNamedQueryName)) { return; } } const ret = next(action); return ret; } case InternalRedux.SUMMARY_ROW_SET: { let nextAction = next(action); adaptable.api.layoutApi.internalApi.setupRowSummaries(); return nextAction; } case InternalRedux.QUICK_FILTER_BAR_SHOW: { let nextAction = next(action); adaptable.showQuickFilter(); return nextAction; } case InternalRedux.QUICK_FILTER_BAR_HIDE: { let nextAction = next(action); adaptable.hideQuickFilter(); return nextAction; } case AlertRedux.ALERT_DEFINITION_ADD: case AlertRedux.ALERT_DEFINITION_EDIT: case AlertRedux.ALERT_DEFINITION_DELETE: case AlertRedux.ALERT_DEFINITION_UNSUSPEND: case AlertRedux.ALERT_DEFINITION_SUSPEND: case AlertRedux.ALERT_DEFINITION_UNSUSPEND_ALL: case AlertRedux.ALERT_DEFINITION_SUSPEND_ALL: { const returnAction = next(action); const alertDefinition = returnAction.alertDefinition; if (alertDefinition) { if (!isScheduledAlertDefinition(alertDefinition) && (returnAction.type === AlertRedux.ALERT_DEFINITION_ADD || returnAction.type === AlertRedux.ALERT_DEFINITION_EDIT || returnAction.type === AlertRedux.ALERT_DEFINITION_UNSUSPEND)) { adaptable.api.internalApi.getAlertService().createReactiveAlert(alertDefinition); } if (!isScheduledAlertDefinition(alertDefinition) && (returnAction.type === AlertRedux.ALERT_DEFINITION_DELETE || returnAction.type === AlertRedux.ALERT_DEFINITION_SUSPEND)) { adaptable.api.internalApi.getAlertService().deleteReactiveAlert(alertDefinition); } } if (returnAction.type === AlertRedux.ALERT_DEFINITION_SUSPEND_ALL) { adaptable.api.internalApi .getAlertService() .getReactiveActiveAlerts() .forEach((alertDefinition) => { adaptable.api.internalApi.getAlertService().deleteReactiveAlert(alertDefinition); }); } if (returnAction.type === AlertRedux.ALERT_DEFINITION_UNSUSPEND_ALL) { adaptable.api.alertApi.internalApi .getReactiveAlertDefinitions() .forEach((alertDefinition) => { if (!adaptable.api.internalApi .getAlertService() .isReactiveAlertActive(alertDefinition)) { adaptable.api.internalApi .getAlertService() .createReactiveAlert(alertDefinition); } }); } adaptable.updateColumnModelAndRefreshGrid(); let module = (adaptable.adaptableModules.get(ModuleConstants.AlertModuleId)); if (module) { module.checkListenToCellDataChanged(); } refreshScheduledAlertJobs(adaptable); return returnAction; } case FlashingCellRedux.FLASHING_CELL_DEFINITION_ADD: case FlashingCellRedux.FLASHING_CELL_DEFINITION_EDIT: case FlashingCellRedux.FLASHING_CELL_DEFINITION_DELETE: case FlashingCellRedux.FLASHING_CELL_DEFINITION_UNSUSPEND: case FlashingCellRedux.FLASHING_CELL_DEFINITION_SUSPEND: case FlashingCellRedux.FLASHING_CELL_DEFINITION_UNSUSPEND_ALL: case FlashingCellRedux.FLASHING_CELL_DEFINITION_SUSPEND_ALL: { const returnAction = next(action); adaptable.updateColumnModelAndRefreshGrid(); let module = (adaptable.adaptableModules.get(ModuleConstants.FlashingCellModuleId)); if (module) { module.checkListenToCellDataChanged(); } return returnAction; } case FreeTextColumnRedux.FREE_TEXT_COLUMN_ADD: case FreeTextColumnRedux.FREE_TEXT_COLUMN_EDIT: case FreeTextColumnRedux.FREE_TEXT_COLUMN_DELETE: { if (action.type === FreeTextColumnRedux.FREE_TEXT_COLUMN_DELETE) { const actionTyped = action; if (!adaptable.api.freeTextColumnApi.internalApi.validateDeletedFreeTextColumn(actionTyped.freeTextColumn)) { return; } } const returnAction = next(action); let module = (adaptable.adaptableModules.get(ModuleConstants.FreeTextColumnModuleId)); if (module) { module.checkListenToCellDataChanged(); } adaptable.api.layoutApi.internalApi.refreshLayout(); return returnAction; } case CalculatedColumnRedux.CALCULATED_COLUMN_ADD: case CalculatedColumnRedux.CALCULATED_COLUMN_EDIT: case CalculatedColumnRedux.CALCULATED_COLUMN_DELETE: { const actionTypedCC = action; const calculatedColumn = actionTypedCC.calculatedColumn; if (action.type === CalculatedColumnRedux.CALCULATED_COLUMN_DELETE) { if (!adaptable.api.calculatedColumnApi.internalApi.validateDeletedCalculatedColumn(calculatedColumn)) { return; } } const returnAction = next(action); if (action.type === CalculatedColumnRedux.CALCULATED_COLUMN_ADD || action.type === CalculatedColumnRedux.CALCULATED_COLUMN_EDIT) { adaptable.api.internalApi .getCalculatedColumnExpressionService() .createAggregatedScalarLiveValue(calculatedColumn); } else { adaptable.api.internalApi .getCalculatedColumnExpressionService() .destroyAggregatedScalarLiveValue(calculatedColumn); } adaptable.api.eventApi.internalApi.fireCalculatedColumnChangedEvent(action.type, calculatedColumn); let module = (adaptable.adaptableModules.get(ModuleConstants.CalculatedColumnModuleId)); if (module) { module.checkListenToCellDataChanged(); } adaptable.api.layoutApi.internalApi.refreshLayout(); return returnAction; } case QuickSearchRedux.QUICK_SEARCH_SET_CELL_MATCHING_STYLE: case QuickSearchRedux.QUICK_SEARCH_SET_TEXT_MATCHING_STYLE: case QuickSearchRedux.QUICK_SEARCH_SET_CURRENT_TEXT_MATCHING_STYLE: { const returnAction = next(action); adaptable.applyQuickSearchFindCssVars(); if (StringExtensions.IsNotNullOrEmpty(adaptable.api.quickSearchApi.getQuickSearchValue())) { adaptable.refreshAgGridFind(); } adaptable.updateColumnModelAndRefreshGrid(); return returnAction; } case FormatColumnRedux.FORMAT_COLUMN_ADD_BATCH: case FormatColumnRedux.FORMAT_COLUMN_ADD: case FormatColumnRedux.FORMAT_COLUMN_EDIT: case FormatColumnRedux.FORMAT_COLUMN_DELETE: case FormatColumnRedux.FORMAT_COLUMN_DELETE_ALL: case FormatColumnRedux.FORMAT_COLUMN_MOVE_DOWN: case FormatColumnRedux.FORMAT_COLUMN_MOVE_UP: case FormatColumnRedux.FORMAT_COLUMN_SUSPEND: case FormatColumnRedux.FORMAT_COLUMN_UNSUSPEND: case FormatColumnRedux.FORMAT_COLUMN_SUSPEND_ALL: case FormatColumnRedux.FORMAT_COLUMN_UNSUSPEND_ALL: case StyledColumnRedux.STYLED_COLUMN_ADD: case StyledColumnRedux.STYLED_COLUMN_EDIT: case StyledColumnRedux.STYLED_COLUMN_DELETE: case StyledColumnRedux.STYLED_COLUMN_SUSPEND: case StyledColumnRedux.STYLED_COLUMN_UNSUSPEND: case StyledColumnRedux.STYLED_COLUMN_SUSPEND_ALL: case StyledColumnRedux.STYLED_COLUMN_UNSUSPEND_ALL: case CustomSortRedux.CUSTOM_SORT_ADD: case CustomSortRedux.CUSTOM_SORT_EDIT: case CustomSortRedux.CUSTOM_SORT_DELETE: case CustomSortRedux.CUSTOM_SORT_SUSPEND: case CustomSortRedux.CUSTOM_SORT_UNSUSPEND: case CustomSortRedux.CUSTOM_SORT_SUSPEND_ALL: case CustomSortRedux.CUSTOM_SORT_UNSUSPEND_ALL: { const returnAction = next(action); adaptable.updateColumnModelAndRefreshGrid(); return returnAction; } case QuickSearchRedux.QUICK_SEARCH_RUN: { let returnAction = next(action); const actionTyped = action; const searchText = actionTyped.quickSearchText; adaptable.setAgGridFindSearchValue(searchText); adaptable.api.gridApi.refreshAllCells(true); if (adaptable.adaptableOptions.quickSearchOptions.filterGridAfterQuickSearch) { if (StringExtensions.IsNotNullOrEmpty(searchText)) { adaptable.setAgGridQuickSearch(searchText); } else { adaptable.clearAgGridQuickSearch(); } } return returnAction; } case InternalRedux.ALERT_DELETE: { const actionTyped = action; let ret = next(action); const adaptableAlert = actionTyped.alert; if (getRuleAlertProperties(adaptableAlert.alertDefinition)?.HighlightCell && isAdaptableCellChangedAlert(adaptableAlert) && adaptableAlert.cellDataChangedInfo) { const rowNode = adaptableAlert.cellDataChangedInfo.rowNode; adaptable.api.gridApi.refreshCell(rowNode, adaptableAlert.cellDataChangedInfo.column.columnId, true); } if (getRuleAlertProperties(adaptableAlert.alertDefinition)?.HighlightRow && isAdaptableRowChangedAlert(adaptableAlert) && adaptableAlert.rowDataChangedInfo) { adaptable.api.gridApi.refreshRowNodes(adaptableAlert.rowDataChangedInfo.rowNodes); } return ret; } case InternalRedux.ALERT_DELETE_ALL: { const actionTyped = action; let ret = next(action); let alerts = actionTyped.alerts; alerts.forEach((alert) => { if (getRuleAlertProperties(alert.alertDefinition)?.HighlightCell && isAdaptableCellChangedAlert(alert) && alert.cellDataChangedInfo) { let rowNode = alert.cellDataChangedInfo.rowNode; adaptable.api.gridApi.refreshCell(rowNode, alert.cellDataChangedInfo.column.columnId, true); } if (getRuleAlertProperties(alert.alertDefinition)?.HighlightRow && isAdaptableRowChangedAlert(alert) && alert.rowDataChangedInfo) { adaptable.api.gridApi.refreshRowNodes(alert.rowDataChangedInfo.rowNodes); } }); return ret; } case InternalRedux.ALERT_REMOVE_CELL_HIGHLIGHT: { let ret = next(action); const actionTyped = action; const adaptableAlert = actionTyped.alert; if (getRuleAlertProperties(adaptableAlert.alertDefinition)?.HighlightCell) { if (isAdaptableCellChangedAlert(adaptableAlert) && adaptableAlert.cellDataChangedInfo) { const rowNode = adaptableAlert.cellDataChangedInfo.rowNode; adaptable.api.gridApi.refreshCell(rowNode, adaptableAlert.cellDataChangedInfo.column.columnId, true); } } return ret; } case InternalRedux.ALERT_REMOVE_ROW_HIGHLIGHT: { let ret = next(action); const actionTyped = action; const adaptableAlert = actionTyped.alert; if (getRuleAlertProperties(adaptableAlert.alertDefinition)?.HighlightRow) { if (isAdaptableCellChangedAlert(adaptableAlert) && adaptableAlert.cellDataChangedInfo) { adaptable.api.gridApi.refreshRowNodes([adaptableAlert.cellDataChangedInfo.rowNode]); } if (isAdaptableRowChangedAlert(adaptableAlert) && adaptableAlert.rowDataChangedInfo) { adaptable.api.gridApi.refreshRowNodes(adaptableAlert.rowDataChangedInfo.rowNodes); } } return ret; } case InternalRedux.HIGHLIGHT_CELL_ADD: { const actionTyped = action; const ret = next(action); const cellHighlightInfo = actionTyped.cellHighlightInfo; const rowNode = adaptable.api.gridApi.getRowNodeForPrimaryKey(cellHighlightInfo.primaryKeyValue); if (rowNode) { adaptable.api.gridApi.refreshCell(rowNode, cellHighlightInfo.columnId, true); } return ret; } case InternalRedux.HIGHLIGHT_CELL_DELETE: { const actionTyped = action; const ret = next(action); const rowNode = adaptable.api.gridApi.getRowNodeForPrimaryKey(actionTyped.primaryKeyValue); if (rowNode) { adaptable.api.gridApi.refreshCell(rowNode, actionTyped.columnId, true); } return ret; } case InternalRedux.HIGHLIGHT_CELL_DELETE_ALL: { const cellHighlightInfos = middlewareAPI.getState().Internal.CellHighlightInfo; const ret = next(action); cellHighlightInfos.forEach((cellHighlightInfo) => { const rowNode = adaptable.api.gridApi.getRowNodeForPrimaryKey(cellHighlightInfo.primaryKeyValue); if (!rowNode) { return; } adaptable.api.gridApi.refreshCell(rowNode, cellHighlightInfo.columnId, true); }); return ret; } case InternalRedux.HIGHLIGHT_COLUMN_ADD: { const actionTyped = action; const ret = next(action); const columnHighlightInfo = actionTyped.columnHighlightInfo; adaptable.api.gridApi.refreshColumn(columnHighlightInfo.columnId); return ret; } case InternalRedux.HIGHLIGHT_COLUMN_DELETE: { const actionTyped = action; const ret = next(action); adaptable.api.gridApi.refreshColumn(actionTyped.columnId); return ret; } case InternalRedux.HIGHLIGHT_COLUMN_DELETE_ALL: { const columnHighlightInfos = middlewareAPI.getState().Internal.ColumnHighlightInfo; const ret = next(action); columnHighlightInfos.forEach((columnHighlightInfo) => { adaptable.api.gridApi.refreshColumn(columnHighlightInfo.columnId); }); return ret; } case InternalRedux.HIGHLIGHT_ROW_ADD: { const actionTyped = action; const ret = next(action); const rowHighlightInfo = actionTyped.rowHighlightInfo; const rowNode = adaptable.api.gridApi.getRowNodeForPrimaryKey(rowHighlightInfo.primaryKeyValue); if (rowNode) { adaptable.api.gridApi.refreshRowNode(rowNode); } return ret; } case InternalRedux.HIGHLIGHT_ROW_DELETE: { const actionTyped = action; const ret = next(action); const rowNode = adaptable.api.gridApi.getRowNodeForPrimaryKey(actionTyped.primaryKeyValue); if (rowNode) { adaptable.api.gridApi.refreshRowNode(rowNode); } return ret; } case InternalRedux.HIGHLIGHT_ROWS_ADD: { const actionTyped = action; const ret = next(action); const rowsHighlightInfo = actionTyped.rowsHighlightInfo; const rowNodes = adaptable.api.gridApi.getRowNodesForPrimaryKeys(rowsHighlightInfo.primaryKeyValues); if (rowNodes.length) { adaptable.api.gridApi.refreshRowNodes(rowNodes); } return ret; } case InternalRedux.HIGHLIGHT_ROWS_DELETE: { const actionTyped = action; const ret = next(action); const rowNodes = adaptable.api.gridApi.getRowNodesForPrimaryKeys(actionTyped.primaryKeyValues); if (rowNodes.length) { adaptable.api.gridApi.refreshRowNodes(rowNodes); } return ret; } case InternalRedux.HIGHLIGHT_ROW_DELETE_ALL: { const rowHighlightInfos = middlewareAPI.getState().Internal.RowHighlightInfo; const ret = next(action); rowHighlightInfos.forEach((rowHighlightInfo) => { const rowNode = adaptable.api.gridApi.getRowNodeForPrimaryKey(rowHighlightInfo.primaryKeyValue); if (!rowNode) { return; } adaptable.api.gridApi.refreshRowNode(rowNode); }); return ret; } case InternalRedux.DATA_CHANGE_HISTORY_UNDO: const actionTypedUndo = action; const cellDataChangedInfo = actionTypedUndo.changeInfo; adaptable.api.gridApi.undoCellEdit(cellDataChangedInfo); return next(action); case InternalRedux.DATA_CHANGE_HISTORY_ENABLE: case InternalRedux.DATA_CHANGE_HISTORY_DISABLE: case InternalRedux.DATA_CHANGE_HISTORY_SUSPEND: case InternalRedux.DATA_CHANGE_HISTORY_RESUME: const returnAction = next(action); let module = (adaptable.adaptableModules.get(ModuleConstants.DataChangeHistoryModuleId)); if (module) { module.checkListenToCellDataChanged(); } return returnAction; case InternalRedux.CREATE_CELL_SUMMARY_INFO: { let module = (adaptable.adaptableModules.get(ModuleConstants.CellSummaryModuleId)); let returnAction = next(action); let selectedCellInfo = middlewareAPI.getState().Internal.SelectedCellInfo; let apiSummaryReturn = module.createCellSummaryInfo(selectedCellInfo); adaptable.api.internalApi.setCellSummaryInfo(apiSummaryReturn); return returnAction; } case InternalRedux.DATA_SET_SELECT: { let returnAction = next(action); const dataSet = adaptable.api.dataSetApi.getCurrentDataSet(); if (dataSet) { adaptable.api.dataSetApi.internalApi.handleDataSetSelected(dataSet); requestAnimationFrame(() => { adaptable.api.dataSetApi.internalApi.showDataSetForm(dataSet); }); } return returnAction; } case ThemeRedux.THEME_SELECT: { let returnAction = next(action); adaptable.api.themeApi.applyCurrentTheme(); return returnAction; } case NoteRedux.NOTE_ADD: case NoteRedux.NOTE_EDIT: case NoteRedux.NOTE_DELETE: { let returnAction = next(action); adaptable.AnnotationsService.checkListenToEvents(); const rowNode = adaptable.api.gridApi.getRowNodeForPrimaryKey(returnAction.adaptableNote.PrimaryKeyValue); adaptable.api.gridApi.refreshCell(rowNode, returnAction.adaptableNote.ColumnId, true); return returnAction; } case CommentsRedux.COMMENT_ADD: case CommentsRedux.COMMENT_EDIT: case CommentsRedux.COMMENT_DELETE: case CommentsRedux.COMMENT_THREAD_DELETE: case CommentsRedux.COMMENT_THREAD_ADD: { let returnAction = next(action); adaptable.AnnotationsService.checkListenToEvents(); let rowNode = null; let columnId = null; if ('cellAddress' in returnAction) { rowNode = adaptable.api.gridApi.getRowNodeForPrimaryKey(returnAction.cellAddress.PrimaryKeyValue); columnId = returnAction.cellAddress.ColumnId; } else if ('cellComments' in returnAction) { rowNode = adaptable.api.gridApi.getRowNodeForPrimaryKey(returnAction.cellComments.PrimaryKeyValue); columnId = returnAction.cellComments.ColumnId; } if (rowNode && columnId) { adaptable.api.gridApi.refreshCell(rowNode, columnId, true); requestAnimationFrame(() => { const commentThreads = adaptable.api.commentApi.getAllComments(); adaptable.api.eventApi.emit('CommentChanged', commentThreads); adaptable.api.optionsApi .getCommentOptions() ?.persistCommentThreads?.(commentThreads); }); } return returnAction; } case CommentsRedux.COMMENTS_THREADS_LOAD: { const previousCommentThreads = adaptable.api.commentApi.getAllComments(); let returnAction = next(action); const newCommentThreads = adaptable.api.commentApi.getAllComments() ?? []; requestAnimationFrame(() => { let addedCommentThreads = []; let deletedCommentThreads = []; const prevCommentThreadsSet = new Set((previousCommentThreads ?? []).map((c) => c.Uuid)); const newCommentThreadsSet = new Set((newCommentThreads ?? []).map((c) => c.Uuid)); for (const commentThread of newCommentThreads) { if (!prevCommentThreadsSet.has(commentThread.Uuid)) { addedCommentThreads.push(commentThread); } } for (const commentThread of previousCommentThreads) { if (!newCommentThreadsSet.has(commentThread.Uuid)) { deletedCommentThreads.push(commentThread); } } [...addedCommentThreads, ...deletedCommentThreads].forEach((commentThread) => { const node = adaptable.api.gridApi.getRowNodeForPrimaryKey(commentThread.PrimaryKeyValue); if (node && commentThread.ColumnId) { adaptable.api.gridApi.refreshCell(node, commentThread.ColumnId, true); } }); }); return returnAction; } case ExportRedux.EXPORT_ENSURE_SCHEDULE_UUIDS: case ExportRedux.SCHEDULED_REPORT_ADD: case ExportRedux.SCHEDULED_REPORT_EDIT: case ExportRedux.SCHEDULED_REPORT_DELETE: case ExportRedux.SCHEDULED_REPORT_SUSPEND: case ExportRedux.SCHEDULED_REPORT_UNSUSPEND: { const returnAction = next(action); refreshScheduledReportJobs(adaptable); return returnAction; } case UserInterfaceRedux.USER_INTERFACE_SET_HIDE_ADAPTABLE_UI: { const returnAction = next(action); const { hideAdaptableUI } = action; middlewareAPI.dispatch(DashboardRedux.DashboardSetIsHidden(hideAdaptableUI)); if (hideAdaptableUI) { adaptable.hideAdaptableToolPanel(); } else { adaptable.showAdaptableToolPanel(); } if (hideAdaptableUI) { adaptable.hideAdaptableStatusBar(); } else { adaptable.showAdaptableStatusBar(); } return returnAction; } case DashboardRedux.DASHBOARD_SET_IS_COLLAPSED: case DashboardRedux.DASHBOARD_SET_MODULE_BUTTONS: case DashboardRedux.DASHBOARD_ACTIVE_TAB_INDEX_CHANGE: case DashboardRedux.DASHBOARD_SET_IS_FLOATING: case DashboardRedux.DASHBOARD_SET_IS_HIDDEN: case DashboardRedux.DASHBOARD_SET_FLOATING_POSITION: case DashboardRedux.DASHBOARD_SET_TABS: case DashboardRedux.DASHBOARD_CLOSE_TOOLBAR: { const oldDashboardState = middlewareAPI.getState().Dashboard; let returnAction = next(action); const newDashboardState = middlewareAPI.getState().Dashboard; adaptable.api.eventApi.internalApi.fireDashboardChangedEvent(action.type, oldDashboardState, newDashboardState); return returnAction; } case LayoutRedux.LAYOUT_ADD: case LayoutRedux.LAYOUT_EDIT: case LayoutRedux.LAYOUT_SAVE: case LayoutRedux.LAYOUT_DELETE: case LayoutRedux.LAYOUT_SELECT: { const oldLayoutState = middlewareAPI.getState().Layout; const previousLayout = adaptable.api.layoutApi.getCurrentLayout(); let returnAction = next(action); const newLayoutState = middlewareAPI.getState().Layout; adaptable.api.eventApi.internalApi.fireLayoutChangedEvent(action.type, oldLayoutState, newLayoutState); const oldLayout = (oldLayoutState.Layouts || []).find((l) => l.Name == oldLayoutState.CurrentLayout) || ERROR_LAYOUT; const newLayout = (newLayoutState.Layouts || []).find((l) => l.Name == newLayoutState.CurrentLayout) || newLayoutState.Layouts[0] || ERROR_LAYOUT; if (returnAction.type == LayoutRedux.LAYOUT_SELECT || returnAction.type == LayoutRedux.LAYOUT_DELETE) { if (newLayout) { adaptable.updateLayoutInManagerAfterStoreHasChanged(newLayout); } } let refreshColumnFilters = false; if (adaptable.api.filterApi.columnFilterApi.internalApi.areColumnFiltersDifferent(oldLayout.ColumnFilters, newLayout.ColumnFilters)) { refreshColumnFilters = true; } let refreshGridFilter = false; if (adaptable.api.filterApi.gridFilterApi.internalApi.isGridFilterDifferent(oldLayout.GridFilter, newLayout.GridFilter)) { refreshGridFilter = true; } if (refreshColumnFilters || refreshGridFilter) { adaptable.applyFiltering({ updateColumnFilterModel: refreshColumnFilters }); } if (returnAction.type == LayoutRedux.LAYOUT_SAVE) { const savingLayout = returnAction.layout; if (previousLayout.Name === savingLayout.Name && previousLayout !== savingLayout && !adaptable.api.layoutApi.internalApi.areLayoutsEqual(previousLayout, savingLayout)) { adaptable.updateLayoutInManagerAfterStoreHasChanged(savingLayout); } } return returnAction; } case LayoutRedux.LAYOUT_COLUMN_FILTER_ADD: case LayoutRedux.LAYOUT_COLUMN_FILTER_EDIT: case LayoutRedux.LAYOUT_COLUMN_FILTER_SET: case LayoutRedux.LAYOUT_COLUMN_FILTER_CLEAR: case LayoutRedux.LAYOUT_COLUMN_FILTER_CLEAR_ALL: case LayoutRedux.LAYOUT_COLUMN_FILTER_SUSPEND: case LayoutRedux.LAYOUT_COLUMN_FILTER_SUSPEND_ALL: case LayoutRedux.LAYOUT_COLUMN_FILTER_UNSUSPEND: case LayoutRedux.LAYOUT_COLUMN_FILTER_UNSUSPEND_ALL: { let returnAction; const shouldTriggerColumnFiltering = adaptable.api.filterApi.columnFilterApi.internalApi.shouldNewColumnFilterTriggerColumnFiltering(action); const oldLayoutState = middlewareAPI.getState().Layout; returnAction = next(action); adaptable.silentUpdateCurrentLayoutModel(); if (shouldTriggerColumnFiltering) { setTimeout(() => { adaptable.applyF