diff --git a/src/ui/units/datasets/components/DatasetPanel/DatasetPanel.tsx b/src/ui/units/datasets/components/DatasetPanel/DatasetPanel.tsx index b6bd7f5f7c..dea3875d11 100644 --- a/src/ui/units/datasets/components/DatasetPanel/DatasetPanel.tsx +++ b/src/ui/units/datasets/components/DatasetPanel/DatasetPanel.tsx @@ -32,7 +32,7 @@ function TabSwitch(props: TabSwitchProps) { return ( switchTab(e.target.value)} > {tabs.map(({value, label, disabled}) => ( diff --git a/src/ui/units/datasets/components/DatasetRouter/DatasetRouter.tsx b/src/ui/units/datasets/components/DatasetRouter/DatasetRouter.tsx index ddee8cf9a8..83bb9fbb0e 100644 --- a/src/ui/units/datasets/components/DatasetRouter/DatasetRouter.tsx +++ b/src/ui/units/datasets/components/DatasetRouter/DatasetRouter.tsx @@ -10,7 +10,6 @@ import {Route, Switch, withRouter} from 'react-router-dom'; import type {Dispatch} from 'redux'; import {bindActionCreators} from 'redux'; import {setCurrentPageEntry} from 'store/actions/asideHeader'; -import {selectAsideHeaderData} from 'store/selectors/asideHeader'; import {registry} from 'ui/registry'; import {resetDatasetState} from 'units/datasets/store/actions/creators'; @@ -19,6 +18,8 @@ import withInaccessibleOnMobile from '../../../../hoc/withInaccessibleOnMobile'; import DatasetPage from '../../containers/DatasetPage/DatasetPage'; import {datasetKeySelector} from '../../store/selectors/dataset'; +import {UnloadConfirmation} from './UnloadConfirmation'; + import './DatasetRouter.scss'; const b = block('dataset-router'); @@ -33,14 +34,7 @@ type Props = StateProps & sdk: SDK; }; -const DatasetRouter = ({ - sdk, - datasetKey, - setCurrentPageEntry, - asideHeaderData, - resetDatasetState, - match, -}: Props) => { +const DatasetRouter = ({sdk, datasetKey, setCurrentPageEntry, resetDatasetState, match}: Props) => { const isAsideHeaderEnabled = getIsAsideHeaderEnabled(); const {extractEntryId} = registry.common.functions.getAll(); const possibleEntryId = extractEntryId(window.location.pathname); @@ -49,7 +43,7 @@ const DatasetRouter = ({ return () => { resetDatasetState(); }; - }, []); + }, [resetDatasetState]); const prevMatch = usePrevious(match) || match; @@ -60,7 +54,7 @@ const DatasetRouter = ({ if (prevEntryId !== currentEntryId) { resetDatasetState(); } - }, [prevMatch.url, match.url]); + }, [prevMatch.url, match.url, extractEntryId, resetDatasetState]); React.useEffect(() => { if (!isAsideHeaderEnabled || !possibleEntryId || !datasetKey) { @@ -71,29 +65,36 @@ const DatasetRouter = ({ entryId: possibleEntryId, key: datasetKey, }); - }, [isAsideHeaderEnabled, possibleEntryId, datasetKey]); + }, [isAsideHeaderEnabled, possibleEntryId, datasetKey, setCurrentPageEntry]); return (
( - - )} + render={(props: RouteComponentProps<{workbookId?: string}>) => { + const {workbookId} = props.match.params; + return ; + }} /> ( - - )} + render={( + props: RouteComponentProps<{datasetId?: string; workbookId?: string}>, + ) => { + const {datasetId, workbookId} = props.match.params; + return ( + + ); + }} /> +
); }; @@ -101,7 +102,6 @@ const DatasetRouter = ({ const mapStateToProps = (state: DatalensGlobalState) => { return { datasetKey: datasetKeySelector(state), - asideHeaderData: selectAsideHeaderData(state), }; }; diff --git a/src/ui/units/datasets/components/DatasetRouter/UnloadConfirmation.tsx b/src/ui/units/datasets/components/DatasetRouter/UnloadConfirmation.tsx new file mode 100644 index 0000000000..b0cb0984c3 --- /dev/null +++ b/src/ui/units/datasets/components/DatasetRouter/UnloadConfirmation.tsx @@ -0,0 +1,56 @@ +import React from 'react'; + +import type History from 'history'; +import {i18n} from 'i18n'; +import {useSelector} from 'react-redux'; +import {Prompt, useLocation} from 'react-router-dom'; +import {selectIsRenameWithoutReload} from 'ui/store/selectors/entryContent'; +import { + isDatasetChangedDatasetSelector, + isDatasetRevisionMismatchSelector, + isSavingDatasetDisabledSelector, +} from 'ui/units/datasets/store/selectors'; + +export const UnloadConfirmation = () => { + const currentLocation = useLocation(); + const isRenameWithoutReload = useSelector(selectIsRenameWithoutReload); + const isSavingDatasetDisabled = useSelector(isSavingDatasetDisabledSelector); + const isDatasetRevisionMismatch = useSelector(isDatasetRevisionMismatchSelector); + const isDatasetChangedDataset = useSelector(isDatasetChangedDatasetSelector); + const [isAlreadyConfirmed, setIsAlreadyConfirmed] = React.useState(false); + const isConfirmationAvailable = + !isAlreadyConfirmed && + (!isSavingDatasetDisabled || isDatasetChangedDataset) && + !isRenameWithoutReload && + !isDatasetRevisionMismatch; + + const message = React.useCallback( + (location: History.Location) => { + setIsAlreadyConfirmed(true); + setTimeout(() => setIsAlreadyConfirmed(false), 0); + if (location.pathname !== currentLocation.pathname) { + return i18n('component.navigation-prompt', 'label_prompt-message'); + } + + return true; + }, + [currentLocation.pathname], + ); + + React.useEffect(() => { + const beforeUnloadHandler = (event: BeforeUnloadEvent) => { + if (isConfirmationAvailable) { + // eslint-disable-next-line no-param-reassign + event.returnValue = true; + } + }; + + window.addEventListener('beforeunload', beforeUnloadHandler); + + return () => { + window.removeEventListener('beforeunload', beforeUnloadHandler); + }; + }, [isConfirmationAvailable]); + + return ; +}; diff --git a/src/ui/units/datasets/constants/index.ts b/src/ui/units/datasets/constants/index.ts index 3b67e4edc1..327364306b 100644 --- a/src/ui/units/datasets/constants/index.ts +++ b/src/ui/units/datasets/constants/index.ts @@ -253,7 +253,7 @@ export const SUBSELECT_SOURCE_TYPES = [ 'YQ_SUBSELECT', ]; -export const DATASETS_EDIT_HISTORY_UNIT_ID = 'datsets'; +export const DATASETS_EDIT_HISTORY_UNIT_ID = 'datasets'; /** This timeout uses for batching operations to decrease validation invocations count */ export const DATASET_VALIDATION_TIMEOUT = 2000; diff --git a/src/ui/units/datasets/containers/Dataset/Dataset.js b/src/ui/units/datasets/containers/Dataset/Dataset.js index 6f0bd18327..9a940f2769 100644 --- a/src/ui/units/datasets/containers/Dataset/Dataset.js +++ b/src/ui/units/datasets/containers/Dataset/Dataset.js @@ -7,7 +7,6 @@ import {I18n} from 'i18n'; import omit from 'lodash/omit'; import PropTypes from 'prop-types'; import {connect} from 'react-redux'; -import {withRouter} from 'react-router-dom'; import SplitPane from 'react-split-pane'; import {compose} from 'recompose'; import {createStructuredSelector} from 'reselect'; @@ -16,7 +15,6 @@ import {HOTKEYS_SCOPES} from 'ui/constants/misc'; import {withHotkeysContext} from 'ui/hoc/withHotkeysContext'; import {openDialogErrorWithTabs} from 'ui/store/actions/dialog'; import {initEditHistoryUnit} from 'ui/store/actions/editHistory'; -import {selectIsRenameWithoutReload} from 'ui/store/selectors/entryContent'; import { addAvatar, addSource, @@ -29,7 +27,7 @@ import { openPreview, refreshSources, saveDataset, - setAsideHeaderWidth, + setCurrentTab, setEditHistoryState, togglePreview, updateDatasetByValidation, @@ -43,7 +41,6 @@ import EntryDialogues, { EntryDialogName, } from '../../../../components/EntryDialogues/EntryDialogues'; import ErrorContent from '../../../../components/ErrorContent/ErrorContent'; -import NavigationPrompt from '../../../../components/NavigationPrompt/NavigationPrompt'; import {PageTitle} from '../../../../components/PageTitle'; import {SlugifyUrl} from '../../../../components/SlugifyUrl'; import UIUtils from '../../../../utils/utils'; @@ -63,6 +60,7 @@ import {getAutoCreatedYTDatasetKey} from '../../helpers/datasets'; import DatasetUtils from '../../helpers/utils'; import { UISelector, + currentTabSelector, datasetErrorSelector, datasetKeySelector, datasetPermissionsSelector, @@ -70,11 +68,9 @@ import { datasetPreviewSelector, datasetSavingErrorSelector, datasetValidationErrorSelector, - isDatasetChangedDatasetSelector, isDatasetRevisionMismatchSelector, isFavoriteDatasetSelector, isLoadingDatasetSelector, - isSavingDatasetDisabledSelector, previewEnabledSelector, sourcePrototypesSelector, sourceTemplateSelector, @@ -104,7 +100,6 @@ class Dataset extends React.Component { progress: false, connectionId: '', connectionType: '', - tab: this.props.tab, propsDatasetCreationDialog: {}, previewPanelSize: BOTTOM_PREVIEW_PANEL_DEFAULT_SIZE, }; @@ -115,7 +110,7 @@ class Dataset extends React.Component { this.props.setEditHistoryState({state}); }, options: { - pathIgnoreList: [], + pathIgnoreList: ['/isLoading'], }, }); } @@ -139,22 +134,19 @@ class Dataset extends React.Component { } this.props.hotkeysContext?.enableScope(HOTKEYS_SCOPES.DATASETS); - window.addEventListener('beforeunload', this.confirmClosePage); } componentDidUpdate(prevProps) { const { datasetId: prevDatasetId, datasetPreview: {view: prevView}, - asideHeaderData: {size: prevSize} = {}, ui: {isSourcesLoading: prevIsSourcesLoading}, } = prevProps; const { - tab, + currentTab, ytPath, datasetId, datasetPreview: {view: curView}, - asideHeaderData: {size: curSize} = {}, ui: {isSourcesLoading}, savingError, } = this.props; @@ -177,17 +169,12 @@ class Dataset extends React.Component { this.setState({previewPanelSize}); } - // The moment when the side cap's resize ends - if (curSize && prevSize !== curSize) { - this.props.setAsideHeaderWidth(curSize); - } - // Before automatically creating a dataset, we are waiting for the sources to load if (prevIsSourcesLoading && !isSourcesLoading && isAuto && ytPath) { this.autoCreateYTDataset(); } - if (tab === TAB_SOURCES && prevIsSourcesLoading && !isSourcesLoading && !isAuto) { + if (currentTab === TAB_SOURCES && prevIsSourcesLoading && !isSourcesLoading && !isAuto) { this.addInitialAvatar(); } @@ -200,7 +187,6 @@ class Dataset extends React.Component { componentWillUnmount() { this.props.hotkeysContext?.disableScope(HOTKEYS_SCOPES.DATASETS); - window.removeEventListener('beforeunload', this.confirmClosePage); } _datasetEditorRef = React.createRef(); @@ -253,15 +239,6 @@ class Dataset extends React.Component { } }; - confirmClosePage = (event) => { - const {isDatasetChanged} = this.props; - - if (isDatasetChanged) { - // eslint-disable-next-line no-param-reassign - event.returnValue = true; - } - }; - openCreationWidgetPage = () => { const {datasetId} = this.props; @@ -368,10 +345,8 @@ class Dataset extends React.Component { }; } - switchTab = (tab) => { - this.setState({ - tab, - }); + switchTab = (currentTab) => { + this.props.setCurrentTab({currentTab}); }; openDialogFieldEditor = () => { @@ -418,7 +393,7 @@ class Dataset extends React.Component { }; getWorkbookId() { - return this.props.match.params.workbookId || this.props.workbookId; + return this.props.workbookIdFromPath || this.props.workbookId; } refreshSources = () => { @@ -483,15 +458,16 @@ class Dataset extends React.Component { sdk, datasetId, datasetPreview: {view, isVisible}, + currentTab, } = this.props; - const {tab, previewPanelSize} = this.state; + const {previewPanelSize} = this.state; const isRightView = view === VIEW_PREVIEW.RIGHT; const mods = {[VIEW_PREVIEW.RIGHT]: isRightView}; const previewPanelStyles = {}; let minSize = 0; - const isSourceOrDatasetTab = tab === TAB_SOURCES || tab === TAB_DATASET; + const isSourceOrDatasetTab = currentTab === TAB_SOURCES || currentTab === TAB_DATASET; if (isVisible && isSourceOrDatasetTab) { minSize = isRightView ? RIGHT_PREVIEW_PANEL_MIN_SIZE : BOTTOM_PREVIEW_PANEL_MIN_SIZE; @@ -517,7 +493,7 @@ class Dataset extends React.Component { > - + )} {this.renderBody()} - ); } @@ -629,10 +599,7 @@ Dataset.propTypes = { isFavorite: PropTypes.bool.isRequired, isAuto: PropTypes.bool.isRequired, previewEnabled: PropTypes.bool.isRequired, - isDatasetChanged: PropTypes.bool.isRequired, isDatasetRevisionMismatch: PropTypes.bool.isRequired, - isRenameWithoutReload: PropTypes.bool, - savingDatasetDisabled: PropTypes.bool.isRequired, fetchFieldTypes: PropTypes.func.isRequired, initializeDataset: PropTypes.func.isRequired, initialFetchDataset: PropTypes.func.isRequired, @@ -644,10 +611,9 @@ Dataset.propTypes = { saveDataset: PropTypes.func.isRequired, changeAmountPreviewRows: PropTypes.func.isRequired, refreshSources: PropTypes.func.isRequired, - setAsideHeaderWidth: PropTypes.func.isRequired, openDialogErrorWithTabs: PropTypes.func.isRequired, initEditHistoryUnit: PropTypes.func.isRequired, - tab: PropTypes.string, + currentTab: PropTypes.string.isRequired, datasetId: PropTypes.string, connectionId: PropTypes.string, datasetKey: PropTypes.string, @@ -660,10 +626,9 @@ Dataset.propTypes = { sourcePrototypes: PropTypes.array, datasetPreview: PropTypes.object.isRequired, history: PropTypes.object.isRequired, - asideHeaderData: PropTypes.shape({ - size: PropTypes.number.isRequired, - }), datasetPermissions: PropTypes.object, + workbookId: PropTypes.string, + workbookIdFromPath: PropTypes.string, }; Dataset.defaultProps = { @@ -678,17 +643,15 @@ const mapStateToProps = createStructuredSelector({ savingError: datasetSavingErrorSelector, validationError: datasetValidationErrorSelector, datasetPreview: datasetPreviewSelector, - isDatasetChanged: isDatasetChangedDatasetSelector, isLoading: isLoadingDatasetSelector, isFavorite: isFavoriteDatasetSelector, - savingDatasetDisabled: isSavingDatasetDisabledSelector, isDatasetRevisionMismatch: isDatasetRevisionMismatchSelector, previewEnabled: previewEnabledSelector, sourcePrototypes: sourcePrototypesSelector, sourceTemplate: sourceTemplateSelector, ui: UISelector, workbookId: workbookIdSelector, - isRenameWithoutReload: selectIsRenameWithoutReload, + currentTab: currentTabSelector, }); const mapDispatchToProps = { fetchFieldTypes, @@ -702,14 +665,12 @@ const mapDispatchToProps = { updateDatasetByValidation, changeAmountPreviewRows, refreshSources, - setAsideHeaderWidth, addSource, addAvatar, openDialogErrorWithTabs, initEditHistoryUnit, setEditHistoryState, + setCurrentTab, }; -export default compose(connect(mapStateToProps, mapDispatchToProps))( - withRouter(withHotkeysContext(Dataset)), -); +export default compose(connect(mapStateToProps, mapDispatchToProps))(withHotkeysContext(Dataset)); diff --git a/src/ui/units/datasets/containers/DatasetEditor/DatasetEditor.js b/src/ui/units/datasets/containers/DatasetEditor/DatasetEditor.js index 7fd768afa2..a154c000b8 100644 --- a/src/ui/units/datasets/containers/DatasetEditor/DatasetEditor.js +++ b/src/ui/units/datasets/containers/DatasetEditor/DatasetEditor.js @@ -156,10 +156,11 @@ class DatasetEditor extends React.Component { }) => { if (fields.length > 0 && fields.every(({guid}) => guid)) { this.props.toggleSaveDataset({enable: !validateEnabled, validationPending: debounce}); + const stacked = debounce && this.props.validation.isPending; switch (actionType) { case 'delete': { - this.props.batchDeleteFields(fields); + this.props.batchDeleteFields(fields, {stacked}); this.updateDataset({ debounce, @@ -170,7 +171,7 @@ class DatasetEditor extends React.Component { break; } case 'update': { - this.props.batchUpdateFields(fields); + this.props.batchUpdateFields(fields, undefined, {stacked}); this.updateDataset({ debounce, @@ -190,7 +191,7 @@ class DatasetEditor extends React.Component { switch (actionType) { case 'duplicate': { - this.props.duplicateField(field); + this.props.duplicateField(field, {stacked: true}); this.updateDataset({ debounce: true, diff --git a/src/ui/units/datasets/containers/DatasetPage/DatasetPage.tsx b/src/ui/units/datasets/containers/DatasetPage/DatasetPage.tsx index 76dceb94c5..fa9a0841d5 100644 --- a/src/ui/units/datasets/containers/DatasetPage/DatasetPage.tsx +++ b/src/ui/units/datasets/containers/DatasetPage/DatasetPage.tsx @@ -7,10 +7,8 @@ import type {SDK} from 'ui'; import {Utils} from 'ui'; import {registry} from 'ui/registry'; -import type {AsideHeaderData} from '../../../../store/typings/asideHeader'; -import {DATASET_TABS, TAB_DATASET, TAB_SOURCES} from '../../constants'; import {ActionQueryParam, QueryParam, mapYTClusterToConnId} from '../../constants/datasets'; -import DatasetUtils from '../../helpers/utils'; +import DatasetUtils, {isCreationProcess} from '../../helpers/utils'; import Dataset from '../Dataset/Dataset'; import {createDatasetPageContext} from './createDatasetPageContext'; @@ -19,8 +17,8 @@ import './DatasetPage.scss'; interface DatasetPageProps extends RouteComponentProps> { sdk: SDK; - asideHeaderData: AsideHeaderData; - isCreationProcess?: boolean; + datasetId?: string; + workbookId?: string; } export const DatasetPageContext = createDatasetPageContext({sdk: {} as SDK, datasetId: ''}); @@ -29,36 +27,19 @@ export const DatasetPageConsumer = DatasetPageContext.Consumer; const b = block('dataset-page'); -const getDatasetTab = (queryTab?: string, isCreationProcess = false) => { - const defaultTab = isCreationProcess ? TAB_SOURCES : TAB_DATASET; - - if (!queryTab) { - return defaultTab; - } - - if (DATASET_TABS.includes(queryTab)) { - return queryTab; - } - - return defaultTab; -}; - class DatasetPage extends React.Component { render() { - const {sdk, isCreationProcess, history, asideHeaderData} = this.props; - return (
@@ -91,15 +72,13 @@ class DatasetPage extends React.Component { } get datasetId() { - const datasetId = this.getParamFromMatch('datasetId'); - - if (!datasetId) { + if (!this.props.datasetId) { return ''; } const {extractEntryId} = registry.common.functions.getAll(); - return extractEntryId(datasetId) || ''; + return extractEntryId(this.props.datasetId) || ''; } get ytPath() { @@ -114,10 +93,6 @@ class DatasetPage extends React.Component { datasetId: this.datasetId, }; } - - private getParamFromMatch(name: string) { - return this.props.match.params[name]; - } } export const useDatasetPageContext = () => React.useContext(DatasetPageContext); diff --git a/src/ui/units/datasets/helpers/utils.ts b/src/ui/units/datasets/helpers/utils.ts index c70d8d8233..12af47a36e 100644 --- a/src/ui/units/datasets/helpers/utils.ts +++ b/src/ui/units/datasets/helpers/utils.ts @@ -149,3 +149,7 @@ export default class DatasetUtils { return DatasetUtils.filterVirtual(field) && field.calc_mode !== 'parameter'; } } + +export function isCreationProcess() { + return /new$/.test(window.location.pathname); +} diff --git a/src/ui/units/datasets/store/actions/creators/dataset.js b/src/ui/units/datasets/store/actions/creators/dataset.js index b9bbb20045..44a8e5b3ba 100644 --- a/src/ui/units/datasets/store/actions/creators/dataset.js +++ b/src/ui/units/datasets/store/actions/creators/dataset.js @@ -1,5 +1,6 @@ import {Toaster} from '@gravity-ui/uikit'; import {i18n} from 'i18n'; +import get from 'lodash/get'; import {batch} from 'react-redux'; import {TIMEOUT_65_SEC} from 'shared'; import {resetEditHistoryUnit} from 'ui/store/actions/editHistory'; @@ -462,7 +463,7 @@ function _getSources() { } export function getSources(connectionId, workbookId) { - return async (dispatch) => { + return async (dispatch, getState) => { dispatch(toggleSourcesLoader(true)); let sources = []; @@ -497,9 +498,10 @@ export function getSources(connectionId, workbookId) { } } finally { batch(() => { + const diffs = get(getState(), 'editHistory.units.datasets.diffs', []); dispatch(toggleSourcesLoader(false)); // Set initial history point - dispatch(addEditHistoryPointDs()); + dispatch(addEditHistoryPointDs({stacked: Boolean(diffs.length)})); }); } diff --git a/src/ui/units/datasets/store/actions/creators/datasetTyped.ts b/src/ui/units/datasets/store/actions/creators/datasetTyped.ts index b03d5757cc..066d3eed14 100644 --- a/src/ui/units/datasets/store/actions/creators/datasetTyped.ts +++ b/src/ui/units/datasets/store/actions/creators/datasetTyped.ts @@ -24,6 +24,7 @@ import logger from '../../../../../libs/logger'; import {getSdk} from '../../../../../libs/schematic-sdk'; import {getFilteredObject} from '../../../../../utils'; import {DATASETS_EDIT_HISTORY_UNIT_ID, TOASTERS_NAMES} from '../../../constants'; +import {EDIT_HISTORY_OPTIONS_KEY} from '../../constants'; import { datasetContentSelector, datasetFieldsSelector, @@ -38,8 +39,10 @@ import type { ConnectionEntry, DatasetError, DatasetReduxAction, + EditHistoryOptions, EditorItemToDisplay, FreeformSource, + SetCurrentTab, SetEditHistoryState, ToggleAllowanceSave, Update, @@ -90,12 +93,19 @@ export function renameDataset(key: string) { payload: key, }; } - +// export function toggleSaveDataset(args: ToggleAllowanceSave['payload']): DatasetReduxAction { - const {enable = true, validationPending} = args; + const {enable = true, validationPending, [EDIT_HISTORY_OPTIONS_KEY]: editHistoryOptions} = args; return { type: DATASET_ACTION_TYPES.TOGGLE_ALLOWANCE_SAVE, - payload: {enable, validationPending}, + payload: { + enable, + validationPending, + [EDIT_HISTORY_OPTIONS_KEY]: { + stacked: true, + ...editHistoryOptions, + }, + }, }; } @@ -192,6 +202,9 @@ const dispatchFetchPreviewDataset = async ( type: DATASET_ACTION_TYPES.PREVIEW_DATASET_FETCH_SUCCESS, payload: { data: data || regular, + [EDIT_HISTORY_OPTIONS_KEY]: { + stacked: true, + }, }, }); } catch (error) { @@ -201,6 +214,9 @@ const dispatchFetchPreviewDataset = async ( type: DATASET_ACTION_TYPES.PREVIEW_DATASET_FETCH_FAILURE, payload: { error, + [EDIT_HISTORY_OPTIONS_KEY]: { + stacked: true, + }, }, }); } @@ -278,12 +294,20 @@ export function queuedFetchPreviewDataset() { }; } -export function toggleLoadPreviewByDefault(enable: boolean) { +export function toggleLoadPreviewByDefault( + enable: boolean, + editHistoryOptions?: EditHistoryOptions, +) { return (dispatch: DatasetDispatch, getState: GetState) => { if (isLoadPreviewByDefaultSelector(getState()) !== enable) { dispatch({ type: DATASET_ACTION_TYPES.TOGGLE_LOAD_PREVIEW_BY_DEFAULT, - payload: {enable}, + payload: { + enable, + [EDIT_HISTORY_OPTIONS_KEY]: { + ...editHistoryOptions, + }, + }, }); dispatch(toggleSaveDataset({enable: true})); @@ -291,72 +315,110 @@ export function toggleLoadPreviewByDefault(enable: boolean) { }; } -export function changeAmountPreviewRows({amountPreviewRows}: {amountPreviewRows: number}) { +export function changeAmountPreviewRows({ + amountPreviewRows, + editHistoryOptions, +}: { + amountPreviewRows: number; + editHistoryOptions?: EditHistoryOptions; +}) { return (dispatch: DatasetDispatch) => { dispatch({ type: DATASET_ACTION_TYPES.CHANGE_AMOUNT_PREVIEW_ROWS, payload: { amountPreviewRows, + [EDIT_HISTORY_OPTIONS_KEY]: { + ...editHistoryOptions, + }, }, }); }; } -export function duplicateField(field: DatasetField) { +export function duplicateField(field: DatasetField, editHistoryOptions?: EditHistoryOptions) { return (dispatch: DatasetDispatch) => { dispatch({ type: DATASET_ACTION_TYPES.DUPLICATE_FIELD, payload: { field, + [EDIT_HISTORY_OPTIONS_KEY]: { + ...editHistoryOptions, + }, }, }); }; } -export function deleteField(field: Partial) { +export function deleteField(field: Partial, editHistoryOptions?: EditHistoryOptions) { return (dispatch: DatasetDispatch) => { dispatch({ type: DATASET_ACTION_TYPES.DELETE_FIELD, payload: { field, + [EDIT_HISTORY_OPTIONS_KEY]: { + ...editHistoryOptions, + }, }, }); }; } -export function batchDeleteFields(fields: Partial[]) { + +export function batchDeleteFields( + fields: Partial[], + editHistoryOptions?: EditHistoryOptions, +) { return (dispatch: DatasetDispatch) => { dispatch({ type: DATASET_ACTION_TYPES.BATCH_DELETE_FIELDS, payload: { fields, + [EDIT_HISTORY_OPTIONS_KEY]: { + ...editHistoryOptions, + }, }, }); }; } -export function addField(field: Partial, ignoreMergeWithSchema?: boolean) { +export function addField( + field: Partial, + ignoreMergeWithSchema?: boolean, + editHistoryOptions?: EditHistoryOptions, +) { return (dispatch: DatasetDispatch) => { dispatch({ type: DATASET_ACTION_TYPES.ADD_FIELD, payload: { field, ignoreMergeWithSchema, + [EDIT_HISTORY_OPTIONS_KEY]: { + ...editHistoryOptions, + }, }, }); }; } -export function updateField(field: Partial, ignoreMergeWithSchema?: boolean) { +export function updateField( + field: Partial, + ignoreMergeWithSchema?: boolean, + editHistoryOptions?: EditHistoryOptions, +) { return (dispatch: DatasetDispatch) => { dispatch({ type: DATASET_ACTION_TYPES.UPDATE_FIELD, payload: { field, ignoreMergeWithSchema, + [EDIT_HISTORY_OPTIONS_KEY]: { + ...editHistoryOptions, + }, }, }); }; } + export function batchUpdateFields( fields: Partial[], ignoreMergeWithSchema?: boolean, + editHistoryOptions?: EditHistoryOptions, ) { return (dispatch: DatasetDispatch) => { dispatch({ @@ -364,17 +426,23 @@ export function batchUpdateFields( payload: { fields, ignoreMergeWithSchema, + [EDIT_HISTORY_OPTIONS_KEY]: { + ...editHistoryOptions, + }, }, }); }; } -export function updateRLS(rls: {[key: string]: string}) { +export function updateRLS(rls: {[key: string]: string}, editHistoryOptions?: EditHistoryOptions) { return (dispatch: DatasetDispatch) => { dispatch({ type: DATASET_ACTION_TYPES.UPDATE_RLS, payload: { rls, + [EDIT_HISTORY_OPTIONS_KEY]: { + ...editHistoryOptions, + }, }, }); @@ -392,22 +460,40 @@ export function clickConnection({connectionId}: {connectionId: string}) { }); }; } -export function addConnection({connection}: {connection: ConnectionEntry}) { +export function addConnection({ + connection, + editHistoryOptions, +}: { + connection: ConnectionEntry; + editHistoryOptions?: EditHistoryOptions; +}) { return (dispatch: DatasetDispatch) => { dispatch({ type: DATASET_ACTION_TYPES.ADD_CONNECTION, payload: { connection, + [EDIT_HISTORY_OPTIONS_KEY]: { + ...editHistoryOptions, + }, }, }); }; } -export function deleteConnection({connectionId}: {connectionId: string}) { +export function deleteConnection({ + connectionId, + editHistoryOptions, +}: { + connectionId: string; + editHistoryOptions?: EditHistoryOptions; +}) { return (dispatch: DatasetDispatch, getState: GetState) => { dispatch({ type: DATASET_ACTION_TYPES.DELETE_CONNECTION, payload: { connectionId, + [EDIT_HISTORY_OPTIONS_KEY]: { + ...editHistoryOptions, + }, }, }); @@ -447,49 +533,83 @@ export function addAvatarPrototypes({ }; } -export function addSource({source}: {source: DatasetSource}) { +export function addSource({ + source, + editHistoryOptions, +}: { + source: DatasetSource; + editHistoryOptions?: EditHistoryOptions; +}) { return (dispatch: DatasetDispatch) => { dispatch({ type: DATASET_ACTION_TYPES.SOURCE_ADD, payload: { source, + [EDIT_HISTORY_OPTIONS_KEY]: { + stacked: true, + ...editHistoryOptions, + }, }, }); }; } -export function updateSource({source}: {source: DatasetSource}) { +export function updateSource({ + source, + editHistoryOptions, +}: { + source: DatasetSource; + editHistoryOptions?: EditHistoryOptions; +}) { return (dispatch: DatasetDispatch) => { dispatch({ type: DATASET_ACTION_TYPES.SOURCE_UPDATE, payload: { source, + [EDIT_HISTORY_OPTIONS_KEY]: { + stacked: true, + ...editHistoryOptions, + }, }, }); }; } -export function deleteSource({sourceId}: {sourceId: string}) { +export function deleteSource({ + sourceId, + editHistoryOptions, +}: { + sourceId: string; + editHistoryOptions?: EditHistoryOptions; +}) { return (dispatch: DatasetDispatch) => { dispatch({ type: DATASET_ACTION_TYPES.SOURCE_DELETE, payload: { sourceId, + [EDIT_HISTORY_OPTIONS_KEY]: { + ...editHistoryOptions, + }, }, }); }; } -export function refreshSources() { +export function refreshSources(editHistoryOptions?: EditHistoryOptions) { return (dispatch: DatasetDispatch) => { dispatch({ type: DATASET_ACTION_TYPES.SOURCES_REFRESH, + [EDIT_HISTORY_OPTIONS_KEY]: { + ...editHistoryOptions, + }, }); }; } export function replaceSource({ source, avatar, + editHistoryOptions, }: { source: DatasetSource; avatar: DatasetSourceAvatar; + editHistoryOptions?: EditHistoryOptions; }) { return (dispatch: DatasetDispatch) => { dispatch({ @@ -497,6 +617,9 @@ export function replaceSource({ payload: { source, avatar, + [EDIT_HISTORY_OPTIONS_KEY]: { + ...editHistoryOptions, + }, }, }); }; @@ -505,9 +628,11 @@ export function replaceSource({ export function replaceConnection({ connection, newConnection, + editHistoryOptions, }: { connection?: ConnectionEntry; newConnection?: ConnectionEntry; + editHistoryOptions?: EditHistoryOptions; }) { return (dispatch: DatasetDispatch) => { dispatch({ @@ -515,84 +640,148 @@ export function replaceConnection({ payload: { connection, newConnection, + [EDIT_HISTORY_OPTIONS_KEY]: { + ...editHistoryOptions, + }, }, }); }; } -export function addAvatar({avatar}: {avatar: DatasetSourceAvatar}) { +export function addAvatar({ + avatar, + editHistoryOptions, +}: { + avatar: DatasetSourceAvatar; + editHistoryOptions?: EditHistoryOptions; +}) { return (dispatch: DatasetDispatch) => { dispatch({ type: DATASET_ACTION_TYPES.AVATAR_ADD, payload: { avatar, + [EDIT_HISTORY_OPTIONS_KEY]: { + stacked: true, + ...editHistoryOptions, + }, }, }); }; } -export function deleteAvatar({avatarId}: {avatarId: string}) { +export function deleteAvatar({ + avatarId, + editHistoryOptions, +}: { + avatarId: string; + editHistoryOptions?: EditHistoryOptions; +}) { return (dispatch: DatasetDispatch) => { dispatch({ type: DATASET_ACTION_TYPES.AVATAR_DELETE, payload: { avatarId, + [EDIT_HISTORY_OPTIONS_KEY]: { + ...editHistoryOptions, + }, }, }); }; } -export function addRelation({relation}: {relation: DatasetAvatarRelation}) { +export function addRelation({ + relation, + editHistoryOptions, +}: { + relation: DatasetAvatarRelation; + editHistoryOptions?: EditHistoryOptions; +}) { return (dispatch: DatasetDispatch) => { dispatch({ type: DATASET_ACTION_TYPES.RELATION_ADD, payload: { relation, + [EDIT_HISTORY_OPTIONS_KEY]: { + ...editHistoryOptions, + }, }, }); }; } -export function updateRelation({relation}: {relation: DatasetAvatarRelation}) { +export function updateRelation({ + relation, + editHistoryOptions, +}: { + relation: DatasetAvatarRelation; + editHistoryOptions?: EditHistoryOptions; +}) { return (dispatch: DatasetDispatch) => { dispatch({ type: DATASET_ACTION_TYPES.RELATION_UPDATE, payload: { relation, + [EDIT_HISTORY_OPTIONS_KEY]: { + ...editHistoryOptions, + }, }, }); }; } -export function deleteRelation({relationId}: {relationId: string}) { +export function deleteRelation({ + relationId, + editHistoryOptions, +}: { + relationId: string; + editHistoryOptions?: EditHistoryOptions; +}) { return (dispatch: DatasetDispatch) => { dispatch({ type: DATASET_ACTION_TYPES.RELATION_DELETE, payload: { relationId, + [EDIT_HISTORY_OPTIONS_KEY]: { + ...editHistoryOptions, + }, }, }); }; } -export function addObligatoryFilter(filter: ApplyData) { +export function addObligatoryFilter(filter: ApplyData, editHistoryOptions?: EditHistoryOptions) { return (dispatch: DatasetDispatch) => { dispatch({ type: DATASET_ACTION_TYPES.ADD_OBLIGATORY_FILTER, - payload: {filter}, + payload: { + filter, + [EDIT_HISTORY_OPTIONS_KEY]: { + ...editHistoryOptions, + }, + }, }); }; } -export function updateObligatoryFilter(filter: ApplyData) { +export function updateObligatoryFilter(filter: ApplyData, editHistoryOptions?: EditHistoryOptions) { return (dispatch: DatasetDispatch) => { dispatch({ type: DATASET_ACTION_TYPES.UPDATE_OBLIGATORY_FILTER, - payload: {filter}, + payload: { + filter, + [EDIT_HISTORY_OPTIONS_KEY]: { + ...editHistoryOptions, + }, + }, }); }; } -export function deleteObligatoryFilter(filterId: string) { +export function deleteObligatoryFilter(filterId: string, editHistoryOptions?: EditHistoryOptions) { return (dispatch: DatasetDispatch) => { dispatch({ type: DATASET_ACTION_TYPES.DELETE_OBLIGATORY_FILTER, - payload: {id: filterId}, + payload: { + id: filterId, + [EDIT_HISTORY_OPTIONS_KEY]: { + ...editHistoryOptions, + }, + }, }); }; } @@ -606,15 +795,6 @@ export function toggleFieldEditorModuleLoader(isLoading: boolean) { }; } -export function setAsideHeaderWidth(width: number) { - return (dispatch: DatasetDispatch) => { - dispatch({ - type: DATASET_ACTION_TYPES.SET_ASIDE_HEADER_WIDTH, - payload: {width}, - }); - }; -} - export function toggleSourcesLoader(isSourcesLoading: boolean) { return (dispatch: DatasetDispatch) => { dispatch({ @@ -816,18 +996,21 @@ export function setEditHistoryState(payload: SetEditHistoryState['payload']): Se }; } -export type AddEditHistoryPointDsArgs = { - stacked?: boolean; -}; - -export function addEditHistoryPointDs({stacked}: AddEditHistoryPointDsArgs = {}) { +export function addEditHistoryPointDs({stacked}: EditHistoryOptions = {}) { return (dispatch: DatasetDispatch, getState: GetState) => { dispatch( addEditHistoryPoint({ unitId: DATASETS_EDIT_HISTORY_UNIT_ID, newState: getState().dataset, - stacked, + stacked: stacked, }), ); }; } + +export function setCurrentTab(payload: SetCurrentTab['payload']): SetCurrentTab { + return { + type: DATASET_ACTION_TYPES.SET_CURRENT_TAB, + payload, + }; +} diff --git a/src/ui/units/datasets/store/actions/types/dataset.ts b/src/ui/units/datasets/store/actions/types/dataset.ts index 0dd2bbd4e9..2e9dfbdc1e 100644 --- a/src/ui/units/datasets/store/actions/types/dataset.ts +++ b/src/ui/units/datasets/store/actions/types/dataset.ts @@ -77,7 +77,6 @@ export const DELETE_OBLIGATORY_FILTER = Symbol('dataset/DELETE_OBLIGATORY_FILTER export const TOGGLE_FIELD_EDITOR_MODULE_LOADING = Symbol( 'dataset/TOGGLE_FIELD_EDITOR_MODULE_LOADING', ); -export const SET_ASIDE_HEADER_WIDTH = Symbol('dataset/SET_ASIDE_HEADER_WIDTH'); export const TOGGLE_SOURCES_LOADER = Symbol('dataset/TOGGLE_SOURCES_LOADER'); export const SET_SOURCES_LOADING_ERROR = Symbol('dataset/SET_SOURCES_LOADING_ERROR'); @@ -89,3 +88,5 @@ export const EDITOR_SET_ITEMS_TO_DISPLAY = Symbol('dataset/EDITOR_SET_ITEMS_TO_D export const RENAME_DATASET = Symbol('dataset/RENAME_DATASET'); export const SET_EDIT_HISTORY_STATE = Symbol('dataset/SET_EDIT_HISTORY_STATE'); + +export const SET_CURRENT_TAB = Symbol('dataset/SET_CURRENT_TAB'); diff --git a/src/ui/units/datasets/store/constants.ts b/src/ui/units/datasets/store/constants.ts index a69706b2f0..310dcbae09 100644 --- a/src/ui/units/datasets/store/constants.ts +++ b/src/ui/units/datasets/store/constants.ts @@ -1,4 +1,7 @@ import type {Dataset} from '../../../../shared'; +import type {DatasetTab} from '../constants'; +import {DATASET_TABS, TAB_DATASET, TAB_SOURCES} from '../constants'; +import DatasetUtils, {isCreationProcess} from '../helpers/utils'; import type {DatasetReduxState} from './types'; @@ -15,6 +18,21 @@ const getDefaultDatasetContent = (): Partial => ({ load_preview_by_default: true, }); +const isDatasetTab = (value: unknown): value is DatasetTab => { + return typeof value === 'string' && DATASET_TABS.includes(value); +}; + +const getCurrentTab = (): DatasetTab => { + const defaultTab = isCreationProcess() ? TAB_SOURCES : TAB_DATASET; + const queryTab = DatasetUtils.getQueryParam('tab'); + + if (isDatasetTab(queryTab)) { + return queryTab; + } + + return defaultTab; +}; + export const initialPreview: DatasetReduxState['preview'] = { previewEnabled: true, readyPreview: 'loading', @@ -63,7 +81,6 @@ export const initialState: DatasetReduxState = { }, ui: { selectedConnectionId: null, - asideHeaderWidth: null, isDatasetChanged: false, isFieldEditorModuleLoading: false, isSourcesLoading: false, @@ -78,9 +95,12 @@ export const initialState: DatasetReduxState = { sourcePrototypes: [], sourceTemplate: null, error: null, + currentTab: getCurrentTab(), }; export const getInitialState = (extra?: Partial): DatasetReduxState => ({ ...initialState, ...extra, }); + +export const EDIT_HISTORY_OPTIONS_KEY = '__editHistoryOptions__'; diff --git a/src/ui/units/datasets/store/edit-history-middleware.ts b/src/ui/units/datasets/store/edit-history-middleware.ts index b76e384861..a9fad648b7 100644 --- a/src/ui/units/datasets/store/edit-history-middleware.ts +++ b/src/ui/units/datasets/store/edit-history-middleware.ts @@ -1,91 +1,28 @@ import debounce from 'lodash/debounce'; +import get from 'lodash/get'; import type {Middleware, MiddlewareAPI} from 'redux'; -import {DATASET_VALIDATION_TIMEOUT} from '../constants'; - import {addEditHistoryPointDs} from './actions/creators/datasetTyped'; -import type { - AddEditHistoryPointDsArgs, - DatasetDispatch, - GetState, -} from './actions/creators/datasetTyped'; -import { - ADD_CONNECTION, - ADD_FIELD, - ADD_OBLIGATORY_FILTER, - AVATAR_ADD, - AVATAR_DELETE, - BATCH_DELETE_FIELDS, - BATCH_UPDATE_FIELDS, - CHANGE_AMOUNT_PREVIEW_ROWS, - CONNECTION_REPLACE, - DELETE_CONNECTION, - DELETE_FIELD, - DELETE_OBLIGATORY_FILTER, - DUPLICATE_FIELD, - PREVIEW_DATASET_FETCH_FAILURE, - PREVIEW_DATASET_FETCH_SUCCESS, - RELATION_ADD, - RELATION_DELETE, - RELATION_UPDATE, - SOURCES_REFRESH, - SOURCE_ADD, - SOURCE_DELETE, - SOURCE_REPLACE, - SOURCE_UPDATE, - TOGGLE_ALLOWANCE_SAVE, - TOGGLE_LOAD_PREVIEW_BY_DEFAULT, - UPDATE_FIELD, - UPDATE_OBLIGATORY_FILTER, - UPDATE_RLS, -} from './actions/types/dataset'; - -type HistoryActionOptions = {stacked?: boolean}; - -const HistoryAction: Record = { - [ADD_CONNECTION]: {}, - [ADD_FIELD]: {}, - [ADD_OBLIGATORY_FILTER]: {}, - [AVATAR_ADD]: {}, - [AVATAR_DELETE]: {}, - [BATCH_DELETE_FIELDS]: {}, - [BATCH_UPDATE_FIELDS]: {}, - [CHANGE_AMOUNT_PREVIEW_ROWS]: {}, - [CONNECTION_REPLACE]: {}, - [DELETE_CONNECTION]: {}, - [DELETE_FIELD]: {}, - [DELETE_OBLIGATORY_FILTER]: {}, - [DUPLICATE_FIELD]: {}, - [PREVIEW_DATASET_FETCH_FAILURE]: {stacked: true}, - [PREVIEW_DATASET_FETCH_SUCCESS]: {stacked: true}, - [RELATION_ADD]: {}, - [RELATION_DELETE]: {}, - [RELATION_UPDATE]: {}, - [SOURCES_REFRESH]: {}, - [SOURCE_ADD]: {stacked: true}, - [SOURCE_DELETE]: {}, - [SOURCE_REPLACE]: {}, - [SOURCE_UPDATE]: {}, - [TOGGLE_ALLOWANCE_SAVE]: {stacked: true}, - [TOGGLE_LOAD_PREVIEW_BY_DEFAULT]: {}, - [UPDATE_FIELD]: {}, - [UPDATE_OBLIGATORY_FILTER]: {}, - [UPDATE_RLS]: {}, -}; +import type {DatasetDispatch, GetState} from './actions/creators/datasetTyped'; +import {EDIT_HISTORY_OPTIONS_KEY} from './constants'; +import type {EditHistoryOptions} from './types'; const middlewareAction = ( store: MiddlewareAPI, - options?: AddEditHistoryPointDsArgs, + options?: EditHistoryOptions, ) => { store.dispatch(addEditHistoryPointDs(options)); }; const debouncedTmpAction = debounce(middlewareAction); -let lastInvocationTimestamp = Date.now(); function isDatasetAction(value: unknown): value is symbol { return typeof value === 'symbol' && value.toString().startsWith('Symbol(dataset'); } +function getEditHistoryOptions(action: unknown) { + return get(action, `payload.${EDIT_HISTORY_OPTIONS_KEY}`) as EditHistoryOptions | undefined; +} + export const editHistoryDsMiddleware: Middleware = (store) => (next) => (action) => { // We should dispatch history action after target action in middleware // eslint-disable-next-line callback-return @@ -95,33 +32,15 @@ export const editHistoryDsMiddleware: Middleware = (store) => (next) => (action) return; } - const historyAction = HistoryAction[action.type]; - - if (historyAction) { - const stackedByDefault = historyAction.stacked; - let betweenInvocationsTimestamp = Infinity; - - if (!stackedByDefault) { - const currentInvocationTimestamp = Date.now(); - betweenInvocationsTimestamp = currentInvocationTimestamp - lastInvocationTimestamp; - lastInvocationTimestamp = currentInvocationTimestamp; - } + const options = getEditHistoryOptions(action); - const state = store.getState(); - // Stacked actions are also those actions that do not have this attribute initially, - // but are called at the time of deferred validation. This is necessary so that all changes - // collapse into one point in the history, so that when switching between points, - // the state of the dataset corresponds to the result of validation that worked with this state. - const stacked = - stackedByDefault || - (state.dataset.savingDataset.disabled && - state.dataset.validation.isPending && - betweenInvocationsTimestamp < DATASET_VALIDATION_TIMEOUT); + if (!options) { + return; + } - if (stacked) { - debouncedTmpAction(store, {stacked}); - } else { - middlewareAction(store, {stacked}); - } + if (options.stacked) { + debouncedTmpAction(store, options); + } else { + middlewareAction(store, options); } }; diff --git a/src/ui/units/datasets/store/reducers/dataset.ts b/src/ui/units/datasets/store/reducers/dataset.ts index b3a016be6b..0a3e91be42 100644 --- a/src/ui/units/datasets/store/reducers/dataset.ts +++ b/src/ui/units/datasets/store/reducers/dataset.ts @@ -49,7 +49,7 @@ import { RELATION_DELETE, RELATION_UPDATE, RENAME_DATASET, - SET_ASIDE_HEADER_WIDTH, + SET_CURRENT_TAB, SET_DATASET_REVISION_MISMATCH, SET_EDIT_HISTORY_STATE, SET_FREEFORM_SOURCES, @@ -1262,17 +1262,6 @@ export default (state: DatasetReduxState = initialState, action: DatasetReduxAct }, }; } - case SET_ASIDE_HEADER_WIDTH: { - const {width} = action.payload; - - return { - ...state, - ui: { - ...state.ui, - asideHeaderWidth: width, - }, - }; - } case TOGGLE_SOURCES_LOADER: { const {isSourcesLoading} = action.payload; @@ -1330,6 +1319,10 @@ export default (state: DatasetReduxState = initialState, action: DatasetReduxAct case SET_EDIT_HISTORY_STATE: { return action.payload.state; } + case SET_CURRENT_TAB: { + const {currentTab} = action.payload; + return {...state, currentTab}; + } default: { return state; } diff --git a/src/ui/units/datasets/store/selectors/dataset.ts b/src/ui/units/datasets/store/selectors/dataset.ts index 1330f0432a..0973d7c81a 100644 --- a/src/ui/units/datasets/store/selectors/dataset.ts +++ b/src/ui/units/datasets/store/selectors/dataset.ts @@ -173,3 +173,5 @@ export const datasetPermissionsSelector = (state: DatalensGlobalState) => state. export const workbookIdSelector = (state: DatalensGlobalState) => { return selectedConnectionSelector(state)?.workbookId || datasetWorkbookId(state) || null; }; + +export const currentTabSelector = (state: DatalensGlobalState) => state.dataset.currentTab; diff --git a/src/ui/units/datasets/store/types/dataset.ts b/src/ui/units/datasets/store/types/dataset.ts index 81de2735a5..008cfb1f54 100644 --- a/src/ui/units/datasets/store/types/dataset.ts +++ b/src/ui/units/datasets/store/types/dataset.ts @@ -10,6 +10,7 @@ import type { WorkbookId, } from '../../../../../shared'; import type {ValidateDatasetResponse} from '../../../../../shared/schema'; +import type {DatasetTab} from '../../constants'; import type { ADD_AVATAR_PROTOTYPES, ADD_AVATAR_TEMPLATE, @@ -54,7 +55,7 @@ import type { RELATION_UPDATE, RENAME_DATASET, RESET_DATASET_STATE, - SET_ASIDE_HEADER_WIDTH, + SET_CURRENT_TAB, SET_DATASET_REVISION_MISMATCH, SET_EDIT_HISTORY_STATE, SET_FREEFORM_SOURCES, @@ -77,6 +78,7 @@ import type { UPDATE_OBLIGATORY_FILTER, UPDATE_RLS, } from '../actions/types/dataset'; +import type {EDIT_HISTORY_OPTIONS_KEY} from '../constants'; // TODO: correctly describe the type export type DatasetError = any | null; @@ -164,6 +166,14 @@ export type FreeformSource = { tab_title: TranslatedItem; } & BaseSource; +export type EditHistoryOptions = { + stacked?: boolean; +}; + +type EditHistoryOptionsProperty = { + [EDIT_HISTORY_OPTIONS_KEY]?: EditHistoryOptions; +}; + type AddFieldUpdate = { action: 'add_field'; field: Partial; @@ -282,7 +292,7 @@ export type DatasetReduxState = { isVisible: boolean; isLoading: boolean; amountPreviewRows: number; - view: 'full' | 'bottom' | 'right'; // VIEW_PREVIEW + view: 'full' | 'bottom' | 'right'; data: string[]; // TODO: correctly describe the type error: DatasetError; isQueued: boolean; @@ -313,7 +323,6 @@ export type DatasetReduxState = { }; ui: { selectedConnectionId: string | null; - asideHeaderWidth: number | null; isDatasetChanged: boolean; isFieldEditorModuleLoading: boolean; isSourcesLoading: boolean; @@ -328,6 +337,7 @@ export type DatasetReduxState = { sourcePrototypes: BaseSource[]; sourceTemplate: FreeformSource | null; // TODO: abandon this thing in favor of freeformSources error: DatasetError; + currentTab: DatasetTab; }; type SetFreeformSources = { @@ -357,13 +367,6 @@ type ToggleSourcesLoader = { }; }; -type SetAsideHeaderWidth = { - type: typeof SET_ASIDE_HEADER_WIDTH; - payload: { - width: number; - }; -}; - type ToggleFieldEditorModuleLoading = { type: typeof TOGGLE_FIELD_EDITOR_MODULE_LOADING; payload: { @@ -390,14 +393,14 @@ type DeleteConnection = { type: typeof DELETE_CONNECTION; payload: { connectionId: string; - }; + } & EditHistoryOptionsProperty; }; type AddConnection = { type: typeof ADD_CONNECTION; payload: { connection: ConnectionEntry; - }; + } & EditHistoryOptionsProperty; }; type ClickConnection = { @@ -426,7 +429,7 @@ type UpdateRls = { type: typeof UPDATE_RLS; payload: { rls: {[key: string]: string}; - }; + } & EditHistoryOptionsProperty; }; type UpdateField = { @@ -434,7 +437,7 @@ type UpdateField = { payload: { field: Partial; ignoreMergeWithSchema?: boolean; - }; + } & EditHistoryOptionsProperty; }; type BatchUpdateFields = { @@ -442,28 +445,28 @@ type BatchUpdateFields = { payload: { fields: Partial[]; ignoreMergeWithSchema?: boolean; - }; + } & EditHistoryOptionsProperty; }; type DeleteField = { type: typeof DELETE_FIELD; payload: { field: Partial; - }; + } & EditHistoryOptionsProperty; }; type BatchDeleteFields = { type: typeof BATCH_DELETE_FIELDS; payload: { fields: Partial[]; - }; + } & EditHistoryOptionsProperty; }; type DuplicateField = { type: typeof DUPLICATE_FIELD; payload: { field: DatasetField; - }; + } & EditHistoryOptionsProperty; }; type AddField = { @@ -474,7 +477,7 @@ type AddField = { // If true, the field will not be added to the schema before validation and the field will appear in the future after the response from the backend // If false, the field will be added to the old schema before validation. ignoreMergeWithSchema?: boolean; - }; + } & EditHistoryOptionsProperty; }; type ToggleViewPreview = { @@ -499,7 +502,7 @@ type ToggleLoadPreviewByDefault = { type: typeof TOGGLE_LOAD_PREVIEW_BY_DEFAULT; payload: { enable: boolean; - }; + } & EditHistoryOptionsProperty; }; type ClosePreview = { @@ -514,7 +517,7 @@ type ChangeAmountPreviewRows = { type: typeof CHANGE_AMOUNT_PREVIEW_ROWS; payload: { amountPreviewRows: number; - }; + } & EditHistoryOptionsProperty; }; export type ToggleAllowanceSave = { @@ -522,7 +525,7 @@ export type ToggleAllowanceSave = { payload: { enable: boolean; validationPending?: boolean; - }; + } & EditHistoryOptionsProperty; }; type ClearPreview = { @@ -537,56 +540,56 @@ type DeleteObligatoryFilter = { type: typeof DELETE_OBLIGATORY_FILTER; payload: { id: string; - }; + } & EditHistoryOptionsProperty; }; type UpdateObligatoryFilter = { type: typeof UPDATE_OBLIGATORY_FILTER; payload: { filter: ApplyData; - }; + } & EditHistoryOptionsProperty; }; type AddObligatoryFilter = { type: typeof ADD_OBLIGATORY_FILTER; payload: { filter: ApplyData; - }; + } & EditHistoryOptionsProperty; }; type DeleteAvatarRelation = { type: typeof RELATION_DELETE; payload: { relationId: string; - }; + } & EditHistoryOptionsProperty; }; type UpdateAvatarRelation = { type: typeof RELATION_UPDATE; payload: { relation: DatasetAvatarRelation; - }; + } & EditHistoryOptionsProperty; }; type AddAvatarRelation = { type: typeof RELATION_ADD; payload: { relation: DatasetAvatarRelation; - }; + } & EditHistoryOptionsProperty; }; type DeleteSourceAvatar = { type: typeof AVATAR_DELETE; payload: { avatarId: string; - }; + } & EditHistoryOptionsProperty; }; type AddSourceAvatar = { type: typeof AVATAR_ADD; payload: { avatar: DatasetSourceAvatar; - }; + } & EditHistoryOptionsProperty; }; type ReplaceConnection = { @@ -594,7 +597,7 @@ type ReplaceConnection = { payload: { connection?: ConnectionEntry; newConnection?: ConnectionEntry; - }; + } & EditHistoryOptionsProperty; }; type ReplaceSource = { @@ -602,46 +605,47 @@ type ReplaceSource = { payload: { source: DatasetSource; avatar: DatasetSourceAvatar; - }; + } & EditHistoryOptionsProperty; }; type SourcesRefresh = { type: typeof SOURCES_REFRESH; + payload?: EditHistoryOptionsProperty; }; type DeleteSource = { type: typeof SOURCE_DELETE; payload: { sourceId: string; - }; + } & EditHistoryOptionsProperty; }; type UpdateSource = { type: typeof SOURCE_UPDATE; payload: { source: DatasetSource; - }; + } & EditHistoryOptionsProperty; }; type AddSource = { type: typeof SOURCE_ADD; payload: { source: DatasetSource; - }; + } & EditHistoryOptionsProperty; }; type PreviewDatasetFetchFailure = { type: typeof PREVIEW_DATASET_FETCH_FAILURE; payload: { error: any; - }; + } & EditHistoryOptionsProperty; }; type PreviewDatasetFetchSuccess = { type: typeof PREVIEW_DATASET_FETCH_SUCCESS; payload: { data: any; - }; + } & EditHistoryOptionsProperty; }; type PreviewDatasetFetchRequest = { @@ -759,13 +763,19 @@ export type SetEditHistoryState = { }; }; +export type SetCurrentTab = { + type: typeof SET_CURRENT_TAB; + payload: { + currentTab: DatasetTab; + }; +}; + export type DatasetReduxAction = | SetFreeformSources | ResetDatasetState | SetDatasetRevisionMismatch | SetSourcesLoadingError | ToggleSourcesLoader - | SetAsideHeaderWidth | ToggleFieldEditorModuleLoading | AddAvatarTemplate | AddAvatarPrototypes @@ -824,4 +834,5 @@ export type DatasetReduxAction = | EditorSetFilter | EditorSetItemsToDisplay | RenameDataset - | SetEditHistoryState; + | SetEditHistoryState + | SetCurrentTab;