diff --git a/packages/web/src/tabs/QueryTab.js b/packages/web/src/tabs/QueryTab.js index 5cbc7f29..f5bcd52d 100644 --- a/packages/web/src/tabs/QueryTab.js +++ b/packages/web/src/tabs/QueryTab.js @@ -18,34 +18,27 @@ import SaveTabModal from '../modals/SaveTabModal'; import useModalState from '../modals/useModalState'; import sqlFormatter from 'sql-formatter'; import useEditorData from '../utility/useEditorData'; -import useSqlTemplate from '../utility/useSqlTemplate'; +import applySqlTemplate from '../utility/applySqlTemplate'; import LoadingInfo from '../widgets/LoadingInfo'; +import useExtensions from '../utility/useExtensions'; export default function QueryTab({ tabid, conid, database, initialArgs, tabVisible, toolbarPortalRef, ...other }) { - const { sqlTemplate } = initialArgs || {}; const [sessionId, setSessionId] = React.useState(null); const [executeNumber, setExecuteNumber] = React.useState(0); const setOpenedTabs = useSetOpenedTabs(); const socket = useSocket(); const [busy, setBusy] = React.useState(false); const saveFileModalState = useModalState(); - const { editorData, setEditorData, isLoading } = useEditorData(tabid); + const extensions = useExtensions(); + const { editorData, setEditorData, isLoading } = useEditorData({ + tabid, + loadFromArgs: initialArgs && initialArgs.sqlTemplate + ? () => applySqlTemplate(initialArgs.sqlTemplate, extensions, { conid, database, ...other }) + : null, + }); - const [sqlFromTemplate, isLoadingTemplate] = useSqlTemplate(sqlTemplate, { conid, database, ...other }); const editorRef = React.useRef(null); - React.useEffect(() => { - if (sqlFromTemplate && sqlTemplate) { - setEditorData(sqlFromTemplate); - // editorRef.current.editor.setValue(sqlFromTemplate); - // editorRef.current.editor.clearSelection(); - changeTab(tabid, setOpenedTabs, (tab) => ({ - ...tab, - props: _.omit(tab.props, ['initialArgs']), - })); - } - }, [sqlFromTemplate]); - const handleSessionDone = React.useCallback(() => { setBusy(false); }, []); @@ -120,7 +113,7 @@ export default function QueryTab({ tabid, conid, database, initialArgs, tabVisib editorRef.current.editor.clearSelection(); }; - if (isLoading || isLoadingTemplate) + if (isLoading) return (
diff --git a/packages/web/src/utility/applySqlTemplate.js b/packages/web/src/utility/applySqlTemplate.js new file mode 100644 index 00000000..bd0a59cb --- /dev/null +++ b/packages/web/src/utility/applySqlTemplate.js @@ -0,0 +1,32 @@ +import { getDbCore, getConnectionInfo, getSqlObjectInfo } from '../utility/metadataLoaders'; +import sqlFormatter from 'sql-formatter'; +import { driverBase, findEngineDriver } from 'dbgate-tools'; + +export default async function applySqlTemplate(sqlTemplate, extensions, props) { + if (sqlTemplate == 'CREATE TABLE') { + const tableInfo = await getDbCore(props, props.objectTypeField || 'tables'); + const connection = await getConnectionInfo(props); + const driver = findEngineDriver(connection, extensions) || driverBase; + const dmp = driver.createDumper(); + if (tableInfo) dmp.createTable(tableInfo); + return dmp.s; + } + if (sqlTemplate == 'CREATE OBJECT') { + const objectInfo = await getSqlObjectInfo(props); + if (objectInfo) { + if (objectInfo.requiresFormat && objectInfo.createSql) return sqlFormatter.format(objectInfo.createSql); + else return objectInfo.createSql; + } + } + if (sqlTemplate == 'EXECUTE PROCEDURE') { + const procedureInfo = await getSqlObjectInfo(props); + const connection = await getConnectionInfo(props); + + const driver = findEngineDriver(connection, extensions) || driverBase; + const dmp = driver.createDumper(); + if (procedureInfo) dmp.put('^execute %f', procedureInfo); + return dmp.s; + } + + return null; +} diff --git a/packages/web/src/utility/useEditorData.js b/packages/web/src/utility/useEditorData.js index 9b03e958..156c3058 100644 --- a/packages/web/src/utility/useEditorData.js +++ b/packages/web/src/utility/useEditorData.js @@ -1,29 +1,44 @@ import React from 'react'; import _ from 'lodash'; import localforage from 'localforage'; +import { changeTab } from './common'; +import { useSetOpenedTabs } from './globalState'; -export default function useEditorData(tabid, initialData = null) { +export default function useEditorData({ tabid, loadFromArgs }) { const localStorageKey = `tabdata_${tabid}`; + const setOpenedTabs = useSetOpenedTabs(); + const changeCounterRef = React.useRef(0); + const savedCounterRef = React.useRef(0); - const [value, setValue] = React.useState(initialData); + const [value, setValue] = React.useState(null); const [isLoading, setIsLoading] = React.useState(true); const valueRef = React.useRef(null); const initialLoad = async () => { - const initFallback = localStorage.getItem(localStorageKey); - if (initFallback != null) { - const init = JSON.parse(initFallback); + if (loadFromArgs) { + const init = await loadFromArgs(); + changeTab(tabid, setOpenedTabs, (tab) => ({ + ...tab, + props: _.omit(tab.props, ['initialArgs']), + })); setValue(init); valueRef.current = init; - // move to local forage - await localforage.setItem(localStorageKey, init); - localStorage.removeItem(localStorageKey); } else { - const init = await localforage.getItem(localStorageKey); - if (init) { + const initFallback = localStorage.getItem(localStorageKey); + if (initFallback != null) { + const init = JSON.parse(initFallback); setValue(init); valueRef.current = init; + // move to local forage + await localforage.setItem(localStorageKey, init); + localStorage.removeItem(localStorageKey); + } else { + const init = await localforage.getItem(localStorageKey); + if (init) { + setValue(init); + valueRef.current = init; + } } } setIsLoading(false); @@ -37,6 +52,7 @@ export default function useEditorData(tabid, initialData = null) { if (valueRef.current == null) return; try { await localforage.setItem(localStorageKey, valueRef.current); + savedCounterRef.current = changeCounterRef.current; } catch (err) { console.error(err); } @@ -44,6 +60,7 @@ export default function useEditorData(tabid, initialData = null) { const saveToStorageFallback = React.useCallback(() => { if (valueRef.current == null) return; + if (savedCounterRef.current == changeCounterRef.current) return; // all saved // on window unload must be synchronous actions, save to local storage instead localStorage.setItem(localStorageKey, JSON.stringify(valueRef.current)); }, [localStorageKey, valueRef]); @@ -53,6 +70,7 @@ export default function useEditorData(tabid, initialData = null) { const handleChange = (newValue) => { if (newValue != null) valueRef.current = newValue; setValue(newValue); + changeCounterRef.current += 1; saveToStorageDebounced(); }; diff --git a/packages/web/src/utility/useSqlTemplate.js b/packages/web/src/utility/useSqlTemplate.js deleted file mode 100644 index bfc76940..00000000 --- a/packages/web/src/utility/useSqlTemplate.js +++ /dev/null @@ -1,48 +0,0 @@ -import React from 'react'; - -import { getDbCore, getConnectionInfo, getSqlObjectInfo } from '../utility/metadataLoaders'; -import sqlFormatter from 'sql-formatter'; -import useExtensions from '../utility/useExtensions'; -import { driverBase, findEngineDriver } from 'dbgate-tools'; - -export default function useSqlTemplate(sqlTemplate, props) { - const [sql, setSql] = React.useState(''); - const extensions = useExtensions(); - const [isLoading, setIsLoading] = React.useState(!!sqlTemplate); - - async function loadTemplate() { - if (sqlTemplate == 'CREATE TABLE') { - const tableInfo = await getDbCore(props, props.objectTypeField || 'tables'); - const connection = await getConnectionInfo(props); - const driver = findEngineDriver(connection, extensions) || driverBase; - const dmp = driver.createDumper(); - if (tableInfo) dmp.createTable(tableInfo); - setSql(dmp.s); - } - if (sqlTemplate == 'CREATE OBJECT') { - const objectInfo = await getSqlObjectInfo(props); - if (objectInfo) { - if (objectInfo.requiresFormat && objectInfo.createSql) setSql(sqlFormatter.format(objectInfo.createSql)); - else setSql(objectInfo.createSql); - } - } - if (sqlTemplate == 'EXECUTE PROCEDURE') { - const procedureInfo = await getSqlObjectInfo(props); - const connection = await getConnectionInfo(props); - - const driver = findEngineDriver(connection, extensions) || driverBase; - const dmp = driver.createDumper(); - if (procedureInfo) dmp.put('^execute %f', procedureInfo); - setSql(dmp.s); - } - setIsLoading(false); - } - - React.useEffect(() => { - if (sqlTemplate) { - loadTemplate(); - } - }, []); - - return [sql, isLoading]; -}