mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-04-20 23:56:30 -04:00
Add Notifications
This commit is contained in:
parent
6bf2adae7f
commit
2049e384dc
@ -136,12 +136,6 @@
|
||||
<version>8.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>eu.hansolo.enzo</groupId>
|
||||
<artifactId>Enzo</artifactId>
|
||||
<version>0.1.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- <dependency>
|
||||
<groupId>org.fxmisc.richtext</groupId>
|
||||
<artifactId>richtextfx</artifactId>
|
||||
|
@ -1,544 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013 by Gerrit Grunwald
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package eu.hansolo.enzo.notification;
|
||||
|
||||
import io.bitsquare.common.UserThread;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.KeyValue;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ObjectPropertyBase;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.event.EventType;
|
||||
import javafx.event.WeakEventHandler;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.stage.Screen;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.StageStyle;
|
||||
import javafx.util.Duration;
|
||||
import org.controlsfx.control.PopOver;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
|
||||
/**
|
||||
* A copy of the original {@link eu.hansolo.enzo.notification.Notification} class at revision eb1d321, containing
|
||||
* several changes that were otherwise not possible through subclassing or other customization via the existing
|
||||
* Notification API. See git history for this file for exact details as to what has been changed. All other
|
||||
* {@code eu.hansolo.enzo.*} types are loaded from the enzo jar (see build.gradle for details).
|
||||
*/
|
||||
public class Notification {
|
||||
private static final Image INFO_ICON = new Image(Notifier.class.getResourceAsStream("info.png"));
|
||||
private static final Image WARNING_ICON = new Image(Notifier.class.getResourceAsStream("warning.png"));
|
||||
private static final Image SUCCESS_ICON = new Image(Notifier.class.getResourceAsStream("success.png"));
|
||||
private static final Image ERROR_ICON = new Image(Notifier.class.getResourceAsStream("error.png"));
|
||||
private final String TITLE;
|
||||
private final String MESSAGE;
|
||||
private final Image IMAGE;
|
||||
|
||||
|
||||
// ******************** Constructors **************************************
|
||||
public Notification(final String TITLE, final String MESSAGE) {
|
||||
this(TITLE, MESSAGE, null);
|
||||
}
|
||||
|
||||
public Notification(final String MESSAGE, final Image IMAGE) {
|
||||
this("", MESSAGE, IMAGE);
|
||||
}
|
||||
|
||||
private Notification(final String TITLE, final String MESSAGE, final Image IMAGE) {
|
||||
this.TITLE = TITLE;
|
||||
this.MESSAGE = MESSAGE;
|
||||
this.IMAGE = IMAGE;
|
||||
}
|
||||
|
||||
|
||||
// ******************** Inner Classes *************************************
|
||||
public enum Notifier {
|
||||
INSTANCE;
|
||||
|
||||
private static final double ICON_WIDTH = 24;
|
||||
private static final double ICON_HEIGHT = 24;
|
||||
private static double width = 321;
|
||||
private static double height = 49;
|
||||
private static double offsetX = 0;
|
||||
private static double offsetY = 2;
|
||||
private static double spacingY = 5;
|
||||
private static Pos popupLocation = Pos.TOP_RIGHT;
|
||||
private static Stage stageRef = null;
|
||||
private Duration popupLifetime;
|
||||
private Duration popupAnimationTime;
|
||||
private Stage stage;
|
||||
private Scene scene;
|
||||
private ObservableList<PopOver> popups;
|
||||
|
||||
|
||||
// ******************** Constructor ***************************************
|
||||
Notifier() {
|
||||
init();
|
||||
initGraphics();
|
||||
}
|
||||
|
||||
|
||||
// ******************** Initialization ************************************
|
||||
private void init() {
|
||||
popupLifetime = Duration.millis(5000);
|
||||
popupAnimationTime = Duration.millis(500);
|
||||
popups = FXCollections.observableArrayList();
|
||||
}
|
||||
|
||||
private void initGraphics() {
|
||||
scene = new Scene(new Region());
|
||||
scene.setFill(null);
|
||||
scene.getStylesheets().setAll(
|
||||
getClass().getResource("/io/bitsquare/gui/bitsquare.css").toExternalForm(),
|
||||
getClass().getResource("/io/bitsquare/gui/images.css").toExternalForm());
|
||||
|
||||
stage = new Stage();
|
||||
stage.initStyle(StageStyle.TRANSPARENT);
|
||||
}
|
||||
|
||||
|
||||
// ******************** Methods *******************************************
|
||||
|
||||
/**
|
||||
* @param STAGE_REF The Notification will be positioned relative to the given Stage.<br>
|
||||
* If null then the Notification will be positioned relative to the primary Screen.
|
||||
* @param POPUP_LOCATION The default is TOP_RIGHT of primary Screen.
|
||||
*/
|
||||
public static void setPopupLocation(final Stage STAGE_REF, final Pos POPUP_LOCATION) {
|
||||
if (null != STAGE_REF) {
|
||||
INSTANCE.stage.initOwner(STAGE_REF);
|
||||
Notifier.stageRef = STAGE_REF;
|
||||
}
|
||||
Notifier.popupLocation = POPUP_LOCATION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Notification's owner stage so that when the owner
|
||||
* stage is closed Notifications will be shut down as well.<br>
|
||||
* This is only needed if <code>setPopupLocation</code> is called
|
||||
* <u>without</u> a stage reference.
|
||||
*
|
||||
* @param OWNER
|
||||
*/
|
||||
public static void setNotificationOwner(final Stage OWNER) {
|
||||
INSTANCE.stage.initOwner(OWNER);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param OFFSET_X The horizontal shift required.
|
||||
* <br> The default is 0 px.
|
||||
*/
|
||||
public static void setOffsetX(final double OFFSET_X) {
|
||||
Notifier.offsetX = OFFSET_X;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param OFFSET_Y The vertical shift required.
|
||||
* <br> The default is 25 px.
|
||||
*/
|
||||
public static void setOffsetY(final double OFFSET_Y) {
|
||||
Notifier.offsetY = OFFSET_Y;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WIDTH The default is 300 px.
|
||||
*/
|
||||
public static void setWidth(final double WIDTH) {
|
||||
Notifier.width = WIDTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param HEIGHT The default is 80 px.
|
||||
*/
|
||||
public static void setHeight(final double HEIGHT) {
|
||||
Notifier.height = HEIGHT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SPACING_Y The spacing between multiple Notifications.
|
||||
* <br> The default is 5 px.
|
||||
*/
|
||||
public static void setSpacingY(final double SPACING_Y) {
|
||||
Notifier.spacingY = SPACING_Y;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
popups.clear();
|
||||
stage.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Duration that the notification will stay on screen before it
|
||||
* will fade out. The default is 5000 ms
|
||||
*
|
||||
* @return the Duration the popup notification will stay on screen
|
||||
*/
|
||||
public Duration getPopupLifetime() {
|
||||
return popupLifetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the Duration that the popup notification will stay on screen before it
|
||||
* will fade out. The parameter is limited to values between 2 and 20 seconds.
|
||||
*
|
||||
* @param POPUP_LIFETIME
|
||||
*/
|
||||
public void setPopupLifetime(final Duration POPUP_LIFETIME) {
|
||||
popupLifetime = Duration.millis(clamp(2000, 20000, POPUP_LIFETIME.toMillis()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Duration that it takes to fade out the notification
|
||||
* The parameter is limited to values between 0 and 1000 ms
|
||||
*
|
||||
* @return the Duration that it takes to fade out the notification
|
||||
*/
|
||||
public Duration getPopupAnimationTime() {
|
||||
return popupAnimationTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the Duration that it takes to fade out the notification
|
||||
* The parameter is limited to values between 0 and 1000 ms
|
||||
* Default value is 500 ms
|
||||
*
|
||||
* @param POPUP_ANIMATION_TIME
|
||||
*/
|
||||
public void setPopupAnimationTime(final Duration POPUP_ANIMATION_TIME) {
|
||||
popupAnimationTime = Duration.millis(clamp(0, 1000, POPUP_ANIMATION_TIME.toMillis()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the given Notification on the screen
|
||||
*
|
||||
* @param NOTIFICATION
|
||||
*/
|
||||
public void notify(final Notification NOTIFICATION) {
|
||||
preOrder();
|
||||
showPopup(NOTIFICATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a Notification with the given parameters on the screen
|
||||
*
|
||||
* @param TITLE
|
||||
* @param MESSAGE
|
||||
* @param IMAGE
|
||||
*/
|
||||
public void notify(final String TITLE, final String MESSAGE, final Image IMAGE) {
|
||||
notify(new Notification(TITLE, MESSAGE, IMAGE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a Notification with the given title and message and an Info icon
|
||||
*
|
||||
* @param TITLE
|
||||
* @param MESSAGE
|
||||
*/
|
||||
public void notifyInfo(final String TITLE, final String MESSAGE) {
|
||||
notify(new Notification(TITLE, MESSAGE, Notification.INFO_ICON));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a Notification with the given title and message and a Warning icon
|
||||
*
|
||||
* @param TITLE
|
||||
* @param MESSAGE
|
||||
*/
|
||||
public void notifyWarning(final String TITLE, final String MESSAGE) {
|
||||
notify(new Notification(TITLE, MESSAGE, Notification.WARNING_ICON));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a Notification with the given title and message and a Checkmark icon
|
||||
*
|
||||
* @param TITLE
|
||||
* @param MESSAGE
|
||||
*/
|
||||
public void notifySuccess(final String TITLE, final String MESSAGE) {
|
||||
notify(new Notification(TITLE, MESSAGE, Notification.SUCCESS_ICON));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a Notification with the given title and message and an Error icon
|
||||
*
|
||||
* @param TITLE
|
||||
* @param MESSAGE
|
||||
*/
|
||||
public void notifyError(final String TITLE, final String MESSAGE) {
|
||||
notify(new Notification(TITLE, MESSAGE, Notification.ERROR_ICON));
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that the given VALUE is within the range of MIN to MAX
|
||||
*
|
||||
* @param MIN
|
||||
* @param MAX
|
||||
* @param VALUE
|
||||
* @return
|
||||
*/
|
||||
private double clamp(final double MIN, final double MAX, final double VALUE) {
|
||||
if (VALUE < MIN) return MIN;
|
||||
if (VALUE > MAX) return MAX;
|
||||
return VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorder the popup Notifications on screen so that the latest Notification will stay on top
|
||||
*/
|
||||
private void preOrder() {
|
||||
if (popups.isEmpty()) return;
|
||||
IntStream.range(0, popups.size()).parallel().forEachOrdered(
|
||||
i -> UserThread.execute(() -> {
|
||||
switch (popupLocation) {
|
||||
case TOP_LEFT:
|
||||
case TOP_CENTER:
|
||||
case TOP_RIGHT:
|
||||
popups.get(i).setY(popups.get(i).getY() + height + spacingY);
|
||||
break;
|
||||
|
||||
case BOTTOM_LEFT:
|
||||
case BOTTOM_CENTER:
|
||||
case BOTTOM_RIGHT:
|
||||
popups.get(i).setY(popups.get(i).getY() - height - spacingY);
|
||||
break;
|
||||
|
||||
default:
|
||||
popups.get(i).setY(popups.get(i).getY() - height - spacingY);
|
||||
break;
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and shows a popup with the data from the given Notification object
|
||||
*
|
||||
* @param NOTIFICATION
|
||||
*/
|
||||
private void showPopup(final Notification NOTIFICATION) {
|
||||
ImageView icon = new ImageView(
|
||||
new Image(Notifier.class.getResourceAsStream("/images/notification_logo.png")));
|
||||
icon.relocate(10, 7);
|
||||
|
||||
Label title = new Label(NOTIFICATION.TITLE);
|
||||
title.setStyle(" -fx-text-fill:#333333; -fx-font-size:12; -fx-font-weight:bold;");
|
||||
title.relocate(60, 6);
|
||||
|
||||
Label message = new Label(NOTIFICATION.MESSAGE);
|
||||
message.relocate(60, 25);
|
||||
message.setStyle(" -fx-text-fill:#333333; -fx-font-size:11; ");
|
||||
|
||||
Pane popupLayout = new Pane();
|
||||
popupLayout.setPrefSize(width, height);
|
||||
popupLayout.getChildren().addAll(icon, title, message);
|
||||
|
||||
PopOver popOver = new PopOver(popupLayout);
|
||||
popOver.setDetachable(false);
|
||||
popOver.setArrowSize(0);
|
||||
popOver.setX(getX());
|
||||
popOver.setY(getY());
|
||||
popOver.addEventHandler(MouseEvent.MOUSE_PRESSED, new WeakEventHandler<>(event ->
|
||||
fireNotificationEvent(new NotificationEvent(NOTIFICATION, Notifier.this, popOver,
|
||||
NotificationEvent.NOTIFICATION_PRESSED))
|
||||
));
|
||||
popups.add(popOver);
|
||||
|
||||
// Add a timeline for popup fade out
|
||||
KeyValue fadeOutBegin = new KeyValue(popOver.opacityProperty(), 1.0);
|
||||
KeyValue fadeOutEnd = new KeyValue(popOver.opacityProperty(), 0.0);
|
||||
|
||||
KeyFrame kfBegin = new KeyFrame(Duration.ZERO, fadeOutBegin);
|
||||
KeyFrame kfEnd = new KeyFrame(popupAnimationTime, fadeOutEnd);
|
||||
|
||||
Timeline timeline = new Timeline(kfBegin, kfEnd);
|
||||
timeline.setDelay(popupLifetime);
|
||||
timeline.setOnFinished(actionEvent -> UserThread.execute(() -> {
|
||||
popOver.hide();
|
||||
popups.remove(popOver);
|
||||
fireNotificationEvent(new NotificationEvent(NOTIFICATION, Notifier.this, popOver,
|
||||
NotificationEvent.HIDE_NOTIFICATION));
|
||||
}));
|
||||
|
||||
if (stage.isShowing()) {
|
||||
stage.toFront();
|
||||
} else {
|
||||
stage.show();
|
||||
}
|
||||
|
||||
popOver.show(stage);
|
||||
fireNotificationEvent(new NotificationEvent(NOTIFICATION, Notifier.this, popOver,
|
||||
NotificationEvent.SHOW_NOTIFICATION));
|
||||
timeline.play();
|
||||
}
|
||||
|
||||
private double getX() {
|
||||
if (null == stageRef) return calcX(0.0, Screen.getPrimary().getBounds().getWidth());
|
||||
|
||||
return calcX(stageRef.getX(), stageRef.getWidth());
|
||||
}
|
||||
|
||||
private double getY() {
|
||||
if (null == stageRef) return calcY(0.0, Screen.getPrimary().getBounds().getHeight());
|
||||
return calcY(stageRef.getY(), stageRef.getHeight());
|
||||
}
|
||||
|
||||
private double calcX(final double LEFT, final double TOTAL_WIDTH) {
|
||||
switch (popupLocation) {
|
||||
case TOP_LEFT:
|
||||
case CENTER_LEFT:
|
||||
case BOTTOM_LEFT:
|
||||
return LEFT + offsetX;
|
||||
case TOP_CENTER:
|
||||
case CENTER:
|
||||
case BOTTOM_CENTER:
|
||||
return LEFT + (TOTAL_WIDTH - width) * 0.5 - offsetX;
|
||||
case TOP_RIGHT:
|
||||
case CENTER_RIGHT:
|
||||
case BOTTOM_RIGHT:
|
||||
return LEFT + TOTAL_WIDTH - width - offsetX;
|
||||
default:
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
private double calcY(final double TOP, final double TOTAL_HEIGHT) {
|
||||
switch (popupLocation) {
|
||||
case TOP_LEFT:
|
||||
case TOP_CENTER:
|
||||
case TOP_RIGHT:
|
||||
return TOP + offsetY;
|
||||
case CENTER_LEFT:
|
||||
case CENTER:
|
||||
case CENTER_RIGHT:
|
||||
return TOP + (TOTAL_HEIGHT - height) / 2 - offsetY;
|
||||
case BOTTOM_LEFT:
|
||||
case BOTTOM_CENTER:
|
||||
case BOTTOM_RIGHT:
|
||||
return TOP + TOTAL_HEIGHT - height - offsetY;
|
||||
default:
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ******************** Event handling ********************************
|
||||
public final ObjectProperty<EventHandler<NotificationEvent>> onNotificationPressedProperty() {
|
||||
return onNotificationPressed;
|
||||
}
|
||||
|
||||
public final void setOnNotificationPressed(EventHandler<NotificationEvent> value) {
|
||||
onNotificationPressedProperty().set(value);
|
||||
}
|
||||
|
||||
public final EventHandler<NotificationEvent> getOnNotificationPressed() {
|
||||
return onNotificationPressedProperty().get();
|
||||
}
|
||||
|
||||
private final ObjectProperty<EventHandler<NotificationEvent>> onNotificationPressed = new
|
||||
ObjectPropertyBase<EventHandler<NotificationEvent>>() {
|
||||
@Override
|
||||
public Object getBean() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "onNotificationPressed";
|
||||
}
|
||||
};
|
||||
|
||||
public final ObjectProperty<EventHandler<NotificationEvent>> onShowNotificationProperty() {
|
||||
return onShowNotification;
|
||||
}
|
||||
|
||||
public final void setOnShowNotification(EventHandler<NotificationEvent> value) {
|
||||
onShowNotificationProperty().set(value);
|
||||
}
|
||||
|
||||
public final EventHandler<NotificationEvent> getOnShowNotification() {
|
||||
return onShowNotificationProperty().get();
|
||||
}
|
||||
|
||||
private final ObjectProperty<EventHandler<NotificationEvent>> onShowNotification = new
|
||||
ObjectPropertyBase<EventHandler<NotificationEvent>>() {
|
||||
@Override
|
||||
public Object getBean() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "onShowNotification";
|
||||
}
|
||||
};
|
||||
|
||||
public final ObjectProperty<EventHandler<NotificationEvent>> onHideNotificationProperty() {
|
||||
return onHideNotification;
|
||||
}
|
||||
|
||||
public final void setOnHideNotification(EventHandler<NotificationEvent> value) {
|
||||
onHideNotificationProperty().set(value);
|
||||
}
|
||||
|
||||
public final EventHandler<NotificationEvent> getOnHideNotification() {
|
||||
return onHideNotificationProperty().get();
|
||||
}
|
||||
|
||||
private final ObjectProperty<EventHandler<NotificationEvent>> onHideNotification = new
|
||||
ObjectPropertyBase<EventHandler<NotificationEvent>>() {
|
||||
@Override
|
||||
public Object getBean() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "onHideNotification";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public void fireNotificationEvent(final NotificationEvent EVENT) {
|
||||
final EventType TYPE = EVENT.getEventType();
|
||||
final EventHandler<NotificationEvent> HANDLER;
|
||||
if (NotificationEvent.NOTIFICATION_PRESSED == TYPE) {
|
||||
HANDLER = getOnNotificationPressed();
|
||||
} else if (NotificationEvent.SHOW_NOTIFICATION == TYPE) {
|
||||
HANDLER = getOnShowNotification();
|
||||
} else if (NotificationEvent.HIDE_NOTIFICATION == TYPE) {
|
||||
HANDLER = getOnHideNotification();
|
||||
} else {
|
||||
HANDLER = null;
|
||||
}
|
||||
if (null == HANDLER) return;
|
||||
HANDLER.handle(EVENT);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -840,10 +840,27 @@ textfield */
|
||||
-fx-text-fill: #333;
|
||||
}
|
||||
|
||||
#popup-message {
|
||||
#popup-bg {
|
||||
-fx-font-size: 15;
|
||||
-fx-text-fill: #333;
|
||||
-fx-background-color: white;
|
||||
-fx-background-radius: 5 5 5 5;
|
||||
-fx-background-insets: 10;
|
||||
-fx-effect: dropshadow(gaussian, #999, 10, 0, 0, 0);
|
||||
}
|
||||
|
||||
#popup-button {
|
||||
-fx-font-size: 15;
|
||||
}
|
||||
#notification-popup-headline {
|
||||
-fx-font-size: 12;
|
||||
-fx-font-weight: bold;
|
||||
-fx-text-fill: #3c3c3c;
|
||||
}
|
||||
|
||||
#notification-popup-bg {
|
||||
-fx-font-size: 11;
|
||||
-fx-text-fill: #3c3c3c;
|
||||
-fx-background-color: linear-gradient(to bottom, #fcfcfc, #e5e5e5);
|
||||
-fx-background-radius: 5 5 5 5;
|
||||
-fx-background-insets: 10;
|
||||
-fx-effect: dropshadow(gaussian, #434343, 10, 0, 0, 0);
|
||||
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare 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.
|
||||
*
|
||||
* Bitsquare 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 Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.gui.components;
|
||||
|
||||
import eu.hansolo.enzo.notification.Notification;
|
||||
import eu.hansolo.enzo.notification.NotificationBuilder;
|
||||
import eu.hansolo.enzo.notification.NotifierBuilder;
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
|
||||
/**
|
||||
* Not sure if we stick with the eu.hansolo.enzo.notification.Notification implementation, so keep it behind a service
|
||||
*/
|
||||
public class SystemNotification {
|
||||
private static final Notification.Notifier notifier = NotifierBuilder.create().build();
|
||||
|
||||
public static void openInfoNotification(String title, String message) {
|
||||
// On windows it causes problems with the hidden stage used in the hansolo Notification implementation
|
||||
// Lets deactivate it for the moment and fix that with a more native-like or real native solution later.
|
||||
// Lets deactivate it for Linux as well, as it is not much tested yet
|
||||
if (Utilities.isOSX())
|
||||
notifier.notify(NotificationBuilder.create().title(title).message(message).build());
|
||||
}
|
||||
}
|
@ -176,11 +176,6 @@ public class MainViewModel implements ViewModel {
|
||||
TxIdTextField.setWalletService(walletService);
|
||||
BalanceTextField.setWalletService(walletService);
|
||||
BalanceWithConfirmationTextField.setWalletService(walletService);
|
||||
|
||||
if (BitsquareApp.DEV_MODE) {
|
||||
preferences.setUseAnimations(false);
|
||||
preferences.setUseEffects(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,49 +1,17 @@
|
||||
package io.bitsquare.gui.main.intructions;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import io.bitsquare.gui.main.notifications.Notification;
|
||||
import io.bitsquare.trade.TradeManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
public class InstructionCenter {
|
||||
private final Logger log = LoggerFactory.getLogger(InstructionCenter.class);
|
||||
private Queue<io.bitsquare.gui.main.notifications.Notification> notifications = new LinkedBlockingQueue<>(3);
|
||||
private io.bitsquare.gui.main.notifications.Notification displayedNotification;
|
||||
private TradeManager tradeManager;
|
||||
|
||||
private final TradeManager tradeManager;
|
||||
|
||||
@Inject
|
||||
public InstructionCenter(TradeManager tradeManager) {
|
||||
this.tradeManager = tradeManager;
|
||||
}
|
||||
|
||||
void queueForDisplay(io.bitsquare.gui.main.notifications.Notification notification) {
|
||||
boolean result = notifications.offer(notification);
|
||||
if (!result)
|
||||
log.warn("The capacity is full with popups in the queue.\n\t" +
|
||||
"Not added new notification=" + notification);
|
||||
displayNext();
|
||||
}
|
||||
|
||||
void isHidden(Notification notification) {
|
||||
if (displayedNotification == null || displayedNotification == notification) {
|
||||
displayedNotification = null;
|
||||
displayNext();
|
||||
} else {
|
||||
log.warn("We got a isHidden called with a wrong notification.\n\t" +
|
||||
"notification (argument)=" + notification + "\n\tdisplayedPopup=" + displayedNotification);
|
||||
}
|
||||
}
|
||||
|
||||
private void displayNext() {
|
||||
if (displayedNotification == null) {
|
||||
if (!notifications.isEmpty()) {
|
||||
displayedNotification = notifications.poll();
|
||||
displayedNotification.display();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,18 @@
|
||||
package io.bitsquare.gui.main.notifications;
|
||||
|
||||
import io.bitsquare.gui.main.popups.Popup;
|
||||
import javafx.animation.Interpolator;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.KeyValue;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.Camera;
|
||||
import javafx.scene.PerspectiveCamera;
|
||||
import javafx.scene.transform.Rotate;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Window;
|
||||
import javafx.util.Duration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -9,6 +21,7 @@ public class Notification extends Popup {
|
||||
private boolean hasBeenDisplayed;
|
||||
|
||||
public Notification() {
|
||||
width = 320;
|
||||
NotificationCenter.add(this);
|
||||
}
|
||||
|
||||
@ -28,15 +41,138 @@ public class Notification extends Popup {
|
||||
return (Notification) super.message(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addSeparator() {
|
||||
// dont show a separator
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
super.show();
|
||||
hasBeenDisplayed = true;
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
super.hide();
|
||||
@Override
|
||||
public void display() {
|
||||
super.display();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void animateHide(Runnable onFinishedHandler) {
|
||||
if (NotificationCenter.useAnimations) {
|
||||
double duration = 400;
|
||||
Interpolator interpolator = Interpolator.SPLINE(0.25, 0.1, 0.25, 1);
|
||||
|
||||
gridPane.setRotationAxis(Rotate.X_AXIS);
|
||||
Camera camera = gridPane.getScene().getCamera();
|
||||
gridPane.getScene().setCamera(new PerspectiveCamera());
|
||||
|
||||
Timeline timeline = new Timeline();
|
||||
ObservableList<KeyFrame> keyFrames = timeline.getKeyFrames();
|
||||
keyFrames.add(new KeyFrame(Duration.millis(0),
|
||||
new KeyValue(gridPane.rotateProperty(), 0, interpolator),
|
||||
new KeyValue(gridPane.opacityProperty(), 1, interpolator)
|
||||
));
|
||||
keyFrames.add(new KeyFrame(Duration.millis(duration),
|
||||
new KeyValue(gridPane.rotateProperty(), -90, interpolator),
|
||||
new KeyValue(gridPane.opacityProperty(), 0, interpolator)
|
||||
));
|
||||
timeline.setOnFinished(event -> {
|
||||
gridPane.setRotate(0);
|
||||
gridPane.setRotationAxis(Rotate.Z_AXIS);
|
||||
gridPane.getScene().setCamera(camera);
|
||||
onFinishedHandler.run();
|
||||
});
|
||||
timeline.play();
|
||||
} else {
|
||||
onFinishedHandler.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void animateDisplay() {
|
||||
if (NotificationCenter.useAnimations) {
|
||||
double startX = 320;
|
||||
double duration = 600;
|
||||
Interpolator interpolator = Interpolator.SPLINE(0.25, 0.1, 0.25, 1);
|
||||
|
||||
Timeline timeline = new Timeline();
|
||||
ObservableList<KeyFrame> keyFrames = timeline.getKeyFrames();
|
||||
keyFrames.add(new KeyFrame(Duration.millis(0),
|
||||
new KeyValue(gridPane.opacityProperty(), 0, interpolator),
|
||||
new KeyValue(gridPane.translateXProperty(), startX, interpolator)
|
||||
));
|
||||
//bouncing
|
||||
/* keyFrames.add(new KeyFrame(Duration.millis(duration * 0.6),
|
||||
new KeyValue(gridPane.opacityProperty(), 1, interpolator),
|
||||
new KeyValue(gridPane.translateXProperty(), -12, interpolator)
|
||||
));
|
||||
keyFrames.add(new KeyFrame(Duration.millis(duration * 0.8),
|
||||
new KeyValue(gridPane.opacityProperty(), 1, interpolator),
|
||||
new KeyValue(gridPane.translateXProperty(), 4, interpolator)
|
||||
));*/
|
||||
keyFrames.add(new KeyFrame(Duration.millis(duration),
|
||||
new KeyValue(gridPane.opacityProperty(), 1, interpolator),
|
||||
new KeyValue(gridPane.translateXProperty(), 0, interpolator)
|
||||
));
|
||||
|
||||
timeline.play();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createGridPane() {
|
||||
super.createGridPane();
|
||||
gridPane.setPadding(new Insets(20, 20, 20, 20));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addCloseButton() {
|
||||
buttonDistance = 10;
|
||||
super.addCloseButton();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyStyles() {
|
||||
gridPane.setId("notification-popup-bg");
|
||||
if (headLineLabel != null)
|
||||
headLineLabel.setId("notification-popup-headline");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setModality() {
|
||||
stage.initModality(Modality.NONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void layout() {
|
||||
Window window = owner.getScene().getWindow();
|
||||
double titleBarHeight = window.getHeight() - owner.getScene().getHeight();
|
||||
stage.setX(Math.round(window.getX() + window.getWidth() - stage.getWidth()));
|
||||
stage.setY(Math.round(window.getY() + titleBarHeight));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addEffectToBackground() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeEffectFromBackground() {
|
||||
}
|
||||
|
||||
/* @Override
|
||||
protected void addCloseButton() {
|
||||
closeButton = new Button("Close");
|
||||
closeButton.setOnAction(event -> {
|
||||
hide();
|
||||
closeHandlerOptional.ifPresent(closeHandler -> closeHandler.run());
|
||||
});
|
||||
GridPane.setHalignment(closeButton, HPos.RIGHT);
|
||||
GridPane.setRowIndex(closeButton, ++rowIndex);
|
||||
GridPane.setColumnIndex(closeButton, 1);
|
||||
gridPane.getChildren().add(closeButton);
|
||||
}*/
|
||||
|
||||
public boolean isHasBeenDisplayed() {
|
||||
return hasBeenDisplayed;
|
||||
}
|
||||
|
@ -3,18 +3,26 @@ package io.bitsquare.gui.main.notifications;
|
||||
import com.google.inject.Inject;
|
||||
import io.bitsquare.app.Log;
|
||||
import io.bitsquare.arbitration.DisputeManager;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.gui.Navigation;
|
||||
import io.bitsquare.gui.main.MainView;
|
||||
import io.bitsquare.gui.main.portfolio.PortfolioView;
|
||||
import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesView;
|
||||
import io.bitsquare.trade.Trade;
|
||||
import io.bitsquare.trade.TradeManager;
|
||||
import io.bitsquare.user.Preferences;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import org.fxmisc.easybind.Subscription;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class NotificationCenter {
|
||||
private static final Logger log = LoggerFactory.getLogger(NotificationCenter.class);
|
||||
@ -25,11 +33,14 @@ public class NotificationCenter {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private final static List<Notification> notifications = new ArrayList<>();
|
||||
private Consumer<String> selectItemByTradeIdConsumer;
|
||||
|
||||
static void add(Notification notification) {
|
||||
notifications.add(notification);
|
||||
}
|
||||
|
||||
static boolean useAnimations;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Instance fields
|
||||
@ -37,9 +48,12 @@ public class NotificationCenter {
|
||||
|
||||
private TradeManager tradeManager;
|
||||
private DisputeManager disputeManager;
|
||||
private Preferences preferences;
|
||||
private Navigation navigation;
|
||||
|
||||
private final Map<String, Subscription> disputeStateSubscriptionsMap = new HashMap<>();
|
||||
private final Map<String, Subscription> tradeStateSubscriptionsMap = new HashMap<>();
|
||||
@Nullable
|
||||
private String selectedTradeId;
|
||||
|
||||
|
||||
@ -48,16 +62,18 @@ public class NotificationCenter {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public NotificationCenter(TradeManager tradeManager, DisputeManager disputeManager) {
|
||||
public NotificationCenter(TradeManager tradeManager, DisputeManager disputeManager, Preferences preferences, Navigation navigation) {
|
||||
this.tradeManager = tradeManager;
|
||||
this.disputeManager = disputeManager;
|
||||
this.preferences = preferences;
|
||||
this.navigation = navigation;
|
||||
|
||||
EasyBind.subscribe(preferences.useAnimationsProperty(), useAnimations -> NotificationCenter.useAnimations = useAnimations);
|
||||
}
|
||||
|
||||
public void onAllServicesInitialized() {
|
||||
tradeManager.getTrades().addListener((ListChangeListener<Trade>) change -> {
|
||||
change.next();
|
||||
log.error("change getRemoved " + change.getRemoved());
|
||||
log.error("change getAddedSubList " + change.getAddedSubList());
|
||||
if (change.wasRemoved()) {
|
||||
change.getRemoved().stream().forEach(trade -> {
|
||||
String tradeId = trade.getId();
|
||||
@ -112,10 +128,14 @@ public class NotificationCenter {
|
||||
return selectedTradeId;
|
||||
}
|
||||
|
||||
public void setSelectedTradeId(String selectedTradeId) {
|
||||
public void setSelectedTradeId(@Nullable String selectedTradeId) {
|
||||
this.selectedTradeId = selectedTradeId;
|
||||
}
|
||||
|
||||
public void setSelectItemByTradeIdConsumer(Consumer<String> selectItemByTradeIdConsumer) {
|
||||
this.selectItemByTradeIdConsumer = selectItemByTradeIdConsumer;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private
|
||||
@ -127,43 +147,54 @@ public class NotificationCenter {
|
||||
if (tradeManager.isBuyer(trade.getOffer())) {
|
||||
switch (tradeState) {
|
||||
case DEPOSIT_PUBLISHED_MSG_RECEIVED:
|
||||
message = "Your offer has been accepted by a seller.\n" +
|
||||
"You need to wait for one blockchain confirmation before starting the payment.";
|
||||
message = "Your offer has been accepted by a seller.";
|
||||
break;
|
||||
case DEPOSIT_CONFIRMED:
|
||||
message = "The deposit transaction of your trade has got the first blockchain confirmation.\n" +
|
||||
"You have to start the payment to the bitcoin seller now.";
|
||||
message = "Your trade has at least one blockchain confirmation.\n" +
|
||||
"You can start the payment now.";
|
||||
|
||||
break;
|
||||
/* case FIAT_PAYMENT_RECEIPT_MSG_RECEIVED:
|
||||
case PAYOUT_TX_COMMITTED:
|
||||
case PAYOUT_TX_SENT:*/
|
||||
case PAYOUT_BROAD_CASTED:
|
||||
message = "The bitcoin seller has confirmed the receipt of your payment and the payout transaction has been published.\n" +
|
||||
"The trade is now completed and you can withdraw your funds.";
|
||||
message = "The trade is now completed and you can withdraw your funds.";
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (tradeState) {
|
||||
case DEPOSIT_PUBLISHED_MSG_RECEIVED:
|
||||
message = "Your offer has been accepted by a buyer.\n" +
|
||||
"You need to wait for one blockchain confirmation before starting the payment.";
|
||||
message = "Your offer has been accepted by a buyer.";
|
||||
break;
|
||||
case FIAT_PAYMENT_STARTED_MSG_RECEIVED:
|
||||
message = "The bitcoin buyer has started the payment.\n" +
|
||||
"Please check your payment account if you have received his payment.";
|
||||
message = "The bitcoin buyer has started the payment.";
|
||||
break;
|
||||
/* case FIAT_PAYMENT_RECEIPT_MSG_SENT:
|
||||
case PAYOUT_TX_RECEIVED:
|
||||
case PAYOUT_TX_COMMITTED:*/
|
||||
case PAYOUT_BROAD_CASTED:
|
||||
message = "The payout transaction has been published.\n" +
|
||||
"The trade is now completed and you can withdraw your funds.";
|
||||
message = "The trade is now completed and you can withdraw your funds.";
|
||||
}
|
||||
}
|
||||
|
||||
if (message != null && !trade.getId().equals(selectedTradeId))
|
||||
new Notification().tradeHeadLine(trade.getShortId()).message(message).show();
|
||||
if (message != null) {
|
||||
Notification notification = new Notification().tradeHeadLine(trade.getShortId()).message(message);
|
||||
|
||||
if (navigation.getCurrentPath() != null && !navigation.getCurrentPath().contains(PendingTradesView.class)) {
|
||||
notification.actionButtonText("Go to \"Open trades\"")
|
||||
.onAction(() -> {
|
||||
navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class);
|
||||
UserThread.runAfter(() -> {
|
||||
selectItemByTradeIdConsumer.accept(trade.getId());
|
||||
}, 1);
|
||||
})
|
||||
.show();
|
||||
} else if (selectedTradeId != null && !trade.getId().equals(selectedTradeId)) {
|
||||
notification.actionButtonText("Select trade")
|
||||
.onAction(() -> selectItemByTradeIdConsumer.accept(trade.getId()))
|
||||
.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onDisputeStateChanged(Trade trade, Trade.DisputeState disputeState) {
|
||||
|
@ -27,7 +27,6 @@ import javafx.beans.value.ChangeListener;
|
||||
import javafx.geometry.HPos;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Orientation;
|
||||
import javafx.geometry.Point2D;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.*;
|
||||
@ -74,6 +73,7 @@ public class Popup {
|
||||
private Preferences preferences;
|
||||
private ChangeListener<Number> positionListener;
|
||||
private Timer centerTime;
|
||||
protected double buttonDistance = 20;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -86,6 +86,7 @@ public class Popup {
|
||||
public void show() {
|
||||
createGridPane();
|
||||
addHeadLine();
|
||||
addSeparator();
|
||||
|
||||
if (showProgressIndicator)
|
||||
addProgressIndicator();
|
||||
@ -96,24 +97,34 @@ public class Popup {
|
||||
|
||||
addCloseButton();
|
||||
addDontShowAgainCheckBox();
|
||||
applyStyles();
|
||||
PopupManager.queueForDisplay(this);
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
owner.getScene().getWindow().xProperty().removeListener(positionListener);
|
||||
owner.getScene().getWindow().yProperty().removeListener(positionListener);
|
||||
animateHide(() -> {
|
||||
Window window = owner.getScene().getWindow();
|
||||
window.xProperty().removeListener(positionListener);
|
||||
window.yProperty().removeListener(positionListener);
|
||||
window.widthProperty().removeListener(positionListener);
|
||||
|
||||
if (centerTime != null)
|
||||
centerTime.cancel();
|
||||
if (centerTime != null)
|
||||
centerTime.cancel();
|
||||
|
||||
MainView.removeBlur();
|
||||
if (stage != null)
|
||||
stage.hide();
|
||||
else
|
||||
log.warn("Stage is null");
|
||||
removeEffectFromBackground();
|
||||
|
||||
cleanup();
|
||||
PopupManager.isHidden(this);
|
||||
if (stage != null)
|
||||
stage.hide();
|
||||
else
|
||||
log.warn("Stage is null");
|
||||
|
||||
cleanup();
|
||||
PopupManager.isHidden(Popup.this);
|
||||
});
|
||||
}
|
||||
|
||||
protected void animateHide(Runnable onFinishedHandler) {
|
||||
onFinishedHandler.run();
|
||||
}
|
||||
|
||||
protected void cleanup() {
|
||||
@ -217,11 +228,6 @@ public class Popup {
|
||||
gridPane.setVgap(5);
|
||||
gridPane.setPadding(new Insets(30, 30, 30, 30));
|
||||
gridPane.setPrefWidth(width);
|
||||
gridPane.setStyle("-fx-background-color: white;" +
|
||||
"-fx-background-radius: 5 5 5 5;" +
|
||||
"-fx-effect: dropshadow(gaussian, #999, 10, 0, 0, 0);" +
|
||||
"-fx-background-insets: 10;"
|
||||
);
|
||||
|
||||
ColumnConstraints columnConstraints1 = new ColumnConstraints();
|
||||
columnConstraints1.setHalignment(HPos.RIGHT);
|
||||
@ -232,7 +238,7 @@ public class Popup {
|
||||
}
|
||||
|
||||
protected void blurAgain() {
|
||||
FxTimer.runLater(Duration.ofMillis(Transitions.DEFAULT_DURATION), () -> MainView.blurLight());
|
||||
FxTimer.runLater(Duration.ofMillis(Transitions.DEFAULT_DURATION), MainView::blurLight);
|
||||
}
|
||||
|
||||
public void display() {
|
||||
@ -244,14 +250,15 @@ public class Popup {
|
||||
scene.getStylesheets().setAll(owner.getScene().getStylesheets());
|
||||
scene.setFill(Color.TRANSPARENT);
|
||||
stage.setScene(scene);
|
||||
stage.initModality(Modality.WINDOW_MODAL);
|
||||
setModality();
|
||||
stage.initStyle(StageStyle.TRANSPARENT);
|
||||
stage.initOwner(owner.getScene().getWindow());
|
||||
Window window = owner.getScene().getWindow();
|
||||
stage.initOwner(window);
|
||||
stage.show();
|
||||
|
||||
centerPopup();
|
||||
layout();
|
||||
|
||||
MainView.blurLight();
|
||||
addEffectToBackground();
|
||||
|
||||
// On Linux the owner stage does not move the child stage as it does on Mac
|
||||
// So we need to apply centerPopup. Further with fast movements the handler loses
|
||||
@ -259,34 +266,61 @@ public class Popup {
|
||||
// Also on Mac sometimes the popups are positioned outside of the main app, so keep it for all OS
|
||||
positionListener = (observable, oldValue, newValue) -> {
|
||||
if (stage != null) {
|
||||
centerPopup();
|
||||
layout();
|
||||
if (centerTime != null)
|
||||
centerTime.cancel();
|
||||
|
||||
centerTime = UserThread.runAfter(this::centerPopup, 3);
|
||||
centerTime = UserThread.runAfter(this::layout, 3);
|
||||
}
|
||||
};
|
||||
owner.getScene().getWindow().xProperty().addListener(positionListener);
|
||||
owner.getScene().getWindow().yProperty().addListener(positionListener);
|
||||
window.xProperty().addListener(positionListener);
|
||||
window.yProperty().addListener(positionListener);
|
||||
window.widthProperty().addListener(positionListener);
|
||||
|
||||
animateDisplay();
|
||||
}
|
||||
|
||||
protected void centerPopup() {
|
||||
protected void animateDisplay() {
|
||||
}
|
||||
|
||||
protected void setModality() {
|
||||
stage.initModality(Modality.WINDOW_MODAL);
|
||||
}
|
||||
|
||||
protected void applyStyles() {
|
||||
gridPane.setId("popup-bg");
|
||||
if (headLineLabel != null)
|
||||
headLineLabel.setId("popup-headline");
|
||||
}
|
||||
|
||||
protected void addEffectToBackground() {
|
||||
MainView.blurLight();
|
||||
}
|
||||
|
||||
protected void removeEffectFromBackground() {
|
||||
MainView.removeBlur();
|
||||
}
|
||||
|
||||
protected void layout() {
|
||||
Window window = owner.getScene().getWindow();
|
||||
double titleBarHeight = window.getHeight() - owner.getScene().getHeight();
|
||||
Point2D point = owner.localToScene(0, 0);
|
||||
stage.setX(Math.round(window.getX() + point.getX() + (owner.getWidth() - stage.getWidth()) / 2));
|
||||
stage.setY(Math.round(window.getY() + titleBarHeight + point.getY() + (owner.getHeight() - stage.getHeight()) / 2));
|
||||
stage.setX(Math.round(window.getX() + (owner.getWidth() - stage.getWidth()) / 2));
|
||||
stage.setY(Math.round(window.getY() + titleBarHeight + (owner.getHeight() - stage.getHeight()) / 2));
|
||||
}
|
||||
|
||||
protected void addHeadLine() {
|
||||
if (headLine != null) {
|
||||
headLineLabel = new Label(BSResources.get(headLine));
|
||||
headLineLabel.setMouseTransparent(true);
|
||||
headLineLabel.setId("popup-headline");
|
||||
GridPane.setHalignment(headLineLabel, HPos.LEFT);
|
||||
GridPane.setRowIndex(headLineLabel, ++rowIndex);
|
||||
GridPane.setColumnSpan(headLineLabel, 2);
|
||||
gridPane.getChildren().addAll(headLineLabel);
|
||||
}
|
||||
}
|
||||
|
||||
protected void addSeparator() {
|
||||
if (headLine != null) {
|
||||
Separator separator = new Separator();
|
||||
separator.setMouseTransparent(true);
|
||||
separator.setOrientation(Orientation.HORIZONTAL);
|
||||
@ -295,7 +329,7 @@ public class Popup {
|
||||
GridPane.setRowIndex(separator, ++rowIndex);
|
||||
GridPane.setColumnSpan(separator, 2);
|
||||
|
||||
gridPane.getChildren().addAll(headLineLabel, separator);
|
||||
gridPane.getChildren().add(separator);
|
||||
}
|
||||
}
|
||||
|
||||
@ -322,7 +356,6 @@ public class Popup {
|
||||
"It will make debugging easier if you can attach the bitsquare.log file which you can find in the application directory.");
|
||||
|
||||
Button githubButton = new Button("Report to Github issue tracker");
|
||||
githubButton.setId("popup-button");
|
||||
GridPane.setMargin(githubButton, new Insets(20, 0, 0, 0));
|
||||
GridPane.setHalignment(githubButton, HPos.RIGHT);
|
||||
GridPane.setRowIndex(githubButton, ++rowIndex);
|
||||
@ -335,7 +368,6 @@ public class Popup {
|
||||
});
|
||||
|
||||
Button mailButton = new Button("Report by email");
|
||||
mailButton.setId("popup-button");
|
||||
GridPane.setHalignment(mailButton, HPos.RIGHT);
|
||||
GridPane.setRowIndex(mailButton, ++rowIndex);
|
||||
GridPane.setColumnIndex(mailButton, 1);
|
||||
@ -373,7 +405,6 @@ public class Popup {
|
||||
|
||||
protected void addCloseButton() {
|
||||
closeButton = new Button(closeButtonText == null ? "Close" : closeButtonText);
|
||||
closeButton.setId("popup-button");
|
||||
closeButton.setOnAction(event -> {
|
||||
hide();
|
||||
closeHandlerOptional.ifPresent(closeHandler -> closeHandler.run());
|
||||
@ -381,7 +412,6 @@ public class Popup {
|
||||
|
||||
if (actionHandlerOptional.isPresent() || actionButtonText != null) {
|
||||
actionButton = new Button(actionButtonText == null ? "Ok" : actionButtonText);
|
||||
actionButton.setId("popup-button");
|
||||
actionButton.setDefaultButton(true);
|
||||
//TODO app wide focus
|
||||
//actionButton.requestFocus();
|
||||
@ -399,13 +429,13 @@ public class Popup {
|
||||
GridPane.setHalignment(hBox, HPos.RIGHT);
|
||||
GridPane.setRowIndex(hBox, ++rowIndex);
|
||||
GridPane.setColumnSpan(hBox, 2);
|
||||
GridPane.setMargin(hBox, new Insets(30, 0, 0, 0));
|
||||
GridPane.setMargin(hBox, new Insets(buttonDistance, 0, 0, 0));
|
||||
gridPane.getChildren().add(hBox);
|
||||
} else {
|
||||
closeButton.setDefaultButton(true);
|
||||
GridPane.setHalignment(closeButton, HPos.RIGHT);
|
||||
if (!showReportErrorButtons)
|
||||
GridPane.setMargin(closeButton, new Insets(20, 0, 0, 0));
|
||||
GridPane.setMargin(closeButton, new Insets(buttonDistance, 0, 0, 0));
|
||||
GridPane.setRowIndex(closeButton, ++rowIndex);
|
||||
GridPane.setColumnIndex(closeButton, 1);
|
||||
gridPane.getChildren().add(closeButton);
|
||||
|
@ -32,6 +32,7 @@ import io.bitsquare.gui.Navigation;
|
||||
import io.bitsquare.gui.common.model.ActivatableDataModel;
|
||||
import io.bitsquare.gui.main.MainView;
|
||||
import io.bitsquare.gui.main.disputes.DisputesView;
|
||||
import io.bitsquare.gui.main.notifications.NotificationCenter;
|
||||
import io.bitsquare.gui.main.popups.SelectDepositTxPopup;
|
||||
import io.bitsquare.gui.main.popups.WalletPasswordPopup;
|
||||
import io.bitsquare.payment.PaymentAccountContractData;
|
||||
@ -71,6 +72,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
||||
public final DisputeManager disputeManager;
|
||||
private final Navigation navigation;
|
||||
private final WalletPasswordPopup walletPasswordPopup;
|
||||
private NotificationCenter notificationCenter;
|
||||
|
||||
final ObservableList<PendingTradesListItem> list = FXCollections.observableArrayList();
|
||||
private final ListChangeListener<Trade> tradesListChangeListener;
|
||||
@ -79,6 +81,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
||||
final ObjectProperty<PendingTradesListItem> selectedItemProperty = new SimpleObjectProperty<>();
|
||||
public final StringProperty txId = new SimpleStringProperty();
|
||||
public final Preferences preferences;
|
||||
private boolean activated;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -88,7 +91,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
||||
@Inject
|
||||
public PendingTradesDataModel(TradeManager tradeManager, WalletService walletService, TradeWalletService tradeWalletService,
|
||||
User user, KeyRing keyRing, DisputeManager disputeManager, Preferences preferences,
|
||||
Navigation navigation, WalletPasswordPopup walletPasswordPopup) {
|
||||
Navigation navigation, WalletPasswordPopup walletPasswordPopup, NotificationCenter notificationCenter) {
|
||||
this.tradeManager = tradeManager;
|
||||
this.walletService = walletService;
|
||||
this.tradeWalletService = tradeWalletService;
|
||||
@ -98,19 +101,27 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
||||
this.preferences = preferences;
|
||||
this.navigation = navigation;
|
||||
this.walletPasswordPopup = walletPasswordPopup;
|
||||
this.notificationCenter = notificationCenter;
|
||||
|
||||
tradesListChangeListener = change -> onListChanged();
|
||||
notificationCenter.setSelectItemByTradeIdConsumer(this::selectItemByTradeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void activate() {
|
||||
tradeManager.getTrades().addListener(tradesListChangeListener);
|
||||
onListChanged();
|
||||
if (selectedItemProperty.get() != null)
|
||||
notificationCenter.setSelectedTradeId(selectedItemProperty.get().getTrade().getId());
|
||||
|
||||
activated = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
tradeManager.getTrades().removeListener(tradesListChangeListener);
|
||||
notificationCenter.setSelectedTradeId(null);
|
||||
activated = false;
|
||||
}
|
||||
|
||||
|
||||
@ -254,12 +265,20 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
||||
doSelectItem(null);
|
||||
}
|
||||
|
||||
private void selectItemByTradeId(String tradeId) {
|
||||
if (activated)
|
||||
list.stream().filter(e -> e.getTrade().getId().equals(tradeId)).findAny().ifPresent(this::doSelectItem);
|
||||
}
|
||||
|
||||
private void doSelectItem(PendingTradesListItem item) {
|
||||
if (item != null) {
|
||||
Trade trade = item.getTrade();
|
||||
isOfferer = tradeManager.isMyOffer(trade.getOffer());
|
||||
if (trade.getDepositTx() != null)
|
||||
txId.set(trade.getDepositTx().getHashAsString());
|
||||
notificationCenter.setSelectedTradeId(trade.getId());
|
||||
} else {
|
||||
notificationCenter.setSelectedTradeId(null);
|
||||
}
|
||||
selectedItemProperty.set(item);
|
||||
}
|
||||
|
@ -45,10 +45,6 @@ public class PendingTradesListItem {
|
||||
return trade.tradeVolumeProperty();
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return trade.getShortId();
|
||||
}
|
||||
|
||||
public Fiat getPrice() {
|
||||
return trade.getOffer().getPrice();
|
||||
}
|
||||
|
@ -217,7 +217,7 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (item != null && !empty) {
|
||||
field = new HyperlinkWithIcon(item.getId(), true);
|
||||
field = new HyperlinkWithIcon(item.getTrade().getShortId(), true);
|
||||
field.setOnAction(event -> tradeDetailsPopup.show(item.getTrade()));
|
||||
field.setTooltip(new Tooltip("Open popup for details"));
|
||||
setGraphic(field);
|
||||
|
@ -112,9 +112,8 @@ public class BuyerStep5View extends TradeStepView {
|
||||
() -> {
|
||||
String id = "TradeCompletedInfoPopup";
|
||||
if (preferences.showAgain(id)) {
|
||||
new Popup()
|
||||
.information("You can review your completed trades under \"Portfolio/History\" or " +
|
||||
"review your transactions under \"Funds/Transactions\"")
|
||||
new Popup().information("You can review your completed trades under \"Portfolio/History\" or " +
|
||||
"review your transactions under \"Funds/Transactions\"")
|
||||
.dontShowAgainId(id, preferences)
|
||||
.show();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user