Skip to content

Commit

Permalink
fix(QueryEditor): dont render Results every time editor input changes (
Browse files Browse the repository at this point in the history
  • Loading branch information
Raubzeug authored Jan 27, 2025
1 parent ba22a39 commit 4db34be
Show file tree
Hide file tree
Showing 3 changed files with 240 additions and 197 deletions.
205 changes: 15 additions & 190 deletions src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,17 @@
import React from 'react';

import NiceModal from '@ebay/nice-modal-react';
import {isEqual} from 'lodash';
import throttle from 'lodash/throttle';
import type Monaco from 'monaco-editor';
import {v4 as uuidv4} from 'uuid';

import {MonacoEditor} from '../../../../components/MonacoEditor/MonacoEditor';
import SplitPane from '../../../../components/SplitPane';
import {useTracingLevelOptionAvailable} from '../../../../store/reducers/capabilities/hooks';
import {
goToNextQuery,
goToPreviousQuery,
queryApi,
saveQueryToHistory,
selectQueriesHistory,
selectQueriesHistoryCurrentIndex,
selectResult,
selectTenantPath,
selectUserInput,
setTenantPath,
} from '../../../../store/reducers/query/query';
import type {QueryResult} from '../../../../store/reducers/query/types';
Expand All @@ -41,8 +34,6 @@ import {
} from '../../../../utils/hooks';
import {useChangedQuerySettings} from '../../../../utils/hooks/useChangedQuerySettings';
import {useLastQueryExecutionSettings} from '../../../../utils/hooks/useLastQueryExecutionSettings';
import {YQL_LANGUAGE_ID} from '../../../../utils/monaco/constats';
import {useUpdateErrorsHighlighting} from '../../../../utils/monaco/highlightErrors';
import {QUERY_ACTIONS} from '../../../../utils/query';
import type {InitialPaneState} from '../../utils/paneVisibilityToggleHelpers';
import {
Expand All @@ -53,16 +44,11 @@ import {Preview} from '../Preview/Preview';
import {QueryEditorControls} from '../QueryEditorControls/QueryEditorControls';
import {QueryResultViewer} from '../QueryResult/QueryResultViewer';
import {QuerySettingsDialog} from '../QuerySettingsDialog/QuerySettingsDialog';
import {SAVE_QUERY_DIALOG} from '../SaveQuery/SaveQuery';
import i18n from '../i18n';

import {useEditorOptions} from './helpers';
import {getKeyBindings} from './keybindings';
import {YqlEditor} from './YqlEditor';

import './QueryEditor.scss';

const CONTEXT_MENU_GROUP_ID = 'navigation';

const b = cn('query-editor');

const initialTenantCommonInfoState = {
Expand All @@ -80,18 +66,14 @@ interface QueryEditorProps {
}

export default function QueryEditor(props: QueryEditorProps) {
const editorOptions = useEditorOptions();
const dispatch = useTypedDispatch();
const {tenantName, path, type, theme, changeUserInput} = props;
const savedPath = useTypedSelector(selectTenantPath);
const result = useTypedSelector(selectResult);
const historyQueries = useTypedSelector(selectQueriesHistory);
const historyCurrentIndex = useTypedSelector(selectQueriesHistoryCurrentIndex);
const input = useTypedSelector(selectUserInput);
const showPreview = useTypedSelector(selectShowPreview);

const updateErrorsHighlighting = useUpdateErrorsHighlighting();

const isResultLoaded = Boolean(result);

const [querySettings] = useQueryExecutionSettings();
Expand Down Expand Up @@ -130,18 +112,9 @@ export default function QueryEditor(props: QueryEditorProps) {
}
}, [showPreview, isResultLoaded]);

const getLastQueryText = useEventHandler(() => {
if (!historyQueries || historyQueries.length === 0) {
return '';
}
return historyQueries[historyQueries.length - 1].queryText;
});

const handleSendExecuteClick = useEventHandler((text?: string) => {
const query = text ?? input;

const handleSendExecuteClick = useEventHandler((text: string, partial?: boolean) => {
setLastUsedQueryAction(QUERY_ACTIONS.execute);
setLastExecutedQueryText(query);
setLastExecutedQueryText(text);
if (!isEqual(lastQueryExecutionSettings, querySettings)) {
resetBanner();
setLastQueryExecutionSettings(querySettings);
Expand All @@ -150,7 +123,7 @@ export default function QueryEditor(props: QueryEditorProps) {

sendQuery({
actionType: 'execute',
query,
query: text,
database: tenantName,
querySettings,
enableTracingLevel,
Expand All @@ -160,9 +133,9 @@ export default function QueryEditor(props: QueryEditorProps) {
dispatch(setShowPreview(false));

// Don't save partial queries in history
if (!text) {
if (query !== historyQueries[historyCurrentIndex]?.queryText) {
dispatch(saveQueryToHistory({queryText: input, queryId}));
if (!partial) {
if (text !== historyQueries[historyCurrentIndex]?.queryText) {
dispatch(saveQueryToHistory({queryText: text, queryId}));
}
}
dispatchResultVisibilityState(PaneVisibilityActionTypes.triggerExpand);
Expand All @@ -172,9 +145,9 @@ export default function QueryEditor(props: QueryEditorProps) {
dispatch(setQueryAction('settings'));
};

const handleGetExplainQueryClick = useEventHandler(() => {
const handleGetExplainQueryClick = useEventHandler((text: string) => {
setLastUsedQueryAction(QUERY_ACTIONS.explain);
setLastExecutedQueryText(input);
setLastExecutedQueryText(text);
if (!isEqual(lastQueryExecutionSettings, querySettings)) {
resetBanner();
setLastQueryExecutionSettings(querySettings);
Expand All @@ -184,7 +157,7 @@ export default function QueryEditor(props: QueryEditorProps) {

sendQuery({
actionType: 'explain',
query: input,
query: text,
database: tenantName,
querySettings,
enableTracingLevel,
Expand All @@ -196,113 +169,6 @@ export default function QueryEditor(props: QueryEditorProps) {
dispatchResultVisibilityState(PaneVisibilityActionTypes.triggerExpand);
});

const handleSendQuery = useEventHandler(() => {
if (lastUsedQueryAction === QUERY_ACTIONS.explain) {
handleGetExplainQueryClick();
} else {
handleSendExecuteClick();
}
});

const editorWillUnmount = () => {
window.ydbEditor = undefined;
};

const editorDidMount = (editor: Monaco.editor.IStandaloneCodeEditor, monaco: typeof Monaco) => {
window.ydbEditor = editor;
const keybindings = getKeyBindings(monaco);
monaco.editor.registerCommand('insertSnippetToEditor', (_asessor, input: string) => {
//suggestController is not properly typed yet in monaco-editor package
const contribution = editor.getContribution<any>('snippetController2');
if (contribution) {
editor.focus();
editor.setValue('');
contribution.insert(input);
}
});
initResizeHandler(editor);
initUserPrompt(editor, getLastQueryText);
editor.focus();
editor.addAction({
id: 'sendQuery',
label: i18n('action.send-query'),
keybindings: [keybindings.sendQuery],
// A precondition for this action.
precondition: undefined,
// A rule to evaluate on top of the precondition in order to dispatch the keybindings.
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 convenience
run: () => handleSendQuery(),
});

const canSendSelectedText = editor.createContextKey<boolean>('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: i18n('action.send-selected-query'),
keybindings: [keybindings.sendSelectedQuery],
precondition: 'canSendSelectedText',
contextMenuGroupId: CONTEXT_MENU_GROUP_ID,
contextMenuOrder: 1,
run: (e) => {
const selection = e.getSelection();
const model = e.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(text);
}
},
});

editor.addAction({
id: 'previous-query',
label: i18n('action.previous-query'),
keybindings: [keybindings.selectPreviousQuery],
contextMenuGroupId: CONTEXT_MENU_GROUP_ID,
contextMenuOrder: 2,
run: () => {
dispatch(goToPreviousQuery());
},
});
editor.addAction({
id: 'next-query',
label: i18n('action.next-query'),
keybindings: [keybindings.selectNextQuery],
contextMenuGroupId: CONTEXT_MENU_GROUP_ID,
contextMenuOrder: 3,
run: () => {
dispatch(goToNextQuery());
},
});
editor.addAction({
id: 'save-query',
label: i18n('action.save-query'),
keybindings: [keybindings.saveQuery],
run: () => {
NiceModal.show(SAVE_QUERY_DIALOG);
},
});
};

const onChange = (newValue: string) => {
updateErrorsHighlighting();
changeUserInput({input: newValue});
};

const onCollapseResultHandler = () => {
dispatchResultVisibilityState(PaneVisibilityActionTypes.triggerCollapse);
};
Expand All @@ -321,7 +187,6 @@ export default function QueryEditor(props: QueryEditorProps) {
onSettingsButtonClick={handleSettingsClick}
isLoading={Boolean(result?.isLoading)}
handleGetExplainQueryClick={handleGetExplainQueryClick}
disabled={!input}
highlightedAction={lastUsedQueryAction}
/>
);
Expand All @@ -345,14 +210,11 @@ export default function QueryEditor(props: QueryEditorProps) {
>
<div className={b('monaco-wrapper')}>
<div className={b('monaco')}>
<MonacoEditor
language={YQL_LANGUAGE_ID}
value={input}
options={editorOptions}
onChange={onChange}
editorDidMount={editorDidMount}
theme={`vs-${theme}`}
editorWillUnmount={editorWillUnmount}
<YqlEditor
changeUserInput={changeUserInput}
theme={theme}
handleSendExecuteClick={handleSendExecuteClick}
handleGetExplainQueryClick={handleGetExplainQueryClick}
/>
</div>
</div>
Expand Down Expand Up @@ -424,40 +286,3 @@ function Result({

return null;
}

function initResizeHandler(editor: Monaco.editor.IStandaloneCodeEditor) {
const layoutEditor = throttle(() => {
editor.layout();
}, 100);

editor.layout();

window.addEventListener('resize', layoutEditor);
editor.onDidDispose(() => {
window.removeEventListener('resize', layoutEditor);
});
}

function initUserPrompt(editor: Monaco.editor.IStandaloneCodeEditor, getInitialText: () => string) {
setUserPrompt(editor.getValue(), getInitialText());
editor.onDidChangeModelContent(() => {
setUserPrompt(editor.getValue(), getInitialText());
});
editor.onDidDispose(() => {
window.onbeforeunload = null;
});
}

function setUserPrompt(text: string, initialText: string) {
const hasUnsavedInput = text ? text !== initialText : false;

if (hasUnsavedInput) {
window.onbeforeunload = (e) => {
e.preventDefault();
// Chrome requires returnValue to be set
e.returnValue = '';
};
} else {
window.onbeforeunload = null;
}
}
Loading

0 comments on commit 4db34be

Please sign in to comment.