Fix issues found during Electron testing. #7494

Fix application crash when using users dialog. #7607
This commit is contained in:
Aditya Toshniwal 2024-07-08 19:24:29 +05:30
parent dcfef154ce
commit 760e38293c
10 changed files with 135 additions and 107 deletions

View File

@ -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' },

View File

@ -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'],
});
};

View File

@ -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();
});

View File

@ -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(),
};

View File

@ -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]);
}
}

View File

@ -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};
}

View File

@ -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,
};

View File

@ -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}

View File

@ -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;

View File

@ -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)=>{