"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AdaptableStore = exports.LoadState = exports.InitState = exports.LOAD_STATE = exports.INIT_STATE = void 0;
const tslib_1 = require("tslib");
const Redux = tslib_1.__importStar(require("redux"));
const Enums_1 = require("../../PredefinedConfig/Common/Enums");
const PluginsRedux = tslib_1.__importStar(require("../ActionsReducers/PluginsRedux"));
const PopupRedux = tslib_1.__importStar(require("../ActionsReducers/PopupRedux"));
const PopupRedux_1 = require("../ActionsReducers/PopupRedux");
const AdaptableReduxLocalStorageEngine_1 = require("./AdaptableReduxLocalStorageEngine");
const AdaptableReduxMerger_1 = require("./AdaptableReduxMerger");
const AdaptableAlert_1 = require("../../PredefinedConfig/Common/AdaptableAlert");
const ConfigConstants = tslib_1.__importStar(require("../../Utilities/Constants/ConfigConstants"));
const GeneralConstants_1 = require("../../Utilities/Constants/GeneralConstants");
const ModuleConstants = tslib_1.__importStar(require("../../Utilities/Constants/ModuleConstants"));
const Emitter_1 = tslib_1.__importDefault(require("../../Utilities/Emitter"));
const StringExtensions_1 = require("../../Utilities/Extensions/StringExtensions");
const PreviewHelper_1 = require("../../Utilities/Helpers/PreviewHelper");
const ObjectFactory_1 = require("../../Utilities/ObjectFactory");
const AdaptableToaster_1 = require("../../View/Components/Popups/AdaptableToaster");
const AlertRedux = tslib_1.__importStar(require("../ActionsReducers/AlertRedux"));
const ApplicationRedux = tslib_1.__importStar(require("../ActionsReducers/ApplicationRedux"));
const BulkUpdateRedux = tslib_1.__importStar(require("../ActionsReducers/BulkUpdateRedux"));
const CalculatedColumnRedux = tslib_1.__importStar(require("../ActionsReducers/CalculatedColumnRedux"));
const ChartingRedux = tslib_1.__importStar(require("../ActionsReducers/ChartingRedux"));
const CommentsRedux = tslib_1.__importStar(require("../ActionsReducers/CommentsRedux"));
const CustomSortRedux = tslib_1.__importStar(require("../ActionsReducers/CustomSortRedux"));
const DashboardRedux = tslib_1.__importStar(require("../ActionsReducers/DashboardRedux"));
const ExportRedux = tslib_1.__importStar(require("../ActionsReducers/ExportRedux"));
const FlashingCellRedux = tslib_1.__importStar(require("../ActionsReducers/FlashingCellRedux"));
const FormatColumnRedux = tslib_1.__importStar(require("../ActionsReducers/FormatColumnRedux"));
const FreeTextColumnRedux = tslib_1.__importStar(require("../ActionsReducers/FreeTextColumnRedux"));
const GridRedux = tslib_1.__importStar(require("../ActionsReducers/GridRedux"));
const LayoutRedux = tslib_1.__importStar(require("../ActionsReducers/LayoutRedux"));
const NamedQueryRedux = tslib_1.__importStar(require("../ActionsReducers/NamedQueryRedux"));
const NoteRedux = tslib_1.__importStar(require("../ActionsReducers/NoteRedux"));
const PlusMinusRedux = tslib_1.__importStar(require("../ActionsReducers/PlusMinusRedux"));
const QuickSearchRedux = tslib_1.__importStar(require("../ActionsReducers/QuickSearchRedux"));
const ScheduleRedux = tslib_1.__importStar(require("../ActionsReducers/ScheduleRedux"));
const ShortcutRedux = tslib_1.__importStar(require("../ActionsReducers/ShortcutRedux"));
const SmartEditRedux = tslib_1.__importStar(require("../ActionsReducers/SmartEditRedux"));
const StatusBarRedux = tslib_1.__importStar(require("../ActionsReducers/StatusBarRedux"));
const StyledColumnRedux = tslib_1.__importStar(require("../ActionsReducers/StyledColumnRedux"));
const SystemRedux = tslib_1.__importStar(require("../ActionsReducers/SystemRedux"));
const SystemRedux_1 = require("../ActionsReducers/SystemRedux");
const TeamSharingRedux = tslib_1.__importStar(require("../ActionsReducers/TeamSharingRedux"));
const ThemeRedux = tslib_1.__importStar(require("../ActionsReducers/ThemeRedux"));
const ToolPanelRedux = tslib_1.__importStar(require("../ActionsReducers/ToolPanelRedux"));
const TeamSharingState_1 = require("../../PredefinedConfig/TeamSharingState");
exports.INIT_STATE = 'INIT_STATE';
exports.LOAD_STATE = 'LOAD_STATE';
const NON_PERSIST_ACTIONS = {
    '@@INIT': true,
    '@@redux/init': true,
    [exports.LOAD_STATE]: true,
    [exports.INIT_STATE]: true,
    // progress indicators should NOT interfere with state management as it may lead to race conditions due to load/persist state being async
    [SystemRedux_1.SYSTEM_PROGRESS_INDICATOR_SHOW]: true,
    [SystemRedux_1.SYSTEM_PROGRESS_INDICATOR_HIDE]: true,
};
const InitState = () => ({
    type: exports.INIT_STATE,
});
exports.InitState = InitState;
const LoadState = (State) => ({
    type: exports.LOAD_STATE,
    State,
});
exports.LoadState = LoadState;
class AdaptableStore {
    /**
     *
     * @param adaptable The Adaptable instance
     * @param postLoadHook A function that hydrates the state after it has been loaded from storage
     */
    constructor(adaptable) {
        /*
          This is the main store for Adaptable State
        */
        this.loadStorageInProgress = false;
        this.loadStateOnStartup = true; // set to false if you want no state
        this.on = (eventName, callback) => {
            return this.emitter.on(eventName, callback);
        };
        this.onAny = (callback) => {
            return this.emitter.onAny(callback);
        };
        this.emit = (eventName, data) => {
            return this.emitter.emit(eventName, data);
        };
        this.loadStore = (config) => {
            const { adaptable, adaptableStateKey, predefinedConfig, postLoadHook } = config;
            const postProcessState = postLoadHook !== null && postLoadHook !== void 0 ? postLoadHook : ((state) => state);
            this.storageEngine.setStateKey(adaptableStateKey);
            // START STATE LOAD
            this.loadStorageInProgress = true;
            return (this.Load = this.storageEngine
                .load(predefinedConfig)
                .then((storedState) => {
                if (storedState && this.loadStateOnStartup) {
                    this.TheStore.dispatch((0, exports.LoadState)(postProcessState(adaptable.adaptableOptions.stateOptions.applyState(storedState))));
                }
            })
                .then(() => {
                this.TheStore.dispatch((0, exports.InitState)());
                // END STATE LOAD
                this.loadStorageInProgress = false;
            }, (e) => {
                adaptable.logger.consoleError('Failed to load previous Adaptable State : ', e);
                //for now i'm still initializing Adaptable even if loading state has failed....
                //we may revisit that later
                this.TheStore.dispatch((0, exports.InitState)());
                // END STATE LOAD
                this.loadStorageInProgress = false;
                this.TheStore.dispatch(PopupRedux.PopupShowAlert({
                    alertType: 'generic',
                    header: 'Configuration',
                    message: 'Error loading your configuration:' + e,
                    alertDefinition: ObjectFactory_1.ObjectFactory.CreateInternalAlertDefinitionForMessages('Error'),
                }));
            }));
        };
        let rootReducerObject = {
            //  Reducers for Non-Persisted State
            Grid: GridRedux.GridReducer,
            Popup: PopupRedux.PopupReducer,
            System: SystemRedux.SystemReducer,
            Plugins: PluginsRedux.PluginsReducer,
            Comment: CommentsRedux.CommentsReducer,
            // Reducers for Persisted State
            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,
            Schedule: ScheduleRedux.ScheduleReducer,
            Shortcut: ShortcutRedux.ShortcutReducer,
            StatusBar: StatusBarRedux.StatusBarReducer,
            StyledColumn: StyledColumnRedux.StyledColumnReducer,
            TeamSharing: TeamSharingRedux.TeamSharingReducer,
            Theme: ThemeRedux.ThemeReducer,
            ToolPanel: ToolPanelRedux.ToolPanelReducer,
        };
        // allow plugins to participate in the root reducer
        adaptable.forPlugins((plugin) => {
            if (plugin.rootReducer) {
                rootReducerObject = Object.assign(Object.assign({}, rootReducerObject), plugin.rootReducer(rootReducerObject));
            }
        });
        const initialRootReducer = Redux.combineReducers(rootReducerObject);
        const rootReducerWithResetManagement = (state, action) => {
            switch (action.type) {
                case exports.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_1.default();
        // If the user has remote storage set then we use Remote Engine, otherwise we use Local Enginge
        // not sure we can do this as we need to be backwardly compatible with existing users so need to stick with adaptable id (which should be unique)
        // const localStorageKey =  'adaptable-adaptable-state-' + adaptable.adaptableOptions.primaryKey;
        storageEngine = (0, AdaptableReduxLocalStorageEngine_1.createEngine)({
            adaptableId: adaptable.adaptableOptions.adaptableId,
            adaptableStateKey: adaptable.adaptableOptions.adaptableStateKey,
            userName: adaptable.adaptableOptions.userName,
            predefinedConfig: adaptable.adaptableOptions.predefinedConfig,
            loadState: adaptable.adaptableOptions.stateOptions.loadState,
            persistState: adaptable.adaptableOptions.stateOptions.persistState,
            debounceStateDelay: adaptable.adaptableOptions.stateOptions.debounceStateDelay,
        });
        const nonPersistentReduxKeys = [
            // Non Persisted State
            ConfigConstants.SYSTEM,
            ConfigConstants.GRID,
            ConfigConstants.POPUP,
            ConfigConstants.PLUGINS,
            ConfigConstants.COMMENT,
        ];
        const didPersistentStateChange = (state, newState) => {
            return Object.keys(newState).some((key) => {
                if (nonPersistentReduxKeys.includes(key)) {
                    return false;
                }
                return (state === null || state === void 0 ? void 0 : state[key]) !== (newState === null || newState === void 0 ? void 0 : newState[key]);
            });
        };
        // this is now VERY BADLY NAMED!
        let rootReducer = (0, AdaptableReduxMerger_1.mergeReducer)(rootReducerWithResetManagement, exports.LOAD_STATE);
        const composeEnhancers = (x) => x;
        const persistedReducer = (state, action) => {
            var _a, _b;
            if (adaptable.isDestroyed) {
                return state;
            }
            const init = state === undefined;
            const newState = rootReducer(state, action);
            // ideally the reducer should be pure,
            // but having the emitter emit the event here
            // is really useful
            const emitterArg = { action, state, newState };
            this.emitter.emit(action.type, emitterArg);
            const finalState = emitterArg.newState;
            const shouldPersist = state !== finalState &&
                didPersistentStateChange(state, finalState) &&
                !((_b = (_a = state === null || state === void 0 ? void 0 : state.System) === null || _a === void 0 ? void 0 : _a.License) === null || _b === void 0 ? void 0 : _b.disablePersistence) &&
                !NON_PERSIST_ACTIONS[action.type] &&
                !init &&
                !this.loadStorageInProgress;
            if (shouldPersist) {
                const storageState = Object.assign({}, finalState);
                nonPersistentReduxKeys.forEach((key) => {
                    delete storageState[key];
                });
                this.currentStorageState = storageState;
                storageEngine.save(storageState, adaptable.adaptableOptions.stateOptions.saveState);
            }
            return finalState;
        };
        const pluginsMiddleware = [];
        adaptable.forPlugins((plugin) => {
            if (plugin.reduxMiddleware) {
                pluginsMiddleware.push(plugin.reduxMiddleware(adaptable));
            }
        });
        //TODO: need to check if we want the storage to be done before or after
        //we enrich the state with the AB middleware
        this.TheStore = Redux.createStore(persistedReducer, composeEnhancers(Redux.applyMiddleware(adaptableMiddleware(adaptable), // the main middleware that actually does stuff,
        ...pluginsMiddleware // the plugins middleware
        )));
        this.storageEngine = storageEngine;
    }
    destroy() {
        var _a;
        (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.clearListeners();
        this.emitter = null;
    }
    getCurrentStorageState() {
        return this.currentStorageState;
    }
    saveStateNow(adaptable) {
        const storageState = this.getCurrentStorageState();
        if (storageState) {
            return this.storageEngine.saveNow(storageState, adaptable.adaptableOptions.stateOptions.saveState);
        }
        return Promise.resolve(true);
    }
}
exports.AdaptableStore = AdaptableStore;
// this is the main function for dealing with Redux Actions which require additional functionality to be triggered.
// Please document each use case where we have to use the Store rather than a module or a popup screen
// This should ideally be the ONLY place where we LISTEN to store changes
const adaptableMiddleware = (adaptable) => (function(middlewareAPI) {
    return function (next) {
        return function (action) {
            var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
            switch (action.type) {
                /*******************
                 * NAMED QUERY ACTIONS
                 *******************/
                /**
                 * Use Case: User has deleted a Named Query
                 * Action: Check whether it is referenced elsewhere before deleting
                 */
                case NamedQueryRedux.NAMED_QUERY_DELETE: {
                    const actionTyped = action;
                    // check if Named Query is not referenced elsewhere
                    const namedQueryReferences = adaptable.api.namedQueryApi.internalApi.getNamedQueryModuleReferences(actionTyped.namedQuery.Name);
                    if (namedQueryReferences.length) {
                        middlewareAPI.dispatch(PopupRedux.PopupShowAlert({
                            alertType: 'generic',
                            header: 'Named Query could not be deleted',
                            message: `It is still referenced in the following modules: ${namedQueryReferences.join(', ')}`,
                            alertDefinition: ObjectFactory_1.ObjectFactory.CreateInternalAlertDefinitionForMessages('Error'),
                        }));
                        return;
                    }
                    const ret = next(action);
                    return ret;
                }
                case NamedQueryRedux.NAMED_QUERY_EDIT: {
                    const actionTyped = action;
                    // check if name was changed, in which case we have to check if it's referenced
                    const editedNamedQuery = actionTyped.namedQuery;
                    const previousNamedQueryName = (_b = (_a = middlewareAPI
                        .getState()
                        .NamedQuery.NamedQueries.find((namedQuery) => namedQuery.Uuid === editedNamedQuery.Uuid)) === null || _a === void 0 ? void 0 : _a.Name) !== null && _b !== void 0 ? _b : '';
                    if (editedNamedQuery.Name !== previousNamedQueryName) {
                        // if query is referenced elsewhere we cannot allow the name change, as it would turn the referents invalid
                        const namedQueryReferences = adaptable.api.namedQueryApi.internalApi.getNamedQueryModuleReferences(previousNamedQueryName);
                        if (namedQueryReferences.length) {
                            middlewareAPI.dispatch(PopupRedux.PopupShowAlert({
                                alertType: 'generic',
                                header: 'Named Query could not be renamed',
                                message: `It is currently referenced in the following modules: ${namedQueryReferences.join(', ')}`,
                                alertDefinition: ObjectFactory_1.ObjectFactory.CreateInternalAlertDefinitionForMessages('Error'),
                            }));
                            return;
                        }
                    }
                    const ret = next(action);
                    return ret;
                }
                /*******************
                 * System Row Summary ACTIONS
                 *******************/
                case SystemRedux.SYSTEM_SUMMARY_ROW_SET: {
                    let nextAction = next(action);
                    adaptable.api.layoutApi.internalApi.setupRowSummaries();
                    return nextAction;
                }
                /*******************
                 * ALERT ACTIONS
                 *******************/
                case AlertRedux.ALERT_READY: {
                    // create observable alerts at grid startup
                    const reactiveAlertDefinitions = adaptable.api.alertApi.internalApi.getActiveReactiveAlertDefinitions();
                    reactiveAlertDefinitions.forEach((reactiveAlertDefinition) => adaptable.api.internalApi
                        .getAlertService()
                        .createReactiveAlert(reactiveAlertDefinition));
                    return;
                }
                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 (returnAction.type === AlertRedux.ALERT_DEFINITION_ADD ||
                        returnAction.type === AlertRedux.ALERT_DEFINITION_EDIT ||
                        returnAction.type === AlertRedux.ALERT_DEFINITION_UNSUSPEND) {
                        // in case of edit, the existing reactive alert will be deleted and recreated
                        adaptable.api.internalApi.getAlertService().createReactiveAlert(alertDefinition);
                    }
                    if (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);
                            }
                        });
                    }
                    // called also for rendered column actions, see RENDERED COLUMN ACTIONS block
                    adaptable.updateColumnModelAndRefreshGrid();
                    return returnAction;
                }
                /*******************
                 * Flashing Cell ACTIONS
                 *******************/
                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);
                    // called also for rendered column actions, see RENDERED COLUMN ACTIONS block
                    adaptable.updateColumnModelAndRefreshGrid();
                    return returnAction;
                }
                /*******************
                 * Alert ACTIONS
                 *******************/
                /**
                 * Use Case: User has deleted a System Alert which has a Highlight Cell
                 * Action: Refresh the cell (to clear the style)
                 */
                case SystemRedux.SYSTEM_ALERT_DELETE: {
                    const actionTyped = action;
                    let ret = next(action);
                    const adaptableAlert = actionTyped.alert;
                    if (((_c = adaptableAlert.alertDefinition.AlertProperties) === null || _c === void 0 ? void 0 : _c.HighlightCell) &&
                        (0, AdaptableAlert_1.isAdaptableCellChangedAlert)(adaptableAlert) &&
                        adaptableAlert.cellDataChangedInfo) {
                        const rowNode = adaptableAlert.cellDataChangedInfo.rowNode;
                        adaptable.refreshCells([rowNode], [adaptableAlert.cellDataChangedInfo.column.columnId], true);
                    }
                    if (((_d = adaptableAlert.alertDefinition.AlertProperties) === null || _d === void 0 ? void 0 : _d.HighlightRow) &&
                        (0, AdaptableAlert_1.isAdaptableRowChangedAlert)(adaptableAlert) &&
                        adaptableAlert.gridDataChangedInfo) {
                        adaptable.api.gridApi.refreshRowNodes(adaptableAlert.gridDataChangedInfo.rowNodes);
                    }
                    return ret;
                }
                /**
                 * Use Case: User has deleted all System Alerts some of which have a Highlight Cell
                 * Action: Refresh the cell (to clear the style)
                 */
                case SystemRedux.SYSTEM_ALERT_DELETE_ALL: {
                    const actionTyped = action;
                    let ret = next(action);
                    let alerts = actionTyped.alerts;
                    alerts.forEach((alert) => {
                        var _a, _b;
                        if (((_a = alert.alertDefinition.AlertProperties) === null || _a === void 0 ? void 0 : _a.HighlightCell) &&
                            (0, AdaptableAlert_1.isAdaptableCellChangedAlert)(alert) &&
                            alert.cellDataChangedInfo) {
                            let rowNode = alert.cellDataChangedInfo.rowNode;
                            adaptable.refreshCells([rowNode], [alert.cellDataChangedInfo.column.columnId], true);
                        }
                        if (((_b = alert.alertDefinition.AlertProperties) === null || _b === void 0 ? void 0 : _b.HighlightRow) &&
                            (0, AdaptableAlert_1.isAdaptableRowChangedAlert)(alert) &&
                            alert.gridDataChangedInfo) {
                            adaptable.api.gridApi.refreshRowNodes(alert.gridDataChangedInfo.rowNodes);
                        }
                    });
                    return ret;
                }
                /**
                 * Use Case: A System Alert had a Highlight Cell with a limited duration
                 * Action: Refresh the cell (to clear the style)
                 */
                case SystemRedux.SYSTEM_ALERT_REMOVE_CELL_HIGHLIGHT: {
                    let ret = next(action);
                    const actionTyped = action;
                    const adaptableAlert = actionTyped.alert;
                    if ((_e = adaptableAlert.alertDefinition.AlertProperties) === null || _e === void 0 ? void 0 : _e.HighlightCell) {
                        if ((0, AdaptableAlert_1.isAdaptableCellChangedAlert)(adaptableAlert) &&
                            adaptableAlert.cellDataChangedInfo) {
                            const rowNode = adaptableAlert.cellDataChangedInfo.rowNode;
                            adaptable.refreshCells([rowNode], [adaptableAlert.cellDataChangedInfo.column.columnId], true);
                        }
                    }
                    return ret;
                }
                case SystemRedux.SYSTEM_ALERT_REMOVE_ROW_HIGHLIGHT: {
                    let ret = next(action);
                    const actionTyped = action;
                    const adaptableAlert = actionTyped.alert;
                    if ((_f = adaptableAlert.alertDefinition.AlertProperties) === null || _f === void 0 ? void 0 : _f.HighlightRow) {
                        if ((0, AdaptableAlert_1.isAdaptableCellChangedAlert)(adaptableAlert) &&
                            adaptableAlert.cellDataChangedInfo) {
                            adaptable.api.gridApi.refreshRowNodes([adaptableAlert.cellDataChangedInfo.rowNode]);
                        }
                        if ((0, AdaptableAlert_1.isAdaptableRowChangedAlert)(adaptableAlert) &&
                            adaptableAlert.gridDataChangedInfo) {
                            adaptable.api.gridApi.refreshRowNodes(adaptableAlert.gridDataChangedInfo.rowNodes);
                        }
                    }
                    return ret;
                }
                case SystemRedux.SYSTEM_HIGHLIGHT_CELL_ADD: {
                    const actionTyped = action;
                    const ret = next(action);
                    const cellHighlightInfo = actionTyped.cellHighlightInfo;
                    const rowNode = adaptable.getRowNodeForPrimaryKey(cellHighlightInfo.primaryKeyValue);
                    if (rowNode) {
                        adaptable.refreshCells([rowNode], [cellHighlightInfo.columnId], true);
                    }
                    return ret;
                }
                case SystemRedux.SYSTEM_HIGHLIGHT_CELL_DELETE: {
                    const actionTyped = action;
                    const ret = next(action);
                    const rowNode = adaptable.getRowNodeForPrimaryKey(actionTyped.primaryKeyValue);
                    if (rowNode) {
                        adaptable.refreshCells([rowNode], [actionTyped.columnId], true);
                    }
                    return ret;
                }
                case SystemRedux.SYSTEM_HIGHLIGHT_CELL_DELETE_ALL: {
                    const cellHighlightInfos = middlewareAPI.getState().System.HighlightedCells;
                    const ret = next(action);
                    cellHighlightInfos.forEach((cellHighlightInfo) => {
                        const rowNode = adaptable.getRowNodeForPrimaryKey(cellHighlightInfo.primaryKeyValue);
                        if (!rowNode) {
                            return;
                        }
                        adaptable.refreshCells([rowNode], [cellHighlightInfo.columnId], true);
                    });
                    return ret;
                }
                case SystemRedux.SYSTEM_HIGHLIGHT_ROW_ADD: {
                    const actionTyped = action;
                    const ret = next(action);
                    const rowHighlightInfo = actionTyped.rowHighlightInfo;
                    const rowNode = adaptable.getRowNodeForPrimaryKey(rowHighlightInfo.primaryKeyValue);
                    if (rowNode) {
                        adaptable.api.gridApi.refreshRowNode(rowNode);
                    }
                    return ret;
                }
                case SystemRedux.SYSTEM_HIGHLIGHT_ROW_DELETE: {
                    const actionTyped = action;
                    const ret = next(action);
                    const rowNode = adaptable.getRowNodeForPrimaryKey(actionTyped.primaryKeyValue);
                    if (rowNode) {
                        adaptable.api.gridApi.refreshRowNode(rowNode);
                    }
                    return ret;
                }
                case SystemRedux.SYSTEM_HIGHLIGHT_ROWS_ADD: {
                    const actionTyped = action;
                    const ret = next(action);
                    const rowsHighlightInfo = actionTyped.rowsHighlightInfo;
                    const rowNodes = adaptable.getRowNodesForPrimaryKeys(rowsHighlightInfo.primaryKeyValues);
                    if (rowNodes.length) {
                        adaptable.api.gridApi.refreshRowNodes(rowNodes);
                    }
                    return ret;
                }
                case SystemRedux.SYSTEM_HIGHLIGHT_ROWS_DELETE: {
                    const actionTyped = action;
                    const ret = next(action);
                    const rowNodes = adaptable.getRowNodesForPrimaryKeys(actionTyped.primaryKeyValues);
                    if (rowNodes.length) {
                        adaptable.api.gridApi.refreshRowNodes(rowNodes);
                    }
                    return ret;
                }
                case SystemRedux.SYSTEM_HIGHLIGHT_CELL_DELETE_ALL: {
                    const rowHighlightInfos = middlewareAPI.getState().System.HighlightedRows;
                    const ret = next(action);
                    rowHighlightInfos.forEach((rowHighlightInfo) => {
                        const rowNode = adaptable.getRowNodeForPrimaryKey(rowHighlightInfo.primaryKeyValue);
                        if (!rowNode) {
                            return;
                        }
                        adaptable.api.gridApi.refreshRowNode(rowNode);
                    });
                    return ret;
                }
                /*******************
                 * SPECIAL COLUMN ACTIONS
                 *******************/
                /**
                 * Use Case: We have added / edited / deleted a Special Column (i.e. one not in initial ColumnDefs)
                 * Action: We update the Special ColumnDefs
                 */
                case FreeTextColumnRedux.FREE_TEXT_COLUMN_ADD:
                case FreeTextColumnRedux.FREE_TEXT_COLUMN_EDIT:
                case CalculatedColumnRedux.CALCULATED_COLUMN_ADD:
                case CalculatedColumnRedux.CALCULATED_COLUMN_EDIT: {
                    const actionTyped = action;
                    const returnAction = next(actionTyped);
                    if (returnAction.type === CalculatedColumnRedux.CALCULATED_COLUMN_ADD ||
                        returnAction.type === CalculatedColumnRedux.CALCULATED_COLUMN_EDIT) {
                        adaptable.api.internalApi
                            .getCalculatedColumnExpressionService()
                            .createAggregatedScalarLiveValue(returnAction.calculatedColumn);
                        adaptable.api.calculatedColumnApi.internalApi.fireCalculatedColumnChangedEvent(action.type, actionTyped.calculatedColumn);
                    }
                    adaptable.updateColumnModelAndRefreshGrid();
                    return returnAction;
                }
                /**
                 * Use Case: We have deleted a CalculatedColumn (i.e. one not in initial ColumnDefs)
                 * Action: Check whether it is referenced elsewhere before deleting then update the Special ColumnDefs
                 */
                case CalculatedColumnRedux.CALCULATED_COLUMN_DELETE: {
                    const actionTyped = action;
                    // check if CalculatedColumn is not referenced elsewhere
                    const calculatedColumnReferences = adaptable.api.calculatedColumnApi.internalApi.getCalculatedColumnModuleReferences(actionTyped.calculatedColumn);
                    if (calculatedColumnReferences.length) {
                        middlewareAPI.dispatch(PopupRedux.PopupShowAlert({
                            alertType: 'generic',
                            header: 'CalculatedColumn could not be deleted',
                            message: `It is still referenced in the following modules: ${calculatedColumnReferences.join(', ')}`,
                            alertDefinition: ObjectFactory_1.ObjectFactory.CreateInternalAlertDefinitionForMessages('Error'),
                        }));
                        return;
                    }
                    const returnAction = next(actionTyped);
                    adaptable.api.internalApi
                        .getCalculatedColumnExpressionService()
                        .destroyAggregatedScalarLiveValue(returnAction.calculatedColumn);
                    adaptable.api.calculatedColumnApi.internalApi.fireCalculatedColumnChangedEvent(action.type, actionTyped.calculatedColumn);
                    adaptable.updateColumnModelAndRefreshGrid();
                    return returnAction;
                }
                /**
                 * Use Case: We have deleted a FreeText Column (i.e. one not in initial ColumnDefs)
                 * Action: Check whether it is referenced elsewhere before deleting then update the Special ColumnDefs
                 */
                case FreeTextColumnRedux.FREE_TEXT_COLUMN_DELETE: {
                    const actionTyped = action;
                    // check if FreeTextColumn is not referenced elsewhere
                    const freeTextColumnReferences = adaptable.api.freeTextColumnApi.internalApi.getFreeTextColumnModuleReferences(actionTyped.freeTextColumn);
                    if (freeTextColumnReferences.length) {
                        middlewareAPI.dispatch(PopupRedux.PopupShowAlert({
                            alertType: 'generic',
                            header: 'FreeTextColumn could not be deleted',
                            message: `It is still referenced in the following modules: ${freeTextColumnReferences.join(', ')}`,
                            alertDefinition: ObjectFactory_1.ObjectFactory.CreateInternalAlertDefinitionForMessages('Error'),
                        }));
                        return;
                    }
                    const returnAction = next(action);
                    adaptable.updateColumnModelAndRefreshGrid();
                    return returnAction;
                }
                /*******************
                 * RENDERED COLUMN ACTIONS
                 *******************/
                /**
                 * Use Case: We have updated an AdapTable Module that affects rendering
                 * Action: We set up all columns again
                 */
                case QuickSearchRedux.QUICK_SEARCH_SET_STYLE:
                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);
                    // called also for alert actions, see ALERT ACTIONS block
                    adaptable.updateColumnModelAndRefreshGrid();
                    return returnAction;
                }
                /*******************
                 * QUICK SEARCH ACTIONS
                 *******************/
                /**
                 * Use Case: User has run a Quick Search
                 * Action1: Call Adaptable to redraw body so cells can be highlighted
                 * Action2: Run Query using Quick Search text
                 */
                case QuickSearchRedux.QUICK_SEARCH_RUN: {
                    let returnAction = next(action);
                    adaptable.refreshAllCells(true);
                    // if set then return a query on the text
                    if (adaptable.adaptableOptions.quickSearchOptions.filterResultsAfterQuickSearch) {
                        const actionTyped = action;
                        const searchText = actionTyped.quickSearchText;
                        if (StringExtensions_1.StringExtensions.IsNotNullOrEmpty(searchText)) {
                            adaptable.setAgGridQuickSearch(searchText);
                        }
                        else {
                            adaptable.setAgGridQuickSearch('');
                        }
                    }
                    return returnAction;
                }
                /*******************
                 * System FILTER ACTIONS
                 *******************/
                case SystemRedux.SYSTEM_QUICK_FILTER_BAR_SHOW: {
                    adaptable.showQuickFilter();
                    return next(action);
                }
                case SystemRedux.SYSTEM_QUICK_FILTER_BAR_HIDE: {
                    adaptable.hideQuickFilter();
                    return next(action);
                }
                case SystemRedux.SYSTEM_FILTER_FORM_HIDE: {
                    adaptable.api.gridApi.hideFilterForm();
                    return next(action);
                }
                /**
                 * Use Case: Column Filters have changed
                 * Action: Apply Column Filtering and fire associated events
                 */
                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;
                    // needs to be called before 'next' so previous and next column filters are known
                    const shouldTriggerColumnFiltering = adaptable.api.columnFilterApi.internalApi.shouldNewColumnFilterTriggerColumnFiltering(action);
                    const currentLayout = adaptable.api.layoutApi.getCurrentLayout();
                    if (((_g = adaptable.adaptableOptions.layoutOptions) === null || _g === void 0 ? void 0 : _g.autoSaveLayouts) &&
                        !currentLayout.IsReadOnly) {
                        returnAction = next(action);
                    }
                    else {
                        if (!middlewareAPI.getState().Grid.CurrentLayout) {
                            const currentLayout = adaptable.api.layoutApi.getCurrentLayout();
                            middlewareAPI.dispatch(GridRedux.LayoutUpdateCurrentDraft(currentLayout));
                        }
                        // we have a layout draft, we have to update that state
                        let draftLayoutAction;
                        switch (action.type) {
                            case LayoutRedux.LAYOUT_COLUMN_FILTER_ADD:
                                draftLayoutAction = GridRedux.LayoutDraftColumnFilterAdd(action.columnFilter);
                                break;
                            case LayoutRedux.LAYOUT_COLUMN_FILTER_EDIT:
                                draftLayoutAction = GridRedux.LayoutDraftColumnFilterEdit(action.columnFilter);
                                break;
                            case LayoutRedux.LAYOUT_COLUMN_FILTER_SET:
                                draftLayoutAction = GridRedux.LayoutDraftColumnFilterSet(action.columnFilter);
                                break;
                            case LayoutRedux.LAYOUT_COLUMN_FILTER_CLEAR:
                                draftLayoutAction = GridRedux.LayoutDraftColumnFilterClear(action.columnFilter);
                                break;
                            case LayoutRedux.LAYOUT_COLUMN_FILTER_CLEAR_ALL:
                                draftLayoutAction = GridRedux.LayoutDraftColumnFilterClearAll();
                                break;
                            case LayoutRedux.LAYOUT_COLUMN_FILTER_SUSPEND:
                                draftLayoutAction = GridRedux.LayoutDraftColumnFilterSuspend(action.columnFilter);
                                break;
                            case LayoutRedux.LAYOUT_COLUMN_FILTER_SUSPEND_ALL:
                                draftLayoutAction = GridRedux.LayoutDraftColumnFilterSuspendAll();
                                break;
                            case LayoutRedux.LAYOUT_COLUMN_FILTER_UNSUSPEND:
                                draftLayoutAction = GridRedux.LayoutDraftColumnFilterUnsuspend(action.columnFilter);
                                break;
                            case LayoutRedux.LAYOUT_COLUMN_FILTER_UNSUSPEND_ALL:
                                draftLayoutAction = GridRedux.LayoutDraftColumnFilterUnsuspendAll();
                                break;
                        }
                        returnAction = next(draftLayoutAction);
                    }
                    setTimeout(() => {
                        if (shouldTriggerColumnFiltering) {
                            adaptable.applyColumnFiltering();
                        }
                    }, 5);
                    adaptable.api.columnFilterApi.internalApi.fireColumnFilterAppliedEvent();
                    adaptable.api.layoutApi.internalApi.fireLayoutChangedEvent(action.type, null, middlewareAPI.getState().Layout);
                    return returnAction;
                }
                /**
                 * Use Case: Grid Filter has changed
                 * Action: Apply Grid Filtering and fire associated events
                 */
                case LayoutRedux.LAYOUT_GRID_FILTER_SET:
                case LayoutRedux.LAYOUT_GRID_FILTER_CLEAR:
                case LayoutRedux.LAYOUT_GRID_FILTER_SUSPEND:
                case LayoutRedux.LAYOUT_GRID_FILTER_UNSUSPEND: {
                    const currentLayout = adaptable.api.layoutApi.getCurrentLayout();
                    let returnAction;
                    // - calls GridFilterApplied - examples
                    adaptable.api.gridFilterApi.internalApi.fireGridFilterAppliedEvent();
                    // - layout change event, row summary is triggered
                    adaptable.api.layoutApi.internalApi.fireLayoutChangedEvent(action.type, null, middlewareAPI.getState().Layout);
                    setTimeout(() => {
                        // - agGridApi.onFilterChanged
                        // - internal 'AdapTableFiltersApplied' event - evaluates row summary
                        // - refresh selected cells and rows
                        adaptable.applyGridFiltering();
                    }, 5);
                    if (((_h = adaptable.adaptableOptions.layoutOptions) === null || _h === void 0 ? void 0 : _h.autoSaveLayouts) &&
                        !currentLayout.IsReadOnly) {
                        return next(action);
                    }
                    else {
                        if (!middlewareAPI.getState().Grid.CurrentLayout) {
                            const currentLayout = adaptable.api.layoutApi.getCurrentLayout();
                            middlewareAPI.dispatch(GridRedux.LayoutUpdateCurrentDraft(currentLayout));
                        }
                        switch (action.type) {
                            case LayoutRedux.LAYOUT_GRID_FILTER_SET:
                                returnAction = GridRedux.LayoutDraftGridFilterSet(Object.assign(Object.assign({}, currentLayout.GridFilter), { Expression: action.gridFilter }));
                                break;
                            case LayoutRedux.LAYOUT_GRID_FILTER_CLEAR:
                                returnAction = GridRedux.LayoutDraftGridFilterSet(null);
                                break;
                            case LayoutRedux.LAYOUT_GRID_FILTER_SUSPEND:
                                returnAction = GridRedux.LayoutDraftGridFilterSet(Object.assign(Object.assign({}, currentLayout.GridFilter), { IsSuspended: true }));
                                break;
                            case LayoutRedux.LAYOUT_GRID_FILTER_UNSUSPEND:
                                returnAction = GridRedux.LayoutDraftGridFilterSet(Object.assign(Object.assign({}, currentLayout.GridFilter), { IsSuspended: false }));
                                break;
                        }
                    }
                    return next(returnAction);
                }
                /*******************
                 * DATA SOURCE ACTIONS
                 *******************/
                /**
                 * Use Case: Data Sources have been amended
                 * Action: Fire Data Source Changed event
                 */
                case SystemRedux.SYSTEM_DATA_SET_SELECT: {
                    let returnAction = next(action);
                    const dataSet = adaptable.api.dataSetApi.getCurrentDataSet();
                    adaptable.api.dataSetApi.internalApi.fireDataSetSelectedEvent(dataSet);
                    requestAnimationFrame(() => {
                        if (dataSet.form) {
                            middlewareAPI.dispatch((0, PopupRedux_1.PopupShowForm)({
                                Id: 'data-set-form',
                                Form: dataSet.form,
                                prepareContext: (context) => {
                                    return new Promise((resolve) => {
                                        const preparedContext = Object.assign(Object.assign({}, context), { dataSet });
                                        resolve(preparedContext);
                                    });
                                },
                            }));
                        }
                    });
                    return returnAction;
                }
                /*******************
                 * THEME ACTIONS
                 *******************/
                /**
                 * Use Case: Theme has changed
                 * Action: Apply new Theme
                 */
                case ThemeRedux.THEME_SELECT: {
                    let returnAction = next(action);
                    adaptable.api.themeApi.applyCurrentTheme();
                    return returnAction;
                }
                /*******************
                 * Note ACTIONS
                 *******************/
                /**
                 * Use Case: Note has been edited/deleted/added
                 * Action: Triangle can be removed/added
                 */
                case NoteRedux.NOTE_ADD:
                case NoteRedux.NOTE_EDIT:
                case NoteRedux.NOTE_DELETE: {
                    let returnAction = next(action);
                    const node = adaptable.api.gridApi.getRowNodeForPrimaryKey(returnAction.adaptableNote.PrimaryKeyValue);
                    adaptable.refreshCells([node], [returnAction.adaptableNote.ColumnId], true);
                    return returnAction;
                }
                /*******************
                 * Comment ACTIONS
                 *******************/
                /**
                 * Use Case: Comments has been edited/deleted/added
                 * Action: Triangle can be removed/added
                 */
                case CommentsRedux.COMMENTS_ADD:
                case CommentsRedux.COMMENTS_EDIT:
                case CommentsRedux.COMMENTS_DELETE:
                case CommentsRedux.COMMENTS_CELL_DELETE:
                case CommentsRedux.COMMENTS_CELL_ADD: {
                    let returnAction = next(action);
                    let node = null;
                    let columnId = null;
                    if ('cellAddress' in returnAction) {
                        node = adaptable.api.gridApi.getRowNodeForPrimaryKey(returnAction.cellAddress.PrimaryKeyValue);
                        columnId = returnAction.cellAddress.ColumnId;
                    }
                    else if ('cellComments' in returnAction) {
                        node = adaptable.api.gridApi.getRowNodeForPrimaryKey(returnAction.cellComments.PrimaryKeyValue);
                        columnId = returnAction.cellComments.ColumnId;
                    }
                    if (node && columnId) {
                        adaptable.refreshCells([node], [columnId], true);
                        requestAnimationFrame(() => {
                            var _a, _b;
                            const commentThreads = adaptable.api.commentApi.getGridComments();
                            adaptable.api.eventApi.emit('CommentChanged', adaptable.api.commentApi.getGridComments());
                            (_b = (_a = adaptable.api.optionsApi
                                .getCommentOptions()) === null || _a === void 0 ? void 0 : _a.persistCommentThreads) === null || _b === void 0 ? void 0 : _b.call(_a, commentThreads);
                        });
                    }
                    return returnAction;
                }
                case CommentsRedux.COMMENTS_LOAD: {
                    const previousCommentThreads = adaptable.api.commentApi.getGridComments();
                    let returnAction = next(action);
                    const newCommentThreads = (_j = adaptable.api.commentApi.getGridComments()) !== null && _j !== void 0 ? _j : [];
                    requestAnimationFrame(() => {
                        let addedCommentThreads = [];
                        let deletedCommentThreads = [];
                        const prevCommentThreadsSet = new Set((previousCommentThreads !== null && previousCommentThreads !== void 0 ? previousCommentThreads : []).map((c) => c.Uuid));
                        const newCommentThreadsSet = new Set((newCommentThreads !== null && newCommentThreads !== void 0 ? 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);
                            }
                        }
                        // This cannot be called because it would trigger an infinite loop
                        // adaptable.api.eventApi.emit('CommentChanged', commentThreads);
                        // refresh all comments cells to show or hide the triangle
                        [...addedCommentThreads, ...deletedCommentThreads].forEach((commentThread) => {
                            const node = adaptable.api.gridApi.getRowNodeForPrimaryKey(commentThread.PrimaryKeyValue);
                            if (node && commentThread.ColumnId) {
                                adaptable.refreshCells([node], [commentThread.ColumnId], true);
                            }
                        });
                    });
                    return returnAction;
                }
                /*******************
                 * SCHEDULE ACTIONS
                 *******************/
                /**
                 * Use Case: Schedule has changed
                 * Action: Set up ALL jobs
                 */
                case ScheduleRedux.REMINDER_SCHEDULE_ADD:
                case ScheduleRedux.REMINDER_SCHEDULE_EDIT:
                case ScheduleRedux.REMINDER_SCHEDULE_DELETE:
                case ScheduleRedux.REMINDER_SCHEDULE_UNSUSPEND:
                case ScheduleRedux.REMINDER_SCHEDULE_SUSPEND:
                case ScheduleRedux.REMINDER_SCHEDULE_UNSUSPEND_ALL:
                case ScheduleRedux.REMINDER_SCHEDULE_SUSPEND_ALL:
                case ScheduleRedux.REPORT_SCHEDULE_ADD:
                case ScheduleRedux.REPORT_SCHEDULE_EDIT:
                case ScheduleRedux.REPORT_SCHEDULE_DELETE:
                case ScheduleRedux.REPORT_SCHEDULE_SUSPEND:
                case ScheduleRedux.REPORT_SCHEDULE_UNSUSPEND:
                case ScheduleRedux.REPORT_SCHEDULE_SUSPEND_ALL:
                case ScheduleRedux.REPORT_SCHEDULE_UNSUSPEND_ALL:
                case ScheduleRedux.IPUSHPULL_SCHEDULE_ADD:
                case ScheduleRedux.IPUSHPULL_SCHEDULE_EDIT:
                case ScheduleRedux.IPUSHPULL_SCHEDULE_DELETE:
                case ScheduleRedux.IPUSHPULL_SCHEDULE_SUSPEND:
                case ScheduleRedux.IPUSHPULL_SCHEDULE_UNSUSPEND:
                case ScheduleRedux.IPUSHPULL_SCHEDULE_SUSPEND_ALL:
                case ScheduleRedux.IPUSHPULL_SCHEDULE_UNSUSPEND_ALL:
                case ScheduleRedux.OPENFIN_SCHEDULE_ADD:
                case ScheduleRedux.OPENFIN_SCHEDULE_EDIT:
                case ScheduleRedux.OPENFIN_SCHEDULE_DELETE:
                case ScheduleRedux.OPENFIN_SCHEDULE_SUSPEND:
                case ScheduleRedux.OPENFIN_SCHEDULE_UNSUSPEND:
                case ScheduleRedux.OPENFIN_SCHEDULE_SUSPEND_ALL:
                case ScheduleRedux.OPENFIN_SCHEDULE_UNSUSPEND_ALL: {
                    let returnAction = next(action);
                    let module = (adaptable.adaptableModules.get(ModuleConstants.ScheduleModuleId));
                    module.setUpScheduleJobs();
                    return returnAction;
                }
                /*******************
                 * DASHBOARD ACTIONS
                 *******************/
                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_INLINE:
                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.dashboardApi.internalApi.fireDashboardChangedEvent(action.type, oldDashboardState, newDashboardState);
                    return returnAction;
                }
                /*******************
                 * LAYOUT ACTIONS
                 *******************/
                case LayoutRedux.LAYOUT_ADD:
                case LayoutRedux.LAYOUT_EDIT:
                case LayoutRedux.LAYOUT_SAVE:
                case LayoutRedux.LAYOUT_DELETE:
                case LayoutRedux.LAYOUT_COLUMN_SET_CAPTION:
                case LayoutRedux.LAYOUT_SELECT: {
                    const oldLayoutState = middlewareAPI.getState().Layout;
                    // this must be called before 'next(action)'
                    const previousLayout = adaptable.api.layoutApi.getCurrentLayout();
                    let returnAction = next(action);
                    const newLayoutState = middlewareAPI.getState().Layout;
                    adaptable.api.layoutApi.internalApi.fireLayoutChangedEvent(action.type, oldLayoutState, newLayoutState);
                    // Tell Grid to apply column filtering if filters have been changed in a loaded Layout
                    const oldFilters = (_k = oldLayoutState.Layouts.find((l) => l.Name == oldLayoutState.CurrentLayout)) === null || _k === void 0 ? void 0 : _k.ColumnFilters;
                    const newFilters = (_l = newLayoutState.Layouts.find((l) => l.Name == newLayoutState.CurrentLayout)) === null || _l === void 0 ? void 0 : _l.ColumnFilters;
                    if (adaptable.api.columnFilterApi.internalApi.areColumnFiltersDifferent(oldFilters, newFilters)) {
                        adaptable.applyColumnFiltering();
                    }
                    // Tell Grid to apply filtering if filters have been changed in a loaded Layout
                    const oldGridFilter = (_m = oldLayoutState.Layouts.find((l) => l.Name == oldLayoutState.CurrentLayout)) === null || _m === void 0 ? void 0 : _m.GridFilter;
                    const newGridFilter = (_o = newLayoutState.Layouts.find((l) => l.Name == newLayoutState.CurrentLayout)) === null || _o === void 0 ? void 0 : _o.GridFilter;
                    if (adaptable.api.gridFilterApi.internalApi.isGridFilterDifferent(oldGridFilter, newGridFilter)) {
                        adaptable.applyGridFiltering();
                    }
                    if (returnAction.type == LayoutRedux.LAYOUT_SELECT ||
                        returnAction.type == LayoutRedux.LAYOUT_DELETE ||
                        returnAction.type == LayoutRedux.LAYOUT_COLUMN_SET_CAPTION) {
                        let gridState = middlewareAPI.getState().Grid;
                        let currentLayout = newLayoutState.Layouts.find((l) => l.Name == newLayoutState.CurrentLayout);
                        if (gridState.CurrentLayout) {
                            currentLayout = gridState.CurrentLayout;
                        }
                        if (currentLayout) {
                            // tell grid the layout has been selected
                            adaptable.setLayout(currentLayout);
                        }
                        if (!((_p = adaptable.adaptableOptions.layoutOptions) === null || _p === void 0 ? void 0 : _p.autoSaveLayouts)) {
                            middlewareAPI.dispatch(GridRedux.LayoutUpdateCurrentDraft(currentLayout));
                        }
                    }
                    if (returnAction.type == LayoutRedux.LAYOUT_ADD ||
                        returnAction.type == LayoutRedux.LAYOUT_SAVE) {
                        const actionTyped = action;
                        // if autosave is false
                        if (!((_q = adaptable.adaptableOptions.layoutOptions) === null || _q === void 0 ? void 0 : _q.autoSaveLayouts)) {
                            // and the current layout is saved, make sure we also update the draft
                            if (actionTyped.layout.Name === newLayoutState.CurrentLayout) {
                                middlewareAPI.dispatch(GridRedux.LayoutUpdateCurrentDraft(actionTyped.layout));
                            }
                        }
                    }
                    // when changing current layout via the api, the layout should update
                    if (returnAction.type == LayoutRedux.LAYOUT_SAVE) {
                        const savingLayout = returnAction.layout;
                        if (previousLayout.Name === savingLayout.Name &&
                            previousLayout !== savingLayout &&
                            // objects may have changed, but have the same contents
                            // this prevents pivot layout from infinite set-layout
                            !adaptable.api.layoutApi.internalApi.areLayoutsEqual(previousLayout, savingLayout)) {
                            adaptable.setLayout(savingLayout);
                        }
                    }
                    adaptable.refreshQuickFilter();
                    return returnAction;
                }
                /*******************
                 * SMART EDIT ACTIONS
                 *******************/
                /**
                 * Use Case: User wants to perform Smart Edit and we need to check if the cell selection is valid
                 * Action (1):  Get the result from the SmartEdit module
                 * If the return is an Alert:
                 * Action (2): If there is a popup open, close it and show the Alert; otherwise just set false valid selection
                 * If the return is valid:
                 * Action (2): Set the valid selection to true
                 * Action (3): Build the Preview Values (via Smart Edit module)
                 * Action (4): Set the Preview Values (this will populate the preview screen)
                 */
                case SystemRedux.SYSTEM_SMARTEDIT_CHECK_CELL_SELECTION: {
                    let module = (adaptable.adaptableModules.get(ModuleConstants.SmartEditModuleId));
                    let state = middlewareAPI.getState();
                    let returnAction = next(action);
                    let apiReturn = module.CheckCorrectCellSelection();
                    let popup = state.Popup.ScreenPopup;
                    // this is a horrible hack and fix for a weird issue
                    // we really need to do smart edit and bulk update better
                    // but this fixes it for now
                    if (popup.ComponentName != 'LayoutPopup') {
                        if (apiReturn.Alert) {
                            // check if Smart Edit is showing as popup and then close and show error (dont want to do that if from toolbar)
                            if (popup.ComponentName == 'SmartEditPopup') {
                                // We are in SmartEditPopup so let's close it
                                middlewareAPI.dispatch(PopupRedux.PopupHideScreen());
                                // and now show the alert Popup
                                middlewareAPI.dispatch(PopupRedux.PopupShowAlert(apiReturn.Alert));
                            }
                            middlewareAPI.dispatch(SystemRedux.SmartEditSetValidSelection(false));
                        }
                        else {
                            middlewareAPI.dispatch(SystemRedux.SmartEditSetValidSelection(true));
                            let apiPreviewReturn = module.BuildPreviewValues(state.System.SmartEditValue, state.System.SmartEditOperation);
                            middlewareAPI.dispatch(SystemRedux.SmartEditSetPreview(apiPreviewReturn));
                        }
                    }
                    return returnAction;
                }
                /**
                 * Use Case: User has changed a Smmart Edit property or requested a preview
                 * Action (1):  Get the new preview set from the Smart Edit module
                 * Action (2):  Set the Preview Values (this will populate the preview screen)
                 */
                case SystemRedux.SYSTEM_SMART_EDIT_CHANGE_OPERATION:
                case SystemRedux.SYSTEM_SMART_EDIT_CHANGE_VALUE:
                case SystemRedux.SYSTEM_SMARTEDIT_FETCH_PREVIEW: {
                    //all our logic needs to be executed AFTER the main reducers
                    //so our state is up to date which allow us not to care about the data within each different action
                    let returnAction = next(action);
                    let module = (adaptable.adaptableModules.get(ModuleConstants.SmartEditModuleId));
                    let state = middlewareAPI.getState();
                    let apiReturn = module.BuildPreviewValues(state.System.SmartEditValue, state.System.SmartEditOperation);
                    middlewareAPI.dispatch(SystemRedux.SmartEditSetPreview(apiReturn));
                    return returnAction;
                }
                /**
                 * Use Case: User has clicked 'Apply' in Smart Edit popup or toolbar
                 * Action (1): Gets the values that need to be applied from the Preview Info and passes to Preview Helper (incl. whether to bypass validation)
                 * Action (2): Sets the cell values in the Grid
                 */
                case SmartEditRedux.SMART_EDIT_APPLY: {
                    const actionTyped = action;
                    let thePreview = middlewareAPI.getState().System.SmartEditPreviewInfo;
                    let newValues = PreviewHelper_1.PreviewHelper.GetCellUpdateRequestsFromPreview(thePreview, actionTyped.bypassValidationWarnings);
                    adaptable.api.gridApi.setCellValues(newValues);
                    middlewareAPI.dispatch(PopupRedux.PopupHideScreen());
                    return next(action);
                }
                /*******************
                 * BULK UPDATE ACTIONS
                 *******************/
                case SystemRedux.SYSTEM_BULK_UPDATE_CHECK_CELL_SELECTION: {
                    let module = (adaptable.adaptableModules.get(ModuleConstants.BulkUpdateModuleId));
                    let state = middlewareAPI.getState();
                    let returnAction = next(action);
                    let apiReturn = module.checkCorrectCellSelection();
                    let popup = state.Popup.ScreenPopup;
                    // this is a horrible hack and fix for a weird issue
                    // we really need to do smart edit and bulk update better
                    // but this fixes it for now
                    if (popup.ComponentName != 'LayoutPopup') {
                        if (apiReturn.Alert) {
                            // check if BulkUpdate is showing as popup
                            if (popup.ComponentName == 'BulkUpdatePopup') {
                                //We close the BulkUpdatePopup
                                middlewareAPI.dispatch(PopupRedux.PopupHideScreen());
                                //We show the Error Popup -- assume that will alwasy be an Error
                                middlewareAPI.dispatch(PopupRedux.PopupShowAlert(apiReturn.Alert));
                            }
                        }
                        middlewareAPI.dispatch(SystemRedux.BulkUpdateSetValidSelection(apiReturn));
                    }
                    return returnAction;
                }
                // Here we have all actions that triggers a refresh of the BulkUpdatePreview
                case SystemRedux.SYSTEM_BULK_UPDATE_CHANGE_VALUE: {
                    //all our logic needs to be executed AFTER the main reducers
                    //so our state is up to date which allow us not to care about the data within each different action
                    let returnAction = next(action);
                    let module = (adaptable.adaptableModules.get(ModuleConstants.BulkUpdateModuleId));
                    let state = middlewareAPI.getState();
                    let apiReturn = module.buildPreviewValues(state.System.BulkUpdateValue);
                    middlewareAPI.dispatch(SystemRedux.BulkUpdateSetPreview(apiReturn));
                    return returnAction;
                }
                case BulkUpdateRedux.BULK_UPDATE_APPLY: {
                    const actionTyped = action;
                    let thePreview = middlewareAPI.getState().System.BulkUpdatePreviewInfo;
                    let newValues = PreviewHelper_1.PreviewHelper.GetCellUpdateRequestsFromPreview(thePreview, actionTyped.bypassValidationWarnings);
                    adaptable.api.gridApi.setCellValues(newValues);
                    middlewareAPI.dispatch(PopupRedux.PopupHideScreen());
                    return next(action);
                }
                /*******************
                 * PLUS MINUS ACTIONS
                 *******************/
                /**
                 * Use Case: Plus Minus Rules have changed
                 * Action: Get Plus Minus module to know if it needs to listen to keydown
                 */
                case PlusMinusRedux.PLUS_MINUS_RULE_ADD:
                case PlusMinusRedux.PLUS_MINUS_RULE_EDIT:
                case PlusMinusRedux.PLUS_MINUS_RULE_DELETE: {
                    let returnAction = next(action);
                    let module = (adaptable.adaptableModules.get(ModuleConstants.PlusMinusModuleId));
                    if (module) {
                        module.checkListenToKeyDown();
                    }
                    return returnAction;
                }
                /**
                 * Use Case: Plus Minus has been applied
                 * Action: update the grid cells
                 */
                case PlusMinusRedux.PLUS_MINUS_APPLY: {
                    let returnAction = next(action);
                    const actionTyped = action;
                    adaptable.api.gridApi.setCellValues(actionTyped.cellUpdateRequests);
                    return returnAction;
                }
                /*******************
                 * SHORTCUT ACTIONS
                 *******************/
                /**
                 * Use Case: Shortcuts have changed
                 * Action: Get Shortcut module to know if it needs to listen to keydown
                 */
                case ShortcutRedux.SHORTCUT_ADD:
                case ShortcutRedux.SHORTCUT_EDIT:
                case ShortcutRedux.SHORTCUT_DELETE: {
                    let returnAction = next(action);
                    let module = (adaptable.adaptableModules.get(ModuleConstants.ShortcutModuleId));
                    if (module) {
                        module.checkListenToKeyDown();
                    }
                    return returnAction;
                }
                /*******************
                 * EXPORT ACTIONS
                 *******************/
                case ExportRedux.REPORT_SELECT: {
                    const actionTyped = action;
                    let returnAction = next(action);
                    const selectedReport = actionTyped.SelectedReport;
                    if (!selectedReport ||
                        (selectedReport === GeneralConstants_1.VISUAL_DATA_REPORT &&
                            middlewareAPI.getState().Export.CurrentDestination !== Enums_1.ExportDestination.Excel)) {
                        middlewareAPI.dispatch(ExportRedux.DestinationSelect(null));
                    }
                    return returnAction;
                }
                case ExportRedux.EXPORT_APPLY: {
                    let module = (adaptable.adaptableModules.get(ModuleConstants.ExportModuleId));
                    const actionTyped = action;
                    module.export(actionTyped.Report, actionTyped.ExportDestination);
                    return next(action);
                }
                // When deleting a report check if its the currently selected one
                // if it is then clear
                case ExportRedux.REPORT_DELETE: {
                    const actionTyped = action;
                    let report = actionTyped.report;
                    let currentReport = middlewareAPI.getState().Export.CurrentReport;
                    if (report && report.Name == currentReport) {
                        middlewareAPI.dispatch(ExportRedux.ReportSelect(GeneralConstants_1.EMPTY_STRING));
                    }
                    return next(action);
                }
                /*******************
                 * CHARTING ACTIONS
                 *******************/
                case ChartingRedux.CHARTING_DELETE_EXTERNAL_CHART: {
                    const returnAction = next(action);
                    adaptable.api.chartingApi.internalApi.onDeleteExternalChart(returnAction.chartDefinition);
                    return returnAction;
                }
                /*******************
                 * TEAM SHARING ACTIONS
                 *******************/
                case TeamSharingRedux.TEAMSHARING_GET: {
                    let returnAction = next(action);
                    adaptable.api.teamSharingApi
                        .loadSharedEntities()
                        .then((sharedEntities) => {
                        middlewareAPI.dispatch(TeamSharingRedux.TeamSharingSet(sharedEntities));
                    })
                        .catch((error) => {
                        adaptable.logger.error('TeamSharing get error : ' + error.message);
                        middlewareAPI.dispatch(PopupRedux.PopupShowAlert({
                            alertType: 'generic',
                            header: 'Team Sharing',
                            message: "Couldn't get shared items: " + error.message,
                            alertDefinition: ObjectFactory_1.ObjectFactory.CreateInternalAlertDefinitionForMessages('Error'),
                        }));
                    });
                    return returnAction;
                }
                case TeamSharingRedux.TEAMSHARING_SHARE_CUSTOM: {
                    const actionTyped = action;
                    let returnAction = next(action);
                    const { Entity, Configuration } = actionTyped;
                    let shareSuccessful;
                    // load latest state from TeamSharing server
                    adaptable.api.teamSharingApi
                        .loadSharedEntities()
                        .then((sharedEntities) => {
                        const customSharedEntity = adaptable.api.internalApi
                            .getTeamSharingService()
                            .buildCustomSharedEntity(Entity, Configuration);
                        let newCustomShare = true;
                        const updatedSharedEntities = sharedEntities.map((entity) => {
                            // if it has the same UUID, override it
                            if ((0, TeamSharingState_1.isCustomSharedEntity)(entity) && entity.Uuid === customSharedEntity.Uuid) {
                                newCustomShare = false;
                                return Object.assign(Object.assign({}, customSharedEntity), { ChangedAt: Date.now(), ChangedBy: adaptable.api.optionsApi.getUserName() });
                            }
                            return entity;
                        });
                        if (newCustomShare) {
                            updatedSharedEntities.push(customSharedEntity);
                        }
                        middlewareAPI.dispatch(TeamSharingRedux.TeamSharingSet(updatedSharedEntities));
                        // update the server state
                        shareSuccessful = true;
                        return adaptable.api.teamSharingApi.persistSharedEntities(updatedSharedEntities);
                    })
                        .then(() => {
                        if (shareSuccessful) {
                            adaptable.logger.info(`Custom Shared Object '${Configuration.Name}' Shared Successfully`);
                        }
                    })
                        .catch((error) => {
                        adaptable.logger.error('TeamSharing share error : ' + error.message, actionTyped.Entity);
                    });
                    return returnAction;
                }
                // share the given AdaptableObject:
                // - if it has dependencies, share them as well
                // - if it's active, ensure that there is not already one present
                // - if it's active, all the dependencies will be active as well
                case TeamSharingRedux.TEAMSHARING_SHARE: {
                    const actionTyped = action;
                    let returnAction = next(action);
                    const { Entity, Configuration, Module } = actionTyped;
                    let shareSuccessful;
                    // load latest state from TeamSharing server
                    adaptable.api.teamSharingApi
                        .loadSharedEntities()
                        .then((sharedEntities) => {
                        // check if there is not already one active SharedEntity for this object
                        const existingActiveSharedEntity = Configuration.type === 'Active' &&
                            middlewareAPI.getState().TeamSharing.ActiveSharedEntityMap[Entity.Uuid];
                        if (existingActiveSharedEntity) {
                            middlewareAPI.dispatch(PopupRedux.PopupShowAlert({
                                alertType: 'generic',
                                header: 'Team Sharing',
                                message: `Couldn't share ${adaptable.api.internalApi.getModuleFriendlyName(Module)}, there is already an existing active share for it: ${existingActiveSharedEntity.Description}`,
                                alertDefinition: ObjectFactory_1.ObjectFactory.CreateInternalAlertDefinitionForMessages('Error'),
                            }));
                            shareSuccessful = false;
                            return;
                        }
                        // build the new shared entities incl. all their dependencies
                        const newSharedEntityInclDependencies = adaptable.api.internalApi
                            .getTeamSharingService()
                            .buildSharedEntityWithDependencies(Entity, Module, Configuration);
                        sharedEntities.push(...newSharedEntityInclDependencies);
                        // update the local state
                        middlewareAPI.dispatch(TeamSharingRedux.TeamSharingLinkItem(...newSharedEntityInclDependencies.filter((sharedEntity) => sharedEntity.Type === 'Active')));
                        middlewareAPI.dispatch(TeamSharingRedux.TeamSharingSet(sharedEntities));
                        // update the server state
                        shareSuccessful = true;
                        return adaptable.api.teamSharingApi.persistSharedEntities(sharedEntities);
                    })
                        .then(() => {
                        if (shareSuccessful) {
                            middlewareAPI.dispatch(PopupRedux.PopupShowAlert({
                                alertType: 'generic',
                                header: 'Team Sharing',
                                message: `${adaptable.api.internalApi.getModuleFriendlyName(Module)} Shared Successfully`,
                                alertDefinition: ObjectFactory_1.ObjectFactory.CreateInternalAlertDefinitionForMessages('Success'),
                            }));
                        }
                    })
                        .catch((error) => {
                        adaptable.logger.error('TeamSharing share error : ' + error.message, actionTyped.Entity);
                        middlewareAPI.dispatch(PopupRedux.PopupShowAlert({
                            alertType: 'generic',
                            header: 'Team Sharing',
                            message: "Couldn't share item: " + error.message,
                            alertDefinition: ObjectFactory_1.ObjectFactory.CreateInternalAlertDefinitionForMessages('Error'),
                        }));
                    });
                    return returnAction;
                }
                // remove the given shared entity:
                // - ensure that no other shared entities depend on it
                case TeamSharingRedux.TEAMSHARING_REMOVE_ITEM: {
                    let returnAction = next(action);
                    const actionTyped = action;
                    const removedEntityId = actionTyped.Uuid;
                    // load latest state from TeamSharing server
                    adaptable.api.teamSharingApi
                        .loadSharedEntities()
                        .then((sharedEntities) => {
                        // ensure that no other SharedDependencies reference the to-be-deleted entity
                        const adaptableSharedEntities = sharedEntities.filter(TeamSharingState_1.isAdaptableSharedEntity);
                        const sharedEntityDependants = adaptable.api.internalApi
                            .getTeamSharingService()
                            .getSharedEntityDependants(removedEntityId, adaptableSharedEntities);
                        if (sharedEntityDependants.length) {
                            middlewareAPI.dispatch(PopupRedux.PopupShowAlert({
                                alertType: 'generic',
                                header: `Cannot remove the shared item as it is referenced in:`,
                                message: sharedEntityDependants
                                    .map((sharedEntity) => `${adaptable.api.internalApi.getModuleFriendlyName(sharedEntity.Module)} ${sharedEntity.Description}`)
                                    .join('\n'),
                                alertDefinition: ObjectFactory_1.ObjectFactory.CreateInternalAlertDefinitionForMessages('Warning'),
                            }));
                            return;
                        }
                        // remove the deleted SharedEntity from local state
                        const newSharedEntities = sharedEntities.filter((s) => s.Uuid !== removedEntityId);
                        middlewareAPI.dispatch(TeamSharingRedux.TeamSharingSet(newSharedEntities));
                        // update TeamSharing server
                        return adaptable.api.teamSharingApi.persistSharedEntities(newSharedEntities);
                    })
                        .catch((error) => {
                        adaptable.logger.error('TeamSharing remove error : ' + error.message);
                        middlewareAPI.dispatch(PopupRedux.PopupShowAlert({
                            alertType: 'generic',
                            header: 'Team Sharing',
                            message: "Couldn't remove item: " + error.message,
                            alertDefinition: ObjectFactory_1.ObjectFactory.CreateInternalAlertDefinitionForMessages('Error'),
                        }));
                    });
                    return returnAction;
                }
                // Import 1. step
                // prepare import of the given shared entity (the actual import is done in 'TEAMSHARING_PROCESS_IMPORT'):
                // - all dependencies will be also included in the import bundle
                // - if there is at least one local AdaptableObject which will be overwritten, display a confirmation pop
                case TeamSharingRedux.TEAMSHARING_IMPORT_ITEM: {
                    let returnAction = next(action);
                    const actionTyped = action;
                    const [importStepActions, needsOverwriteConfirmation] = adaptable.api.internalApi
                        .getTeamSharingService()
                        .buildSharedEntityImportActions(actionTyped.SharedEntity);
                    const processImportAction = TeamSharingRedux.TeamSharingProcessImport(importStepActions);
                    if (needsOverwriteConfirmation &&
                        !adaptable.api.optionsApi.getTeamSharingOptions().suppressOverrideConfigWarning) {
                        let confirmation = {
                            CancelButtonText: 'Cancel Import',
                            Header: 'Overwriting Existing Object',
                            Msg: 'This import will overwrite an existing object in your State. Do you wish to continue?',
                            ConfirmButtonText: 'Import',
                            CancelAction: null,
                            ConfirmAction: processImportAction,
                            ShowInputBox: false,
                            MessageType: 'Warning',
                        };
                        middlewareAPI.dispatch(PopupRedux.PopupShowConfirmation(confirmation));
                    }
                    else {
                        middlewareAPI.dispatch(processImportAction);
                    }
                    return returnAction;
                }
                // Import 2. step
                // import the actual AdaptableObjects (either create or update them)
                case TeamSharingRedux.TEAMSHARING_PROCESS_IMPORT: {
                    let returnAction = next(action);
                    const actionTyped = action;
                    const { ImportSteps } = actionTyped;
                    const activeSharedEntities = [];
                    // iterate backwards to import the dependencies first
                    // it should work also the other way around, but nevertheless...
                    for (let i = ImportSteps.length - 1; i >= 0; i--) {
                        const { sharedEntity, importAction } = ImportSteps[i];
                        if (importAction) {
                            middlewareAPI.dispatch(importAction);
                            if (sharedEntity.Type === 'Active') {
                                activeSharedEntities.push(sharedEntity);
                            }
                        }
                        else {
                            adaptable.logger.error('Team Sharing Import Error: Unknown item type', sharedEntity);
                        }
                    }
                    if (activeSharedEntities.length) {
                        middlewareAPI.dispatch(TeamSharingRedux.TeamSharingLinkItem(...activeSharedEntities));
                    }
                    // mark the import process as ended after (we assume that) the import is finished
                    adaptable.api.teamSharingApi.internalApi
                        .waitForTeamSharingImportEnd()
                        .then(() => middlewareAPI.dispatch(TeamSharingRedux.TeamSharingCommitImport()))
                        .catch(() => middlewareAPI.dispatch(TeamSharingRedux.TeamSharingCommitImport()));
                    middlewareAPI.dispatch(PopupRedux.PopupShowAlert({
                        alertType: 'generic',
                        header: 'Team Sharing',
                        message: `Item Successfully Imported`,
                        alertDefinition: ObjectFactory_1.ObjectFactory.CreateInternalAlertDefinitionForMessages('Success'),
                    }));
                    return returnAction;
                }
                // update the active SharedEntity with the latest (updated) state of the local AdaptableObject
                // - if the remote server already has a newer version of it, the update is skipped
                case TeamSharingRedux.TEAMSHARING_UPDATE_ITEM: {
                    const returnAction = next(action);
                    const actionTyped = action;
                    if (middlewareAPI.getState().TeamSharing.importProcessInProgress) {
                        // we are in the middle of an import process, we don't have to react to these object changes
                        return returnAction;
                    }
                    const { ChangedAdaptableObject, UserName } = actionTyped;
                    adaptable.api.teamSharingApi
                        .loadSharedEntities()
                        .then((sharedEntities) => {
                        const adaptableSharedEntities = sharedEntities.filter(TeamSharingState_1.isAdaptableSharedEntity);
                        // check if remote server has a newer revision
                        const [localRevision, remoteRevision] = adaptable.api.internalApi
                            .getTeamSharingService()
                            .getSharedEntityLocalAndRemoteRevisions(ChangedAdaptableObject.Uuid, adaptableSharedEntities);
                        if (remoteRevision > localRevision) {
                            // if remote has a newer version, we will NOT overwrite it
                            return;
                        }
                        const [updatedSharedEntities, newActiveEntities] = adaptable.api.internalApi
                            .getTeamSharingService()
                            .updateActiveSharedEntity(ChangedAdaptableObject, UserName, adaptableSharedEntities);
                        middlewareAPI.dispatch(TeamSharingRedux.TeamSharingLinkItem(...newActiveEntities));
                        middlewareAPI.dispatch(TeamSharingRedux.TeamSharingSet(updatedSharedEntities));
                        return adaptable.api.teamSharingApi.persistSharedEntities(updatedSharedEntities);
                    })
                        .catch((error) => {
                        adaptable.logger.error('TeamSharing update active item error : ' + error.message);
                        middlewareAPI.dispatch(PopupRedux.PopupShowAlert({
                            alertType: 'generic',
                            header: 'Team Sharing',
                            message: "Couldn't update active item: " + error.message,
                            alertDefinition: ObjectFactory_1.ObjectFactory.CreateInternalAlertDefinitionForMessages('Error'),
                        }));
                    });
                    return returnAction;
                }
                case TeamSharingRedux.TEAMSHARING_CHECK_FOR_UPDATES: {
                    const returnAction = next(action);
                    adaptable.api.teamSharingApi
                        .loadSharedEntities()
                        .then((sharedEntities) => {
                        // update local state
                        middlewareAPI.dispatch(TeamSharingRedux.TeamSharingSet(sharedEntities));
                        adaptable.api.internalApi.getTeamSharingService().showUpdateNotifications();
                    })
                        .catch((error) => {
                        adaptable.logger.error('TeamSharing update error : ' + error.message);
                        middlewareAPI.dispatch(PopupRedux.PopupShowAlert({
                            alertType: 'generic',
                            header: 'Team Sharing',
                            message: "Couldn't check for updates: " + error.message,
                            alertDefinition: ObjectFactory_1.ObjectFactory.CreateInternalAlertDefinitionForMessages('Error'),
                        }));
                    });
                    return returnAction;
                }
                /*******************
                 * DATA CHANGE HISTORY ACTIONS
                 *******************/
                case SystemRedux.SYSTEM_DATA_CHANGE_HISTORY_UNDO:
                    const actionTypedUndo = action;
                    const cellDataChangedInfo = actionTypedUndo.changeInfo;
                    adaptable.api.gridApi.undoCellEdit(cellDataChangedInfo);
                    return next(action);
                /*******************
                 * SYSTEM (INTERNAL) ACTIONS
                 *******************/
                case SystemRedux.SYSTEM_SET_NEW_COLUMN_LIST_ORDER:
                    const actionTyped = action;
                    adaptable.setColumnOrder(actionTyped.visibleColumnList);
                    return next(action);
                /*******************
                 * GRID (INTERNAL) ACTIONS
                 *******************/
                case GridRedux.GRID_CREATE_CELLS_SUMMARY: {
                    let module = (adaptable.adaptableModules.get(ModuleConstants.CellSummaryModuleId));
                    let returnAction = next(action);
                    let selectedCellInfo = middlewareAPI.getState().Grid.SelectedCellInfo;
                    let apiSummaryReturn = module.createCellSummary(selectedCellInfo);
                    middlewareAPI.dispatch(GridRedux.GridSetCellSummary(apiSummaryReturn));
                    return returnAction;
                }
                case GridRedux.GRID_REFRESH_CELLS: {
                    const actionTyped = action;
                    let ret = next(action);
                    adaptable.refreshCells(actionTyped.rowNodes, actionTyped.columnIds, true);
                    return ret;
                }
                case GridRedux.GRID_SET_SORT: {
                    let ret = next(action);
                    adaptable.api.gridApi.internalApi.fireGridSortedEvent();
                    return ret;
                }
                case GridRedux.GRID_CLEAR_SORT: {
                    let ret = next(action);
                    adaptable.api.gridApi.internalApi.fireGridSortedEvent();
                    return ret;
                }
                /*******************
                 * POPUP (INTERNAL) ACTIONS
                 *******************/
                case PopupRedux.POPUP_SHOW_ALERT: {
                    (0, AdaptableToaster_1.showToast)({
                        api: adaptable.api,
                        adaptableAlert: action.alert,
                    });
                    return next(action);
                }
                case PopupRedux.POPUP_CONFIRM_PROMPT: {
                    let promptConfirmationAction = middlewareAPI.getState().Popup.PromptPopup.ConfirmAction;
                    if (promptConfirmationAction) {
                        let inputText = action.InputText;
                        promptConfirmationAction.InputText = inputText;
                        middlewareAPI.dispatch(promptConfirmationAction);
                    }
                    return next(action);
                }
                case PopupRedux.POPUP_CONFIRM_CONFIRMATION: {
                    let confirmationAction = middlewareAPI.getState().Popup.ConfirmationPopup.ConfirmAction;
                    if (confirmationAction) {
                        middlewareAPI.dispatch(confirmationAction);
                    }
                    return next(action);
                }
                case PopupRedux.POPUP_CANCEL_CONFIRMATION: {
                    let cancelAction = middlewareAPI.getState().Popup.ConfirmationPopup.CancelAction;
                    if (cancelAction) {
                        middlewareAPI.dispatch(cancelAction);
                    }
                    return next(action);
                }
                /*******************
                 * MANAGING STATE ACTIONS
                 *******************/
                case exports.INIT_STATE: {
                    let returnAction = next(action);
                    if (adaptable.isReady) {
                        // TODO see #create-create-module-menu
                        // create the module menu (for use in the dashboard and the toolpanel)
                        // we need this here for when the state key is changed
                        // since we need the transient state to be restored in the same
                        // way as when Adaptable is initialised.
                        adaptable.ModuleService.createModuleMenus();
                    }
                    return returnAction;
                }
                default: {
                    return next(action);
                }
            }
        };
    };
});
