diff --git a/app/src/electron.js b/app/src/electron.js
index 9eb6b9707..6a403a8d7 100644
--- a/app/src/electron.js
+++ b/app/src/electron.js
@@ -45,29 +45,36 @@ function buildMenu() {
mainWindow.webContents.executeJavaScript(`dbgate_createNewConnection()`);
},
},
- {
- label: 'New query',
- click() {
- mainWindow.webContents.executeJavaScript(`dbgate_newQuery()`);
- },
- },
{
label: 'Open file',
click() {
mainWindow.webContents.executeJavaScript(`dbgate_openFile()`);
},
},
+ { type: 'separator' },
+ { role: 'close' },
+ ],
+ },
+ {
+ label: 'Window',
+ submenu: [
+ {
+ label: 'New query',
+ click() {
+ mainWindow.webContents.executeJavaScript(`dbgate_newQuery()`);
+ },
+ },
+ { type: 'separator' },
{
label: 'Close all tabs',
click() {
mainWindow.webContents.executeJavaScript('dbgate_closeAll()');
},
},
- { type: 'separator' },
{ role: 'minimize' },
- { role: 'close' },
],
},
+
// {
// label: 'Edit',
// submenu: [
diff --git a/packages/web/src/charts/ChartToolbar.js b/packages/web/src/charts/ChartToolbar.js
index fc60b4832..62b3a1aaf 100644
--- a/packages/web/src/charts/ChartToolbar.js
+++ b/packages/web/src/charts/ChartToolbar.js
@@ -1,17 +1,9 @@
import React from 'react';
-import useHasPermission from '../utility/useHasPermission';
import ToolbarButton from '../widgets/ToolbarButton';
-export default function ChartToolbar({ save, modelState, dispatchModel }) {
- const hasPermission = useHasPermission();
-
+export default function ChartToolbar({ modelState, dispatchModel }) {
return (
<>
- {hasPermission('files/charts/write') && (
-
- Save
-
- )}
dispatchModel({ type: 'undo' })} icon="icon undo">
Undo
diff --git a/packages/web/src/designer/QueryDesignToolbar.js b/packages/web/src/designer/QueryDesignToolbar.js
index 22c2fa3c4..c53092e69 100644
--- a/packages/web/src/designer/QueryDesignToolbar.js
+++ b/packages/web/src/designer/QueryDesignToolbar.js
@@ -1,18 +1,15 @@
import React from 'react';
-import useHasPermission from '../utility/useHasPermission';
import ToolbarButton from '../widgets/ToolbarButton';
export default function QueryDesignToolbar({
execute,
isDatabaseDefined,
busy,
- save,
modelState,
dispatchModel,
isConnected,
kill,
}) {
- const hasPermission = useHasPermission();
return (
<>
@@ -21,11 +18,6 @@ export default function QueryDesignToolbar({
Kill
- {hasPermission('files/query/write') && (
-
- Save
-
- )}
dispatchModel({ type: 'undo' })} icon="icon undo">
Undo
diff --git a/packages/web/src/markdown/MarkdownToolbar.js b/packages/web/src/markdown/MarkdownToolbar.js
index 108d325ce..7c87dab63 100644
--- a/packages/web/src/markdown/MarkdownToolbar.js
+++ b/packages/web/src/markdown/MarkdownToolbar.js
@@ -1,17 +1,9 @@
import React from 'react';
-import useHasPermission from '../utility/useHasPermission';
import ToolbarButton from '../widgets/ToolbarButton';
-export default function MarkdownToolbar({ save, showPreview }) {
- const hasPermission = useHasPermission();
-
+export default function MarkdownToolbar({ showPreview }) {
return (
<>
- {hasPermission('files/markdown/write') && (
-
- Save
-
- )}
Preview
diff --git a/packages/web/src/modals/SaveFileModal.js b/packages/web/src/modals/SaveFileModal.js
index 829eea28c..db587740d 100644
--- a/packages/web/src/modals/SaveFileModal.js
+++ b/packages/web/src/modals/SaveFileModal.js
@@ -34,11 +34,10 @@ export default function SaveFileModal({
}
};
- const handleSaveAs = async filePath => {
+ const handleSaveToDisk = async filePath => {
const path = window.require('path');
-
const parsed = path.parse(filePath);
- if (!parsed.ext) filePath += `.${fileExtension}`;
+ // if (!parsed.ext) filePath += `.${fileExtension}`;
await axios.post('files/save-as', { filePath, data, format });
modalState.close();
@@ -67,11 +66,15 @@ export default function SaveFileModal({
value="Save to disk"
onClick={() => {
const file = electron.remote.dialog.showSaveDialogSync(electron.remote.getCurrentWindow(), {
- filters: { name: `${fileExtension.toUpperCase()} files`, extensions: [fileExtension] },
- defaultPath: filePath,
+ filters: [
+ { name: `${fileExtension.toUpperCase()} files`, extensions: [fileExtension] },
+ { name: `All files`, extensions: ['*'] },
+ ],
+ defaultPath: filePath || `${name}.${fileExtension}`,
+ properties: ['showOverwriteConfirmation'],
});
if (file) {
- handleSaveAs(file);
+ handleSaveToDisk(file);
}
}}
/>
diff --git a/packages/web/src/modals/SaveTabModal.js b/packages/web/src/modals/SaveTabModal.js
index 0111d1114..3161292b2 100644
--- a/packages/web/src/modals/SaveTabModal.js
+++ b/packages/web/src/modals/SaveTabModal.js
@@ -1,12 +1,28 @@
import React from 'react';
+import axios from '../utility/axios';
import { changeTab } from '../utility/common';
import { useOpenedTabs, useSetOpenedTabs } from '../utility/globalState';
import keycodes from '../utility/keycodes';
+import SaveFileToolbarButton from '../utility/SaveFileToolbarButton';
+import ToolbarPortal from '../utility/ToolbarPortal';
+import useHasPermission from '../utility/useHasPermission';
import SaveFileModal from './SaveFileModal';
+import useModalState from './useModalState';
-export default function SaveTabModal({ data, folder, format, modalState, tabid, tabVisible, fileExtension }) {
+export default function SaveTabModal({
+ data,
+ folder,
+ format,
+ tabid,
+ tabVisible,
+ fileExtension,
+ toolbarPortalRef = undefined,
+}) {
const setOpenedTabs = useSetOpenedTabs();
const openedTabs = useOpenedTabs();
+ const saveFileModalState = useModalState();
+ const hasPermission = useHasPermission();
+ const canSave = hasPermission(`files/${folder}/write`);
const { savedFile, savedFilePath } = openedTabs.find(x => x.tabid == tabid).props || {};
const onSave = (title, newProps) => {
@@ -21,35 +37,63 @@ export default function SaveTabModal({ data, folder, format, modalState, tabid,
}));
};
+ const handleSave = async () => {
+ if (savedFile) {
+ await axios.post('files/save', { folder, file: savedFile, data, format });
+ }
+ if (savedFilePath) {
+ await axios.post('files/save-as', { filePath: savedFilePath, data, format });
+ }
+ };
+ const handleSaveRef = React.useRef(handleSave);
+ handleSaveRef.current = handleSave;
+
const handleKeyboard = React.useCallback(
e => {
if (e.keyCode == keycodes.s && e.ctrlKey) {
e.preventDefault();
- modalState.open();
+ if (e.shiftKey) {
+ saveFileModalState.open();
+ } else {
+ if (savedFile || savedFilePath) handleSaveRef.current();
+ else saveFileModalState.open();
+ }
}
},
- [modalState]
+ [saveFileModalState]
);
React.useEffect(() => {
- if (tabVisible) {
+ if (tabVisible && canSave) {
document.addEventListener('keydown', handleKeyboard);
return () => {
document.removeEventListener('keydown', handleKeyboard);
};
}
- }, [tabVisible, handleKeyboard]);
+ }, [tabVisible, handleKeyboard, canSave]);
return (
-
+ <>
+
+
+ {canSave && (
+
+
+
+ )}
+ >
);
}
diff --git a/packages/web/src/query/QueryToolbar.js b/packages/web/src/query/QueryToolbar.js
index 72567f0f3..7c51409be 100644
--- a/packages/web/src/query/QueryToolbar.js
+++ b/packages/web/src/query/QueryToolbar.js
@@ -2,7 +2,7 @@ import React from 'react';
import useHasPermission from '../utility/useHasPermission';
import ToolbarButton from '../widgets/ToolbarButton';
-export default function QueryToolbar({ execute, isDatabaseDefined, busy, save, format, isConnected, kill }) {
+export default function QueryToolbar({ execute, isDatabaseDefined, busy, format, isConnected, kill }) {
const hasPermission = useHasPermission();
return (
<>
@@ -15,11 +15,11 @@ export default function QueryToolbar({ execute, isDatabaseDefined, busy, save, f
Kill
- {hasPermission('files/sql/write') && (
+ {/* {hasPermission('files/sql/write') && (
Save
- )}
+ )} */}
Format
diff --git a/packages/web/src/query/ShellToolbar.js b/packages/web/src/query/ShellToolbar.js
index f141a8bd7..a9c747f40 100644
--- a/packages/web/src/query/ShellToolbar.js
+++ b/packages/web/src/query/ShellToolbar.js
@@ -1,9 +1,7 @@
import React from 'react';
-import useHasPermission from '../utility/useHasPermission';
import ToolbarButton from '../widgets/ToolbarButton';
-export default function ShellToolbar({ execute, cancel, busy, edit, save, editAvailable }) {
- const hasPermission = useHasPermission();
+export default function ShellToolbar({ execute, cancel, busy, edit, editAvailable }) {
return (
<>
@@ -15,11 +13,6 @@ export default function ShellToolbar({ execute, cancel, busy, edit, save, editAv
Show wizard
- {hasPermission('files/shell/write') && (
-
- Save
-
- )}
>
);
}
diff --git a/packages/web/src/tabs/ChartTab.js b/packages/web/src/tabs/ChartTab.js
index 61a880a29..934ce1279 100644
--- a/packages/web/src/tabs/ChartTab.js
+++ b/packages/web/src/tabs/ChartTab.js
@@ -4,17 +4,16 @@ import { createFreeTableModel } from 'dbgate-datalib';
import useUndoReducer from '../utility/useUndoReducer';
import ReactDOM from 'react-dom';
import { useUpdateDatabaseForTab } from '../utility/globalState';
-import useModalState from '../modals/useModalState';
import LoadingInfo from '../widgets/LoadingInfo';
import ErrorInfo from '../widgets/ErrorInfo';
import useEditorData from '../utility/useEditorData';
import SaveTabModal from '../modals/SaveTabModal';
import ChartEditor from '../charts/ChartEditor';
import ChartToolbar from '../charts/ChartToolbar';
+import ToolbarPortal from '../utility/ToolbarPortal';
export default function ChartTab({ tabVisible, toolbarPortalRef, conid, database, tabid }) {
const [modelState, dispatchModel] = useUndoReducer(createFreeTableModel());
- const saveFileModalState = useModalState();
const { initialData, setEditorData, errorMessage, isLoading } = useEditorData({
tabid,
});
@@ -57,21 +56,17 @@ export default function ChartTab({ tabVisible, toolbarPortalRef, conid, database
database={database}
/>
- {toolbarPortalRef &&
- toolbarPortalRef.current &&
- tabVisible &&
- ReactDOM.createPortal(
- ,
- toolbarPortalRef.current
- )}
+
+
+
>
);
}
diff --git a/packages/web/src/tabs/MarkdownEditorTab.js b/packages/web/src/tabs/MarkdownEditorTab.js
index 4725464a6..63241a8e7 100644
--- a/packages/web/src/tabs/MarkdownEditorTab.js
+++ b/packages/web/src/tabs/MarkdownEditorTab.js
@@ -11,10 +11,10 @@ import LoadingInfo from '../widgets/LoadingInfo';
import { useOpenedTabs, useSetOpenedTabs } from '../utility/globalState';
import useOpenNewTab from '../utility/useOpenNewTab';
import { setSelectedTabFunc } from '../utility/common';
+import ToolbarPortal from '../utility/ToolbarPortal';
export default function MarkdownEditorTab({ tabid, tabVisible, toolbarPortalRef, ...other }) {
const { editorData, setEditorData, isLoading, saveToStorage } = useEditorData({ tabid });
- const saveFileModalState = useModalState();
const openedTabs = useOpenedTabs();
const setOpenedTabs = useSetOpenedTabs();
const openNewTab = useOpenNewTab();
@@ -61,21 +61,17 @@ export default function MarkdownEditorTab({ tabid, tabVisible, toolbarPortalRef,
onKeyDown={handleKeyDown}
mode="markdown"
/>
- {toolbarPortalRef &&
- toolbarPortalRef.current &&
- tabVisible &&
- ReactDOM.createPortal(
- ,
- toolbarPortalRef.current
- )}
+
+
+
>
);
diff --git a/packages/web/src/tabs/QueryDesignTab.js b/packages/web/src/tabs/QueryDesignTab.js
index deb2f16d1..c9b9648a8 100644
--- a/packages/web/src/tabs/QueryDesignTab.js
+++ b/packages/web/src/tabs/QueryDesignTab.js
@@ -15,7 +15,6 @@ import keycodes from '../utility/keycodes';
import { changeTab } from '../utility/common';
import useSocket from '../utility/SocketProvider';
import SaveTabModal from '../modals/SaveTabModal';
-import useModalState from '../modals/useModalState';
import sqlFormatter from 'sql-formatter';
import useEditorData from '../utility/useEditorData';
import LoadingInfo from '../widgets/LoadingInfo';
@@ -27,6 +26,7 @@ import { generateDesignedQuery } from '../designer/designerTools';
import useUndoReducer from '../utility/useUndoReducer';
import { StatusBarItem } from '../widgets/StatusBar';
import useTimerLabel from '../utility/useTimerLabel';
+import ToolbarPortal from '../utility/ToolbarPortal';
export default function QueryDesignTab({
tabid,
@@ -43,7 +43,6 @@ export default function QueryDesignTab({
const setOpenedTabs = useSetOpenedTabs();
const socket = useSocket();
const [busy, setBusy] = React.useState(false);
- const saveFileModalState = useModalState();
const extensions = useExtensions();
const connection = useConnectionInfo({ conid });
const engine = findEngineDriver(connection, extensions);
@@ -196,36 +195,32 @@ export default function QueryDesignTab({
)}
- {toolbarPortalRef &&
- toolbarPortalRef.current &&
- tabVisible &&
- ReactDOM.createPortal(
- ,
- toolbarPortalRef.current
- )}
+
+
+
{statusbarPortalRef &&
statusbarPortalRef.current &&
tabVisible &&
ReactDOM.createPortal({timerLabel.text}, statusbarPortalRef.current)}
>
);
diff --git a/packages/web/src/tabs/QueryTab.js b/packages/web/src/tabs/QueryTab.js
index fcf6a8787..8f19529a1 100644
--- a/packages/web/src/tabs/QueryTab.js
+++ b/packages/web/src/tabs/QueryTab.js
@@ -23,6 +23,7 @@ import LoadingInfo from '../widgets/LoadingInfo';
import useExtensions from '../utility/useExtensions';
import useTimerLabel from '../utility/useTimerLabel';
import { StatusBarItem } from '../widgets/StatusBar';
+import ToolbarPortal from '../utility/ToolbarPortal';
function createSqlPreview(sql) {
if (!sql) return undefined;
@@ -58,7 +59,6 @@ export default function QueryTab({
const setOpenedTabs = useSetOpenedTabs();
const socket = useSocket();
const [busy, setBusy] = React.useState(false);
- const saveFileModalState = useModalState();
const extensions = useExtensions();
const timerLabel = useTimerLabel();
const { editorData, setEditorData, isLoading } = useEditorData({
@@ -201,7 +201,7 @@ export default function QueryTab({
)}
- {toolbarPortalRef &&
+ {/* {toolbarPortalRef &&
toolbarPortalRef.current &&
tabVisible &&
ReactDOM.createPortal(
@@ -216,19 +216,31 @@ export default function QueryTab({
kill={handleKill}
/>,
toolbarPortalRef.current
- )}
+ )} */}
{statusbarPortalRef &&
statusbarPortalRef.current &&
tabVisible &&
ReactDOM.createPortal({timerLabel.text}, statusbarPortalRef.current)}
+
+
+
>
);
diff --git a/packages/web/src/tabs/ShellTab.js b/packages/web/src/tabs/ShellTab.js
index f541704cb..3f9174b6d 100644
--- a/packages/web/src/tabs/ShellTab.js
+++ b/packages/web/src/tabs/ShellTab.js
@@ -14,10 +14,10 @@ import useShowModal from '../modals/showModal';
import ImportExportModal from '../modals/ImportExportModal';
import useEditorData from '../utility/useEditorData';
import SaveTabModal from '../modals/SaveTabModal';
-import useModalState from '../modals/useModalState';
import LoadingInfo from '../widgets/LoadingInfo';
import useTimerLabel from '../utility/useTimerLabel';
import { StatusBarItem } from '../widgets/StatusBar';
+import ToolbarPortal from '../utility/ToolbarPortal';
const configRegex = /\s*\/\/\s*@ImportExportConfigurator\s*\n\s*\/\/\s*(\{[^\n]+\})\n/;
const requireRegex = /\s*(\/\/\s*@require\s+[^\n]+)\n/g;
@@ -27,7 +27,6 @@ export default function ShellTab({ tabid, tabVisible, toolbarPortalRef, statusba
const [busy, setBusy] = React.useState(false);
const showModal = useShowModal();
const { editorData, setEditorData, isLoading } = useEditorData({ tabid });
- const saveFileModalState = useModalState();
const timerLabel = useTimerLabel();
const setOpenedTabs = useSetOpenedTabs();
@@ -120,32 +119,27 @@ export default function ShellTab({ tabid, tabVisible, toolbarPortalRef, statusba
/>
- {toolbarPortalRef &&
- toolbarPortalRef.current &&
- tabVisible &&
- ReactDOM.createPortal(
- ,
- toolbarPortalRef.current
- )}
+
+
+
{statusbarPortalRef &&
statusbarPortalRef.current &&
tabVisible &&
ReactDOM.createPortal({timerLabel.text}, statusbarPortalRef.current)}
>
);
diff --git a/packages/web/src/utility/SaveFileToolbarButton.js b/packages/web/src/utility/SaveFileToolbarButton.js
new file mode 100644
index 000000000..7e35cda3e
--- /dev/null
+++ b/packages/web/src/utility/SaveFileToolbarButton.js
@@ -0,0 +1,22 @@
+import React from 'react';
+import { DropDownMenuItem } from '../modals/DropDownMenu';
+import ToolbarButton, { ToolbarDropDownButton } from '../widgets/ToolbarButton';
+
+export default function SaveFileToolbarButton({ tabid, save, saveAs }) {
+ if (!saveAs) return null;
+
+ if (save) {
+ return (
+
+ Save
+ Save As
+
+ );
+ }
+
+ return (
+
+ Save As
+
+ );
+}
diff --git a/packages/web/src/utility/ToolbarPortal.js b/packages/web/src/utility/ToolbarPortal.js
new file mode 100644
index 000000000..ba184681f
--- /dev/null
+++ b/packages/web/src/utility/ToolbarPortal.js
@@ -0,0 +1,13 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+
+export default function ToolbarPortal({ toolbarPortalRef, tabVisible, children }) {
+ return (
+ (toolbarPortalRef &&
+ toolbarPortalRef.current &&
+ tabVisible &&
+ children &&
+ ReactDOM.createPortal(children, toolbarPortalRef.current)) ||
+ null
+ );
+}