Ensure that modal dialogs are not triggered more than once to avoid duplicates. #8316

pull/8571/head
Yogesh Mahajan 2025-03-18 15:58:16 +05:30 committed by GitHub
parent f25cde8c5f
commit b2669930f5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 75 additions and 51 deletions

View File

@ -31,6 +31,9 @@ let startPageUrl = null;
let serverCheckUrl = null;
let pgAdminMainScreen = null;
let configureWindow = null,
viewLogWindow = null;
let serverPort = 5050;
let appStartTime = (new Date()).getTime();
const __dirname = path.dirname(fileURLToPath(import.meta.url));
@ -77,7 +80,8 @@ contextMenu({
Menu.setApplicationMenu(null);
function openConfigure() {
const win = new BrowserWindow({
if (configureWindow === null){
configureWindow = new BrowserWindow({
show: false,
width: 600,
height: 580,
@ -88,11 +92,12 @@ function openConfigure() {
preload: path.join(__dirname, 'other_preload.js'),
},
});
win.loadFile('./src/html/configure.html');
win.once('ready-to-show', ()=>{
win.show();
configureWindow.loadFile('./src/html/configure.html');
configureWindow.once('ready-to-show', ()=>{
configureWindow.show();
});
}
}
function showErrorDialog(intervalID) {
if(!splashWindow.isVisible()) {
@ -304,21 +309,23 @@ function launchPgAdminWindow() {
setupMenu(pgAdminMainScreen, {
'view_logs': ()=>{
const win = new BrowserWindow({
show: false,
width: 800,
height: 460,
position: 'center',
resizable: false,
icon: '../../assets/pgAdmin4.png',
webPreferences: {
preload: path.join(__dirname, 'other_preload.js'),
},
});
win.loadFile('./src/html/view_log.html');
win.once('ready-to-show', ()=>{
win.show();
});
if(viewLogWindow === null){
viewLogWindow = new BrowserWindow({
show: false,
width: 800,
height: 460,
position: 'center',
resizable: false,
icon: '../../assets/pgAdmin4.png',
webPreferences: {
preload: path.join(__dirname, 'other_preload.js'),
},
});
viewLogWindow.loadFile('./src/html/view_log.html');
viewLogWindow.once('ready-to-show', ()=>{
viewLogWindow.show();
});
}
},
'configure': openConfigure,
'reloadApp': reloadApp,

View File

@ -43,7 +43,7 @@ class About {
pgAdmin.Browser.notifier.showModal(gettext('About %s', pgAdmin.Browser.utils.app_name), () => {
return <AboutComponent />;
}, { isFullScreen: false, isResizeable: true, showFullScreen: true,
isFullWidth: true, dialogWidth: dlgWidth, dialogHeight: dlgHeight, minHeight: dlgHeight
isFullWidth: true, dialogWidth: dlgWidth, dialogHeight: dlgHeight, minHeight: dlgHeight, id:'id-about'
});
}
}

View File

@ -46,6 +46,7 @@ export const BROWSER_PANELS = {
WELCOME_PSQL_TOOL: 'id-welcome-psql'
};
export const WORKSPACES = {
DEFAULT: 'default_workspace',
QUERY_TOOL: 'query_tool_workspace',

View File

@ -689,7 +689,6 @@ export default function PreferencesComponent({ ...props }) {
const text = `${gettext('All preferences will be reset to their default values.')}<br><br>${gettext('Do you want to proceed?')}<br><br>
${gettext('Note:')}<br> <ul style="padding-left:20px"><li style="list-style-type:disc">${gettext('The object explorer tree will be refreshed automatically to reflect the changes.')}</li>
<li style="list-style-type:disc">${gettext('If the application language changes, a reload of the application will be required. You can choose to reload later at your convenience.')}</li></ul>`;
pgAdmin.Browser.notifier.showModal(
gettext('Reset all preferences'),
(closeModal)=>{
@ -710,7 +709,7 @@ ${gettext('Note:')}<br> <ul style="padding-left:20px"><li style="list-style-type
</StyledBox>
);
},
{ isFullScreen: false, isResizeable: false, showFullScreen: false, isFullWidth: false, showTitle: true},
{ isFullScreen: false, isResizeable: false, showFullScreen: false, isFullWidth: false, showTitle: true, id: 'id-reset-preferences'},
);
};

View File

@ -48,7 +48,6 @@ export default class Preferences {
// This is a callback function to show preferences.
show() {
// Render Preferences component
pgAdmin.Browser.notifier.showModal(gettext('Preferences'), (closeModal) => {
return <PreferencesComponent
@ -56,6 +55,6 @@ export default class Preferences {
// Render preferences tree component
return <PreferencesTree pgBrowser={this.pgBrowser} data={prefTreeData} />;
}} closeModal={closeModal} />;
}, { isFullScreen: false, isResizeable: true, showFullScreen: true, isFullWidth: true, dialogWidth: 900, dialogHeight: 550 });
}, { isFullScreen: false, isResizeable: true, showFullScreen: true, isFullWidth: true, dialogWidth: 900, dialogHeight: 550, id: 'id-preferences' });
}
}

View File

@ -151,7 +151,6 @@ export default function BrowserComponent({pgAdmin}) {
let { name: browser } = useMemo(()=>getBrowser(), []);
const [uiReady, setUiReady] = useState(false);
const confirmOnClose = getPreferencesForModule('browser').confirm_on_refresh_close;
useBeforeUnload({
enabled: confirmOnClose,
beforeClose: (forceClose)=>{
@ -159,7 +158,11 @@ export default function BrowserComponent({pgAdmin}) {
gettext('Quit pgAdmin 4'),
gettext('Are you sure you want to quit the application?'),
function() { forceClose(); },
function() { return true;},
function() { return true; },
gettext('Yes'),
gettext('No'),
'default',
'id-app-quit'
);
},
isNewTab: true,

View File

@ -67,7 +67,7 @@ export function showServerPassword() {
}}
/>
);
});
}, {id: 'id-connect-server'});
}
function masterPassCallbacks(masterpass_callback_queue) {
@ -176,7 +176,7 @@ export function showMasterPassword(isPWDPresent, errmsg, masterpass_callback_que
}}
/>
);
});
}, {id: 'id-master-password'});
}
}
@ -275,7 +275,7 @@ export function showChangeUserPassword(url) {
/>;
},
{ isFullScreen: false, isResizeable: true, showFullScreen: false, isFullWidth: true,
dialogWidth: pgAdmin.Browser.stdW.md, dialogHeight: pgAdmin.Browser.stdH.md});
dialogWidth: pgAdmin.Browser.stdW.md, dialogHeight: pgAdmin.Browser.stdH.md, id: 'id-change-password'});
}
export function showNamedRestorePoint() {
@ -351,7 +351,7 @@ export function showChangeOwnership() {
/>;
},
{ isFullScreen: false, isResizeable: true, showFullScreen: true, isFullWidth: true,
dialogWidth: pgAdmin.Browser.stdW.md, dialogHeight: pgAdmin.Browser.stdH.md});
dialogWidth: pgAdmin.Browser.stdW.md, dialogHeight: pgAdmin.Browser.stdH.md, id: 'id-change-owner' });
}
export function showUrlDialog() {
@ -388,6 +388,6 @@ export function showQuickSearch() {
pgAdmin.Browser.notifier.showModal(gettext('Quick Search'), (closeModal) => {
return <QuickSearch closeModal={closeModal}/>;
},
{ isFullScreen: false, isResizeable: false, showFullScreen: false, isFullWidth: false, showTitle: false}
{ isFullScreen: false, isResizeable: false, showFullScreen: false, isFullWidth: false, showTitle: false, id: 'id-quick-search'}
);
}

View File

@ -83,7 +83,7 @@ function alert(title, text, onOkClick, okLabel = gettext('OK')) {
});
}
function confirm(title, text, onOkClick, onCancelClick, okLabel = gettext('Yes'), cancelLabel = gettext('No'), okIcon = 'default') {
function confirm(title, text, onOkClick, onCancelClick, okLabel = gettext('Yes'), cancelLabel = gettext('No'), okIcon = 'default', modalId=null) {
// bind the modal provider before calling
this.showModal(title, (closeModal) => {
const onCancelClickClose = () => {
@ -98,7 +98,7 @@ function confirm(title, text, onOkClick, onCancelClick, okLabel = gettext('Yes')
return (
<AlertContent text={text} confirm onOkClick={onOkClickClose} onCancelClick={onCancelClickClose} okLabel={okLabel} cancelLabel={cancelLabel} okIcon={okIcon}/>
);
});
}, {modalId: modalId});
}
function confirmDelete(title, text, onDeleteClick, onCancelClick, deleteLabel = gettext('Delete'), cancelLabel = gettext('Cancel')) {
@ -127,16 +127,23 @@ function confirmDelete(title, text, onDeleteClick, onCancelClick, deleteLabel =
export default function ModalProvider({ children }) {
const [modals, setModals] = React.useState([]);
const showModal = (title, content, modalOptions) => {
let id = getEpoch().toString() + crypto.getRandomValues(new Uint8Array(4));
setModals((prev) => [...prev, {
id: id,
title: title,
content: content,
...modalOptions,
}]);
if(modalOptions?.id){
id = modalOptions.id;
}
setModals((prev) => {
if(prev?.find(modal=> modal.id === modalOptions?.id)){
return prev;
}
return [...prev, {
id: id,
title: title,
content: content,
...modalOptions,
}];});
};
const closeModal = (id) => {
setModals((prev) => {
return prev.filter((o) => o.id != id);

View File

@ -175,10 +175,10 @@ class Notifier {
this.modal.alert(title, text, onOkClick, okLabel);
}
confirm(title, text, onOkClick, onCancelClick, okLabel=gettext('Yes'), cancelLabel=gettext('No'), okIcon='default') {
confirm(title, text, onOkClick, onCancelClick, okLabel=gettext('Yes'), cancelLabel=gettext('No'), okIcon='default', modalId=null) {
/* Use this if you want to use pgAdmin global notifier.
Or else, if you want to use modal inside iframe only then use ModalProvider eg- query tool */
this.modal.confirm(title, text, onOkClick, onCancelClick, okLabel, cancelLabel, okIcon);
this.modal.confirm(title, text, onOkClick, onCancelClick, okLabel, cancelLabel, okIcon, modalId);
}
confirmDelete(title, text, onDeleteClick, onCancelClick, okLabel = gettext('Delete'), cancelLabel = gettext('Cancel')){

View File

@ -11,7 +11,6 @@ import React from 'react';
import gettext from 'sources/gettext';
import pgAdmin from 'sources/pgadmin';
import DebuggerArgumentComponent from './components/DebuggerArgumentComponent';
export default class FunctionArguments {
@ -28,6 +27,6 @@ export default class FunctionArguments {
// Render Debugger argument component
pgAdmin.Browser.notifier.showModal(gettext('Debugger'), (closeModal) => {
return <DebuggerArgumentComponent closeModal={closeModal} debuggerInfo={debugInfo} restartDebug={restartDebug} isEdbProc={isEdbProc} transId={transId} pgTreeInfo={treeInfo} pgData={d}></DebuggerArgumentComponent>;
}, { isFullScreen: false, isResizeable: true, showFullScreen: true, isFullWidth: true, dialogWidth: pgAdmin.Browser.stdW.md, dialogHeight: pgAdmin.Browser.stdH.md });
}, { isFullScreen: false, isResizeable: true, showFullScreen: true, isFullWidth: true, dialogWidth: pgAdmin.Browser.stdW.md, dialogHeight: pgAdmin.Browser.stdH.md, id:'id-debugger'});
}
}

View File

@ -378,7 +378,7 @@ export default class ERDTool extends React.Component {
bodyObj.onSaveDiagram(false, true);
}}
/>
));
), {id: 'id-erd-close-confirmation'});
return false;
} else {
this.forceClose();

View File

@ -100,6 +100,10 @@ export const CONNECTION_STATUS_MESSAGE = {
[CONNECTION_STATUS.TRANSACTION_STATUS_UNKNOWN]: gettext('The connection with the server is bad.')
};
export const MODAL_DIALOGS = {
QT_CONFIRMATIONS: 'id-qt-close-confirmation',
};
export const PANELS = {
QUERY: 'id-query',
MESSAGES: 'id-messages',

View File

@ -34,6 +34,7 @@ import CustomPropTypes from '../../../../../../static/js/custom_prop_types';
import ConfirmTransactionContent from '../dialogs/ConfirmTransactionContent';
import { LayoutDocker } from '../../../../../../static/js/helpers/Layout';
import CloseRunningDialog from '../dialogs/CloseRunningDialog';
import { MODAL_DIALOGS } from '../QueryToolConstants';
const StyledBox = styled(Box)(({theme}) => ({
padding: '2px 4px',
@ -56,6 +57,7 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros, onAddT
const eventBus = useContext(QueryToolEventsContext);
const queryToolCtx = useContext(QueryToolContext);
const queryToolConnCtx = useContext(QueryToolConnectionContext);
const modalId = MODAL_DIALOGS.QT_CONFIRMATIONS;
const [highlightFilter, setHighlightFilter] = useState(false);
const [limit, setLimit] = useState('-1');
@ -237,7 +239,7 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros, onAddT
eventBus.fireEvent(QUERY_TOOL_EVENTS.FORCE_CLOSE_PANEL);
}}
/>
));
), {id: modalId});
return;
} else {
eventBus.fireEvent(QUERY_TOOL_EVENTS.FORCE_CLOSE_PANEL);
@ -258,7 +260,7 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros, onAddT
onCommitClick();
}}
/>
));
), {id: modalId});
};
useEffect(()=>{
if(isInTxn()) {

View File

@ -24,6 +24,7 @@ import ConfirmExecuteQueryContent from '../dialogs/ConfirmExecuteQueryContent';
import usePreferences from '../../../../../../preferences/static/js/store';
import { getTitle } from '../../sqleditor_title';
import PropTypes from 'prop-types';
import { MODAL_DIALOGS } from '../QueryToolConstants';
async function registerAutocomplete(editor, api, transId) {
@ -65,6 +66,7 @@ export default function Query({onTextSelect, setQtStatePartial}) {
const pgAdmin = usePgAdmin();
const preferencesStore = usePreferences();
const queryToolPref = queryToolCtx.preferences.sqleditor;
const modalId = MODAL_DIALOGS.QT_CONFIRMATIONS;
const highlightError = (cmObj, {errormsg: result, data}, executeCursor)=>{
let errorLineNo = 0,
startMarker = 0,
@ -343,7 +345,7 @@ export default function Query({onTextSelect, setQtStatePartial}) {
eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_SAVE_FILE);
}}
/>
));
), {id:modalId});
};
const formatSQL = ()=>{

View File

@ -31,6 +31,7 @@ import { GraphVisualiser } from './GraphVisualiser';
import { usePgAdmin } from '../../../../../../static/js/PgAdminProvider';
import pgAdmin from 'sources/pgadmin';
import ConnectServerContent from '../../../../../../static/js/Dialogs/ConnectServerContent';
import { MODAL_DIALOGS } from '../QueryToolConstants';
const StyledBox = styled(Box)(({theme}) => ({
display: 'flex',
@ -832,6 +833,7 @@ export function ResultSet() {
const [selectedColumns, setSelectedColumns] = useState(new Set());
// NONE - no select, PAGE - show select all, ALL - select all.
const [allRowsSelect, setAllRowsSelect] = useState('NONE');
const modalId = MODAL_DIALOGS.QT_CONFIRMATIONS;
// We'll use this track if any changes were saved.
// It will help to decide whether results refresh is required or not on page change.
@ -1145,7 +1147,7 @@ export function ResultSet() {
eventBus.fireEvent(QUERY_TOOL_EVENTS.WARN_SAVE_TEXT_CLOSE);
}}
/>
));
), {modalId:modalId});
};
useEffect(()=>{
let isDirty = _.size(dataChangeStore.updated) || _.size(dataChangeStore.added) || _.size(dataChangeStore.deleted);

View File

@ -457,12 +457,11 @@ UserManagementDialog.propTypes = {
export function showUserManagement() {
const title = gettext('User Management');
pgAdmin.Browser.notifier.showModal(title, (onClose) => {
return <UserManagementDialog
onClose={()=>{onClose();}}
/>;
},
{ isFullScreen: false, isResizeable: true, showFullScreen: false, isFullWidth: true,
dialogWidth: pgAdmin.Browser.stdW.lg, dialogHeight: pgAdmin.Browser.stdH.md});
dialogWidth: pgAdmin.Browser.stdW.lg, dialogHeight: pgAdmin.Browser.stdH.md, id: 'id-user-management'});
}