mirror of
https://github.com/dbgate/dbgate
synced 2024-11-07 20:26:23 +00:00
use editor data hook
This commit is contained in:
parent
9a42d1d6bd
commit
fc5c0eb239
@ -42,8 +42,7 @@ export function SavedSqlFileAppObject({ data, commonProps }) {
|
||||
onLoad={(data) => {
|
||||
newQuery({
|
||||
title: file,
|
||||
// @ts-ignore
|
||||
initialScript: data,
|
||||
initialData: data,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
@ -60,14 +59,15 @@ export function SavedShellFileAppObject({ data, commonProps }) {
|
||||
format="text"
|
||||
icon="img shell"
|
||||
onLoad={(data) => {
|
||||
openNewTab(setOpenedTabs, {
|
||||
title: file,
|
||||
icon: 'img shell',
|
||||
tabComponent: 'ShellTab',
|
||||
props: {
|
||||
initialScript: data,
|
||||
openNewTab(
|
||||
setOpenedTabs,
|
||||
{
|
||||
title: file,
|
||||
icon: 'img shell',
|
||||
tabComponent: 'ShellTab',
|
||||
},
|
||||
});
|
||||
data
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@ -9,10 +9,10 @@ import ModalContent from './ModalContent';
|
||||
import ModalFooter from './ModalFooter';
|
||||
// import FormikForm from '../utility/FormikForm';
|
||||
|
||||
export default function SaveFileModal({ getData, folder, format, modalState, name, onSave = undefined }) {
|
||||
export default function SaveFileModal({ data, folder, format, modalState, name, onSave = undefined }) {
|
||||
const handleSubmit = async (values) => {
|
||||
const { name } = values;
|
||||
await axios.post('files/save', { folder, file: name, data: getData(), format });
|
||||
await axios.post('files/save', { folder, file: name, data, format });
|
||||
modalState.close();
|
||||
if (onSave) onSave(name);
|
||||
};
|
||||
|
16
packages/web/src/modals/SaveTabModal.js
Normal file
16
packages/web/src/modals/SaveTabModal.js
Normal file
@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import { changeTab } from '../utility/common';
|
||||
import { useOpenedTabs, useSetOpenedTabs } from '../utility/globalState';
|
||||
import SaveFileModal from './SaveFileModal';
|
||||
|
||||
export default function SaveTabModal({ data, folder, format, modalState, tabid }) {
|
||||
const setOpenedTabs = useSetOpenedTabs();
|
||||
const openedTabs = useOpenedTabs();
|
||||
|
||||
const name = openedTabs.find((x) => x.tabid == tabid).title;
|
||||
const onSave = (name) => changeTab(tabid, setOpenedTabs, (tab) => ({ ...tab, title: name }));
|
||||
|
||||
return (
|
||||
<SaveFileModal data={data} folder={folder} format={format} modalState={modalState} name={name} onSave={onSave} />
|
||||
);
|
||||
}
|
@ -11,16 +11,20 @@ export default function useNewQuery() {
|
||||
|
||||
const tooltip = `${connection.displayName || connection.server}\n${database}`;
|
||||
|
||||
return ({ title = undefined, ...props } = {}) =>
|
||||
openNewTab(setOpenedTabs, {
|
||||
title: title || 'Query',
|
||||
icon: 'img sql-file',
|
||||
tooltip,
|
||||
tabComponent: 'QueryTab',
|
||||
props: {
|
||||
...props,
|
||||
conid: connection._id,
|
||||
database,
|
||||
return ({ title = undefined, initialData = undefined, ...props } = {}) =>
|
||||
openNewTab(
|
||||
setOpenedTabs,
|
||||
{
|
||||
title: title || 'Query',
|
||||
icon: 'img sql-file',
|
||||
tooltip,
|
||||
tabComponent: 'QueryTab',
|
||||
props: {
|
||||
...props,
|
||||
conid: connection._id,
|
||||
database,
|
||||
},
|
||||
},
|
||||
});
|
||||
initialData
|
||||
);
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import _ from 'lodash';
|
||||
import ReactDOM from 'react-dom';
|
||||
import axios from '../utility/axios';
|
||||
|
||||
import { useConnectionInfo, getDbCore, getConnectionInfo, getSqlObjectInfo } from '../utility/metadataLoaders';
|
||||
import { useConnectionInfo } from '../utility/metadataLoaders';
|
||||
import SqlEditor from '../sqleditor/SqlEditor';
|
||||
import { useUpdateDatabaseForTab, useSetOpenedTabs, useOpenedTabs } from '../utility/globalState';
|
||||
import { useUpdateDatabaseForTab, useSetOpenedTabs } from '../utility/globalState';
|
||||
import QueryToolbar from '../query/QueryToolbar';
|
||||
import SocketMessagesView from '../query/SocketMessagesView';
|
||||
import { TabPage } from '../widgets/TabControl';
|
||||
@ -14,103 +14,38 @@ import { VerticalSplitter } from '../widgets/Splitter';
|
||||
import keycodes from '../utility/keycodes';
|
||||
import { changeTab } from '../utility/common';
|
||||
import useSocket from '../utility/SocketProvider';
|
||||
import SaveFileModal from '../modals/SaveFileModal';
|
||||
import SaveTabModal from '../modals/SaveTabModal';
|
||||
import useModalState from '../modals/useModalState';
|
||||
import sqlFormatter from 'sql-formatter';
|
||||
import useExtensions from '../utility/useExtensions';
|
||||
import { driverBase, findEngineDriver } from 'dbgate-tools';
|
||||
import useEditorData from '../utility/useEditorData';
|
||||
import useSqlTemplate from '../utility/useSqlTemplate';
|
||||
import LoadingInfo from '../widgets/LoadingInfo';
|
||||
|
||||
function useSqlTemplate(sqlTemplate, props) {
|
||||
const [sql, setSql] = React.useState('');
|
||||
const extensions = useExtensions();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
if (sqlTemplate) {
|
||||
loadTemplate();
|
||||
}
|
||||
}, []);
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
export default function QueryTab({
|
||||
tabid,
|
||||
conid,
|
||||
database,
|
||||
initialArgs,
|
||||
tabVisible,
|
||||
toolbarPortalRef,
|
||||
initialScript,
|
||||
storageKey,
|
||||
...other
|
||||
}) {
|
||||
const loadingText = 'Loading SQL template...';
|
||||
const localStorageKey = storageKey || `tabdata_sql_${tabid}`;
|
||||
export default function QueryTab({ tabid, conid, database, initialArgs, tabVisible, toolbarPortalRef, ...other }) {
|
||||
const { sqlTemplate } = initialArgs || {};
|
||||
const [queryText, setQueryText] = React.useState(
|
||||
() => localStorage.getItem(localStorageKey) || initialScript || (sqlTemplate ? loadingText : '')
|
||||
);
|
||||
const queryTextRef = React.useRef(queryText);
|
||||
const [sessionId, setSessionId] = React.useState(null);
|
||||
const [executeNumber, setExecuteNumber] = React.useState(0);
|
||||
const setOpenedTabs = useSetOpenedTabs();
|
||||
const openedTabs = useOpenedTabs();
|
||||
const socket = useSocket();
|
||||
const [busy, setBusy] = React.useState(false);
|
||||
const saveFileModalState = useModalState();
|
||||
const { editorData, setEditorData, isLoading } = useEditorData(tabid);
|
||||
|
||||
const [sqlFromTemplate, isLoadingTemplate] = useSqlTemplate(sqlTemplate, { conid, database, ...other });
|
||||
const editorRef = React.useRef(null);
|
||||
|
||||
const sqlFromTemplate = useSqlTemplate(sqlTemplate, { conid, database, ...other });
|
||||
React.useEffect(() => {
|
||||
if (sqlFromTemplate && queryText == loadingText) {
|
||||
editorRef.current.editor.setValue(sqlFromTemplate);
|
||||
editorRef.current.editor.clearSelection();
|
||||
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 saveToStorage = React.useCallback(() => {
|
||||
try {
|
||||
localStorage.setItem(localStorageKey, queryTextRef.current);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}, [localStorageKey, queryTextRef]);
|
||||
const saveToStorageDebounced = React.useMemo(() => _.debounce(saveToStorage, 5000), [saveToStorage]);
|
||||
|
||||
React.useEffect(() => {
|
||||
window.addEventListener('beforeunload', saveToStorage);
|
||||
return () => {
|
||||
saveToStorage();
|
||||
window.removeEventListener('beforeunload', saveToStorage);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleSessionDone = React.useCallback(() => {
|
||||
setBusy(false);
|
||||
}, []);
|
||||
@ -124,32 +59,13 @@ export default function QueryTab({
|
||||
}
|
||||
}, [sessionId, socket]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!storageKey)
|
||||
changeTab(tabid, setOpenedTabs, (tab) => ({
|
||||
...tab,
|
||||
props: {
|
||||
...tab.props,
|
||||
storageKey: localStorageKey,
|
||||
},
|
||||
}));
|
||||
}, [storageKey]);
|
||||
|
||||
React.useEffect(() => {
|
||||
changeTab(tabid, setOpenedTabs, (tab) => ({ ...tab, busy }));
|
||||
}, [busy]);
|
||||
|
||||
const editorRef = React.useRef(null);
|
||||
|
||||
useUpdateDatabaseForTab(tabVisible, conid, database);
|
||||
const connection = useConnectionInfo({ conid });
|
||||
|
||||
const handleChange = (text) => {
|
||||
if (text != null) queryTextRef.current = text;
|
||||
setQueryText(text);
|
||||
saveToStorageDebounced();
|
||||
};
|
||||
|
||||
const handleExecute = async () => {
|
||||
if (busy) return;
|
||||
setExecuteNumber((num) => num + 1);
|
||||
@ -167,7 +83,7 @@ export default function QueryTab({
|
||||
setBusy(true);
|
||||
await axios.post('sessions/execute-query', {
|
||||
sesid,
|
||||
sql: selectedText || queryText,
|
||||
sql: selectedText || editorData,
|
||||
});
|
||||
};
|
||||
|
||||
@ -204,17 +120,23 @@ export default function QueryTab({
|
||||
editorRef.current.editor.clearSelection();
|
||||
};
|
||||
|
||||
if (isLoading || isLoadingTemplate)
|
||||
return (
|
||||
<div>
|
||||
<LoadingInfo message="Loading SQL script" />
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<VerticalSplitter>
|
||||
<SqlEditor
|
||||
value={queryText}
|
||||
onChange={handleChange}
|
||||
value={editorData || ''}
|
||||
onChange={setEditorData}
|
||||
tabVisible={tabVisible}
|
||||
engine={connection && connection.engine}
|
||||
onKeyDown={handleKeyDown}
|
||||
editorRef={editorRef}
|
||||
readOnly={queryText == loadingText}
|
||||
conid={conid}
|
||||
database={database}
|
||||
/>
|
||||
@ -248,14 +170,7 @@ export default function QueryTab({
|
||||
/>,
|
||||
toolbarPortalRef.current
|
||||
)}
|
||||
<SaveFileModal
|
||||
modalState={saveFileModalState}
|
||||
getData={() => localStorage.getItem(localStorageKey)}
|
||||
format="text"
|
||||
folder="sql"
|
||||
name={openedTabs.find((x) => x.tabid == tabid).title}
|
||||
onSave={(name) => changeTab(tabid, setOpenedTabs, (tab) => ({ ...tab, title: name }))}
|
||||
/>
|
||||
<SaveTabModal modalState={saveFileModalState} data={editorData} format="text" folder="sql" tabid={tabid} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import _ from 'lodash';
|
||||
import localforage from 'localforage';
|
||||
import axios from '../utility/axios';
|
||||
import { useSetOpenedTabs } from '../utility/globalState';
|
||||
import { VerticalSplitter } from '../widgets/Splitter';
|
||||
@ -22,13 +23,13 @@ export default function ShellTab({
|
||||
storageKey,
|
||||
...other
|
||||
}) {
|
||||
const localStorageKey = storageKey || `tabdata_shell_${tabid}`;
|
||||
const [shellText, setShellText] = React.useState(() => localStorage.getItem(localStorageKey) || initialScript || '');
|
||||
const localStorageKey = `tabdata_shell_${tabid}`;
|
||||
const [shellText, setShellText] = React.useState(initialScript || '');
|
||||
const shellTextRef = React.useRef(shellText);
|
||||
const [busy, setBusy] = React.useState(false);
|
||||
const showModal = useShowModal();
|
||||
|
||||
const saveToStorage = React.useCallback(() => localStorage.setItem(localStorageKey, shellTextRef.current), [
|
||||
const saveToStorage = React.useCallback(() => localforage.setItem(localStorageKey, shellTextRef.current), [
|
||||
localStorageKey,
|
||||
shellTextRef,
|
||||
]);
|
||||
|
@ -1,4 +1,5 @@
|
||||
import uuidv1 from 'uuid/v1';
|
||||
import localforage from 'localforage';
|
||||
|
||||
export class LoadingToken {
|
||||
constructor() {
|
||||
@ -14,8 +15,11 @@ export function sleep(milliseconds) {
|
||||
return new Promise((resolve) => window.setTimeout(() => resolve(null), milliseconds));
|
||||
}
|
||||
|
||||
export function openNewTab(setOpenedTabs, newTab) {
|
||||
export async function openNewTab(setOpenedTabs, newTab, initialData = undefined) {
|
||||
const tabid = uuidv1();
|
||||
if (initialData) {
|
||||
await localforage.setItem(`tabdata_${tabid}`, initialData);
|
||||
}
|
||||
setOpenedTabs((files) => [
|
||||
...(files || []).map((x) => ({ ...x, selected: false })),
|
||||
{
|
||||
|
52
packages/web/src/utility/useEditorData.js
Normal file
52
packages/web/src/utility/useEditorData.js
Normal file
@ -0,0 +1,52 @@
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import localforage from 'localforage';
|
||||
|
||||
export default function useEditorData(tabid, initialData = null) {
|
||||
const localStorageKey = `tabdata_${tabid}`;
|
||||
|
||||
const [value, setValue] = React.useState(initialData);
|
||||
const [isLoading, setIsLoading] = React.useState(true);
|
||||
|
||||
const valueRef = React.useRef(null);
|
||||
|
||||
const initialLoad = async () => {
|
||||
const init = await localforage.getItem(localStorageKey);
|
||||
if (init) {
|
||||
setValue(init);
|
||||
valueRef.current = init;
|
||||
}
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
initialLoad();
|
||||
}, []);
|
||||
|
||||
const saveToStorage = React.useCallback(async () => {
|
||||
if (valueRef.current == null) return;
|
||||
try {
|
||||
await localforage.setItem(localStorageKey, valueRef.current);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}, [localStorageKey, valueRef]);
|
||||
|
||||
const saveToStorageDebounced = React.useMemo(() => _.debounce(saveToStorage, 500), [saveToStorage]);
|
||||
|
||||
const handleChange = (newValue) => {
|
||||
if (newValue != null) valueRef.current = newValue;
|
||||
setValue(newValue);
|
||||
saveToStorageDebounced();
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
window.addEventListener('beforeunload', saveToStorage);
|
||||
return () => {
|
||||
saveToStorage();
|
||||
window.removeEventListener('beforeunload', saveToStorage);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return { editorData: value, setEditorData: handleChange, isLoading };
|
||||
}
|
48
packages/web/src/utility/useSqlTemplate.js
Normal file
48
packages/web/src/utility/useSqlTemplate.js
Normal file
@ -0,0 +1,48 @@
|
||||
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];
|
||||
}
|
Loading…
Reference in New Issue
Block a user