package org.ovirt.engine.ui.common.widget; import java.util.ArrayList; import java.util.List; import org.gwtbootstrap3.client.ui.constants.Placement; import org.ovirt.engine.ui.common.CommonApplicationConstants; import org.ovirt.engine.ui.common.CommonApplicationMessages; import org.ovirt.engine.ui.common.gin.AssetProvider; import org.ovirt.engine.ui.common.utils.ElementUtils; import org.ovirt.engine.ui.common.widget.panel.AlertPanel; import org.ovirt.engine.ui.common.widget.panel.AlertPanel.Type; import org.ovirt.engine.ui.uicompat.external.StringUtils; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.Element; import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeHtmlBuilder; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.RootPanel; /** * Handles display of application-wide alert messages to the user via PatternFly * based {@link AlertPanel} widget. * <p> * Includes functionality to {@linkplain #setCanShowAlerts disable showing alerts}, * which queues up any alert messages sent while showing alerts is disabled. Queued * up messages will be replayed to the user once showing alerts is re-enabled. * <p> * If a message is longer than a single line in the alert box, its text will be * truncated and an ellipsis will show. On hover, a tooltip will show the full text * of the alert. * * @see AlertPanel */ public class AlertManager { private static final String ALERT_PANEL_CLASS = "ovirt-alert-main"; //$NON-NLS-1$ private static final String ALERT_PANEL_DANGER_CLASS = "ovirt-alert-main-danger"; //$NON-NLS-1$ private static final String ALERT_PANEL_WARNING_CLASS = "ovirt-alert-main-warning"; //$NON-NLS-1$ private static final String ALERT_PANEL_SUCCESS_CLASS = "ovirt-alert-main-success"; //$NON-NLS-1$ private static final String ALERT_PANEL_INFO_CLASS = "ovirt-alert-main-info"; //$NON-NLS-1$ private static final String ALERT_MESSAGE_CLASS = "ovirt-alert-main-message"; //$NON-NLS-1$ private static final String BOOTSTRAP_CENTER_CLASS = "center-block"; //$NON-NLS-1$ private static final String RELOAD_LINK = "javascript:window.location.reload(true)"; //$NON-NLS-1$ private static final CommonApplicationConstants CONSTANTS = AssetProvider.getConstants(); private static final CommonApplicationMessages MESSAGES = AssetProvider.getMessages(); private final List<ScheduledCommand> deferredAlertCommands = new ArrayList<>(); private boolean canShowAlerts; // TODO(vs) why are we holding reference to a specific instance? private AlertPanel alert; public AlertManager() { setCanShowAlerts(false); } /** * Controls whether alerts can be shown (<code>canShowAlerts=true</code>) * or queued up to be shown later on (<code>canShowAlerts=false</code>). */ public void setCanShowAlerts(boolean canShowAlerts) { this.canShowAlerts = canShowAlerts; if (canShowAlerts) { // Replay deferred alerts for (ScheduledCommand command : deferredAlertCommands) { command.execute(); } deferredAlertCommands.clear(); } } /** * Informs the user about an uncaught exception. */ public void showUncaughtExceptionAlert(Throwable t) { SafeHtmlBuilder alertMessage = new SafeHtmlBuilder() .appendHtmlConstant(MESSAGES.uncaughtExceptionAlertMessage(RELOAD_LINK)); String errorDetails = t.getMessage(); if (!StringUtils.isEmpty(errorDetails)) { alertMessage.appendEscaped(CONSTANTS.space()); alertMessage.appendEscaped(MESSAGES.uncaughtExceptionAlertMessageDetails(errorDetails)); //new line alertMessage.appendHtmlConstant("<br />"); //$NON-NLS-1$ alertMessage.appendEscaped(CONSTANTS.checkUiLogs()); } showAlert(Type.DANGER, alertMessage.toSafeHtml()); } /** * Displays an application-wide alert message. * <p> * If showing alerts is disabled, the alert will be queued up and replayed * once showing alerts is re-enabled. * * @see #setCanShowAlerts */ public void showAlert(final Type type, final SafeHtml message) { showAlert(type, message, 0); } public void showAlert(final Type type, final SafeHtml message, final int autoHideMs) { ScheduledCommand command = () -> { if (alert == null) { alert = createAlert(type, message); attachAlert(alert); } else { alert.incCount(); updateAlert(type, message, alert); } if (autoHideMs > 0) { final Timer timer = new Timer() { @Override public void run() { detachAlert(alert); } }; alert.getWidget().addCloseHandler(evt -> { timer.cancel(); alert = null; }); timer.schedule(autoHideMs); } else { alert.getWidget().addCloseHandler(evt -> alert = null); } }; if (canShowAlerts) { command.execute(); } else { deferredAlertCommands.add(command); } } void attachAlert(AlertPanel alertPanel) { // Add widget's DOM element straight into HTML body RootPanel.get().add(alertPanel); // Use tool tip in case the textual content overflows Element messageDivElement = alertPanel.getMessageAt(0).getElement(); if (ElementUtils.detectHorizontalOverflow(messageDivElement)) { alertPanel.getAlertTooltip().setText(messageDivElement.getInnerText()); alertPanel.getAlertTooltip().setPlacement(Placement.BOTTOM); } } void detachAlert(AlertPanel alertPanel) { RootPanel.get().remove(alertPanel); } AlertPanel createAlert(Type type, SafeHtml message) { AlertPanel alertPanel = new AlertPanel(); updateAlert(type, message, alertPanel); return alertPanel; } private void updateAlert(Type type, SafeHtml message, AlertPanel alertPanel) { alertPanel.clearMessages(); alertPanel.setType(type); alertPanel.setWidgetColumnSize(null); alertPanel.getWidget().setDismissable(true); alertPanel.addStyleName(ALERT_PANEL_CLASS); alertPanel.addStyleName(BOOTSTRAP_CENTER_CLASS); switch (type) { case DANGER: alertPanel.addStyleName(ALERT_PANEL_DANGER_CLASS); break; case WARNING: alertPanel.addStyleName(ALERT_PANEL_WARNING_CLASS); break; case SUCCESS: alertPanel.addStyleName(ALERT_PANEL_SUCCESS_CLASS); break; case INFO: alertPanel.addStyleName(ALERT_PANEL_INFO_CLASS); break; } alertPanel.addMessage(message, ALERT_MESSAGE_CLASS); } }