diff --git a/packages/web/src/App.js b/packages/web/src/App.js index 535ee02e3..9e862b759 100644 --- a/packages/web/src/App.js +++ b/packages/web/src/App.js @@ -1,7 +1,12 @@ import React from 'react'; import './index.css'; import Screen from './Screen'; -import { CurrentWidgetProvider, CurrentDatabaseProvider, OpenedTabsProvider } from './utility/globalState'; +import { + CurrentWidgetProvider, + CurrentDatabaseProvider, + OpenedTabsProvider, + SavedSqlFilesProvider, +} from './utility/globalState'; import { SocketProvider } from './utility/SocketProvider'; function App() { @@ -10,7 +15,9 @@ function App() { - + + + diff --git a/packages/web/src/appobj/savedSqlFileAppObject.js b/packages/web/src/appobj/savedSqlFileAppObject.js new file mode 100644 index 000000000..aab741b90 --- /dev/null +++ b/packages/web/src/appobj/savedSqlFileAppObject.js @@ -0,0 +1,33 @@ +import React from 'react'; +import _ from 'lodash'; +import { SqlIcon } from '../icons'; +import { openNewTab } from '../utility/common'; + +const savedSqlFileAppObject = () => ({ name, storageKey }, { setOpenedTabs, newQuery, openedTabs }) => { + const key = storageKey; + const title = name; + const Icon = SqlIcon; + + const onClick = () => { + const existing = openedTabs.find((x) => x.props && x.props.storageKey == storageKey); + if (existing) { + setOpenedTabs( + openedTabs.map((x) => ({ + ...x, + selected: x == existing, + })) + ); + } else { + console.log('OPENING QUERY', title, storageKey); + + newQuery({ + title, + storageKey, + }); + } + }; + + return { title, key, Icon, onClick }; +}; + +export default savedSqlFileAppObject; diff --git a/packages/web/src/modals/SaveSqlFileModal.js b/packages/web/src/modals/SaveSqlFileModal.js new file mode 100644 index 000000000..ca24f4d46 --- /dev/null +++ b/packages/web/src/modals/SaveSqlFileModal.js @@ -0,0 +1,38 @@ +import React from 'react'; +import axios from '../utility/axios'; +import ModalBase from './ModalBase'; +import { FormRow, FormButton, FormTextField, FormSelectField, FormSubmit } from '../utility/forms'; +import { TextField } from '../utility/inputs'; +import { Formik, Form } from 'formik'; +import { useSetSavedSqlFiles } from '../utility/globalState'; +// import FormikForm from '../utility/FormikForm'; + +export default function SaveSqlFileModal({ storageKey, modalState, name, onSave = undefined }) { + const setSavedSqlFiles = useSetSavedSqlFiles(); + const handleSubmit = async (values) => { + const { name } = values; + setSavedSqlFiles((files) => [ + ...files.filter((x) => x.storageKey != storageKey), + { + name, + storageKey, + }, + ]); + modalState.close(); + if (onSave) onSave(name); + }; + return ( + +

Save SQL file

+ +
+ + + + + + +
+
+ ); +} diff --git a/packages/web/src/query/QueryToolbar.js b/packages/web/src/query/QueryToolbar.js index 02f19ec95..766ee6dd7 100644 --- a/packages/web/src/query/QueryToolbar.js +++ b/packages/web/src/query/QueryToolbar.js @@ -1,7 +1,7 @@ import React from 'react'; import ToolbarButton from '../widgets/ToolbarButton'; -export default function QueryToolbar({ execute, cancel, isDatabaseDefined, busy }) { +export default function QueryToolbar({ execute, cancel, isDatabaseDefined, busy, save }) { return ( <> @@ -10,6 +10,7 @@ export default function QueryToolbar({ execute, cancel, isDatabaseDefined, busy Cancel + Save ); } diff --git a/packages/web/src/query/useNewQuery.js b/packages/web/src/query/useNewQuery.js index 123ca7d54..2ea2ac32d 100644 --- a/packages/web/src/query/useNewQuery.js +++ b/packages/web/src/query/useNewQuery.js @@ -11,13 +11,14 @@ export default function useNewQuery() { const tooltip = `${connection.displayName || connection.server}\n${database}`; - return () => + return ({ title = undefined, ...props } = {}) => openNewTab(setOpenedTabs, { - title: 'Query', + title: title || 'Query', icon: 'sql.svg', tooltip, tabComponent: 'QueryTab', props: { + ...props, conid: connection._id, database, }, diff --git a/packages/web/src/tabs/QueryTab.js b/packages/web/src/tabs/QueryTab.js index 2d5661cf5..cbc11f28b 100644 --- a/packages/web/src/tabs/QueryTab.js +++ b/packages/web/src/tabs/QueryTab.js @@ -4,7 +4,7 @@ import _ from 'lodash'; import axios from '../utility/axios'; import { useConnectionInfo } from '../utility/metadataLoaders'; import SqlEditor from '../sqleditor/SqlEditor'; -import { useUpdateDatabaseForTab, useSetOpenedTabs } from '../utility/globalState'; +import { useUpdateDatabaseForTab, useSetOpenedTabs, useOpenedTabs } from '../utility/globalState'; import QueryToolbar from '../query/QueryToolbar'; import SessionMessagesView from '../query/SessionMessagesView'; import { TabPage } from '../widgets/TabControl'; @@ -13,16 +13,20 @@ import { VerticalSplitter } from '../widgets/Splitter'; import keycodes from '../utility/keycodes'; import { changeTab } from '../utility/common'; import useSocket from '../utility/SocketProvider'; +import SaveSqlFileModal from '../modals/SaveSqlFileModal'; +import useModalState from '../modals/useModalState'; -export default function QueryTab({ tabid, conid, database, tabVisible, toolbarPortalRef, initialScript }) { - const localStorageKey = `sql_${tabid}`; +export default function QueryTab({ tabid, conid, database, tabVisible, toolbarPortalRef, initialScript, storageKey }) { + const localStorageKey = storageKey || `sql_${tabid}`; const [queryText, setQueryText] = React.useState(() => localStorage.getItem(localStorageKey) || initialScript || ''); 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 saveSqlFileModalState = useModalState(); const saveToStorage = React.useCallback(() => localStorage.setItem(localStorageKey, queryTextRef.current), [ localStorageKey, @@ -51,6 +55,17 @@ export default function QueryTab({ tabid, conid, database, tabVisible, toolbarPo } }, [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]); @@ -136,9 +151,16 @@ export default function QueryTab({ tabid, conid, database, tabVisible, toolbarPo execute={handleExecute} busy={busy} cancel={handleCancel} + save={saveSqlFileModalState.open} />, toolbarPortalRef.current )} + x.tabid == tabid).title} + onSave={(name) => changeTab(tabid, setOpenedTabs, (tab) => ({ ...tab, title: name }))} + /> ); } diff --git a/packages/web/src/utility/globalState.js b/packages/web/src/utility/globalState.js index 842d256b1..d5bab86c9 100644 --- a/packages/web/src/utility/globalState.js +++ b/packages/web/src/utility/globalState.js @@ -3,6 +3,7 @@ import React from 'react'; import useStorage from './useStorage'; import { useConnectionInfo } from './metadataLoaders'; import usePrevious from './usePrevious'; +import useNewQuery from '../query/useNewQuery'; function createGlobalState(defaultValue) { const Context = React.createContext(null); @@ -80,8 +81,16 @@ export function useUpdateDatabaseForTab(tabVisible, conid, database) { export function useAppObjectParams() { const setOpenedTabs = useSetOpenedTabs(); const currentDatabase = useCurrentDatabase(); + const newQuery = useNewQuery(); + const openedTabs = useOpenedTabs(); + return { setOpenedTabs, currentDatabase, + newQuery, + openedTabs, }; } + +const [SavedSqlFilesProvider, useSavedSqlFiles, useSetSavedSqlFiles] = createStorageState('savedSqlFiles', []); +export { SavedSqlFilesProvider, useSavedSqlFiles, useSetSavedSqlFiles }; diff --git a/packages/web/src/widgets/FilesWidget.js b/packages/web/src/widgets/FilesWidget.js index 741f29e3c..6a7532f9f 100644 --- a/packages/web/src/widgets/FilesWidget.js +++ b/packages/web/src/widgets/FilesWidget.js @@ -3,13 +3,13 @@ import styled from 'styled-components'; import _ from 'lodash'; import { AppObjectList } from '../appobj/AppObjectList'; -import { useOpenedTabs } from '../utility/globalState'; +import { useOpenedTabs, useSavedSqlFiles } from '../utility/globalState'; import openedTabAppObject from '../appobj/openedTabAppObject'; import { SearchBoxWrapper, InnerContainer, Input, MainContainer, OuterContainer, WidgetTitle } from './WidgetStyles'; +import savedSqlFileAppObject from '../appobj/savedSqlFileAppObject'; function OpenedTabsList() { const tabs = useOpenedTabs(); - console.log('TABS', tabs); return ( <> @@ -21,13 +21,28 @@ function OpenedTabsList() { ); } +function SavedSqlFilesList() { + const files = useSavedSqlFiles(); + + return ( + <> + Saved SQL files + + + + + ); +} + export default function FilesWidget() { return ( - + + + ); }