mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2024-11-21 16:27:39 -06:00
Fix issues found during Electron testing. #7494
Fix application crash when using users dialog. #7607
This commit is contained in:
parent
dcfef154ce
commit
760e38293c
@ -7,7 +7,7 @@
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import { app, Menu, ipcMain } from 'electron';
|
||||
import { app, Menu, ipcMain, BrowserWindow } from 'electron';
|
||||
|
||||
const isMac = process.platform == 'darwin';
|
||||
let mainMenu;
|
||||
@ -72,8 +72,8 @@ function buildMenu(pgadminMenus, pgAdminMainScreen, callbacks) {
|
||||
{
|
||||
label: 'View',
|
||||
submenu: [
|
||||
{ label: 'Reload', click: ()=>pgAdminMainScreen.webContents.reload()},
|
||||
{ label: 'Toggle Developer Tools', click: ()=>pgAdminMainScreen.webContents.openDevTools({ mode: 'bottom' })},
|
||||
{ label: 'Reload', click: ()=>BrowserWindow.getFocusedWindow().webContents.reload()},
|
||||
{ label: 'Toggle Developer Tools', click: ()=>BrowserWindow.getFocusedWindow().webContents.openDevTools({ mode: 'bottom' })},
|
||||
{ type: 'separator' },
|
||||
{ role: 'resetZoom' },
|
||||
{ role: 'zoomIn' },
|
||||
|
@ -11,7 +11,7 @@ import fs from 'fs';
|
||||
import path from 'path';
|
||||
import net from 'net';
|
||||
import {platform} from 'os';
|
||||
import { app } from 'electron';
|
||||
import { app, session } from 'electron';
|
||||
|
||||
let pgadminServerProcess = null;
|
||||
|
||||
@ -141,7 +141,7 @@ export const getServerLogFile = () => {
|
||||
|
||||
// This function is used to kill the server process, remove the log files
|
||||
// and quit the application.
|
||||
export const cleanupAndQuitApp = (pgAdminWindowObject) => {
|
||||
export const cleanupAndQuitApp = () => {
|
||||
// Remove the server log file on exit
|
||||
removeLogFile();
|
||||
|
||||
@ -155,10 +155,7 @@ export const cleanupAndQuitApp = (pgAdminWindowObject) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (pgAdminWindowObject != null) {
|
||||
const ses = pgAdminWindowObject.webContents.session;
|
||||
ses.clearStorageData({
|
||||
storages: ['cookies'],
|
||||
});
|
||||
}
|
||||
session.defaultSession.clearStorageData({
|
||||
storages: ['cookies', 'localstorage'],
|
||||
});
|
||||
};
|
||||
|
@ -58,7 +58,7 @@ function openConfigure() {
|
||||
const win = new BrowserWindow({
|
||||
show: false,
|
||||
width: 600,
|
||||
height: 600,
|
||||
height: 620,
|
||||
position: 'center',
|
||||
resizable: false,
|
||||
icon: '../../assets/pgAdmin4.png',
|
||||
@ -81,8 +81,8 @@ function showErrorDialog(intervalID) {
|
||||
|
||||
new BrowserWindow({
|
||||
'frame': true,
|
||||
'width': 790,
|
||||
'height': 430,
|
||||
'width': 800,
|
||||
'height': 450,
|
||||
'position': 'center',
|
||||
'resizable': false,
|
||||
'focus': true,
|
||||
@ -251,8 +251,8 @@ function launchPgAdminWindow() {
|
||||
'view_logs': ()=>{
|
||||
const win = new BrowserWindow({
|
||||
show: false,
|
||||
width: 790,
|
||||
height: 425,
|
||||
width: 800,
|
||||
height: 460,
|
||||
position: 'center',
|
||||
resizable: false,
|
||||
icon: '../../assets/pgAdmin4.png',
|
||||
@ -302,9 +302,12 @@ function launchPgAdminWindow() {
|
||||
}
|
||||
});
|
||||
|
||||
pgAdminMainScreen.on('closed', ()=>{
|
||||
misc.cleanupAndQuitApp();
|
||||
});
|
||||
|
||||
pgAdminMainScreen.on('close', () => {
|
||||
configStore.set('bounds', pgAdminMainScreen.getBounds());
|
||||
misc.cleanupAndQuitApp(pgAdminMainScreen);
|
||||
pgAdminMainScreen.removeAllListeners('close');
|
||||
pgAdminMainScreen.close();
|
||||
});
|
||||
|
@ -61,7 +61,7 @@ export default class MainMenuFactory {
|
||||
return {
|
||||
...sm.serialize(),
|
||||
submenu: sm.getMenuItems()?.map((smsm)=>{
|
||||
MainMenuFactory.electronCallbacks[`${smName}_${smsm.name}`] = sm.callback;
|
||||
MainMenuFactory.electronCallbacks[`${smName}_${smsm.name}`] = smsm.callback;
|
||||
return {
|
||||
...smsm.serialize(),
|
||||
};
|
||||
|
@ -107,7 +107,7 @@ export default class FileManagerModule {
|
||||
if(res.canceled) {
|
||||
onCancel?.();
|
||||
} else {
|
||||
onOK?.(res.filePaths[0]);
|
||||
onOK?.(res.filePath ? res.filePath : res.filePaths[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
import React, {useRef, useEffect, useState, useCallback, useLayoutEffect} from 'react';
|
||||
import moment from 'moment';
|
||||
import { isMac } from './keyboard_shortcuts';
|
||||
import { getBrowser } from './utils';
|
||||
|
||||
/* React hook for setInterval */
|
||||
export function useInterval(callback, delay) {
|
||||
@ -209,3 +210,47 @@ export function useWindowSize() {
|
||||
export function useForceUpdate() {
|
||||
return React.useReducer(() => ({}), {})[1];
|
||||
}
|
||||
|
||||
export function useBeforeUnload({enabled, isNewTab, beforeClose, closePanel }) {
|
||||
const onBeforeUnload = useCallback((e)=>{
|
||||
e.preventDefault();
|
||||
e.returnValue = 'prevent';
|
||||
}, []);
|
||||
|
||||
const onBeforeUnloadElectron = useCallback((e)=>{
|
||||
e.preventDefault();
|
||||
e.returnValue = 'prevent';
|
||||
beforeClose?.();
|
||||
}, []);
|
||||
|
||||
useEffect(()=>{
|
||||
if(getBrowser().name == 'Electron') {
|
||||
window.addEventListener('beforeunload', onBeforeUnloadElectron);
|
||||
} else {
|
||||
if(enabled){
|
||||
window.addEventListener('beforeunload', onBeforeUnload);
|
||||
} else {
|
||||
window.removeEventListener('beforeunload', onBeforeUnload);
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', onBeforeUnloadElectron);
|
||||
window.removeEventListener('beforeunload', onBeforeUnload);
|
||||
};
|
||||
}, [enabled]);
|
||||
|
||||
|
||||
function forceClose() {
|
||||
if(getBrowser().name == 'Electron' && isNewTab) {
|
||||
window.removeEventListener('beforeunload', onBeforeUnloadElectron);
|
||||
// somehow window.close was not working may becuase the removeEventListener
|
||||
// was not completely executed. Add timeout.
|
||||
setTimeout(()=>window.close(), 50);
|
||||
} else {
|
||||
closePanel?.();
|
||||
}
|
||||
}
|
||||
|
||||
return {forceClose};
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useBeforeUnload } from '../../../../../../static/js/custom_hooks';
|
||||
|
||||
export default function BeforeUnload({enabled, isNewTab, beforeClose, closePanel, getForceClose}) {
|
||||
const {forceClose} = useBeforeUnload(
|
||||
{enabled, isNewTab, beforeClose, closePanel}
|
||||
);
|
||||
|
||||
getForceClose(forceClose);
|
||||
|
||||
return <></>;
|
||||
}
|
||||
|
||||
BeforeUnload.propTypes = {
|
||||
enabled: PropTypes.bool,
|
||||
isNewTab: PropTypes.bool,
|
||||
beforeClose: PropTypes.func,
|
||||
closePanel: PropTypes.func,
|
||||
getForceClose: PropTypes.func,
|
||||
};
|
@ -35,6 +35,7 @@ import { LAYOUT_EVENTS } from '../../../../../../static/js/helpers/Layout';
|
||||
import usePreferences from '../../../../../../preferences/static/js/store';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import BeforeUnload from './BeforeUnload';
|
||||
|
||||
/* Custom react-diagram action for keyboard events */
|
||||
export class KeyboardShortcutAction extends Action {
|
||||
@ -144,12 +145,13 @@ export default class ERDTool extends React.Component {
|
||||
_.bindAll(this, ['onLoadDiagram', 'onSaveDiagram', 'onSQLClick',
|
||||
'onImageClick', 'onAddNewNode', 'onEditTable', 'onCloneNode', 'onDeleteNode', 'onNoteClick',
|
||||
'onNoteClose', 'onOneToManyClick', 'onManyToManyClick', 'onAutoDistribute', 'onDetailsToggle',
|
||||
'onChangeColors', 'onDropNode', 'onBeforeUnload', 'onNotationChange',
|
||||
'onChangeColors', 'onDropNode', 'onNotationChange', 'closePanel'
|
||||
]);
|
||||
|
||||
this.diagram.zoomToFit = this.diagram.zoomToFit.bind(this.diagram);
|
||||
this.diagram.zoomIn = this.diagram.zoomIn.bind(this.diagram);
|
||||
this.diagram.zoomOut = this.diagram.zoomOut.bind(this.diagram);
|
||||
this.forceClose = this.closePanel;
|
||||
}
|
||||
|
||||
registerModelEvents() {
|
||||
@ -315,14 +317,7 @@ export default class ERDTool extends React.Component {
|
||||
|
||||
this.props.panelDocker.eventBus.registerListener(LAYOUT_EVENTS.CLOSING, (id)=>{
|
||||
if(this.props.panelId == id) {
|
||||
window.removeEventListener('beforeunload', this.onBeforeUnload);
|
||||
if(this.state.dirty) {
|
||||
this.closeOnSave = false;
|
||||
this.confirmBeforeClose();
|
||||
return false;
|
||||
}
|
||||
this.closePanel();
|
||||
return true;
|
||||
this.confirmBeforeClose();
|
||||
}
|
||||
});
|
||||
|
||||
@ -354,45 +349,34 @@ export default class ERDTool extends React.Component {
|
||||
if(this.props.params.gen) {
|
||||
await this.loadTablesData();
|
||||
}
|
||||
|
||||
if(this.state.is_close_tab_warning) {
|
||||
window.addEventListener('beforeunload', this.onBeforeUnload);
|
||||
} else {
|
||||
window.removeEventListener('beforeunload', this.onBeforeUnload);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('beforeunload', this.onBeforeUnload);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if(this.state.dirty) {
|
||||
this.setTitle(this.state.current_file, true);
|
||||
}
|
||||
// Add beforeunload event if "Confirm on close or refresh" option is enabled in the preferences.
|
||||
if(this.state.is_close_tab_warning){
|
||||
window.addEventListener('beforeunload', this.onBeforeUnload);
|
||||
} else {
|
||||
window.removeEventListener('beforeunload', this.onBeforeUnload);
|
||||
}
|
||||
}
|
||||
|
||||
confirmBeforeClose() {
|
||||
let bodyObj = this;
|
||||
this.context.showModal(gettext('Save changes?'), (closeModal)=>(
|
||||
<ConfirmSaveContent
|
||||
closeModal={closeModal}
|
||||
text={gettext('The diagram has changed. Do you want to save changes?')}
|
||||
onDontSave={()=>{
|
||||
bodyObj.closePanel();
|
||||
}}
|
||||
onSave={()=>{
|
||||
bodyObj.onSaveDiagram(false, true);
|
||||
}}
|
||||
/>
|
||||
));
|
||||
return false;
|
||||
if(this.state.dirty) {
|
||||
this.closeOnSave = false;
|
||||
this.context.showModal(gettext('Save changes?'), (closeModal)=>(
|
||||
<ConfirmSaveContent
|
||||
closeModal={closeModal}
|
||||
text={gettext('The diagram has changed. Do you want to save changes?')}
|
||||
onDontSave={()=>{
|
||||
bodyObj.forceClose();
|
||||
}}
|
||||
onSave={()=>{
|
||||
bodyObj.onSaveDiagram(false, true);
|
||||
}}
|
||||
/>
|
||||
));
|
||||
return false;
|
||||
} else {
|
||||
this.forceClose();
|
||||
}
|
||||
}
|
||||
|
||||
closePanel() {
|
||||
@ -463,15 +447,6 @@ export default class ERDTool extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeUnload(e) {
|
||||
if(this.state.dirty) {
|
||||
e.preventDefault();
|
||||
e.returnValue = 'prevent';
|
||||
} else {
|
||||
delete e['returnValue'];
|
||||
}
|
||||
}
|
||||
|
||||
onDropNode(e) {
|
||||
let nodeDropData = JSON.parse(e.dataTransfer.getData('text'));
|
||||
if(nodeDropData.objUrl && nodeDropData.nodeType === 'table') {
|
||||
@ -627,7 +602,7 @@ export default class ERDTool extends React.Component {
|
||||
this.setTitle(fileName);
|
||||
this.setLoading(null);
|
||||
if(this.closeOnSave) {
|
||||
this.closePanel();
|
||||
this.forceClose();
|
||||
}
|
||||
}).catch((err)=>{
|
||||
this.setLoading(null);
|
||||
@ -910,6 +885,15 @@ export default class ERDTool extends React.Component {
|
||||
|
||||
return (
|
||||
<StyledBox ref={this.containerRef} height="100%" display="flex" flexDirection="column">
|
||||
<BeforeUnload
|
||||
enabled={this.state.is_close_tab_warning}
|
||||
isNewTab={this.state.is_new_tab}
|
||||
beforeClose={()=>{
|
||||
this.confirmBeforeClose();
|
||||
}}
|
||||
closePanel={this.closePanel}
|
||||
getForceClose={(fn)=>this.forceClose = fn}
|
||||
/>
|
||||
<ConnectionBar status={this.state.conn_status} bgcolor={this.props.params.bgcolor}
|
||||
fgcolor={this.props.params.fgcolor} title={_.unescape(this.props.params.title)}/>
|
||||
<MainToolBar preferences={this.state.preferences} eventBus={this.eventBus}
|
||||
|
@ -19,12 +19,12 @@ import { Messages } from './sections/Messages';
|
||||
import getApiInstance, {callFetch, parseApiError} from '../../../../../static/js/api_instance';
|
||||
import url_for from 'sources/url_for';
|
||||
import { PANELS, QUERY_TOOL_EVENTS, CONNECTION_STATUS, MAX_QUERY_LENGTH } from './QueryToolConstants';
|
||||
import { useInterval } from '../../../../../static/js/custom_hooks';
|
||||
import { useBeforeUnload, useInterval } from '../../../../../static/js/custom_hooks';
|
||||
import { Box } from '@mui/material';
|
||||
import { getDatabaseLabel, getTitle, setQueryToolDockerTitle } from '../sqleditor_title';
|
||||
import gettext from 'sources/gettext';
|
||||
import NewConnectionDialog from './dialogs/NewConnectionDialog';
|
||||
import { evalFunc, getBrowser } from '../../../../../static/js/utils';
|
||||
import { evalFunc } from '../../../../../static/js/utils';
|
||||
import { Notifications } from './sections/Notifications';
|
||||
import MacrosDialog from './dialogs/MacrosDialog';
|
||||
import FilterDialog from './dialogs/FilterDialog';
|
||||
@ -90,11 +90,6 @@ function setPanelTitle(docker, panelId, title, qtState, dirty=false) {
|
||||
}
|
||||
}
|
||||
|
||||
function onBeforeUnload(e) {
|
||||
e.preventDefault();
|
||||
e.returnValue = 'prevent';
|
||||
}
|
||||
|
||||
const FIXED_PREF = {
|
||||
find: {
|
||||
'control': true,
|
||||
@ -405,11 +400,16 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
|
||||
});
|
||||
};
|
||||
|
||||
const onBeforeUnloadElectron = (e)=>{
|
||||
e.preventDefault();
|
||||
e.returnValue = 'prevent';
|
||||
eventBus.current.fireEvent(QUERY_TOOL_EVENTS.WARN_SAVE_DATA_CLOSE);
|
||||
};
|
||||
const {forceClose} = useBeforeUnload({
|
||||
enabled: qtState.preferences.browser.confirm_on_refresh_close,
|
||||
isNewTab: qtState.is_new_tab,
|
||||
beforeClose: ()=>{
|
||||
eventBus.current.fireEvent(QUERY_TOOL_EVENTS.WARN_SAVE_DATA_CLOSE);
|
||||
},
|
||||
closePanel: ()=>{
|
||||
qtPanelDocker.close(qtPanelId, true);
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(()=>{
|
||||
getSQLScript();
|
||||
@ -424,19 +424,11 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
|
||||
});
|
||||
|
||||
eventBus.current.registerListener(QUERY_TOOL_EVENTS.FORCE_CLOSE_PANEL, ()=>{
|
||||
if(getBrowser().name == 'Electron' && qtState.is_new_tab) {
|
||||
window.removeEventListener('beforeunload', onBeforeUnloadElectron);
|
||||
// somehow window.close was not working may becuase the removeEventListener
|
||||
// was not completely executed. Add timeout.
|
||||
setTimeout(()=>window.close(), 50);
|
||||
} else {
|
||||
qtPanelDocker.close(qtPanelId, true);
|
||||
}
|
||||
forceClose();
|
||||
});
|
||||
|
||||
qtPanelDocker.eventBus.registerListener(LAYOUT_EVENTS.CLOSING, (id)=>{
|
||||
if(qtPanelId == id) {
|
||||
window.removeEventListener('beforeunload', onBeforeUnload);
|
||||
eventBus.current.fireEvent(QUERY_TOOL_EVENTS.WARN_SAVE_DATA_CLOSE);
|
||||
}
|
||||
});
|
||||
@ -643,24 +635,6 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(()=> {
|
||||
// Add beforeunload event if "Confirm on close or refresh" option is enabled in the preferences.
|
||||
if(getBrowser().name == 'Electron') {
|
||||
window.addEventListener('beforeunload', onBeforeUnloadElectron);
|
||||
} else {
|
||||
if(qtState.preferences.browser.confirm_on_refresh_close){
|
||||
window.addEventListener('beforeunload', onBeforeUnload);
|
||||
} else {
|
||||
window.removeEventListener('beforeunload', onBeforeUnload);
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', onBeforeUnloadElectron);
|
||||
window.removeEventListener('beforeunload', onBeforeUnload);
|
||||
};
|
||||
}, [qtState.preferences.browser]);
|
||||
|
||||
const updateQueryToolConnection = (connectionData, isNew=false)=>{
|
||||
let currSelectedConn = _.find(qtState.connection_list, (c)=>c.is_selected);
|
||||
let currConnected = qtState.connected;
|
||||
|
@ -317,7 +317,7 @@ function UserManagementDialog({onClose}) {
|
||||
const [roles, setRoles] = React.useState([]);
|
||||
const api = getApiInstance();
|
||||
|
||||
React.useEffect(async ()=>{
|
||||
const fetchData = async ()=>{
|
||||
try {
|
||||
api.get(url_for('user_management.auth_sources'))
|
||||
.then(res=>{
|
||||
@ -337,6 +337,10 @@ function UserManagementDialog({onClose}) {
|
||||
} catch (error) {
|
||||
pgAdmin.Browser.notifier.error(parseApiError(error));
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(()=>{
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
const onSaveClick = (_isNew, changeData)=>{
|
||||
|
Loading…
Reference in New Issue
Block a user