///////////////////////////////////////////////////////////// // // pgAdmin 4 - PostgreSQL Tools // // Copyright (C) 2013 - 2024, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // ////////////////////////////////////////////////////////////// import { SnackbarProvider, SnackbarContent } from 'notistack'; import { styled } from '@mui/material/styles'; import {Box} from '@mui/material'; import CloseIcon from '@mui/icons-material/CloseRounded'; import { DefaultButton, PrimaryButton } from '../components/Buttons'; import HTMLReactParser from 'html-react-parser'; import CheckRoundedIcon from '@mui/icons-material/CheckRounded'; import PropTypes from 'prop-types'; import React, { useEffect } from 'react'; import { NotifierMessage, MESSAGE_TYPE } from '../components/FormComponents'; import CustomPropTypes from '../custom_prop_types'; import gettext from 'sources/gettext'; import _ from 'lodash'; import { useModal } from './ModalProvider'; import { parseApiError } from '../api_instance'; const Root = styled('div')(({theme}) => ({ '& .Notifier-footer': { display: 'flex', justifyContent: 'flex-end', padding: '0.5rem', ...theme.mixins.panelBorder.top, '& .Notifier-margin': { marginLeft: '0.25rem', } }, })); const AUTO_HIDE_DURATION = 3000; // In milliseconds export const FinalNotifyContent = React.forwardRef(({children}, ref) => { return {children}; }); FinalNotifyContent.displayName = 'FinalNotifyContent'; FinalNotifyContent.propTypes = { children: CustomPropTypes.children, }; function AlertContent({text, confirm, okLabel=gettext('OK'), cancelLabel=gettext('Cancel'), onOkClick, onCancelClick}) { return ( {HTMLReactParser(text)} {confirm && } onClick={onCancelClick} >{cancelLabel} } } onClick={onOkClick} autoFocus={true} >{okLabel} ); } AlertContent.propTypes = { text: PropTypes.string, confirm: PropTypes.bool, onOkClick: PropTypes.func, onCancelClick: PropTypes.func, okLabel: PropTypes.string, cancelLabel: PropTypes.string, }; // This can be called from iframe, // so need to separate the context to avoid hooks error class SnackbarNotifier { constructor(snackbar) { this.snackbarObj = snackbar; } notify(content, autoHideDuration) { if (content) { let options = {autoHideDuration, content:(key) => ( {React.cloneElement(content, {onClose:()=>{this.snackbarObj.closeSnackbar(key);}})} )}; options.content.displayName = 'content'; this.snackbarObj.enqueueSnackbar(options); } } callNotify(msg, type, autoHideDuration) { this.notify( , autoHideDuration ); } } class Notifier { constructor(modal, snackbar) { this.modal = modal; this.snackbar = snackbar; } success(msg, autoHideDuration = AUTO_HIDE_DURATION) { this.snackbar.callNotify(msg, MESSAGE_TYPE.SUCCESS, autoHideDuration); } warning(msg, autoHideDuration = AUTO_HIDE_DURATION) { this.snackbar.callNotify(msg, MESSAGE_TYPE.WARNING, autoHideDuration); } info(msg, autoHideDuration = AUTO_HIDE_DURATION) { this.snackbar.callNotify(msg, MESSAGE_TYPE.INFO, autoHideDuration); } error(msg, autoHideDuration = AUTO_HIDE_DURATION) { this.snackbar.callNotify(msg, MESSAGE_TYPE.ERROR, autoHideDuration); } // proxy notify(...args) { this.snackbar.notify(...args); } pgRespErrorNotify(error, prefixMsg='') { if (error.response?.status === 410) { this.alert( gettext('Error: Object not found - %s.', error.response.statusText), parseApiError(error) ); } else { this.error(prefixMsg + ' ' + parseApiError(error)); } } pgNotifier(type, error, promptmsg, onJSONResult) { let msg; if(!error.response) { msg = parseApiError(error); promptmsg = gettext('Connection Lost'); } else if(error.response.headers['content-type'] == 'application/json') { let resp = error.response.data; if(resp.info == 'CRYPTKEY_MISSING') { let pgBrowser = window.pgAdmin.Browser; pgBrowser.set_master_password('', ()=> { if(onJSONResult && typeof(onJSONResult) == 'function') { onJSONResult('CRYPTKEY_SET'); } }, ()=> { if(onJSONResult && typeof(onJSONResult) == 'function') { onJSONResult('CRYPTKEY_NOT_SET'); } }); return; } else if (resp.result != null && (!resp.errormsg || resp.errormsg == '') && onJSONResult && typeof(onJSONResult) == 'function') { return onJSONResult(resp.result); } msg = _.escape(resp.result) || _.escape(resp.errormsg) || 'Unknown error'; } else { if (type === 'error') { this.alert('Error', promptmsg); } return; } if(type == 'error-noalert' && onJSONResult && typeof(onJSONResult) == 'function') { return onJSONResult(); } this.alert(promptmsg, msg.replace(new RegExp(/\r?\n/, 'g'), '
')); onJSONResult?.('ALERT_CALLED'); } alert(title, text, onOkClick, okLabel=gettext('OK')) { /* 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.alert(title, text, onOkClick, okLabel); } confirm(title, text, onOkClick, onCancelClick, okLabel=gettext('Yes'), cancelLabel=gettext('No'), extras=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, extras); } showModal(title, content, modalOptions) { this.modal.showModal(title, content, modalOptions); } } export function NotifierProvider({ pgAdmin, pgWindow, getInstance, children, onReady }) { const modal = useModal(); useEffect(()=>{ // if opened in an iframe then use top pgAdmin // pgAdmin itself can open in iframe, skip this in that case. if(window.self != window.top && pgWindow != window ) { pgAdmin.Browser.notifier = new Notifier(modal, pgWindow.pgAdmin.Browser.notifier.snackbar); onReady?.(); getInstance?.(pgAdmin.Browser.notifier); } }, []); // if open in a window, then create your own Snackbar // if pgAdmin is opened inside an iframe then it also same as new window. if(window.self == window.top || (window.self != window.top && pgWindow == window )) { return ( { pgAdmin.Browser.notifier = new Notifier(modal, new SnackbarNotifier(obj)); getInstance?.(pgAdmin.Browser.notifier); onReady?.(); }} disableWindowBlurListener={true} > {children} ); } return ( ( {children} ) ); } NotifierProvider.propTypes = { pgAdmin: PropTypes.object, pgWindow: PropTypes.object, getInstance: PropTypes.func, children: CustomPropTypes.children, onReady: PropTypes.func, }; export default Notifier;