From 0cfef6d17d9a4ddaa572254949c5797a32fe57da Mon Sep 17 00:00:00 2001 From: Valerii Sidorenko Date: Wed, 28 Feb 2024 12:33:32 +0100 Subject: [PATCH] feat(QueryEditor): execute query with selected text --- .../Tenant/ObjectGeneral/ObjectGeneral.tsx | 2 +- src/containers/Tenant/Query/Query.tsx | 4 +- .../{QueryEditor.js => QueryEditor.tsx} | 538 +++++++++--------- .../QueryEditorControls.tsx | 8 +- src/store/reducers/executeQuery.ts | 60 +- src/types/store/executeQuery.ts | 9 +- src/types/store/explainQuery.ts | 1 + 7 files changed, 308 insertions(+), 314 deletions(-) rename src/containers/Tenant/Query/QueryEditor/{QueryEditor.js => QueryEditor.tsx} (55%) diff --git a/src/containers/Tenant/ObjectGeneral/ObjectGeneral.tsx b/src/containers/Tenant/ObjectGeneral/ObjectGeneral.tsx index e078e9d0fd..b5c67efbbb 100644 --- a/src/containers/Tenant/ObjectGeneral/ObjectGeneral.tsx +++ b/src/containers/Tenant/ObjectGeneral/ObjectGeneral.tsx @@ -18,7 +18,7 @@ import './ObjectGeneral.scss'; const b = cn('object-general'); interface ObjectGeneralProps { - type?: EPathType; + type: EPathType; additionalTenantProps?: AdditionalTenantsProps; additionalNodesProps?: AdditionalNodesProps; } diff --git a/src/containers/Tenant/Query/Query.tsx b/src/containers/Tenant/Query/Query.tsx index fcc9bd0270..b682833457 100644 --- a/src/containers/Tenant/Query/Query.tsx +++ b/src/containers/Tenant/Query/Query.tsx @@ -19,8 +19,8 @@ const b = block('ydb-query'); interface QueryProps { theme: string; - path?: string; - type?: EPathType; + path: string; + type: EPathType; } export const Query = (props: QueryProps) => { diff --git a/src/containers/Tenant/Query/QueryEditor/QueryEditor.js b/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx similarity index 55% rename from src/containers/Tenant/Query/QueryEditor/QueryEditor.js rename to src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx index 4298a5a0a0..0cd675a457 100644 --- a/src/containers/Tenant/Query/QueryEditor/QueryEditor.js +++ b/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx @@ -1,9 +1,9 @@ import {useEffect, useReducer, useRef, useState} from 'react'; -import PropTypes from 'prop-types'; import {connect} from 'react-redux'; import cn from 'bem-cn-lite'; import _ from 'lodash'; import MonacoEditor from 'react-monaco-editor'; +import type Monaco from 'monaco-editor'; import SplitPane from '../../../../components/SplitPane'; @@ -12,8 +12,6 @@ import { saveQueryToHistory, goToPreviousQuery, goToNextQuery, - MONACO_HOT_KEY_ACTIONS, - setMonacoHotKey, setTenantPath, } from '../../../../store/reducers/executeQuery'; import {getExplainQuery, getExplainQueryAst} from '../../../../store/reducers/explainQuery'; @@ -27,8 +25,10 @@ import { } from '../../../../utils/constants'; import {useSetting, useQueryModes} from '../../../../utils/hooks'; import {QUERY_ACTIONS} from '../../../../utils/query'; +import {parseJson} from '../../../../utils/utils'; import { + InitialPaneState, PaneVisibilityActionTypes, paneVisibilityToggleReducerCreator, } from '../../utils/paneVisibilityToggleHelpers'; @@ -38,6 +38,13 @@ import {ExecuteResult} from '../ExecuteResult/ExecuteResult'; import {ExplainResult} from '../ExplainResult/ExplainResult'; import {QueryEditorControls} from '../QueryEditorControls/QueryEditorControls'; +import type {QueryAction, QueryMode, SavedQuery} from '../../../../types/store/query'; +import type {ExecuteQueryState} from '../../../../types/store/executeQuery'; +import type {ExplainQueryState} from '../../../../types/store/explainQuery'; +import type {ValueOf} from '../../../../types/common'; +import type {EPathType} from '../../../../types/api/schema'; +import type {RootState} from '../../../../store'; + import './QueryEditor.scss'; const EDITOR_OPTIONS = { @@ -53,30 +60,48 @@ const RESULT_TYPES = { EXECUTE: 'execute', EXPLAIN: 'explain', }; +const MONACO_HOT_KEY_ACTIONS = { + sendQuery: 'sendQuery', + sendSelectedQuery: 'sendSelectedQuery', +}; const b = cn('query-editor'); -const propTypes = { - sendExecuteQuery: PropTypes.func, - changeUserInput: PropTypes.func, - path: PropTypes.string, - response: PropTypes.oneOfType([PropTypes.bool, PropTypes.array]), - executeQuery: PropTypes.object, - explainQuery: PropTypes.object, - setMonacoHotKey: PropTypes.func, - theme: PropTypes.string, - type: PropTypes.string, - initialQueryMode: PropTypes.string, - setTenantPath: PropTypes.func, -}; - const initialTenantCommonInfoState = { triggerExpand: false, triggerCollapse: false, collapsed: true, }; -function QueryEditor(props) { - const {path, executeQuery, theme, setTenantPath, changeUserInput} = props; + +interface QueryEditorProps { + path: string; + sendExecuteQuery: (...args: Parameters) => void; + getExplainQuery: (...args: Parameters) => void; + getExplainQueryAst: (...args: Parameters) => void; + changeUserInput: (arg: {input: string}) => void; + goToNextQuery: (...args: Parameters) => void; + goToPreviousQuery: (...args: Parameters) => void; + setTenantPath: (...args: Parameters) => void; + executeQuery: ExecuteQueryState; + explainQuery: ExplainQueryState; + theme: string; + type: EPathType; + showPreview: boolean; + setShowPreview: (...args: Parameters) => void; + saveQueryToHistory: (...args: Parameters) => void; +} + +function QueryEditor(props: QueryEditorProps) { + const { + path, + setTenantPath: setPath, + executeQuery, + explainQuery, + type, + theme, + changeUserInput, + showPreview, + } = props; const {tenantPath: savedPath} = executeQuery; const [resultType, setResultType] = useState(RESULT_TYPES.EXECUTE); @@ -84,39 +109,63 @@ function QueryEditor(props) { const [isResultLoaded, setIsResultLoaded] = useState(false); const [queryMode, setQueryMode] = useQueryModes(); const [useMultiSchema] = useSetting(QUERY_USE_MULTI_SCHEMA_KEY); - const [lastUsedQueryAction, setLastUsedQueryAction] = useSetting(LAST_USED_QUERY_ACTION_KEY); - const [savedQueries, setSavedQueries] = useSetting(SAVED_QUERIES_KEY); + const [lastUsedQueryAction, setLastUsedQueryAction] = useSetting( + LAST_USED_QUERY_ACTION_KEY, + ); + const [savedQueries, setSavedQueries] = useSetting(SAVED_QUERIES_KEY); + const [monacoHotKey, setMonacoHotKey] = useState | null>( + null, + ); useEffect(() => { if (savedPath !== path) { if (savedPath) { changeUserInput({input: ''}); } - setTenantPath(path); + setPath(path); } - }, [changeUserInput, setTenantPath, path, savedPath]); + }, [changeUserInput, setPath, path, savedPath]); const [resultVisibilityState, dispatchResultVisibilityState] = useReducer( paneVisibilityToggleReducerCreator(DEFAULT_IS_QUERY_RESULT_COLLAPSED), initialTenantCommonInfoState, ); - const editorRef = useRef(null); + const editorRef = useRef(); useEffect(() => { + const updateEditor = () => { + if (editorRef.current) { + editorRef.current.layout(); + } + }; + + const onChangeWindow = _.throttle(() => { + updateEditor(); + }, 100); + updateEditor(); window.addEventListener('resize', onChangeWindow); - window.addEventListener('storage', storageEventHandler); - return () => { window.removeEventListener('resize', onChangeWindow); - window.removeEventListener('storage', storageEventHandler); - window.onbeforeunload = undefined; - props.setMonacoHotKey(null); }; }, []); + useEffect(() => { + const storageEventHandler = (event: StorageEvent) => { + if (event.key === SAVED_QUERIES_KEY) { + const v = parseJson(event.newValue); + setSavedQueries(v); + } + }; + + window.addEventListener('storage', storageEventHandler); + return () => { + window.removeEventListener('storage', storageEventHandler); + }; + }, [setSavedQueries]); + useEffect(() => { dispatchResultVisibilityState(PaneVisibilityActionTypes.triggerCollapse); }, []); @@ -130,28 +179,73 @@ function QueryEditor(props) { }, [props.showPreview, isResultLoaded]); useEffect(() => { - const { - explainQuery: {data}, - executeQuery: {input, history}, - } = props; + const {input, history} = executeQuery; const hasUnsavedInput = input - ? input !== history.queries[history.queries?.length - 1] + ? input !== history.queries[history.queries.length - 1]?.queryText : false; if (hasUnsavedInput) { - window.onbeforeunload = checkIfHasUnsavedInput; + window.onbeforeunload = (e) => { + e.preventDefault(); + // Chrome requires returnValue to be set + e.returnValue = ''; + }; } else { - window.onbeforeunload = undefined; + window.onbeforeunload = null; } + return () => { + window.onbeforeunload = null; + }; + }, [executeQuery]); - if (!data || resultType !== RESULT_TYPES.EXPLAIN) { + const handleSendExecuteClick = (mode: QueryMode | undefined, text?: string) => { + if (!mode) { return; } - }, [props.executeQuery, props.executeQuery]); + const {input, history} = executeQuery; + + const schema = useMultiSchema ? 'multi' : 'modern'; + + const query = text ?? input; + + setLastUsedQueryAction(QUERY_ACTIONS.execute); + setResultType(RESULT_TYPES.EXECUTE); + props.sendExecuteQuery({ + query, + database: path, + mode, + schema, + }); + setIsResultLoaded(true); + props.setShowPreview(false); + + // Don't save partial queries in history + if (!text) { + const {queries, currentIndex} = history; + if (query !== queries[currentIndex]?.queryText) { + props.saveQueryToHistory(input, mode); + } + } + dispatchResultVisibilityState(PaneVisibilityActionTypes.triggerExpand); + }; + + const handleGetExplainQueryClick = (mode: QueryMode | undefined) => { + const {input} = executeQuery; + + setLastUsedQueryAction(QUERY_ACTIONS.explain); + setResultType(RESULT_TYPES.EXPLAIN); + props.getExplainQuery({ + query: input, + database: path, + mode: mode, + }); + setIsResultLoaded(true); + props.setShowPreview(false); + dispatchResultVisibilityState(PaneVisibilityActionTypes.triggerExpand); + }; useEffect(() => { - const {monacoHotKey, setMonacoHotKey} = props; if (monacoHotKey === null) { return; } @@ -159,34 +253,31 @@ function QueryEditor(props) { switch (monacoHotKey) { case MONACO_HOT_KEY_ACTIONS.sendQuery: { if (lastUsedQueryAction === QUERY_ACTIONS.explain) { - return handleGetExplainQueryClick(queryMode); + handleGetExplainQueryClick(queryMode); } else { - return handleSendExecuteClick(queryMode); + handleSendExecuteClick(queryMode); } + break; } - case MONACO_HOT_KEY_ACTIONS.goPrev: { - return handlePreviousHistoryClick(); - } - case MONACO_HOT_KEY_ACTIONS.goNext: { - return handleNextHistoryClick(); - } - default: { - return; + case MONACO_HOT_KEY_ACTIONS.sendSelectedQuery: { + const selection = editorRef.current?.getSelection(); + const model = editorRef.current?.getModel(); + if (selection && model) { + const text = model.getValueInRange({ + startLineNumber: selection.getSelectionStart().lineNumber, + startColumn: selection.getSelectionStart().column, + endLineNumber: selection.getPosition().lineNumber, + endColumn: selection.getPosition().column, + }); + handleSendExecuteClick(queryMode, text); + } + break; } } - }, [props.monacoHotKey]); - - const checkIfHasUnsavedInput = (e) => { - e.preventDefault(); - // Chrome requires returnValue to be set - e.returnValue = ''; - }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [monacoHotKey]); - const handleKeyBinding = (value) => { - return () => props.setMonacoHotKey(value); - }; - - const editorDidMount = (editor, monaco) => { + const editorDidMount = (editor: Monaco.editor.IStandaloneCodeEditor, monaco: typeof Monaco) => { editorRef.current = editor; editor.focus(); editor.addAction({ @@ -197,14 +288,35 @@ function QueryEditor(props) { monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, ], // A precondition for this action. - precondition: null, + precondition: undefined, // A rule to evaluate on top of the precondition in order to dispatch the keybindings. - keybindingContext: null, + keybindingContext: undefined, contextMenuGroupId: CONTEXT_MENU_GROUP_ID, contextMenuOrder: 1, // Method that will be executed when the action is triggered. - // @param editor The editor instance is passed in as a convinience - run: handleKeyBinding(MONACO_HOT_KEY_ACTIONS.sendQuery), + // @param editor The editor instance is passed in as a convenience + run: () => setMonacoHotKey(MONACO_HOT_KEY_ACTIONS.sendQuery), + }); + + const canSendSelectedText = editor.createContextKey('canSendSelectedText', false); + editor.onDidChangeCursorSelection(({selection, secondarySelections}) => { + const notEmpty = + selection.selectionStartLineNumber !== selection.positionLineNumber || + selection.selectionStartColumn !== selection.positionColumn; + const hasMultipleSelections = secondarySelections.length > 0; + canSendSelectedText.set(notEmpty && !hasMultipleSelections); + }); + editor.addAction({ + id: 'sendSelectedQuery', + label: 'Send selected query', + keybindings: [ + // eslint-disable-next-line no-bitwise + monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.Enter, + ], + precondition: 'canSendSelectedText', + contextMenuGroupId: CONTEXT_MENU_GROUP_ID, + contextMenuOrder: 1, + run: () => setMonacoHotKey(MONACO_HOT_KEY_ACTIONS.sendSelectedQuery), }); editor.addAction({ @@ -216,7 +328,9 @@ function QueryEditor(props) { ], contextMenuGroupId: CONTEXT_MENU_GROUP_ID, contextMenuOrder: 2, - run: handleKeyBinding(MONACO_HOT_KEY_ACTIONS.goPrev), + run: () => { + props.goToPreviousQuery(); + }, }); editor.addAction({ id: 'next-query', @@ -227,69 +341,18 @@ function QueryEditor(props) { ], contextMenuGroupId: CONTEXT_MENU_GROUP_ID, contextMenuOrder: 3, - run: handleKeyBinding(MONACO_HOT_KEY_ACTIONS.goNext), + run: () => { + props.goToNextQuery(); + }, }); }; - const onChange = (newValue) => { - props.changeUserInput({input: newValue}); - }; - - const handleSendExecuteClick = (mode) => { - const { - path, - executeQuery: {input, history}, - sendExecuteQuery, - saveQueryToHistory, - setShowPreview, - } = props; - - const schema = useMultiSchema ? 'multi' : 'modern'; - setLastUsedQueryAction(QUERY_ACTIONS.execute); - setResultType(RESULT_TYPES.EXECUTE); - sendExecuteQuery({ - query: input, - database: path, - mode, - schema, - }); - setIsResultLoaded(true); - setShowPreview(false); - - const {queries, currentIndex} = history; - if (input !== queries[currentIndex]) { - saveQueryToHistory(input, mode); - } - dispatchResultVisibilityState(PaneVisibilityActionTypes.triggerExpand); - }; - - const handleGetExplainQueryClick = (mode) => { - const { - path, - executeQuery: {input}, - getExplainQuery, - setShowPreview, - } = props; - - setLastUsedQueryAction(QUERY_ACTIONS.explain); - setResultType(RESULT_TYPES.EXPLAIN); - getExplainQuery({ - query: input, - database: path, - mode: mode, - }); - setIsResultLoaded(true); - setShowPreview(false); - dispatchResultVisibilityState(PaneVisibilityActionTypes.triggerExpand); + const onChange = (newValue: string) => { + props.changeUserInput({input: newValue}); }; const handleAstQuery = () => { - const { - path, - executeQuery: {input}, - getExplainQueryAst, - } = props; - getExplainQueryAst({query: input, database: path}); + props.getExplainQueryAst({query: executeQuery.input, database: path}); }; const onCollapseResultHandler = () => { @@ -303,152 +366,24 @@ function QueryEditor(props) { dispatchResultVisibilityState(PaneVisibilityActionTypes.clear); }; - const renderExecuteQuery = () => { - const { - executeQuery: {data, error, stats}, - } = props; - - return data || error ? ( - - ) : null; - }; - - const renderExplainQuery = () => { - const { - explainQuery: {data, dataAst, error, loading, loadingAst}, - theme, - } = props; - - return ( - - ); - }; - - const renderResult = () => { - let result; - switch (resultType) { - case RESULT_TYPES.EXECUTE: - result = renderExecuteQuery(); - break; - case RESULT_TYPES.EXPLAIN: - result = renderExplainQuery(); - break; - default: - result = null; - } - - return result; - }; - - const renderPreview = () => { - const {path, type} = props; - return ; - }; - - const handlePreviousHistoryClick = () => { - const { - changeUserInput, - executeQuery: {history}, - goToPreviousQuery, - } = props; - const {queries, currentIndex} = history; - - if (previousButtonIsDisabled()) { - return; - } - - goToPreviousQuery(); - changeUserInput({input: queries[currentIndex - 1]}); - }; - - const handleNextHistoryClick = () => { - const { - changeUserInput, - executeQuery: {history}, - goToNextQuery, - } = props; - const {queries, currentIndex} = history; - - if (nextButtonIsDisabled()) { - return; - } - - goToNextQuery(); - changeUserInput({input: queries[currentIndex + 1]}); - }; - - const previousButtonIsDisabled = () => { - const { - history: {currentIndex}, - } = props.executeQuery; - - return currentIndex <= 0; - }; - - const nextButtonIsDisabled = () => { - const { - history: {queries, currentIndex}, - } = props.executeQuery; - - return queries.length - 1 === currentIndex; - }; - - const onChangeWindow = _.throttle(() => { - updateEditor(); - }, 100); - - const storageEventHandler = (event) => { - if (event.key === SAVED_QUERIES_KEY) { - setSavedQueries(event.newValue); - } - }; - - const updateEditor = () => { - if (editorRef.current) { - editorRef.current.layout(); - } - }; - - const onSaveQueryHandler = (queryName) => { - const { - executeQuery: {input}, - } = props; + const onSaveQueryHandler = (queryName: string) => { + const {input} = executeQuery; const queryIndex = savedQueries.findIndex( (el) => el.name.toLowerCase() === queryName.toLowerCase(), ); const newSavedQueries = [...savedQueries]; const newQuery = {name: queryName, body: input}; - if (queryIndex !== -1) { - newSavedQueries[queryIndex] = newQuery; - } else { + if (queryIndex === -1) { newSavedQueries.push(newQuery); + } else { + newSavedQueries[queryIndex] = newQuery; } setSavedQueries(newSavedQueries); }; const renderControls = () => { - const {executeQuery, explainQuery} = props; - return ( ); }; - const result = renderResult(); - return (
- {props.showPreview ? renderPreview() : result} +
); } -const mapStateToProps = (state) => { +const mapStateToProps = (state: RootState) => { return { executeQuery: state.executeQuery, explainQuery: state.explainQuery, showPreview: state.schema.showPreview, - currentSchema: state.schema.currentSchema, - monacoHotKey: state.executeQuery?.monacoHotKey, }; }; @@ -523,10 +466,73 @@ const mapDispatchToProps = { getExplainQuery, getExplainQueryAst, setShowPreview, - setMonacoHotKey, setTenantPath, }; -QueryEditor.propTypes = propTypes; - export default connect(mapStateToProps, mapDispatchToProps)(QueryEditor); + +interface ResultProps { + executeQuery: ExecuteQueryState; + explainQuery: ExplainQueryState; + resultVisibilityState: InitialPaneState; + onExpandResultHandler: () => void; + onCollapseResultHandler: () => void; + type: EPathType; + handleAstQuery: () => void; + theme: string; + resultType: ValueOf | undefined; + path: string; + showPreview?: boolean; +} +function Result({ + executeQuery, + explainQuery, + resultVisibilityState, + onExpandResultHandler, + onCollapseResultHandler, + type, + handleAstQuery, + theme, + resultType, + path, + showPreview, +}: ResultProps) { + if (showPreview) { + return ; + } + + if (resultType === RESULT_TYPES.EXECUTE) { + const {data, error, stats} = executeQuery; + return data || error ? ( + + ) : null; + } + + if (resultType === RESULT_TYPES.EXPLAIN) { + const {data, dataAst, error, loading, loadingAst} = explainQuery; + + return ( + + ); + } + + return null; +} diff --git a/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx b/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx index ac5c34076d..2248fa0b25 100644 --- a/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx +++ b/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx @@ -52,7 +52,7 @@ interface QueryEditorControlsProps { disabled: boolean; onUpdateQueryMode: (mode: QueryMode) => void; queryMode: QueryMode; - highlitedAction: QueryAction; + highlightedAction: QueryAction; } export const QueryEditorControls = ({ @@ -65,7 +65,7 @@ export const QueryEditorControls = ({ disabled, onUpdateQueryMode, queryMode, - highlitedAction, + highlightedAction, }: QueryEditorControlsProps) => { const querySelectorMenuItems = useMemo(() => { return Object.entries(QueryModeSelectorOptions).map(([mode, {title, description}]) => { @@ -85,9 +85,9 @@ export const QueryEditorControls = ({ }); }, [onUpdateQueryMode]); - const runView: ButtonView | undefined = highlitedAction === 'execute' ? 'action' : undefined; + const runView: ButtonView | undefined = highlightedAction === 'execute' ? 'action' : undefined; const explainView: ButtonView | undefined = - highlitedAction === 'explain' ? 'action' : undefined; + highlightedAction === 'explain' ? 'action' : undefined; return (
diff --git a/src/store/reducers/executeQuery.ts b/src/store/reducers/executeQuery.ts index efb7f5e87f..934a0aef60 100644 --- a/src/store/reducers/executeQuery.ts +++ b/src/store/reducers/executeQuery.ts @@ -5,7 +5,6 @@ import type { ExecuteQueryAction, ExecuteQueryState, ExecuteQueryStateSlice, - MonacoHotKeyAction, QueryInHistory, } from '../../types/store/executeQuery'; import type {QueryRequestParams, QueryMode, QuerySyntax} from '../../types/store/query'; @@ -24,7 +23,6 @@ const CHANGE_USER_INPUT = 'query/CHANGE_USER_INPUT'; const SAVE_QUERY_TO_HISTORY = 'query/SAVE_QUERY_TO_HISTORY'; const GO_TO_PREVIOUS_QUERY = 'query/GO_TO_PREVIOUS_QUERY'; const GO_TO_NEXT_QUERY = 'query/GO_TO_NEXT_QUERY'; -const SET_MONACO_HOT_KEY = 'query/SET_MONACO_HOT_KEY'; const SET_TENANT_PATH = 'query/SET_TENANT_PATH'; const queriesHistoryInitial = settingsManager.readUserSettingsValue( @@ -34,23 +32,18 @@ const queriesHistoryInitial = settingsManager.readUserSettingsValue( const sliceLimit = queriesHistoryInitial.length - MAXIMUM_QUERIES_IN_HISTORY; -export const MONACO_HOT_KEY_ACTIONS = { - sendQuery: 'sendQuery', - goPrev: 'goPrev', - goNext: 'goNext', -} as const; - const initialState = { loading: false, input: '', history: { - queries: queriesHistoryInitial.slice(sliceLimit < 0 ? 0 : sliceLimit), + queries: queriesHistoryInitial + .slice(sliceLimit < 0 ? 0 : sliceLimit) + .map(getQueryInHistory), currentIndex: queriesHistoryInitial.length > MAXIMUM_QUERIES_IN_HISTORY ? MAXIMUM_QUERIES_IN_HISTORY - 1 : queriesHistoryInitial.length - 1, }, - monacoHotKey: null, }; const executeQuery: Reducer = ( @@ -112,7 +105,12 @@ const executeQuery: Reducer = ( } case GO_TO_PREVIOUS_QUERY: { - const newCurrentIndex = Math.max(0, state.history.currentIndex - 1); + const currentIndex = state.history.currentIndex; + if (currentIndex <= 0) { + return state; + } + const newCurrentIndex = currentIndex - 1; + const query = state.history.queries[newCurrentIndex]; return { ...state, @@ -120,12 +118,18 @@ const executeQuery: Reducer = ( ...state.history, currentIndex: newCurrentIndex, }, + input: query.queryText, }; } case GO_TO_NEXT_QUERY: { const lastIndexInHistory = state.history.queries.length - 1; - const newCurrentIndex = Math.min(lastIndexInHistory, state.history.currentIndex + 1); + const currentIndex = state.history.currentIndex; + if (currentIndex >= lastIndexInHistory) { + return state; + } + const newCurrentIndex = currentIndex + 1; + const query = state.history.queries[newCurrentIndex]; return { ...state, @@ -133,15 +137,10 @@ const executeQuery: Reducer = ( ...state.history, currentIndex: newCurrentIndex, }, + input: query.queryText, }; } - case SET_MONACO_HOT_KEY: { - return { - ...state, - monacoHotKey: action.data, - }; - } case SET_TENANT_PATH: { return { ...state, @@ -210,13 +209,6 @@ export const changeUserInput = ({input}: {input: string}) => { } as const; }; -export const setMonacoHotKey = (value: MonacoHotKeyAction | null) => { - return { - type: SET_MONACO_HOT_KEY, - data: value, - } as const; -}; - export const setTenantPath = (value: string) => { return { type: SET_TENANT_PATH, @@ -225,14 +217,16 @@ export const setTenantPath = (value: string) => { }; export const selectQueriesHistory = (state: ExecuteQueryStateSlice): QueryInHistory[] => { - return state.executeQuery.history.queries.map((rawQuery) => { - if (typeof rawQuery === 'string') { - return { - queryText: rawQuery, - }; - } - return rawQuery; - }); + return state.executeQuery.history.queries; }; +function getQueryInHistory(rawQuery: string | QueryInHistory) { + if (typeof rawQuery === 'string') { + return { + queryText: rawQuery, + }; + } + return rawQuery; +} + export default executeQuery; diff --git a/src/types/store/executeQuery.ts b/src/types/store/executeQuery.ts index b65ba4b5b9..a8c94f473e 100644 --- a/src/types/store/executeQuery.ts +++ b/src/types/store/executeQuery.ts @@ -3,17 +3,12 @@ import { changeUserInput, saveQueryToHistory, goToPreviousQuery, - setMonacoHotKey, goToNextQuery, setTenantPath, - MONACO_HOT_KEY_ACTIONS, } from '../../store/reducers/executeQuery'; import type {ApiRequestAction} from '../../store/utils'; -import type {ValueOf} from '../common'; import type {IQueryResult, QueryError, QueryErrorResponse} from './query'; -export type MonacoHotKeyAction = ValueOf; - export interface QueryInHistory { queryText: string; syntax?: string; @@ -24,10 +19,9 @@ export interface ExecuteQueryState { input: string; history: { // String type for backward compatibility - queries: (QueryInHistory | string)[]; + queries: QueryInHistory[]; currentIndex: number; }; - monacoHotKey: null | MonacoHotKeyAction; tenantPath?: string; data?: IQueryResult; stats?: IQueryResult['stats']; @@ -42,7 +36,6 @@ export type ExecuteQueryAction = | ReturnType | ReturnType | ReturnType - | ReturnType | ReturnType; export interface ExecuteQueryStateSlice { diff --git a/src/types/store/explainQuery.ts b/src/types/store/explainQuery.ts index 1bc89a2f25..43e80e3960 100644 --- a/src/types/store/explainQuery.ts +++ b/src/types/store/explainQuery.ts @@ -18,6 +18,7 @@ export interface PreparedExplainResponse { export interface ExplainQueryState { loading: boolean; + loadingAst?: boolean; data?: PreparedExplainResponse['plan']; dataAst?: PreparedExplainResponse['ast']; error?: string | QueryErrorResponse;