From 8687c9d71682860d8c3ab986dfed34605f72ecb7 Mon Sep 17 00:00:00 2001 From: mufazalov Date: Tue, 18 Nov 2025 18:23:11 +0300 Subject: [PATCH] refactor(SavedQueries): move queries manipulations to hook --- .../Tenant/Query/QueryEditor/helpers.ts | 4 +- .../Tenant/Query/SaveQuery/SaveQuery.tsx | 32 ++++++++--- .../Query/SavedQueries/SavedQueries.tsx | 9 ++- .../Tenant/Query/utils/useSavedQueries.tsx | 57 +++++++++++++++---- .../reducers/queryActions/queryActions.ts | 45 --------------- 5 files changed, 77 insertions(+), 70 deletions(-) diff --git a/src/containers/Tenant/Query/QueryEditor/helpers.ts b/src/containers/Tenant/Query/QueryEditor/helpers.ts index 53c7f9d734..d9a23a2aec 100644 --- a/src/containers/Tenant/Query/QueryEditor/helpers.ts +++ b/src/containers/Tenant/Query/QueryEditor/helpers.ts @@ -46,7 +46,7 @@ export function useCodeAssistHelpers() { const [ignoreSuggestion] = codeAssistApi.useIgnoreSuggestionMutation(); const [sendUserQueriesData] = codeAssistApi.useSendUserQueriesDataMutation(); const historyQueries = useTypedSelector(selectQueriesHistory); - const savedQueries = useSavedQueries(); + const {savedQueries} = useSavedQueries(); const getCodeAssistSuggestions = React.useCallback( async (promptFiles: PromptFile[]) => sendCodeAssistPrompt(promptFiles).unwrap(), @@ -74,7 +74,7 @@ export function useCodeAssistHelpers() { name: `query${index}.yql`, text: query.queryText, })), - ...savedQueries.map((query) => ({ + ...(savedQueries ?? []).map((query) => ({ name: query.name, text: query.body, })), diff --git a/src/containers/Tenant/Query/SaveQuery/SaveQuery.tsx b/src/containers/Tenant/Query/SaveQuery/SaveQuery.tsx index 05687141bc..4da588b10f 100644 --- a/src/containers/Tenant/Query/SaveQuery/SaveQuery.tsx +++ b/src/containers/Tenant/Query/SaveQuery/SaveQuery.tsx @@ -7,10 +7,10 @@ import {Button, Dialog, DropdownMenu, TextInput} from '@gravity-ui/uikit'; import {setIsDirty} from '../../../../store/reducers/query/query'; import { clearQueryNameToEdit, - saveQuery, selectQueryName, setQueryAction, } from '../../../../store/reducers/queryActions/queryActions'; +import type {SavedQuery} from '../../../../types/store/query'; import {cn} from '../../../../utils/cn'; import {useTypedDispatch, useTypedSelector} from '../../../../utils/hooks'; import {useSavedQueries} from '../utils/useSavedQueries'; @@ -27,10 +27,16 @@ interface SaveQueryProps { function useSaveQueryHandler(dialogProps?: SaveQueryDialogCommonProps) { const dispatch = useTypedDispatch(); + const {savedQueries, saveQuery} = useSavedQueries(); + const onSaveQueryClick = React.useCallback(() => { - NiceModal.show(SAVE_QUERY_DIALOG, dialogProps); + NiceModal.show(SAVE_QUERY_DIALOG, { + ...dialogProps, + savedQueries, + onSaveQuery: saveQuery, + }); dispatch(clearQueryNameToEdit()); - }, [dispatch, dialogProps]); + }, [dispatch, dialogProps, savedQueries, saveQuery]); return onSaveQueryClick; } @@ -54,8 +60,10 @@ export function SaveQuery({buttonProps = {}}: SaveQueryProps) { const queryNameToEdit = useTypedSelector(selectQueryName); const onSaveQueryClick = useSaveQueryHandler(); + const {saveQuery} = useSavedQueries(); + const onEditQueryClick = () => { - dispatch(saveQuery(queryNameToEdit)); + saveQuery(queryNameToEdit); dispatch(setIsDirty(false)); dispatch(clearQueryNameToEdit()); }; @@ -95,10 +103,18 @@ interface SaveQueryDialogCommonProps { interface SaveQueryDialogProps extends SaveQueryDialogCommonProps { open: boolean; + savedQueries?: SavedQuery[]; + onSaveQuery: (name: string | null) => void; } -function SaveQueryDialog({onSuccess, onCancel, onClose, open}: SaveQueryDialogProps) { - const savedQueries = useSavedQueries(); +function SaveQueryDialog({ + onSuccess, + onCancel, + onClose, + open, + savedQueries, + onSaveQuery, +}: SaveQueryDialogProps) { const dispatch = useTypedDispatch(); const [queryName, setQueryName] = React.useState(''); const [validationError, setValidationError] = React.useState(); @@ -107,7 +123,7 @@ function SaveQueryDialog({onSuccess, onCancel, onClose, open}: SaveQueryDialogPr if (!value) { return i18n('error.name-not-empty'); } - if (savedQueries.some((q) => q.name.toLowerCase() === value.trim().toLowerCase())) { + if (savedQueries?.some((q) => q.name.toLowerCase() === value.trim().toLowerCase())) { return i18n('error.name-exists'); } return undefined; @@ -131,7 +147,7 @@ function SaveQueryDialog({onSuccess, onCancel, onClose, open}: SaveQueryDialogPr }; const onSaveClick = () => { - dispatch(saveQuery(queryName)); + onSaveQuery(queryName); dispatch(setIsDirty(false)); onCloseDialog(); onSuccess?.(); diff --git a/src/containers/Tenant/Query/SavedQueries/SavedQueries.tsx b/src/containers/Tenant/Query/SavedQueries/SavedQueries.tsx index bd46e1fa89..155ad04ba1 100644 --- a/src/containers/Tenant/Query/SavedQueries/SavedQueries.tsx +++ b/src/containers/Tenant/Query/SavedQueries/SavedQueries.tsx @@ -11,7 +11,6 @@ import {TableWithControlsLayout} from '../../../../components/TableWithControlsL import {TruncatedQuery} from '../../../../components/TruncatedQuery/TruncatedQuery'; import {setIsDirty} from '../../../../store/reducers/query/query'; import { - deleteSavedQuery, selectSavedQueriesFilter, setQueryNameToEdit, setSavedQueriesFilter, @@ -24,7 +23,7 @@ import {useTypedDispatch, useTypedSelector} from '../../../../utils/hooks'; import {useChangeInputWithConfirmation} from '../../../../utils/hooks/withConfirmation/useChangeInputWithConfirmation'; import {MAX_QUERY_HEIGHT, QUERY_TABLE_SETTINGS} from '../../utils/constants'; import i18n from '../i18n'; -import {useFilteredSavedQueries} from '../utils/useSavedQueries'; +import {useSavedQueries} from '../utils/useSavedQueries'; import './SavedQueries.scss'; @@ -68,7 +67,7 @@ interface SavedQueriesProps { } export const SavedQueries = ({changeUserInput}: SavedQueriesProps) => { - const savedQueries = useFilteredSavedQueries(); + const {filteredSavedQueries, deleteSavedQuery} = useSavedQueries(); const dispatch = useTypedDispatch(); const filter = useTypedSelector(selectSavedQueriesFilter); @@ -86,7 +85,7 @@ export const SavedQueries = ({changeUserInput}: SavedQueriesProps) => { const onConfirmDeleteClick = () => { closeDeleteDialog(); - dispatch(deleteSavedQuery(queryNameToDelete)); + deleteSavedQuery(queryNameToDelete); setQueryNameToDelete(''); }; @@ -158,7 +157,7 @@ export const SavedQueries = ({changeUserInput}: SavedQueriesProps) => { b('row')} diff --git a/src/containers/Tenant/Query/utils/useSavedQueries.tsx b/src/containers/Tenant/Query/utils/useSavedQueries.tsx index 6e0b0f0e26..413b2a1901 100644 --- a/src/containers/Tenant/Query/utils/useSavedQueries.tsx +++ b/src/containers/Tenant/Query/utils/useSavedQueries.tsx @@ -6,21 +6,58 @@ import type {SavedQuery} from '../../../../types/store/query'; import {useSetting, useTypedSelector} from '../../../../utils/hooks'; export function useSavedQueries() { - const [savedQueries] = useSetting(SETTING_KEYS.SAVED_QUERIES, []); + const [savedQueries, saveQueries] = useSetting(SETTING_KEYS.SAVED_QUERIES); - return savedQueries; -} - -export function useFilteredSavedQueries() { - const savedQueries = useSavedQueries(); const filter = useTypedSelector(selectSavedQueriesFilter).trim().toLowerCase(); + const currentInput = useTypedSelector((state) => state.query.input); const filteredSavedQueries = React.useMemo(() => { - if (filter.length === 0) { - return savedQueries; + const queries = savedQueries ?? []; + + if (filter) { + return queries.filter((item) => item.body.toLowerCase().includes(filter)); } - return savedQueries.filter((item) => item.body.toLowerCase().includes(filter)); + return queries; }, [savedQueries, filter]); - return filteredSavedQueries; + const deleteSavedQuery = React.useCallback( + (queryName: string) => { + const queries = savedQueries ?? []; + const nextSavedQueries = queries.filter((el) => !findQueryByName(el, queryName)); + + saveQueries(nextSavedQueries); + }, + [savedQueries, saveQueries], + ); + + const saveQuery = React.useCallback( + (queryName: string | null) => { + if (!queryName) { + return; + } + + const currentQueries = savedQueries ?? []; + const nextSavedQueries = [...currentQueries]; + + const queryIndex = currentQueries.findIndex((el) => findQueryByName(el, queryName)); + + if (queryIndex >= 0) { + nextSavedQueries[queryIndex] = { + ...currentQueries[queryIndex], + body: currentInput, + }; + } else { + nextSavedQueries.push({name: queryName, body: currentInput}); + } + + saveQueries(nextSavedQueries); + }, + [savedQueries, saveQueries, currentInput], + ); + + return {savedQueries, filteredSavedQueries, deleteSavedQuery, saveQuery}; +} + +function findQueryByName(query: SavedQuery, name: string) { + return query.name.toLowerCase() === name.toLowerCase(); } diff --git a/src/store/reducers/queryActions/queryActions.ts b/src/store/reducers/queryActions/queryActions.ts index 47cfc7fc5e..f33c5e8e60 100644 --- a/src/store/reducers/queryActions/queryActions.ts +++ b/src/store/reducers/queryActions/queryActions.ts @@ -1,12 +1,6 @@ import type {PayloadAction} from '@reduxjs/toolkit'; import {createSlice} from '@reduxjs/toolkit'; -import {settingsManager} from '../../../services/settings'; -import type {SavedQuery} from '../../../types/store/query'; -import type {AppDispatch, GetState} from '../../defaultStore'; -import {SETTING_KEYS} from '../settings/constants'; -import {getSettingValue, setSettingValue} from '../settings/settings'; - import type {QueryActions, QueryActionsState} from './types'; const initialState: QueryActionsState = { @@ -43,42 +37,3 @@ export default slice.reducer; export const {setQueryNameToEdit, clearQueryNameToEdit, setQueryAction, setSavedQueriesFilter} = slice.actions; export const {selectQueryName, selectQueryAction, selectSavedQueriesFilter} = slice.selectors; - -export function deleteSavedQuery(queryName: string) { - return function deleteSavedQueryThunk(dispatch: AppDispatch, getState: GetState) { - const state = getState(); - const savedQueries = - (getSettingValue(state, SETTING_KEYS.SAVED_QUERIES) as SavedQuery[]) ?? []; - const newSavedQueries = savedQueries.filter( - (el) => el.name.toLowerCase() !== queryName.toLowerCase(), - ); - dispatch(setSettingValue(SETTING_KEYS.SAVED_QUERIES, newSavedQueries)); - settingsManager.setUserSettingsValue(SETTING_KEYS.SAVED_QUERIES, newSavedQueries); - }; -} - -export function saveQuery(queryName: string | null) { - return function saveQueryThunk(dispatch: AppDispatch, getState: GetState) { - const state = getState(); - const savedQueries = - (getSettingValue(state, SETTING_KEYS.SAVED_QUERIES) as SavedQuery[]) ?? []; - const queryBody = state.query.input; - if (queryName === null) { - return; - } - const nextSavedQueries = [...savedQueries]; - - const query = nextSavedQueries.find( - (el) => el.name.toLowerCase() === queryName.toLowerCase(), - ); - - if (query) { - query.body = queryBody; - } else { - nextSavedQueries.push({name: queryName, body: queryBody}); - } - - dispatch(setSettingValue(SETTING_KEYS.SAVED_QUERIES, nextSavedQueries)); - settingsManager.setUserSettingsValue(SETTING_KEYS.SAVED_QUERIES, nextSavedQueries); - }; -}