diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainController.java index 996a8e164d..997c686439 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainController.java @@ -20,6 +20,7 @@ import bisq.application.ApplicationService; import bisq.bisq_easy.NavigationTarget; import bisq.bonded_roles.security_manager.alert.AlertService; +import bisq.bonded_roles.security_manager.alert.AlertType; import bisq.bonded_roles.security_manager.alert.AuthorizedAlertData; import bisq.common.observable.collection.CollectionObserver; import bisq.desktop.ServiceProvider; @@ -28,6 +29,7 @@ import bisq.desktop.common.view.Navigation; import bisq.desktop.common.view.NavigationController; import bisq.desktop.components.overlay.Popup; +import bisq.desktop.main.alert.AlertBannerController; import bisq.desktop.main.content.ContentController; import bisq.desktop.main.left.LeftNavController; import bisq.desktop.main.notification.NotificationPanelController; @@ -53,6 +55,7 @@ public class MainController extends NavigationController { private final SettingsService settingsService; private final UpdaterService updaterService; private final ApplicationService.Config config; + private final AlertBannerController alertBannerController; public MainController(ServiceProvider serviceProvider) { super(NavigationTarget.MAIN); @@ -66,11 +69,13 @@ public MainController(ServiceProvider serviceProvider) { leftNavController = new LeftNavController(serviceProvider); TopPanelController topPanelController = new TopPanelController(serviceProvider); NotificationPanelController notificationPanelController = new NotificationPanelController(serviceProvider); + alertBannerController = new AlertBannerController(); view = new MainView(model, this, leftNavController.getView().getRoot(), topPanelController.getView().getRoot(), - notificationPanelController.getView().getRoot()); + notificationPanelController.getView().getRoot(), + alertBannerController.getView().getRoot()); } @Override @@ -105,18 +110,12 @@ public void add(AuthorizedAlertData authorizedAlertData) { } settingsService.getConsumedAlertIds().add(authorizedAlertData.getId()); Optional optionalMessage = authorizedAlertData.getMessage(); - switch (authorizedAlertData.getAlertType()) { - case INFO: - optionalMessage.ifPresentOrElse(message -> new Popup().attention(message).show(), - () -> log.warn("optionalMessage not present")); - break; - case WARN: - case EMERGENCY: - optionalMessage.ifPresentOrElse(message -> new Popup().warning(message).show(), - () -> log.warn("optionalMessage not present")); - break; - case BAN: - break; + + if (optionalMessage.isPresent()) { + log.info("Showing alert with message {}", optionalMessage.get()); + alertBannerController.showAlert(optionalMessage.get(), authorizedAlertData.getAlertType()); + } else { + log.warn("optionalMessage not present"); } }); } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainView.java index 125f1099a9..adbe2827f7 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainView.java @@ -34,12 +34,13 @@ public MainView(MainModel model, MainController controller, AnchorPane leftNav, HBox topPanel, - BorderPane notificationPanel) { + BorderPane notificationPanel, + BorderPane alertBanner) { super(new HBox(), model, controller); anchorPane = new AnchorPane(); VBox.setVgrow(anchorPane, Priority.ALWAYS); - VBox vBox = new VBox(topPanel, notificationPanel, anchorPane); + VBox vBox = new VBox(topPanel, notificationPanel, alertBanner, anchorPane); vBox.setFillWidth(true); HBox.setHgrow(vBox, Priority.ALWAYS); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java new file mode 100644 index 0000000000..169f214abc --- /dev/null +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java @@ -0,0 +1,53 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.desktop.main.alert; + +import bisq.bonded_roles.security_manager.alert.AlertType; +import bisq.desktop.common.view.Controller; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class AlertBannerController implements Controller { + private final AlertBannerModel model; + @Getter + private final AlertBannerView view; + + public AlertBannerController() { + model = new AlertBannerModel(); + view = new AlertBannerView(model, this); + } + + @Override + public void onActivate() { + } + + @Override + public void onDeactivate() { + } + + public void showAlert(String message, AlertType alertType) { + model.getMessage().set(message); + model.getAlertType().set(alertType); + model.getIsAlertVisible().set(true); + } + + void onClose() { + model.getIsAlertVisible().set(false); + } +} diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java new file mode 100644 index 0000000000..f01312828a --- /dev/null +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java @@ -0,0 +1,33 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.desktop.main.alert; + +import bisq.bonded_roles.security_manager.alert.AlertType; +import bisq.desktop.common.view.Model; +import javafx.beans.property.*; +import lombok.Getter; + +@Getter +public class AlertBannerModel implements Model { + private final BooleanProperty isAlertVisible = new SimpleBooleanProperty(); + private final StringProperty message = new SimpleStringProperty(); + private final ObjectProperty alertType = new SimpleObjectProperty<>(); + + public AlertBannerModel() { + } +} diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerView.java new file mode 100644 index 0000000000..9835a8055b --- /dev/null +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerView.java @@ -0,0 +1,133 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.desktop.main.alert; + +import bisq.bonded_roles.security_manager.alert.AlertType; +import bisq.desktop.common.Transitions; +import bisq.desktop.common.view.View; +import bisq.desktop.components.containers.Spacer; +import bisq.desktop.components.controls.BisqIconButton; +import javafx.animation.Timeline; +import javafx.beans.value.ChangeListener; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.layout.*; +import lombok.extern.slf4j.Slf4j; +import org.fxmisc.easybind.EasyBind; +import org.fxmisc.easybind.Subscription; + +@Slf4j +public class AlertBannerView extends View { + public static final int DURATION = Transitions.DEFAULT_DURATION / 2; + + Label content = new Label(); + private final Button closeButton; + private final HBox banner; + private final ChangeListener contentHeightListener; + private Timeline slideInRightTimeline, slideOutTopTimeline; + private Subscription isVisiblePin, alertTypePin; + + public AlertBannerView(AlertBannerModel model, AlertBannerController controller) { + super(new BorderPane(), model, controller); + + root.setManaged(false); + root.setVisible(false); + + content.setWrapText(true); + + closeButton = BisqIconButton.createIconButton("close-white"); + + banner = new HBox(content, Spacer.fillHBox(), closeButton); + banner.setAlignment(Pos.TOP_CENTER); + banner.setPadding(new Insets(10, 10, 10, 15)); + + contentHeightListener = ((observable, oldValue, newValue) -> banner.setMinHeight(newValue.doubleValue() + 25)); // padding = 25 + + root.setCenter(banner); + root.setPadding(new Insets(20, 40, 20, 40)); + } + + @Override + protected void onViewAttached() { + content.textProperty().bind(model.getMessage()); + + isVisiblePin = EasyBind.subscribe(model.getIsAlertVisible(), isVisible -> { + if (slideInRightTimeline != null) { + slideInRightTimeline.stop(); + slideInRightTimeline = null; + root.setTranslateX(0); + root.setOpacity(1); + } + if (slideOutTopTimeline != null) { + slideOutTopTimeline.stop(); + slideOutTopTimeline = null; + root.setTranslateY(0); + root.setOpacity(0); + root.setManaged(false); + root.setVisible(false); + } + if (isVisible) { + root.setManaged(true); + root.setVisible(true); + root.setTranslateY(0); + slideInRightTimeline = Transitions.slideInRight(root, DURATION, () -> { + }); + } else { + root.setTranslateX(0); + root.setTranslateY(0); + slideOutTopTimeline = Transitions.slideAndFadeOutTop(root, DURATION, () -> { + root.setManaged(false); + root.setVisible(false); + }); + } + }); + + alertTypePin = EasyBind.subscribe(model.getAlertType(), this::addAlertTypeStyleClass); + + content.heightProperty().addListener(contentHeightListener); + + closeButton.setOnAction(e -> controller.onClose()); + } + + @Override + protected void onViewDetached() { + content.textProperty().unbind(); + + isVisiblePin.unsubscribe(); + alertTypePin.unsubscribe(); + + content.heightProperty().removeListener(contentHeightListener); + + closeButton.setOnAction(null); + } + + private void addAlertTypeStyleClass(AlertType alertType) { + banner.getStyleClass().clear(); + String alertTypeClass; + if (alertType == AlertType.INFO) { + alertTypeClass = "info-banner"; + } else if (alertType == AlertType.WARN) { + alertTypeClass = "warn-banner"; + } else { + alertTypeClass = "emergency-banner"; + } + banner.getStyleClass().addAll("alert-banner", alertTypeClass); + } +} diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java index 0b9fbfe046..d9d2473fdb 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java @@ -508,7 +508,6 @@ private void updateSelectedMarketFilter(Filters.Markets marketFilter) { .map(menuItem -> (DropdownFilterMenuItem) menuItem) .forEach(menuItem -> menuItem.updateSelection(marketFilter == menuItem.getFilter())); - favouritesTableView.getSelectionModel().select(getModel().getSelectedMarketChannelItem().get()); marketsTableView.getSelectionModel().select(getModel().getSelectedMarketChannelItem().get()); } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/notification/NotificationPanelView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/notification/NotificationPanelView.java index 8c6245ae21..c5602a5f28 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/notification/NotificationPanelView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/notification/NotificationPanelView.java @@ -36,6 +36,7 @@ @Slf4j public class NotificationPanelView extends View { public static final int DURATION = Transitions.DEFAULT_DURATION / 2; + private final Label notificationHeadline; private final Button closeButton; private final Hyperlink hyperlink; diff --git a/apps/desktop/desktop/src/main/resources/css/application.css b/apps/desktop/desktop/src/main/resources/css/application.css index 2e3b8d2db4..06089c4dce 100644 --- a/apps/desktop/desktop/src/main/resources/css/application.css +++ b/apps/desktop/desktop/src/main/resources/css/application.css @@ -116,9 +116,39 @@ -fx-background-color: -bisq-dark-grey-20; } +/******************************************************************************* + * AlertBanner * + ******************************************************************************/ + +.alert-banner { + -fx-fill: -bisq-dark-grey-50; + -fx-text-fill: -bisq-dark-grey-50; + -fx-font-size: 1.15em; + -fx-font-family: "IBM Plex Sans"; +} + +.info-banner, +.warn-banner, +.emergency-banner { + -fx-background-radius: 8; + -fx-border-color: transparent; +} + +.info-banner { + -fx-background-color: -bisq-mid-grey-10; +} + +.warn-banner { + -fx-background-color: -bisq2-yellow-dim-50; +} + +.emergency-banner { + -fx-background-color: -bisq2-red-dim-50; +} + /******************************************************************************* - * NotificationPane * + * NotificationPane * ******************************************************************************/ .notification-pane {