electron: save file to custom location

This commit is contained in:
Jan Prochazka
2021-01-28 18:52:59 +01:00
parent df976a84d2
commit fe1c5f5801
14 changed files with 132 additions and 13 deletions

View File

@@ -51,6 +51,12 @@ function buildMenu() {
mainWindow.webContents.executeJavaScript(`dbgate_newQuery()`); mainWindow.webContents.executeJavaScript(`dbgate_newQuery()`);
}, },
}, },
{
label: 'Open file',
click() {
mainWindow.webContents.executeJavaScript(`dbgate_openFile()`);
},
},
{ {
label: 'Close all tabs', label: 'Close all tabs',
click() { click() {

View File

@@ -78,6 +78,11 @@ module.exports = {
} }
}, },
saveAs_meta: 'post',
async saveAs({ filePath, data, format }) {
await fs.writeFile(filePath, serialize(format, data));
},
favorites_meta: 'get', favorites_meta: 'get',
async favorites() { async favorites() {
if (!hasPermission(`files/favorites/read`)) return []; if (!hasPermission(`files/favorites/read`)) return [];

View File

@@ -124,6 +124,15 @@ function getDbIcon(key) {
return 'icon file'; return 'icon file';
} }
function buildTooltip(tab) {
let res = tab.tooltip;
if (tab.props && tab.props.savedFilePath) {
if (res) res += '\n';
res += tab.props.savedFilePath;
}
return res;
}
export default function TabsPanel() { export default function TabsPanel() {
// const formatDbKey = (conid, database) => `${database}-${conid}`; // const formatDbKey = (conid, database) => `${database}-${conid}`;
const theme = useTheme(); const theme = useTheme();
@@ -252,7 +261,7 @@ export default function TabsPanel() {
{_.sortBy(tabsByDb[dbKey], ['title', 'tabid']).map(tab => ( {_.sortBy(tabsByDb[dbKey], ['title', 'tabid']).map(tab => (
<FileTabItem <FileTabItem
{...tab} {...tab}
title={tab.tooltip} title={buildTooltip(tab)}
key={tab.tabid} key={tab.tabid}
theme={theme} theme={theme}
onClick={e => handleTabClick(e, tab.tabid)} onClick={e => handleTabClick(e, tab.tabid)}

View File

@@ -149,7 +149,7 @@ export async function openDatabaseObjectDetail(
openNewTab( openNewTab(
{ {
title: pureName, title: 'Query #',
tooltip, tooltip,
icon: sqlTemplate ? 'img sql-file' : icons[objectTypeField], icon: sqlTemplate ? 'img sql-file' : icons[objectTypeField],
tabComponent: sqlTemplate ? 'QueryTab' : tabComponent, tabComponent: sqlTemplate ? 'QueryTab' : tabComponent,

View File

@@ -6,14 +6,52 @@ import ModalHeader from './ModalHeader';
import ModalContent from './ModalContent'; import ModalContent from './ModalContent';
import ModalFooter from './ModalFooter'; import ModalFooter from './ModalFooter';
import { FormProvider } from '../utility/FormProvider'; import { FormProvider } from '../utility/FormProvider';
import FormStyledButton from '../widgets/FormStyledButton';
import getElectron from '../utility/getElectron';
export default function SaveFileModal({
data,
folder,
format,
modalState,
name,
fileExtension,
filePath,
onSave = undefined,
}) {
const electron = getElectron();
export default function SaveFileModal({ data, folder, format, modalState, name, onSave = undefined }) {
const handleSubmit = async values => { const handleSubmit = async values => {
const { name } = values; const { name } = values;
await axios.post('files/save', { folder, file: name, data, format }); await axios.post('files/save', { folder, file: name, data, format });
modalState.close(); modalState.close();
if (onSave) onSave(name); if (onSave) {
onSave(name, {
savedFile: name,
savedFolder: folder,
savedFilePath: null,
});
}
}; };
const handleSaveAs = async filePath => {
const path = window.require('path');
const parsed = path.parse(filePath);
if (!parsed.ext) filePath += `.${fileExtension}`;
await axios.post('files/save-as', { filePath, data, format });
modalState.close();
if (onSave) {
onSave(parsed.name, {
savedFile: null,
savedFolder: null,
savedFilePath: filePath,
});
}
};
return ( return (
<ModalBase modalState={modalState}> <ModalBase modalState={modalState}>
<ModalHeader modalState={modalState}>Save file</ModalHeader> <ModalHeader modalState={modalState}>Save file</ModalHeader>
@@ -23,6 +61,21 @@ export default function SaveFileModal({ data, folder, format, modalState, name,
</ModalContent> </ModalContent>
<ModalFooter> <ModalFooter>
<FormSubmit value="Save" onClick={handleSubmit} /> <FormSubmit value="Save" onClick={handleSubmit} />
{electron && (
<FormStyledButton
type="button"
value="Save to disk"
onClick={() => {
const file = electron.remote.dialog.showSaveDialogSync(electron.remote.getCurrentWindow(), {
filters: { name: `${fileExtension.toUpperCase()} files`, extensions: [fileExtension] },
defaultPath: filePath,
});
if (file) {
handleSaveAs(file);
}
}}
/>
)}
</ModalFooter> </ModalFooter>
</FormProvider> </FormProvider>
</ModalBase> </ModalBase>

View File

@@ -4,22 +4,22 @@ import { useOpenedTabs, useSetOpenedTabs } from '../utility/globalState';
import keycodes from '../utility/keycodes'; import keycodes from '../utility/keycodes';
import SaveFileModal from './SaveFileModal'; import SaveFileModal from './SaveFileModal';
export default function SaveTabModal({ data, folder, format, modalState, tabid, tabVisible }) { export default function SaveTabModal({ data, folder, format, modalState, tabid, tabVisible, fileExtension }) {
const setOpenedTabs = useSetOpenedTabs(); const setOpenedTabs = useSetOpenedTabs();
const openedTabs = useOpenedTabs(); const openedTabs = useOpenedTabs();
const { savedFile } = openedTabs.find(x => x.tabid == tabid).props || {}; const { savedFile, savedFilePath } = openedTabs.find(x => x.tabid == tabid).props || {};
const onSave = name => const onSave = (title, newProps) => {
changeTab(tabid, setOpenedTabs, tab => ({ changeTab(tabid, setOpenedTabs, tab => ({
...tab, ...tab,
title: name, title,
props: { props: {
...tab.props, ...tab.props,
savedFile: name,
savedFolder: folder,
savedFormat: format, savedFormat: format,
...newProps,
}, },
})); }));
};
const handleKeyboard = React.useCallback( const handleKeyboard = React.useCallback(
e => { e => {
@@ -47,6 +47,8 @@ export default function SaveTabModal({ data, folder, format, modalState, tabid,
format={format} format={format}
modalState={modalState} modalState={modalState}
name={savedFile || 'newFile'} name={savedFile || 'newFile'}
filePath={savedFilePath}
fileExtension={fileExtension}
onSave={onSave} onSave={onSave}
/> />
); );

View File

@@ -63,6 +63,7 @@ export default function ChartTab({ tabVisible, toolbarPortalRef, conid, database
format="json" format="json"
folder="charts" folder="charts"
tabid={tabid} tabid={tabid}
fileExtension='chart'
/> />
{toolbarPortalRef && {toolbarPortalRef &&
toolbarPortalRef.current && toolbarPortalRef.current &&

View File

@@ -75,6 +75,7 @@ export default function MarkdownEditorTab({ tabid, tabVisible, toolbarPortalRef,
format="text" format="text"
folder="markdown" folder="markdown"
tabid={tabid} tabid={tabid}
fileExtension='md'
/> />
</> </>
); );

View File

@@ -225,6 +225,7 @@ export default function QueryDesignTab({
format="json" format="json"
folder="query" folder="query"
tabid={tabid} tabid={tabid}
fileExtension='qdesign'
/> />
</> </>
); );

View File

@@ -228,6 +228,7 @@ export default function QueryTab({
format="text" format="text"
folder="sql" folder="sql"
tabid={tabid} tabid={tabid}
fileExtension='sql'
/> />
</> </>
); );

View File

@@ -145,6 +145,7 @@ export default function ShellTab({ tabid, tabVisible, toolbarPortalRef, statusba
format="text" format="text"
folder="shell" folder="shell"
tabid={tabid} tabid={tabid}
fileExtension='js'
/> />
</> </>
); );

View File

@@ -0,0 +1,31 @@
import useNewQuery from '../query/useNewQuery';
import getElectron from './getElectron';
export default function useOpenElectronFile() {
const electron = getElectron();
const newQuery = useNewQuery();
return () => {
const filePaths = electron.remote.dialog.showOpenDialogSync(electron.remote.getCurrentWindow(), {
filters: { name: `SQL files`, extensions: ['sql'] },
});
const filePath = filePaths && filePaths[0];
if (filePath) {
if (filePath.match(/.sql$/i)) {
const path = window.require('path');
const fs = window.require('fs');
const parsed = path.parse(filePath);
const data = fs.readFileSync(filePath, { encoding: 'utf-8' });
newQuery({
title: parsed.name,
initialData: data,
// @ts-ignore
savedFilePath: filePath,
savedFormat: 'text',
});
}
}
};
}

View File

@@ -23,11 +23,16 @@ export default function useOpenNewTab() {
async (newTab, initialData = undefined, options) => { async (newTab, initialData = undefined, options) => {
let existing = null; let existing = null;
const { savedFile } = newTab.props || {}; const { savedFile, savedFolder, savedFilePath } = newTab.props || {};
if (savedFile) { if (savedFile || savedFilePath) {
existing = openedTabs.find( existing = openedTabs.find(
x => x =>
x.props && x.tabComponent == newTab.tabComponent && x.closedTime == null && x.props.savedFile == savedFile x.props &&
x.tabComponent == newTab.tabComponent &&
x.closedTime == null &&
x.props.savedFile == savedFile &&
x.props.savedFolder == savedFolder &&
x.props.savedFilePath == savedFilePath
); );
} }

View File

@@ -25,6 +25,7 @@ import tabs from '../tabs';
import FavoriteModal from '../modals/FavoriteModal'; import FavoriteModal from '../modals/FavoriteModal';
import { useOpenFavorite } from '../appobj/FavoriteFileAppObject'; import { useOpenFavorite } from '../appobj/FavoriteFileAppObject';
import ErrorMessageModal from '../modals/ErrorMessageModal'; import ErrorMessageModal from '../modals/ErrorMessageModal';
import useOpenElectronFile from '../utility/useOpenElectronFile';
const ToolbarContainer = styled.div` const ToolbarContainer = styled.div`
display: flex; display: flex;
@@ -48,6 +49,7 @@ export default function ToolBar({ toolbarPortalRef }) {
const electron = getElectron(); const electron = getElectron();
const favorites = useFavorites(); const favorites = useFavorites();
const openFavorite = useOpenFavorite(); const openFavorite = useOpenFavorite();
const openElectronFile = useOpenElectronFile();
const currentTab = openedTabs.find(x => x.selected); const currentTab = openedTabs.find(x => x.selected);
@@ -58,6 +60,7 @@ export default function ToolBar({ toolbarPortalRef }) {
window['dbgate_newQuery'] = newQuery; window['dbgate_newQuery'] = newQuery;
window['dbgate_closeAll'] = () => setOpenedTabs([]); window['dbgate_closeAll'] = () => setOpenedTabs([]);
window['dbgate_showAbout'] = showAbout; window['dbgate_showAbout'] = showAbout;
window['dbgate_openFile'] = openElectronFile;
}); });
const showAbout = () => { const showAbout = () => {