From 95f367c837df63cde1b6167dee0f4e7031a77f91 Mon Sep 17 00:00:00 2001
From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com>
Date: Tue, 30 Jun 2020 18:06:59 -0400
Subject: [PATCH] Extract AlertManagerState from AlertManager (#2163)
---
.../js/src/admin/components/BasicsPage.js | 6 +-
.../core/js/src/admin/components/MailPage.js | 12 ++--
framework/core/js/src/common/Application.js | 29 +++++-----
.../js/src/common/components/AlertManager.js | 56 ++-----------------
.../core/js/src/common/components/Modal.js | 14 ++---
.../js/src/common/states/AlertManagerState.js | 50 +++++++++++++++++
.../src/forum/components/ChangeEmailModal.js | 2 +-
.../src/forum/components/EditPostComposer.js | 13 ++---
.../forum/components/ForgotPasswordModal.js | 2 +-
.../js/src/forum/components/LogInModal.js | 2 +-
.../js/src/forum/components/ReplyComposer.js | 13 ++---
.../core/js/src/forum/utils/UserControls.js | 11 ++--
12 files changed, 107 insertions(+), 103 deletions(-)
create mode 100644 framework/core/js/src/common/states/AlertManagerState.js
diff --git a/framework/core/js/src/admin/components/BasicsPage.js b/framework/core/js/src/admin/components/BasicsPage.js
index 497a0fddb..68f9ba142 100644
--- a/framework/core/js/src/admin/components/BasicsPage.js
+++ b/framework/core/js/src/admin/components/BasicsPage.js
@@ -2,7 +2,6 @@ import Page from '../../common/components/Page';
import FieldSet from '../../common/components/FieldSet';
import Select from '../../common/components/Select';
import Button from '../../common/components/Button';
-import Alert from '../../common/components/Alert';
import saveSettings from '../utils/saveSettings';
import ItemList from '../../common/utils/ItemList';
import Switch from '../../common/components/Switch';
@@ -186,7 +185,10 @@ export default class BasicsPage extends Page {
saveSettings(settings)
.then(() => {
- app.alerts.show((this.successAlert = new Alert({ type: 'success', children: app.translator.trans('core.admin.basics.saved_message') })));
+ this.successAlert = app.alerts.show({
+ type: 'success',
+ children: app.translator.trans('core.admin.basics.saved_message'),
+ });
})
.catch(() => {})
.then(() => {
diff --git a/framework/core/js/src/admin/components/MailPage.js b/framework/core/js/src/admin/components/MailPage.js
index cbec021b3..13ad25dfc 100644
--- a/framework/core/js/src/admin/components/MailPage.js
+++ b/framework/core/js/src/admin/components/MailPage.js
@@ -179,9 +179,10 @@ export default class MailPage extends Page {
})
.then((response) => {
this.sendingTest = false;
- app.alerts.show(
- (this.testEmailSuccessAlert = new Alert({ type: 'success', children: app.translator.trans('core.admin.email.send_test_mail_success') }))
- );
+ this.testEmailSuccessAlert = app.alerts.show({
+ type: 'success',
+ children: app.translator.trans('core.admin.email.send_test_mail_success'),
+ });
})
.catch((error) => {
this.sendingTest = false;
@@ -204,7 +205,10 @@ export default class MailPage extends Page {
saveSettings(settings)
.then(() => {
- app.alerts.show((this.successAlert = new Alert({ type: 'success', children: app.translator.trans('core.admin.basics.saved_message') })));
+ this.successAlert = app.alerts.show({
+ type: 'success',
+ children: app.translator.trans('core.admin.basics.saved_message'),
+ });
})
.catch(() => {})
.then(() => {
diff --git a/framework/core/js/src/common/Application.js b/framework/core/js/src/common/Application.js
index 72a88cdf7..77d08181d 100644
--- a/framework/core/js/src/common/Application.js
+++ b/framework/core/js/src/common/Application.js
@@ -1,5 +1,4 @@
import ItemList from './utils/ItemList';
-import Alert from './components/Alert';
import Button from './components/Button';
import ModalManager from './components/ModalManager';
import AlertManager from './components/AlertManager';
@@ -23,6 +22,7 @@ import Group from './models/Group';
import Notification from './models/Notification';
import { flattenDeep } from 'lodash-es';
import PageState from './states/PageState';
+import AlertManagerState from './states/AlertManagerState';
/**
* The `App` class provides a container for an application, as well as various
@@ -109,13 +109,13 @@ export default class Application {
booted = false;
/**
- * An Alert that was shown as a result of an AJAX request error. If present,
- * it will be dismissed on the next successful request.
+ * The key for an Alert that was shown as a result of an AJAX request error.
+ * If present, it will be dismissed on the next successful request.
*
- * @type {null|Alert}
+ * @type {int}
* @private
*/
- requestError = null;
+ requestErrorAlert = null;
/**
* The page the app is currently on.
@@ -139,6 +139,11 @@ export default class Application {
*/
previous = new PageState(null);
+ /*
+ * An object that manages the state of active alerts.
+ */
+ alerts = new AlertManagerState();
+
data;
title = '';
@@ -175,7 +180,7 @@ export default class Application {
mount(basePath = '') {
this.modal = m.mount(document.getElementById('modal'), );
- this.alerts = m.mount(document.getElementById('alerts'), );
+ m.mount(document.getElementById('alerts'), );
this.drawer = new Drawer();
@@ -313,7 +318,7 @@ export default class Application {
}
};
- if (this.requestError) this.alerts.dismiss(this.requestError.alert);
+ if (this.requestErrorAlert) this.alerts.dismiss(this.requestErrorAlert);
// Now make the request. If it's a failure, inspect the error that was
// returned and show an alert containing its contents.
@@ -322,8 +327,6 @@ export default class Application {
m.request(options).then(
(response) => deferred.resolve(response),
(error) => {
- this.requestError = error;
-
let children;
switch (error.status) {
@@ -357,7 +360,7 @@ export default class Application {
// the details property is decoded to transform escaped characters such as '\n'
const formattedError = error.response && Array.isArray(error.response.errors) && error.response.errors.map((e) => decodeURI(e.detail));
- error.alert = new Alert({
+ error.alert = {
type: 'error',
children,
controls: isDebug && [
@@ -365,7 +368,7 @@ export default class Application {
Debug
,
],
- });
+ };
try {
options.errorHandler(error);
@@ -381,7 +384,7 @@ export default class Application {
console.groupEnd();
}
- this.alerts.show(error.alert);
+ this.requestErrorAlert = this.alerts.show(error.alert);
}
deferred.reject(error);
@@ -397,7 +400,7 @@ export default class Application {
* @private
*/
showDebug(error, formattedError) {
- this.alerts.dismiss(this.requestError.alert);
+ this.alerts.dismiss(this.requestErrorAlert);
this.modal.show(new RequestErrorModal({ error, formattedError }));
}
diff --git a/framework/core/js/src/common/components/AlertManager.js b/framework/core/js/src/common/components/AlertManager.js
index 1592e57fc..614e01b57 100644
--- a/framework/core/js/src/common/components/AlertManager.js
+++ b/framework/core/js/src/common/components/AlertManager.js
@@ -7,20 +7,16 @@ import Alert from './Alert';
*/
export default class AlertManager extends Component {
init() {
- /**
- * An array of Alert components which are currently showing.
- *
- * @type {Alert[]}
- * @protected
- */
- this.components = [];
+ this.state = this.props.state;
}
view() {
return (
- {this.components.map((component) => (
-
{component}
+ {Object.entries(this.state.getActiveAlerts()).map(([key, alert]) => (
+
+ {(alert.componentClass || Alert).component({ ...alert.attrs, ondismiss: this.state.dismiss.bind(this.state, key) })}
+
))}
);
@@ -32,46 +28,4 @@ export default class AlertManager extends Component {
// to be retained across route changes.
context.retain = true;
}
-
- /**
- * Show an Alert in the alerts area.
- *
- * @param {Alert} component
- * @public
- */
- show(component) {
- if (!(component instanceof Alert)) {
- throw new Error('The AlertManager component can only show Alert components');
- }
-
- component.props.ondismiss = this.dismiss.bind(this, component);
-
- this.components.push(component);
- m.redraw();
- }
-
- /**
- * Dismiss an alert.
- *
- * @param {Alert} component
- * @public
- */
- dismiss(component) {
- const index = this.components.indexOf(component);
-
- if (index !== -1) {
- this.components.splice(index, 1);
- m.redraw();
- }
- }
-
- /**
- * Clear all alerts.
- *
- * @public
- */
- clear() {
- this.components = [];
- m.redraw();
- }
}
diff --git a/framework/core/js/src/common/components/Modal.js b/framework/core/js/src/common/components/Modal.js
index 872bb1b6a..86cef3031 100644
--- a/framework/core/js/src/common/components/Modal.js
+++ b/framework/core/js/src/common/components/Modal.js
@@ -11,16 +11,16 @@ import Button from './Button';
export default class Modal extends Component {
init() {
/**
- * An alert component to show below the header.
+ * Attributes for an alert component to show below the header.
*
- * @type {Alert}
+ * @type {object}
*/
- this.alert = null;
+ this.alertAttrs = null;
}
view() {
- if (this.alert) {
- this.alert.props.dismissible = false;
+ if (this.alertAttrs) {
+ this.alertAttrs.dismissible = false;
}
return (
@@ -43,7 +43,7 @@ export default class Modal extends Component {
{this.title()}
- {alert ? {this.alert}
: ''}
+ {this.alertAttrs ? {Alert.component(this.alertAttrs)}
: ''}
{this.content()}
@@ -123,7 +123,7 @@ export default class Modal extends Component {
* @param {RequestError} error
*/
onerror(error) {
- this.alert = error.alert;
+ this.alertAttrs = error.alert;
m.redraw();
diff --git a/framework/core/js/src/common/states/AlertManagerState.js b/framework/core/js/src/common/states/AlertManagerState.js
new file mode 100644
index 000000000..6dd2a0f29
--- /dev/null
+++ b/framework/core/js/src/common/states/AlertManagerState.js
@@ -0,0 +1,50 @@
+import Alert from '../components/Alert';
+
+export default class AlertManagerState {
+ constructor() {
+ this.activeAlerts = {};
+ this.alertId = 0;
+ }
+
+ getActiveAlerts() {
+ return this.activeAlerts;
+ }
+
+ /**
+ * Show an Alert in the alerts area.
+ */
+ show(attrs, componentClass = Alert) {
+ // Breaking Change Compliance Warning, Remove in Beta 15.
+ // This is applied to the first argument (attrs) because previously, the alert was passed as the first argument.
+ if (attrs === Alert || attrs instanceof Alert) {
+ // This is duplicated so that if the error is caught, an error message still shows up in the debug console.
+ console.error('The AlertManager can only show Alerts. Whichever extension triggered this alert should be updated to comply with beta 14.');
+ throw new Error('The AlertManager can only show Alerts. Whichever extension triggered this alert should be updated to comply with beta 14.');
+ }
+ // End Change Compliance Warning, Remove in Beta 15
+ this.activeAlerts[++this.alertId] = { attrs, componentClass };
+ m.redraw();
+
+ return this.alertId;
+ }
+
+ /**
+ * Dismiss an alert.
+ */
+ dismiss(key) {
+ if (!key || !(key in this.activeAlerts)) return;
+
+ delete this.activeAlerts[key];
+ m.redraw();
+ }
+
+ /**
+ * Clear all alerts.
+ *
+ * @public
+ */
+ clear() {
+ this.activeAlerts = {};
+ m.redraw();
+ }
+}
diff --git a/framework/core/js/src/forum/components/ChangeEmailModal.js b/framework/core/js/src/forum/components/ChangeEmailModal.js
index ce1b1592a..ff45dbede 100644
--- a/framework/core/js/src/forum/components/ChangeEmailModal.js
+++ b/framework/core/js/src/forum/components/ChangeEmailModal.js
@@ -122,7 +122,7 @@ export default class ChangeEmailModal extends Modal {
onerror(error) {
if (error.status === 401) {
- error.alert.props.children = app.translator.trans('core.forum.change_email.incorrect_password_message');
+ error.alert.children = app.translator.trans('core.forum.change_email.incorrect_password_message');
}
super.onerror(error);
diff --git a/framework/core/js/src/forum/components/EditPostComposer.js b/framework/core/js/src/forum/components/EditPostComposer.js
index 86b9703e5..258e8ecc3 100644
--- a/framework/core/js/src/forum/components/EditPostComposer.js
+++ b/framework/core/js/src/forum/components/EditPostComposer.js
@@ -1,5 +1,4 @@
import ComposerBody from './ComposerBody';
-import Alert from '../../common/components/Alert';
import Button from '../../common/components/Button';
import icon from '../../common/helpers/icon';
@@ -101,13 +100,11 @@ export default class EditPostComposer extends ComposerBody {
app.alerts.dismiss(alert);
},
});
- app.alerts.show(
- (alert = new Alert({
- type: 'success',
- children: app.translator.trans('core.forum.composer_edit.edited_message'),
- controls: [viewButton],
- }))
- );
+ alert = app.alerts.show({
+ type: 'success',
+ children: app.translator.trans('core.forum.composer_edit.edited_message'),
+ controls: [viewButton],
+ });
}
app.composer.hide();
diff --git a/framework/core/js/src/forum/components/ForgotPasswordModal.js b/framework/core/js/src/forum/components/ForgotPasswordModal.js
index 4b443e92e..7ccdcfd1a 100644
--- a/framework/core/js/src/forum/components/ForgotPasswordModal.js
+++ b/framework/core/js/src/forum/components/ForgotPasswordModal.js
@@ -104,7 +104,7 @@ export default class ForgotPasswordModal extends Modal {
onerror(error) {
if (error.status === 404) {
- error.alert.props.children = app.translator.trans('core.forum.forgot_password.not_found_message');
+ error.alert.children = app.translator.trans('core.forum.forgot_password.not_found_message');
}
super.onerror(error);
diff --git a/framework/core/js/src/forum/components/LogInModal.js b/framework/core/js/src/forum/components/LogInModal.js
index a6d87230e..cd029f30d 100644
--- a/framework/core/js/src/forum/components/LogInModal.js
+++ b/framework/core/js/src/forum/components/LogInModal.js
@@ -179,7 +179,7 @@ export default class LogInModal extends Modal {
onerror(error) {
if (error.status === 401) {
- error.alert.props.children = app.translator.trans('core.forum.log_in.invalid_login_message');
+ error.alert.children = app.translator.trans('core.forum.log_in.invalid_login_message');
}
super.onerror(error);
diff --git a/framework/core/js/src/forum/components/ReplyComposer.js b/framework/core/js/src/forum/components/ReplyComposer.js
index fd6de3c18..af942d186 100644
--- a/framework/core/js/src/forum/components/ReplyComposer.js
+++ b/framework/core/js/src/forum/components/ReplyComposer.js
@@ -1,5 +1,4 @@
import ComposerBody from './ComposerBody';
-import Alert from '../../common/components/Alert';
import Button from '../../common/components/Button';
import icon from '../../common/helpers/icon';
import extractText from '../../common/utils/extractText';
@@ -104,13 +103,11 @@ export default class ReplyComposer extends ComposerBody {
app.alerts.dismiss(alert);
},
});
- app.alerts.show(
- (alert = new Alert({
- type: 'success',
- children: app.translator.trans('core.forum.composer_reply.posted_message'),
- controls: [viewButton],
- }))
- );
+ alert = app.alerts.show({
+ type: 'success',
+ children: app.translator.trans('core.forum.composer_reply.posted_message'),
+ controls: [viewButton],
+ });
}
app.composer.hide();
diff --git a/framework/core/js/src/forum/utils/UserControls.js b/framework/core/js/src/forum/utils/UserControls.js
index ae1863213..7271f1fc5 100644
--- a/framework/core/js/src/forum/utils/UserControls.js
+++ b/framework/core/js/src/forum/utils/UserControls.js
@@ -1,4 +1,3 @@
-import Alert from '../../common/components/Alert';
import Button from '../../common/components/Button';
import Separator from '../../common/components/Separator';
import EditUserModal from '../components/EditUserModal';
@@ -134,12 +133,10 @@ export default {
error: 'core.forum.user_controls.delete_error_message',
}[type];
- app.alerts.show(
- new Alert({
- type,
- children: app.translator.trans(message, { username, email }),
- })
- );
+ app.alerts.show({
+ type,
+ children: app.translator.trans(message, { username, email }),
+ });
},
/**