codekingpro/portable-devtools
114k
1/////////////////////////////////////////////////////////////2//3// pgAdmin 4 - PostgreSQL Tools4//5// Copyright (C) 2013 - 2024, The pgAdmin Development Team6// This software is released under the PostgreSQL Licence7//8//////////////////////////////////////////////////////////////9 10import { SnackbarProvider, SnackbarContent } from 'notistack';11import { makeStyles } from '@mui/styles';12import {Box} from '@mui/material';13import CloseIcon from '@mui/icons-material/CloseRounded';14import { DefaultButton, PrimaryButton } from '../components/Buttons';15import HTMLReactParser from 'html-react-parser';16import CheckRoundedIcon from '@mui/icons-material/CheckRounded';17import PropTypes from 'prop-types';18import React, { useEffect } from 'react';19import { NotifierMessage, MESSAGE_TYPE } from '../components/FormComponents';20import CustomPropTypes from '../custom_prop_types';21import gettext from 'sources/gettext';22import _ from 'lodash';23import { useModal } from './ModalProvider';24import { parseApiError } from '../api_instance';25 26const AUTO_HIDE_DURATION = 3000; // In milliseconds27 28export const FinalNotifyContent = React.forwardRef(({children}, ref) => {29 return <SnackbarContent style= {{ justifyContent: 'end', maxWidth: '700px' }} ref={ref}>{children}</SnackbarContent>;30});31FinalNotifyContent.displayName = 'FinalNotifyContent';32FinalNotifyContent.propTypes = {33 children: CustomPropTypes.children,34};35 36const useModalStyles = makeStyles((theme)=>({37 footer: {38 display: 'flex',39 justifyContent: 'flex-end',40 padding: '0.5rem',41 ...theme.mixins.panelBorder.top,42 },43 margin: {44 marginLeft: '0.25rem',45 },46}));47function AlertContent({text, confirm, okLabel=gettext('OK'), cancelLabel=gettext('Cancel'), onOkClick, onCancelClick}) {48 const classes = useModalStyles();49 return (50 <Box display="flex" flexDirection="column" height="100%">51 <Box flexGrow="1" p={2}>{HTMLReactParser(text)}</Box>52 <Box className={classes.footer}>53 {confirm &&54 <DefaultButton startIcon={<CloseIcon />} onClick={onCancelClick} >{cancelLabel}</DefaultButton>55 }56 <PrimaryButton className={classes.margin} startIcon={<CheckRoundedIcon />} onClick={onOkClick} autoFocus={true} >{okLabel}</PrimaryButton>57 </Box>58 </Box>59 );60}61AlertContent.propTypes = {62 text: PropTypes.string,63 confirm: PropTypes.bool,64 onOkClick: PropTypes.func,65 onCancelClick: PropTypes.func,66 okLabel: PropTypes.string,67 cancelLabel: PropTypes.string,68};69 70// This can be called from iframe,71// so need to separate the context to avoid hooks error72class SnackbarNotifier {73 constructor(snackbar) {74 this.snackbarObj = snackbar;75 }76 77 notify(content, autoHideDuration) {78 if (content) {79 let options = {autoHideDuration, content:(key) => (80 <FinalNotifyContent>{React.cloneElement(content, {onClose:()=>{this.snackbarObj.closeSnackbar(key);}})}</FinalNotifyContent>81 )};82 options.content.displayName = 'content';83 this.snackbarObj.enqueueSnackbar(null, options);84 }85 }86 87 callNotify(msg, type, autoHideDuration) {88 this.notify(89 <NotifierMessage style={{maxWidth: '50vw'}} type={type} message={msg} closable={true} />,90 autoHideDuration91 );92 }93}94 95class Notifier {96 constructor(modal, snackbar) {97 this.modal = modal;98 this.snackbar = snackbar;99 }100 101 success(msg, autoHideDuration = AUTO_HIDE_DURATION) {102 this.snackbar.callNotify(msg, MESSAGE_TYPE.SUCCESS, autoHideDuration);103 }104 105 warning(msg, autoHideDuration = AUTO_HIDE_DURATION) {106 this.snackbar.callNotify(msg, MESSAGE_TYPE.WARNING, autoHideDuration);107 }108 109 info(msg, autoHideDuration = AUTO_HIDE_DURATION) {110 this.snackbar.callNotify(msg, MESSAGE_TYPE.INFO, autoHideDuration);111 }112 113 error(msg, autoHideDuration = AUTO_HIDE_DURATION) {114 this.snackbar.callNotify(msg, MESSAGE_TYPE.ERROR, autoHideDuration);115 }116 117 // proxy118 notify(...args) {119 this.snackbar.notify(...args);120 }121 122 pgRespErrorNotify(error, prefixMsg='') {123 if (error.response?.status === 410) {124 this.alert(gettext('Error: Object not found - %s.', error.response.statusText), parseApiError(error));125 } else {126 this.error(prefixMsg + ' ' + parseApiError(error));127 }128 }129 130 pgNotifier(type, error, promptmsg, onJSONResult) {131 let msg;132 133 if(!error.response) {134 msg = parseApiError(error);135 promptmsg = gettext('Connection Lost');136 } else if(error.response.headers['content-type'] == 'application/json') {137 let resp = error.response.data;138 if(resp.info == 'CRYPTKEY_MISSING') {139 let pgBrowser = window.pgAdmin.Browser;140 pgBrowser.set_master_password('', ()=> {141 if(onJSONResult && typeof(onJSONResult) == 'function') {142 onJSONResult('CRYPTKEY_SET');143 }144 }, ()=> {145 if(onJSONResult && typeof(onJSONResult) == 'function') {146 onJSONResult('CRYPTKEY_NOT_SET');147 }148 });149 return;150 } else if (resp.result != null && (!resp.errormsg || resp.errormsg == '') &&151 onJSONResult && typeof(onJSONResult) == 'function') {152 return onJSONResult(resp.result);153 }154 msg = _.escape(resp.result) || _.escape(resp.errormsg) || 'Unknown error';155 } else {156 if (type === 'error') {157 this.alert('Error', promptmsg);158 }159 return;160 }161 if(type == 'error-noalert' && onJSONResult && typeof(onJSONResult) == 'function') {162 return onJSONResult();163 }164 this.alert(promptmsg, msg.replace(new RegExp(/\r?\n/, 'g'), '<br />'));165 onJSONResult('ALERT_CALLED');166 }167 168 alert(title, text, onOkClick, okLabel=gettext('OK')) {169 /* Use this if you want to use pgAdmin global notifier.170 Or else, if you want to use modal inside iframe only then use ModalProvider eg- query tool */171 this.modal.alert(title, text, onOkClick, okLabel);172 }173 174 confirm(title, text, onOkClick, onCancelClick, okLabel=gettext('Yes'), cancelLabel=gettext('No')) {175 /* Use this if you want to use pgAdmin global notifier.176 Or else, if you want to use modal inside iframe only then use ModalProvider eg- query tool */177 this.modal.confirm(title, text, onOkClick, onCancelClick, okLabel, cancelLabel);178 }179 180 showModal(title, content, modalOptions) {181 this.modal.showModal(title, content, modalOptions);182 }183}184 185export function NotifierProvider({ pgAdmin, pgWindow, getInstance, children, onReady }) {186 const modal = useModal();187 188 useEffect(()=>{189 // if open in an iframe then use top pgAdmin190 if(window.self != window.top) {191 pgAdmin.Browser.notifier = new Notifier(modal, pgWindow.pgAdmin.Browser.notifier.snackbar);192 onReady?.();193 getInstance?.(pgAdmin.Browser.notifier);194 }195 }, []);196 197 // if open in a window, then create your own Snackbar198 if(window.self == window.top) {199 return (200 <SnackbarProvider201 maxSnack={30}202 anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}203 ref={(obj)=>{204 pgAdmin.Browser.notifier = new Notifier(modal, new SnackbarNotifier(obj));205 getInstance?.(pgAdmin.Browser.notifier);206 onReady?.();207 }}208 >209 {children}210 </SnackbarProvider>211 );212 }213 return (214 <>215 {children}216 </>217 );218}219 220NotifierProvider.propTypes = {221 pgAdmin: PropTypes.object,222 pgWindow: PropTypes.object,223 getInstance: PropTypes.func,224 children: CustomPropTypes.children,225 onReady: PropTypes.func,226};227 228export default Notifier;229 