mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-06-25 07:10:48 -04:00
move network code to module
This commit is contained in:
parent
105a63847a
commit
c6ece486ed
384 changed files with 11571 additions and 21763 deletions
|
@ -16,12 +16,10 @@
|
|||
|
||||
package eu.hansolo.enzo.notification;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import io.bitsquare.common.UserThread;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.KeyValue;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ObjectPropertyBase;
|
||||
import javafx.collections.FXCollections;
|
||||
|
@ -30,18 +28,21 @@ import javafx.event.EventHandler;
|
|||
import javafx.event.EventType;
|
||||
import javafx.event.WeakEventHandler;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.*;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.image.*;
|
||||
import javafx.scene.input.*;
|
||||
import javafx.scene.layout.*;
|
||||
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
|
||||
|
@ -50,13 +51,13 @@ import org.controlsfx.control.PopOver;
|
|||
* {@code eu.hansolo.enzo.*} types are loaded from the enzo jar (see build.gradle for details).
|
||||
*/
|
||||
public class Notification {
|
||||
public static final Image INFO_ICON = new Image(Notifier.class.getResourceAsStream("info.png"));
|
||||
public static final Image WARNING_ICON = new Image(Notifier.class.getResourceAsStream("warning.png"));
|
||||
public static final Image SUCCESS_ICON = new Image(Notifier.class.getResourceAsStream("success.png"));
|
||||
public static final Image ERROR_ICON = new Image(Notifier.class.getResourceAsStream("error.png"));
|
||||
public final String TITLE;
|
||||
public final String MESSAGE;
|
||||
public final Image IMAGE;
|
||||
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 **************************************
|
||||
|
@ -68,7 +69,7 @@ public class Notification {
|
|||
this("", MESSAGE, IMAGE);
|
||||
}
|
||||
|
||||
public Notification(final String TITLE, final String MESSAGE, final Image IMAGE) {
|
||||
private Notification(final String TITLE, final String MESSAGE, final Image IMAGE) {
|
||||
this.TITLE = TITLE;
|
||||
this.MESSAGE = MESSAGE;
|
||||
this.IMAGE = IMAGE;
|
||||
|
@ -313,7 +314,7 @@ public class Notification {
|
|||
private void preOrder() {
|
||||
if (popups.isEmpty()) return;
|
||||
IntStream.range(0, popups.size()).parallel().forEachOrdered(
|
||||
i -> Platform.runLater(() -> {
|
||||
i -> UserThread.execute(() -> {
|
||||
switch (popupLocation) {
|
||||
case TOP_LEFT:
|
||||
case TOP_CENTER:
|
||||
|
@ -377,7 +378,7 @@ public class Notification {
|
|||
|
||||
Timeline timeline = new Timeline(kfBegin, kfEnd);
|
||||
timeline.setDelay(popupLifetime);
|
||||
timeline.setOnFinished(actionEvent -> Platform.runLater(() -> {
|
||||
timeline.setOnFinished(actionEvent -> UserThread.execute(() -> {
|
||||
popOver.hide();
|
||||
popups.remove(popOver);
|
||||
fireNotificationEvent(new NotificationEvent(NOTIFICATION, Notifier.this, popOver,
|
||||
|
|
|
@ -17,6 +17,15 @@
|
|||
|
||||
package io.bitsquare.app;
|
||||
|
||||
import ch.qos.logback.classic.Logger;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.vinumeris.updatefx.UpdateFX;
|
||||
import io.bitsquare.alert.AlertManager;
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
import io.bitsquare.gui.SystemTray;
|
||||
import io.bitsquare.gui.common.view.CachingViewLoader;
|
||||
import io.bitsquare.gui.common.view.View;
|
||||
|
@ -24,54 +33,57 @@ import io.bitsquare.gui.common.view.ViewLoader;
|
|||
import io.bitsquare.gui.common.view.guice.InjectorViewFactory;
|
||||
import io.bitsquare.gui.main.MainView;
|
||||
import io.bitsquare.gui.main.debug.DebugView;
|
||||
import io.bitsquare.gui.popups.EmptyWalletPopup;
|
||||
import io.bitsquare.gui.popups.SendAlertMessagePopup;
|
||||
import io.bitsquare.gui.util.ImageUtil;
|
||||
import io.bitsquare.p2p.P2PService;
|
||||
import io.bitsquare.storage.Storage;
|
||||
import io.bitsquare.util.Utilities;
|
||||
|
||||
import org.bitcoinj.utils.Threading;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.bitsquare.trade.offer.OpenOfferManager;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.*;
|
||||
import javafx.scene.image.*;
|
||||
import javafx.scene.input.*;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyCodeCombination;
|
||||
import javafx.scene.input.KeyCombination;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.StageStyle;
|
||||
|
||||
import org.controlsfx.dialog.Dialogs;
|
||||
|
||||
import org.reactfx.EventStreams;
|
||||
import org.reactfx.util.FxTimer;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ch.qos.logback.classic.Logger;
|
||||
import com.vinumeris.updatefx.UpdateFX;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static io.bitsquare.app.BitsquareEnvironment.APP_NAME_KEY;
|
||||
|
||||
public class BitsquareApp extends Application {
|
||||
private static final Logger log = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(BitsquareApp.class);
|
||||
|
||||
public static final boolean DEV_MODE = false;
|
||||
public static final boolean DEV_MODE = true;
|
||||
|
||||
private static Environment env;
|
||||
|
||||
private BitsquareAppModule bitsquareAppModule;
|
||||
private Injector injector;
|
||||
private Stage primaryStage;
|
||||
|
||||
public static Stage getPrimaryStage() {
|
||||
return primaryStage;
|
||||
}
|
||||
|
||||
private static Stage primaryStage;
|
||||
private Scene scene;
|
||||
private List<String> corruptedDatabaseFiles = new ArrayList<>();
|
||||
private final List<String> corruptedDatabaseFiles = new ArrayList<>();
|
||||
private MainView mainView;
|
||||
|
||||
public static Runnable shutDownHandler;
|
||||
|
@ -83,27 +95,24 @@ public class BitsquareApp extends Application {
|
|||
|
||||
@Override
|
||||
public void start(Stage primaryStage) throws IOException {
|
||||
this.primaryStage = primaryStage;
|
||||
BitsquareApp.primaryStage = primaryStage;
|
||||
|
||||
Logging.setup(Paths.get(env.getProperty(BitsquareEnvironment.APP_DATA_DIR_KEY), "bitsquare").toString());
|
||||
|
||||
UserThread.setExecutor(Platform::runLater);
|
||||
|
||||
shutDownHandler = this::stop;
|
||||
restartDownHandler = this::restart;
|
||||
|
||||
// setup UncaughtExceptionHandler
|
||||
Thread.UncaughtExceptionHandler handler = (thread, throwable) -> {
|
||||
// Might come from another thread
|
||||
Platform.runLater(() -> showErrorPopup(throwable, true));
|
||||
UserThread.execute(() -> showErrorPopup(throwable, false));
|
||||
};
|
||||
Thread.setDefaultUncaughtExceptionHandler(handler);
|
||||
Thread.currentThread().setUncaughtExceptionHandler(handler);
|
||||
|
||||
try {
|
||||
log.trace("BitsquareApp.start");
|
||||
|
||||
// Set user thread for callbacks from backend threads
|
||||
Threading.USER_THREAD = Platform::runLater;
|
||||
|
||||
// Use CrashFX for report crash logs
|
||||
/*CrashFX.setup("Bitsquare/" + Version.VERSION,
|
||||
Paths.get(env.getProperty(BitsquareEnvironment.APP_DATA_DIR_KEY), "crashes"),
|
||||
|
@ -126,7 +135,7 @@ public class BitsquareApp extends Application {
|
|||
mainView.setPersistedFilesCorrupted(corruptedDatabaseFiles);
|
||||
});
|
||||
|
||||
scene = new Scene(mainView.getRoot(), 1000, 650);
|
||||
scene = new Scene(mainView.getRoot(), 1000, 740);
|
||||
scene.getStylesheets().setAll(
|
||||
"/io/bitsquare/gui/bitsquare.css",
|
||||
"/io/bitsquare/gui/images.css");
|
||||
|
@ -147,8 +156,15 @@ public class BitsquareApp extends Application {
|
|||
else if (new KeyCodeCombination(KeyCode.D, KeyCombination.SHORTCUT_DOWN).match(keyEvent))
|
||||
//if (BitsquareApp.DEV_MODE)
|
||||
showDebugWindow();
|
||||
else if (new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN).match(keyEvent))
|
||||
showFPSWindow();
|
||||
else if (new KeyCodeCombination(KeyCode.E, KeyCombination.SHORTCUT_DOWN).match(keyEvent))
|
||||
showEmptyWalletPopup();
|
||||
else if (new KeyCodeCombination(KeyCode.A, KeyCombination.SHORTCUT_DOWN).match(keyEvent))
|
||||
showSendAlertMessagePopup();
|
||||
});
|
||||
|
||||
|
||||
// configure the primary stage
|
||||
primaryStage.setTitle(env.getRequiredProperty(APP_NAME_KEY));
|
||||
primaryStage.setScene(scene);
|
||||
|
@ -172,10 +188,22 @@ public class BitsquareApp extends Application {
|
|||
|
||||
//showDebugWindow();
|
||||
} catch (Throwable throwable) {
|
||||
showErrorPopup(throwable, true);
|
||||
showErrorPopup(throwable, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void showSendAlertMessagePopup() {
|
||||
AlertManager alertManager = injector.getInstance(AlertManager.class);
|
||||
new SendAlertMessagePopup()
|
||||
.onAddAlertMessage((alertMessage, privKeyString) -> alertManager.addAlertMessageIfKeyIsValid(alertMessage, privKeyString))
|
||||
.onRemoveAlertMessage(privKeyString -> alertManager.removeAlertMessageIfKeyIsValid(privKeyString))
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showEmptyWalletPopup() {
|
||||
injector.getInstance(EmptyWalletPopup.class).show();
|
||||
}
|
||||
|
||||
private void showErrorPopup(Throwable throwable, boolean doShutDown) {
|
||||
if (scene == null) {
|
||||
scene = new Scene(new StackPane(), 1000, 650);
|
||||
|
@ -184,11 +212,16 @@ public class BitsquareApp extends Application {
|
|||
}
|
||||
try {
|
||||
throwable.printStackTrace();
|
||||
Dialogs.create()
|
||||
.owner(primaryStage)
|
||||
.title("Error")
|
||||
.message("A fatal exception occurred at startup.")
|
||||
.showException(throwable);
|
||||
try {
|
||||
Dialogs.create()
|
||||
.owner(primaryStage)
|
||||
.title("Error")
|
||||
.message("A fatal exception occurred at startup.")
|
||||
.showException(throwable);
|
||||
} catch (Throwable throwable3) {
|
||||
log.error("Error at displaying Throwable.");
|
||||
throwable3.printStackTrace();
|
||||
}
|
||||
if (doShutDown)
|
||||
stop();
|
||||
} catch (Throwable throwable2) {
|
||||
|
@ -220,19 +253,71 @@ public class BitsquareApp extends Application {
|
|||
stage.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
bitsquareAppModule.close(injector);
|
||||
System.exit(0);
|
||||
|
||||
private void showFPSWindow() {
|
||||
Label label = new Label();
|
||||
EventStreams.animationTicks()
|
||||
.latestN(100)
|
||||
.map(ticks -> {
|
||||
int n = ticks.size() - 1;
|
||||
return n * 1_000_000_000.0 / (ticks.get(n) - ticks.get(0));
|
||||
})
|
||||
.map(d -> String.format("FPS: %.3f", d))
|
||||
.feedTo(label.textProperty());
|
||||
|
||||
Pane root = new StackPane();
|
||||
root.getChildren().add(label);
|
||||
Stage stage = new Stage();
|
||||
stage.setScene(new Scene(root));
|
||||
stage.setTitle("FPS");
|
||||
stage.initModality(Modality.NONE);
|
||||
stage.initStyle(StageStyle.UTILITY);
|
||||
stage.initOwner(scene.getWindow());
|
||||
stage.setX(primaryStage.getX() + primaryStage.getWidth() + 10);
|
||||
stage.setY(primaryStage.getY());
|
||||
stage.setWidth(200);
|
||||
stage.setHeight(100);
|
||||
stage.show();
|
||||
|
||||
}
|
||||
|
||||
public void restart() {
|
||||
@Override
|
||||
public void stop() {
|
||||
gracefulShutDown(() -> {
|
||||
log.info("App shutdown complete");
|
||||
System.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
private void gracefulShutDown(ResultHandler resultHandler) {
|
||||
log.debug("gracefulShutDown");
|
||||
try {
|
||||
bitsquareAppModule.close(injector);
|
||||
UpdateFX.restartApp();
|
||||
if (injector != null) {
|
||||
OpenOfferManager openOfferManager = injector.getInstance(OpenOfferManager.class);
|
||||
openOfferManager.shutDown(() -> {
|
||||
P2PService p2PService = injector.getInstance(P2PService.class);
|
||||
p2PService.shutDown(() -> {
|
||||
WalletService walletService = injector.getInstance(WalletService.class);
|
||||
walletService.shutDownDone.addListener((observable, oldValue, newValue) -> {
|
||||
bitsquareAppModule.close(injector);
|
||||
resultHandler.handleResult();
|
||||
});
|
||||
injector.getInstance(WalletService.class).shutDown();
|
||||
});
|
||||
});
|
||||
// we wait max 5 sec.
|
||||
FxTimer.runLater(Duration.ofMillis(5000), resultHandler::handleResult);
|
||||
} else {
|
||||
FxTimer.runLater(Duration.ofMillis(500), resultHandler::handleResult);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
// in dev mode restart does not work
|
||||
stop();
|
||||
log.info("App shutdown failed with exception");
|
||||
t.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private void restart() {
|
||||
gracefulShutDown(UpdateFX::restartApp);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,31 +17,25 @@
|
|||
|
||||
package io.bitsquare.app;
|
||||
|
||||
import com.vinumeris.updatefx.UpdateFX;
|
||||
import io.bitsquare.BitsquareException;
|
||||
import io.bitsquare.btc.BitcoinNetwork;
|
||||
import io.bitsquare.btc.RegTestHost;
|
||||
import io.bitsquare.p2p.BootstrapNodes;
|
||||
import io.bitsquare.p2p.Node;
|
||||
import io.bitsquare.p2p.Utils;
|
||||
import io.bitsquare.util.joptsimple.EnumValueConverter;
|
||||
import joptsimple.OptionException;
|
||||
import joptsimple.OptionParser;
|
||||
import joptsimple.OptionSet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.vinumeris.updatefx.UpdateFX;
|
||||
import joptsimple.OptionException;
|
||||
import joptsimple.OptionParser;
|
||||
import joptsimple.OptionSet;
|
||||
|
||||
import static io.bitsquare.app.BitsquareEnvironment.*;
|
||||
import static io.bitsquare.p2p.Node.*;
|
||||
import static io.bitsquare.p2p.tomp2p.TomP2PModule.*;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
public class BitsquareAppMain extends BitsquareExecutable {
|
||||
|
@ -77,7 +71,7 @@ public class BitsquareAppMain extends BitsquareExecutable {
|
|||
|
||||
// That will be called from UpdateFX after updates are checked
|
||||
public static void realMain(String[] args) throws Exception {
|
||||
log.trace("realMain");
|
||||
//log.trace("realMain");
|
||||
// For some reason the JavaFX launch process results in us losing the thread context class loader: reset it.
|
||||
// In order to work around a bug in JavaFX 8u25 and below, you must include the following code as the first line of your realMain method:
|
||||
Thread.currentThread().setContextClassLoader(BitsquareAppMain.class.getClassLoader());
|
||||
|
@ -112,12 +106,15 @@ public class BitsquareAppMain extends BitsquareExecutable {
|
|||
description("Clean application data directory", DEFAULT_APP_DATA_DIR_CLEAN))
|
||||
.withRequiredArg()
|
||||
.ofType(boolean.class);
|
||||
parser.accepts(NAME_KEY, description("Name of this node", null))
|
||||
parser.accepts(ProgramArguments.NAME_KEY, description("Name of this node", null))
|
||||
.withRequiredArg();
|
||||
parser.accepts(PORT_KEY, description("Port to listen on", Node.CLIENT_PORT))
|
||||
parser.accepts(ProgramArguments.PORT_KEY, description("Port to listen on", Utils.findFreeSystemPort()))
|
||||
.withRequiredArg()
|
||||
.ofType(int.class);
|
||||
parser.accepts(USE_MANUAL_PORT_FORWARDING_KEY, description("Use manual port forwarding", false))
|
||||
parser.accepts(ProgramArguments.USE_LOCALHOST, description("Use localhost network for development", false))
|
||||
.withRequiredArg()
|
||||
.ofType(boolean.class);
|
||||
parser.accepts(ProgramArguments.DEV_TEST, description("Enable arbitrator dev priv key", false))
|
||||
.withRequiredArg()
|
||||
.ofType(boolean.class);
|
||||
parser.accepts(BitcoinNetwork.KEY, description("Bitcoin network", BitcoinNetwork.DEFAULT))
|
||||
|
@ -125,25 +122,11 @@ public class BitsquareAppMain extends BitsquareExecutable {
|
|||
.ofType(BitcoinNetwork.class)
|
||||
.withValuesConvertedBy(new EnumValueConverter(BitcoinNetwork.class));
|
||||
|
||||
|
||||
parser.accepts(RegTestHost.KEY, description("", RegTestHost.DEFAULT))
|
||||
.withRequiredArg()
|
||||
.ofType(RegTestHost.class)
|
||||
.withValuesConvertedBy(new EnumValueConverter(RegTestHost.class));
|
||||
|
||||
BootstrapNodes bootstrapNodes = new BootstrapNodes();
|
||||
bootstrapNodes.initWithNetworkId(Node.REG_TEST_P2P_ID); // use regtest as default
|
||||
parser.accepts(BOOTSTRAP_NODE_NAME_KEY, description("Bootstrap node name", bootstrapNodes.getLocalhostNode().getName()))
|
||||
.withRequiredArg();
|
||||
parser.accepts(BOOTSTRAP_NODE_IP_KEY, description("Bootstrap node IP", bootstrapNodes.getLocalhostNode().getIp()))
|
||||
.withRequiredArg();
|
||||
parser.accepts(BOOTSTRAP_NODE_P2P_ID_KEY, description("Bootstrap node p2p network ID", bootstrapNodes.getLocalhostNode().getPort()))
|
||||
.withRequiredArg()
|
||||
.ofType(int.class);
|
||||
parser.accepts(BOOTSTRAP_NODE_PORT_KEY, description("Bootstrap node port", bootstrapNodes.getLocalhostNode().getPort()))
|
||||
.withRequiredArg()
|
||||
.ofType(int.class);
|
||||
parser.accepts(NETWORK_INTERFACE_KEY, description("Network interface", null))
|
||||
.withRequiredArg();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,39 +17,31 @@
|
|||
|
||||
package io.bitsquare.app;
|
||||
|
||||
import io.bitsquare.BitsquareModule;
|
||||
import com.google.inject.Singleton;
|
||||
import io.bitsquare.alert.AlertModule;
|
||||
import io.bitsquare.arbitration.ArbitratorModule;
|
||||
import io.bitsquare.arbitration.tomp2p.TomP2PArbitratorModule;
|
||||
import io.bitsquare.btc.BitcoinModule;
|
||||
import io.bitsquare.crypto.CryptoModule;
|
||||
import io.bitsquare.crypto.KeyRing;
|
||||
import io.bitsquare.crypto.KeyStorage;
|
||||
import io.bitsquare.common.crypto.KeyRing;
|
||||
import io.bitsquare.common.crypto.KeyStorage;
|
||||
import io.bitsquare.crypto.EncryptionServiceModule;
|
||||
import io.bitsquare.gui.GuiModule;
|
||||
import io.bitsquare.gui.common.view.CachingViewLoader;
|
||||
import io.bitsquare.p2p.P2PModule;
|
||||
import io.bitsquare.p2p.tomp2p.TomP2PModule;
|
||||
import io.bitsquare.storage.Storage;
|
||||
import io.bitsquare.trade.TradeModule;
|
||||
import io.bitsquare.trade.offer.OfferModule;
|
||||
import io.bitsquare.trade.offer.tomp2p.TomP2POfferModule;
|
||||
import io.bitsquare.user.AccountSettings;
|
||||
import io.bitsquare.user.Preferences;
|
||||
import io.bitsquare.user.User;
|
||||
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Singleton;
|
||||
import javafx.stage.Stage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import static com.google.inject.name.Names.named;
|
||||
|
||||
class BitsquareAppModule extends BitsquareModule {
|
||||
class BitsquareAppModule extends AppModule {
|
||||
private static final Logger log = LoggerFactory.getLogger(BitsquareAppModule.class);
|
||||
|
||||
private final Stage primaryStage;
|
||||
|
@ -61,11 +53,11 @@ class BitsquareAppModule extends BitsquareModule {
|
|||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(CachingViewLoader.class).in(Singleton.class);
|
||||
bind(KeyStorage.class).in(Singleton.class);
|
||||
bind(KeyRing.class).in(Singleton.class);
|
||||
bind(User.class).in(Singleton.class);
|
||||
bind(Preferences.class).in(Singleton.class);
|
||||
bind(AccountSettings.class).in(Singleton.class);
|
||||
|
||||
File storageDir = new File(env.getRequiredProperty(Storage.DIR_KEY));
|
||||
bind(File.class).annotatedWith(named(Storage.DIR_KEY)).toInstance(storageDir);
|
||||
|
@ -78,44 +70,44 @@ class BitsquareAppModule extends BitsquareModule {
|
|||
|
||||
// ordering is used for shut down sequence
|
||||
install(tradeModule());
|
||||
install(cryptoModule());
|
||||
install(encryptionServiceModule());
|
||||
install(arbitratorModule());
|
||||
install(offerModule());
|
||||
install(p2pModule());
|
||||
install(torModule());
|
||||
install(bitcoinModule());
|
||||
install(guiModule());
|
||||
install(alertModule());
|
||||
}
|
||||
|
||||
protected TradeModule tradeModule() {
|
||||
private TradeModule tradeModule() {
|
||||
return new TradeModule(env);
|
||||
}
|
||||
|
||||
protected CryptoModule cryptoModule() {
|
||||
return new CryptoModule(env);
|
||||
private EncryptionServiceModule encryptionServiceModule() {
|
||||
return new EncryptionServiceModule(env);
|
||||
}
|
||||
|
||||
protected ArbitratorModule arbitratorModule() {
|
||||
return new TomP2PArbitratorModule(env);
|
||||
private ArbitratorModule arbitratorModule() {
|
||||
return new ArbitratorModule(env);
|
||||
}
|
||||
|
||||
protected OfferModule offerModule() {
|
||||
return new TomP2POfferModule(env);
|
||||
private AlertModule alertModule() {
|
||||
return new AlertModule(env);
|
||||
}
|
||||
|
||||
protected P2PModule p2pModule() {
|
||||
return new TomP2PModule(env);
|
||||
private OfferModule offerModule() {
|
||||
return new OfferModule(env);
|
||||
}
|
||||
|
||||
protected BitcoinModule bitcoinModule() {
|
||||
private P2PModule torModule() {
|
||||
return new P2PModule(env);
|
||||
}
|
||||
|
||||
private BitcoinModule bitcoinModule() {
|
||||
return new BitcoinModule(env);
|
||||
}
|
||||
|
||||
protected GuiModule guiModule() {
|
||||
private GuiModule guiModule() {
|
||||
return new GuiModule(env, primaryStage);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doClose(Injector injector) {
|
||||
log.trace("doClose " + getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,32 +17,26 @@
|
|||
|
||||
package io.bitsquare.app;
|
||||
|
||||
import io.bitsquare.util.Utilities;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.vinumeris.updatefx.Crypto;
|
||||
import com.vinumeris.updatefx.UpdateFX;
|
||||
import com.vinumeris.updatefx.UpdateSummary;
|
||||
import com.vinumeris.updatefx.Updater;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import org.bouncycastle.math.ec.ECPoint;
|
||||
import rx.Observable;
|
||||
import rx.subjects.BehaviorSubject;
|
||||
import rx.subjects.Subject;
|
||||
import org.reactfx.util.FxTimer;
|
||||
import org.reactfx.util.Timer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
public class UpdateProcess {
|
||||
private static final Logger log = LoggerFactory.getLogger(UpdateProcess.class);
|
||||
|
@ -53,6 +47,7 @@ public class UpdateProcess {
|
|||
private static final Path ROOT_CLASS_PATH = UpdateFX.findCodePath(BitsquareAppMain.class);
|
||||
|
||||
private final BitsquareEnvironment environment;
|
||||
private ResultHandler resultHandler;
|
||||
|
||||
public enum State {
|
||||
CHECK_FOR_UPDATES,
|
||||
|
@ -65,7 +60,6 @@ public class UpdateProcess {
|
|||
public final ObjectProperty<State> state = new SimpleObjectProperty<>(State.CHECK_FOR_UPDATES);
|
||||
|
||||
private String releaseUrl;
|
||||
private final Subject<State, State> process = BehaviorSubject.create();
|
||||
private Timer timeoutTimer;
|
||||
|
||||
@Inject
|
||||
|
@ -77,17 +71,17 @@ public class UpdateProcess {
|
|||
UpdateFX.restartApp();
|
||||
}
|
||||
|
||||
public Observable<State> getProcess() {
|
||||
return process.asObservable();
|
||||
public void setResultHandler(ResultHandler resultHandler) {
|
||||
this.resultHandler = resultHandler;
|
||||
}
|
||||
|
||||
public void init() {
|
||||
log.info("UpdateFX current version " + Version.PATCH_VERSION);
|
||||
log.info("UpdateFX checking for patch version " + Version.PATCH_VERSION);
|
||||
|
||||
// process.timeout() will cause an error state back but we don't want to break startup in case of an timeout
|
||||
timeoutTimer = Utilities.setTimeout(10000, () -> {
|
||||
timeoutTimer = FxTimer.runLater(Duration.ofMillis(10000), () -> {
|
||||
log.error("Timeout reached for UpdateFX");
|
||||
process.onCompleted();
|
||||
resultHandler.handleResult();
|
||||
});
|
||||
String userAgent = environment.getProperty(BitsquareEnvironment.APP_NAME_KEY) + Version.VERSION;
|
||||
|
||||
|
@ -97,7 +91,7 @@ public class UpdateProcess {
|
|||
if (releaseUrl != null && releaseUrl.length() > 0) {
|
||||
log.info("New release available at: " + releaseUrl);
|
||||
state.set(State.NEW_RELEASE);
|
||||
timeoutTimer.cancel();
|
||||
timeoutTimer.stop();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
|
@ -121,11 +115,10 @@ public class UpdateProcess {
|
|||
log.trace("progressProperty newValue = " + newValue);
|
||||
});*/
|
||||
|
||||
log.info("Checking for updates!");
|
||||
updater.setOnSucceeded(event -> {
|
||||
try {
|
||||
UpdateSummary summary = updater.get();
|
||||
log.info("summary " + summary.toString());
|
||||
//log.info("summary " + summary.toString());
|
||||
if (summary.descriptions != null && summary.descriptions.size() > 0) {
|
||||
log.info("One liner: {}", summary.descriptions.get(0).getOneLiner());
|
||||
log.info("{}", summary.descriptions.get(0).getDescription());
|
||||
|
@ -135,13 +128,13 @@ public class UpdateProcess {
|
|||
state.set(State.UPDATE_AVAILABLE);
|
||||
// We stop the timeout and treat it not completed.
|
||||
// The user should click the restart button manually if there are updates available.
|
||||
timeoutTimer.cancel();
|
||||
timeoutTimer.stop();
|
||||
}
|
||||
else if (summary.highestVersion == Version.PATCH_VERSION) {
|
||||
log.info("UP_TO_DATE");
|
||||
state.set(State.UP_TO_DATE);
|
||||
timeoutTimer.cancel();
|
||||
process.onCompleted();
|
||||
timeoutTimer.stop();
|
||||
resultHandler.handleResult();
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
log.error("Exception at processing UpdateSummary: " + e.getMessage());
|
||||
|
@ -149,8 +142,8 @@ public class UpdateProcess {
|
|||
// we treat errors as update not as critical errors to prevent startup,
|
||||
// so we use state.onCompleted() instead of state.onError()
|
||||
state.set(State.FAILURE);
|
||||
timeoutTimer.cancel();
|
||||
process.onCompleted();
|
||||
timeoutTimer.stop();
|
||||
resultHandler.handleResult();
|
||||
}
|
||||
});
|
||||
updater.setOnFailed(event -> {
|
||||
|
@ -160,8 +153,8 @@ public class UpdateProcess {
|
|||
// we treat errors as update not as critical errors to prevent startup,
|
||||
// so we use state.onCompleted() instead of state.onError()
|
||||
state.set(State.FAILURE);
|
||||
timeoutTimer.cancel();
|
||||
process.onCompleted();
|
||||
timeoutTimer.stop();
|
||||
resultHandler.handleResult();
|
||||
});
|
||||
|
||||
Thread thread = new Thread(updater, "Online update check");
|
||||
|
|
|
@ -17,35 +17,27 @@
|
|||
|
||||
package io.bitsquare.gui;
|
||||
|
||||
import io.bitsquare.BitsquareModule;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.name.Names;
|
||||
import io.bitsquare.app.AppModule;
|
||||
import io.bitsquare.app.BitsquareEnvironment;
|
||||
import io.bitsquare.gui.common.fxml.FxmlViewLoader;
|
||||
import io.bitsquare.gui.common.view.CachingViewLoader;
|
||||
import io.bitsquare.gui.common.view.ViewFactory;
|
||||
import io.bitsquare.gui.common.view.ViewLoader;
|
||||
import io.bitsquare.gui.common.view.guice.InjectorViewFactory;
|
||||
import io.bitsquare.gui.components.Popups;
|
||||
import io.bitsquare.gui.main.MainView;
|
||||
import io.bitsquare.gui.main.offer.offerbook.OfferBook;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.gui.util.Transitions;
|
||||
import io.bitsquare.gui.util.validation.BankAccountNumberValidator;
|
||||
import io.bitsquare.gui.util.validation.BtcValidator;
|
||||
import io.bitsquare.gui.util.validation.FiatValidator;
|
||||
import io.bitsquare.gui.util.validation.InputValidator;
|
||||
import io.bitsquare.gui.util.validation.PasswordValidator;
|
||||
import io.bitsquare.gui.util.validation.*;
|
||||
import io.bitsquare.locale.BSResources;
|
||||
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.name.Names;
|
||||
import javafx.stage.Stage;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
public class GuiModule extends BitsquareModule {
|
||||
public class GuiModule extends AppModule {
|
||||
|
||||
private final Stage primaryStage;
|
||||
|
||||
|
@ -66,10 +58,9 @@ public class GuiModule extends BitsquareModule {
|
|||
bind(Navigation.class).in(Singleton.class);
|
||||
|
||||
bind(OfferBook.class).in(Singleton.class);
|
||||
bind(OverlayManager.class).in(Singleton.class);
|
||||
bind(BSFormatter.class).in(Singleton.class);
|
||||
|
||||
bind(BankAccountNumberValidator.class).in(Singleton.class);
|
||||
bind(IBANValidator.class).in(Singleton.class);
|
||||
bind(BtcValidator.class).in(Singleton.class);
|
||||
bind(FiatValidator.class).in(Singleton.class);
|
||||
bind(InputValidator.class).in(Singleton.class);
|
||||
|
@ -77,7 +68,6 @@ public class GuiModule extends BitsquareModule {
|
|||
bind(Transitions.class).in(Singleton.class);
|
||||
|
||||
bind(Stage.class).toInstance(primaryStage);
|
||||
Popups.primaryStage = primaryStage;
|
||||
|
||||
bindConstant().annotatedWith(Names.named(MainView.TITLE_KEY)).to(env.getRequiredProperty(BitsquareEnvironment.APP_NAME_KEY));
|
||||
}
|
||||
|
|
|
@ -17,30 +17,27 @@
|
|||
|
||||
package io.bitsquare.gui;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import io.bitsquare.app.Version;
|
||||
import io.bitsquare.gui.common.view.View;
|
||||
import io.bitsquare.gui.common.view.ViewPath;
|
||||
import io.bitsquare.gui.main.MainView;
|
||||
import io.bitsquare.gui.main.offer.BuyOfferView;
|
||||
import io.bitsquare.gui.main.market.MarketView;
|
||||
import io.bitsquare.storage.Storage;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Navigation implements Serializable {
|
||||
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
||||
private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
|
||||
transient private static final Logger log = LoggerFactory.getLogger(Navigation.class);
|
||||
|
||||
transient private static final ViewPath DEFAULT_VIEW_PATH = ViewPath.to(MainView.class, BuyOfferView.class);
|
||||
transient private static final ViewPath DEFAULT_VIEW_PATH = ViewPath.to(MainView.class, MarketView.class);
|
||||
|
||||
|
||||
public interface Listener {
|
||||
|
@ -50,7 +47,7 @@ public class Navigation implements Serializable {
|
|||
// New listeners can be added during iteration so we use CopyOnWriteArrayList to
|
||||
// prevent invalid array modification
|
||||
transient private final List<Listener> listeners = new CopyOnWriteArrayList<>();
|
||||
transient private final Storage<Navigation> storage;
|
||||
transient private final Storage<Navigation> remoteStorage;
|
||||
transient private ViewPath currentPath;
|
||||
// Used for returning to the last important view. After setup is done we want to
|
||||
// return to the last opened view (e.g. sell/buy)
|
||||
|
@ -61,10 +58,10 @@ public class Navigation implements Serializable {
|
|||
|
||||
|
||||
@Inject
|
||||
public Navigation(Storage<Navigation> storage) {
|
||||
this.storage = storage;
|
||||
public Navigation(Storage<Navigation> remoteStorage) {
|
||||
this.remoteStorage = remoteStorage;
|
||||
|
||||
Navigation persisted = storage.initAndGetPersisted(this);
|
||||
Navigation persisted = remoteStorage.initAndGetPersisted(this);
|
||||
if (persisted != null) {
|
||||
previousPath = persisted.getPreviousPath();
|
||||
}
|
||||
|
@ -106,7 +103,7 @@ public class Navigation implements Serializable {
|
|||
|
||||
currentPath = newPath;
|
||||
previousPath = currentPath;
|
||||
storage.queueUpForSave();
|
||||
remoteStorage.queueUpForSave();
|
||||
listeners.stream().forEach((e) -> e.onNavigationRequested(currentPath));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,59 +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;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class OverlayManager {
|
||||
|
||||
private final static List<OverlayListener> listeners = new ArrayList<>();
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public OverlayManager() {
|
||||
}
|
||||
|
||||
public static void blurContent() {
|
||||
listeners.stream().forEach(OverlayListener::onBlurContentRequested);
|
||||
}
|
||||
|
||||
public static void removeBlurContent() {
|
||||
listeners.stream().forEach(OverlayListener::onRemoveBlurContentRequested);
|
||||
}
|
||||
|
||||
public static void addListener(OverlayListener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public static void removeListener(OverlayListener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
public interface OverlayListener {
|
||||
void onBlurContentRequested();
|
||||
|
||||
void onRemoveBlurContentRequested();
|
||||
}
|
||||
}
|
|
@ -18,21 +18,18 @@
|
|||
package io.bitsquare.gui;
|
||||
|
||||
import io.bitsquare.BitsquareException;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
import io.bitsquare.gui.util.ImageUtil;
|
||||
import io.bitsquare.util.Utilities;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* There is no JavaFX support yet, so we need to use AWT.
|
||||
|
@ -58,7 +55,7 @@ public class SystemTray {
|
|||
systemTray = new SystemTray(stage, onExit);
|
||||
}
|
||||
|
||||
public SystemTray(Stage stage, Runnable onExit) {
|
||||
private SystemTray(Stage stage, Runnable onExit) {
|
||||
this.stage = stage;
|
||||
this.onExit = onExit;
|
||||
init();
|
||||
|
@ -116,11 +113,11 @@ public class SystemTray {
|
|||
toggleShowHideItem.addActionListener(e -> {
|
||||
if (stage.isShowing()) {
|
||||
toggleShowHideItem.setLabel(SHOW_WINDOW_LABEL);
|
||||
Platform.runLater(stage::hide);
|
||||
UserThread.execute(stage::hide);
|
||||
}
|
||||
else {
|
||||
toggleShowHideItem.setLabel(HIDE_WINDOW_LABEL);
|
||||
Platform.runLater(stage::show);
|
||||
UserThread.execute(stage::show);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -17,23 +17,31 @@ upper border on tab: cfcfcf
|
|||
lower border on tab: b5b5b5
|
||||
upper gradient color on tab: d3d3d3
|
||||
lower gradient color on tab: dddddd
|
||||
bg color of non edit textFields: fafafa
|
||||
*/
|
||||
|
||||
/* Splash */
|
||||
|
||||
.root {
|
||||
-bs-grey: #666666;
|
||||
-bs-light-grey: #cccccc;
|
||||
-bs-bg-grey: #dddddd;
|
||||
-bs-bg-green: #00aa33;
|
||||
-bs-error-red: #dd0000;
|
||||
-bs-light-grey: #cccccc;
|
||||
-bs-content-bg-grey: #f4f4f4;
|
||||
-bs-very-light-grey: #f8f8f8;
|
||||
|
||||
-fx-accent: #0f87c3;
|
||||
-bs-green: #00aa33;
|
||||
-bs-error-red: #dd0000;
|
||||
|
||||
-bs-blue-transparent: #0f87c344;
|
||||
-bs-green-transparent: #00aa3344;
|
||||
|
||||
-fx-default-button: derive(-fx-accent, 95%);
|
||||
-fx-focus-color: -fx-accent;
|
||||
-fx-faint-focus-color: #0f87c322;
|
||||
-fx-selection-bar: derive(-fx-accent, 50%);
|
||||
}
|
||||
|
||||
/* Splash */
|
||||
#splash {
|
||||
-fx-background-color: #ffffff;
|
||||
}
|
||||
|
@ -44,7 +52,7 @@ lower gradient color on tab: dddddd
|
|||
|
||||
#splash-bitcoin-network-label {
|
||||
-fx-text-fill: -fx-accent;
|
||||
-fx-weight: bold;
|
||||
-fx-font-weight: bold;
|
||||
}
|
||||
|
||||
/* Main UI */
|
||||
|
@ -53,7 +61,7 @@ lower gradient color on tab: dddddd
|
|||
}
|
||||
|
||||
#content-pane {
|
||||
-fx-background-color: #f4f4f4;
|
||||
-fx-background-color: -bs-content-bg-grey;
|
||||
}
|
||||
|
||||
#footer-pane {
|
||||
|
@ -97,9 +105,8 @@ lower gradient color on tab: dddddd
|
|||
}
|
||||
|
||||
#nav-balance-label {
|
||||
-fx-font-weight: bold;
|
||||
-fx-font-size: 10;
|
||||
-fx-alignment: center;
|
||||
-fx-background-color: #dddddd;
|
||||
}
|
||||
|
||||
#nav-alert-label {
|
||||
|
@ -221,12 +228,9 @@ textfield */
|
|||
-fx-fill: black;
|
||||
}
|
||||
|
||||
.table-view .table-row-cell .copy-icon .text {
|
||||
-fx-fill: -fx-accent;
|
||||
}
|
||||
|
||||
.table-view .table-row-cell .copy-icon .text,
|
||||
.table-view .table-row-cell .copy-icon .text:hover {
|
||||
-fx-fill: black;
|
||||
-fx-fill: -fx-accent;
|
||||
}
|
||||
|
||||
.table-view .table-row-cell:selected .copy-icon .text {
|
||||
|
@ -237,22 +241,31 @@ textfield */
|
|||
-fx-fill: black;
|
||||
}
|
||||
|
||||
.table-view .table-row-cell .hyperlink .text {
|
||||
-fx-fill: -fx-accent;
|
||||
}
|
||||
|
||||
.table-view .table-row-cell .hyperlink .text:hover {
|
||||
-fx-fill: black;
|
||||
}
|
||||
|
||||
.table-view .table-row-cell:selected .hyperlink .text {
|
||||
-fx-fill: white;
|
||||
}
|
||||
|
||||
.table-view .table-row-cell .hyperlink .text {
|
||||
-fx-fill: -fx-accent;
|
||||
}
|
||||
|
||||
.table-view .table-row-cell .hyperlink .text:hover,
|
||||
.table-view .table-row-cell:selected .hyperlink .tooltip .text,
|
||||
.table-view .table-row-cell:selected .hyperlink .text:hover {
|
||||
-fx-fill: black;
|
||||
}
|
||||
|
||||
.table-view .table-row-cell .hyperlink:hover,
|
||||
.table-view .table-row-cell .hyperlink:visited,
|
||||
.table-view .table-row-cell .hyperlink:hover:visited {
|
||||
-fx-underline: false;
|
||||
}
|
||||
|
||||
.table-view .table-row-cell .hyperlink:focused {
|
||||
-fx-border-style: none;
|
||||
-fx-border-width: 0px;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Icons *
|
||||
|
@ -269,7 +282,7 @@ textfield */
|
|||
}
|
||||
|
||||
#clickable-icon:hover {
|
||||
-fx-text-fill: #666666;
|
||||
-fx-text-fill: -bs-grey;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
|
@ -364,12 +377,21 @@ textfield */
|
|||
-fx-border-insets: 0 0 0 -2;
|
||||
}
|
||||
|
||||
#currency-info-label-disabled {
|
||||
-fx-border-radius: 0 4 4 0;
|
||||
-fx-padding: 4 4 4 4;
|
||||
-fx-background-color: #e0e0e0;
|
||||
-fx-border-color: #e0e0e0;
|
||||
-fx-border-style: solid solid solid none;
|
||||
-fx-border-insets: 0 0 0 -2;
|
||||
}
|
||||
|
||||
#totals-separator {
|
||||
-fx-background: #AAAAAA;
|
||||
}
|
||||
|
||||
#payment-info {
|
||||
-fx-background-color: #f4f4f4;
|
||||
-fx-background-color: -bs-content-bg-grey;
|
||||
}
|
||||
|
||||
/* Account setup */
|
||||
|
@ -404,8 +426,8 @@ textfield */
|
|||
}
|
||||
|
||||
#wizard-item-background-deactivated {
|
||||
-fx-body-color: linear-gradient(to bottom, #f4f4f4, #F0F0F0);
|
||||
-fx-outer-border: linear-gradient(to bottom, #dddddd, #ccc);
|
||||
-fx-body-color: linear-gradient(to bottom, -bs-content-bg-grey, #F0F0F0);
|
||||
-fx-outer-border: linear-gradient(to bottom, -bs-bg-grey, #ccc);
|
||||
-fx-background-color: -fx-shadow-highlight-color,
|
||||
-fx-outer-border,
|
||||
-fx-inner-border,
|
||||
|
@ -426,7 +448,7 @@ textfield */
|
|||
}
|
||||
|
||||
#wizard-item-background-completed {
|
||||
-fx-body-color: linear-gradient(to bottom, #f4f4f4, #F0F0F0);
|
||||
-fx-body-color: linear-gradient(to bottom, -bs-content-bg-grey, #F0F0F0);
|
||||
-fx-outer-border: linear-gradient(to bottom, #99ba9c, #619865);
|
||||
-fx-background-color: -fx-shadow-highlight-color,
|
||||
-fx-outer-border,
|
||||
|
@ -456,8 +478,8 @@ textfield */
|
|||
}
|
||||
|
||||
#account-settings-item-background-disabled {
|
||||
-fx-body-color: linear-gradient(to bottom, #f4f4f4, #F0F0F0);
|
||||
-fx-outer-border: linear-gradient(to bottom, #dddddd, #ccc);
|
||||
-fx-body-color: linear-gradient(to bottom, -bs-content-bg-grey, #F0F0F0);
|
||||
-fx-outer-border: linear-gradient(to bottom, -bs-bg-grey, #ccc);
|
||||
-fx-background-color: -fx-shadow-highlight-color,
|
||||
-fx-outer-border,
|
||||
-fx-inner-border,
|
||||
|
@ -467,8 +489,8 @@ textfield */
|
|||
}
|
||||
|
||||
#account-settings-item-background-active {
|
||||
-fx-body-color: linear-gradient(to bottom, #f4f4f4, #F0F0F0);
|
||||
-fx-outer-border: linear-gradient(to bottom, #dddddd, #ccc);
|
||||
-fx-body-color: linear-gradient(to bottom, -bs-content-bg-grey, #F0F0F0);
|
||||
-fx-outer-border: linear-gradient(to bottom, -bs-bg-grey, #ccc);
|
||||
-fx-background-color: -fx-shadow-highlight-color,
|
||||
-fx-outer-border,
|
||||
-fx-inner-border,
|
||||
|
@ -491,8 +513,8 @@ textfield */
|
|||
/* Pending trades */
|
||||
#trade-wizard-item-background-disabled {
|
||||
-fx-text-fill: -bs-grey;
|
||||
-fx-body-color: linear-gradient(to bottom, #f4f4f4, #F0F0F0);
|
||||
-fx-outer-border: linear-gradient(to bottom, #dddddd, #ccc);
|
||||
-fx-body-color: linear-gradient(to bottom, -bs-content-bg-grey, #F0F0F0);
|
||||
-fx-outer-border: linear-gradient(to bottom, -bs-bg-grey, #ccc);
|
||||
-fx-background-color: -fx-shadow-highlight-color,
|
||||
-fx-outer-border,
|
||||
-fx-inner-border,
|
||||
|
@ -515,7 +537,7 @@ textfield */
|
|||
}
|
||||
|
||||
#trade-wizard-item-background-completed {
|
||||
-fx-body-color: linear-gradient(to bottom, #f4f4f4, #E1E9E1);
|
||||
-fx-body-color: linear-gradient(to bottom, -bs-content-bg-grey, #E1E9E1);
|
||||
-fx-outer-border: linear-gradient(to bottom, #99ba9c, #619865);
|
||||
-fx-background-color: -fx-shadow-highlight-color,
|
||||
-fx-outer-border,
|
||||
|
@ -530,19 +552,19 @@ textfield */
|
|||
-fx-font-weight: bold;
|
||||
-fx-font-size: 14;
|
||||
-fx-text-fill: -bs-grey;
|
||||
-fx-background-color: #f4f4f4;
|
||||
-fx-background-color: -bs-content-bg-grey;
|
||||
}
|
||||
|
||||
#titled-group-bg-label-active {
|
||||
-fx-font-weight: bold;
|
||||
-fx-font-size: 14;
|
||||
-fx-text-fill: -fx-accent;
|
||||
-fx-background-color: #f4f4f4;
|
||||
-fx-background-color: -bs-content-bg-grey;
|
||||
}
|
||||
|
||||
#titled-group-bg {
|
||||
-fx-body-color: linear-gradient(to bottom, #f4f4f4, #F0F0F0);
|
||||
-fx-outer-border: linear-gradient(to bottom, #dddddd, #ccc);
|
||||
-fx-body-color: linear-gradient(to bottom, -bs-content-bg-grey, #F0F0F0);
|
||||
-fx-outer-border: linear-gradient(to bottom, -bs-bg-grey, #ccc);
|
||||
-fx-background-color: -fx-shadow-highlight-color,
|
||||
-fx-outer-border,
|
||||
-fx-inner-border,
|
||||
|
@ -552,7 +574,7 @@ textfield */
|
|||
}
|
||||
|
||||
#titled-group-bg-active {
|
||||
-fx-body-color: linear-gradient(to bottom, #f4f4f4, #F0F0F0);
|
||||
-fx-body-color: linear-gradient(to bottom, -bs-content-bg-grey, #F0F0F0);
|
||||
-fx-outer-border: linear-gradient(to bottom, #9bbdc9, #57acc9);
|
||||
-fx-background-color: -fx-shadow-highlight-color,
|
||||
-fx-outer-border,
|
||||
|
@ -562,9 +584,160 @@ textfield */
|
|||
-fx-background-radius: 3px, 3px, 2px, 1px;
|
||||
}
|
||||
|
||||
/* TitledSeparator */
|
||||
#titled-separator:horizontal .line {
|
||||
-fx-border-color: transparent #f4f4f4 transparent #f4f4f4;
|
||||
-fx-background-color: transparent;
|
||||
-fx-border-width: 10px;
|
||||
/* TableGroupHeadline */
|
||||
#table-group-headline {
|
||||
-fx-background-color: -bs-content-bg-grey;
|
||||
-fx-background-insets: 10 0 -1 0, 0, 1, 2;
|
||||
-fx-background-radius: 3px, 3px, 2px, 1px;
|
||||
}
|
||||
|
||||
/* copied form modena.css text-input */
|
||||
#flowpane-checkboxes-bg {
|
||||
-fx-text-fill: -fx-text-inner-color;
|
||||
-fx-highlight-fill: derive(-fx-control-inner-background, -20%);
|
||||
-fx-highlight-text-fill: -fx-text-inner-color;
|
||||
-fx-prompt-text-fill: derive(-fx-control-inner-background, -30%);
|
||||
-fx-background-color: linear-gradient(to bottom, derive(-fx-text-box-border, -10%), -fx-text-box-border),
|
||||
linear-gradient(from 0px 0px to 0px 5px, derive(-fx-control-inner-background, -9%), -fx-control-inner-background);
|
||||
-fx-background-insets: 0, 1;
|
||||
-fx-background-radius: 3, 2;
|
||||
-fx-padding: 0.333333em 0.583em 0.333333em 0.583em; /* 4 7 4 7 */
|
||||
}
|
||||
|
||||
#flowpane-checkboxes-non-editable-bg {
|
||||
-fx-text-fill: -fx-text-inner-color;
|
||||
-fx-highlight-fill: derive(-fx-control-inner-background, -20%);
|
||||
-fx-highlight-text-fill: -fx-text-inner-color;
|
||||
-fx-prompt-text-fill: derive(-fx-control-inner-background, -30%);
|
||||
-fx-background-color: linear-gradient(to bottom, derive(-fx-text-box-border, -10%), -fx-text-box-border),
|
||||
linear-gradient(from 0px 0px to 0px 5px, derive(-fx-control-inner-background, -9%), -bs-very-light-grey);
|
||||
-fx-background-insets: 0, 1;
|
||||
-fx-background-radius: 3, 2;
|
||||
-fx-padding: 0.333333em 0.583em 0.333333em 0.583em; /* 4 7 4 7 */
|
||||
}
|
||||
|
||||
/* message-list-view*/
|
||||
#message-list-view.list-view:focused > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected {
|
||||
-fx-background-color: white;
|
||||
}
|
||||
|
||||
#message-list-view.list-view > .virtual-flow > .clipped-container > .sheet > .list-cell {
|
||||
-fx-background-color: white;
|
||||
}
|
||||
|
||||
#message-list-view.list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled {
|
||||
-fx-background-color: white;
|
||||
}
|
||||
|
||||
#message-list-view.list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected {
|
||||
-fx-background-color: white;
|
||||
}
|
||||
|
||||
#message-list-view.list-view:focused > .virtual-flow > .clipped-container > .sheet > .list-cell {
|
||||
-fx-background-color: white;
|
||||
}
|
||||
|
||||
#message-list-view.list-cell {
|
||||
-fx-padding: 0.25em 0.583em 0.25em 0.583em;
|
||||
}
|
||||
|
||||
#message-list-view.list-view {
|
||||
-fx-background-color: -fx-box-border, -fx-control-inner-background;
|
||||
-fx-background-insets: 0, 1;
|
||||
-fx-padding: 1;
|
||||
}
|
||||
|
||||
#message-list-view.list-view:focused {
|
||||
-fx-background-color: -fx-box-border, -fx-control-inner-background;
|
||||
-fx-background-insets: 0, 1;
|
||||
-fx-padding: 1;
|
||||
}
|
||||
|
||||
/* bubble */
|
||||
#message-bubble-green {
|
||||
-fx-background-color: -bs-green;
|
||||
-fx-background-radius: 10 10 10 10;
|
||||
}
|
||||
|
||||
#message-bubble-blue {
|
||||
-fx-background-color: -fx-accent;
|
||||
-fx-background-radius: 10 10 10 10;
|
||||
}
|
||||
|
||||
#message-bubble-grey {
|
||||
-fx-background-color: -bs-light-grey;
|
||||
-fx-background-radius: 10 10 10 10;
|
||||
}
|
||||
|
||||
.attachment-icon {
|
||||
-fx-text-fill: white;
|
||||
-fx-cursor: hand;
|
||||
}
|
||||
|
||||
.attachment-icon-black {
|
||||
-fx-text-fill: black;
|
||||
-fx-cursor: hand;
|
||||
}
|
||||
|
||||
/********************************************************************************************************************
|
||||
*
|
||||
* Text validation
|
||||
*
|
||||
********************************************************************************************************************/
|
||||
|
||||
.text-field.validation_error, .text-area.validation_error .content, .date-picker.validation_error > .text-field {
|
||||
-fx-background-color: red,
|
||||
linear-gradient(
|
||||
to bottom,
|
||||
derive(#ff8986, 40%) 5%,
|
||||
derive(#ff8986, 70%) 40%
|
||||
);
|
||||
}
|
||||
|
||||
/********************************************************************************************************************
|
||||
*
|
||||
* Chart
|
||||
*
|
||||
********************************************************************************************************************/
|
||||
|
||||
#charts .chart-legend {
|
||||
-fx-font-size: 14;
|
||||
-fx-alignment: center;
|
||||
}
|
||||
|
||||
#charts .axis {
|
||||
-fx-tick-label-fill: black;
|
||||
}
|
||||
|
||||
#charts .chart-plot-background {
|
||||
-fx-background-color: white;
|
||||
}
|
||||
|
||||
#charts .default-color0.chart-area-symbol {
|
||||
-fx-background-color: -bs-green, white;
|
||||
}
|
||||
|
||||
#charts .default-color1.chart-area-symbol {
|
||||
-fx-background-color: -fx-accent, white;
|
||||
}
|
||||
|
||||
#charts .default-color0.chart-series-area-line {
|
||||
-fx-stroke: -bs-green;
|
||||
}
|
||||
|
||||
#charts .default-color1.chart-series-area-line {
|
||||
-fx-stroke: -fx-accent;
|
||||
}
|
||||
|
||||
#charts .default-color0.chart-series-area-fill {
|
||||
-fx-fill: -bs-green-transparent;
|
||||
}
|
||||
|
||||
#charts .default-color1.chart-series-area-fill {
|
||||
-fx-fill: -bs-blue-transparent;
|
||||
}
|
||||
|
||||
#charts .axis-label {
|
||||
-fx-font-size: 16;
|
||||
-fx-alignment: center;
|
||||
}
|
||||
|
|
|
@ -22,18 +22,13 @@ import io.bitsquare.gui.common.view.FxmlView;
|
|||
import io.bitsquare.gui.common.view.View;
|
||||
import io.bitsquare.gui.common.view.ViewFactory;
|
||||
import io.bitsquare.gui.common.view.ViewLoader;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import java.util.ResourceBundle;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javafx.fxml.FXMLLoader;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.springframework.core.annotation.AnnotationUtils.getDefaultValue;
|
||||
|
@ -92,7 +87,7 @@ public class FxmlViewLoader implements ViewLoader {
|
|||
}
|
||||
}
|
||||
|
||||
public View loadFromFxml(URL fxmlUrl) {
|
||||
private View loadFromFxml(URL fxmlUrl) {
|
||||
checkNotNull(fxmlUrl, "FXML URL must not be null");
|
||||
try {
|
||||
FXMLLoader loader = new FXMLLoader(fxmlUrl, resourceBundle);
|
||||
|
|
|
@ -19,18 +19,18 @@ package io.bitsquare.gui.common.model;
|
|||
|
||||
public interface Activatable {
|
||||
|
||||
void activate();
|
||||
void _activate();
|
||||
|
||||
void deactivate();
|
||||
void _deactivate();
|
||||
|
||||
|
||||
Activatable NOOP_INSTANCE = new Activatable() {
|
||||
@Override
|
||||
public void activate() {
|
||||
public void _activate() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
public void _deactivate() {
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -17,27 +17,27 @@
|
|||
|
||||
package io.bitsquare.gui.common.model;
|
||||
|
||||
public abstract class ActivatableWithDataModel<D extends Activatable> extends WithDataModel<D> implements Activatable {
|
||||
public class ActivatableWithDataModel<D extends Activatable> extends WithDataModel<D> implements Activatable {
|
||||
|
||||
public ActivatableWithDataModel(D dataModel) {
|
||||
super(dataModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void activate() {
|
||||
dataModel.activate();
|
||||
this.doActivate();
|
||||
public final void _activate() {
|
||||
dataModel._activate();
|
||||
this.activate();
|
||||
}
|
||||
|
||||
protected void doActivate() {
|
||||
protected void activate() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void deactivate() {
|
||||
dataModel.deactivate();
|
||||
this.doDeactivate();
|
||||
public final void _deactivate() {
|
||||
dataModel._deactivate();
|
||||
this.deactivate();
|
||||
}
|
||||
|
||||
protected void doDeactivate() {
|
||||
protected void deactivate() {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,11 +17,15 @@
|
|||
|
||||
package io.bitsquare.gui.common.model;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
public abstract class WithDataModel<D> {
|
||||
public class WithDataModel<D> {
|
||||
protected final Logger log = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
protected final D dataModel;
|
||||
public final D dataModel;
|
||||
|
||||
protected WithDataModel(D dataModel) {
|
||||
this.dataModel = checkNotNull(dataModel, "Delegate object must not be null");
|
||||
|
|
|
@ -18,8 +18,7 @@
|
|||
package io.bitsquare.gui.common.view;
|
||||
|
||||
import io.bitsquare.gui.common.model.Activatable;
|
||||
|
||||
import javafx.scene.*;
|
||||
import javafx.scene.Node;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
|
@ -34,20 +33,17 @@ public abstract class ActivatableViewAndModel<R extends Node, M extends Activata
|
|||
}
|
||||
|
||||
@Override
|
||||
public final void activate() {
|
||||
model.activate();
|
||||
this.doActivate();
|
||||
}
|
||||
|
||||
protected void doActivate() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void deactivate() {
|
||||
model.deactivate();
|
||||
this.doDeactivate();
|
||||
}
|
||||
|
||||
protected void doDeactivate() {
|
||||
protected void prepareInitialize() {
|
||||
if (root != null) {
|
||||
root.sceneProperty().addListener((ov, oldValue, newValue) -> {
|
||||
if (oldValue == null && newValue != null) {
|
||||
model._activate();
|
||||
activate();
|
||||
} else if (oldValue != null && newValue == null) {
|
||||
model._deactivate();
|
||||
deactivate();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ package io.bitsquare.gui.common.view;
|
|||
import io.bitsquare.app.Version;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
@ -30,7 +29,7 @@ public class ViewPath extends ArrayList<Class<? extends View>> implements Serial
|
|||
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
||||
private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
|
||||
|
||||
public ViewPath() {
|
||||
private ViewPath() {
|
||||
}
|
||||
|
||||
public ViewPath(Collection<? extends Class<? extends View>> c) {
|
||||
|
|
|
@ -17,37 +17,34 @@
|
|||
|
||||
package io.bitsquare.gui.components;
|
||||
|
||||
import io.bitsquare.gui.OverlayManager;
|
||||
import io.bitsquare.gui.util.GUIUtil;
|
||||
import io.bitsquare.util.Utilities;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.uri.BitcoinURI;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import de.jensd.fx.fontawesome.AwesomeDude;
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
import io.bitsquare.gui.main.MainView;
|
||||
import io.bitsquare.gui.popups.Popup;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.image.*;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.stage.Window;
|
||||
|
||||
import de.jensd.fx.fontawesome.AwesomeDude;
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
|
||||
import net.glxn.qrgen.QRCode;
|
||||
import net.glxn.qrgen.image.ImageType;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.uri.BitcoinURI;
|
||||
import org.controlsfx.control.PopOver;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.URI;
|
||||
|
||||
public class AddressTextField extends AnchorPane {
|
||||
private static final Logger log = LoggerFactory.getLogger(AddressTextField.class);
|
||||
|
||||
|
@ -71,8 +68,8 @@ public class AddressTextField extends AnchorPane {
|
|||
Utilities.openURI(URI.create(getBitcoinURI()));
|
||||
} catch (Exception e) {
|
||||
log.warn(e.getMessage());
|
||||
Popups.openWarningPopup("Warning", "Opening a system Bitcoin wallet application has failed. " +
|
||||
"Perhaps you don't have one installed?");
|
||||
new Popup().warning("Opening a system Bitcoin wallet application has failed. " +
|
||||
"Perhaps you don't have one installed?").show();
|
||||
}
|
||||
});
|
||||
textField.focusTraversableProperty().set(focusTraversableProperty().get());
|
||||
|
@ -85,7 +82,7 @@ public class AddressTextField extends AnchorPane {
|
|||
AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY);
|
||||
copyIcon.setOnMouseClicked(e -> {
|
||||
if (address.get() != null && address.get().length() > 0)
|
||||
GUIUtil.copyToClipboard(address.get());
|
||||
Utilities.copyToClipboard(address.get());
|
||||
});
|
||||
|
||||
Label qrCode = new Label();
|
||||
|
@ -111,15 +108,13 @@ public class AddressTextField extends AnchorPane {
|
|||
PopOver popOver = new PopOver(pane);
|
||||
popOver.setDetachedTitle("Scan QR code for this address");
|
||||
popOver.setDetached(true);
|
||||
popOver.setOnHiding(windowEvent -> {
|
||||
OverlayManager.removeBlurContent();
|
||||
});
|
||||
popOver.setOnHiding(windowEvent -> MainView.removeBlur());
|
||||
|
||||
Window window = getScene().getWindow();
|
||||
double x = Math.round(window.getX() + (window.getWidth() - 320) / 2);
|
||||
double y = Math.round(window.getY() + (window.getHeight() - 240) / 2);
|
||||
popOver.show(getScene().getWindow(), x, y);
|
||||
OverlayManager.blurContent();
|
||||
MainView.blur();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -20,19 +20,24 @@ package io.bitsquare.gui.components;
|
|||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.btc.listeners.BalanceListener;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.effect.BlurType;
|
||||
import javafx.scene.effect.DropShadow;
|
||||
import javafx.scene.effect.Effect;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.paint.Color;
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.effect.*;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.paint.*;
|
||||
|
||||
public class BalanceTextField extends AnchorPane {
|
||||
|
||||
private final TextField textField;
|
||||
private static WalletService walletService;
|
||||
|
||||
public static void setWalletService(WalletService walletService) {
|
||||
BalanceTextField.walletService = walletService;
|
||||
}
|
||||
|
||||
private final TextField textField;
|
||||
private final Effect fundedEffect = new DropShadow(BlurType.THREE_PASS_BOX, Color.GREEN, 4, 0.0, 0, 0);
|
||||
private final Effect notFundedEffect = new DropShadow(BlurType.THREE_PASS_BOX, Color.ORANGERED, 4, 0.0, 0, 0);
|
||||
private BSFormatter formatter;
|
||||
|
@ -53,7 +58,7 @@ public class BalanceTextField extends AnchorPane {
|
|||
getChildren().addAll(textField);
|
||||
}
|
||||
|
||||
public void setup(WalletService walletService, Address address, BSFormatter formatter) {
|
||||
public void setup(Address address, BSFormatter formatter) {
|
||||
this.formatter = formatter;
|
||||
|
||||
walletService.addBalanceListener(new BalanceListener(address) {
|
||||
|
|
|
@ -22,18 +22,25 @@ import io.bitsquare.btc.listeners.AddressConfidenceListener;
|
|||
import io.bitsquare.btc.listeners.BalanceListener;
|
||||
import io.bitsquare.gui.components.confidence.ConfidenceProgressIndicator;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.effect.BlurType;
|
||||
import javafx.scene.effect.DropShadow;
|
||||
import javafx.scene.effect.Effect;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.paint.Color;
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.TransactionConfidence;
|
||||
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.effect.*;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.paint.*;
|
||||
|
||||
public class BalanceWithConfirmationTextField extends AnchorPane {
|
||||
|
||||
private static WalletService walletService;
|
||||
|
||||
public static void setWalletService(WalletService walletService) {
|
||||
BalanceWithConfirmationTextField.walletService = walletService;
|
||||
}
|
||||
|
||||
private final TextField textField;
|
||||
private final Tooltip progressIndicatorTooltip;
|
||||
private final ConfidenceProgressIndicator progressIndicator;
|
||||
|
@ -70,7 +77,7 @@ public class BalanceWithConfirmationTextField extends AnchorPane {
|
|||
getChildren().addAll(textField, progressIndicator);
|
||||
}
|
||||
|
||||
public void setup(WalletService walletService, Address address, BSFormatter formatter) {
|
||||
public void setup(Address address, BSFormatter formatter) {
|
||||
this.formatter = formatter;
|
||||
walletService.addAddressConfidenceListener(new AddressConfidenceListener(address) {
|
||||
@Override
|
||||
|
|
|
@ -17,27 +17,22 @@
|
|||
|
||||
package io.bitsquare.gui.components;
|
||||
|
||||
import de.jensd.fx.fontawesome.AwesomeDude;
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.locale.BSResources;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.IntegerProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.VPos;
|
||||
import javafx.scene.*;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.text.*;
|
||||
|
||||
import de.jensd.fx.fontawesome.AwesomeDude;
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.control.Hyperlink;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.OverrunStyle;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.text.TextFlow;
|
||||
|
||||
/**
|
||||
* Convenience Component for info icon, info text and link display in a GridPane.
|
||||
|
@ -94,7 +89,7 @@ public class InfoDisplay extends Parent {
|
|||
testLabel.widthProperty().addListener((ov, o, n) -> {
|
||||
useReadMore = (double) n > textFlow.getWidth();
|
||||
link.setText(BSResources.get(useReadMore ? "shared.readMore" : "shared.openHelp"));
|
||||
Platform.runLater(() -> textFlow.getChildren().setAll(label, link));
|
||||
UserThread.execute(() -> textFlow.getChildren().setAll(label, link));
|
||||
});
|
||||
|
||||
// update the width when the window gets resized
|
||||
|
@ -134,7 +129,7 @@ public class InfoDisplay extends Parent {
|
|||
newValue.getWindow().widthProperty().addListener(listener);
|
||||
// localToScene does deliver 0 instead of the correct x position when scene propery gets set,
|
||||
// so we delay for 1 render cycle
|
||||
Platform.runLater(() -> {
|
||||
UserThread.execute(() -> {
|
||||
label.setVisible(true);
|
||||
label.prefWidthProperty().unbind();
|
||||
label.setPrefWidth(newValue.getWindow().getWidth() - localToScene(0, 0).getX() - 35);
|
||||
|
@ -150,7 +145,7 @@ public class InfoDisplay extends Parent {
|
|||
|
||||
public void setText(String text) {
|
||||
this.text.set(text);
|
||||
Platform.runLater(() -> {
|
||||
UserThread.execute(() -> {
|
||||
if (getScene() != null) {
|
||||
label.setVisible(true);
|
||||
label.prefWidthProperty().unbind();
|
||||
|
|
|
@ -1,219 +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 io.bitsquare.gui.OverlayManager;
|
||||
import io.bitsquare.locale.BSResources;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import org.controlsfx.control.action.AbstractAction;
|
||||
import org.controlsfx.control.action.Action;
|
||||
import org.controlsfx.dialog.Dialog;
|
||||
import org.controlsfx.dialog.Dialogs;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Popups {
|
||||
private static final Logger log = LoggerFactory.getLogger(Popups.class);
|
||||
|
||||
public static Stage primaryStage;
|
||||
|
||||
public static void removeBlurContent() {
|
||||
OverlayManager.removeBlurContent();
|
||||
}
|
||||
|
||||
// Information
|
||||
public static void openInfoPopup(String message) {
|
||||
openInfoPopup(null, message);
|
||||
}
|
||||
|
||||
public static void openInfoPopup(String masthead, String message) {
|
||||
OverlayManager.blurContent();
|
||||
List<Action> actions = new ArrayList<>();
|
||||
actions.add(new AbstractAction(BSResources.get("shared.close")) {
|
||||
@Override
|
||||
public void handle(ActionEvent actionEvent) {
|
||||
getProperties().put("type", "CLOSE");
|
||||
Dialog.Actions.CLOSE.handle(actionEvent);
|
||||
}
|
||||
});
|
||||
openInfoPopup(masthead, message, actions);
|
||||
}
|
||||
|
||||
public static void openInfoPopup(String masthead, String message, List<Action> actions) {
|
||||
Dialogs.create()
|
||||
.owner(primaryStage)
|
||||
.message(message)
|
||||
.masthead(masthead)
|
||||
.actions(actions)
|
||||
.showInformation();
|
||||
removeBlurContent();
|
||||
}
|
||||
|
||||
// Confirm
|
||||
public static Action openConfirmPopup(String title, String message) {
|
||||
return openConfirmPopup(title, null, message);
|
||||
}
|
||||
|
||||
public static Action openConfirmPopup(String title, String masthead, String message) {
|
||||
OverlayManager.blurContent();
|
||||
List<Action> actions = new ArrayList<>();
|
||||
actions.add(new AbstractAction(BSResources.get("shared.ok")) {
|
||||
@Override
|
||||
public void handle(ActionEvent actionEvent) {
|
||||
getProperties().put("type", "OK");
|
||||
Dialog.Actions.OK.handle(actionEvent);
|
||||
}
|
||||
});
|
||||
actions.add(new AbstractAction(BSResources.get("shared.cancel")) {
|
||||
@Override
|
||||
public void handle(ActionEvent actionEvent) {
|
||||
getProperties().put("type", "CANCEL");
|
||||
Dialog.Actions.CANCEL.handle(actionEvent);
|
||||
}
|
||||
});
|
||||
return openConfirmPopup(title, masthead, message, actions);
|
||||
}
|
||||
|
||||
public static Action openConfirmPopup(String title, String masthead, String message, List<Action> actions) {
|
||||
return Dialogs.create()
|
||||
.owner(primaryStage)
|
||||
.title(title)
|
||||
.message(message)
|
||||
.masthead(masthead)
|
||||
.actions(actions)
|
||||
.showConfirm();
|
||||
}
|
||||
|
||||
// Warning
|
||||
public static void openWarningPopup(String message) {
|
||||
openWarningPopup("Warning", message);
|
||||
}
|
||||
|
||||
public static void openWarningPopup(String title, String message) {
|
||||
openWarningPopup(title, null, message);
|
||||
}
|
||||
|
||||
public static void openWarningPopup(String title, String masthead, String message) {
|
||||
OverlayManager.blurContent();
|
||||
List<Action> actions = new ArrayList<>();
|
||||
actions.add(new AbstractAction(BSResources.get("shared.close")) {
|
||||
@Override
|
||||
public void handle(ActionEvent actionEvent) {
|
||||
getProperties().put("type", "CLOSE");
|
||||
Dialog.Actions.CLOSE.handle(actionEvent);
|
||||
}
|
||||
});
|
||||
openWarningPopup(title, masthead, message, actions);
|
||||
}
|
||||
|
||||
private static void openWarningPopup(String title, String masthead, String message, List<Action> actions) {
|
||||
Dialogs.create()
|
||||
.owner(primaryStage)
|
||||
.title(title)
|
||||
.message(message)
|
||||
.masthead(masthead)
|
||||
.actions(actions)
|
||||
.showWarning();
|
||||
removeBlurContent();
|
||||
}
|
||||
|
||||
// Error
|
||||
public static void openErrorPopup(String message) {
|
||||
openErrorPopup("Error", message);
|
||||
}
|
||||
|
||||
public static void openErrorPopup(String title, String message) {
|
||||
openErrorPopup(title, null, message);
|
||||
}
|
||||
|
||||
public static void openErrorPopup(String title, String masthead, String message) {
|
||||
OverlayManager.blurContent();
|
||||
List<Action> actions = new ArrayList<>();
|
||||
actions.add(new AbstractAction(BSResources.get("shared.close")) {
|
||||
@Override
|
||||
public void handle(ActionEvent actionEvent) {
|
||||
getProperties().put("type", "CLOSE");
|
||||
Dialog.Actions.CLOSE.handle(actionEvent);
|
||||
}
|
||||
});
|
||||
openErrorPopup(title, masthead, message, actions);
|
||||
}
|
||||
|
||||
private static void openErrorPopup(String title, String masthead, String message, List<Action> actions) {
|
||||
Dialogs.create()
|
||||
.owner(primaryStage)
|
||||
.title(title)
|
||||
.message(message)
|
||||
.masthead(masthead)
|
||||
.actions(actions)
|
||||
.showError();
|
||||
removeBlurContent();
|
||||
}
|
||||
|
||||
// Exception
|
||||
public static void openExceptionPopup(Throwable throwable) {
|
||||
openExceptionPopup(throwable, "Exception", "That should not have happened...");
|
||||
}
|
||||
|
||||
public static void openExceptionPopup(Throwable throwable, String title, String message) {
|
||||
openExceptionPopup(throwable, title, null, message);
|
||||
}
|
||||
|
||||
private static void openExceptionPopup(Throwable throwable, String title, String masthead, String message) {
|
||||
OverlayManager.blurContent();
|
||||
List<Action> actions = new ArrayList<>();
|
||||
actions.add(new AbstractAction(BSResources.get("shared.close")) {
|
||||
@Override
|
||||
public void handle(ActionEvent actionEvent) {
|
||||
getProperties().put("type", "CLOSE");
|
||||
Dialog.Actions.CLOSE.handle(actionEvent);
|
||||
}
|
||||
});
|
||||
Dialogs.create()
|
||||
.owner(primaryStage)
|
||||
.title(title)
|
||||
.message(message)
|
||||
.masthead(masthead)
|
||||
.actions(actions)
|
||||
.showException(throwable);
|
||||
removeBlurContent();
|
||||
}
|
||||
|
||||
|
||||
// custom
|
||||
public static void openInsufficientMoneyPopup() {
|
||||
openWarningPopup("Warning", "There is not enough money available",
|
||||
"Please pay in first to your wallet.");
|
||||
}
|
||||
|
||||
public static boolean isOK(Action response) {
|
||||
return response.getProperties().get("type").equals("OK");
|
||||
}
|
||||
|
||||
public static boolean isYes(Action response) {
|
||||
return response.getProperties().get("type").equals("YES");
|
||||
}
|
||||
|
||||
}
|
|
@ -17,11 +17,10 @@
|
|||
|
||||
package io.bitsquare.gui.components;
|
||||
|
||||
import io.bitsquare.util.Utilities;
|
||||
|
||||
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
|
||||
|
|
|
@ -17,15 +17,15 @@
|
|||
|
||||
package io.bitsquare.gui.components;
|
||||
|
||||
import io.bitsquare.gui.util.GUIUtil;
|
||||
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.*;
|
||||
|
||||
import de.jensd.fx.fontawesome.AwesomeDude;
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
|
||||
public class TextFieldWithCopyIcon extends AnchorPane {
|
||||
|
||||
|
@ -45,7 +45,7 @@ public class TextFieldWithCopyIcon extends AnchorPane {
|
|||
AnchorPane.setRightAnchor(copyIcon, 0.0);
|
||||
copyIcon.setOnMouseClicked(e -> {
|
||||
if (getText() != null && getText().length() > 0)
|
||||
GUIUtil.copyToClipboard(getText());
|
||||
Utilities.copyToClipboard(getText());
|
||||
});
|
||||
TextField txIdLabel = new TextField();
|
||||
txIdLabel.setEditable(false);
|
||||
|
@ -63,7 +63,7 @@ public class TextFieldWithCopyIcon extends AnchorPane {
|
|||
// Getter/Setter
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public String getText() {
|
||||
private String getText() {
|
||||
return text.get();
|
||||
}
|
||||
|
||||
|
|
|
@ -20,8 +20,9 @@ package io.bitsquare.gui.components;
|
|||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.Pane;
|
||||
|
||||
public class TitledGroupBg extends Pane {
|
||||
|
||||
|
@ -50,7 +51,7 @@ public class TitledGroupBg extends Pane {
|
|||
label.setId("titled-group-bg-label");
|
||||
}
|
||||
|
||||
public void setActive() {
|
||||
private void setActive() {
|
||||
setId("titled-group-bg-active");
|
||||
label.setId("titled-group-bg-label-active");
|
||||
}
|
||||
|
|
|
@ -1,78 +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 javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.*;
|
||||
|
||||
public class TitledSeparator extends Pane {
|
||||
|
||||
private final Label label;
|
||||
private final StringProperty text = new SimpleStringProperty();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public TitledSeparator() {
|
||||
GridPane.setMargin(this, new Insets(-10, -10, -10, -10));
|
||||
GridPane.setColumnSpan(this, 2);
|
||||
|
||||
Separator separator = new Separator();
|
||||
separator.prefWidthProperty().bind(widthProperty());
|
||||
separator.setLayoutX(0);
|
||||
separator.setLayoutY(6);
|
||||
separator.setId("titled-separator");
|
||||
|
||||
label = new Label();
|
||||
label.textProperty().bind(text);
|
||||
label.setLayoutX(8);
|
||||
label.setLayoutY(-8);
|
||||
label.setPadding(new Insets(0, 7, 0, 5));
|
||||
setActive();
|
||||
getChildren().addAll(separator, label);
|
||||
|
||||
}
|
||||
|
||||
public void setInactive() {
|
||||
setId("titled-group-bg");
|
||||
label.setId("titled-group-bg-label");
|
||||
}
|
||||
|
||||
public void setActive() {
|
||||
setId("titled-group-bg-active");
|
||||
label.setId("titled-group-bg-label-active");
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text.get();
|
||||
}
|
||||
|
||||
public StringProperty textProperty() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text.set(text);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -17,32 +17,42 @@
|
|||
|
||||
package io.bitsquare.gui.components;
|
||||
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.btc.listeners.TxConfidenceListener;
|
||||
import io.bitsquare.gui.components.confidence.ConfidenceProgressIndicator;
|
||||
import io.bitsquare.gui.util.GUIUtil;
|
||||
import io.bitsquare.util.Utilities;
|
||||
|
||||
import org.bitcoinj.core.TransactionConfidence;
|
||||
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.*;
|
||||
|
||||
import de.jensd.fx.fontawesome.AwesomeDude;
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.btc.listeners.TxConfidenceListener;
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
import io.bitsquare.gui.components.confidence.ConfidenceProgressIndicator;
|
||||
import io.bitsquare.gui.popups.Popup;
|
||||
import io.bitsquare.user.Preferences;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import org.bitcoinj.core.TransactionConfidence;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class TxIdTextField extends AnchorPane {
|
||||
private static final Logger log = LoggerFactory.getLogger(TxIdTextField.class);
|
||||
|
||||
private static Preferences preferences;
|
||||
|
||||
public static void setPreferences(Preferences preferences) {
|
||||
TxIdTextField.preferences = preferences;
|
||||
}
|
||||
|
||||
private static WalletService walletService;
|
||||
|
||||
public static void setWalletService(WalletService walletService) {
|
||||
TxIdTextField.walletService = walletService;
|
||||
}
|
||||
|
||||
private final TextField textField;
|
||||
private final Tooltip progressIndicatorTooltip;
|
||||
private final ConfidenceProgressIndicator progressIndicator;
|
||||
private final Label copyIcon;
|
||||
private TxConfidenceListener txConfidenceListener;
|
||||
private WalletService walletService;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -80,8 +90,8 @@ public class TxIdTextField extends AnchorPane {
|
|||
getChildren().addAll(textField, copyIcon, progressIndicator);
|
||||
}
|
||||
|
||||
public void setup(WalletService walletService, String txID) {
|
||||
this.walletService = walletService;
|
||||
|
||||
public void setup(String txID) {
|
||||
if (txConfidenceListener != null)
|
||||
walletService.removeTxConfidenceListener(txConfidenceListener);
|
||||
|
||||
|
@ -97,18 +107,16 @@ public class TxIdTextField extends AnchorPane {
|
|||
textField.setText(txID);
|
||||
textField.setOnMouseClicked(mouseEvent -> {
|
||||
try {
|
||||
// TODO get the url form the app preferences
|
||||
Utilities.openWebPage("https://www.biteasy.com/testnet/transactions/" + txID);
|
||||
// https://insight.bitpay.com/ only mainnet?
|
||||
//Utilities.openURL("https://blockchain.info/tx/" + txID);
|
||||
if (preferences != null)
|
||||
Utilities.openWebPage(preferences.getBlockChainExplorer().txUrl + txID);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
Popups.openWarningPopup("Warning", "Opening browser failed. Please check your internet " +
|
||||
"connection.");
|
||||
new Popup().warning("Opening browser failed. Please check your internet " +
|
||||
"connection.").show();
|
||||
}
|
||||
});
|
||||
|
||||
copyIcon.setOnMouseClicked(e -> GUIUtil.copyToClipboard(txID));
|
||||
copyIcon.setOnMouseClicked(e -> Utilities.copyToClipboard(txID));
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
|
|
|
@ -26,14 +26,14 @@
|
|||
package io.bitsquare.gui.components.confidence;
|
||||
|
||||
import io.bitsquare.gui.components.confidence.skin.ConfidenceProgressIndicatorSkin;
|
||||
|
||||
import javafx.beans.property.DoubleProperty;
|
||||
import javafx.beans.property.DoublePropertyBase;
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.css.StyleableProperty;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.control.Control;
|
||||
import javafx.scene.control.Skin;
|
||||
|
||||
// TODO Copied form OpenJFX, check license issues and way how we integrated it
|
||||
// We changed behaviour which was not exposed via APIs
|
||||
|
@ -45,22 +45,22 @@ import javafx.scene.control.*;
|
|||
* <p>
|
||||
* ProgressIndicator sets focusTraversable to false.
|
||||
* </p>
|
||||
* <p/>
|
||||
* <p/>
|
||||
* <p>
|
||||
* <p>
|
||||
* This first example creates a ProgressIndicator with an indeterminate value :
|
||||
* <pre><code>
|
||||
* import javafx.scene.control.ProgressIndicator;
|
||||
* ProgressIndicator p1 = new ProgressIndicator();
|
||||
* </code></pre>
|
||||
* <p/>
|
||||
* <p/>
|
||||
* <p>
|
||||
* <p>
|
||||
* This next example creates a ProgressIndicator which is 25% complete :
|
||||
* <pre><code>
|
||||
* import javafx.scene.control.ProgressIndicator;
|
||||
* ProgressIndicator p2 = new ProgressIndicator();
|
||||
* p2.setProgress(0.25F);
|
||||
* </code></pre>
|
||||
* <p/>
|
||||
* <p>
|
||||
* Implementation of ProgressIndicator According to JavaFX UI Control API Specification
|
||||
*
|
||||
* @since JavaFX 2.0
|
||||
|
@ -83,7 +83,7 @@ public class ConfidenceProgressIndicator extends Control {
|
|||
**************************************************************************/
|
||||
/**
|
||||
* Initialize the style class to 'progress-indicator'.
|
||||
* <p/>
|
||||
* <p>
|
||||
* This is the selector class from which CSS can be used to style
|
||||
* this control.
|
||||
*/
|
||||
|
|
|
@ -17,11 +17,12 @@
|
|||
|
||||
package io.bitsquare.gui.components.processbar;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javafx.beans.property.IntegerProperty;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.control.Control;
|
||||
import javafx.scene.control.Skin;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ProcessStepBar<T> extends Control {
|
||||
|
||||
|
@ -65,7 +66,7 @@ public class ProcessStepBar<T> extends Control {
|
|||
((ProcessStepBarSkin) getSkin()).setProcessStepItems(processStepItems);
|
||||
}
|
||||
|
||||
public void setSelectedIndex(int selectedIndex) {
|
||||
private void setSelectedIndex(int selectedIndex) {
|
||||
this.selectedIndex.set(selectedIndex);
|
||||
if (getSkin() != null)
|
||||
((ProcessStepBarSkin) getSkin()).setSelectedIndex(selectedIndex);
|
||||
|
|
|
@ -14,6 +14,10 @@
|
|||
-fx-image: url("../../../images/info.png");
|
||||
}
|
||||
|
||||
#light_close {
|
||||
-fx-image: url("../../../images/light_close.png");
|
||||
}
|
||||
|
||||
#image-alert-round {
|
||||
-fx-image: url("../../../images/alert_round.png");
|
||||
}
|
||||
|
@ -47,12 +51,12 @@
|
|||
}
|
||||
|
||||
/* navigation buttons */
|
||||
#image-nav-home {
|
||||
-fx-image: url("../../../images/nav/home.png");
|
||||
#image-nav-market {
|
||||
-fx-image: url("../../../images/nav/market.png");
|
||||
}
|
||||
|
||||
#image-nav-home-active {
|
||||
-fx-image: url("../../../images/nav/home_active.png");
|
||||
#image-nav-market-active {
|
||||
-fx-image: url("../../../images/nav/market_active.png");
|
||||
}
|
||||
|
||||
#image-nav-buyoffer {
|
||||
|
@ -87,12 +91,12 @@
|
|||
-fx-image: url("../../../images/nav/funds_active.png");
|
||||
}
|
||||
|
||||
#image-nav-msg {
|
||||
-fx-image: url("../../../images/nav/msg.png");
|
||||
#image-nav-disputes {
|
||||
-fx-image: url("../../../images/nav/disputes.png");
|
||||
}
|
||||
|
||||
#image-nav-msg-active {
|
||||
-fx-image: url("../../../images/nav/msg_active.png");
|
||||
#image-nav-disputes-active {
|
||||
-fx-image: url("../../../images/nav/disputes_active.png");
|
||||
}
|
||||
|
||||
#image-nav-settings {
|
||||
|
@ -169,4 +173,25 @@
|
|||
|
||||
#image-offer_state_not_available {
|
||||
-fx-image: url("../../../images/offer/offer_state_not_available.png");
|
||||
}
|
||||
}
|
||||
|
||||
#image-attachment {
|
||||
-fx-image: url("../../../images/attachment.png");
|
||||
}
|
||||
|
||||
#bubble_arrow_grey_left {
|
||||
-fx-image: url("../../../images/bubble_arrow_grey_left.png");
|
||||
}
|
||||
|
||||
#bubble_arrow_blue_left {
|
||||
-fx-image: url("../../../images/bubble_arrow_blue_left.png");
|
||||
}
|
||||
|
||||
#bubble_arrow_grey_right {
|
||||
-fx-image: url("../../../images/bubble_arrow_grey_right.png");
|
||||
}
|
||||
|
||||
#bubble_arrow_blue_right {
|
||||
-fx-image: url("../../../images/bubble_arrow_blue_right.png");
|
||||
}
|
||||
|
||||
|
|
|
@ -16,8 +16,7 @@
|
|||
~ along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<StackPane fx:id="root" fx:controller="io.bitsquare.gui.main.MainView"
|
||||
prefHeight="750" prefWidth="1000" stylesheets="/io/bitsquare/gui/bitsquare.css"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
</StackPane>
|
|
@ -18,43 +18,35 @@
|
|||
package io.bitsquare.gui.main;
|
||||
|
||||
import io.bitsquare.BitsquareException;
|
||||
import io.bitsquare.fiat.FiatAccount;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.common.util.Tuple2;
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
import io.bitsquare.gui.Navigation;
|
||||
import io.bitsquare.gui.OverlayManager;
|
||||
import io.bitsquare.gui.common.view.CachingViewLoader;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.common.view.InitializableView;
|
||||
import io.bitsquare.gui.common.view.View;
|
||||
import io.bitsquare.gui.common.view.ViewLoader;
|
||||
import io.bitsquare.gui.components.Popups;
|
||||
import io.bitsquare.gui.common.view.*;
|
||||
import io.bitsquare.gui.components.SystemNotification;
|
||||
import io.bitsquare.gui.main.account.AccountView;
|
||||
import io.bitsquare.gui.main.disputes.DisputesView;
|
||||
import io.bitsquare.gui.main.funds.FundsView;
|
||||
import io.bitsquare.gui.main.home.HomeView;
|
||||
import io.bitsquare.gui.main.msg.MsgView;
|
||||
import io.bitsquare.gui.main.market.MarketView;
|
||||
import io.bitsquare.gui.main.offer.BuyOfferView;
|
||||
import io.bitsquare.gui.main.offer.SellOfferView;
|
||||
import io.bitsquare.gui.main.portfolio.PortfolioView;
|
||||
import io.bitsquare.gui.main.settings.SettingsView;
|
||||
import io.bitsquare.gui.popups.Popup;
|
||||
import io.bitsquare.gui.util.Transitions;
|
||||
import io.bitsquare.util.Utilities;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.*;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.effect.*;
|
||||
import javafx.scene.image.*;
|
||||
import javafx.scene.effect.DropShadow;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.paint.*;
|
||||
import javafx.scene.text.*;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.text.TextAlignment;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import java.util.List;
|
||||
|
||||
import static javafx.scene.layout.AnchorPane.*;
|
||||
|
||||
|
@ -63,11 +55,27 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
|
|||
|
||||
public static final String TITLE_KEY = "view.title";
|
||||
|
||||
public static BorderPane getBaseApplicationContainer() {
|
||||
return baseApplicationContainer;
|
||||
}
|
||||
|
||||
public static void blur() {
|
||||
transitions.blur(baseApplicationContainer);
|
||||
}
|
||||
|
||||
public static void blurLight() {
|
||||
transitions.blur(baseApplicationContainer, Transitions.DEFAULT_DURATION, true, false, 5);
|
||||
}
|
||||
|
||||
public static void removeBlur() {
|
||||
transitions.removeBlur(baseApplicationContainer);
|
||||
}
|
||||
|
||||
private final ToggleGroup navButtons = new ToggleGroup();
|
||||
|
||||
private final ViewLoader viewLoader;
|
||||
private final Navigation navigation;
|
||||
private final Transitions transitions;
|
||||
private static Transitions transitions;
|
||||
private final String title;
|
||||
private ChangeListener<String> walletServiceErrorMsgListener;
|
||||
private ChangeListener<String> blockchainSyncIconIdListener;
|
||||
|
@ -84,6 +92,7 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
|
|||
private Label updateInfoLabel;
|
||||
private List<String> persistedFilesCorrupted;
|
||||
private Tooltip downloadButtonTooltip;
|
||||
private static BorderPane baseApplicationContainer;
|
||||
|
||||
@Inject
|
||||
public MainView(MainViewModel model, CachingViewLoader viewLoader, Navigation navigation, Transitions transitions,
|
||||
|
@ -91,36 +100,36 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
|
|||
super(model);
|
||||
this.viewLoader = viewLoader;
|
||||
this.navigation = navigation;
|
||||
this.transitions = transitions;
|
||||
MainView.transitions = transitions;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize() {
|
||||
ToggleButton homeButton = new NavButton(HomeView.class, "Overview") {{
|
||||
setDisable(true); // TODO for alpha
|
||||
}};
|
||||
ToggleButton marketButton = new NavButton(MarketView.class, "Market");
|
||||
ToggleButton buyButton = new NavButton(BuyOfferView.class, "Buy BTC");
|
||||
ToggleButton sellButton = new NavButton(SellOfferView.class, "Sell BTC");
|
||||
ToggleButton portfolioButton = new NavButton(PortfolioView.class, "Portfolio");
|
||||
ToggleButton fundsButton = new NavButton(FundsView.class, "Funds");
|
||||
ToggleButton msgButton = new NavButton(MsgView.class, "Messages") {{
|
||||
setDisable(true); // TODO for alpha
|
||||
}};
|
||||
ToggleButton disputesButton = new NavButton(DisputesView.class, "Support");
|
||||
ToggleButton settingsButton = new NavButton(SettingsView.class, "Settings");
|
||||
ToggleButton accountButton = new NavButton(AccountView.class, "Account") {{
|
||||
setDisable(true); // TODO for alpha
|
||||
}};
|
||||
ToggleButton accountButton = new NavButton(AccountView.class, "Account");
|
||||
Pane portfolioButtonHolder = new Pane(portfolioButton);
|
||||
Pane bankAccountComboBoxHolder = new Pane();
|
||||
Pane disputesButtonHolder = new Pane(disputesButton);
|
||||
|
||||
HBox leftNavPane = new HBox(homeButton, buyButton, sellButton, portfolioButtonHolder, fundsButton, new Pane(msgButton)) {{
|
||||
HBox leftNavPane = new HBox(marketButton, buyButton, sellButton, portfolioButtonHolder, fundsButton, disputesButtonHolder) {{
|
||||
setSpacing(10);
|
||||
setLeftAnchor(this, 10d);
|
||||
setTopAnchor(this, 0d);
|
||||
}};
|
||||
|
||||
HBox rightNavPane = new HBox(bankAccountComboBoxHolder, settingsButton, accountButton) {{
|
||||
Tuple2<TextField, VBox> availableBalanceBox = getBalanceBox("Available balance");
|
||||
availableBalanceBox.first.textProperty().bind(model.availableBalance);
|
||||
|
||||
Tuple2<TextField, VBox> lockedBalanceBox = getBalanceBox("Locked balance");
|
||||
lockedBalanceBox.first.textProperty().bind(model.lockedBalance);
|
||||
|
||||
HBox rightNavPane = new HBox(availableBalanceBox.second, lockedBalanceBox.second, settingsButton, accountButton) {{
|
||||
setSpacing(10);
|
||||
setRightAnchor(this, 10d);
|
||||
setTopAnchor(this, 0d);
|
||||
|
@ -138,13 +147,15 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
|
|||
setId("content-pane");
|
||||
}};
|
||||
|
||||
BorderPane baseApplicationContainer = new BorderPane(applicationContainer) {{
|
||||
baseApplicationContainer = new BorderPane(applicationContainer) {{
|
||||
setId("base-content-container");
|
||||
}};
|
||||
baseApplicationContainer.setBottom(createFooter());
|
||||
|
||||
setupNotificationIcon(portfolioButtonHolder);
|
||||
|
||||
setupDisputesIcon(disputesButtonHolder);
|
||||
|
||||
navigation.addListener(viewPath -> {
|
||||
if (viewPath.size() != 2 || viewPath.indexOf(MainView.class) != 0)
|
||||
return;
|
||||
|
@ -161,25 +172,21 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
|
|||
.setSelected(true);
|
||||
});
|
||||
|
||||
configureBlurring(baseApplicationContainer);
|
||||
|
||||
VBox splashScreen = createSplashScreen();
|
||||
|
||||
root.getChildren().addAll(baseApplicationContainer, splashScreen);
|
||||
|
||||
model.showAppScreen.addListener((ov, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
bankAccountComboBoxHolder.getChildren().setAll(createBankAccountComboBox());
|
||||
|
||||
navigation.navigateToPreviousVisitedView();
|
||||
|
||||
if (!persistedFilesCorrupted.isEmpty()) {
|
||||
// show warning that some files has been corrupted
|
||||
Popups.openWarningPopup("Those data base file(s) are not compatible with our current code base." +
|
||||
new Popup().warning("Those data base file(s) are not compatible with our current code base." +
|
||||
"\n" + persistedFilesCorrupted.toString() +
|
||||
"\n\nWe made a backup of the corrupted file(s) and applied the default values." +
|
||||
"\n\nThe backup is located at: [data directory]/db/corrupted"
|
||||
);
|
||||
).show();
|
||||
}
|
||||
|
||||
transitions.fadeOutAndRemove(splashScreen, 1500, actionEvent -> disposeSplashScreen());
|
||||
|
@ -187,7 +194,26 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
|
|||
});
|
||||
|
||||
// Delay a bit to give time for rendering the splash screen
|
||||
Platform.runLater(model::initBackend);
|
||||
UserThread.execute(model::initializeAllServices);
|
||||
}
|
||||
|
||||
private Tuple2<TextField, VBox> getBalanceBox(String text) {
|
||||
TextField textField = new TextField();
|
||||
textField.setEditable(false);
|
||||
textField.setPrefWidth(100);
|
||||
textField.setMouseTransparent(true);
|
||||
textField.setFocusTraversable(false);
|
||||
textField.setStyle("-fx-alignment: center; -fx-background-color: white;");
|
||||
|
||||
Label label = new Label(text);
|
||||
label.setId("nav-balance-label");
|
||||
label.setPadding(new Insets(0, 5, 0, 5));
|
||||
label.setPrefWidth(textField.getPrefWidth());
|
||||
VBox vBox = new VBox();
|
||||
vBox.setSpacing(3);
|
||||
vBox.setPadding(new Insets(11, 0, 0, 0));
|
||||
vBox.getChildren().addAll(textField, label);
|
||||
return new Tuple2(textField, vBox);
|
||||
}
|
||||
|
||||
public void setPersistedFilesCorrupted(List<String> persistedFilesCorrupted) {
|
||||
|
@ -209,7 +235,6 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
|
|||
blockchainSyncLabel.textProperty().bind(model.blockchainSyncInfo);
|
||||
walletServiceErrorMsgListener = (ov, oldValue, newValue) -> {
|
||||
blockchainSyncLabel.setId("splash-error-state-msg");
|
||||
// error popup is called by error handler at createFooter
|
||||
};
|
||||
model.walletServiceErrorMsg.addListener(walletServiceErrorMsgListener);
|
||||
|
||||
|
@ -258,7 +283,6 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
|
|||
bootstrapErrorMsgListener = (ov, oldValue, newValue) -> {
|
||||
bootstrapStateLabel.setId("splash-error-state-msg");
|
||||
bootstrapIndicator.setVisible(false);
|
||||
// error popup is handled by handler at createFooter
|
||||
};
|
||||
model.bootstrapErrorMsg.addListener(bootstrapErrorMsgListener);
|
||||
|
||||
|
@ -353,6 +377,8 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
|
|||
downloadButton.visibleProperty().unbind();
|
||||
downloadButton.managedProperty().unbind();
|
||||
downloadButtonTooltip.textProperty().unbind();
|
||||
|
||||
model.onSplashScreenRemoved();
|
||||
}
|
||||
|
||||
|
||||
|
@ -424,7 +450,7 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
|
|||
bootstrapLabel.setId("footer-pane");
|
||||
setRightAnchor(bootstrapLabel, 100d);
|
||||
setBottomAnchor(bootstrapLabel, 7d);
|
||||
bootstrapLabel.textProperty().bind(model.bootstrapInfoFooter);
|
||||
bootstrapLabel.textProperty().bind(model.p2pNetworkInfoFooter);
|
||||
|
||||
ImageView bootstrapIcon = new ImageView();
|
||||
setRightAnchor(bootstrapIcon, 60d);
|
||||
|
@ -435,18 +461,18 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
|
|||
numPeersLabel.setId("footer-num-peers");
|
||||
setRightAnchor(numPeersLabel, 10d);
|
||||
setBottomAnchor(numPeersLabel, 7d);
|
||||
numPeersLabel.textProperty().bind(model.numDHTPeers);
|
||||
numPeersLabel.textProperty().bind(model.numP2PNetworkPeers);
|
||||
model.bootstrapErrorMsg.addListener((ov, oldValue, newValue) -> {
|
||||
if (newValue != null) {
|
||||
bootstrapLabel.setId("splash-error-state-msg");
|
||||
bootstrapLabel.textProperty().unbind();
|
||||
bootstrapLabel.setText("Not connected");
|
||||
Popups.openErrorPopup("Error", "Connecting to the P2P network failed. \n" + newValue
|
||||
+ "\nPlease check your internet connection.");
|
||||
new Popup().error("Connecting to the P2P network failed. \n" + newValue
|
||||
+ "\nPlease check your internet connection.").show();
|
||||
}
|
||||
else {
|
||||
bootstrapLabel.setId("footer-pane");
|
||||
bootstrapLabel.textProperty().bind(model.bootstrapInfoFooter);
|
||||
bootstrapLabel.textProperty().bind(model.p2pNetworkInfoFooter);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -459,11 +485,11 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
|
|||
return footerContainer;
|
||||
}
|
||||
|
||||
private void setupNotificationIcon(Pane portfolioButtonHolder) {
|
||||
Label numPendingTradesLabel = new Label();
|
||||
numPendingTradesLabel.textProperty().bind(model.numPendingTradesAsString);
|
||||
numPendingTradesLabel.relocate(5, 1);
|
||||
numPendingTradesLabel.setId("nav-alert-label");
|
||||
private void setupNotificationIcon(Pane buttonHolder) {
|
||||
Label label = new Label();
|
||||
label.textProperty().bind(model.numPendingTradesAsString);
|
||||
label.relocate(5, 1);
|
||||
label.setId("nav-alert-label");
|
||||
|
||||
ImageView icon = new ImageView();
|
||||
icon.setLayoutX(0.5);
|
||||
|
@ -473,60 +499,37 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
|
|||
notification.relocate(30, 9);
|
||||
notification.setMouseTransparent(true);
|
||||
notification.setEffect(new DropShadow(4, 1, 2, Color.GREY));
|
||||
notification.getChildren().addAll(icon, numPendingTradesLabel);
|
||||
notification.getChildren().addAll(icon, label);
|
||||
notification.visibleProperty().bind(model.showPendingTradesNotification);
|
||||
portfolioButtonHolder.getChildren().add(notification);
|
||||
buttonHolder.getChildren().add(notification);
|
||||
|
||||
model.showPendingTradesNotification.addListener((ov, oldValue, newValue) -> {
|
||||
if (newValue)
|
||||
SystemNotification.openInfoNotification(title, "You got a new trade message.");
|
||||
SystemNotification.openInfoNotification(title, "You received a new trade message.");
|
||||
});
|
||||
}
|
||||
|
||||
private VBox createBankAccountComboBox() {
|
||||
final ComboBox<FiatAccount> comboBox = new ComboBox<>(model.getBankAccounts());
|
||||
comboBox.setLayoutY(12);
|
||||
comboBox.setVisibleRowCount(5);
|
||||
comboBox.setConverter(model.getBankAccountsConverter());
|
||||
comboBox.disableProperty().bind(model.bankAccountsComboBoxDisable);
|
||||
comboBox.promptTextProperty().bind(model.bankAccountsComboBoxPrompt);
|
||||
private void setupDisputesIcon(Pane buttonHolder) {
|
||||
Label label = new Label();
|
||||
label.textProperty().bind(model.numOpenDisputesAsString);
|
||||
label.relocate(5, 1);
|
||||
label.setId("nav-alert-label");
|
||||
|
||||
comboBox.getSelectionModel().selectedItemProperty().addListener((ov, oldValue, newValue) ->
|
||||
model.setCurrentBankAccount(newValue));
|
||||
ImageView icon = new ImageView();
|
||||
icon.setLayoutX(0.5);
|
||||
icon.setId("image-alert-round");
|
||||
|
||||
model.currentBankAccount.addListener((ov, oldValue, newValue) ->
|
||||
comboBox.getSelectionModel().select(newValue));
|
||||
comboBox.getSelectionModel().select(model.currentBankAccount.get());
|
||||
Pane notification = new Pane();
|
||||
notification.relocate(30, 9);
|
||||
notification.setMouseTransparent(true);
|
||||
notification.setEffect(new DropShadow(4, 1, 2, Color.GREY));
|
||||
notification.getChildren().addAll(icon, label);
|
||||
notification.visibleProperty().bind(model.showOpenDisputesNotification);
|
||||
buttonHolder.getChildren().add(notification);
|
||||
|
||||
final Label titleLabel = new Label("Bank account");
|
||||
titleLabel.setMouseTransparent(true);
|
||||
titleLabel.setId("nav-button-label");
|
||||
comboBox.widthProperty().addListener((ov, o, n) ->
|
||||
titleLabel.setLayoutX(((double) n - titleLabel.getWidth()) / 2));
|
||||
|
||||
VBox vBox = new VBox();
|
||||
vBox.setPadding(new Insets(12, 8, 0, 5));
|
||||
vBox.setSpacing(2);
|
||||
vBox.setAlignment(Pos.CENTER);
|
||||
vBox.getChildren().setAll(comboBox, titleLabel);
|
||||
|
||||
// TODO for alpha
|
||||
vBox.setDisable(true);
|
||||
|
||||
return vBox;
|
||||
}
|
||||
|
||||
private void configureBlurring(Node node) {
|
||||
OverlayManager.addListener(new OverlayManager.OverlayListener() {
|
||||
@Override
|
||||
public void onBlurContentRequested() {
|
||||
transitions.blur(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoveBlurContentRequested() {
|
||||
transitions.removeBlur(node);
|
||||
}
|
||||
model.showOpenDisputesNotification.addListener((ov, oldValue, newValue) -> {
|
||||
if (newValue)
|
||||
SystemNotification.openInfoNotification(title, "You received a dispute message.");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -577,7 +580,6 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
|
|||
}
|
||||
|
||||
private void openBTCConnectionErrorPopup(String errorMsg) {
|
||||
Popups.openErrorPopup("Error", "Connecting to the bitcoin network failed. \n" + errorMsg);
|
||||
// + "\nPlease check your internet connection."
|
||||
new Popup().error("Connecting to the bitcoin network failed. \n" + errorMsg).show();
|
||||
}
|
||||
}
|
|
@ -17,65 +17,72 @@
|
|||
|
||||
package io.bitsquare.gui.main;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import io.bitsquare.alert.Alert;
|
||||
import io.bitsquare.alert.AlertManager;
|
||||
import io.bitsquare.app.BitsquareApp;
|
||||
import io.bitsquare.app.UpdateProcess;
|
||||
import io.bitsquare.app.Version;
|
||||
import io.bitsquare.arbitration.ArbitrationRepository;
|
||||
import io.bitsquare.arbitration.ArbitratorManager;
|
||||
import io.bitsquare.arbitration.Dispute;
|
||||
import io.bitsquare.arbitration.DisputeManager;
|
||||
import io.bitsquare.btc.AddressEntry;
|
||||
import io.bitsquare.btc.TradeWalletService;
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.crypto.KeyRing;
|
||||
import io.bitsquare.fiat.FiatAccount;
|
||||
import io.bitsquare.btc.listeners.BalanceListener;
|
||||
import io.bitsquare.common.crypto.KeyRing;
|
||||
import io.bitsquare.gui.common.model.ViewModel;
|
||||
import io.bitsquare.gui.components.BalanceTextField;
|
||||
import io.bitsquare.gui.components.BalanceWithConfirmationTextField;
|
||||
import io.bitsquare.gui.components.TxIdTextField;
|
||||
import io.bitsquare.gui.popups.*;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.locale.CountryUtil;
|
||||
import io.bitsquare.p2p.BaseP2PService;
|
||||
import io.bitsquare.p2p.BootstrapNodes;
|
||||
import io.bitsquare.p2p.ClientNode;
|
||||
import io.bitsquare.p2p.tomp2p.BootstrappedPeerBuilder;
|
||||
import io.bitsquare.p2p.P2PService;
|
||||
import io.bitsquare.p2p.P2PServiceListener;
|
||||
import io.bitsquare.trade.Trade;
|
||||
import io.bitsquare.trade.TradeManager;
|
||||
import io.bitsquare.trade.offer.OpenOffer;
|
||||
import io.bitsquare.trade.offer.OpenOfferManager;
|
||||
import io.bitsquare.user.Preferences;
|
||||
import io.bitsquare.user.User;
|
||||
import io.bitsquare.util.Utilities;
|
||||
|
||||
import org.bitcoinj.store.BlockStoreException;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.DoubleProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleDoubleProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
import org.bitcoinj.core.*;
|
||||
import org.bitcoinj.store.BlockStoreException;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import org.fxmisc.easybind.monadic.MonadicBinding;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.reactfx.util.FxTimer;
|
||||
import org.reactfx.util.Timer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import rx.Observable;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
class MainViewModel implements ViewModel {
|
||||
private static final Logger log = LoggerFactory.getLogger(MainViewModel.class);
|
||||
|
||||
private static final long BLOCKCHAIN_SYNC_TIMEOUT = 60000;
|
||||
private static final long LOST_P2P_CONNECTION_TIMEOUT = 5000;
|
||||
private static final long LOST_BTC_CONNECTION_TIMEOUT = 5000;
|
||||
// private static final long LOST_BTC_CONNECTION_TIMEOUT = 5000;
|
||||
|
||||
private final User user;
|
||||
private final KeyRing keyRing;
|
||||
private final WalletService walletService;
|
||||
private final ArbitrationRepository arbitrationRepository;
|
||||
private final ClientNode clientNode;
|
||||
private final TradeWalletService tradeWalletService;
|
||||
private final ArbitratorManager arbitratorManager;
|
||||
private final P2PService p2PService;
|
||||
private final TradeManager tradeManager;
|
||||
private final OpenOfferManager openOfferManager;
|
||||
private final DisputeManager disputeManager;
|
||||
private final Preferences preferences;
|
||||
private final KeyRing keyRing;
|
||||
private final AlertManager alertManager;
|
||||
private final WalletPasswordPopup walletPasswordPopup;
|
||||
private final UpdateProcess updateProcess;
|
||||
private final BSFormatter formatter;
|
||||
|
||||
|
@ -85,90 +92,194 @@ class MainViewModel implements ViewModel {
|
|||
final DoubleProperty blockchainSyncProgress = new SimpleDoubleProperty(-1);
|
||||
final StringProperty walletServiceErrorMsg = new SimpleStringProperty();
|
||||
final StringProperty blockchainSyncIconId = new SimpleStringProperty();
|
||||
final StringProperty numBTCPeers = new SimpleStringProperty();
|
||||
final StringProperty availableBalance = new SimpleStringProperty();
|
||||
final StringProperty lockedBalance = new SimpleStringProperty();
|
||||
private final StringProperty numBTCPeers = new SimpleStringProperty();
|
||||
|
||||
// P2P network
|
||||
final StringProperty bootstrapInfo = new SimpleStringProperty("Connecting to P2P network...");
|
||||
final StringProperty bootstrapInfoFooter = new SimpleStringProperty();
|
||||
final StringProperty p2pNetworkInfoFooter = new SimpleStringProperty("Setting up Tor hidden service...");
|
||||
final DoubleProperty bootstrapProgress = new SimpleDoubleProperty(-1);
|
||||
final StringProperty bootstrapErrorMsg = new SimpleStringProperty();
|
||||
final StringProperty bootstrapIconId = new SimpleStringProperty();
|
||||
final StringProperty numDHTPeers = new SimpleStringProperty();
|
||||
final StringProperty numP2PNetworkPeers = new SimpleStringProperty();
|
||||
|
||||
// software update
|
||||
final StringProperty updateInfo = new SimpleStringProperty();
|
||||
String version = "v." + Version.VERSION;
|
||||
final String version = "v." + Version.VERSION;
|
||||
|
||||
final BooleanProperty showRestartButton = new SimpleBooleanProperty(false);
|
||||
final BooleanProperty showDownloadButton = new SimpleBooleanProperty(false);
|
||||
final StringProperty newReleaseUrl = new SimpleStringProperty();
|
||||
final StringProperty updateIconId = new SimpleStringProperty();
|
||||
|
||||
final StringProperty bankAccountsComboBoxPrompt = new SimpleStringProperty();
|
||||
final BooleanProperty bankAccountsComboBoxDisable = new SimpleBooleanProperty();
|
||||
final ObjectProperty<FiatAccount> currentBankAccount = new SimpleObjectProperty<>();
|
||||
|
||||
final BooleanProperty showAppScreen = new SimpleBooleanProperty();
|
||||
final StringProperty numPendingTradesAsString = new SimpleStringProperty();
|
||||
final BooleanProperty showPendingTradesNotification = new SimpleBooleanProperty();
|
||||
final StringProperty numOpenDisputesAsString = new SimpleStringProperty();
|
||||
final BooleanProperty showOpenDisputesNotification = new SimpleBooleanProperty();
|
||||
private final BooleanProperty isSplashScreenRemoved = new SimpleBooleanProperty();
|
||||
|
||||
final String bitcoinNetworkAsString;
|
||||
|
||||
private Timer blockchainSyncTimeoutTimer;
|
||||
private Timer lostP2PConnectionTimeoutTimer;
|
||||
private Timer lostBTCConnectionTimeoutTimer;
|
||||
private MonadicBinding<Boolean> allServicesDone;
|
||||
private User user;
|
||||
//private Timer lostBTCConnectionTimeoutTimer;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public MainViewModel(User user, KeyRing keyRing, WalletService walletService, ArbitrationRepository arbitrationRepository, ClientNode clientNode,
|
||||
TradeManager tradeManager, OpenOfferManager openOfferManager, Preferences preferences, UpdateProcess updateProcess,
|
||||
BootstrapNodes bootstrapNodes, BSFormatter formatter) {
|
||||
public MainViewModel(WalletService walletService, TradeWalletService tradeWalletService,
|
||||
ArbitratorManager arbitratorManager, P2PService p2PService, TradeManager tradeManager,
|
||||
OpenOfferManager openOfferManager, DisputeManager disputeManager, Preferences preferences,
|
||||
KeyRing keyRing, User user,
|
||||
AlertManager alertManager,
|
||||
WalletPasswordPopup walletPasswordPopup, UpdateProcess updateProcess, BSFormatter formatter) {
|
||||
this.user = user;
|
||||
this.keyRing = keyRing;
|
||||
log.debug("in");
|
||||
this.walletService = walletService;
|
||||
this.arbitrationRepository = arbitrationRepository;
|
||||
this.clientNode = clientNode;
|
||||
this.tradeWalletService = tradeWalletService;
|
||||
this.arbitratorManager = arbitratorManager;
|
||||
this.p2PService = p2PService;
|
||||
this.tradeManager = tradeManager;
|
||||
this.openOfferManager = openOfferManager;
|
||||
this.disputeManager = disputeManager;
|
||||
this.preferences = preferences;
|
||||
this.keyRing = keyRing;
|
||||
this.alertManager = alertManager;
|
||||
this.walletPasswordPopup = walletPasswordPopup;
|
||||
this.updateProcess = updateProcess;
|
||||
this.formatter = formatter;
|
||||
|
||||
bitcoinNetworkAsString = formatter.formatBitcoinNetwork(preferences.getBitcoinNetwork());
|
||||
bootstrapNodes.initWithNetworkId(preferences.getBitcoinNetwork().ordinal() + 10);
|
||||
|
||||
updateProcess.state.addListener((observableValue, oldValue, newValue) -> applyUpdateState(newValue));
|
||||
applyUpdateState(updateProcess.state.get());
|
||||
TxIdTextField.setPreferences(preferences);
|
||||
TxIdTextField.setWalletService(walletService);
|
||||
BalanceTextField.setWalletService(walletService);
|
||||
BalanceWithConfirmationTextField.setWalletService(walletService);
|
||||
|
||||
currentBankAccount.bind(user.currentFiatAccountProperty());
|
||||
user.fiatAccountsObservableList().addListener((ListChangeListener<FiatAccount>) change -> {
|
||||
bankAccountsComboBoxDisable.set(change.getList().isEmpty());
|
||||
bankAccountsComboBoxPrompt.set(change.getList().isEmpty() ? "No accounts" : "");
|
||||
});
|
||||
bankAccountsComboBoxDisable.set(user.fiatAccountsObservableList().isEmpty());
|
||||
bankAccountsComboBoxPrompt.set(user.fiatAccountsObservableList().isEmpty() ? "No accounts" : "");
|
||||
if (BitsquareApp.DEV_MODE) {
|
||||
preferences.setShowPlaceOfferConfirmation(false);
|
||||
preferences.setShowTakeOfferConfirmation(false);
|
||||
preferences.setUseAnimations(false);
|
||||
preferences.setUseEffects(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void restart() {
|
||||
updateProcess.restart();
|
||||
}
|
||||
|
||||
public void initBackend() {
|
||||
Platform.runLater(updateProcess::init);
|
||||
public void initializeAllServices() {
|
||||
log.trace("initializeAllServices");
|
||||
|
||||
p2pNetworkInfoFooter.set("Connecting to tor network...");
|
||||
|
||||
BooleanProperty updateProcessDone = initUpdateFx();
|
||||
BooleanProperty walletInitialized = initBitcoinWallet();
|
||||
BooleanProperty bootstrapDone = initP2PNetwork();
|
||||
|
||||
// need to store it to not get garbage collected
|
||||
allServicesDone = EasyBind.combine(updateProcessDone, walletInitialized, bootstrapDone,
|
||||
(a, b, c) -> a.booleanValue() && b.booleanValue() && c.booleanValue());
|
||||
allServicesDone.subscribe((observable, oldValue, newValue) -> {
|
||||
if (newValue)
|
||||
onAllServicesInitialized();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Initialisation
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private BooleanProperty initP2PNetwork() {
|
||||
/* if (networkService.getNetworkInfo() instanceof TomP2PNetworkInfo) {
|
||||
TomP2PNetworkInfo networkInfo = (TomP2PNetworkInfo) networkService.getNetworkInfo();
|
||||
networkInfo.numPeersProperty().addListener((observable, oldValue, newValue) -> {
|
||||
numDHTPeers.set(String.valueOf(newValue) + " peers");
|
||||
if ((int) newValue == 0) {
|
||||
if (lostP2PConnectionTimeoutTimer != null)
|
||||
lostP2PConnectionTimeoutTimer.stop();
|
||||
lostP2PConnectionTimeoutTimer = FxTimer.runLater(Duration.ofMillis(LOST_P2P_CONNECTION_TIMEOUT), () -> {
|
||||
log.trace("Connection lost timeout reached");
|
||||
bootstrapErrorMsg.set("We lost connection to the last peer.");
|
||||
});
|
||||
} else if ((int) oldValue == 0 && (int) newValue > 0) {
|
||||
if (lostP2PConnectionTimeoutTimer != null) {
|
||||
lostP2PConnectionTimeoutTimer.stop();
|
||||
lostP2PConnectionTimeoutTimer = null;
|
||||
}
|
||||
bootstrapErrorMsg.set(null);
|
||||
}
|
||||
});
|
||||
|
||||
networkInfo.stateProperty().addListener((ov, oldValue, newValue) -> {
|
||||
setBootstrapState(newValue);
|
||||
});
|
||||
}*/
|
||||
final BooleanProperty p2pNetworkReady = new SimpleBooleanProperty();
|
||||
p2PService.start(new P2PServiceListener() {
|
||||
@Override
|
||||
public void onTorNodeReady() {
|
||||
p2pNetworkInfoFooter.set("Tor node created.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllDataReceived() {
|
||||
p2pNetworkInfoFooter.set("Data received from peer.");
|
||||
p2pNetworkReady.set(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticated() {
|
||||
p2pNetworkInfoFooter.set("Authenticated in P2P network.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHiddenServiceReady() {
|
||||
p2pNetworkInfoFooter.set("Tor hidden service available.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetupFailed(Throwable throwable) {
|
||||
bootstrapErrorMsg.set("Error at starting P2P network. " + throwable.getMessage());
|
||||
bootstrapInfo.set("Connecting to the P2P network failed.");
|
||||
bootstrapProgress.set(0);
|
||||
}
|
||||
});
|
||||
return p2pNetworkReady;
|
||||
}
|
||||
|
||||
private BooleanProperty initBitcoinWallet() {
|
||||
if (walletService.downloadPercentageProperty().get() > -1)
|
||||
startBlockchainSyncTimeout();
|
||||
|
||||
walletService.downloadPercentageProperty().addListener((ov, oldValue, newValue) -> {
|
||||
EasyBind.subscribe(walletService.downloadPercentageProperty(), newValue -> setBitcoinNetworkSyncProgress((double) newValue));
|
||||
|
||||
/* walletService.downloadPercentageProperty().addListener((ov, oldValue, newValue) -> {
|
||||
setBitcoinNetworkSyncProgress((double) newValue);
|
||||
});
|
||||
setBitcoinNetworkSyncProgress(walletService.downloadPercentageProperty().get());
|
||||
// Sometimes we don't get the updates, so add an additional setter after 2 seconds
|
||||
Utilities.setTimeout(2000, () -> setBitcoinNetworkSyncProgress(walletService.downloadPercentageProperty().get()));
|
||||
FxTimer.runLater(Duration.ofMillis(2000), () -> setBitcoinNetworkSyncProgress(walletService.downloadPercentageProperty().get()));*/
|
||||
|
||||
walletService.numPeersProperty().addListener((observable, oldValue, newValue) -> {
|
||||
log.debug("Bitcoin peers " + newValue);
|
||||
numBTCPeers.set(String.valueOf(newValue) + " peers");
|
||||
if ((int) newValue < 1) {
|
||||
/* if ((int) newValue < 1) {
|
||||
if (lostBTCConnectionTimeoutTimer != null)
|
||||
lostBTCConnectionTimeoutTimer.cancel();
|
||||
lostBTCConnectionTimeoutTimer = Utilities.setTimeout(LOST_BTC_CONNECTION_TIMEOUT, () -> {
|
||||
|
@ -182,106 +293,173 @@ class MainViewModel implements ViewModel {
|
|||
lostBTCConnectionTimeoutTimer = null;
|
||||
}
|
||||
walletServiceErrorMsg.set(null);
|
||||
}
|
||||
}*/
|
||||
});
|
||||
final BooleanProperty walletInitialized = new SimpleBooleanProperty();
|
||||
walletService.initialize(null,
|
||||
() -> {
|
||||
log.trace("wallet initialized");
|
||||
walletInitialized.set(true);
|
||||
},
|
||||
errorMessage -> setWalletServiceException(errorMessage));
|
||||
return walletInitialized;
|
||||
}
|
||||
|
||||
// Set executor for all P2PServices
|
||||
BaseP2PService.setUserThread(Platform::runLater);
|
||||
|
||||
clientNode.numPeersProperty().addListener((observable, oldValue, newValue) -> {
|
||||
numDHTPeers.set(String.valueOf(newValue) + " peers");
|
||||
if ((int) newValue == 0) {
|
||||
if (lostP2PConnectionTimeoutTimer != null)
|
||||
lostP2PConnectionTimeoutTimer.cancel();
|
||||
lostP2PConnectionTimeoutTimer = Utilities.setTimeout(LOST_P2P_CONNECTION_TIMEOUT, () -> {
|
||||
log.trace("Connection lost timeout reached");
|
||||
bootstrapErrorMsg.set("We lost connection to the last peer.");
|
||||
});
|
||||
}
|
||||
else if ((int) oldValue == 0 && (int) newValue > 0) {
|
||||
if (lostP2PConnectionTimeoutTimer != null) {
|
||||
lostP2PConnectionTimeoutTimer.cancel();
|
||||
lostP2PConnectionTimeoutTimer = null;
|
||||
}
|
||||
bootstrapErrorMsg.set(null);
|
||||
}
|
||||
@NotNull
|
||||
private BooleanProperty initUpdateFx() {
|
||||
updateProcess.init();
|
||||
final BooleanProperty updateProcessDone = new SimpleBooleanProperty();
|
||||
updateProcess.setResultHandler(() -> {
|
||||
log.trace("updateProcess completed");
|
||||
updateProcessDone.set(true);
|
||||
});
|
||||
|
||||
clientNode.setExecutor(Platform::runLater);
|
||||
Observable<BootstrappedPeerBuilder.State> bootstrapStateAsObservable = clientNode.bootstrap(keyRing.getDhtSignatureKeyPair());
|
||||
bootstrapStateAsObservable.publish();
|
||||
bootstrapStateAsObservable.subscribe(
|
||||
state -> Platform.runLater(() -> setBootstrapState(state)),
|
||||
error -> Platform.runLater(() -> {
|
||||
bootstrapErrorMsg.set(error.getMessage());
|
||||
bootstrapInfo.set("Connecting to the P2P network failed.");
|
||||
bootstrapProgress.set(0);
|
||||
|
||||
}),
|
||||
() -> log.trace("message completed"));
|
||||
|
||||
Observable<Object> walletServiceObservable = walletService.initialize(Platform::runLater);
|
||||
walletServiceObservable.subscribe(
|
||||
next -> {
|
||||
//log.trace("wallet next");
|
||||
},
|
||||
error -> Platform.runLater(() -> {
|
||||
log.trace("wallet error");
|
||||
setWalletServiceException(error);
|
||||
}),
|
||||
() -> {
|
||||
log.trace("wallet completed");
|
||||
});
|
||||
|
||||
Observable<UpdateProcess.State> updateProcessObservable = this.updateProcess.getProcess();
|
||||
updateProcessObservable.subscribe(next -> {
|
||||
//log.trace("updateProcess next");
|
||||
},
|
||||
error -> {
|
||||
},
|
||||
() -> {
|
||||
log.trace("updateProcess completed");
|
||||
});
|
||||
|
||||
Observable<?> allServices = Observable.merge(bootstrapStateAsObservable, walletServiceObservable, updateProcessObservable);
|
||||
allServices.subscribe(
|
||||
next -> {
|
||||
},
|
||||
error -> {
|
||||
},
|
||||
() -> Platform.runLater(this::onAllServicesInitialized)
|
||||
);
|
||||
return updateProcessDone;
|
||||
}
|
||||
|
||||
private void onAllServicesInitialized() {
|
||||
log.trace("backend completed");
|
||||
log.trace("onAllServicesInitialized");
|
||||
|
||||
setBitcoinNetworkSyncProgress(walletService.downloadPercentageProperty().get());
|
||||
tradeManager.getPendingTrades().addListener((ListChangeListener<Trade>) change -> updateNumPendingTrades());
|
||||
updateNumPendingTrades();
|
||||
showAppScreen.set(true);
|
||||
|
||||
// TODO for alpha version
|
||||
if (!user.isRegistered()) {
|
||||
FiatAccount fiatAccount = new FiatAccount(FiatAccount.Type.IRC,
|
||||
"EUR",
|
||||
CountryUtil.getDefaultCountry(),
|
||||
"Demo (Name of bank)",
|
||||
"Demo (Account holder name)",
|
||||
"Demo (E.g. IBAN) ",
|
||||
"Demo (E.g. BIC) ");
|
||||
user.addFiatAccount(fiatAccount);
|
||||
user.setAccountID(walletService.getRegistrationAddressEntry().toString());
|
||||
}
|
||||
// disputeManager
|
||||
disputeManager.getDisputesAsObservableList().addListener((ListChangeListener<Dispute>) change -> {
|
||||
change.next();
|
||||
addDisputeClosedChangeListener(change.getAddedSubList());
|
||||
updateDisputeStates();
|
||||
});
|
||||
addDisputeClosedChangeListener(disputeManager.getDisputesAsObservableList());
|
||||
updateDisputeStates();
|
||||
disputeManager.onAllServicesInitialized();
|
||||
|
||||
// Load all arbitrators in background. Any class requiring a loaded list of arbitrators need to register itself as listener to handle the async
|
||||
// operation.
|
||||
log.debug("loadAllArbitrators");
|
||||
arbitrationRepository.loadAllArbitrators();
|
||||
|
||||
// tradeManager
|
||||
tradeManager.getTrades().addListener((ListChangeListener<Trade>) c -> updateBalance());
|
||||
|
||||
tradeManager.getTrades().addListener((ListChangeListener<Trade>) change -> {
|
||||
change.next();
|
||||
addDisputeStateListeners(change.getAddedSubList());
|
||||
pendingTradesChanged();
|
||||
});
|
||||
pendingTradesChanged();
|
||||
addDisputeStateListeners(tradeManager.getTrades());
|
||||
tradeManager.onAllServicesInitialized();
|
||||
|
||||
|
||||
// arbitratorManager
|
||||
arbitratorManager.onAllServicesInitialized();
|
||||
|
||||
|
||||
// walletService
|
||||
// In case we have any offers open or a pending trade we need to unlock our trading wallet so a trade can be executed automatically
|
||||
// Otherwise we delay the password request to create offer, or take offer.
|
||||
// When the password is set it will be stored to the tradeWalletService as well, so its only needed after a restart.
|
||||
if (walletService.getWallet().isEncrypted() &&
|
||||
(openOfferManager.getOpenOffers().size() > 0
|
||||
|| tradeManager.getTrades().size() > 0
|
||||
|| disputeManager.getDisputesAsObservableList().size() > 0))
|
||||
walletPasswordPopup.show().onAesKey(aesKey -> tradeWalletService.setAesKey(aesKey));
|
||||
|
||||
if (tradeManager.pendingTradesInitializedProperty().get() && isSplashScreenRemoved.get())
|
||||
applyTradePeriodState();
|
||||
else {
|
||||
isSplashScreenRemoved.addListener((observable, oldValue, newValue) -> {
|
||||
if (tradeManager.pendingTradesInitializedProperty().get() && isSplashScreenRemoved.get())
|
||||
applyTradePeriodState();
|
||||
});
|
||||
tradeManager.pendingTradesInitializedProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (tradeManager.pendingTradesInitializedProperty().get() && isSplashScreenRemoved.get())
|
||||
applyTradePeriodState();
|
||||
});
|
||||
}
|
||||
walletService.addBalanceListener(new BalanceListener() {
|
||||
@Override
|
||||
public void onBalanceChanged(Coin balance) {
|
||||
updateBalance();
|
||||
}
|
||||
});
|
||||
updateBalance();
|
||||
setBitcoinNetworkSyncProgress(walletService.downloadPercentageProperty().get());
|
||||
|
||||
|
||||
// openOfferManager
|
||||
openOfferManager.getOpenOffers().addListener((ListChangeListener<OpenOffer>) c -> updateBalance());
|
||||
openOfferManager.onAllServicesInitialized();
|
||||
|
||||
|
||||
// alertManager
|
||||
alertManager.alertMessageProperty().addListener((observable, oldValue, newValue) -> displayAlertIfPresent(newValue));
|
||||
displayAlertIfPresent(alertManager.alertMessageProperty().get());
|
||||
|
||||
|
||||
// tac
|
||||
if (!preferences.getTacAccepted() && !BitsquareApp.DEV_MODE)
|
||||
new TacPopup().url(WebViewPopup.getLocalUrl("tac.html")).onAgree(() -> preferences.setTacAccepted(true)).show();
|
||||
|
||||
|
||||
// now show app
|
||||
showAppScreen.set(true);
|
||||
}
|
||||
|
||||
private void displayAlertIfPresent(Alert alert) {
|
||||
boolean alreadyDisplayed = alert != null && alert.equals(user.getDisplayedAlert());
|
||||
user.setDisplayedAlert(alert);
|
||||
|
||||
if (alert != null && !alreadyDisplayed) {
|
||||
new DisplayAlertMessagePopup().alertMessage(alert).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBalance() {
|
||||
updateAvailableBalance();
|
||||
updateLockedBalance();
|
||||
}
|
||||
|
||||
private void updateLockedBalance() {
|
||||
List<AddressEntry> result = new ArrayList<>();
|
||||
|
||||
result.addAll(Stream.concat(openOfferManager.getOpenOffers().stream(), tradeManager.getTrades().stream())
|
||||
.map(tradable -> walletService.getAddressEntryByOfferId(tradable.getOffer().getId()))
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
Optional<Coin> totalLockedOptional = result.stream().map(e -> walletService.getBalanceForAddress(e.getAddress())).reduce((a, b) -> a.add(b));
|
||||
if (totalLockedOptional.isPresent())
|
||||
lockedBalance.set(formatter.formatCoinWithCode(totalLockedOptional.get()));
|
||||
else
|
||||
lockedBalance.set(formatter.formatCoinWithCode(Coin.ZERO));
|
||||
}
|
||||
|
||||
private void updateAvailableBalance() {
|
||||
List<AddressEntry> result = new ArrayList<>();
|
||||
|
||||
List<String> reservedTrades = Stream.concat(openOfferManager.getOpenOffers().stream(), tradeManager.getTrades().stream())
|
||||
.map(tradable -> tradable.getOffer().getId())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
result.addAll(walletService.getAddressEntryList().stream()
|
||||
.filter(e -> walletService.getBalanceForAddress(e.getAddress()).isPositive())
|
||||
.filter(e -> !reservedTrades.contains(e.getOfferId()))
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
Optional<Coin> totalAvailableOptional = result.stream().map(e -> walletService.getBalanceForAddress(e.getAddress())).reduce((a, b) -> a.add(b));
|
||||
if (totalAvailableOptional.isPresent())
|
||||
availableBalance.set(formatter.formatCoinWithCode(totalAvailableOptional.get()));
|
||||
else
|
||||
availableBalance.set(formatter.formatCoinWithCode(Coin.ZERO));
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// UI callbacks
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void onSplashScreenRemoved() {
|
||||
isSplashScreenRemoved.set(true);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Apply states
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void applyUpdateState(UpdateProcess.State state) {
|
||||
switch (state) {
|
||||
case CHECK_FOR_UPDATES:
|
||||
|
@ -310,7 +488,7 @@ class MainViewModel implements ViewModel {
|
|||
}
|
||||
}
|
||||
|
||||
private void setBootstrapState(BootstrappedPeerBuilder.State state) {
|
||||
/* private void setBootstrapState(TomP2PNetworkInfo.State state) {
|
||||
switch (state) {
|
||||
case DISCOVERY_DIRECT_SUCCEEDED:
|
||||
bootstrapIconId.set("image-connection-direct");
|
||||
|
@ -344,6 +522,38 @@ class MainViewModel implements ViewModel {
|
|||
bootstrapProgress.set(-1);
|
||||
break;
|
||||
}
|
||||
}*/
|
||||
|
||||
private void applyTradePeriodState() {
|
||||
updateTradePeriodState();
|
||||
tradeWalletService.addBlockChainListener(new BlockChainListener() {
|
||||
@Override
|
||||
public void notifyNewBestBlock(StoredBlock block) throws VerificationException {
|
||||
updateTradePeriodState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reorganize(StoredBlock splitPoint, List<StoredBlock> oldBlocks, List<StoredBlock> newBlocks)
|
||||
throws VerificationException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTransactionRelevant(Transaction tx) throws ScriptException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveFromBlock(Transaction tx, StoredBlock block, AbstractBlockChain.NewBlockType blockType, int relativityOffset)
|
||||
throws VerificationException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean notifyTransactionIsInBlock(Sha256Hash txHash, StoredBlock block, AbstractBlockChain.NewBlockType blockType,
|
||||
int relativityOffset) throws VerificationException {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setWalletServiceException(Throwable error) {
|
||||
|
@ -354,51 +564,100 @@ class MainViewModel implements ViewModel {
|
|||
walletServiceErrorMsg.set("Please check your network connection.\n\n" +
|
||||
"You must allow outgoing TCP connections to port 18333 for the bitcoin testnet.\n\n" +
|
||||
"See https://github.com/bitsquare/bitsquare/wiki for instructions.");
|
||||
}
|
||||
else if (error.getCause() instanceof BlockStoreException) {
|
||||
} else if (error.getCause() instanceof BlockStoreException) {
|
||||
walletServiceErrorMsg.set("You cannot run 2 instances of the program.");
|
||||
}
|
||||
else if (error.getMessage() != null) {
|
||||
} else if (error.getMessage() != null) {
|
||||
walletServiceErrorMsg.set(error.getMessage());
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
walletServiceErrorMsg.set(error.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTradePeriodState() {
|
||||
tradeManager.getTrades().stream().forEach(trade -> {
|
||||
int bestChainHeight = tradeWalletService.getBestChainHeight();
|
||||
if (trade.getCheckPaymentTimeAsBlockHeight() > 0 && bestChainHeight >= trade.getCheckPaymentTimeAsBlockHeight())
|
||||
trade.setTradePeriodState(Trade.TradePeriodState.HALF_REACHED);
|
||||
|
||||
public StringConverter<FiatAccount> getBankAccountsConverter() {
|
||||
return new StringConverter<FiatAccount>() {
|
||||
@Override
|
||||
public String toString(FiatAccount fiatAccount) {
|
||||
return fiatAccount.nameOfBank;
|
||||
if (trade.getOpenDisputeTimeAsBlockHeight() > 0 && bestChainHeight >= trade.getOpenDisputeTimeAsBlockHeight())
|
||||
trade.setTradePeriodState(Trade.TradePeriodState.TRADE_PERIOD_OVER);
|
||||
|
||||
switch (trade.getTradePeriodState()) {
|
||||
case NORMAL:
|
||||
break;
|
||||
case HALF_REACHED:
|
||||
if (!trade.isHalfTradePeriodReachedWarningDisplayed()) {
|
||||
new Popup().warning("Your trade with ID " + trade.getShortId() +
|
||||
" has reached the half of the max. allowed trading period and " +
|
||||
"is still not completed.\nPlease check your trade state at Portfolio/open trades for further information.").show();
|
||||
trade.setHalfTradePeriodReachedWarningDisplayed(true);
|
||||
}
|
||||
break;
|
||||
case TRADE_PERIOD_OVER:
|
||||
if (!trade.isTradePeriodOverWarningDisplayed()) {
|
||||
new Popup().warning("Your trade with ID " + trade.getShortId() + " has reached the max. allowed trading period and is " +
|
||||
"not completed.\nPlease check your trade at Portfolio/Open trades for contacting the arbitrator.").show();
|
||||
trade.setTradePeriodOverWarningDisplayed(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FiatAccount fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
public ObservableList<FiatAccount> getBankAccounts() {
|
||||
return user.fiatAccountsObservableList();
|
||||
private void addDisputeClosedChangeListener(List<? extends Dispute> list) {
|
||||
list.stream().forEach(e -> e.isClosedProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue)
|
||||
updateDisputeStates();
|
||||
}));
|
||||
}
|
||||
|
||||
public void setCurrentBankAccount(FiatAccount currentFiatAccount) {
|
||||
user.setCurrentFiatAccountProperty(currentFiatAccount);
|
||||
private void updateDisputeStates() {
|
||||
int openDisputes = disputeManager.getDisputesAsObservableList().stream().filter(e -> !e.isClosed()).collect(Collectors.toList()).size();
|
||||
if (openDisputes > 0)
|
||||
numOpenDisputesAsString.set(String.valueOf(openDisputes));
|
||||
if (openDisputes > 9)
|
||||
numOpenDisputesAsString.set("?");
|
||||
|
||||
showOpenDisputesNotification.set(openDisputes > 0);
|
||||
}
|
||||
|
||||
private void updateNumPendingTrades() {
|
||||
long numPendingTrades = tradeManager.getPendingTrades().size();
|
||||
private void pendingTradesChanged() {
|
||||
long numPendingTrades = tradeManager.getTrades().size();
|
||||
if (numPendingTrades > 0)
|
||||
numPendingTradesAsString.set(String.valueOf(numPendingTrades));
|
||||
if (numPendingTrades > 9)
|
||||
numPendingTradesAsString.set("-");
|
||||
numPendingTradesAsString.set("?");
|
||||
|
||||
showPendingTradesNotification.set(numPendingTrades > 0);
|
||||
}
|
||||
|
||||
private void addDisputeStateListeners(List<? extends Trade> addedTrades) {
|
||||
addedTrades.stream().forEach(trade -> trade.disputeStateProperty().addListener((observable, oldValue, newValue) -> {
|
||||
switch (newValue) {
|
||||
case NONE:
|
||||
break;
|
||||
case DISPUTE_REQUESTED:
|
||||
break;
|
||||
case DISPUTE_STARTED_BY_PEER:
|
||||
disputeManager.findOwnDispute(trade.getId()).ifPresent(dispute -> {
|
||||
String msg;
|
||||
if (dispute.isSupportTicket())
|
||||
msg = "Your trading peer has encountered technical problems and requested support for trade with ID " + trade.getShortId() + ".\n" +
|
||||
"Please await further instructions from the arbitrator.\n" +
|
||||
"Your funds are safe and will be refunded as soon the problem is resolved.";
|
||||
else
|
||||
msg = "Your trading peer has requested a dispute for trade with ID " + trade.getShortId() + ".";
|
||||
|
||||
new Popup().information(msg).show();
|
||||
});
|
||||
break;
|
||||
case DISPUTE_CLOSED:
|
||||
new Popup().information("A support ticket for trade with ID " + trade.getShortId() + " has been closed.").show();
|
||||
break;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private void setBitcoinNetworkSyncProgress(double value) {
|
||||
blockchainSyncProgress.set(value);
|
||||
if (value >= 1) {
|
||||
|
@ -406,15 +665,13 @@ class MainViewModel implements ViewModel {
|
|||
|
||||
blockchainSyncInfo.set("Blockchain synchronization complete.");
|
||||
blockchainSyncIconId.set("image-connection-synced");
|
||||
}
|
||||
else if (value > 0.0) {
|
||||
} else if (value > 0.0) {
|
||||
// We stop as soon the download started the timeout
|
||||
stopBlockchainSyncTimeout();
|
||||
|
||||
blockchainSyncInfo.set("Synchronizing blockchain: " + formatter.formatToPercent(value));
|
||||
blockchainSyncInfoFooter.set("Synchronizing: " + formatter.formatToPercent(value));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
blockchainSyncInfo.set("Connecting to the bitcoin network...");
|
||||
blockchainSyncInfoFooter.set("Connecting...");
|
||||
}
|
||||
|
@ -424,7 +681,7 @@ class MainViewModel implements ViewModel {
|
|||
log.trace("startBlockchainSyncTimeout");
|
||||
stopBlockchainSyncTimeout();
|
||||
|
||||
blockchainSyncTimeoutTimer = Utilities.setTimeout(BLOCKCHAIN_SYNC_TIMEOUT, () -> {
|
||||
blockchainSyncTimeoutTimer = FxTimer.runLater(Duration.ofMillis(BLOCKCHAIN_SYNC_TIMEOUT), () -> {
|
||||
log.trace("Timeout reached");
|
||||
setWalletServiceException(new TimeoutException());
|
||||
});
|
||||
|
@ -433,9 +690,8 @@ class MainViewModel implements ViewModel {
|
|||
private void stopBlockchainSyncTimeout() {
|
||||
if (blockchainSyncTimeoutTimer != null) {
|
||||
log.trace("stopBlockchainSyncTimeout");
|
||||
blockchainSyncTimeoutTimer.cancel();
|
||||
blockchainSyncTimeoutTimer.stop();
|
||||
blockchainSyncTimeoutTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,14 +17,15 @@
|
|||
~ along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.control.Tab?>
|
||||
<?import javafx.scene.control.TabPane?>
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
<TabPane fx:id="root" fx:controller="io.bitsquare.gui.main.account.AccountView"
|
||||
prefHeight="630.0" prefWidth="1000.0"
|
||||
AnchorPane.bottomAnchor="0" AnchorPane.leftAnchor="0"
|
||||
AnchorPane.rightAnchor="0" AnchorPane.topAnchor="0"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
<Tab fx:id="accountSettingsTab" closable="false"/>
|
||||
<Tab fx:id="arbitratorSettingsTab" text="Arbitrator setup" closable="false"/>
|
||||
<Tab fx:id="arbitratorRegistrationTab" text="Arbitrator registration" closable="false"/>
|
||||
|
||||
</TabPane>
|
|
@ -18,26 +18,22 @@
|
|||
package io.bitsquare.gui.main.account;
|
||||
|
||||
import io.bitsquare.gui.Navigation;
|
||||
import io.bitsquare.gui.common.view.ActivatableView;
|
||||
import io.bitsquare.gui.common.view.CachingViewLoader;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.common.view.View;
|
||||
import io.bitsquare.gui.common.view.ViewLoader;
|
||||
import io.bitsquare.gui.common.view.*;
|
||||
import io.bitsquare.gui.main.MainView;
|
||||
import io.bitsquare.gui.main.account.arbitrator.ArbitratorSettingsView;
|
||||
import io.bitsquare.gui.main.account.arbitratorregistration.ArbitratorRegistrationView;
|
||||
import io.bitsquare.gui.main.account.settings.AccountSettingsView;
|
||||
import io.bitsquare.gui.main.account.setup.AccountSetupWizard;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.control.Tab;
|
||||
import javafx.scene.control.TabPane;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@FxmlView
|
||||
public class AccountView extends ActivatableView<TabPane, AccountViewModel> {
|
||||
|
||||
@FXML Tab accountSettingsTab, arbitratorSettingsTab;
|
||||
@FXML
|
||||
Tab accountSettingsTab, arbitratorRegistrationTab;
|
||||
|
||||
private Navigation.Listener navigationListener;
|
||||
private ChangeListener<Tab> tabChangeListener;
|
||||
|
@ -46,6 +42,7 @@ public class AccountView extends ActivatableView<TabPane, AccountViewModel> {
|
|||
private final Navigation navigation;
|
||||
private View accountSetupWizardView;
|
||||
private Tab tab;
|
||||
private ArbitratorRegistrationView arbitratorRegistrationView;
|
||||
|
||||
@Inject
|
||||
private AccountView(AccountViewModel model, CachingViewLoader viewLoader, Navigation navigation) {
|
||||
|
@ -65,35 +62,35 @@ public class AccountView extends ActivatableView<TabPane, AccountViewModel> {
|
|||
if (newValue == accountSettingsTab)
|
||||
navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class);
|
||||
else
|
||||
navigation.navigateTo(MainView.class, AccountView.class, ArbitratorSettingsView.class);
|
||||
navigation.navigateTo(MainView.class, AccountView.class, ArbitratorRegistrationView.class);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activate() {
|
||||
protected void activate() {
|
||||
navigation.addListener(navigationListener);
|
||||
root.getSelectionModel().selectedItemProperty().addListener(tabChangeListener);
|
||||
|
||||
if (navigation.getCurrentPath().size() == 2 && navigation.getCurrentPath().get(1) == AccountView.class) {
|
||||
if (model.getNeedRegistration()) {
|
||||
/* if (model.getNeedRegistration()) {
|
||||
navigation.navigateTo(MainView.class, AccountView.class, AccountSetupWizard.class);
|
||||
}
|
||||
else {
|
||||
if (root.getSelectionModel().getSelectedItem() == accountSettingsTab)
|
||||
navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class);
|
||||
else
|
||||
navigation.navigateTo(MainView.class, AccountView.class, ArbitratorSettingsView.class);
|
||||
}
|
||||
else {*/
|
||||
if (root.getSelectionModel().getSelectedItem() == accountSettingsTab)
|
||||
navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class);
|
||||
else
|
||||
navigation.navigateTo(MainView.class, AccountView.class, ArbitratorRegistrationView.class);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
protected void deactivate() {
|
||||
navigation.removeListener(navigationListener);
|
||||
root.getSelectionModel().selectedItemProperty().removeListener(tabChangeListener);
|
||||
|
||||
if (accountSetupWizardView != null)
|
||||
tab.setContent(null);
|
||||
/* if (accountSetupWizardView != null)
|
||||
tab.setContent(null);*/
|
||||
}
|
||||
|
||||
private void loadView(Class<? extends View> viewClass) {
|
||||
|
@ -102,24 +99,25 @@ public class AccountView extends ActivatableView<TabPane, AccountViewModel> {
|
|||
if (view instanceof AccountSettingsView) {
|
||||
tab = accountSettingsTab;
|
||||
tab.setText("Account settings");
|
||||
arbitratorSettingsTab.setDisable(false);
|
||||
arbitratorRegistrationTab.setDisable(false);
|
||||
if (arbitratorRegistrationView != null)
|
||||
arbitratorRegistrationView.onTabSelection(false);
|
||||
}
|
||||
else if (view instanceof AccountSetupWizard) {
|
||||
/* else if (view instanceof AccountSetupWizard) {
|
||||
tab = accountSettingsTab;
|
||||
tab.setText("Account setup");
|
||||
arbitratorSettingsTab.setDisable(true);
|
||||
arbitratorRegistrationTab.setDisable(true);
|
||||
accountSetupWizardView = view;
|
||||
}
|
||||
else if (view instanceof ArbitratorSettingsView) {
|
||||
tab = arbitratorSettingsTab;
|
||||
}*/
|
||||
else if (view instanceof ArbitratorRegistrationView) {
|
||||
tab = arbitratorRegistrationTab;
|
||||
arbitratorRegistrationView = (ArbitratorRegistrationView) view;
|
||||
arbitratorRegistrationView.onTabSelection(true);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("View not supported: " + view);
|
||||
}
|
||||
|
||||
// for IRC demo we deactivate the arbitratorSettingsTab
|
||||
// arbitratorSettingsTab.setDisable(true);
|
||||
|
||||
tab.setContent(view.getRoot());
|
||||
root.getSelectionModel().select(tab);
|
||||
}
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
~ 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/>.
|
||||
-->
|
||||
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<AnchorPane fx:id="root" fx:controller="io.bitsquare.gui.main.account.arbitrator.ArbitratorSettingsView"
|
||||
prefHeight="660.0" prefWidth="1000.0"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
<VBox spacing="20">
|
||||
<padding>
|
||||
<Insets left="20" top="20" right="20"/>
|
||||
</padding>
|
||||
<Label text="Arbitration system is not implemented yet. At the moment there are just very basic UI screens for the registration and editing."/>
|
||||
<Button text="Register yourself as an arbitrator" onAction="#onArbitratorRegistration"/>
|
||||
<Button text="Edit my arbitrator details" onAction="#onArbitratorEdit"/>
|
||||
</VBox>
|
||||
</AnchorPane>
|
||||
|
||||
|
||||
|
|
@ -1,80 +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.main.account.arbitrator;
|
||||
|
||||
import io.bitsquare.gui.common.view.AbstractView;
|
||||
import io.bitsquare.gui.common.view.CachingViewLoader;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.common.view.View;
|
||||
import io.bitsquare.gui.common.view.ViewLoader;
|
||||
import io.bitsquare.gui.main.account.arbitrator.registration.ArbitratorRegistrationView;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.*;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
@FxmlView
|
||||
public class ArbitratorSettingsView extends AbstractView {
|
||||
|
||||
private final ViewLoader viewLoader;
|
||||
private final Stage primaryStage;
|
||||
|
||||
@Inject
|
||||
private ArbitratorSettingsView(CachingViewLoader viewLoader, Stage primaryStage) {
|
||||
this.viewLoader = viewLoader;
|
||||
this.primaryStage = primaryStage;
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void onArbitratorRegistration() {
|
||||
View view = viewLoader.load(ArbitratorRegistrationView.class);
|
||||
showStage(view);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void onArbitratorEdit() {
|
||||
View view = viewLoader.load(ArbitratorRegistrationView.class);
|
||||
showStage(view);
|
||||
((ArbitratorRegistrationView) view).setEditMode(true);
|
||||
}
|
||||
|
||||
private void showStage(View view) {
|
||||
Stage stage = new Stage();
|
||||
stage.setTitle("Arbitrator");
|
||||
stage.setMinWidth(800);
|
||||
stage.setMinHeight(400);
|
||||
stage.setWidth(800);
|
||||
stage.setHeight(600);
|
||||
stage.setX(primaryStage.getX() + 50);
|
||||
stage.setY(primaryStage.getY() + 50);
|
||||
stage.initModality(Modality.WINDOW_MODAL);
|
||||
stage.initOwner(primaryStage);
|
||||
Scene scene = new Scene((Parent) view.getRoot(), 800, 600);
|
||||
stage.setScene(scene);
|
||||
stage.setOnCloseRequest(e -> {
|
||||
// need to unset root to be re-uasabel to other popups screens
|
||||
scene.setRoot(new Pane());
|
||||
});
|
||||
stage.show();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ 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/>.
|
||||
-->
|
||||
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<AnchorPane fx:id="root" fx:controller="io.bitsquare.gui.main.account.arbitrator.browser.BrowserView"
|
||||
prefHeight="600" prefWidth="800" AnchorPane.bottomAnchor="30.0" AnchorPane.leftAnchor="10.0"
|
||||
AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
<Pane fx:id="arbitratorProfile"/>
|
||||
<Button fx:id="prevButton" text="Previous" onAction="#onPrevious" AnchorPane.bottomAnchor="45.0"
|
||||
AnchorPane.leftAnchor="10.0"/>
|
||||
<Button fx:id="nextButton" text="Next" onAction="#onNext" AnchorPane.bottomAnchor="45.0"
|
||||
AnchorPane.rightAnchor="10.0"/>
|
||||
<Button fx:id="selectButton" text="Select" onAction="#onSelect" AnchorPane.bottomAnchor="45.0"
|
||||
AnchorPane.leftAnchor="350.0"/>
|
||||
<Button fx:id="closeButton" text="close" onAction="#onClose" AnchorPane.bottomAnchor="10.0"
|
||||
AnchorPane.leftAnchor="10.0"/>
|
||||
</AnchorPane>
|
|
@ -1,140 +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.main.account.arbitrator.browser;
|
||||
|
||||
import io.bitsquare.arbitration.Arbitrator;
|
||||
import io.bitsquare.arbitration.ArbitratorService;
|
||||
import io.bitsquare.gui.common.view.ActivatableView;
|
||||
import io.bitsquare.gui.common.view.CachingViewLoader;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.common.view.View;
|
||||
import io.bitsquare.gui.common.view.ViewLoader;
|
||||
import io.bitsquare.gui.main.account.arbitrator.profile.ArbitratorProfileView;
|
||||
import io.bitsquare.user.AccountSettings;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
@FxmlView
|
||||
public class BrowserView extends ActivatableView<Pane, Void> implements ArbitratorService.Listener {
|
||||
|
||||
@FXML Button prevButton, nextButton, selectButton, closeButton;
|
||||
@FXML Pane arbitratorProfile;
|
||||
|
||||
private Arbitrator currentArbitrator;
|
||||
private ArbitratorProfileView arbitratorProfileView;
|
||||
private int index = -1;
|
||||
|
||||
private final List<Arbitrator> allArbitrators = new ArrayList<>();
|
||||
|
||||
private final ViewLoader viewLoader;
|
||||
private final AccountSettings accountSettings;
|
||||
private final ArbitratorService arbitratorService;
|
||||
|
||||
@Inject
|
||||
public BrowserView(CachingViewLoader viewLoader, AccountSettings accountSettings,
|
||||
ArbitratorService arbitratorService) {
|
||||
this.viewLoader = viewLoader;
|
||||
this.accountSettings = accountSettings;
|
||||
this.arbitratorService = arbitratorService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
arbitratorService.addListener(this);
|
||||
/* arbitratorService.loadAllArbitrators(() -> log.debug("Arbitrators successful loaded " + arbitratorService.getAllArbitrators().size()),
|
||||
(errorMessage -> log.error(errorMessage)));*/
|
||||
|
||||
View view = viewLoader.load(ArbitratorProfileView.class);
|
||||
root.getChildren().set(0, view.getRoot());
|
||||
arbitratorProfileView = (ArbitratorProfileView) view;
|
||||
|
||||
checkButtonState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onArbitratorAdded(Arbitrator arbitrator) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllArbitratorsLoaded(Map<String, Arbitrator> arbitratorsMap) {
|
||||
|
||||
}
|
||||
/*
|
||||
@Override
|
||||
public void onAllArbitratorsLoaded(List<Arbitrator> arbitrators) {
|
||||
allArbitrators.clear();
|
||||
allArbitrators.addAll(arbitrators);
|
||||
|
||||
if (!allArbitrators.isEmpty()) {
|
||||
index = 0;
|
||||
currentArbitrator = allArbitrators.get(index);
|
||||
arbitratorProfileView.applyArbitrator(currentArbitrator);
|
||||
checkButtonState();
|
||||
}
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public void onArbitratorRemoved(Arbitrator arbitrator) {
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void onPrevious() {
|
||||
if (index > 0) {
|
||||
index--;
|
||||
currentArbitrator = allArbitrators.get(index);
|
||||
arbitratorProfileView.applyArbitrator(currentArbitrator);
|
||||
}
|
||||
checkButtonState();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void onNext() {
|
||||
if (index < allArbitrators.size() - 1) {
|
||||
index++;
|
||||
currentArbitrator = allArbitrators.get(index);
|
||||
arbitratorProfileView.applyArbitrator(currentArbitrator);
|
||||
}
|
||||
checkButtonState();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void onSelect() {
|
||||
accountSettings.addAcceptedArbitrator(currentArbitrator);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void onClose() {
|
||||
Stage stage = (Stage) root.getScene().getWindow();
|
||||
stage.close();
|
||||
}
|
||||
|
||||
private void checkButtonState() {
|
||||
prevButton.setDisable(index < 1);
|
||||
nextButton.setDisable(index == allArbitrators.size() - 1 || index == -1);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ 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/>.
|
||||
-->
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<GridPane fx:id="root" fx:controller="io.bitsquare.gui.main.account.arbitrator.profile.ArbitratorProfileView"
|
||||
hgap="5.0" vgap="5.0" AnchorPane.bottomAnchor="80.0" AnchorPane.leftAnchor="10.0"
|
||||
AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
|
||||
<Label fx:id="nameLabel" text="Name:"/>
|
||||
<TextField fx:id="nameTextField" editable="false" focusTraversable="false" GridPane.columnIndex="1"/>
|
||||
<Label text="Languages:" GridPane.rowIndex="1"/>
|
||||
<TextField fx:id="languagesTextField" editable="false" focusTraversable="false" GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="1"/>
|
||||
|
||||
<Label text="Reputation:" GridPane.rowIndex="2"/>
|
||||
<TextField fx:id="reputationTextField" editable="false" focusTraversable="false" GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="2"/>
|
||||
|
||||
<Label text="Max. trade volume:" GridPane.rowIndex="3"/>
|
||||
<TextField fx:id="maxTradeVolumeTextField" editable="false" focusTraversable="false" GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="3"/>
|
||||
|
||||
<Label text="Passive service fee:" GridPane.rowIndex="4"/>
|
||||
<TextField fx:id="passiveServiceFeeTextField" editable="false" focusTraversable="false" GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="4"/>
|
||||
|
||||
<Label text="Arbitration fee:" GridPane.rowIndex="5"/>
|
||||
<TextField fx:id="feeTextField" editable="false" focusTraversable="false" GridPane.columnIndex="1"
|
||||
GridPane.columnSpan="2" GridPane.rowIndex="5"/>
|
||||
|
||||
<Label text="Methods of arbitration:" GridPane.rowIndex="6"/>
|
||||
<TextField fx:id="methodsTextField" editable="false" focusTraversable="false" GridPane.columnIndex="1"
|
||||
GridPane.columnSpan="2" GridPane.rowIndex="6"/>
|
||||
|
||||
<Label text="ID verifications:" GridPane.rowIndex="7"/>
|
||||
<TextField fx:id="idVerificationsTextField" editable="false" focusTraversable="false" GridPane.columnIndex="1"
|
||||
GridPane.columnSpan="2" GridPane.rowIndex="7"/>
|
||||
|
||||
<Label text="Web page:" GridPane.rowIndex="9"/>
|
||||
<TextField fx:id="webPageTextField" editable="false" focusTraversable="false" GridPane.columnIndex="1"
|
||||
GridPane.columnSpan="2" GridPane.rowIndex="9"/>
|
||||
|
||||
<Label text="Description:" GridPane.rowIndex="10" GridPane.valignment="TOP">
|
||||
<GridPane.margin>
|
||||
<Insets top="10.0"/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<TextArea fx:id="descriptionTextArea" editable="false" focusTraversable="false" prefHeight="150.0"
|
||||
GridPane.columnIndex="1" GridPane.columnSpan="2" GridPane.rowIndex="10"/>
|
||||
|
||||
<columnConstraints>
|
||||
<ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES" minWidth="10.0"/>
|
||||
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0"/>
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0"/>
|
||||
</columnConstraints>
|
||||
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="ALWAYS"/>
|
||||
</rowConstraints>
|
||||
|
||||
</GridPane>
|
|
@ -1,73 +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.main.account.arbitrator.profile;
|
||||
|
||||
import io.bitsquare.arbitration.Arbitrator;
|
||||
import io.bitsquare.gui.common.view.AbstractView;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
|
||||
@FxmlView
|
||||
public class ArbitratorProfileView extends AbstractView {
|
||||
|
||||
@FXML Label nameLabel;
|
||||
@FXML TextArea descriptionTextArea;
|
||||
@FXML TextField nameTextField, languagesTextField, reputationTextField, feeTextField, methodsTextField,
|
||||
passiveServiceFeeTextField, idVerificationsTextField, webPageTextField, maxTradeVolumeTextField;
|
||||
|
||||
private final BSFormatter formatter;
|
||||
|
||||
@Inject
|
||||
public ArbitratorProfileView(BSFormatter formatter) {
|
||||
this.formatter = formatter;
|
||||
}
|
||||
|
||||
public void applyArbitrator(Arbitrator arbitrator) {
|
||||
if (arbitrator != null && arbitrator.getIdType() != null) {
|
||||
String name = "";
|
||||
switch (arbitrator.getIdType()) {
|
||||
case REAL_LIFE_ID:
|
||||
name = "Name:";
|
||||
break;
|
||||
case NICKNAME:
|
||||
name = "Nickname:";
|
||||
break;
|
||||
case COMPANY:
|
||||
name = "Company:";
|
||||
break;
|
||||
}
|
||||
nameLabel.setText(name);
|
||||
|
||||
nameTextField.setText(arbitrator.getName());
|
||||
// languagesTextField.setText(formatter.languageLocalesToString(arbitrator.getLanguages()));
|
||||
reputationTextField.setText(arbitrator.getReputation().toString());
|
||||
feeTextField.setText(String.valueOf(arbitrator.getFee() + " BTC"));
|
||||
methodsTextField.setText(formatter.arbitrationMethodsToString(arbitrator.getArbitrationMethods()));
|
||||
idVerificationsTextField.setText(
|
||||
formatter.arbitrationIDVerificationsToString(arbitrator.getIdVerifications()));
|
||||
webPageTextField.setText(arbitrator.getWebUrl());
|
||||
descriptionTextArea.setText(arbitrator.getDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,192 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ 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/>.
|
||||
-->
|
||||
|
||||
<?import io.bitsquare.gui.components.confidence.ConfidenceProgressIndicator?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<AnchorPane fx:id="root"
|
||||
fx:controller="io.bitsquare.gui.main.account.arbitrator.registration.ArbitratorRegistrationView"
|
||||
prefHeight="600" prefWidth="800" AnchorPane.bottomAnchor="30.0" AnchorPane.leftAnchor="10.0"
|
||||
AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
|
||||
<Accordion fx:id="accordion" AnchorPane.bottomAnchor="30.0" AnchorPane.leftAnchor="10.0"
|
||||
AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0">
|
||||
<panes>
|
||||
|
||||
<TitledPane fx:id="profileTitledPane" text="Profile" expanded="true">
|
||||
|
||||
<GridPane hgap="5.0" vgap="5.0" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="10.0"
|
||||
AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0">
|
||||
|
||||
<Label fx:id="nameLabel" text="Name:"/>
|
||||
<TextField fx:id="nameTextField" GridPane.columnIndex="1" GridPane.columnSpan="3"/>
|
||||
|
||||
<Label text="ID type:" GridPane.rowIndex="1"/>
|
||||
<TextField fx:id="idTypeTextField" editable="false" focusTraversable="false"
|
||||
GridPane.columnIndex="1" GridPane.rowIndex="1"/>
|
||||
<ComboBox fx:id="idTypeComboBox" onAction="#onSelectIDType" promptText="Select ID type"
|
||||
prefWidth="150.0" GridPane.columnIndex="3" GridPane.rowIndex="1"/>
|
||||
|
||||
<Label text="Supported language(s):" GridPane.rowIndex="2"/>
|
||||
<TextField fx:id="languagesTextField" editable="false" focusTraversable="false"
|
||||
GridPane.columnIndex="1" GridPane.rowIndex="2"/>
|
||||
<Button onAction="#onClearLanguages" text="Clear" GridPane.columnIndex="2" GridPane.rowIndex="2"/>
|
||||
<ComboBox fx:id="languageComboBox" onAction="#onAddLanguage" prefWidth="150.0"
|
||||
promptText="Add language" GridPane.columnIndex="3" GridPane.rowIndex="2"/>
|
||||
|
||||
<Label text="Max. trade volume:" GridPane.rowIndex="3"/>
|
||||
<TextField fx:id="maxTradeVolumeTextField" promptText="Recommended: 0.1 - 2 BTC"
|
||||
GridPane.columnIndex="1" GridPane.rowIndex="3" GridPane.columnSpan="3"/>
|
||||
|
||||
<Label text="Passive service fee (%):" GridPane.rowIndex="4"/>
|
||||
<TextField fx:id="passiveServiceFeeTextField" promptText="Recommended: 0.1 - 1%"
|
||||
GridPane.columnIndex="1" GridPane.rowIndex="4" GridPane.columnSpan="3"/>
|
||||
|
||||
<Label text="Min. passive service fee" GridPane.rowIndex="5"/>
|
||||
<TextField fx:id="minPassiveServiceFeeTextField" promptText="Recommended: 0.0001 - 0.005 BTC"
|
||||
GridPane.columnIndex="1" GridPane.rowIndex="5" GridPane.columnSpan="3"/>
|
||||
|
||||
<Label text="Arbitration fee (%):" GridPane.rowIndex="6"/>
|
||||
<TextField fx:id="arbitrationFeeTextField" promptText="Recommended: 5 - 20 %"
|
||||
GridPane.columnIndex="1" GridPane.rowIndex="6" GridPane.columnSpan="3"/>
|
||||
|
||||
<Label text="Min. arbitration fee:" GridPane.rowIndex="7"/>
|
||||
<TextField fx:id="minArbitrationFeeTextField" promptText="Recommended: 0.1 - 0.2 BTC"
|
||||
GridPane.columnIndex="1" GridPane.rowIndex="7" GridPane.columnSpan="3"/>
|
||||
|
||||
<Label text="Method(s) used for arbitration:" GridPane.rowIndex="8"/>
|
||||
<TextField fx:id="methodsTextField" editable="false" focusTraversable="false"
|
||||
GridPane.columnIndex="1" GridPane.rowIndex="8"/>
|
||||
<Button onAction="#onClearMethods" text="Clear" GridPane.columnIndex="2" GridPane.rowIndex="8"/>
|
||||
<ComboBox fx:id="methodsComboBox" onAction="#onAddMethod" prefWidth="150.0" promptText="Add method"
|
||||
GridPane.columnIndex="3" GridPane.rowIndex="8"/>
|
||||
|
||||
<Label text="Offer ID verification(s):" GridPane.rowIndex="9"/>
|
||||
<TextField fx:id="idVerificationsTextField" promptText="Optional" editable="false"
|
||||
focusTraversable="false" GridPane.columnIndex="1" GridPane.rowIndex="9"/>
|
||||
<Button onAction="#onClearIDVerifications" text="Clear" GridPane.columnIndex="2"
|
||||
GridPane.rowIndex="9"/>
|
||||
<ComboBox fx:id="idVerificationsComboBox" onAction="#onAddIDVerification" prefWidth="150.0"
|
||||
promptText="Add verification" GridPane.columnIndex="3"
|
||||
GridPane.rowIndex="9"/>
|
||||
|
||||
<Label text="Web page:" GridPane.rowIndex="10"/>
|
||||
<TextField fx:id="webPageTextField" GridPane.columnIndex="1" GridPane.columnSpan="3"
|
||||
GridPane.rowIndex="10"/>
|
||||
|
||||
<Label text="Description:" GridPane.rowIndex="11" GridPane.valignment="TOP">
|
||||
<GridPane.margin>
|
||||
<Insets top="10.0"/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<TextArea fx:id="descriptionTextArea" prefHeight="150.0" GridPane.columnIndex="1"
|
||||
GridPane.columnSpan="3" GridPane.rowIndex="11"/>
|
||||
|
||||
<Button fx:id="saveProfileButton" defaultButton="true" onAction="#onSaveProfile" text="Next"
|
||||
GridPane.columnIndex="1" GridPane.rowIndex="12"/>
|
||||
|
||||
<columnConstraints>
|
||||
<ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES" minWidth="10.0"/>
|
||||
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0"/>
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0"/>
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="150.0"/>
|
||||
</columnConstraints>
|
||||
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="ALWAYS"/>
|
||||
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
|
||||
</rowConstraints>
|
||||
|
||||
</GridPane>
|
||||
</TitledPane>
|
||||
|
||||
<TitledPane fx:id="paySecurityDepositTitledPane" text="Pay security deposit">
|
||||
<AnchorPane>
|
||||
<VBox spacing="20" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
|
||||
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||
|
||||
<Label fx:id="infoLabel"/>
|
||||
|
||||
<GridPane hgap="5.0" vgap="5.0">
|
||||
|
||||
<Label text="Security deposit address:"/>
|
||||
<TextField fx:id="securityDepositAddressTextField" editable="false" focusTraversable="false"
|
||||
GridPane.columnIndex="1"/>
|
||||
<Label fx:id="copyIcon" id="copy-icon" GridPane.columnIndex="2">
|
||||
<padding>
|
||||
<Insets bottom="0.0" left="0.0" right="0.0" top="-1.0"/>
|
||||
</padding>
|
||||
<tooltip>
|
||||
<Tooltip text="Copy address to clipboard"/>
|
||||
</tooltip>
|
||||
</Label>
|
||||
|
||||
<Label text="Balance:" GridPane.rowIndex="1"/>
|
||||
<TextField fx:id="balanceTextField" editable="false" focusTraversable="false"
|
||||
GridPane.columnIndex="1" GridPane.rowIndex="1"/>
|
||||
<ConfidenceProgressIndicator fx:id="progressIndicator" GridPane.columnIndex="2"
|
||||
GridPane.halignment="LEFT"
|
||||
GridPane.rowIndex="1" GridPane.rowSpan="2"
|
||||
GridPane.valignment="TOP">
|
||||
<GridPane.margin>
|
||||
<Insets top="2.0"/>
|
||||
</GridPane.margin>
|
||||
</ConfidenceProgressIndicator>
|
||||
<Label fx:id="confirmationLabel" text="Checking confirmations..." GridPane.columnIndex="3"
|
||||
GridPane.rowIndex="1"/>
|
||||
|
||||
<Button fx:id="paymentDoneButton" defaultButton="true" onAction="#onPaymentDone"
|
||||
text="Payment done" disable="true" GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="2"/>
|
||||
|
||||
|
||||
<columnConstraints>
|
||||
<ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES"/>
|
||||
<ColumnConstraints hgrow="ALWAYS"/>
|
||||
<ColumnConstraints hgrow="SOMETIMES" prefWidth="20.0" minWidth="20"/>
|
||||
<ColumnConstraints hgrow="SOMETIMES"/>
|
||||
<ColumnConstraints hgrow="SOMETIMES"/>
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
</rowConstraints>
|
||||
|
||||
</GridPane>
|
||||
</VBox>
|
||||
</AnchorPane>
|
||||
</TitledPane>
|
||||
|
||||
</panes>
|
||||
</Accordion>
|
||||
</AnchorPane>
|
|
@ -1,366 +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.main.account.arbitrator.registration;
|
||||
|
||||
import io.bitsquare.arbitration.Arbitrator;
|
||||
import io.bitsquare.arbitration.ArbitratorService;
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.gui.common.view.ActivatableView;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.components.confidence.ConfidenceProgressIndicator;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.gui.util.GUIUtil;
|
||||
import io.bitsquare.locale.BSResources;
|
||||
import io.bitsquare.locale.LanguageUtil;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.core.Wallet;
|
||||
import org.bitcoinj.core.WalletEventListener;
|
||||
import org.bitcoinj.script.Script;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
import de.jensd.fx.fontawesome.AwesomeDude;
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
|
||||
@FxmlView
|
||||
public class ArbitratorRegistrationView extends ActivatableView<AnchorPane, Void> {
|
||||
|
||||
@FXML Accordion accordion;
|
||||
@FXML TextArea descriptionTextArea;
|
||||
@FXML Button saveProfileButton, paymentDoneButton;
|
||||
@FXML Label nameLabel, infoLabel, copyIcon, confirmationLabel;
|
||||
@FXML ComboBox<String> languageComboBox;
|
||||
@FXML ComboBox<Arbitrator.ID_TYPE> idTypeComboBox;
|
||||
@FXML ComboBox<Arbitrator.METHOD> methodsComboBox;
|
||||
@FXML ConfidenceProgressIndicator progressIndicator;
|
||||
@FXML ComboBox<Arbitrator.ID_VERIFICATION> idVerificationsComboBox;
|
||||
@FXML TitledPane profileTitledPane, paySecurityDepositTitledPane;
|
||||
@FXML TextField nameTextField, idTypeTextField, languagesTextField, maxTradeVolumeTextField,
|
||||
passiveServiceFeeTextField, minPassiveServiceFeeTextField, arbitrationFeeTextField,
|
||||
minArbitrationFeeTextField, methodsTextField, idVerificationsTextField, webPageTextField,
|
||||
securityDepositAddressTextField, balanceTextField;
|
||||
|
||||
private boolean isEditMode;
|
||||
private Arbitrator.ID_TYPE idType;
|
||||
|
||||
private List<String> languageCodes = new ArrayList<>();
|
||||
private List<Arbitrator.METHOD> methodList = new ArrayList<>();
|
||||
private List<Arbitrator.ID_VERIFICATION> idVerificationList = new ArrayList<>();
|
||||
|
||||
// TODO not set
|
||||
private Arbitrator arbitrator;
|
||||
|
||||
private final WalletService walletService;
|
||||
private final ArbitratorService arbitratorService;
|
||||
private final BSFormatter formatter;
|
||||
|
||||
|
||||
@Inject
|
||||
private ArbitratorRegistrationView(WalletService walletService,
|
||||
ArbitratorService arbitratorService, BSFormatter formatter) {
|
||||
this.walletService = walletService;
|
||||
this.arbitratorService = arbitratorService;
|
||||
this.formatter = formatter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
accordion.setExpandedPane(profileTitledPane);
|
||||
|
||||
applyArbitrator();
|
||||
|
||||
languageComboBox.setItems(FXCollections.observableArrayList(LanguageUtil.getAllLanguageLocaleCodes()));
|
||||
languageComboBox.setConverter(new StringConverter<String>() {
|
||||
@Override
|
||||
public String toString(String code) {
|
||||
return LanguageUtil.getDisplayName(code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
idTypeComboBox.setItems(FXCollections.observableArrayList(
|
||||
new ArrayList<>(EnumSet.allOf(Arbitrator.ID_TYPE.class))));
|
||||
idTypeComboBox.setConverter(new StringConverter<Arbitrator.ID_TYPE>() {
|
||||
@Override
|
||||
public String toString(Arbitrator.ID_TYPE item) {
|
||||
return BSResources.get(item.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Arbitrator.ID_TYPE fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
methodsComboBox.setItems(FXCollections.observableArrayList(new ArrayList<>(EnumSet.allOf(Arbitrator.METHOD
|
||||
.class))));
|
||||
methodsComboBox.setConverter(new StringConverter<Arbitrator.METHOD>() {
|
||||
@Override
|
||||
public String toString(Arbitrator.METHOD item) {
|
||||
return BSResources.get(item.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Arbitrator.METHOD fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
idVerificationsComboBox.setItems(
|
||||
FXCollections.observableArrayList(new ArrayList<>(EnumSet.allOf(Arbitrator.ID_VERIFICATION.class))));
|
||||
idVerificationsComboBox.setConverter(new StringConverter<Arbitrator.ID_VERIFICATION>() {
|
||||
@Override
|
||||
public String toString(Arbitrator.ID_VERIFICATION item) {
|
||||
return BSResources.get(item.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Arbitrator.ID_VERIFICATION fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setEditMode(boolean isEditMode) {
|
||||
this.isEditMode = isEditMode;
|
||||
|
||||
if (isEditMode) {
|
||||
saveProfileButton.setText("Save");
|
||||
profileTitledPane.setCollapsible(false);
|
||||
paySecurityDepositTitledPane.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void onSelectIDType() {
|
||||
idType = idTypeComboBox.getSelectionModel().getSelectedItem();
|
||||
if (idType != null) {
|
||||
idTypeTextField.setText(BSResources.get(idType.toString()));
|
||||
|
||||
String name = "";
|
||||
switch (idType) {
|
||||
case REAL_LIFE_ID:
|
||||
name = "Name:";
|
||||
break;
|
||||
case NICKNAME:
|
||||
name = "Nickname:";
|
||||
break;
|
||||
case COMPANY:
|
||||
name = "Company:";
|
||||
break;
|
||||
}
|
||||
nameLabel.setText(name);
|
||||
|
||||
idTypeComboBox.getSelectionModel().clearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void onAddLanguage() {
|
||||
String item = languageComboBox.getSelectionModel().getSelectedItem();
|
||||
if (!languageCodes.contains(item) && item != null) {
|
||||
languageCodes.add(item);
|
||||
languagesTextField.setText(formatter.languageCodesToString(languageCodes));
|
||||
languageComboBox.getSelectionModel().clearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void onClearLanguages() {
|
||||
languageCodes.clear();
|
||||
languagesTextField.setText("");
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void onAddMethod() {
|
||||
Arbitrator.METHOD item = methodsComboBox.getSelectionModel().getSelectedItem();
|
||||
if (!methodList.contains(item) && item != null) {
|
||||
methodList.add(item);
|
||||
methodsTextField.setText(formatter.arbitrationMethodsToString(methodList));
|
||||
methodsComboBox.getSelectionModel().clearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void onClearMethods() {
|
||||
methodList.clear();
|
||||
methodsTextField.setText("");
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void onAddIDVerification() {
|
||||
Arbitrator.ID_VERIFICATION idVerification = idVerificationsComboBox.getSelectionModel().getSelectedItem();
|
||||
if (idVerification != null) {
|
||||
if (!idVerificationList.contains(idVerification)) {
|
||||
idVerificationList.add(idVerification);
|
||||
idVerificationsTextField.setText(
|
||||
formatter.arbitrationIDVerificationsToString(idVerificationList));
|
||||
}
|
||||
}
|
||||
|
||||
idVerificationsComboBox.getSelectionModel().clearSelection();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void onClearIDVerifications() {
|
||||
idVerificationList.clear();
|
||||
idVerificationsTextField.setText("");
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void onSaveProfile() {
|
||||
arbitrator.setWebUrl(webPageTextField.getText());
|
||||
arbitrator.setFee(formatter.parseToCoin(arbitrationFeeTextField.getText()));
|
||||
arbitrator.setIdType(idType);
|
||||
arbitrator.setIdVerifications(idVerificationList);
|
||||
arbitrator.setLanguageCodes(languageCodes);
|
||||
arbitrator.setArbitrationMethods(methodList);
|
||||
arbitrator.save();
|
||||
|
||||
if (isEditMode) {
|
||||
close();
|
||||
}
|
||||
else {
|
||||
setupPaySecurityDepositScreen();
|
||||
accordion.setExpandedPane(paySecurityDepositTitledPane);
|
||||
}
|
||||
|
||||
arbitratorService.addArbitrator(arbitrator,
|
||||
() -> {
|
||||
// log.debug("arbitrator added successfully " + arbitratorService.getAllArbitrators().size());
|
||||
},
|
||||
log::error);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void onPaymentDone() {
|
||||
}
|
||||
|
||||
private void setupPaySecurityDepositScreen() {
|
||||
infoLabel.setText("You need to pay 2 x the max. trading volume as security deposit.\n\nThat payment will be " +
|
||||
"locked into a MultiSig fund and be refunded when you leave the arbitration pool.\nIn case of fraud " +
|
||||
"(collusion, not fulfilling the min. dispute quality requirements) you will lose your security " +
|
||||
"deposit.\n" +
|
||||
"If you have a negative feedback from your clients you will lose a part of the security deposit,\n" +
|
||||
"depending on the overall relation of negative to positive ratings you received after a dispute " +
|
||||
"resolution.\n\nPlease pay in 2 BTC");
|
||||
|
||||
|
||||
String securityDepositAddress = walletService.getRegistrationAddressEntry() != null ?
|
||||
walletService.getRegistrationAddressEntry().toString() : "";
|
||||
securityDepositAddressTextField.setText(securityDepositAddress);
|
||||
|
||||
AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY);
|
||||
copyIcon.setOnMouseClicked(e -> GUIUtil.copyToClipboard(securityDepositAddress));
|
||||
|
||||
paymentDoneButton.setDisable(walletService.getArbitratorDepositBalance().isZero());
|
||||
log.debug("getArbitratorDepositBalance " + walletService.getArbitratorDepositBalance());
|
||||
walletService.getWallet().addEventListener(new WalletEventListener() {
|
||||
@Override
|
||||
public void onCoinsReceived(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) {
|
||||
paymentDoneButton.setDisable(newBalance.isZero());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCoinsSent(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReorganize(Wallet wallet) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalletChanged(Wallet wallet) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScriptsChanged(Wallet var1, List<Script> scripts, boolean isAddingScripts) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onKeysAdded(List<ECKey> keys) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void applyArbitrator() {
|
||||
if (arbitrator != null) {
|
||||
String name = "";
|
||||
switch (arbitrator.getIdType()) {
|
||||
case REAL_LIFE_ID:
|
||||
name = "Name:";
|
||||
break;
|
||||
case NICKNAME:
|
||||
name = "Nickname:";
|
||||
break;
|
||||
case COMPANY:
|
||||
name = "Company:";
|
||||
break;
|
||||
}
|
||||
nameLabel.setText(name);
|
||||
|
||||
nameTextField.setText(arbitrator.getName());
|
||||
idTypeTextField.setText(BSResources.get(arbitrator.getIdType().toString()));
|
||||
// languagesTextField.setText(formatter.languageLocalesToString(arbitrator.getLanguages()));
|
||||
arbitrationFeeTextField.setText(String.valueOf(arbitrator.getFee()));
|
||||
methodsTextField.setText(formatter.arbitrationMethodsToString(arbitrator.getArbitrationMethods()));
|
||||
idVerificationsTextField.setText(
|
||||
formatter.arbitrationIDVerificationsToString(arbitrator.getIdVerifications()));
|
||||
webPageTextField.setText(arbitrator.getWebUrl());
|
||||
descriptionTextArea.setText(arbitrator.getDescription());
|
||||
|
||||
idType = arbitrator.getIdType();
|
||||
languageCodes = arbitrator.getLanguageCodes();
|
||||
methodList = arbitrator.getArbitrationMethods();
|
||||
idVerificationList = arbitrator.getIdVerifications();
|
||||
}
|
||||
}
|
||||
|
||||
private void close() {
|
||||
Stage stage = (Stage) root.getScene().getWindow();
|
||||
stage.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,92 +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.main.account.content.altcoin;
|
||||
|
||||
import io.bitsquare.fiat.FiatAccount;
|
||||
import io.bitsquare.gui.common.model.Activatable;
|
||||
import io.bitsquare.gui.common.model.DataModel;
|
||||
import io.bitsquare.locale.CountryUtil;
|
||||
import io.bitsquare.locale.CurrencyUtil;
|
||||
import io.bitsquare.user.User;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
class AltCoinAccountDataModel implements Activatable, DataModel {
|
||||
|
||||
private final User user;
|
||||
|
||||
final StringProperty nickName = new SimpleStringProperty();
|
||||
final StringProperty currencyCode = new SimpleStringProperty();
|
||||
final ObjectProperty<FiatAccount.Type> type = new SimpleObjectProperty<>();
|
||||
|
||||
final ObservableList<FiatAccount.Type> allTypes =
|
||||
FXCollections.observableArrayList(FiatAccount.Type.getAllBankAccountTypes());
|
||||
final ObservableList<String> allCurrencyCodes = FXCollections.observableArrayList(CurrencyUtil.getAllCurrencyCodes());
|
||||
final ObservableList<FiatAccount> allFiatAccounts = FXCollections.observableArrayList();
|
||||
|
||||
|
||||
@Inject
|
||||
public AltCoinAccountDataModel(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activate() {
|
||||
allFiatAccounts.setAll(user.fiatAccountsObservableList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
void saveBankAccount() {
|
||||
FiatAccount fiatAccount = new FiatAccount(type.get(),
|
||||
currencyCode.get(),
|
||||
CountryUtil.getDefaultCountry(),
|
||||
nickName.get(),
|
||||
nickName.get(),
|
||||
"irc",
|
||||
"irc");
|
||||
user.addFiatAccount(fiatAccount);
|
||||
allFiatAccounts.setAll(user.fiatAccountsObservableList());
|
||||
reset();
|
||||
}
|
||||
|
||||
void setType(FiatAccount.Type type) {
|
||||
this.type.set(type);
|
||||
}
|
||||
|
||||
void setCurrencyCode(String currencyCode) {
|
||||
this.currencyCode.set(currencyCode);
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
nickName.set(null);
|
||||
|
||||
type.set(null);
|
||||
currencyCode.set(null);
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
~ 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/>.
|
||||
-->
|
||||
|
||||
|
||||
<?import io.bitsquare.gui.components.InfoDisplay?>
|
||||
<?import io.bitsquare.gui.components.InputTextField?>
|
||||
<?import io.bitsquare.gui.components.TitledGroupBg?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<GridPane fx:id="root" fx:controller="io.bitsquare.gui.main.account.content.altcoin.AltCoinAccountView" hgap="5.0"
|
||||
vgap="5.0"
|
||||
AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
|
||||
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
|
||||
<!--
|
||||
Setup
|
||||
-->
|
||||
<TitledGroupBg text="Setup your payments account" GridPane.rowSpan="8"/>
|
||||
|
||||
<Label text="Payments method:" GridPane.rowIndex="0">
|
||||
<GridPane.margin>
|
||||
<Insets top="10"/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<ComboBox fx:id="typesComboBox" promptText="Select payments method" onAction="#onSelectType"
|
||||
GridPane.rowIndex="0" GridPane.columnIndex="1">
|
||||
<GridPane.margin>
|
||||
<Insets top="10"/>
|
||||
</GridPane.margin>
|
||||
</ComboBox>
|
||||
|
||||
<Label text="Check if your nickname is available:" GridPane.rowIndex="1"/>
|
||||
<Hyperlink text="Open IRC @Freenode #bitsquare-trading in browser"
|
||||
onAction="#onOpenIRC" GridPane.columnIndex="1" GridPane.rowIndex="1" minHeight="26"/>
|
||||
|
||||
<Label text="Your IRC nick name:" GridPane.rowIndex="2"/>
|
||||
<InputTextField fx:id="ircNickNameTextField" GridPane.columnIndex="1" GridPane.rowIndex="2"/>
|
||||
|
||||
<Label text="Currency:" GridPane.rowIndex="3"/>
|
||||
<ComboBox fx:id="currencyComboBox" promptText="Select currency"
|
||||
onAction="#onSelectCurrency" GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="3"/>
|
||||
|
||||
<InfoDisplay gridPane="$root" onAction="#onOpenSetupHelp" rowIndex="4"
|
||||
text="The payments account data will be saved in a encrypted form to the Bitcoin block chain and will be used in the trade process for account verification."/>
|
||||
|
||||
<HBox fx:id="buttonsHBox" GridPane.columnIndex="1" GridPane.rowIndex="5" spacing="10">
|
||||
<Button fx:id="saveButton" text="Save" onAction="#onSave" defaultButton="true" disable="true"/>
|
||||
<GridPane.margin>
|
||||
<Insets top="15.0" bottom="5.0"/>
|
||||
</GridPane.margin>
|
||||
</HBox>
|
||||
|
||||
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" halignment="RIGHT" minWidth="250.0"/>
|
||||
<ColumnConstraints hgrow="ALWAYS" minWidth="300.0"/>
|
||||
</columnConstraints>
|
||||
|
||||
<rowConstraints>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
</rowConstraints>
|
||||
|
||||
</GridPane>
|
|
@ -1,196 +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.main.account.content.altcoin;
|
||||
|
||||
import io.bitsquare.fiat.FiatAccount;
|
||||
import io.bitsquare.gui.common.view.ActivatableViewAndModel;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.common.view.Wizard;
|
||||
import io.bitsquare.gui.components.InputTextField;
|
||||
import io.bitsquare.gui.components.Popups;
|
||||
import io.bitsquare.gui.main.help.Help;
|
||||
import io.bitsquare.gui.main.help.HelpId;
|
||||
import io.bitsquare.locale.CurrencyUtil;
|
||||
import io.bitsquare.util.Utilities;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.util.Callback;
|
||||
|
||||
/**
|
||||
* Just temporary for giving the user a possibility to test the app via simulating the bank transfer in a IRC chat.
|
||||
*/
|
||||
@FxmlView
|
||||
public class AltCoinAccountView extends ActivatableViewAndModel<GridPane, AltCoinAccountViewModel> implements Wizard.Step {
|
||||
|
||||
@FXML HBox buttonsHBox;
|
||||
@FXML InputTextField ircNickNameTextField;
|
||||
@FXML Button saveButton;
|
||||
@FXML ComboBox<FiatAccount.Type> typesComboBox;
|
||||
@FXML ComboBox<String> currencyComboBox;
|
||||
|
||||
private Wizard wizard;
|
||||
|
||||
@Inject
|
||||
public AltCoinAccountView(AltCoinAccountViewModel model) {
|
||||
super(model);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doActivate() {
|
||||
ircNickNameTextField.setValidator(model.getNickNameValidator());
|
||||
|
||||
typesComboBox.setItems(model.getAllTypes());
|
||||
typesComboBox.setConverter(model.getTypesConverter());
|
||||
// we use a custom cell for deactivating non IRC items, later we use the standard cell and the StringConverter
|
||||
typesComboBox.setCellFactory(new Callback<ListView<FiatAccount.Type>, ListCell<FiatAccount.Type>>() {
|
||||
@Override
|
||||
public ListCell<FiatAccount.Type> call(ListView<FiatAccount.Type> p) {
|
||||
return new ListCell<FiatAccount.Type>() {
|
||||
|
||||
@Override
|
||||
protected void updateItem(FiatAccount.Type item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
setText(model.getBankAccountType(item));
|
||||
|
||||
if (item == null || empty) {
|
||||
setGraphic(null);
|
||||
}
|
||||
else if (item != FiatAccount.Type.IRC) {
|
||||
setOpacity(0.3);
|
||||
setDisable(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
typesComboBox.getSelectionModel().select(0);
|
||||
|
||||
currencyComboBox.setItems(model.getAllCurrencyCodes());
|
||||
currencyComboBox.setConverter(model.getCurrencyConverter());
|
||||
// we use a custom cell for deactivating non EUR items, later we use the standard cell and the StringConverter
|
||||
currencyComboBox.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
|
||||
@Override
|
||||
public ListCell<String> call(ListView<String> p) {
|
||||
return new ListCell<String>() {
|
||||
|
||||
@Override
|
||||
protected void updateItem(String currencyCode, boolean empty) {
|
||||
super.updateItem(currencyCode, empty);
|
||||
|
||||
if (currencyCode == null || empty) {
|
||||
setGraphic(null);
|
||||
}
|
||||
else {
|
||||
setText(currencyCode + " (" + CurrencyUtil.getDisplayName(currencyCode) + ")");
|
||||
|
||||
if (!currencyCode.equals("EUR")) {
|
||||
setOpacity(0.3);
|
||||
setDisable(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
currencyComboBox.getSelectionModel().select(0);
|
||||
|
||||
setupListeners();
|
||||
setupBindings();
|
||||
|
||||
Platform.runLater(() -> Popups.openInfoPopup("Demo setup for simulating the banking transfer",
|
||||
"For demo purposes we use a special setup so that users can simulate the banking transfer when " +
|
||||
"meeting in an IRC chat room.\n" +
|
||||
"You need to define your IRC nickname and later in the trade process you can find your " +
|
||||
"trading partner with his IRC nickname in the chat room and simulate the bank transfer " +
|
||||
"activities, which are:\n\n" +
|
||||
"1. Bitcoin buyer indicates that he has started the bank transfer.\n\n" +
|
||||
"2. Bitcoin seller confirms that he has received the national currency from the " +
|
||||
"bank transfer."));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWizard(Wizard wizard) {
|
||||
this.wizard = wizard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideWizardNavigation() {
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onSelectType() {
|
||||
model.setType(typesComboBox.getSelectionModel().getSelectedItem());
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onSelectCurrency() {
|
||||
model.setCurrencyCode(currencyComboBox.getSelectionModel().getSelectedItem());
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onSave() {
|
||||
boolean isValid = model.requestSaveBankAccount().isValid;
|
||||
if (wizard != null && isValid)
|
||||
wizard.nextStep(this);
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onOpenSetupHelp() {
|
||||
Help.openWindow(HelpId.SETUP_FIAT_ACCOUNT);
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onOpenIRC() {
|
||||
try {
|
||||
Utilities.openWebPage("https://webchat.freenode.net/?channels=bitsquare-trading");
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
Popups.openWarningPopup("Warning", "Opening browser failed. Please check your internet " +
|
||||
"connection.");
|
||||
}
|
||||
}
|
||||
|
||||
private void setupListeners() {
|
||||
model.type.addListener((ov, oldValue, newValue) -> {
|
||||
if (newValue != null)
|
||||
typesComboBox.getSelectionModel().select(typesComboBox.getItems().indexOf(newValue));
|
||||
else
|
||||
typesComboBox.getSelectionModel().clearSelection();
|
||||
});
|
||||
|
||||
model.currencyCode.addListener((ov, oldValue, newValue) -> {
|
||||
if (newValue != null)
|
||||
currencyComboBox.getSelectionModel().select(currencyComboBox.getItems().indexOf(newValue));
|
||||
else
|
||||
currencyComboBox.getSelectionModel().clearSelection();
|
||||
});
|
||||
}
|
||||
|
||||
private void setupBindings() {
|
||||
// input
|
||||
ircNickNameTextField.textProperty().bindBidirectional(model.ircNickName);
|
||||
saveButton.disableProperty().bind(model.saveButtonDisable);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,142 +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.main.account.content.altcoin;
|
||||
|
||||
import io.bitsquare.fiat.FiatAccount;
|
||||
import io.bitsquare.gui.common.model.ActivatableWithDataModel;
|
||||
import io.bitsquare.gui.common.model.ViewModel;
|
||||
import io.bitsquare.gui.util.validation.BankAccountNumberValidator;
|
||||
import io.bitsquare.gui.util.validation.InputValidator;
|
||||
import io.bitsquare.locale.BSResources;
|
||||
import io.bitsquare.locale.CurrencyUtil;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
class AltCoinAccountViewModel extends ActivatableWithDataModel<AltCoinAccountDataModel> implements ViewModel {
|
||||
|
||||
private final InputValidator nickNameValidator;
|
||||
|
||||
final StringProperty ircNickName = new SimpleStringProperty();
|
||||
final StringProperty currencyCode = new SimpleStringProperty();
|
||||
final BooleanProperty saveButtonDisable = new SimpleBooleanProperty(true);
|
||||
final ObjectProperty<FiatAccount.Type> type = new SimpleObjectProperty<>();
|
||||
|
||||
@Inject
|
||||
public AltCoinAccountViewModel(AltCoinAccountDataModel dataModel, BankAccountNumberValidator nickNameValidator) {
|
||||
super(dataModel);
|
||||
this.nickNameValidator = nickNameValidator;
|
||||
|
||||
// input
|
||||
ircNickName.bindBidirectional(dataModel.nickName);
|
||||
type.bindBidirectional(dataModel.type);
|
||||
currencyCode.bindBidirectional(dataModel.currencyCode);
|
||||
|
||||
dataModel.nickName.addListener((ov, oldValue, newValue) -> validateInput());
|
||||
}
|
||||
|
||||
|
||||
InputValidator.ValidationResult requestSaveBankAccount() {
|
||||
InputValidator.ValidationResult result = validateInput();
|
||||
if (result.isValid) {
|
||||
dataModel.saveBankAccount();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
StringConverter<FiatAccount.Type> getTypesConverter() {
|
||||
return new StringConverter<FiatAccount.Type>() {
|
||||
@Override
|
||||
public String toString(FiatAccount.Type TypeInfo) {
|
||||
return BSResources.get(TypeInfo.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FiatAccount.Type fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
String getBankAccountType(FiatAccount.Type fiatAccountType) {
|
||||
return fiatAccountType != null ? BSResources.get(fiatAccountType.toString()) : "";
|
||||
}
|
||||
|
||||
StringConverter<String> getCurrencyConverter() {
|
||||
return new StringConverter<String>() {
|
||||
@Override
|
||||
public String toString(String currencyCode) {
|
||||
return currencyCode + " (" + CurrencyUtil.getDisplayName(currencyCode) + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
ObservableList<FiatAccount.Type> getAllTypes() {
|
||||
return dataModel.allTypes;
|
||||
}
|
||||
|
||||
ObservableList<String> getAllCurrencyCodes() {
|
||||
return dataModel.allCurrencyCodes;
|
||||
}
|
||||
|
||||
InputValidator getNickNameValidator() {
|
||||
return nickNameValidator;
|
||||
}
|
||||
|
||||
|
||||
void setType(FiatAccount.Type type) {
|
||||
dataModel.setType(type);
|
||||
validateInput();
|
||||
}
|
||||
|
||||
void setCurrencyCode(String currencyCode) {
|
||||
dataModel.setCurrencyCode(currencyCode);
|
||||
validateInput();
|
||||
}
|
||||
|
||||
|
||||
private InputValidator.ValidationResult validateInput() {
|
||||
InputValidator.ValidationResult result = nickNameValidator.validate(dataModel.nickName.get());
|
||||
if (dataModel.currencyCode.get() == null)
|
||||
result = new InputValidator.ValidationResult(false,
|
||||
"You have not selected a currency");
|
||||
if (result.isValid) {
|
||||
if (dataModel.type.get() == null)
|
||||
result = new InputValidator.ValidationResult(false,
|
||||
"You have not selected a payments method");
|
||||
}
|
||||
|
||||
saveButtonDisable.set(!result.isValid);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,170 +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.main.account.content.fiat;
|
||||
|
||||
import io.bitsquare.fiat.FiatAccount;
|
||||
import io.bitsquare.gui.common.model.Activatable;
|
||||
import io.bitsquare.gui.common.model.DataModel;
|
||||
import io.bitsquare.locale.Country;
|
||||
import io.bitsquare.locale.CountryUtil;
|
||||
import io.bitsquare.locale.CurrencyUtil;
|
||||
import io.bitsquare.locale.Region;
|
||||
import io.bitsquare.user.AccountSettings;
|
||||
import io.bitsquare.user.User;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
class FiatAccountDataModel implements Activatable, DataModel {
|
||||
|
||||
private final User user;
|
||||
private final AccountSettings accountSettings;
|
||||
|
||||
final StringProperty title = new SimpleStringProperty();
|
||||
final StringProperty holderName = new SimpleStringProperty();
|
||||
final StringProperty primaryID = new SimpleStringProperty();
|
||||
final StringProperty secondaryID = new SimpleStringProperty();
|
||||
final StringProperty primaryIDPrompt = new SimpleStringProperty();
|
||||
final StringProperty secondaryIDPrompt = new SimpleStringProperty();
|
||||
final StringProperty currencyCode = new SimpleStringProperty();
|
||||
final BooleanProperty countryNotInAcceptedCountriesList = new SimpleBooleanProperty();
|
||||
final ObjectProperty<FiatAccount.Type> type = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Country> country = new SimpleObjectProperty<>();
|
||||
|
||||
final ObservableList<FiatAccount.Type> allTypes = FXCollections.observableArrayList(FiatAccount.Type
|
||||
.getAllBankAccountTypes());
|
||||
final ObservableList<FiatAccount> allFiatAccounts = FXCollections.observableArrayList();
|
||||
final ObservableList<String> allCurrencyCodes = FXCollections.observableArrayList(CurrencyUtil
|
||||
.getAllCurrencyCodes());
|
||||
final ObservableList<Region> allRegions = FXCollections.observableArrayList(CountryUtil.getAllRegions());
|
||||
|
||||
|
||||
@Inject
|
||||
public FiatAccountDataModel(User user, AccountSettings accountSettings) {
|
||||
this.user = user;
|
||||
this.accountSettings = accountSettings;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void activate() {
|
||||
allFiatAccounts.setAll(user.fiatAccountsObservableList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
|
||||
void saveBankAccount() {
|
||||
FiatAccount fiatAccount = new FiatAccount(type.get(),
|
||||
currencyCode.get(),
|
||||
country.get(),
|
||||
title.get(),
|
||||
holderName.get(),
|
||||
primaryID.get(),
|
||||
secondaryID.get());
|
||||
user.addFiatAccount(fiatAccount);
|
||||
allFiatAccounts.setAll(user.fiatAccountsObservableList());
|
||||
countryNotInAcceptedCountriesList.set(!accountSettings.getAcceptedCountries().contains(country.get()));
|
||||
reset();
|
||||
}
|
||||
|
||||
void removeBankAccount() {
|
||||
user.removeFiatAccount(user.currentFiatAccountProperty().get());
|
||||
allFiatAccounts.setAll(user.fiatAccountsObservableList());
|
||||
reset();
|
||||
}
|
||||
|
||||
// We ask the user if he likes to add his own bank account country to the accepted country list if he has not
|
||||
// already added it before
|
||||
void addCountryToAcceptedCountriesList() {
|
||||
accountSettings.addAcceptedCountry(country.get());
|
||||
countryNotInAcceptedCountriesList.set(false);
|
||||
}
|
||||
|
||||
void selectBankAccount(FiatAccount fiatAccount) {
|
||||
user.setCurrentFiatAccountProperty(fiatAccount);
|
||||
|
||||
if (fiatAccount != null) {
|
||||
title.set(fiatAccount.nameOfBank);
|
||||
holderName.set(fiatAccount.accountHolderName);
|
||||
primaryID.set(fiatAccount.accountPrimaryID);
|
||||
secondaryID.set(fiatAccount.accountSecondaryID);
|
||||
primaryIDPrompt.set(fiatAccount.type.primaryId);
|
||||
secondaryIDPrompt.set(fiatAccount.type.secondaryId);
|
||||
|
||||
type.set(fiatAccount.type);
|
||||
country.set(fiatAccount.country);
|
||||
currencyCode.set(fiatAccount.currencyCode);
|
||||
}
|
||||
else {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ObservableList<Country> getAllCountriesFor(Region selectedRegion) {
|
||||
return FXCollections.observableArrayList(CountryUtil.getAllCountriesFor(selectedRegion));
|
||||
}
|
||||
|
||||
|
||||
void setType(FiatAccount.Type type) {
|
||||
this.type.set(type);
|
||||
|
||||
if (type != null) {
|
||||
primaryIDPrompt.set(type.primaryId);
|
||||
secondaryIDPrompt.set(type.secondaryId);
|
||||
}
|
||||
else {
|
||||
primaryIDPrompt.set(null);
|
||||
secondaryIDPrompt.set(null);
|
||||
}
|
||||
}
|
||||
|
||||
void setCountry(Country country) {
|
||||
this.country.set(country);
|
||||
}
|
||||
|
||||
void setCurrencyCode(String currencyCode) {
|
||||
this.currencyCode.set(currencyCode);
|
||||
}
|
||||
|
||||
|
||||
private void reset() {
|
||||
title.set(null);
|
||||
holderName.set(null);
|
||||
primaryID.set(null);
|
||||
secondaryID.set(null);
|
||||
primaryIDPrompt.set(null);
|
||||
secondaryIDPrompt.set(null);
|
||||
|
||||
type.set(null);
|
||||
country.set(null);
|
||||
currencyCode.set(null);
|
||||
}
|
||||
}
|
|
@ -1,137 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
~ 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/>.
|
||||
-->
|
||||
|
||||
|
||||
<?import io.bitsquare.gui.components.InfoDisplay?>
|
||||
<?import io.bitsquare.gui.components.InputTextField?>
|
||||
<?import io.bitsquare.gui.components.TitledGroupBg?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<GridPane fx:id="root" fx:controller="io.bitsquare.gui.main.account.content.fiat.FiatAccountView" hgap="5.0"
|
||||
vgap="5.0"
|
||||
AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
|
||||
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
|
||||
<!--
|
||||
Setup
|
||||
-->
|
||||
<TitledGroupBg text="Setup your payments account" GridPane.rowSpan="8"/>
|
||||
|
||||
<Label text="Payments method:" GridPane.rowIndex="0">
|
||||
<GridPane.margin>
|
||||
<Insets top="10"/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<ComboBox fx:id="typesComboBox" promptText="Select payments method" onAction="#onSelectType"
|
||||
GridPane.rowIndex="0" GridPane.columnIndex="1">
|
||||
<GridPane.margin>
|
||||
<Insets top="10"/>
|
||||
</GridPane.margin>
|
||||
</ComboBox>
|
||||
|
||||
<Label text="Name of Bank:" GridPane.rowIndex="1"/>
|
||||
<InputTextField fx:id="nameOfBankTextField" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
|
||||
|
||||
<Label text="Location of Bank:" GridPane.rowIndex="2"/>
|
||||
<HBox GridPane.columnIndex="1" GridPane.rowIndex="2" spacing="10">
|
||||
<ComboBox fx:id="regionComboBox" promptText="Select region"
|
||||
onAction="#onSelectRegion" prefWidth="150.0"/>
|
||||
<ComboBox fx:id="countryComboBox" promptText="Select country" onAction="#onSelectCountry"
|
||||
visible="false" prefWidth="150.0"/>
|
||||
</HBox>
|
||||
|
||||
<Label text="Your name:" GridPane.rowIndex="3"/>
|
||||
<InputTextField fx:id="holderNameTextField" GridPane.columnIndex="1" GridPane.rowIndex="3"/>
|
||||
|
||||
<Label text="IBAN:" GridPane.rowIndex="4"/>
|
||||
<InputTextField fx:id="primaryIDTextField" GridPane.columnIndex="1" GridPane.rowIndex="4"/>
|
||||
|
||||
<Label text="BIC:" GridPane.rowIndex="5"/>
|
||||
<InputTextField fx:id="secondaryIDTextField" GridPane.columnIndex="1" GridPane.rowIndex="5"/>
|
||||
|
||||
<Label text="Currency:" GridPane.rowIndex="6"/>
|
||||
<ComboBox fx:id="currencyComboBox" promptText="Select currency"
|
||||
onAction="#onSelectCurrency" GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="6"/>
|
||||
|
||||
<InfoDisplay gridPane="$root" onAction="#onOpenSetupHelp" rowIndex="7"
|
||||
text="The payments account data will be saved in a encrypted form to the Bitcoin block chain and will be used in the trade process for account verification."/>
|
||||
|
||||
<HBox fx:id="buttonsHBox" GridPane.columnIndex="1" GridPane.rowIndex="8" spacing="10">
|
||||
<Button fx:id="saveButton" text="Save" onAction="#onSave" defaultButton="true" disable="true"/>
|
||||
<Button fx:id="completedButton" text="Continue to the next step" onAction="#onCompleted" disable="true"/>
|
||||
<GridPane.margin>
|
||||
<Insets top="15.0" bottom="5.0"/>
|
||||
</GridPane.margin>
|
||||
</HBox>
|
||||
|
||||
<!--
|
||||
Manage
|
||||
-->
|
||||
<TitledGroupBg text="Manage payments accounts" GridPane.rowIndex="9" GridPane.rowSpan="4">
|
||||
<padding>
|
||||
<Insets top="40.0"/>
|
||||
</padding>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="-10" left="-10" right="-10" top="20"/>
|
||||
</GridPane.margin>
|
||||
</TitledGroupBg>
|
||||
|
||||
<Label text="Select payments account:" GridPane.rowIndex="9">
|
||||
<GridPane.margin>
|
||||
<Insets top="40"/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<ComboBox fx:id="selectionComboBox" onAction="#onSelectAccount" GridPane.rowIndex="9"
|
||||
GridPane.columnIndex="1">
|
||||
<GridPane.margin>
|
||||
<Insets top="40"/>
|
||||
</GridPane.margin>
|
||||
</ComboBox>
|
||||
|
||||
<Label text="Remove selected account:" GridPane.rowIndex="10"/>
|
||||
<Button fx:id="removeBankAccountButton" text="Remove selected payments account" onAction="#onRemoveAccount"
|
||||
GridPane.columnIndex="1" GridPane.rowIndex="10" disable="true"/>
|
||||
|
||||
<InfoDisplay gridPane="$root" onAction="#onOpenManageAccountsHelp" rowIndex="11"
|
||||
text="When you change your payments accounts later you need to do the renew the registration and pay the small registration fee."/>
|
||||
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" halignment="RIGHT" minWidth="200.0"/>
|
||||
<ColumnConstraints hgrow="ALWAYS" minWidth="300.0"/>
|
||||
</columnConstraints>
|
||||
|
||||
<rowConstraints>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
</rowConstraints>
|
||||
|
||||
</GridPane>
|
|
@ -1,257 +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.main.account.content.fiat;
|
||||
|
||||
import io.bitsquare.fiat.FiatAccount;
|
||||
import io.bitsquare.gui.OverlayManager;
|
||||
import io.bitsquare.gui.common.view.ActivatableViewAndModel;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.common.view.Wizard;
|
||||
import io.bitsquare.gui.components.InputTextField;
|
||||
import io.bitsquare.gui.components.Popups;
|
||||
import io.bitsquare.gui.main.help.Help;
|
||||
import io.bitsquare.gui.main.help.HelpId;
|
||||
import io.bitsquare.gui.util.validation.InputValidator;
|
||||
import io.bitsquare.locale.BSResources;
|
||||
import io.bitsquare.locale.Country;
|
||||
import io.bitsquare.locale.Region;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.*;
|
||||
|
||||
import org.controlsfx.control.action.AbstractAction;
|
||||
import org.controlsfx.control.action.Action;
|
||||
import org.controlsfx.dialog.Dialog;
|
||||
|
||||
import static javafx.beans.binding.Bindings.createBooleanBinding;
|
||||
|
||||
@FxmlView
|
||||
public class FiatAccountView extends ActivatableViewAndModel<GridPane, FiatAccountViewModel> implements Wizard.Step {
|
||||
|
||||
@FXML HBox buttonsHBox;
|
||||
@FXML ComboBox<Region> regionComboBox;
|
||||
@FXML ComboBox<Country> countryComboBox;
|
||||
@FXML InputTextField nameOfBankTextField, holderNameTextField, primaryIDTextField, secondaryIDTextField;
|
||||
@FXML Button saveButton, completedButton, removeBankAccountButton;
|
||||
@FXML ComboBox<FiatAccount> selectionComboBox;
|
||||
@FXML ComboBox<FiatAccount.Type> typesComboBox;
|
||||
@FXML ComboBox<String> currencyComboBox;
|
||||
|
||||
private Wizard wizard;
|
||||
|
||||
|
||||
@Inject
|
||||
public FiatAccountView(FiatAccountViewModel model) {
|
||||
super(model);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
typesComboBox.setItems(model.getAllTypes());
|
||||
typesComboBox.setConverter(model.getTypesConverter());
|
||||
selectionComboBox.setConverter(model.getSelectionConverter());
|
||||
currencyComboBox.setItems(model.getAllCurrencyCodes());
|
||||
currencyComboBox.setConverter(model.getCurrencyConverter());
|
||||
regionComboBox.setItems(model.getAllRegions());
|
||||
regionComboBox.setConverter(model.getRegionConverter());
|
||||
countryComboBox.setConverter(model.getCountryConverter());
|
||||
|
||||
nameOfBankTextField.setValidator(model.getBankAccountNumberValidator());
|
||||
holderNameTextField.setValidator(model.getBankAccountNumberValidator());
|
||||
primaryIDTextField.setValidator(model.getBankAccountNumberValidator());
|
||||
secondaryIDTextField.setValidator(model.getBankAccountNumberValidator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doActivate() {
|
||||
setupListeners();
|
||||
setupBindings();
|
||||
|
||||
selectionComboBox.setItems(model.getAllBankAccounts());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWizard(Wizard wizard) {
|
||||
this.wizard = wizard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideWizardNavigation() {
|
||||
buttonsHBox.getChildren().remove(completedButton);
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onSelectAccount() {
|
||||
if (selectionComboBox.getSelectionModel().getSelectedItem() != null)
|
||||
model.selectBankAccount(selectionComboBox.getSelectionModel().getSelectedItem());
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onSelectType() {
|
||||
model.setType(typesComboBox.getSelectionModel().getSelectedItem());
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onSelectCurrency() {
|
||||
model.setCurrencyCode(currencyComboBox.getSelectionModel().getSelectedItem());
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onSelectRegion() {
|
||||
countryComboBox.setVisible(true);
|
||||
Region region = regionComboBox.getSelectionModel().getSelectedItem();
|
||||
if (region != null)
|
||||
countryComboBox.setItems(model.getAllCountriesFor(region));
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onSelectCountry() {
|
||||
Country country = countryComboBox.getSelectionModel().getSelectedItem();
|
||||
if (country != null)
|
||||
model.setCountry(country);
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onSave() {
|
||||
InputValidator.ValidationResult result = model.requestSaveBankAccount();
|
||||
if (result.isValid) {
|
||||
selectionComboBox.getSelectionModel().select(null);
|
||||
Popups.openInfoPopup("Your payments account has been saved.",
|
||||
"You can add more accounts or continue to the next step.");
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onCompleted() {
|
||||
if (wizard != null)
|
||||
wizard.nextStep(this);
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onRemoveAccount() {
|
||||
model.removeBankAccount();
|
||||
selectionComboBox.getSelectionModel().select(null);
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onOpenSetupHelp() {
|
||||
Help.openWindow(HelpId.SETUP_FIAT_ACCOUNT);
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onOpenManageAccountsHelp() {
|
||||
Help.openWindow(HelpId.MANAGE_FIAT_ACCOUNT);
|
||||
}
|
||||
|
||||
|
||||
private void setupListeners() {
|
||||
model.type.addListener((ov, oldValue, newValue) -> {
|
||||
if (newValue != null)
|
||||
typesComboBox.getSelectionModel().select(typesComboBox.getItems().indexOf(newValue));
|
||||
else
|
||||
typesComboBox.getSelectionModel().clearSelection();
|
||||
});
|
||||
|
||||
model.currencyCode.addListener((ov, oldValue, newValue) -> {
|
||||
if (newValue != null)
|
||||
currencyComboBox.getSelectionModel().select(currencyComboBox.getItems().indexOf(newValue));
|
||||
else
|
||||
currencyComboBox.getSelectionModel().clearSelection();
|
||||
});
|
||||
|
||||
model.country.addListener((ov, oldValue, newValue) -> {
|
||||
if (newValue != null) {
|
||||
int regionIndex = regionComboBox.getItems().indexOf(newValue.region);
|
||||
if (regionIndex >= 0 && regionIndex < regionComboBox.getItems().size())
|
||||
regionComboBox.getSelectionModel().select(regionComboBox.getItems().indexOf(newValue.region));
|
||||
|
||||
int countryIndex = countryComboBox.getItems().indexOf(newValue);
|
||||
if (countryIndex >= 0 && countryIndex < countryComboBox.getItems().size())
|
||||
countryComboBox.getSelectionModel().select(countryIndex);
|
||||
}
|
||||
else {
|
||||
regionComboBox.getSelectionModel().clearSelection();
|
||||
countryComboBox.getSelectionModel().clearSelection();
|
||||
}
|
||||
});
|
||||
|
||||
model.getCountryNotInAcceptedCountriesList().addListener((ov, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
OverlayManager.blurContent();
|
||||
List<Action> actions = new ArrayList<>();
|
||||
actions.add(new AbstractAction(BSResources.get("shared.no")) {
|
||||
@Override
|
||||
public void handle(ActionEvent actionEvent) {
|
||||
getProperties().put("type", "NO");
|
||||
Dialog.Actions.NO.handle(actionEvent);
|
||||
}
|
||||
});
|
||||
|
||||
actions.add(new AbstractAction(BSResources.get("shared.yes")) {
|
||||
@Override
|
||||
public void handle(ActionEvent actionEvent) {
|
||||
getProperties().put("type", "YES");
|
||||
Dialog.Actions.YES.handle(actionEvent);
|
||||
}
|
||||
});
|
||||
|
||||
Action response = Popups.openConfirmPopup("Warning", null,
|
||||
"The country of your payments account is not included in your list of accepted countries" +
|
||||
".\n\nDo you want to add it automatically?",
|
||||
actions);
|
||||
|
||||
if (Popups.isYes(response))
|
||||
model.addCountryToAcceptedCountriesList();
|
||||
|
||||
OverlayManager.removeBlurContent();
|
||||
}
|
||||
});
|
||||
|
||||
model.getAllBankAccounts().addListener((ListChangeListener<FiatAccount>) change ->
|
||||
completedButton.setDisable(model.getAllBankAccounts().isEmpty()));
|
||||
completedButton.setDisable(model.getAllBankAccounts().isEmpty());
|
||||
}
|
||||
|
||||
private void setupBindings() {
|
||||
// input
|
||||
nameOfBankTextField.textProperty().bindBidirectional(model.title);
|
||||
holderNameTextField.textProperty().bindBidirectional(model.holderName);
|
||||
primaryIDTextField.textProperty().bindBidirectional(model.primaryID);
|
||||
secondaryIDTextField.textProperty().bindBidirectional(model.secondaryID);
|
||||
|
||||
primaryIDTextField.promptTextProperty().bind(model.primaryIDPrompt);
|
||||
secondaryIDTextField.promptTextProperty().bind(model.secondaryIDPrompt);
|
||||
selectionComboBox.promptTextProperty().bind(model.selectionPrompt);
|
||||
selectionComboBox.disableProperty().bind(model.selectionDisable);
|
||||
|
||||
saveButton.disableProperty().bind(model.saveButtonDisable);
|
||||
|
||||
removeBankAccountButton.disableProperty().bind(createBooleanBinding(() ->
|
||||
(selectionComboBox.getSelectionModel().selectedIndexProperty().get() == -1),
|
||||
selectionComboBox.getSelectionModel().selectedIndexProperty()));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,274 +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.main.account.content.fiat;
|
||||
|
||||
import io.bitsquare.fiat.FiatAccount;
|
||||
import io.bitsquare.gui.common.model.ActivatableWithDataModel;
|
||||
import io.bitsquare.gui.common.model.ViewModel;
|
||||
import io.bitsquare.gui.util.validation.BankAccountNumberValidator;
|
||||
import io.bitsquare.gui.util.validation.InputValidator;
|
||||
import io.bitsquare.locale.BSResources;
|
||||
import io.bitsquare.locale.Country;
|
||||
import io.bitsquare.locale.CurrencyUtil;
|
||||
import io.bitsquare.locale.Region;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
class FiatAccountViewModel extends ActivatableWithDataModel<FiatAccountDataModel> implements ViewModel {
|
||||
|
||||
private final BankAccountNumberValidator bankAccountNumberValidator;
|
||||
|
||||
final StringProperty title = new SimpleStringProperty();
|
||||
final StringProperty holderName = new SimpleStringProperty();
|
||||
final StringProperty primaryID = new SimpleStringProperty();
|
||||
final StringProperty secondaryID = new SimpleStringProperty();
|
||||
final StringProperty primaryIDPrompt = new SimpleStringProperty();
|
||||
final StringProperty secondaryIDPrompt = new SimpleStringProperty();
|
||||
final StringProperty selectionPrompt = new SimpleStringProperty();
|
||||
final StringProperty currencyCode = new SimpleStringProperty();
|
||||
final BooleanProperty selectionDisable = new SimpleBooleanProperty();
|
||||
final BooleanProperty saveButtonDisable = new SimpleBooleanProperty(true);
|
||||
final ObjectProperty<FiatAccount.Type> type = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Country> country = new SimpleObjectProperty<>();
|
||||
|
||||
|
||||
@Inject
|
||||
public FiatAccountViewModel(FiatAccountDataModel dataModel, BankAccountNumberValidator bankAccountNumberValidator) {
|
||||
super(dataModel);
|
||||
this.bankAccountNumberValidator = bankAccountNumberValidator;
|
||||
|
||||
// input
|
||||
title.bindBidirectional(dataModel.title);
|
||||
holderName.bindBidirectional(dataModel.holderName);
|
||||
primaryID.bindBidirectional(dataModel.primaryID);
|
||||
secondaryID.bindBidirectional(dataModel.secondaryID);
|
||||
type.bindBidirectional(dataModel.type);
|
||||
country.bindBidirectional(dataModel.country);
|
||||
currencyCode.bindBidirectional(dataModel.currencyCode);
|
||||
|
||||
primaryIDPrompt.bind(dataModel.primaryIDPrompt);
|
||||
secondaryIDPrompt.bind(dataModel.secondaryIDPrompt);
|
||||
|
||||
selectionPrompt.set("No bank account available");
|
||||
selectionDisable.set(true);
|
||||
|
||||
dataModel.title.addListener((ov, oldValue, newValue) -> validateInput());
|
||||
holderName.addListener((ov, oldValue, newValue) -> validateInput());
|
||||
primaryID.addListener((ov, oldValue, newValue) -> validateInput());
|
||||
secondaryID.addListener((ov, oldValue, newValue) -> validateInput());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doActivate() {
|
||||
dataModel.allFiatAccounts.addListener((ListChangeListener<FiatAccount>) change -> applyAllBankAccounts());
|
||||
applyAllBankAccounts();
|
||||
}
|
||||
|
||||
|
||||
InputValidator.ValidationResult requestSaveBankAccount() {
|
||||
InputValidator.ValidationResult result = validateInput();
|
||||
if (result.isValid) {
|
||||
dataModel.saveBankAccount();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void removeBankAccount() {
|
||||
dataModel.removeBankAccount();
|
||||
}
|
||||
|
||||
void addCountryToAcceptedCountriesList() {
|
||||
dataModel.addCountryToAcceptedCountriesList();
|
||||
}
|
||||
|
||||
void selectBankAccount(FiatAccount fiatAccount) {
|
||||
dataModel.selectBankAccount(fiatAccount);
|
||||
}
|
||||
|
||||
|
||||
StringConverter<FiatAccount.Type> getTypesConverter() {
|
||||
return new StringConverter<FiatAccount.Type>() {
|
||||
@Override
|
||||
public String toString(FiatAccount.Type TypeInfo) {
|
||||
return BSResources.get(TypeInfo.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FiatAccount.Type fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
StringConverter<FiatAccount> getSelectionConverter() {
|
||||
return new StringConverter<FiatAccount>() {
|
||||
@Override
|
||||
public String toString(FiatAccount fiatAccount) {
|
||||
return fiatAccount.nameOfBank;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FiatAccount fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
StringConverter<String> getCurrencyConverter() {
|
||||
return new StringConverter<String>() {
|
||||
|
||||
@Override
|
||||
public String toString(String currencyCode) {
|
||||
return currencyCode + " (" + CurrencyUtil.getDisplayName(currencyCode) + ")";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
StringConverter<Region> getRegionConverter() {
|
||||
return new StringConverter<io.bitsquare.locale.Region>() {
|
||||
@Override
|
||||
public String toString(io.bitsquare.locale.Region region) {
|
||||
return region.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public io.bitsquare.locale.Region fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
StringConverter<Country> getCountryConverter() {
|
||||
return new StringConverter<Country>() {
|
||||
@Override
|
||||
public String toString(Country country) {
|
||||
return country.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Country fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
ObservableList<FiatAccount.Type> getAllTypes() {
|
||||
return dataModel.allTypes;
|
||||
}
|
||||
|
||||
ObservableList<FiatAccount> getAllBankAccounts() {
|
||||
return dataModel.allFiatAccounts;
|
||||
}
|
||||
|
||||
ObservableList<String> getAllCurrencyCodes() {
|
||||
return dataModel.allCurrencyCodes;
|
||||
}
|
||||
|
||||
ObservableList<Region> getAllRegions() {
|
||||
return dataModel.allRegions;
|
||||
}
|
||||
|
||||
BooleanProperty getCountryNotInAcceptedCountriesList() {
|
||||
return dataModel.countryNotInAcceptedCountriesList;
|
||||
}
|
||||
|
||||
ObservableList<Country> getAllCountriesFor(Region selectedRegion) {
|
||||
return dataModel.getAllCountriesFor(selectedRegion);
|
||||
}
|
||||
|
||||
BankAccountNumberValidator getBankAccountNumberValidator() {
|
||||
return bankAccountNumberValidator;
|
||||
}
|
||||
|
||||
|
||||
void setType(FiatAccount.Type type) {
|
||||
dataModel.setType(type);
|
||||
validateInput();
|
||||
}
|
||||
|
||||
void setCountry(Country country) {
|
||||
dataModel.setCountry(country);
|
||||
validateInput();
|
||||
}
|
||||
|
||||
void setCurrencyCode(String currencyCode) {
|
||||
dataModel.setCurrencyCode(currencyCode);
|
||||
validateInput();
|
||||
}
|
||||
|
||||
|
||||
private void applyAllBankAccounts() {
|
||||
if (dataModel.allFiatAccounts.isEmpty()) {
|
||||
selectionPrompt.set("No bank account available");
|
||||
selectionDisable.set(true);
|
||||
}
|
||||
else {
|
||||
selectionPrompt.set("Select bank account");
|
||||
selectionDisable.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
private InputValidator.ValidationResult validateInput() {
|
||||
InputValidator.ValidationResult result = bankAccountNumberValidator.validate(dataModel.title.get());
|
||||
if (result.isValid) {
|
||||
result = bankAccountNumberValidator.validate(dataModel.holderName.get());
|
||||
if (result.isValid) {
|
||||
result = bankAccountNumberValidator.validate(dataModel.primaryID.get());
|
||||
if (result.isValid) {
|
||||
result = bankAccountNumberValidator.validate(dataModel.secondaryID.get());
|
||||
if (result.isValid) {
|
||||
if (dataModel.currencyCode.get() == null)
|
||||
result = new InputValidator.ValidationResult(false,
|
||||
"You have not selected a currency");
|
||||
if (result.isValid) {
|
||||
if (dataModel.country.get() == null)
|
||||
result = new InputValidator.ValidationResult(false,
|
||||
"You have not selected a country of the payments account");
|
||||
if (result.isValid) {
|
||||
if (dataModel.type.get() == null)
|
||||
result = new InputValidator.ValidationResult(false,
|
||||
"You have not selected a payments method");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
saveButtonDisable.set(!result.isValid);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -17,58 +17,16 @@
|
|||
~ along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<?import io.bitsquare.gui.components.InfoDisplay?>
|
||||
<?import io.bitsquare.gui.components.TitledGroupBg?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<GridPane fx:id="root" fx:controller="io.bitsquare.gui.main.account.content.password.PasswordView" hgap="5.0"
|
||||
vgap="5.0"
|
||||
<GridPane fx:id="root" fx:controller="io.bitsquare.gui.main.account.content.password.PasswordView"
|
||||
hgap="5.0" vgap="5.0"
|
||||
AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
|
||||
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"
|
||||
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="-10.0"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
|
||||
<TitledGroupBg text="Setup password" GridPane.rowSpan="4"/>
|
||||
|
||||
<Label text="Enter password:">
|
||||
<GridPane.margin>
|
||||
<Insets top="10.0"/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
|
||||
<PasswordField fx:id="passwordField" promptText="Min. 8 characters" GridPane.columnIndex="1">
|
||||
<GridPane.margin>
|
||||
<Insets top="10.0"/>
|
||||
</GridPane.margin>
|
||||
</PasswordField>
|
||||
|
||||
<Label text="Repeat password:" GridPane.rowIndex="1"/>
|
||||
<PasswordField fx:id="repeatedPasswordField" promptText="Repeat password" GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="1"/>
|
||||
|
||||
<HBox fx:id="buttonsHBox" spacing="10" GridPane.columnIndex="1" GridPane.rowIndex="2">
|
||||
<Button fx:id="saveButton" text="Save password" onAction="#onSaved" disable="true" defaultButton="true"/>
|
||||
<Button fx:id="skipButton" text="I will not use a password" onAction="#onSkipped"/>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5"/>
|
||||
</GridPane.margin>
|
||||
</HBox>
|
||||
|
||||
<InfoDisplay gridPane="$root" onAction="#onOpenHelp" rowIndex="3"
|
||||
text="Protect your wallet with a strong password. You need to enter the password any time you withdraw Bitcoin from your trading wallets. You can change the password later in the settings. Open the help menu for more information."/>
|
||||
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" halignment="RIGHT" minWidth="140.0"/>
|
||||
<ColumnConstraints hgrow="ALWAYS" minWidth="300.0"/>
|
||||
</columnConstraints>
|
||||
|
||||
<rowConstraints>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
</rowConstraints>
|
||||
|
||||
</GridPane>
|
||||
|
||||
|
||||
</GridPane>
|
|
@ -17,67 +17,184 @@
|
|||
|
||||
package io.bitsquare.gui.main.account.content.password;
|
||||
|
||||
import io.bitsquare.btc.TradeWalletService;
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.common.util.Tuple2;
|
||||
import io.bitsquare.common.util.Tuple3;
|
||||
import io.bitsquare.crypto.ScryptUtil;
|
||||
import io.bitsquare.gui.common.view.ActivatableView;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.common.view.InitializableView;
|
||||
import io.bitsquare.gui.common.view.Wizard;
|
||||
import io.bitsquare.gui.main.help.Help;
|
||||
import io.bitsquare.gui.main.help.HelpId;
|
||||
import io.bitsquare.gui.components.PasswordTextField;
|
||||
import io.bitsquare.gui.components.TitledGroupBg;
|
||||
import io.bitsquare.gui.popups.Popup;
|
||||
import io.bitsquare.gui.util.Layout;
|
||||
import io.bitsquare.gui.util.validation.InputValidator;
|
||||
import io.bitsquare.gui.util.validation.PasswordValidator;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ProgressIndicator;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import org.bitcoinj.core.Wallet;
|
||||
import org.bitcoinj.crypto.KeyCrypterScrypt;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.*;
|
||||
import static io.bitsquare.gui.util.FormBuilder.*;
|
||||
|
||||
@FxmlView
|
||||
public class PasswordView extends InitializableView<GridPane, PasswordViewModel> implements Wizard.Step {
|
||||
public class PasswordView extends ActivatableView<GridPane, Void> {
|
||||
|
||||
@FXML HBox buttonsHBox;
|
||||
@FXML Button saveButton, skipButton;
|
||||
@FXML PasswordField oldPasswordField, passwordField, repeatedPasswordField;
|
||||
private final PasswordValidator passwordValidator;
|
||||
private final WalletService walletService;
|
||||
private final TradeWalletService tradeWalletService;
|
||||
|
||||
private Wizard wizard;
|
||||
private PasswordTextField passwordField;
|
||||
private PasswordTextField repeatedPasswordField;
|
||||
private Button pwButton;
|
||||
private TitledGroupBg headline;
|
||||
private int gridRow = 0;
|
||||
private Label repeatedPasswordLabel;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor, lifecycle
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
private PasswordView(PasswordViewModel model) {
|
||||
super(model);
|
||||
private PasswordView(PasswordValidator passwordValidator, WalletService walletService, TradeWalletService tradeWalletService) {
|
||||
this.passwordValidator = passwordValidator;
|
||||
this.walletService = walletService;
|
||||
this.tradeWalletService = tradeWalletService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
passwordField.textProperty().bindBidirectional(model.passwordField);
|
||||
repeatedPasswordField.textProperty().bindBidirectional(model.repeatedPasswordField);
|
||||
headline = addTitledGroupBg(root, gridRow, 3, "");
|
||||
passwordField = addLabelPasswordTextField(root, gridRow, "Enter password:", Layout.FIRST_ROW_DISTANCE).second;
|
||||
passwordField.setValidator(passwordValidator);
|
||||
passwordField.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
validatePasswords();
|
||||
});
|
||||
|
||||
saveButton.disableProperty().bind(model.saveButtonDisabled);
|
||||
Tuple2<Label, PasswordTextField> tuple2 = addLabelPasswordTextField(root, ++gridRow, "Repeat password:");
|
||||
repeatedPasswordLabel = tuple2.first;
|
||||
repeatedPasswordField = tuple2.second;
|
||||
repeatedPasswordField.setValidator(passwordValidator);
|
||||
repeatedPasswordField.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
validatePasswords();
|
||||
});
|
||||
|
||||
Tuple3<Button, ProgressIndicator, Label> tuple = addButtonWithStatus(root, ++gridRow, "", 0);
|
||||
pwButton = tuple.first;
|
||||
ProgressIndicator progressIndicator = tuple.second;
|
||||
Label deriveStatusLabel = tuple.third;
|
||||
pwButton.setDisable(true);
|
||||
|
||||
setText();
|
||||
|
||||
pwButton.setOnAction(e -> {
|
||||
pwButton.setDisable(true);
|
||||
deriveStatusLabel.setText("Derive key from password");
|
||||
progressIndicator.setProgress(-1);
|
||||
progressIndicator.setVisible(true);
|
||||
|
||||
KeyCrypterScrypt keyCrypterScrypt;
|
||||
Wallet wallet = walletService.getWallet();
|
||||
if (wallet.isEncrypted())
|
||||
keyCrypterScrypt = (KeyCrypterScrypt) wallet.getKeyCrypter();
|
||||
else
|
||||
keyCrypterScrypt = new KeyCrypterScrypt(ScryptUtil.SCRYPT_PARAMETERS);
|
||||
|
||||
ScryptUtil.deriveKeyWithScrypt(keyCrypterScrypt, passwordField.getText(), aesKey -> {
|
||||
deriveStatusLabel.setText("");
|
||||
progressIndicator.setVisible(false);
|
||||
|
||||
if (wallet.isEncrypted()) {
|
||||
if (wallet.checkAESKey(aesKey)) {
|
||||
wallet.decrypt(aesKey);
|
||||
tradeWalletService.setAesKey(null);
|
||||
new Popup()
|
||||
.information("Wallet successfully decrypted and password protection removed.")
|
||||
.show();
|
||||
passwordField.setText("");
|
||||
repeatedPasswordField.setText("");
|
||||
} else {
|
||||
new Popup()
|
||||
.headLine("Wrong password")
|
||||
.message("Please try entering your password again, carefully checking for typos or spelling errors.")
|
||||
.show();
|
||||
}
|
||||
} else {
|
||||
wallet.encrypt(keyCrypterScrypt, aesKey);
|
||||
// we save the key for the trade wallet as we don't require passwords here
|
||||
tradeWalletService.setAesKey(aesKey);
|
||||
new Popup()
|
||||
.information("Wallet successfully encrypted and password protection enabled.")
|
||||
.show();
|
||||
passwordField.setText("");
|
||||
repeatedPasswordField.setText("");
|
||||
}
|
||||
setText();
|
||||
});
|
||||
});
|
||||
|
||||
addTitledGroupBg(root, ++gridRow, 1, "Information", Layout.GROUP_DISTANCE);
|
||||
addMultilineLabel(root, gridRow,
|
||||
"With password protection you need to enter your password when" +
|
||||
" withdrawing Bitcoin out of your wallet or " +
|
||||
"if you want to view or restore a wallet from seed words.\n" +
|
||||
"For the transactions used in the trade process we don't support password protection as that would make automatic offer " +
|
||||
"execution impossible.",
|
||||
Layout.FIRST_ROW_AND_GROUP_DISTANCE);
|
||||
}
|
||||
|
||||
private void setText() {
|
||||
if (walletService.getWallet().isEncrypted()) {
|
||||
pwButton.setText("Remove password");
|
||||
headline.setText("Remove password protection for wallet");
|
||||
repeatedPasswordField.setVisible(false);
|
||||
repeatedPasswordField.setManaged(false);
|
||||
repeatedPasswordLabel.setVisible(false);
|
||||
repeatedPasswordLabel.setManaged(false);
|
||||
} else {
|
||||
pwButton.setText("Set password");
|
||||
headline.setText("Set password protection for wallet");
|
||||
repeatedPasswordField.setVisible(true);
|
||||
repeatedPasswordField.setManaged(true);
|
||||
repeatedPasswordLabel.setVisible(true);
|
||||
repeatedPasswordLabel.setManaged(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWizard(Wizard wizard) {
|
||||
this.wizard = wizard;
|
||||
protected void activate() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideWizardNavigation() {
|
||||
buttonsHBox.getChildren().remove(skipButton);
|
||||
protected void deactivate() {
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onSaved() {
|
||||
if (wizard != null && model.requestSavePassword())
|
||||
wizard.nextStep(this);
|
||||
else
|
||||
log.debug(model.getErrorMessage()); // TODO use validating passwordTF
|
||||
}
|
||||
private void validatePasswords() {
|
||||
passwordValidator.setExternalValidationResult(null);
|
||||
InputValidator.ValidationResult result = passwordValidator.validate(passwordField.getText());
|
||||
if (result.isValid) {
|
||||
if (walletService.getWallet().isEncrypted()) {
|
||||
pwButton.setDisable(false);
|
||||
return;
|
||||
} else {
|
||||
result = passwordValidator.validate(repeatedPasswordField.getText());
|
||||
|
||||
@FXML
|
||||
private void onSkipped() {
|
||||
if (wizard != null)
|
||||
wizard.nextStep(this);
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onOpenHelp() {
|
||||
Help.openWindow(HelpId.SETUP_PASSWORD);
|
||||
if (result.isValid) {
|
||||
if (passwordField.getText().equals(repeatedPasswordField.getText())) {
|
||||
pwButton.setDisable(false);
|
||||
return;
|
||||
} else {
|
||||
passwordValidator.setExternalValidationResult(new InputValidator.ValidationResult(false, "The 2 passwords do not match."));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pwButton.setDisable(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,87 +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.main.account.content.password;
|
||||
|
||||
import io.bitsquare.gui.common.model.ViewModel;
|
||||
import io.bitsquare.gui.util.validation.InputValidator;
|
||||
import io.bitsquare.gui.util.validation.PasswordValidator;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
|
||||
class PasswordViewModel implements ViewModel {
|
||||
|
||||
private final PasswordValidator passwordValidator;
|
||||
|
||||
private String errorMessage;
|
||||
|
||||
final StringProperty passwordField = new SimpleStringProperty();
|
||||
final StringProperty repeatedPasswordField = new SimpleStringProperty();
|
||||
final BooleanProperty saveButtonDisabled = new SimpleBooleanProperty(true);
|
||||
|
||||
|
||||
@Inject
|
||||
public PasswordViewModel(PasswordValidator passwordValidator) {
|
||||
this.passwordValidator = passwordValidator;
|
||||
|
||||
passwordField.addListener((ov) -> saveButtonDisabled.set(!validate()));
|
||||
repeatedPasswordField.addListener((ov) -> saveButtonDisabled.set(!validate()));
|
||||
}
|
||||
|
||||
|
||||
boolean requestSavePassword() {
|
||||
if (validate()) {
|
||||
savePassword(passwordField.get());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void savePassword(String password) {
|
||||
//TODO Implement password encryption for wallet
|
||||
}
|
||||
|
||||
String getErrorMessage() {
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
|
||||
private boolean validate() {
|
||||
InputValidator.ValidationResult result = passwordValidator.validate(passwordField.get());
|
||||
if (result.isValid) {
|
||||
result = passwordValidator.validate(repeatedPasswordField.get());
|
||||
if (result.isValid) {
|
||||
if (passwordField.get().equals(repeatedPasswordField.get()))
|
||||
return true;
|
||||
else
|
||||
errorMessage = "The 2 passwords does not match.";
|
||||
}
|
||||
else {
|
||||
errorMessage = result.errorMessage;
|
||||
}
|
||||
}
|
||||
else {
|
||||
errorMessage = result.errorMessage;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,130 +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.main.account.content.registration;
|
||||
|
||||
import io.bitsquare.btc.AddressEntry;
|
||||
import io.bitsquare.btc.FeePolicy;
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.btc.listeners.BalanceListener;
|
||||
import io.bitsquare.gui.common.model.DataModel;
|
||||
import io.bitsquare.user.User;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.InsufficientMoneyException;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class RegistrationDataModel implements DataModel {
|
||||
private static final Logger log = LoggerFactory.getLogger(RegistrationDataModel.class);
|
||||
|
||||
private final WalletService walletService;
|
||||
private final User user;
|
||||
|
||||
private String transactionId;
|
||||
private AddressEntry addressEntry;
|
||||
|
||||
final BooleanProperty isWalletFunded = new SimpleBooleanProperty();
|
||||
final BooleanProperty payFeeSuccess = new SimpleBooleanProperty();
|
||||
final StringProperty payFeeErrorMessage = new SimpleStringProperty();
|
||||
|
||||
|
||||
@Inject
|
||||
public RegistrationDataModel(WalletService walletService, User user) {
|
||||
|
||||
this.walletService = walletService;
|
||||
this.user = user;
|
||||
|
||||
if (walletService != null && walletService.getWallet() != null) {
|
||||
addressEntry = walletService.getRegistrationAddressEntry();
|
||||
walletService.addBalanceListener(new BalanceListener(getAddressEntry().getAddress()) {
|
||||
@Override
|
||||
public void onBalanceChanged(@NotNull Coin balance) {
|
||||
updateBalance(balance);
|
||||
}
|
||||
});
|
||||
updateBalance(walletService.getBalanceForAddress(getAddressEntry().getAddress()));
|
||||
}
|
||||
}
|
||||
|
||||
void payFee() {
|
||||
FutureCallback<Transaction> callback = new FutureCallback<Transaction>() {
|
||||
@Override
|
||||
public void onSuccess(@Nullable Transaction transaction) {
|
||||
log.debug("payRegistrationFee onSuccess");
|
||||
if (transaction != null) {
|
||||
transactionId = transaction.getHashAsString();
|
||||
log.info("payRegistrationFee onSuccess tx id:" + transaction.getHashAsString());
|
||||
|
||||
if (getAddressEntry() != null)
|
||||
user.setAccountID(getAddressEntry().toString());
|
||||
|
||||
payFeeSuccess.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NotNull Throwable t) {
|
||||
log.debug("payRegistrationFee onFailure");
|
||||
payFeeErrorMessage.set("Fee payment failed with error: " + t.getMessage());
|
||||
}
|
||||
};
|
||||
try {
|
||||
walletService.payRegistrationFee(user.getStringifiedBankAccounts(), callback);
|
||||
} catch (InsufficientMoneyException e) {
|
||||
payFeeErrorMessage.set("Fee payment failed with error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
WalletService getWalletService() {
|
||||
return walletService;
|
||||
}
|
||||
|
||||
Coin getFeeAsCoin() {
|
||||
return FeePolicy.REGISTRATION_FEE;
|
||||
}
|
||||
|
||||
String getTransactionId() {
|
||||
return transactionId;
|
||||
}
|
||||
|
||||
AddressEntry getAddressEntry() {
|
||||
return addressEntry;
|
||||
}
|
||||
|
||||
|
||||
private void updateBalance(@NotNull Coin balance) {
|
||||
isWalletFunded.set(balance.compareTo(getFeeAsCoin()) >= 0);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
~ 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/>.
|
||||
-->
|
||||
|
||||
<?import io.bitsquare.gui.components.AddressTextField?>
|
||||
<?import io.bitsquare.gui.components.BalanceTextField?>
|
||||
<?import io.bitsquare.gui.components.InfoDisplay?>
|
||||
<?import io.bitsquare.gui.components.TitledGroupBg?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<GridPane fx:id="root" fx:controller="io.bitsquare.gui.main.account.content.registration.RegistrationView" hgap="5.0"
|
||||
vgap="5.0"
|
||||
AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
|
||||
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
|
||||
<TitledGroupBg text="Complete the registration" GridPane.rowSpan="4"/>
|
||||
|
||||
<Label text="Registration costs:" GridPane.rowIndex="0">
|
||||
<GridPane.margin>
|
||||
<Insets top="10.0"/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<TextField fx:id="feeTextField"
|
||||
GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="0" editable="false" focusTraversable="false">
|
||||
<GridPane.margin>
|
||||
<Insets top="10.0"/>
|
||||
</GridPane.margin>
|
||||
</TextField>
|
||||
|
||||
<Label text="Registration wallet address:" GridPane.rowIndex="1"/>
|
||||
<AddressTextField fx:id="addressTextField" GridPane.columnIndex="1" GridPane.rowIndex="1"
|
||||
focusTraversable="true"/>
|
||||
|
||||
<Label text="Registration wallet balance:" GridPane.rowIndex="2">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0"/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<BalanceTextField fx:id="balanceTextField" GridPane.columnIndex="1" GridPane.rowIndex="2"
|
||||
focusTraversable="false">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0"/>
|
||||
</GridPane.margin>
|
||||
</BalanceTextField>
|
||||
|
||||
<InfoDisplay gridPane="$root" onAction="#onOpenHelp" rowIndex="3"
|
||||
text="You need to pay a 0.0002 BTC which is needed for storing your encrypted account data in the blockchain. That will be used in the trade process for account verification."/>
|
||||
|
||||
<HBox spacing="10" GridPane.columnIndex="1" GridPane.rowIndex="4">
|
||||
<Button fx:id="payButton" text="Pay registration fee" onAction="#onPayFee" defaultButton="true"/>
|
||||
<ProgressIndicator fx:id="paymentSpinner" progress="0" visible="false" prefHeight="24"
|
||||
prefWidth="24"/>
|
||||
<Label fx:id="paymentSpinnerInfoLabel" text="Payment is in progress..." visible="false">
|
||||
<padding>
|
||||
<Insets top="5.0"/>
|
||||
</padding>
|
||||
</Label>
|
||||
<GridPane.margin>
|
||||
<Insets top="15.0"/>
|
||||
</GridPane.margin>
|
||||
</HBox>
|
||||
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" halignment="RIGHT" minWidth="200.0"/>
|
||||
<ColumnConstraints hgrow="ALWAYS" minWidth="300.0"/>
|
||||
</columnConstraints>
|
||||
|
||||
<rowConstraints>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
</rowConstraints>
|
||||
</GridPane>
|
|
@ -1,138 +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.main.account.content.registration;
|
||||
|
||||
import io.bitsquare.gui.OverlayManager;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.common.view.InitializableView;
|
||||
import io.bitsquare.gui.common.view.Wizard;
|
||||
import io.bitsquare.gui.components.AddressTextField;
|
||||
import io.bitsquare.gui.components.BalanceTextField;
|
||||
import io.bitsquare.gui.components.Popups;
|
||||
import io.bitsquare.gui.main.help.Help;
|
||||
import io.bitsquare.gui.main.help.HelpId;
|
||||
import io.bitsquare.locale.BSResources;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.*;
|
||||
|
||||
import org.controlsfx.control.action.AbstractAction;
|
||||
import org.controlsfx.control.action.Action;
|
||||
import org.controlsfx.dialog.Dialog;
|
||||
|
||||
@FxmlView
|
||||
public class RegistrationView extends InitializableView<GridPane, RegistrationViewModel> implements Wizard.Step {
|
||||
|
||||
@FXML TextField feeTextField;
|
||||
@FXML AddressTextField addressTextField;
|
||||
@FXML BalanceTextField balanceTextField;
|
||||
@FXML Button payButton;
|
||||
@FXML Label paymentSpinnerInfoLabel;
|
||||
@FXML ProgressIndicator paymentSpinner;
|
||||
|
||||
private Wizard wizard;
|
||||
|
||||
@Inject
|
||||
private RegistrationView(RegistrationViewModel model) {
|
||||
super(model);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
feeTextField.setText(model.getFeeAsString());
|
||||
addressTextField.setAmountAsCoin(model.getFeeAsCoin());
|
||||
addressTextField.setPaymentLabel(model.getPaymentLabel());
|
||||
addressTextField.setAddress(model.getAddressAsString());
|
||||
|
||||
balanceTextField.setup(model.getWalletService(), model.address.get(),
|
||||
model.getFormatter());
|
||||
|
||||
payButton.disableProperty().bind(model.isPayButtonDisabled);
|
||||
|
||||
model.requestPlaceOfferErrorMessage.addListener((o, oldValue, newValue) -> {
|
||||
if (newValue != null) {
|
||||
Popups.openErrorPopup(BSResources.get("shared.error"),
|
||||
BSResources.get("An error occurred when paying the registration fee"),
|
||||
newValue);
|
||||
}
|
||||
});
|
||||
|
||||
paymentSpinnerInfoLabel.visibleProperty().bind(model.isPaymentSpinnerVisible);
|
||||
|
||||
model.isPaymentSpinnerVisible.addListener((ov, oldValue, newValue) -> {
|
||||
paymentSpinner.setProgress(newValue ? -1 : 0);
|
||||
paymentSpinner.setVisible(newValue);
|
||||
});
|
||||
|
||||
model.showTransactionPublishedScreen.addListener((o, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
OverlayManager.blurContent();
|
||||
|
||||
List<Action> actions = new ArrayList<>();
|
||||
/* actions.add(new AbstractAction(BSResources.get("shared.copyTxId")) {
|
||||
@Override
|
||||
public void handle(ActionEvent actionEvent) {
|
||||
getProperties().put("type", "COPY");
|
||||
Utilities.copyToClipboard(presentationModel.getTransactionId());
|
||||
}
|
||||
});*/
|
||||
actions.add(new AbstractAction(BSResources.get("shared.close")) {
|
||||
@Override
|
||||
public void handle(ActionEvent actionEvent) {
|
||||
getProperties().put("type", "CLOSE");
|
||||
if (wizard != null)
|
||||
wizard.nextStep(RegistrationView.this);
|
||||
Dialog.Actions.CLOSE.handle(actionEvent);
|
||||
}
|
||||
});
|
||||
|
||||
Popups.openInfoPopup(BSResources.get("You have been successfully registered."),
|
||||
BSResources.get("Congratulation you have been successfully registered.\n\n" +
|
||||
" You can now start trading."),
|
||||
actions);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWizard(Wizard wizard) {
|
||||
this.wizard = wizard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideWizardNavigation() {
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onPayFee() {
|
||||
model.payFee();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onOpenHelp() {
|
||||
Help.openWindow(HelpId.PAY_ACCOUNT_FEE);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,125 +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.main.account.content.registration;
|
||||
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.gui.common.model.ViewModel;
|
||||
import io.bitsquare.gui.common.model.WithDataModel;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.locale.BSResources;
|
||||
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
|
||||
|
||||
class RegistrationViewModel extends WithDataModel<RegistrationDataModel> implements ViewModel {
|
||||
|
||||
final BooleanProperty isPayButtonDisabled = new SimpleBooleanProperty(true);
|
||||
final StringProperty requestPlaceOfferErrorMessage = new SimpleStringProperty();
|
||||
final BooleanProperty showTransactionPublishedScreen = new SimpleBooleanProperty();
|
||||
final BooleanProperty isPaymentSpinnerVisible = new SimpleBooleanProperty(false);
|
||||
|
||||
// That is needed for the addressTextField
|
||||
final ObjectProperty<Address> address = new SimpleObjectProperty<>();
|
||||
private final BSFormatter formatter;
|
||||
|
||||
|
||||
@Inject
|
||||
public RegistrationViewModel(RegistrationDataModel dataModel, BSFormatter formatter) {
|
||||
super(dataModel);
|
||||
this.formatter = formatter;
|
||||
|
||||
if (dataModel.getAddressEntry() != null) {
|
||||
address.set(dataModel.getAddressEntry().getAddress());
|
||||
}
|
||||
|
||||
dataModel.isWalletFunded.addListener((ov, oldValue, newValue) -> {
|
||||
if (newValue)
|
||||
validateInput();
|
||||
});
|
||||
validateInput();
|
||||
|
||||
dataModel.payFeeSuccess.addListener((ov, oldValue, newValue) -> {
|
||||
isPayButtonDisabled.set(newValue);
|
||||
showTransactionPublishedScreen.set(newValue);
|
||||
isPaymentSpinnerVisible.set(false);
|
||||
});
|
||||
|
||||
dataModel.payFeeErrorMessage.addListener((ov, oldValue, newValue) -> {
|
||||
if (newValue != null) {
|
||||
requestPlaceOfferErrorMessage.set(newValue);
|
||||
isPaymentSpinnerVisible.set(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void payFee() {
|
||||
dataModel.payFeeErrorMessage.set(null);
|
||||
dataModel.payFeeSuccess.set(false);
|
||||
|
||||
isPayButtonDisabled.set(true);
|
||||
isPaymentSpinnerVisible.set(true);
|
||||
|
||||
dataModel.payFee();
|
||||
}
|
||||
|
||||
|
||||
WalletService getWalletService() {
|
||||
return dataModel.getWalletService();
|
||||
}
|
||||
|
||||
BSFormatter getFormatter() {
|
||||
return formatter;
|
||||
}
|
||||
|
||||
Coin getFeeAsCoin() {
|
||||
return dataModel.getFeeAsCoin();
|
||||
}
|
||||
|
||||
String getAddressAsString() {
|
||||
return dataModel.getAddressEntry() != null ? dataModel.getAddressEntry().getAddress().toString() : "";
|
||||
}
|
||||
|
||||
String getPaymentLabel() {
|
||||
return BSResources.get("Bitsquare account registration fee");
|
||||
}
|
||||
|
||||
String getFeeAsString() {
|
||||
return formatter.formatCoinWithCode(dataModel.getFeeAsCoin());
|
||||
}
|
||||
|
||||
String getTransactionId() {
|
||||
return dataModel.getTransactionId();
|
||||
}
|
||||
|
||||
|
||||
private void validateInput() {
|
||||
isPayButtonDisabled.set(!(dataModel.isWalletFunded.get()));
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,106 +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.main.account.content.restrictions;
|
||||
|
||||
import io.bitsquare.arbitration.Arbitrator;
|
||||
import io.bitsquare.gui.common.model.Activatable;
|
||||
import io.bitsquare.gui.common.model.DataModel;
|
||||
import io.bitsquare.locale.Country;
|
||||
import io.bitsquare.locale.CountryUtil;
|
||||
import io.bitsquare.locale.LanguageUtil;
|
||||
import io.bitsquare.locale.Region;
|
||||
import io.bitsquare.user.AccountSettings;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
class RestrictionsDataModel implements Activatable, DataModel {
|
||||
|
||||
private final AccountSettings accountSettings;
|
||||
|
||||
final ObservableList<String> languageCodes = FXCollections.observableArrayList();
|
||||
final ObservableList<Country> countries = FXCollections.observableArrayList();
|
||||
final ObservableList<Arbitrator> arbitrators = FXCollections.observableArrayList();
|
||||
final ObservableList<String> allLanguageCodes = FXCollections.observableArrayList(LanguageUtil
|
||||
.getAllLanguageLocaleCodes());
|
||||
final ObservableList<Region> allRegions = FXCollections.observableArrayList(CountryUtil.getAllRegions());
|
||||
|
||||
|
||||
@Inject
|
||||
public RestrictionsDataModel(AccountSettings accountSettings) {
|
||||
this.accountSettings = accountSettings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activate() {
|
||||
countries.setAll(accountSettings.getAcceptedCountries());
|
||||
languageCodes.setAll(accountSettings.getAcceptedLanguageLocaleCodes());
|
||||
arbitrators.setAll(accountSettings.getAcceptedArbitrators());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
}
|
||||
|
||||
ObservableList<Country> getAllCountriesFor(Region selectedRegion) {
|
||||
return FXCollections.observableArrayList(CountryUtil.getAllCountriesFor(selectedRegion));
|
||||
}
|
||||
|
||||
void updateArbitratorList() {
|
||||
arbitrators.setAll(accountSettings.getAcceptedArbitrators());
|
||||
}
|
||||
|
||||
void addLanguageCode(String code) {
|
||||
if (code != null && !languageCodes.contains(code)) {
|
||||
languageCodes.add(code);
|
||||
accountSettings.addAcceptedLanguageLocale(code);
|
||||
}
|
||||
}
|
||||
|
||||
void removeLanguage(String code) {
|
||||
languageCodes.remove(code);
|
||||
accountSettings.removeAcceptedLanguageLocale(code);
|
||||
}
|
||||
|
||||
void addCountry(Country country) {
|
||||
if (!countries.contains(country) && country != null) {
|
||||
countries.add(country);
|
||||
accountSettings.addAcceptedCountry(country);
|
||||
}
|
||||
}
|
||||
|
||||
ObservableList<Country> getListWithAllEuroCountries() {
|
||||
// TODO use Set instead of List
|
||||
// In addAcceptedCountry there is a check to no add duplicates, so it works correctly for now
|
||||
CountryUtil.getAllEuroCountries().stream().forEach(accountSettings::addAcceptedCountry);
|
||||
countries.setAll(accountSettings.getAcceptedCountries());
|
||||
return countries;
|
||||
}
|
||||
|
||||
void removeCountry(Country country) {
|
||||
countries.remove(country);
|
||||
accountSettings.removeAcceptedCountry(country);
|
||||
}
|
||||
|
||||
void removeArbitrator(Arbitrator arbitrator) {
|
||||
arbitrators.remove(arbitrator);
|
||||
accountSettings.removeAcceptedArbitrator(arbitrator);
|
||||
}
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
~ 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/>.
|
||||
-->
|
||||
|
||||
<?import io.bitsquare.gui.components.InfoDisplay?>
|
||||
<?import io.bitsquare.gui.components.TitledGroupBg?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<GridPane fx:id="root" fx:controller="io.bitsquare.gui.main.account.content.restrictions.RestrictionsView" hgap="5.0"
|
||||
vgap="5.0"
|
||||
AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
|
||||
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
|
||||
<!--
|
||||
languages
|
||||
-->
|
||||
<TitledGroupBg text="Add languages" GridPane.rowSpan="3"/>
|
||||
|
||||
<Label text="Accepted languages:" GridPane.rowIndex="0" GridPane.valignment="TOP">
|
||||
<padding>
|
||||
<Insets top="10"/>
|
||||
</padding>
|
||||
</Label>
|
||||
<ListView fx:id="languagesListView" GridPane.columnIndex="1" GridPane.rowIndex="0" prefHeight="80.0"/>
|
||||
<ComboBox fx:id="languageComboBox" onAction="#onAddLanguage" promptText="Add language"
|
||||
GridPane.columnIndex="1" GridPane.rowIndex="1"
|
||||
prefWidth="150.0"/>
|
||||
|
||||
|
||||
<InfoDisplay gridPane="$root" onAction="#onOpenLanguagesHelp" rowIndex="2"
|
||||
text="Trade with users who have at least 1 shared language."/>
|
||||
|
||||
<!--
|
||||
countries
|
||||
-->
|
||||
<TitledGroupBg text="Add countries" GridPane.rowIndex="3" GridPane.rowSpan="3">
|
||||
<padding>
|
||||
<Insets top="50.0"/>
|
||||
</padding>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="-10" left="-10" right="-10" top="30"/>
|
||||
</GridPane.margin>
|
||||
</TitledGroupBg>
|
||||
|
||||
<Label text="Accepted countries:" GridPane.rowIndex="3" GridPane.valignment="TOP">
|
||||
<GridPane.margin>
|
||||
<Insets top="50"/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<ListView fx:id="countriesListView" GridPane.columnIndex="1" GridPane.rowIndex="3" prefHeight="80.0"
|
||||
>
|
||||
<GridPane.margin>
|
||||
<Insets top="40"/>
|
||||
</GridPane.margin>
|
||||
</ListView>
|
||||
|
||||
<HBox GridPane.columnIndex="1" GridPane.rowIndex="4" spacing="10">
|
||||
<ComboBox fx:id="regionComboBox" onAction="#onSelectRegion" promptText="Select region"
|
||||
prefWidth="150.0"/>
|
||||
<ComboBox fx:id="countryComboBox" onAction="#onAddCountry" promptText="Add country" visible="false"
|
||||
prefWidth="150.0"/>
|
||||
<Button fx:id="addAllEuroCountriesButton" text="Add Euro countries" onAction="#onAddAllEuroCountries"
|
||||
prefWidth="150.0" visible="false"/>
|
||||
</HBox>
|
||||
|
||||
<InfoDisplay gridPane="$root" onAction="#onOpenCountriesHelp" rowIndex="5"
|
||||
text="Restrict trades with these payments account countries."/>
|
||||
|
||||
|
||||
<!--
|
||||
arbitrators
|
||||
-->
|
||||
<TitledGroupBg text="Add arbitrators" GridPane.rowIndex="6" GridPane.rowSpan="3">
|
||||
<padding>
|
||||
<Insets top="50.0"/>
|
||||
</padding>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="-10" left="-10" right="-10" top="30"/>
|
||||
</GridPane.margin>
|
||||
</TitledGroupBg>
|
||||
<Label text="Accepted arbitrators:" GridPane.rowIndex="6" GridPane.valignment="TOP">
|
||||
<GridPane.margin>
|
||||
<Insets top="50"/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<ListView fx:id="arbitratorsListView" GridPane.columnIndex="1" GridPane.rowIndex="6" prefHeight="80.0">
|
||||
<GridPane.margin>
|
||||
<Insets top="40"/>
|
||||
</GridPane.margin>
|
||||
</ListView>
|
||||
<Button text="Add arbitrator" onAction="#onOpenArbitratorScreen" GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="7"/>
|
||||
|
||||
<InfoDisplay gridPane="$root" onAction="#onOpenArbitratorsHelp" rowIndex="8"
|
||||
text="You need to choose at least 3 arbitrators."/>
|
||||
|
||||
<Button fx:id="completedButton" text="Completed" onAction="#onCompleted" disable="true" GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="9"
|
||||
defaultButton="true">
|
||||
<GridPane.margin>
|
||||
<Insets top="20.0"/>
|
||||
</GridPane.margin>
|
||||
</Button>
|
||||
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" halignment="RIGHT" minWidth="140.0"/>
|
||||
<ColumnConstraints hgrow="ALWAYS" minWidth="300.0"/>
|
||||
</columnConstraints>
|
||||
|
||||
<rowConstraints>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
|
||||
</rowConstraints>
|
||||
|
||||
</GridPane>
|
|
@ -1,324 +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.main.account.content.restrictions;
|
||||
|
||||
import io.bitsquare.arbitration.Arbitrator;
|
||||
import io.bitsquare.gui.common.view.ActivatableViewAndModel;
|
||||
import io.bitsquare.gui.common.view.CachingViewLoader;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.common.view.View;
|
||||
import io.bitsquare.gui.common.view.ViewLoader;
|
||||
import io.bitsquare.gui.common.view.Wizard;
|
||||
import io.bitsquare.gui.main.account.arbitrator.browser.BrowserView;
|
||||
import io.bitsquare.gui.main.help.Help;
|
||||
import io.bitsquare.gui.main.help.HelpId;
|
||||
import io.bitsquare.gui.util.ImageUtil;
|
||||
import io.bitsquare.locale.Country;
|
||||
import io.bitsquare.locale.LanguageUtil;
|
||||
import io.bitsquare.locale.Region;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.*;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.image.*;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.util.Callback;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
@FxmlView
|
||||
public class RestrictionsView extends ActivatableViewAndModel<GridPane, RestrictionsViewModel> implements Wizard.Step {
|
||||
|
||||
@FXML ListView<String> languagesListView;
|
||||
@FXML ListView<Country> countriesListView;
|
||||
@FXML ListView<Arbitrator> arbitratorsListView;
|
||||
@FXML ComboBox<String> languageComboBox;
|
||||
@FXML ComboBox<Region> regionComboBox;
|
||||
@FXML ComboBox<Country> countryComboBox;
|
||||
@FXML Button completedButton, addAllEuroCountriesButton;
|
||||
|
||||
private Wizard wizard;
|
||||
|
||||
private final ViewLoader viewLoader;
|
||||
private final Stage primaryStage;
|
||||
|
||||
@Inject
|
||||
private RestrictionsView(RestrictionsViewModel model, CachingViewLoader viewLoader, Stage primaryStage) {
|
||||
super(model);
|
||||
this.viewLoader = viewLoader;
|
||||
this.primaryStage = primaryStage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
initLanguage();
|
||||
initCountry();
|
||||
initArbitrators();
|
||||
|
||||
completedButton.disableProperty().bind(model.doneButtonDisable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doActivate() {
|
||||
languagesListView.setItems(model.getLanguageCodes());
|
||||
countriesListView.setItems(model.getCountryList());
|
||||
arbitratorsListView.setItems(model.getArbitratorList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWizard(Wizard wizard) {
|
||||
this.wizard = wizard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideWizardNavigation() {
|
||||
root.getChildren().remove(completedButton);
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onAddLanguage() {
|
||||
model.addLanguage(languageComboBox.getSelectionModel().getSelectedItem());
|
||||
languageComboBox.getSelectionModel().clearSelection();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onSelectRegion() {
|
||||
countryComboBox.setVisible(true);
|
||||
Region region = regionComboBox.getSelectionModel().getSelectedItem();
|
||||
countryComboBox.setItems(model.getAllCountriesFor(region));
|
||||
|
||||
addAllEuroCountriesButton.setVisible(region.code.equals("EU"));
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onAddCountry() {
|
||||
model.addCountry(countryComboBox.getSelectionModel().getSelectedItem());
|
||||
countryComboBox.getSelectionModel().clearSelection();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onAddAllEuroCountries() {
|
||||
countriesListView.setItems(model.getListWithAllEuroCountries());
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onOpenArbitratorScreen() {
|
||||
View view = viewLoader.load(BrowserView.class);
|
||||
showStage(view);
|
||||
}
|
||||
|
||||
|
||||
@FXML
|
||||
private void onCompleted() {
|
||||
if (wizard != null)
|
||||
wizard.nextStep(this);
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onOpenLanguagesHelp() {
|
||||
Help.openWindow(HelpId.SETUP_RESTRICTION_LANGUAGES);
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onOpenCountriesHelp() {
|
||||
Help.openWindow(HelpId.SETUP_RESTRICTION_COUNTRIES);
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onOpenArbitratorsHelp() {
|
||||
Help.openWindow(HelpId.SETUP_RESTRICTION_ARBITRATORS);
|
||||
}
|
||||
|
||||
private void showStage(View view) {
|
||||
final Stage stage = new Stage();
|
||||
stage.setTitle("Arbitrator selection");
|
||||
stage.setMinWidth(800);
|
||||
stage.setMinHeight(500);
|
||||
stage.setWidth(800);
|
||||
stage.setHeight(600);
|
||||
stage.setX(primaryStage.getX() + 50);
|
||||
stage.setY(primaryStage.getY() + 50);
|
||||
stage.initModality(Modality.WINDOW_MODAL);
|
||||
stage.initOwner(primaryStage);
|
||||
Scene scene = new Scene((Parent) view.getRoot(), 800, 600);
|
||||
stage.setScene(scene);
|
||||
stage.setOnHidden(windowEvent -> {
|
||||
if (view instanceof BrowserView)
|
||||
updateArbitratorList();
|
||||
});
|
||||
stage.show();
|
||||
}
|
||||
|
||||
void updateArbitratorList() {
|
||||
model.updateArbitratorList();
|
||||
arbitratorsListView.setItems(model.getArbitratorList());
|
||||
}
|
||||
|
||||
private void initLanguage() {
|
||||
languagesListView.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
|
||||
@Override
|
||||
public ListCell<String> call(ListView<String> list) {
|
||||
return new ListCell<String>() {
|
||||
final Label label = new Label();
|
||||
final ImageView icon = ImageUtil.getImageViewById(ImageUtil.REMOVE_ICON);
|
||||
final Button removeButton = new Button("", icon);
|
||||
final AnchorPane pane = new AnchorPane(label, removeButton);
|
||||
|
||||
{
|
||||
label.setLayoutY(5);
|
||||
removeButton.setId("icon-button");
|
||||
AnchorPane.setRightAnchor(removeButton, 0d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(final String item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (item != null && !empty) {
|
||||
label.setText(LanguageUtil.getDisplayName(item));
|
||||
removeButton.setOnAction(actionEvent -> removeLanguage(item));
|
||||
setGraphic(pane);
|
||||
}
|
||||
else {
|
||||
setGraphic(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
languageComboBox.setItems(model.getAllLanguageCodes());
|
||||
languageComboBox.setConverter(new StringConverter<String>() {
|
||||
@Override
|
||||
public String toString(String code) {
|
||||
return LanguageUtil.getDisplayName(code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initCountry() {
|
||||
regionComboBox.setItems(model.getAllRegions());
|
||||
regionComboBox.setConverter(new StringConverter<io.bitsquare.locale.Region>() {
|
||||
@Override
|
||||
public String toString(io.bitsquare.locale.Region region) {
|
||||
return region.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public io.bitsquare.locale.Region fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
countriesListView.setCellFactory(new Callback<ListView<Country>, ListCell<Country>>() {
|
||||
@Override
|
||||
public ListCell<Country> call(ListView<Country> list) {
|
||||
return new ListCell<Country>() {
|
||||
final Label label = new Label();
|
||||
final ImageView icon = ImageUtil.getImageViewById(ImageUtil.REMOVE_ICON);
|
||||
final Button removeButton = new Button("", icon);
|
||||
final AnchorPane pane = new AnchorPane(label, removeButton);
|
||||
|
||||
{
|
||||
label.setLayoutY(5);
|
||||
removeButton.setId("icon-button");
|
||||
AnchorPane.setRightAnchor(removeButton, 0d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(final Country country, boolean empty) {
|
||||
super.updateItem(country, empty);
|
||||
if (country != null && !empty) {
|
||||
label.setText(country.name);
|
||||
removeButton.setOnAction(actionEvent -> removeCountry(country));
|
||||
setGraphic(pane);
|
||||
}
|
||||
else {
|
||||
setGraphic(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
countryComboBox.setConverter(new StringConverter<Country>() {
|
||||
@Override
|
||||
public String toString(Country country) {
|
||||
return country.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Country fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initArbitrators() {
|
||||
arbitratorsListView.setCellFactory(new Callback<ListView<Arbitrator>, ListCell<Arbitrator>>() {
|
||||
@Override
|
||||
public ListCell<Arbitrator> call(ListView<Arbitrator> list) {
|
||||
return new ListCell<Arbitrator>() {
|
||||
final Label label = new Label();
|
||||
final ImageView icon = ImageUtil.getImageViewById(ImageUtil.REMOVE_ICON);
|
||||
final Button removeButton = new Button("", icon);
|
||||
final AnchorPane pane = new AnchorPane(label, removeButton);
|
||||
|
||||
{
|
||||
label.setLayoutY(5);
|
||||
removeButton.setId("icon-button");
|
||||
AnchorPane.setRightAnchor(removeButton, 0d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(final Arbitrator item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (item != null && !empty) {
|
||||
label.setText(item.getName());
|
||||
removeButton.setOnAction(actionEvent -> removeArbitrator(item));
|
||||
setGraphic(pane);
|
||||
}
|
||||
else {
|
||||
setGraphic(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void removeLanguage(String locale) {
|
||||
model.removeLanguage(locale);
|
||||
}
|
||||
|
||||
private void removeCountry(Country country) {
|
||||
model.removeCountry(country);
|
||||
}
|
||||
|
||||
private void removeArbitrator(Arbitrator arbitrator) {
|
||||
model.removeArbitrator(arbitrator);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,116 +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.main.account.content.restrictions;
|
||||
|
||||
import io.bitsquare.arbitration.Arbitrator;
|
||||
import io.bitsquare.gui.common.model.ActivatableWithDataModel;
|
||||
import io.bitsquare.gui.common.model.ViewModel;
|
||||
import io.bitsquare.locale.Country;
|
||||
import io.bitsquare.locale.Region;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
class RestrictionsViewModel extends ActivatableWithDataModel<RestrictionsDataModel> implements ViewModel {
|
||||
|
||||
final BooleanProperty doneButtonDisable = new SimpleBooleanProperty(true);
|
||||
|
||||
|
||||
@Inject
|
||||
public RestrictionsViewModel(RestrictionsDataModel dataModel) {
|
||||
super(dataModel);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void doActivate() {
|
||||
updateDoneButtonDisableState();
|
||||
}
|
||||
|
||||
void addLanguage(String locale) {
|
||||
dataModel.addLanguageCode(locale);
|
||||
updateDoneButtonDisableState();
|
||||
}
|
||||
|
||||
void removeLanguage(String locale) {
|
||||
dataModel.removeLanguage(locale);
|
||||
updateDoneButtonDisableState();
|
||||
}
|
||||
|
||||
void addCountry(Country country) {
|
||||
dataModel.addCountry(country);
|
||||
updateDoneButtonDisableState();
|
||||
}
|
||||
|
||||
void removeCountry(Country country) {
|
||||
dataModel.removeCountry(country);
|
||||
updateDoneButtonDisableState();
|
||||
}
|
||||
|
||||
void removeArbitrator(Arbitrator arbitrator) {
|
||||
dataModel.removeArbitrator(arbitrator);
|
||||
updateDoneButtonDisableState();
|
||||
}
|
||||
|
||||
void updateArbitratorList() {
|
||||
dataModel.updateArbitratorList();
|
||||
updateDoneButtonDisableState();
|
||||
}
|
||||
|
||||
|
||||
ObservableList<Country> getListWithAllEuroCountries() {
|
||||
return dataModel.getListWithAllEuroCountries();
|
||||
}
|
||||
|
||||
ObservableList<Country> getAllCountriesFor(Region selectedRegion) {
|
||||
return dataModel.getAllCountriesFor(selectedRegion);
|
||||
}
|
||||
|
||||
ObservableList<String> getLanguageCodes() {
|
||||
return dataModel.languageCodes;
|
||||
}
|
||||
|
||||
ObservableList<Region> getAllRegions() {
|
||||
return dataModel.allRegions;
|
||||
}
|
||||
|
||||
ObservableList<String> getAllLanguageCodes() {
|
||||
return dataModel.allLanguageCodes;
|
||||
}
|
||||
|
||||
ObservableList<Country> getCountryList() {
|
||||
return dataModel.countries;
|
||||
}
|
||||
|
||||
ObservableList<Arbitrator> getArbitratorList() {
|
||||
return dataModel.arbitrators;
|
||||
}
|
||||
|
||||
|
||||
//TODO Revert size() > -1 to 0(2 later). For mock testing disabled arbitratorList test
|
||||
private void updateDoneButtonDisableState() {
|
||||
boolean isValid = dataModel.languageCodes != null && dataModel.languageCodes.size() > 0 &&
|
||||
dataModel.countries != null && dataModel.countries.size() > 0 &&
|
||||
dataModel.arbitrators != null && dataModel.arbitrators.size() > -1;
|
||||
doneButtonDisable.set(!isValid);
|
||||
}
|
||||
|
||||
}
|
|
@ -17,51 +17,16 @@
|
|||
~ along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<?import io.bitsquare.gui.components.InfoDisplay?>
|
||||
<?import io.bitsquare.gui.components.TitledGroupBg?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.text.*?>
|
||||
<GridPane fx:id="root" fx:controller="io.bitsquare.gui.main.account.content.seedwords.SeedWordsView" hgap="5.0"
|
||||
vgap="5.0"
|
||||
<GridPane fx:id="root" fx:controller="io.bitsquare.gui.main.account.content.seedwords.SeedWordsView"
|
||||
hgap="5.0" vgap="5.0"
|
||||
AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
|
||||
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"
|
||||
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="-10.0"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
|
||||
<TitledGroupBg text="Backup your wallet seed words" GridPane.rowSpan="4"/>
|
||||
|
||||
<Label text="Wallet seed words:" GridPane.columnIndex="0" GridPane.rowIndex="1" GridPane.valignment="TOP">
|
||||
<GridPane.margin>
|
||||
<Insets top="7.0"/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<TextArea fx:id="seedWordsTextArea" GridPane.columnIndex="1" GridPane.rowIndex="1" wrapText="true"
|
||||
prefHeight="80">
|
||||
<font>
|
||||
<Font size="16.0"/>
|
||||
</font>
|
||||
</TextArea>
|
||||
<Button fx:id="completedButton" text="I have made my backup" onAction="#onCompleted"
|
||||
GridPane.columnIndex="1" GridPane.rowIndex="2"
|
||||
defaultButton="true">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5"/>
|
||||
</GridPane.margin>
|
||||
</Button>
|
||||
<InfoDisplay gridPane="$root" onAction="#onOpenHelp" rowIndex="3"
|
||||
text="You can recreate your wallet our of these words when you lose your wallet. Backup it on paper to have better protection against online theft. Open the help menu for more information."/>
|
||||
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" halignment="RIGHT" minWidth="140.0"/>
|
||||
<ColumnConstraints hgrow="ALWAYS" minWidth="300.0"/>
|
||||
</columnConstraints>
|
||||
|
||||
<rowConstraints>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
<RowConstraints vgrow="NEVER"/>
|
||||
</rowConstraints>
|
||||
|
||||
</GridPane>
|
||||
</GridPane>
|
||||
|
|
|
@ -17,55 +17,212 @@
|
|||
|
||||
package io.bitsquare.gui.main.account.content.seedwords;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
import io.bitsquare.app.BitsquareApp;
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.gui.common.view.ActivatableView;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.common.view.InitializableView;
|
||||
import io.bitsquare.gui.common.view.Wizard;
|
||||
import io.bitsquare.gui.main.help.Help;
|
||||
import io.bitsquare.gui.main.help.HelpId;
|
||||
import io.bitsquare.gui.popups.Popup;
|
||||
import io.bitsquare.gui.popups.WalletPasswordPopup;
|
||||
import io.bitsquare.gui.util.Layout;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.DatePicker;
|
||||
import javafx.scene.control.TextArea;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import org.bitcoinj.core.Context;
|
||||
import org.bitcoinj.core.Wallet;
|
||||
import org.bitcoinj.crypto.KeyCrypter;
|
||||
import org.bitcoinj.crypto.MnemonicCode;
|
||||
import org.bitcoinj.crypto.MnemonicException;
|
||||
import org.bitcoinj.wallet.DeterministicSeed;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.List;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.*;
|
||||
import static io.bitsquare.gui.util.FormBuilder.*;
|
||||
import static javafx.beans.binding.Bindings.createBooleanBinding;
|
||||
|
||||
@FxmlView
|
||||
public class SeedWordsView extends InitializableView<GridPane, SeedWordsViewModel> implements Wizard.Step {
|
||||
public class SeedWordsView extends ActivatableView<GridPane, Void> {
|
||||
private final WalletService walletService;
|
||||
private final WalletPasswordPopup walletPasswordPopup;
|
||||
|
||||
@FXML Button completedButton;
|
||||
@FXML TextArea seedWordsTextArea;
|
||||
private Button restoreButton;
|
||||
private TextArea seedWordsTextArea;
|
||||
private DatePicker datePicker;
|
||||
|
||||
private Wizard wizard;
|
||||
private int gridRow = 0;
|
||||
private DeterministicSeed keyChainSeed;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor, lifecycle
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
private SeedWordsView(SeedWordsViewModel model) {
|
||||
super(model);
|
||||
private SeedWordsView(WalletService walletService, WalletPasswordPopup walletPasswordPopup) {
|
||||
this.walletService = walletService;
|
||||
this.walletPasswordPopup = walletPasswordPopup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
seedWordsTextArea.setText(model.seedWords.get());
|
||||
protected void initialize() {
|
||||
addTitledGroupBg(root, gridRow, 3, "Backup or restore your wallet seed words");
|
||||
seedWordsTextArea = addLabelTextArea(root, gridRow, "Wallet seed words:", "", Layout.FIRST_ROW_DISTANCE).second;
|
||||
seedWordsTextArea.setPrefHeight(60);
|
||||
datePicker = addLabelDatePicker(root, ++gridRow, "Creation Date:").second;
|
||||
restoreButton = addButton(root, ++gridRow, "Restore wallet");
|
||||
|
||||
addTitledGroupBg(root, ++gridRow, 1, "Information", Layout.GROUP_DISTANCE);
|
||||
addMultilineLabel(root, gridRow, "Please write down you wallet seed words and the creation date.\n" +
|
||||
"You can recover your wallet with those words and the date in emergency case.",
|
||||
Layout.FIRST_ROW_AND_GROUP_DISTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWizard(Wizard wizard) {
|
||||
this.wizard = wizard;
|
||||
public void activate() {
|
||||
seedWordsTextArea.getStyleClass().remove("validation_error");
|
||||
datePicker.getStyleClass().remove("validation_error");
|
||||
|
||||
DeterministicSeed keyChainSeed = walletService.getWallet().getKeyChainSeed();
|
||||
if (keyChainSeed.isEncrypted()) {
|
||||
restoreButton.setDisable(true);
|
||||
seedWordsTextArea.setDisable(true);
|
||||
datePicker.setDisable(true);
|
||||
askForPassword();
|
||||
} else {
|
||||
showSeedScreen(keyChainSeed);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideWizardNavigation() {
|
||||
root.getChildren().remove(completedButton);
|
||||
protected void deactivate() {
|
||||
seedWordsTextArea.setText("");
|
||||
datePicker.setValue(null);
|
||||
restoreButton.disableProperty().unbind();
|
||||
seedWordsTextArea.getStyleClass().remove("validation_error");
|
||||
datePicker.getStyleClass().remove("validation_error");
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onCompleted() {
|
||||
if (wizard != null)
|
||||
wizard.nextStep(this);
|
||||
|
||||
private void askForPassword() {
|
||||
walletPasswordPopup.show().onAesKey(aesKey -> {
|
||||
Wallet wallet = walletService.getWallet();
|
||||
KeyCrypter keyCrypter = wallet.getKeyCrypter();
|
||||
keyChainSeed = wallet.getKeyChainSeed();
|
||||
DeterministicSeed decryptedSeed = keyChainSeed.decrypt(keyCrypter, "", aesKey);
|
||||
showSeedScreen(decryptedSeed);
|
||||
});
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onOpenHelp() {
|
||||
Help.openWindow(HelpId.SETUP_SEED_WORDS);
|
||||
}
|
||||
}
|
||||
private void showSeedScreen(DeterministicSeed seed) {
|
||||
seedWordsTextArea.setDisable(false);
|
||||
datePicker.setDisable(false);
|
||||
List<String> mnemonicCode = seed.getMnemonicCode();
|
||||
if (mnemonicCode != null)
|
||||
seedWordsTextArea.setText(Joiner.on(" ").join(mnemonicCode));
|
||||
LocalDate creationDate = Instant.ofEpochSecond(seed.getCreationTimeSeconds()).atZone(ZoneId.systemDefault()).toLocalDate();
|
||||
datePicker.setValue(creationDate);
|
||||
restoreButton.setOnAction(e -> onRestore());
|
||||
|
||||
BooleanProperty seedWordsEdited = new SimpleBooleanProperty();
|
||||
BooleanProperty seedWordsValid = new SimpleBooleanProperty(true);
|
||||
seedWordsValid.addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
seedWordsTextArea.getStyleClass().remove("validation_error");
|
||||
} else {
|
||||
seedWordsTextArea.getStyleClass().add("validation_error");
|
||||
}
|
||||
});
|
||||
seedWordsTextArea.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
seedWordsEdited.set(true);
|
||||
try {
|
||||
MnemonicCode codec = new MnemonicCode();
|
||||
codec.check(Splitter.on(" ").splitToList(newValue));
|
||||
seedWordsValid.set(true);
|
||||
} catch (IOException | MnemonicException e) {
|
||||
seedWordsValid.set(false);
|
||||
}
|
||||
|
||||
if (creationDate.equals(datePicker.getValue()))
|
||||
datePicker.setValue(null);
|
||||
});
|
||||
|
||||
BooleanProperty dateValid = new SimpleBooleanProperty(true);
|
||||
dateValid.addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue)
|
||||
datePicker.getStyleClass().remove("validation_error");
|
||||
else
|
||||
datePicker.getStyleClass().add("validation_error");
|
||||
});
|
||||
datePicker.valueProperty().addListener((observable, oldValue, newValue) -> {
|
||||
dateValid.set(newValue != null && !newValue.isAfter(LocalDate.now()));
|
||||
});
|
||||
|
||||
restoreButton.disableProperty().bind(createBooleanBinding(() -> !seedWordsValid.get() || !dateValid.get() || !seedWordsEdited.get(),
|
||||
seedWordsValid, dateValid, seedWordsEdited));
|
||||
}
|
||||
|
||||
private void onRestore() {
|
||||
Wallet wallet = walletService.getWallet();
|
||||
if (wallet.getBalance(Wallet.BalanceType.AVAILABLE).value > 0) {
|
||||
new Popup()
|
||||
.headLine("Wallet is not empty")
|
||||
.warning("You must empty this wallet out before attempting to restore an older one, as mixing wallets " +
|
||||
"together can lead to invalidated backups.\n\n" +
|
||||
"Please finalize your trades, close all your open offers and go to the Funds section to withdraw your Bitcoin.\n" +
|
||||
"In case you cannot access your Bitcoin you can use the emergency tool to empty the wallet.\n" +
|
||||
"To open that emergency tool press cmd + e.")
|
||||
.show();
|
||||
} else if (wallet.isEncrypted()) {
|
||||
new Popup()
|
||||
.headLine("Wallet is encrypted")
|
||||
.information("After restore, the wallet will no longer be encrypted and you must set a new password.")
|
||||
.closeButtonText("I understand")
|
||||
.onClose(() -> doRestore()).show();
|
||||
} else {
|
||||
doRestore();
|
||||
}
|
||||
}
|
||||
|
||||
private void doRestore() {
|
||||
log.info("Attempting wallet restore using seed '{}' from date {}", seedWordsTextArea.getText(), datePicker.getValue());
|
||||
long date = datePicker.getValue().atStartOfDay().toEpochSecond(ZoneOffset.UTC);
|
||||
DeterministicSeed seed = new DeterministicSeed(Splitter.on(" ").splitToList(seedWordsTextArea.getText()), null, "", date);
|
||||
Context ctx = Context.get();
|
||||
new Thread(() -> {
|
||||
Context.propagate(ctx);
|
||||
walletService.restoreSeedWords(seed,
|
||||
() -> UserThread.execute(() -> {
|
||||
log.debug("Wallet restored with seed words");
|
||||
|
||||
new Popup()
|
||||
.information("Wallet restored successfully with the new seed words.\n\n" +
|
||||
"You need to restart now the application.")
|
||||
.closeButtonText("Restart")
|
||||
.onClose(() -> BitsquareApp.restartDownHandler.run()).show();
|
||||
}),
|
||||
throwable -> UserThread.execute(() -> {
|
||||
log.error(throwable.getMessage());
|
||||
new Popup()
|
||||
.headLine("Wrong password")
|
||||
.warning("Please try entering your password again, carefully checking for typos or spelling errors.")
|
||||
.show();
|
||||
|
||||
new Popup()
|
||||
.error("An error occurred when restoring the wallet with seed words.\n" +
|
||||
"Error message: " + throwable.getMessage())
|
||||
.show();
|
||||
}));
|
||||
}, "Restore wallet thread").start();
|
||||
}
|
||||
}
|
|
@ -1,44 +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.main.account.content.seedwords;
|
||||
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.gui.common.model.ViewModel;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
|
||||
class SeedWordsViewModel implements ViewModel {
|
||||
|
||||
final StringProperty seedWords = new SimpleStringProperty();
|
||||
|
||||
@Inject
|
||||
public SeedWordsViewModel(WalletService walletService, BSFormatter formatter) {
|
||||
if (walletService.getWallet() != null) {
|
||||
List<String> mnemonicCode = walletService.getWallet().getKeyChainSeed().getMnemonicCode();
|
||||
if (mnemonicCode != null) {
|
||||
seedWords.set(formatter.mnemonicCodeToString(mnemonicCode));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,33 +17,29 @@
|
|||
|
||||
package io.bitsquare.gui.main.account.settings;
|
||||
|
||||
import io.bitsquare.gui.Navigation;
|
||||
import io.bitsquare.gui.common.view.ActivatableViewAndModel;
|
||||
import io.bitsquare.gui.common.view.CachingViewLoader;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.common.view.View;
|
||||
import io.bitsquare.gui.common.view.ViewLoader;
|
||||
import io.bitsquare.gui.common.view.ViewPath;
|
||||
import io.bitsquare.gui.common.view.Wizard;
|
||||
import io.bitsquare.gui.main.MainView;
|
||||
import io.bitsquare.gui.main.account.AccountView;
|
||||
import io.bitsquare.gui.main.account.content.changepassword.ChangePasswordView;
|
||||
import io.bitsquare.gui.main.account.content.fiat.FiatAccountView;
|
||||
import io.bitsquare.gui.main.account.content.registration.RegistrationView;
|
||||
import io.bitsquare.gui.main.account.content.restrictions.RestrictionsView;
|
||||
import io.bitsquare.gui.main.account.content.seedwords.SeedWordsView;
|
||||
import io.bitsquare.gui.util.Colors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.paint.*;
|
||||
|
||||
import de.jensd.fx.fontawesome.AwesomeDude;
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
import io.bitsquare.gui.Navigation;
|
||||
import io.bitsquare.gui.common.view.*;
|
||||
import io.bitsquare.gui.main.MainView;
|
||||
import io.bitsquare.gui.main.account.AccountView;
|
||||
import io.bitsquare.gui.main.account.content.arbitratorselection.ArbitratorSelectionView;
|
||||
import io.bitsquare.gui.main.account.content.backup.BackupView;
|
||||
import io.bitsquare.gui.main.account.content.password.PasswordView;
|
||||
import io.bitsquare.gui.main.account.content.paymentsaccount.PaymentAccountView;
|
||||
import io.bitsquare.gui.main.account.content.seedwords.SeedWordsView;
|
||||
import io.bitsquare.gui.util.Colors;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ToggleButton;
|
||||
import javafx.scene.control.ToggleGroup;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.paint.Paint;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@FxmlView
|
||||
public class AccountSettingsView extends ActivatableViewAndModel {
|
||||
|
@ -51,7 +47,8 @@ public class AccountSettingsView extends ActivatableViewAndModel {
|
|||
private final ViewLoader viewLoader;
|
||||
private final Navigation navigation;
|
||||
|
||||
private MenuItem seedWords, password, restrictions, fiatAccount, registration;
|
||||
// private MenuItem registration;
|
||||
private MenuItem password, seedWords, backup, paymentAccount, arbitratorSelection;
|
||||
private Navigation.Listener listener;
|
||||
|
||||
@FXML private VBox leftVBox;
|
||||
|
@ -73,21 +70,22 @@ public class AccountSettingsView extends ActivatableViewAndModel {
|
|||
};
|
||||
|
||||
ToggleGroup toggleGroup = new ToggleGroup();
|
||||
seedWords = new MenuItem(navigation, toggleGroup, "Wallet seed", SeedWordsView.class);
|
||||
password = new MenuItem(navigation, toggleGroup, "Wallet password", ChangePasswordView.class);
|
||||
restrictions = new MenuItem(navigation, toggleGroup, "Arbitrator selection", RestrictionsView.class);
|
||||
fiatAccount = new MenuItem(navigation, toggleGroup, "Payments account(s)", FiatAccountView.class);
|
||||
registration = new MenuItem(navigation, toggleGroup, "Renew your account", RegistrationView.class);
|
||||
password = new MenuItem(navigation, toggleGroup, "Wallet password", PasswordView.class, AwesomeIcon.UNLOCK_ALT);
|
||||
seedWords = new MenuItem(navigation, toggleGroup, "Wallet seed", SeedWordsView.class, AwesomeIcon.KEY);
|
||||
backup = new MenuItem(navigation, toggleGroup, "Backup", BackupView.class, AwesomeIcon.CLOUD_DOWNLOAD);
|
||||
paymentAccount = new MenuItem(navigation, toggleGroup, "Payments account(s)", PaymentAccountView.class, AwesomeIcon.MONEY);
|
||||
arbitratorSelection = new MenuItem(navigation, toggleGroup, "Arbitrator selection", ArbitratorSelectionView.class, AwesomeIcon.USER_MD);
|
||||
// registration = new MenuItem(navigation, toggleGroup, "Renew your account", RegistrationView.class, AwesomeIcon.BRIEFCASE);
|
||||
|
||||
leftVBox.getChildren().addAll(seedWords, password, restrictions, fiatAccount, registration);
|
||||
leftVBox.getChildren().addAll(password, seedWords, backup, paymentAccount, arbitratorSelection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doActivate() {
|
||||
protected void activate() {
|
||||
navigation.addListener(listener);
|
||||
ViewPath viewPath = navigation.getCurrentPath();
|
||||
if (viewPath.size() == 3 && viewPath.indexOf(AccountSettingsView.class) == 2) {
|
||||
navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class, SeedWordsView.class);
|
||||
navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class, PasswordView.class);
|
||||
}
|
||||
else if (viewPath.size() == 4 && viewPath.indexOf(AccountSettingsView.class) == 2) {
|
||||
loadView(viewPath.get(3));
|
||||
|
@ -95,28 +93,37 @@ public class AccountSettingsView extends ActivatableViewAndModel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void doDeactivate() {
|
||||
protected void deactivate() {
|
||||
navigation.removeListener(listener);
|
||||
}
|
||||
|
||||
private void loadView(Class<? extends View> viewClass) {
|
||||
/* if (viewClass.equals(PaymentAccountView.class)) {
|
||||
PaymentAccountView view = new PaymentAccountView();
|
||||
content.getChildren().setAll(view.getRoot());
|
||||
paymentAccount.setSelected(true);
|
||||
}
|
||||
else {*/
|
||||
View view = viewLoader.load(viewClass);
|
||||
content.getChildren().setAll(view.getRoot());
|
||||
if (view instanceof Wizard.Step)
|
||||
((Wizard.Step) view).hideWizardNavigation();
|
||||
|
||||
if (view instanceof SeedWordsView) seedWords.setSelected(true);
|
||||
else if (view instanceof ChangePasswordView) password.setSelected(true);
|
||||
else if (view instanceof RestrictionsView) restrictions.setSelected(true);
|
||||
else if (view instanceof FiatAccountView) fiatAccount.setSelected(true);
|
||||
else if (view instanceof RegistrationView) registration.setSelected(true);
|
||||
|
||||
if (view instanceof PasswordView) password.setSelected(true);
|
||||
else if (view instanceof SeedWordsView) seedWords.setSelected(true);
|
||||
else if (view instanceof BackupView) backup.setSelected(true);
|
||||
else if (view instanceof PaymentAccountView) paymentAccount.setSelected(true);
|
||||
else if (view instanceof ArbitratorSelectionView) arbitratorSelection.setSelected(true);
|
||||
// else if (view instanceof RegistrationView) registration.setSelected(true);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MenuItem extends ToggleButton {
|
||||
|
||||
MenuItem(Navigation navigation, ToggleGroup toggleGroup, String title, Class<? extends View> viewClass) {
|
||||
MenuItem(Navigation navigation, ToggleGroup toggleGroup, String title, Class<? extends View> viewClass, AwesomeIcon awesomeIcon) {
|
||||
|
||||
setToggleGroup(toggleGroup);
|
||||
setText(title);
|
||||
|
@ -126,14 +133,12 @@ class MenuItem extends ToggleButton {
|
|||
setAlignment(Pos.CENTER_LEFT);
|
||||
|
||||
Label icon = new Label();
|
||||
icon.setTextFill(Paint.valueOf("#999"));
|
||||
if (viewClass == SeedWordsView.class)
|
||||
AwesomeDude.setIcon(icon, AwesomeIcon.INFO_SIGN);
|
||||
else if (viewClass == RegistrationView.class)
|
||||
AwesomeDude.setIcon(icon, AwesomeIcon.BRIEFCASE);
|
||||
else
|
||||
AwesomeDude.setIcon(icon, AwesomeIcon.EDIT_SIGN);
|
||||
|
||||
AwesomeDude.setIcon(icon, awesomeIcon);
|
||||
icon.setTextFill(Paint.valueOf("#333"));
|
||||
icon.setPadding(new Insets(0, 5, 0, 0));
|
||||
icon.setAlignment(Pos.CENTER);
|
||||
icon.setMinWidth(25);
|
||||
icon.setMaxWidth(25);
|
||||
setGraphic(icon);
|
||||
|
||||
setOnAction((event) ->
|
||||
|
@ -146,7 +151,7 @@ class MenuItem extends ToggleButton {
|
|||
}
|
||||
else {
|
||||
setId("account-settings-item-background-active");
|
||||
icon.setTextFill(Paint.valueOf("#999"));
|
||||
icon.setTextFill(Paint.valueOf("#333"));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -157,7 +162,7 @@ class MenuItem extends ToggleButton {
|
|||
}
|
||||
else {
|
||||
setId("account-settings-item-background-active");
|
||||
icon.setTextFill(Paint.valueOf("#999"));
|
||||
icon.setTextFill(Paint.valueOf("#333"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -18,27 +18,22 @@
|
|||
package io.bitsquare.gui.main.account.setup;
|
||||
|
||||
import io.bitsquare.gui.Navigation;
|
||||
import io.bitsquare.gui.common.view.ActivatableView;
|
||||
import io.bitsquare.gui.common.view.CachingViewLoader;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.common.view.View;
|
||||
import io.bitsquare.gui.common.view.ViewLoader;
|
||||
import io.bitsquare.gui.common.view.Wizard;
|
||||
import io.bitsquare.gui.common.view.*;
|
||||
import io.bitsquare.gui.main.MainView;
|
||||
import io.bitsquare.gui.main.account.content.fiat.FiatAccountView;
|
||||
import io.bitsquare.gui.main.account.content.arbitratorselection.ArbitratorSelectionView;
|
||||
import io.bitsquare.gui.main.account.content.password.PasswordView;
|
||||
import io.bitsquare.gui.main.account.content.registration.RegistrationView;
|
||||
import io.bitsquare.gui.main.account.content.restrictions.RestrictionsView;
|
||||
import io.bitsquare.gui.main.account.content.paymentsaccount.PaymentAccountView;
|
||||
import io.bitsquare.gui.main.account.content.seedwords.SeedWordsView;
|
||||
import io.bitsquare.gui.main.offer.BuyOfferView;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.image.*;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@FxmlView
|
||||
public class AccountSetupWizard extends ActivatableView implements Wizard {
|
||||
|
@ -46,7 +41,7 @@ public class AccountSetupWizard extends ActivatableView implements Wizard {
|
|||
@FXML VBox leftVBox;
|
||||
@FXML AnchorPane content;
|
||||
|
||||
private WizardItem seedWords, password, fiatAccount, restrictions, registration;
|
||||
private WizardItem seedWords, password, fiatAccount, restrictions;
|
||||
private Navigation.Listener listener;
|
||||
|
||||
private final ViewLoader viewLoader;
|
||||
|
@ -72,49 +67,38 @@ public class AccountSetupWizard extends ActivatableView implements Wizard {
|
|||
else if (viewClass == PasswordView.class) {
|
||||
seedWords.onCompleted();
|
||||
password.show();
|
||||
}
|
||||
else if (viewClass == RestrictionsView.class) {
|
||||
} else if (viewClass == ArbitratorSelectionView.class) {
|
||||
seedWords.onCompleted();
|
||||
password.onCompleted();
|
||||
restrictions.show();
|
||||
}
|
||||
else if (viewClass == FiatAccountView.class) {
|
||||
} else if (viewClass == PaymentAccountView.class) {
|
||||
seedWords.onCompleted();
|
||||
password.onCompleted();
|
||||
restrictions.onCompleted();
|
||||
fiatAccount.show();
|
||||
}
|
||||
else if (viewClass == RegistrationView.class) {
|
||||
seedWords.onCompleted();
|
||||
password.onCompleted();
|
||||
restrictions.onCompleted();
|
||||
fiatAccount.onCompleted();
|
||||
registration.show();
|
||||
}
|
||||
};
|
||||
|
||||
seedWords = new WizardItem(SeedWordsView.class,
|
||||
"Backup wallet seed", "Write down the seed word for your wallet");
|
||||
password = new WizardItem(PasswordView.class,
|
||||
"Setup password", "Protect your wallet with a password");
|
||||
restrictions = new WizardItem(RestrictionsView.class,
|
||||
restrictions = new WizardItem(ArbitratorSelectionView.class,
|
||||
"Select arbitrators", "Select which arbitrators you want to use for trading");
|
||||
fiatAccount = new WizardItem(FiatAccountView.class,
|
||||
fiatAccount = new WizardItem(PaymentAccountView.class,
|
||||
" Setup Payments account(s)", "You need to setup at least one payment account");
|
||||
registration = new WizardItem(RegistrationView.class,
|
||||
"Register your account", "The registration in the Blockchain requires a payment of 0.0002 BTC");
|
||||
|
||||
leftVBox.getChildren().addAll(seedWords, password, restrictions, fiatAccount, registration);
|
||||
leftVBox.getChildren().addAll(seedWords, password, restrictions, fiatAccount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activate() {
|
||||
protected void activate() {
|
||||
navigation.addListener(listener);
|
||||
seedWords.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
protected void deactivate() {
|
||||
navigation.removeListener(listener);
|
||||
}
|
||||
|
||||
|
@ -127,17 +111,11 @@ public class AccountSetupWizard extends ActivatableView implements Wizard {
|
|||
else if (currentStep instanceof PasswordView) {
|
||||
password.onCompleted();
|
||||
restrictions.show();
|
||||
}
|
||||
else if (currentStep instanceof RestrictionsView) {
|
||||
} else if (currentStep instanceof ArbitratorSelectionView) {
|
||||
restrictions.onCompleted();
|
||||
fiatAccount.show();
|
||||
}
|
||||
else if (currentStep instanceof FiatAccountView) {
|
||||
} else if (currentStep instanceof PaymentAccountView) {
|
||||
fiatAccount.onCompleted();
|
||||
registration.show();
|
||||
}
|
||||
else if (currentStep instanceof RegistrationView) {
|
||||
registration.onCompleted();
|
||||
|
||||
if (navigation.getReturnPath() != null)
|
||||
navigation.navigateTo(navigation.getReturnPath());
|
||||
|
@ -146,7 +124,7 @@ public class AccountSetupWizard extends ActivatableView implements Wizard {
|
|||
}
|
||||
}
|
||||
|
||||
protected void loadView(Class<? extends View> viewClass) {
|
||||
private void loadView(Class<? extends View> viewClass) {
|
||||
View view = viewLoader.load(viewClass);
|
||||
content.getChildren().setAll(view.getRoot());
|
||||
if (view instanceof Wizard.Step)
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
|
||||
<?import io.bitsquare.gui.components.TitledGroupBg?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.control.ComboBox?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<GridPane fx:id="root" fx:controller="io.bitsquare.gui.main.debug.DebugView"
|
||||
hgap="5.0" vgap="5.0"
|
||||
|
@ -38,7 +39,15 @@
|
|||
<Insets top="10"/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<ComboBox fx:id="taskComboBox" onAction="#onSelectTask" promptText="Select task" GridPane.rowIndex="0" GridPane.columnIndex="1">
|
||||
<ComboBox fx:id="taskComboBox1" onAction="#onSelectTask1" promptText="Select task" GridPane.rowIndex="0"
|
||||
GridPane.columnIndex="1">
|
||||
<GridPane.margin>
|
||||
<Insets top="10"/>
|
||||
</GridPane.margin>
|
||||
</ComboBox>
|
||||
|
||||
<ComboBox fx:id="taskComboBox2" onAction="#onSelectTask2" promptText="Select task" GridPane.rowIndex="1"
|
||||
GridPane.columnIndex="1">
|
||||
<GridPane.margin>
|
||||
<Insets top="10"/>
|
||||
</GridPane.margin>
|
||||
|
|
|
@ -30,53 +30,32 @@ import io.bitsquare.trade.protocol.placeoffer.tasks.BroadcastCreateOfferFeeTx;
|
|||
import io.bitsquare.trade.protocol.placeoffer.tasks.CreateOfferFeeTx;
|
||||
import io.bitsquare.trade.protocol.placeoffer.tasks.ValidateOffer;
|
||||
import io.bitsquare.trade.protocol.trade.BuyerAsOffererProtocol;
|
||||
import io.bitsquare.trade.protocol.trade.BuyerAsTakerProtocol;
|
||||
import io.bitsquare.trade.protocol.trade.SellerAsOffererProtocol;
|
||||
import io.bitsquare.trade.protocol.trade.SellerAsTakerProtocol;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.buyer.CreateDepositTxInputs;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.buyer.ProcessDepositTxInputsRequest;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.buyer.ProcessFinalizePayoutTxRequest;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.buyer.ProcessPublishDepositTxRequest;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.buyer.SendDepositTxPublishedMessage;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.buyer.SendFiatTransferStartedMessage;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.buyer.SendPayDepositRequest;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.buyer.SendPayoutTxFinalizedMessage;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.buyer.SignAndFinalizePayoutTx;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.buyer.SignAndPublishDepositTx;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.buyer.VerifyAndSignContract;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.offerer.VerifyTakeOfferFeePayment;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.offerer.VerifyTakerAccount;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.seller.CommitDepositTx;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.seller.CreateAndSignContract;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.seller.CreateAndSignDepositTx;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.seller.ProcessDepositTxPublishedMessage;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.seller.ProcessFiatTransferStartedMessage;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.seller.ProcessPayDepositRequest;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.seller.ProcessPayoutTxFinalizedMessage;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.seller.SendDepositTxInputsRequest;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.seller.SendFinalizePayoutTxRequest;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.seller.SendPublishDepositTxRequest;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.seller.SignPayoutTx;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.buyer.*;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.offerer.*;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.seller.*;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.shared.CommitPayoutTx;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.shared.InitWaitPeriodForOpenDispute;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.shared.SetupPayoutTxLockTimeReachedListener;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.taker.BroadcastTakeOfferFeeTx;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.taker.CreateTakeOfferFeeTx;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.taker.VerifyOfferFeePayment;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.taker.VerifyOffererAccount;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import io.bitsquare.trade.protocol.trade.tasks.shared.SignPayoutTx;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.taker.*;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Arrays;
|
||||
|
||||
@FxmlView
|
||||
public class DebugView extends InitializableView {
|
||||
|
||||
|
||||
@FXML ComboBox<Class> taskComboBox;
|
||||
@FXML
|
||||
ComboBox<Class> taskComboBox1, taskComboBox2;
|
||||
|
||||
@Inject
|
||||
public DebugView() {
|
||||
|
@ -84,7 +63,7 @@ public class DebugView extends InitializableView {
|
|||
|
||||
@Override
|
||||
public void initialize() {
|
||||
final ObservableList<Class> items = FXCollections.observableArrayList(Arrays.asList(
|
||||
final ObservableList<Class> items1 = FXCollections.observableArrayList(Arrays.asList(
|
||||
/*---- Protocol ----*/
|
||||
OfferAvailabilityProtocol.class,
|
||||
GetPeerAddress.class,
|
||||
|
@ -104,15 +83,18 @@ public class DebugView extends InitializableView {
|
|||
|
||||
/*---- Protocol ----*/
|
||||
BuyerAsOffererProtocol.class,
|
||||
ProcessDepositTxInputsRequest.class,
|
||||
CreateDepositTxInputs.class,
|
||||
SendPayDepositRequest.class,
|
||||
|
||||
ProcessPublishDepositTxRequest.class,
|
||||
ProcessPayDepositRequest.class,
|
||||
VerifyArbitrationSelection.class,
|
||||
VerifyTakerAccount.class,
|
||||
VerifyAndSignContract.class,
|
||||
SignAndPublishDepositTx.class,
|
||||
SendDepositTxPublishedMessage.class,
|
||||
CreateAndSignContract.class,
|
||||
CreateAndSignDepositTxAsBuyer.class,
|
||||
LoadTakeOfferFeeTx.class,
|
||||
InitWaitPeriodForOpenDispute.class,
|
||||
SetupDepositBalanceListener.class,
|
||||
SendPublishDepositTxRequest.class,
|
||||
|
||||
ProcessDepositTxPublishedMessage.class,
|
||||
AddDepositTxToWallet.class,
|
||||
|
||||
VerifyTakeOfferFeePayment.class,
|
||||
SendFiatTransferStartedMessage.class,
|
||||
|
@ -127,18 +109,17 @@ public class DebugView extends InitializableView {
|
|||
|
||||
/*---- Protocol ----*/
|
||||
SellerAsTakerProtocol.class,
|
||||
SelectArbitrator.class,
|
||||
CreateTakeOfferFeeTx.class,
|
||||
BroadcastTakeOfferFeeTx.class,
|
||||
SendDepositTxInputsRequest.class,
|
||||
CreateDepositTxInputsAsSeller.class,
|
||||
SendPayDepositRequest.class,
|
||||
|
||||
ProcessPayDepositRequest.class,
|
||||
ProcessPublishDepositTxRequest.class,
|
||||
VerifyOffererAccount.class,
|
||||
CreateAndSignContract.class,
|
||||
CreateAndSignDepositTx.class,
|
||||
SendPublishDepositTxRequest.class,
|
||||
|
||||
ProcessDepositTxPublishedMessage.class,
|
||||
CommitDepositTx.class,
|
||||
VerifyAndSignContract.class,
|
||||
SignAndPublishDepositTxAsSeller.class,
|
||||
SendDepositTxPublishedMessage.class,
|
||||
|
||||
ProcessFiatTransferStartedMessage.class,
|
||||
|
||||
|
@ -148,14 +129,87 @@ public class DebugView extends InitializableView {
|
|||
|
||||
ProcessPayoutTxFinalizedMessage.class,
|
||||
CommitPayoutTx.class,
|
||||
SetupPayoutTxLockTimeReachedListener.class
|
||||
SetupPayoutTxLockTimeReachedListener.class,
|
||||
Boolean.class /* used as seperator*/
|
||||
)
|
||||
);
|
||||
final ObservableList<Class> items2 = FXCollections.observableArrayList(Arrays.asList(
|
||||
/*---- Protocol ----*/
|
||||
BuyerAsTakerProtocol.class,
|
||||
SelectArbitrator.class,
|
||||
CreateTakeOfferFeeTx.class,
|
||||
BroadcastTakeOfferFeeTx.class,
|
||||
CreateDepositTxInputsAsSeller.class,
|
||||
SendPayDepositRequest.class,
|
||||
|
||||
ProcessPublishDepositTxRequest.class,
|
||||
VerifyOffererAccount.class,
|
||||
VerifyAndSignContract.class,
|
||||
SignAndPublishDepositTxAsSeller.class,
|
||||
SendDepositTxPublishedMessage.class,
|
||||
|
||||
VerifyOfferFeePayment.class,
|
||||
SignPayoutTx.class,
|
||||
SendFiatTransferStartedMessage.class,
|
||||
|
||||
ProcessFinalizePayoutTxRequest.class,
|
||||
SignAndFinalizePayoutTx.class,
|
||||
CommitPayoutTx.class,
|
||||
SendPayoutTxFinalizedMessage.class,
|
||||
SetupPayoutTxLockTimeReachedListener.class,
|
||||
Boolean.class, /* used as seperator*/
|
||||
|
||||
|
||||
/*---- Protocol ----*/
|
||||
SellerAsOffererProtocol.class,
|
||||
ProcessPayDepositRequest.class,
|
||||
VerifyArbitrationSelection.class,
|
||||
VerifyTakerAccount.class,
|
||||
InitWaitPeriodForOpenDispute.class,
|
||||
CreateAndSignContract.class,
|
||||
CreateAndSignDepositTxAsBuyer.class,
|
||||
SetupDepositBalanceListener.class,
|
||||
SendPublishDepositTxRequest.class,
|
||||
|
||||
ProcessDepositTxPublishedMessage.class,
|
||||
AddDepositTxToWallet.class,
|
||||
|
||||
ProcessFiatTransferStartedMessage.class,
|
||||
|
||||
VerifyTakeOfferFeePayment.class,
|
||||
SignPayoutTx.class,
|
||||
SendFinalizePayoutTxRequest.class,
|
||||
|
||||
ProcessPayoutTxFinalizedMessage.class,
|
||||
CommitPayoutTx.class,
|
||||
SetupPayoutTxLockTimeReachedListener.class,
|
||||
Boolean.class /* used as seperator*/
|
||||
)
|
||||
);
|
||||
|
||||
taskComboBox1.setVisibleRowCount(items1.size());
|
||||
taskComboBox1.setItems(items1);
|
||||
taskComboBox1.setConverter(new StringConverter<Class>() {
|
||||
@Override
|
||||
public String toString(Class item) {
|
||||
if (item.getSimpleName().contains("Protocol"))
|
||||
return "--- " + item.getSimpleName() + " ---";
|
||||
else if (item.getSimpleName().contains("Boolean"))
|
||||
return "";
|
||||
else
|
||||
return item.getSimpleName();
|
||||
}
|
||||
|
||||
taskComboBox.setVisibleRowCount(items.size());
|
||||
taskComboBox.setItems(items);
|
||||
taskComboBox.setConverter(new StringConverter<Class>() {
|
||||
@Override
|
||||
public Class fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
taskComboBox2.setVisibleRowCount(items2.size());
|
||||
taskComboBox2.setItems(items2);
|
||||
taskComboBox2.setConverter(new StringConverter<Class>() {
|
||||
@Override
|
||||
public String toString(Class item) {
|
||||
if (item.getSimpleName().contains("Protocol"))
|
||||
|
@ -174,11 +228,21 @@ public class DebugView extends InitializableView {
|
|||
}
|
||||
|
||||
@FXML
|
||||
void onSelectTask() {
|
||||
Class item = taskComboBox.getSelectionModel().getSelectedItem();
|
||||
void onSelectTask1() {
|
||||
Class item = taskComboBox1.getSelectionModel().getSelectedItem();
|
||||
if (!item.getSimpleName().contains("Protocol")) {
|
||||
Task.taskToIntercept = item;
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onSelectTask2() {
|
||||
Class item = taskComboBox2.getSelectionModel().getSelectedItem();
|
||||
if (!item.getSimpleName().contains("Protocol")) {
|
||||
Task.taskToIntercept = item;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -16,14 +16,15 @@
|
|||
~ along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.control.Tab?>
|
||||
<?import javafx.scene.control.TabPane?>
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
<TabPane fx:id="root" fx:controller="io.bitsquare.gui.main.funds.FundsView"
|
||||
AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0"
|
||||
AnchorPane.topAnchor="0.0"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
|
||||
<Tab fx:id="reservedTab" text="Reserved for trades" closable="false"/>
|
||||
<Tab fx:id="reservedTab" text="Reserved for trades" closable="false"></Tab>
|
||||
<Tab fx:id="withdrawalTab" text="Open for withdrawal" closable="false"/>
|
||||
<Tab fx:id="transactionsTab" text="Transactions" closable="false"/>
|
||||
|
||||
|
|
|
@ -17,23 +17,24 @@
|
|||
|
||||
package io.bitsquare.gui.main.funds;
|
||||
|
||||
import io.bitsquare.app.BitsquareApp;
|
||||
import io.bitsquare.gui.Navigation;
|
||||
import io.bitsquare.gui.common.model.Activatable;
|
||||
import io.bitsquare.gui.common.view.ActivatableViewAndModel;
|
||||
import io.bitsquare.gui.common.view.CachingViewLoader;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.common.view.View;
|
||||
import io.bitsquare.gui.common.view.ViewLoader;
|
||||
import io.bitsquare.gui.common.view.*;
|
||||
import io.bitsquare.gui.main.MainView;
|
||||
import io.bitsquare.gui.main.funds.reserved.ReservedView;
|
||||
import io.bitsquare.gui.main.funds.transactions.TransactionsView;
|
||||
import io.bitsquare.gui.main.funds.withdrawal.WithdrawalView;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import io.bitsquare.gui.popups.FirstTimePopup;
|
||||
import io.bitsquare.gui.popups.WebViewPopup;
|
||||
import io.bitsquare.user.PopupId;
|
||||
import io.bitsquare.user.Preferences;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.control.Tab;
|
||||
import javafx.scene.control.TabPane;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@FxmlView
|
||||
public class FundsView extends ActivatableViewAndModel<TabPane, Activatable> {
|
||||
|
@ -46,11 +47,13 @@ public class FundsView extends ActivatableViewAndModel<TabPane, Activatable> {
|
|||
|
||||
private final ViewLoader viewLoader;
|
||||
private final Navigation navigation;
|
||||
private Preferences preferences;
|
||||
|
||||
@Inject
|
||||
public FundsView(CachingViewLoader viewLoader, Navigation navigation) {
|
||||
public FundsView(CachingViewLoader viewLoader, Navigation navigation, Preferences preferences) {
|
||||
this.viewLoader = viewLoader;
|
||||
this.navigation = navigation;
|
||||
this.preferences = preferences;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -71,7 +74,7 @@ public class FundsView extends ActivatableViewAndModel<TabPane, Activatable> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void doActivate() {
|
||||
protected void activate() {
|
||||
root.getSelectionModel().selectedItemProperty().addListener(tabChangeListener);
|
||||
navigation.addListener(navigationListener);
|
||||
|
||||
|
@ -81,10 +84,13 @@ public class FundsView extends ActivatableViewAndModel<TabPane, Activatable> {
|
|||
navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class);
|
||||
else if (root.getSelectionModel().getSelectedItem() == transactionsTab)
|
||||
navigation.navigateTo(MainView.class, FundsView.class, TransactionsView.class);
|
||||
|
||||
if (preferences.getShowAgainMap().get(PopupId.TRADE_WALLET) && !BitsquareApp.DEV_MODE)
|
||||
new FirstTimePopup(preferences).url(WebViewPopup.getLocalUrl("tradeWallet.html")).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doDeactivate() {
|
||||
protected void deactivate() {
|
||||
root.getSelectionModel().selectedItemProperty().removeListener(tabChangeListener);
|
||||
navigation.removeListener(navigationListener);
|
||||
currentTab = null;
|
||||
|
|
|
@ -23,23 +23,24 @@ import io.bitsquare.btc.listeners.AddressConfidenceListener;
|
|||
import io.bitsquare.btc.listeners.BalanceListener;
|
||||
import io.bitsquare.gui.components.confidence.ConfidenceProgressIndicator;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
|
||||
import io.bitsquare.trade.Tradable;
|
||||
import io.bitsquare.trade.Trade;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.TransactionConfidence;
|
||||
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.scene.control.*;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ReservedListItem {
|
||||
private final StringProperty addressString = new SimpleStringProperty();
|
||||
private final Logger log = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
private final BalanceListener balanceListener;
|
||||
|
||||
private final Label balanceLabel;
|
||||
|
||||
private final Tradable tradable;
|
||||
private final AddressEntry addressEntry;
|
||||
|
||||
private final WalletService walletService;
|
||||
|
@ -49,14 +50,15 @@ public class ReservedListItem {
|
|||
private final ConfidenceProgressIndicator progressIndicator;
|
||||
|
||||
private final Tooltip tooltip;
|
||||
|
||||
private final String addressString;
|
||||
private Coin balance;
|
||||
|
||||
public ReservedListItem(AddressEntry addressEntry, WalletService walletService, BSFormatter formatter) {
|
||||
public ReservedListItem(Tradable tradable, AddressEntry addressEntry, WalletService walletService, BSFormatter formatter) {
|
||||
this.tradable = tradable;
|
||||
this.addressEntry = addressEntry;
|
||||
this.walletService = walletService;
|
||||
this.formatter = formatter;
|
||||
this.addressString.set(getAddress().toString());
|
||||
addressString = addressEntry.getAddressString();
|
||||
|
||||
// confidence
|
||||
progressIndicator = new ConfidenceProgressIndicator();
|
||||
|
@ -96,7 +98,34 @@ public class ReservedListItem {
|
|||
private void updateBalance(Coin balance) {
|
||||
this.balance = balance;
|
||||
if (balance != null) {
|
||||
balanceLabel.setText(formatter.formatCoin(balance));
|
||||
if (tradable instanceof Trade) {
|
||||
Trade trade = (Trade) tradable;
|
||||
Trade.Phase phase = trade.getState().getPhase();
|
||||
switch (phase) {
|
||||
case PREPARATION:
|
||||
case TAKER_FEE_PAID:
|
||||
balanceLabel.setText(formatter.formatCoinWithCode(balance) + " locked in deposit");
|
||||
break;
|
||||
case DEPOSIT_PAID:
|
||||
case FIAT_SENT:
|
||||
case FIAT_RECEIVED:
|
||||
// TODO show amount locked
|
||||
balanceLabel.setText("Locked in deposit");
|
||||
break;
|
||||
case PAYOUT_PAID:
|
||||
balanceLabel.setText(formatter.formatCoinWithCode(balance) + " in wallet");
|
||||
break;
|
||||
case WITHDRAWN:
|
||||
log.error("Invalid state at updateBalance (WITHDRAWN)");
|
||||
balanceLabel.setText(formatter.formatCoinWithCode(balance) + " already withdrawn");
|
||||
break;
|
||||
case DISPUTE:
|
||||
balanceLabel.setText(formatter.formatCoinWithCode(balance) + " locked because of open ticket");
|
||||
break;
|
||||
}
|
||||
|
||||
} else
|
||||
balanceLabel.setText(formatter.formatCoin(balance));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,23 +157,18 @@ public class ReservedListItem {
|
|||
|
||||
public final String getLabel() {
|
||||
switch (addressEntry.getContext()) {
|
||||
case REGISTRATION_FEE:
|
||||
return "Registration fee";
|
||||
case TRADE:
|
||||
checkNotNull(addressEntry.getOfferId());
|
||||
return "Offer ID: " + addressEntry.getOfferId();
|
||||
case ARBITRATOR_DEPOSIT:
|
||||
if (tradable instanceof Trade)
|
||||
return "Trade ID: " + addressEntry.getShortOfferId();
|
||||
else
|
||||
return "Offer ID: " + addressEntry.getShortOfferId();
|
||||
case ARBITRATOR:
|
||||
return "Arbitration deposit";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
public final StringProperty addressStringProperty() {
|
||||
return this.addressString;
|
||||
}
|
||||
|
||||
Address getAddress() {
|
||||
private Address getAddress() {
|
||||
return addressEntry.getAddress();
|
||||
}
|
||||
|
||||
|
@ -167,4 +191,8 @@ public class ReservedListItem {
|
|||
public Coin getBalance() {
|
||||
return balance;
|
||||
}
|
||||
|
||||
public String getAddressString() {
|
||||
return addressString;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,26 +18,20 @@
|
|||
-->
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.cell.*?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<VBox fx:id="root" fx:controller="io.bitsquare.gui.main.funds.reserved.ReservedView"
|
||||
spacing="10" xmlns:fx="http://javafx.com/fxml">
|
||||
<padding>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
|
||||
<Insets bottom="0.0" left="10.0" right="10.0" top="10.0"/>
|
||||
</padding>
|
||||
|
||||
<TableView fx:id="table" VBox.vgrow="ALWAYS">
|
||||
<columns>
|
||||
<TableColumn text="Label" fx:id="labelColumn" minWidth="100" sortable="false"/>
|
||||
<TableColumn text="Address" fx:id="addressColumn" minWidth="240" sortable="false">
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="addressString"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
<TableColumn text="Address" fx:id="addressColumn" minWidth="240" sortable="false"/>
|
||||
<TableColumn text="Balance" fx:id="balanceColumn" minWidth="50" sortable="false"/>
|
||||
<TableColumn text="Copy" fx:id="copyColumn" minWidth="30" sortable="false"/>
|
||||
<TableColumn text="Status" fx:id="confidenceColumn" minWidth="30" sortable="false"/>
|
||||
<TableColumn text="Confirmations" fx:id="confidenceColumn" minWidth="30" sortable="false"/>
|
||||
</columns>
|
||||
</TableView>
|
||||
|
||||
|
|
|
@ -19,49 +19,57 @@ package io.bitsquare.gui.main.funds.reserved;
|
|||
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.btc.listeners.BalanceListener;
|
||||
import io.bitsquare.gui.common.view.ActivatableViewAndModel;
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
import io.bitsquare.gui.common.view.ActivatableView;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.popups.OfferDetailsPopup;
|
||||
import io.bitsquare.gui.popups.Popup;
|
||||
import io.bitsquare.gui.popups.TradeDetailsPopup;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.gui.util.GUIUtil;
|
||||
import io.bitsquare.trade.Trade;
|
||||
import io.bitsquare.trade.TradeManager;
|
||||
import io.bitsquare.trade.offer.OpenOffer;
|
||||
import io.bitsquare.trade.offer.OpenOfferManager;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import io.bitsquare.user.Preferences;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.util.Callback;
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import de.jensd.fx.fontawesome.AwesomeDude;
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
import javax.inject.Inject;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@FxmlView
|
||||
public class ReservedView extends ActivatableViewAndModel {
|
||||
|
||||
public class ReservedView extends ActivatableView<VBox, Void> {
|
||||
@FXML TableView<ReservedListItem> table;
|
||||
@FXML TableColumn<ReservedListItem, ReservedListItem> labelColumn, addressColumn, balanceColumn, copyColumn,
|
||||
confidenceColumn;
|
||||
@FXML
|
||||
TableColumn<ReservedListItem, ReservedListItem> labelColumn, addressColumn, balanceColumn, confidenceColumn;
|
||||
|
||||
private final WalletService walletService;
|
||||
private final TradeManager tradeManager;
|
||||
private OpenOfferManager openOfferManager;
|
||||
private final OpenOfferManager openOfferManager;
|
||||
private final Preferences preferences;
|
||||
private final BSFormatter formatter;
|
||||
private final OfferDetailsPopup offerDetailsPopup;
|
||||
private final TradeDetailsPopup tradeDetailsPopup;
|
||||
private final ObservableList<ReservedListItem> addressList = FXCollections.observableArrayList();
|
||||
|
||||
@Inject
|
||||
private ReservedView(WalletService walletService, TradeManager tradeManager, OpenOfferManager openOfferManager, BSFormatter formatter) {
|
||||
private ReservedView(WalletService walletService, TradeManager tradeManager, OpenOfferManager openOfferManager, Preferences preferences,
|
||||
BSFormatter formatter, OfferDetailsPopup offerDetailsPopup, TradeDetailsPopup tradeDetailsPopup) {
|
||||
this.walletService = walletService;
|
||||
this.tradeManager = tradeManager;
|
||||
this.openOfferManager = openOfferManager;
|
||||
this.preferences = preferences;
|
||||
this.formatter = formatter;
|
||||
this.offerDetailsPopup = offerDetailsPopup;
|
||||
this.tradeDetailsPopup = tradeDetailsPopup;
|
||||
}
|
||||
|
||||
|
||||
|
@ -71,13 +79,13 @@ public class ReservedView extends ActivatableViewAndModel {
|
|||
table.setPlaceholder(new Label("No funded are reserved in open offers or trades"));
|
||||
|
||||
setLabelColumnCellFactory();
|
||||
setAddressColumnCellFactory();
|
||||
setBalanceColumnCellFactory();
|
||||
setCopyColumnCellFactory();
|
||||
setConfidenceColumnCellFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doActivate() {
|
||||
protected void activate() {
|
||||
fillList();
|
||||
table.setItems(addressList);
|
||||
|
||||
|
@ -90,22 +98,27 @@ public class ReservedView extends ActivatableViewAndModel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void doDeactivate() {
|
||||
protected void deactivate() {
|
||||
addressList.forEach(ReservedListItem::cleanup);
|
||||
}
|
||||
|
||||
|
||||
private void fillList() {
|
||||
addressList.forEach(ReservedListItem::cleanup);
|
||||
addressList.clear();
|
||||
addressList.addAll(Stream.concat(openOfferManager.getOpenOffers().stream(), tradeManager.getPendingTrades().stream())
|
||||
.map(tradable -> new ReservedListItem(walletService.getAddressEntry(tradable.getOffer().getId()), walletService, formatter))
|
||||
addressList.addAll(Stream.concat(openOfferManager.getOpenOffers().stream(), tradeManager.getTrades().stream())
|
||||
.map(tradable -> new ReservedListItem(tradable, walletService.getAddressEntryByOfferId(tradable.getOffer().getId()), walletService, formatter))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
// List<AddressEntry> addressEntryList = walletService.getAddressEntryList();
|
||||
/* addressList.addAll(addressEntryList.stream()
|
||||
.filter(e -> walletService.getBalanceForAddress(e.getAddress()).isPositive())
|
||||
.map(anAddressEntryList -> new ReservedListItem(anAddressEntryList, walletService, formatter))
|
||||
.collect(Collectors.toList()));*/
|
||||
private void openBlockExplorer(ReservedListItem item) {
|
||||
try {
|
||||
Utilities.openWebPage(preferences.getBlockChainExplorer().addressUrl + item.getAddressString());
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
new Popup().warning("Opening browser failed. Please check your internet " +
|
||||
"connection.").show();
|
||||
}
|
||||
}
|
||||
|
||||
private void setLabelColumnCellFactory() {
|
||||
|
@ -127,13 +140,18 @@ public class ReservedView extends ActivatableViewAndModel {
|
|||
|
||||
if (item != null && !empty) {
|
||||
hyperlink = new Hyperlink(item.getLabel());
|
||||
hyperlink.setId("id-link");
|
||||
if (item.getAddressEntry().getOfferId() != null) {
|
||||
Tooltip tooltip = new Tooltip(item.getAddressEntry().getOfferId());
|
||||
Tooltip.install(hyperlink, tooltip);
|
||||
|
||||
hyperlink.setOnAction(event -> log.info("Show trade details " + item.getAddressEntry
|
||||
().getOfferId()));
|
||||
hyperlink.setOnAction(event -> {
|
||||
Optional<Trade> tradeOptional = tradeManager.getTradeById(item.getAddressEntry().getOfferId());
|
||||
Optional<OpenOffer> openOfferOptional = openOfferManager.getOpenOfferById(item.getAddressEntry().getOfferId());
|
||||
if (tradeOptional.isPresent())
|
||||
tradeDetailsPopup.show(tradeOptional.get());
|
||||
else if (openOfferOptional.isPresent())
|
||||
offerDetailsPopup.show(openOfferOptional.get().getOffer());
|
||||
});
|
||||
}
|
||||
setGraphic(hyperlink);
|
||||
}
|
||||
|
@ -147,6 +165,36 @@ public class ReservedView extends ActivatableViewAndModel {
|
|||
});
|
||||
}
|
||||
|
||||
private void setAddressColumnCellFactory() {
|
||||
addressColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
addressColumn.setCellFactory(
|
||||
new Callback<TableColumn<ReservedListItem, ReservedListItem>, TableCell<ReservedListItem,
|
||||
ReservedListItem>>() {
|
||||
|
||||
@Override
|
||||
public TableCell<ReservedListItem, ReservedListItem> call(TableColumn<ReservedListItem,
|
||||
ReservedListItem> column) {
|
||||
return new TableCell<ReservedListItem, ReservedListItem>() {
|
||||
private Hyperlink hyperlink;
|
||||
|
||||
@Override
|
||||
public void updateItem(final ReservedListItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (item != null && !empty) {
|
||||
hyperlink = new Hyperlink(item.getAddressString());
|
||||
hyperlink.setOnAction(event -> openBlockExplorer(item));
|
||||
setGraphic(hyperlink);
|
||||
} else {
|
||||
setGraphic(null);
|
||||
setId(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setBalanceColumnCellFactory() {
|
||||
balanceColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
balanceColumn.setCellFactory(
|
||||
|
@ -167,43 +215,6 @@ public class ReservedView extends ActivatableViewAndModel {
|
|||
});
|
||||
}
|
||||
|
||||
private void setCopyColumnCellFactory() {
|
||||
copyColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
copyColumn.setCellFactory(
|
||||
new Callback<TableColumn<ReservedListItem, ReservedListItem>, TableCell<ReservedListItem,
|
||||
ReservedListItem>>() {
|
||||
|
||||
@Override
|
||||
public TableCell<ReservedListItem, ReservedListItem> call(TableColumn<ReservedListItem,
|
||||
ReservedListItem> column) {
|
||||
return new TableCell<ReservedListItem, ReservedListItem>() {
|
||||
final Label copyIcon = new Label();
|
||||
|
||||
{
|
||||
copyIcon.getStyleClass().add("copy-icon");
|
||||
AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY);
|
||||
Tooltip.install(copyIcon, new Tooltip("Copy address to clipboard"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(final ReservedListItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (item != null && !empty) {
|
||||
setGraphic(copyIcon);
|
||||
copyIcon.setOnMouseClicked(e -> GUIUtil.copyToClipboard(item
|
||||
.addressStringProperty().get()));
|
||||
|
||||
}
|
||||
else {
|
||||
setGraphic(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setConfidenceColumnCellFactory() {
|
||||
confidenceColumn.setCellValueFactory((addressListItem) ->
|
||||
new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
|
|
|
@ -18,17 +18,17 @@
|
|||
-->
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.cell.*?>
|
||||
<?import javafx.scene.control.cell.PropertyValueFactory?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<VBox fx:id="root" fx:controller="io.bitsquare.gui.main.funds.transactions.TransactionsView"
|
||||
spacing="10" xmlns:fx="http://javafx.com/fxml">
|
||||
<padding>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
|
||||
<Insets bottom="0.0" left="10.0" right="10.0" top="10.0"/>
|
||||
</padding>
|
||||
<TableView fx:id="table" VBox.vgrow="ALWAYS">
|
||||
<columns>
|
||||
<TableColumn text="Date" fx:id="dateColumn" minWidth="100" sortable="false">
|
||||
<TableColumn text="Date/Time" fx:id="dateColumn" minWidth="100" sortable="false">
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="date"/>
|
||||
</cellValueFactory>
|
||||
|
@ -45,7 +45,7 @@
|
|||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
|
||||
<TableColumn text="Status" fx:id="confidenceColumn" minWidth="30" sortable="false"/>
|
||||
<TableColumn text="Confirmations" fx:id="confidenceColumn" minWidth="30" sortable="false"/>
|
||||
</columns>
|
||||
</TableView>
|
||||
</VBox>
|
||||
|
|
|
@ -18,28 +18,27 @@
|
|||
package io.bitsquare.gui.main.funds.transactions;
|
||||
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.gui.common.view.ActivatableViewAndModel;
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
import io.bitsquare.gui.common.view.ActivatableView;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.components.Popups;
|
||||
import io.bitsquare.gui.popups.Popup;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.util.Utilities;
|
||||
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import io.bitsquare.user.Preferences;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.util.Callback;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@FxmlView
|
||||
public class TransactionsView extends ActivatableViewAndModel {
|
||||
public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||
|
||||
@FXML TableView<TransactionsListItem> table;
|
||||
@FXML TableColumn<TransactionsListItem, TransactionsListItem> dateColumn, addressColumn, amountColumn, typeColumn,
|
||||
|
@ -49,11 +48,13 @@ public class TransactionsView extends ActivatableViewAndModel {
|
|||
|
||||
private final WalletService walletService;
|
||||
private final BSFormatter formatter;
|
||||
private final Preferences preferences;
|
||||
|
||||
@Inject
|
||||
private TransactionsView(WalletService walletService, BSFormatter formatter) {
|
||||
private TransactionsView(WalletService walletService, BSFormatter formatter, Preferences preferences) {
|
||||
this.walletService = walletService;
|
||||
this.formatter = formatter;
|
||||
this.preferences = preferences;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -66,7 +67,7 @@ public class TransactionsView extends ActivatableViewAndModel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void doActivate() {
|
||||
protected void activate() {
|
||||
List<Transaction> transactions = walletService.getWallet().getRecentTransactions(10000, true);
|
||||
transactionsListItems = FXCollections.observableArrayList();
|
||||
transactionsListItems.addAll(transactions.stream().map(transaction ->
|
||||
|
@ -76,7 +77,7 @@ public class TransactionsView extends ActivatableViewAndModel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void doDeactivate() {
|
||||
protected void deactivate() {
|
||||
transactionsListItems.forEach(TransactionsListItem::cleanup);
|
||||
}
|
||||
|
||||
|
@ -85,12 +86,11 @@ public class TransactionsView extends ActivatableViewAndModel {
|
|||
log.debug("openTxDetails " + item);
|
||||
|
||||
try {
|
||||
// TODO get the url form the app preferences
|
||||
Utilities.openWebPage("https://www.biteasy.com/testnet/addresses/" + item.getAddressString());
|
||||
Utilities.openWebPage(preferences.getBlockChainExplorer().addressUrl + item.getAddressString());
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
Popups.openWarningPopup("Warning", "Opening browser failed. Please check your internet " +
|
||||
"connection.");
|
||||
new Popup().warning("Opening browser failed. Please check your internet " +
|
||||
"connection.").show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,7 +112,6 @@ public class TransactionsView extends ActivatableViewAndModel {
|
|||
|
||||
if (item != null && !empty) {
|
||||
hyperlink = new Hyperlink(item.getAddressString());
|
||||
hyperlink.setId("id-link");
|
||||
hyperlink.setOnAction(event -> openTxDetails(item));
|
||||
setGraphic(hyperlink);
|
||||
}
|
||||
|
|
|
@ -23,19 +23,13 @@ import io.bitsquare.btc.listeners.AddressConfidenceListener;
|
|||
import io.bitsquare.btc.listeners.BalanceListener;
|
||||
import io.bitsquare.gui.components.confidence.ConfidenceProgressIndicator;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.TransactionConfidence;
|
||||
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.scene.control.*;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
public class WithdrawalListItem {
|
||||
private final StringProperty addressString = new SimpleStringProperty();
|
||||
private final BalanceListener balanceListener;
|
||||
|
||||
private final Label balanceLabel;
|
||||
|
@ -51,12 +45,13 @@ public class WithdrawalListItem {
|
|||
private final Tooltip tooltip;
|
||||
|
||||
private Coin balance;
|
||||
private final String addressString;
|
||||
|
||||
public WithdrawalListItem(AddressEntry addressEntry, WalletService walletService, BSFormatter formatter) {
|
||||
this.addressEntry = addressEntry;
|
||||
this.walletService = walletService;
|
||||
this.formatter = formatter;
|
||||
this.addressString.set(getAddress().toString());
|
||||
addressString = addressEntry.getAddressString();
|
||||
|
||||
// confidence
|
||||
progressIndicator = new ConfidenceProgressIndicator();
|
||||
|
@ -128,23 +123,15 @@ public class WithdrawalListItem {
|
|||
|
||||
public final String getLabel() {
|
||||
switch (addressEntry.getContext()) {
|
||||
case REGISTRATION_FEE:
|
||||
return "Registration fee";
|
||||
case TRADE:
|
||||
checkNotNull(addressEntry.getOfferId());
|
||||
return "Offer ID: " + addressEntry.getOfferId();
|
||||
case ARBITRATOR_DEPOSIT:
|
||||
return "Arbitration deposit";
|
||||
return "Offer ID: " + addressEntry.getShortOfferId();
|
||||
case ARBITRATOR:
|
||||
return "Arbitration fee";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
public final StringProperty addressStringProperty() {
|
||||
return this.addressString;
|
||||
}
|
||||
|
||||
Address getAddress() {
|
||||
private Address getAddress() {
|
||||
return addressEntry.getAddress();
|
||||
}
|
||||
|
||||
|
@ -167,4 +154,8 @@ public class WithdrawalListItem {
|
|||
public Coin getBalance() {
|
||||
return balance;
|
||||
}
|
||||
|
||||
public String getAddressString() {
|
||||
return addressString;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.control.cell.*?>
|
||||
<?import javafx.scene.control.cell.PropertyValueFactory?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<VBox fx:id="root" fx:controller="io.bitsquare.gui.main.funds.withdrawal.WithdrawalView"
|
||||
spacing="10" xmlns:fx="http://javafx.com/fxml">
|
||||
|
@ -36,8 +36,7 @@
|
|||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
<TableColumn text="Balance" fx:id="balanceColumn" minWidth="50" sortable="false"/>
|
||||
<TableColumn text="Copy" fx:id="copyColumn" minWidth="30" sortable="false"/>
|
||||
<TableColumn text="Status" fx:id="confidenceColumn" minWidth="30" sortable="false"/>
|
||||
<TableColumn text="Confirmations" fx:id="confidenceColumn" minWidth="30" sortable="false"/>
|
||||
</columns>
|
||||
</TableView>
|
||||
|
||||
|
|
|
@ -17,75 +17,89 @@
|
|||
|
||||
package io.bitsquare.gui.main.funds.withdrawal;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import io.bitsquare.app.BitsquareApp;
|
||||
import io.bitsquare.btc.AddressEntry;
|
||||
import io.bitsquare.btc.FeePolicy;
|
||||
import io.bitsquare.btc.Restrictions;
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.btc.listeners.BalanceListener;
|
||||
import io.bitsquare.gui.common.view.ActivatableViewAndModel;
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
import io.bitsquare.gui.common.view.ActivatableView;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.components.Popups;
|
||||
import io.bitsquare.gui.popups.OfferDetailsPopup;
|
||||
import io.bitsquare.gui.popups.Popup;
|
||||
import io.bitsquare.gui.popups.TradeDetailsPopup;
|
||||
import io.bitsquare.gui.popups.WalletPasswordPopup;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.gui.util.GUIUtil;
|
||||
import io.bitsquare.gui.util.validation.BtcAddressValidator;
|
||||
import io.bitsquare.gui.util.validation.BtcValidator;
|
||||
import io.bitsquare.trade.Tradable;
|
||||
import io.bitsquare.trade.Trade;
|
||||
import io.bitsquare.trade.TradeManager;
|
||||
import io.bitsquare.trade.closed.ClosedTradableManager;
|
||||
import io.bitsquare.trade.failed.FailedTradesManager;
|
||||
import io.bitsquare.trade.offer.OpenOffer;
|
||||
import io.bitsquare.trade.offer.OpenOfferManager;
|
||||
|
||||
import org.bitcoinj.core.AddressFormatException;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.InsufficientMoneyException;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import io.bitsquare.user.Preferences;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.util.Callback;
|
||||
|
||||
import de.jensd.fx.fontawesome.AwesomeDude;
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
|
||||
import org.controlsfx.control.action.Action;
|
||||
|
||||
import org.bitcoinj.core.AddressFormatException;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.InsufficientMoneyException;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.spongycastle.crypto.params.KeyParameter;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@FxmlView
|
||||
public class WithdrawalView extends ActivatableViewAndModel {
|
||||
public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
|
||||
@FXML Button withdrawButton;
|
||||
@FXML TableView<WithdrawalListItem> table;
|
||||
@FXML TextField withdrawFromTextField, withdrawToTextField, amountTextField;
|
||||
@FXML TableColumn<WithdrawalListItem, WithdrawalListItem> labelColumn, addressColumn, balanceColumn, copyColumn,
|
||||
confidenceColumn;
|
||||
@FXML
|
||||
TableColumn<WithdrawalListItem, WithdrawalListItem> labelColumn, addressColumn, balanceColumn, confidenceColumn;
|
||||
|
||||
private final WalletService walletService;
|
||||
private TradeManager tradeManager;
|
||||
private OpenOfferManager openOfferManager;
|
||||
private final TradeManager tradeManager;
|
||||
private final ClosedTradableManager closedTradableManager;
|
||||
private final FailedTradesManager failedTradesManager;
|
||||
private final OpenOfferManager openOfferManager;
|
||||
private final BSFormatter formatter;
|
||||
private final Preferences preferences;
|
||||
private final BtcAddressValidator btcAddressValidator;
|
||||
private final BtcValidator btcValidator;
|
||||
private final WalletPasswordPopup walletPasswordPopup;
|
||||
private final OfferDetailsPopup offerDetailsPopup;
|
||||
private final TradeDetailsPopup tradeDetailsPopup;
|
||||
private final ObservableList<WithdrawalListItem> addressList = FXCollections.observableArrayList();
|
||||
|
||||
@Inject
|
||||
private WithdrawalView(WalletService walletService, TradeManager tradeManager, OpenOfferManager openOfferManager, BSFormatter formatter,
|
||||
BtcAddressValidator btcAddressValidator, BtcValidator btcValidator) {
|
||||
private WithdrawalView(WalletService walletService, TradeManager tradeManager, ClosedTradableManager closedTradableManager,
|
||||
FailedTradesManager failedTradesManager, OpenOfferManager openOfferManager, BSFormatter formatter, Preferences preferences,
|
||||
BtcAddressValidator btcAddressValidator, WalletPasswordPopup walletPasswordPopup,
|
||||
OfferDetailsPopup offerDetailsPopup, TradeDetailsPopup tradeDetailsPopup) {
|
||||
this.walletService = walletService;
|
||||
this.tradeManager = tradeManager;
|
||||
this.closedTradableManager = closedTradableManager;
|
||||
this.failedTradesManager = failedTradesManager;
|
||||
this.openOfferManager = openOfferManager;
|
||||
this.formatter = formatter;
|
||||
this.preferences = preferences;
|
||||
this.btcAddressValidator = btcAddressValidator;
|
||||
this.btcValidator = btcValidator;
|
||||
this.walletPasswordPopup = walletPasswordPopup;
|
||||
this.offerDetailsPopup = offerDetailsPopup;
|
||||
this.tradeDetailsPopup = tradeDetailsPopup;
|
||||
}
|
||||
|
||||
|
||||
|
@ -95,19 +109,34 @@ public class WithdrawalView extends ActivatableViewAndModel {
|
|||
table.setPlaceholder(new Label("No funds for withdrawal available"));
|
||||
|
||||
setLabelColumnCellFactory();
|
||||
setAddressColumnCellFactory();
|
||||
setBalanceColumnCellFactory();
|
||||
setCopyColumnCellFactory();
|
||||
setConfidenceColumnCellFactory();
|
||||
|
||||
if (BitsquareApp.DEV_MODE)
|
||||
withdrawToTextField.setText("mwajQdfYnve1knXnmv7JdeiVpeogTsck6S");
|
||||
}
|
||||
|
||||
private boolean areInputsValid() {
|
||||
return btcAddressValidator.validate(withdrawFromTextField.getText()).and(
|
||||
btcAddressValidator.validate(withdrawToTextField.getText())).and(
|
||||
btcValidator.validate(amountTextField.getText())).isValid;
|
||||
btcAddressValidator.validate(withdrawToTextField.getText())).isValid;
|
||||
}
|
||||
|
||||
private void openTxDetails(WithdrawalListItem item) {
|
||||
// TODO Open popup with details view
|
||||
log.debug("openTxDetails " + item);
|
||||
|
||||
try {
|
||||
Utilities.openWebPage(preferences.getBlockChainExplorer().addressUrl + item.getAddressString());
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
new Popup().warning("Opening browser failed. Please check your internet " +
|
||||
"connection.").show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doActivate() {
|
||||
protected void activate() {
|
||||
withdrawButton.disableProperty().bind(Bindings.createBooleanBinding(() -> !areInputsValid(),
|
||||
withdrawFromTextField.textProperty(), amountTextField.textProperty(), withdrawToTextField.textProperty()));
|
||||
table.getSelectionModel().selectedItemProperty().addListener((observableValue, oldValue, newValue) -> {
|
||||
|
@ -137,7 +166,7 @@ public class WithdrawalView extends ActivatableViewAndModel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void doDeactivate() {
|
||||
protected void deactivate() {
|
||||
addressList.forEach(WithdrawalListItem::cleanup);
|
||||
withdrawButton.disableProperty().unbind();
|
||||
}
|
||||
|
@ -163,51 +192,62 @@ public class WithdrawalView extends ActivatableViewAndModel {
|
|||
}
|
||||
};
|
||||
|
||||
Action response = Popups.openConfirmPopup(
|
||||
"Withdrawal request", "Confirm your request",
|
||||
"Your withdrawal request:\n\n" + "Amount: " + amountTextField.getText() + " BTC\n" + "Sending" +
|
||||
" address: " + withdrawFromTextField.getText() + "\n" + "Receiving address: " +
|
||||
withdrawToTextField.getText() + "\n" + "Transaction fee: " +
|
||||
formatter.formatCoinWithCode(FeePolicy.TX_FEE) + "\n" +
|
||||
"You receive in total: " +
|
||||
formatter.formatCoinWithCode(amount.subtract(FeePolicy.TX_FEE)) + " BTC\n\n" +
|
||||
"Are you sure you withdraw that amount?");
|
||||
|
||||
Popups.removeBlurContent();
|
||||
if (Popups.isOK(response)) {
|
||||
try {
|
||||
walletService.sendFunds(withdrawFromTextField.getText(), withdrawToTextField.getText(),
|
||||
amount, callback);
|
||||
fillList();
|
||||
} catch (AddressFormatException e) {
|
||||
Popups.openErrorPopup("Address invalid",
|
||||
"The address is not correct. Please check the address format.");
|
||||
|
||||
} catch (InsufficientMoneyException e) {
|
||||
Popups.openInsufficientMoneyPopup();
|
||||
} catch (IllegalArgumentException e) {
|
||||
Popups.openErrorPopup("Wrong inputs", "Please check the inputs.");
|
||||
}
|
||||
if (BitsquareApp.DEV_MODE) {
|
||||
doWithdraw(amount, callback);
|
||||
} else {
|
||||
new Popup().headLine("Confirm your withdrawal request").message("Amount: " + amountTextField.getText() + " " +
|
||||
"BTC\n" +
|
||||
"Sending" +
|
||||
" address: " + withdrawFromTextField.getText() + "\n" + "Receiving address: " +
|
||||
withdrawToTextField.getText() + "\n" + "Transaction fee: " +
|
||||
formatter.formatCoinWithCode(FeePolicy.TX_FEE) + "\n" +
|
||||
"Receivers amount: " +
|
||||
formatter.formatCoinWithCode(amount.subtract(FeePolicy.TX_FEE)) + " BTC\n\n" +
|
||||
"Are you sure you withdraw that amount?")
|
||||
.onAction(() -> {
|
||||
doWithdraw(amount, callback);
|
||||
})
|
||||
.show();
|
||||
}
|
||||
}
|
||||
else {
|
||||
Popups.openErrorPopup("Insufficient amount",
|
||||
"The amount to transfer is lower the the transaction fee and the min. possible tx value.");
|
||||
new Popup().error("The amount to transfer is lower the the transaction fee and the min. possible tx value.").show();
|
||||
}
|
||||
}
|
||||
|
||||
private void doWithdraw(Coin amount, FutureCallback<Transaction> callback) {
|
||||
if (walletService.getWallet().isEncrypted())
|
||||
walletPasswordPopup.show().onAesKey(aesKey -> sendFunds(amount, aesKey, callback));
|
||||
else
|
||||
sendFunds(amount, null, callback);
|
||||
fillList();
|
||||
}
|
||||
|
||||
private void sendFunds(Coin amount, KeyParameter aesKey, FutureCallback<Transaction> callback) {
|
||||
try {
|
||||
walletService.sendFunds(withdrawFromTextField.getText(), withdrawToTextField.getText(), amount, aesKey, callback);
|
||||
} catch (AddressFormatException e) {
|
||||
new Popup().error("The address is not correct. Please check the address format.").show();
|
||||
} catch (InsufficientMoneyException e) {
|
||||
log.warn(e.getMessage());
|
||||
new Popup().error("You don't have enough fund in your wallet.").show();
|
||||
}
|
||||
|
||||
withdrawFromTextField.setText("");
|
||||
withdrawFromTextField.setPromptText("Select a source address from the table");
|
||||
amountTextField.setText("");
|
||||
amountTextField.setPromptText("");
|
||||
withdrawToTextField.setText("");
|
||||
if (!BitsquareApp.DEV_MODE)
|
||||
withdrawToTextField.setText("");
|
||||
withdrawToTextField.setPromptText("");
|
||||
}
|
||||
|
||||
private void fillList() {
|
||||
addressList.clear();
|
||||
|
||||
List<AddressEntry> addressEntryList = walletService.getAddressEntryList();
|
||||
|
||||
List<String> reservedTrades = Stream.concat(openOfferManager.getOpenOffers().stream(), tradeManager.getPendingTrades().stream())
|
||||
List<String> reservedTrades = Stream.concat(openOfferManager.getOpenOffers().stream(), tradeManager.getTrades().stream())
|
||||
.map(tradable -> tradable.getOffer().getId())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
|
@ -237,13 +277,11 @@ public class WithdrawalView extends ActivatableViewAndModel {
|
|||
|
||||
if (item != null && !empty) {
|
||||
hyperlink = new Hyperlink(item.getLabel());
|
||||
hyperlink.setId("id-link");
|
||||
if (item.getAddressEntry().getOfferId() != null) {
|
||||
Tooltip tooltip = new Tooltip(item.getAddressEntry().getOfferId());
|
||||
Tooltip tooltip = new Tooltip(item.getAddressEntry().getShortOfferId());
|
||||
Tooltip.install(hyperlink, tooltip);
|
||||
|
||||
hyperlink.setOnAction(event -> log.info("Show trade details " + item.getAddressEntry
|
||||
().getOfferId()));
|
||||
hyperlink.setOnAction(event -> openDetails(item));
|
||||
}
|
||||
setGraphic(hyperlink);
|
||||
}
|
||||
|
@ -257,6 +295,36 @@ public class WithdrawalView extends ActivatableViewAndModel {
|
|||
});
|
||||
}
|
||||
|
||||
private void setAddressColumnCellFactory() {
|
||||
addressColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
addressColumn.setCellFactory(
|
||||
new Callback<TableColumn<WithdrawalListItem, WithdrawalListItem>, TableCell<WithdrawalListItem,
|
||||
WithdrawalListItem>>() {
|
||||
|
||||
@Override
|
||||
public TableCell<WithdrawalListItem, WithdrawalListItem> call(TableColumn<WithdrawalListItem,
|
||||
WithdrawalListItem> column) {
|
||||
return new TableCell<WithdrawalListItem, WithdrawalListItem>() {
|
||||
private Hyperlink hyperlink;
|
||||
|
||||
@Override
|
||||
public void updateItem(final WithdrawalListItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (item != null && !empty) {
|
||||
hyperlink = new Hyperlink(item.getAddressString());
|
||||
hyperlink.setOnAction(event -> openTxDetails(item));
|
||||
setGraphic(hyperlink);
|
||||
} else {
|
||||
setGraphic(null);
|
||||
setId(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setBalanceColumnCellFactory() {
|
||||
balanceColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
balanceColumn.setCellFactory(
|
||||
|
@ -277,43 +345,6 @@ public class WithdrawalView extends ActivatableViewAndModel {
|
|||
});
|
||||
}
|
||||
|
||||
private void setCopyColumnCellFactory() {
|
||||
copyColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
copyColumn.setCellFactory(
|
||||
new Callback<TableColumn<WithdrawalListItem, WithdrawalListItem>, TableCell<WithdrawalListItem,
|
||||
WithdrawalListItem>>() {
|
||||
|
||||
@Override
|
||||
public TableCell<WithdrawalListItem, WithdrawalListItem> call(TableColumn<WithdrawalListItem,
|
||||
WithdrawalListItem> column) {
|
||||
return new TableCell<WithdrawalListItem, WithdrawalListItem>() {
|
||||
final Label copyIcon = new Label();
|
||||
|
||||
{
|
||||
copyIcon.getStyleClass().add("copy-icon");
|
||||
AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY);
|
||||
Tooltip.install(copyIcon, new Tooltip("Copy address to clipboard"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(final WithdrawalListItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (item != null && !empty) {
|
||||
setGraphic(copyIcon);
|
||||
copyIcon.setOnMouseClicked(e -> GUIUtil.copyToClipboard(item
|
||||
.addressStringProperty().get()));
|
||||
|
||||
}
|
||||
else {
|
||||
setGraphic(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setConfidenceColumnCellFactory() {
|
||||
confidenceColumn.setCellValueFactory((addressListItem) ->
|
||||
new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
|
@ -342,6 +373,20 @@ public class WithdrawalView extends ActivatableViewAndModel {
|
|||
});
|
||||
}
|
||||
|
||||
private void openDetails(WithdrawalListItem item) {
|
||||
String offerId = item.getAddressEntry().getOfferId();
|
||||
Optional<Tradable> tradableOptional = closedTradableManager.getTradableById(offerId);
|
||||
if (tradableOptional.isPresent()) {
|
||||
Tradable tradable = tradableOptional.get();
|
||||
if (tradable instanceof Trade) {
|
||||
tradeDetailsPopup.show((Trade) tradable);
|
||||
} else if (tradable instanceof OpenOffer) {
|
||||
offerDetailsPopup.show(tradable.getOffer());
|
||||
}
|
||||
} else {
|
||||
failedTradesManager.getTradeById(offerId).ifPresent(trade -> tradeDetailsPopup.show(trade));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -17,9 +17,8 @@
|
|||
|
||||
package io.bitsquare.gui.main.help;
|
||||
|
||||
import io.bitsquare.gui.components.Popups;
|
||||
import io.bitsquare.util.Utilities;
|
||||
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
import io.bitsquare.gui.popups.Popup;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -37,8 +36,8 @@ public class Help {
|
|||
Utilities.openWebPage("https://github.com/bitsquare/bitsquare/wiki/User-Guide");
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
Popups.openWarningPopup("Warning", "Opening browser failed. Please check your internet " +
|
||||
"connection.");
|
||||
new Popup().warning("Opening browser failed. Please check your internet " +
|
||||
"connection.").show();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
~ 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/>.
|
||||
-->
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<TabPane fx:id="root" fx:controller="io.bitsquare.gui.main.home.HomeView"
|
||||
AnchorPane.bottomAnchor="0" AnchorPane.leftAnchor="0"
|
||||
AnchorPane.rightAnchor="0" AnchorPane.topAnchor="0"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
<Tab text="Overview" closable="false">
|
||||
<VBox spacing="20">
|
||||
<padding>
|
||||
<Insets left="20" top="20" right="20"/>
|
||||
</padding>
|
||||
<Label text="Overview not implemented yet. It will contain latest activities, market and price information and notifications."/>
|
||||
</VBox>
|
||||
</Tab>
|
||||
</TabPane>
|
|
@ -1,27 +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.main.home;
|
||||
|
||||
import io.bitsquare.gui.common.view.AbstractView;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
|
||||
// home is just hosting the arbiters buttons yet, but that's just for dev, not clear yet what will be in home,
|
||||
// probably overview, event history, news, charts,... -> low prio
|
||||
@FxmlView
|
||||
public class HomeView extends AbstractView {
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
~ 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/>.
|
||||
-->
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<TabPane fx:id="root" fx:controller="io.bitsquare.gui.main.msg.MsgView"
|
||||
AnchorPane.bottomAnchor="0" AnchorPane.leftAnchor="0"
|
||||
AnchorPane.rightAnchor="0" AnchorPane.topAnchor="0"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
|
||||
<Tab text="Message" closable="false">
|
||||
<VBox spacing="20">
|
||||
<padding>
|
||||
<Insets left="20" top="20" right="20"/>
|
||||
</padding>
|
||||
<Label text="Messages not implemented yet. It will contain communication tools with arbitrators, mailbox for trade messages if client was offline and notifications."/>
|
||||
</VBox>
|
||||
</Tab>
|
||||
</TabPane>
|
|
@ -1,27 +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.main.msg;
|
||||
|
||||
import io.bitsquare.gui.common.view.AbstractView;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
|
||||
// will be probably only used for arbitration communication, will be renamed and the icon changed
|
||||
@FxmlView
|
||||
public class MsgView extends AbstractView {
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package io.bitsquare.gui.main.offer;
|
||||
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.gui.Navigation;
|
||||
import io.bitsquare.gui.common.view.ActivatableView;
|
||||
import io.bitsquare.gui.common.view.View;
|
||||
|
@ -26,18 +27,16 @@ import io.bitsquare.gui.main.MainView;
|
|||
import io.bitsquare.gui.main.offer.createoffer.CreateOfferView;
|
||||
import io.bitsquare.gui.main.offer.offerbook.OfferBookView;
|
||||
import io.bitsquare.gui.main.offer.takeoffer.TakeOfferView;
|
||||
import io.bitsquare.locale.CurrencyUtil;
|
||||
import io.bitsquare.locale.TradeCurrency;
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.utils.Fiat;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.scene.control.Tab;
|
||||
import javafx.scene.control.TabPane;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.*;
|
||||
|
||||
public abstract class OfferView extends ActivatableView<TabPane, Void> {
|
||||
|
||||
private OfferBookView offerBookView;
|
||||
|
@ -46,8 +45,6 @@ public abstract class OfferView extends ActivatableView<TabPane, Void> {
|
|||
private AnchorPane createOfferPane;
|
||||
private AnchorPane takeOfferPane;
|
||||
private Navigation.Listener listener;
|
||||
private Coin amount;
|
||||
private Fiat price;
|
||||
private Offer offer;
|
||||
|
||||
private final ViewLoader viewLoader;
|
||||
|
@ -55,6 +52,7 @@ public abstract class OfferView extends ActivatableView<TabPane, Void> {
|
|||
private final Offer.Direction direction;
|
||||
private Tab createOfferTab;
|
||||
private Tab takeOfferTab;
|
||||
private TradeCurrency tradeCurrency;
|
||||
|
||||
protected OfferView(ViewLoader viewLoader, Navigation navigation) {
|
||||
this.viewLoader = viewLoader;
|
||||
|
@ -73,11 +71,11 @@ public abstract class OfferView extends ActivatableView<TabPane, Void> {
|
|||
@Override
|
||||
protected void activate() {
|
||||
// We need to remove open validation error popups
|
||||
// Platform.runLater needed as focus-out event is called after selectedIndexProperty changed
|
||||
// UserThread.execute needed as focus-out event is called after selectedIndexProperty changed
|
||||
// TODO Find a way to do that in the InputTextField directly, but a tab change does not trigger any event...
|
||||
TabPane tabPane = root;
|
||||
tabPane.getSelectionModel().selectedIndexProperty()
|
||||
.addListener((observableValue, oldValue, newValue) -> Platform.runLater(InputTextField::hideErrorMessageDisplay));
|
||||
.addListener((observableValue, oldValue, newValue) -> UserThread.execute(InputTextField::hideErrorMessageDisplay));
|
||||
|
||||
// We want to get informed when a tab get closed
|
||||
tabPane.getTabs().addListener((ListChangeListener<Tab>) change -> {
|
||||
|
@ -91,6 +89,8 @@ public abstract class OfferView extends ActivatableView<TabPane, Void> {
|
|||
}
|
||||
});
|
||||
|
||||
tradeCurrency = CurrencyUtil.getDefaultFiatCurrency();
|
||||
|
||||
navigation.addListener(listener);
|
||||
navigation.navigateTo(MainView.class, this.getClass(), OfferBookView.class);
|
||||
}
|
||||
|
@ -100,15 +100,11 @@ public abstract class OfferView extends ActivatableView<TabPane, Void> {
|
|||
navigation.removeListener(listener);
|
||||
}
|
||||
|
||||
public void createOffer(Coin amount, Fiat price) {
|
||||
this.amount = amount;
|
||||
this.price = price;
|
||||
public void createOffer() {
|
||||
navigation.navigateTo(MainView.class, this.getClass(), CreateOfferView.class);
|
||||
}
|
||||
|
||||
public void takeOffer(Coin amount, Fiat price, Offer offer) {
|
||||
this.amount = amount;
|
||||
this.price = price;
|
||||
public void takeOffer(Offer offer) {
|
||||
this.offer = offer;
|
||||
navigation.navigateTo(MainView.class, this.getClass(), TakeOfferView.class);
|
||||
}
|
||||
|
@ -128,17 +124,14 @@ public abstract class OfferView extends ActivatableView<TabPane, Void> {
|
|||
|
||||
OfferActionHandler offerActionHandler = new OfferActionHandler() {
|
||||
@Override
|
||||
public void createOffer(Coin amount, Fiat price) {
|
||||
OfferView.this.amount = amount;
|
||||
OfferView.this.price = price;
|
||||
public void onCreateOffer(TradeCurrency tradeCurrency) {
|
||||
OfferView.this.tradeCurrency = tradeCurrency;
|
||||
OfferView.this.navigation.navigateTo(MainView.class, OfferView.this.getClass(),
|
||||
CreateOfferView.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void takeOffer(Coin amount, Fiat price, Offer offer) {
|
||||
OfferView.this.amount = amount;
|
||||
OfferView.this.price = price;
|
||||
public void onTakeOffer(Offer offer) {
|
||||
OfferView.this.offer = offer;
|
||||
OfferView.this.navigation.navigateTo(MainView.class, OfferView.this.getClass(),
|
||||
TakeOfferView.class);
|
||||
|
@ -153,12 +146,11 @@ public abstract class OfferView extends ActivatableView<TabPane, Void> {
|
|||
// CreateOffer and TakeOffer must not be cached by ViewLoader as we cannot use a view multiple times
|
||||
// in different graphs
|
||||
createOfferView = (CreateOfferView) view;
|
||||
createOfferView.initWithData(direction, amount, price);
|
||||
createOfferPane = ((CreateOfferView) view).getRoot();
|
||||
createOfferView.initWithData(direction, tradeCurrency);
|
||||
createOfferPane = createOfferView.getRoot();
|
||||
createOfferTab = new Tab("Create offer");
|
||||
createOfferView.setCloseHandler(() -> {
|
||||
tabPane.getTabs().remove(createOfferTab);
|
||||
});
|
||||
// close handler from close on create offer action
|
||||
createOfferView.setCloseHandler(() -> tabPane.getTabs().remove(createOfferTab));
|
||||
createOfferTab.setContent(createOfferPane);
|
||||
tabPane.getTabs().add(createOfferTab);
|
||||
tabPane.getSelectionModel().select(createOfferTab);
|
||||
|
@ -168,12 +160,11 @@ public abstract class OfferView extends ActivatableView<TabPane, Void> {
|
|||
// CreateOffer and TakeOffer must not be cached by ViewLoader as we cannot use a view multiple times
|
||||
// in different graphs
|
||||
takeOfferView = (TakeOfferView) view;
|
||||
takeOfferView.initWithData(amount, offer);
|
||||
takeOfferView.initWithData(offer);
|
||||
takeOfferPane = ((TakeOfferView) view).getRoot();
|
||||
takeOfferTab = new Tab("Take offer");
|
||||
takeOfferView.setCloseHandler(() -> {
|
||||
tabPane.getTabs().remove(takeOfferTab);
|
||||
});
|
||||
// close handler from close on take offer action
|
||||
takeOfferView.setCloseHandler(() -> tabPane.getTabs().remove(takeOfferTab));
|
||||
takeOfferTab.setContent(takeOfferPane);
|
||||
tabPane.getTabs().add(takeOfferTab);
|
||||
tabPane.getSelectionModel().select(takeOfferTab);
|
||||
|
@ -182,7 +173,10 @@ public abstract class OfferView extends ActivatableView<TabPane, Void> {
|
|||
|
||||
|
||||
private void onCreateOfferViewRemoved() {
|
||||
createOfferView = null;
|
||||
if (createOfferView != null) {
|
||||
createOfferView.onClose();
|
||||
createOfferView = null;
|
||||
}
|
||||
offerBookView.enableCreateOfferButton();
|
||||
|
||||
// update the navigation state
|
||||
|
@ -190,16 +184,19 @@ public abstract class OfferView extends ActivatableView<TabPane, Void> {
|
|||
}
|
||||
|
||||
private void onTakeOfferViewRemoved() {
|
||||
takeOfferView = null;
|
||||
if (takeOfferView != null) {
|
||||
takeOfferView.onClose();
|
||||
takeOfferView = null;
|
||||
}
|
||||
|
||||
// update the navigation state
|
||||
navigation.navigateTo(MainView.class, this.getClass(), OfferBookView.class);
|
||||
}
|
||||
|
||||
public interface OfferActionHandler {
|
||||
void createOffer(Coin amount, Fiat price);
|
||||
void onCreateOffer(TradeCurrency tradeCurrency);
|
||||
|
||||
void takeOffer(Coin amount, Fiat price, Offer offer);
|
||||
void onTakeOffer(Offer offer);
|
||||
}
|
||||
|
||||
public interface CloseHandler {
|
||||
|
|
|
@ -17,79 +17,70 @@
|
|||
|
||||
package io.bitsquare.gui.main.offer.createoffer;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import io.bitsquare.arbitration.Arbitrator;
|
||||
import io.bitsquare.btc.AddressEntry;
|
||||
import io.bitsquare.btc.FeePolicy;
|
||||
import io.bitsquare.btc.TradeWalletService;
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.btc.listeners.BalanceListener;
|
||||
import io.bitsquare.fiat.FiatAccount;
|
||||
import io.bitsquare.gui.common.model.Activatable;
|
||||
import io.bitsquare.gui.common.model.DataModel;
|
||||
import io.bitsquare.common.crypto.KeyRing;
|
||||
import io.bitsquare.gui.common.model.ActivatableDataModel;
|
||||
import io.bitsquare.gui.popups.WalletPasswordPopup;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.locale.Country;
|
||||
import io.bitsquare.locale.TradeCurrency;
|
||||
import io.bitsquare.p2p.Address;
|
||||
import io.bitsquare.p2p.P2PService;
|
||||
import io.bitsquare.payment.PaymentAccount;
|
||||
import io.bitsquare.payment.SepaAccount;
|
||||
import io.bitsquare.trade.handlers.TransactionResultHandler;
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
import io.bitsquare.trade.offer.OpenOfferManager;
|
||||
import io.bitsquare.user.AccountSettings;
|
||||
import io.bitsquare.user.Preferences;
|
||||
import io.bitsquare.user.User;
|
||||
|
||||
import javafx.beans.property.*;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.SetChangeListener;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.utils.ExchangeRate;
|
||||
import org.bitcoinj.utils.Fiat;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Domain for that UI element.
|
||||
* Note that the create offer domain has a deeper scope in the application domain (TradeManager).
|
||||
* That model is just responsible for the domain specific parts displayed needed in that UI element.
|
||||
*/
|
||||
class CreateOfferDataModel implements Activatable, DataModel {
|
||||
private static final Logger log = LoggerFactory.getLogger(CreateOfferDataModel.class);
|
||||
|
||||
|
||||
class CreateOfferDataModel extends ActivatableDataModel {
|
||||
private final OpenOfferManager openOfferManager;
|
||||
private final WalletService walletService;
|
||||
private final AccountSettings accountSettings;
|
||||
private final TradeWalletService tradeWalletService;
|
||||
private final Preferences preferences;
|
||||
private final User user;
|
||||
private final Address address;
|
||||
private final KeyRing keyRing;
|
||||
private final WalletPasswordPopup walletPasswordPopup;
|
||||
private final BSFormatter formatter;
|
||||
private final String offerId;
|
||||
private final AddressEntry addressEntry;
|
||||
final ObjectProperty<Coin> offerFeeAsCoin = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Coin> networkFeeAsCoin = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Coin> securityDepositAsCoin = new SimpleObjectProperty<>();
|
||||
private final Coin offerFeeAsCoin;
|
||||
private final Coin networkFeeAsCoin;
|
||||
private final Coin securityDepositAsCoin;
|
||||
private final BalanceListener balanceListener;
|
||||
private final ChangeListener<FiatAccount> currentFiatAccountListener;
|
||||
private final SetChangeListener<PaymentAccount> paymentAccountsChangeListener;
|
||||
|
||||
private Offer.Direction direction;
|
||||
|
||||
final StringProperty requestPlaceOfferErrorMessage = new SimpleStringProperty();
|
||||
final StringProperty transactionId = new SimpleStringProperty();
|
||||
final StringProperty bankAccountCurrency = new SimpleStringProperty();
|
||||
final StringProperty bankAccountCounty = new SimpleStringProperty();
|
||||
final StringProperty bankAccountType = new SimpleStringProperty();
|
||||
final StringProperty fiatCode = new SimpleStringProperty();
|
||||
private TradeCurrency tradeCurrency;
|
||||
|
||||
final StringProperty tradeCurrencyCode = new SimpleStringProperty();
|
||||
final StringProperty btcCode = new SimpleStringProperty();
|
||||
|
||||
final BooleanProperty requestPlaceOfferSuccess = new SimpleBooleanProperty();
|
||||
final BooleanProperty isWalletFunded = new SimpleBooleanProperty();
|
||||
final BooleanProperty useMBTC = new SimpleBooleanProperty();
|
||||
|
||||
|
@ -99,9 +90,9 @@ class CreateOfferDataModel implements Activatable, DataModel {
|
|||
final ObjectProperty<Fiat> volumeAsFiat = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Coin> totalToPayAsCoin = new SimpleObjectProperty<>();
|
||||
|
||||
final ObservableList<Country> acceptedCountries = FXCollections.observableArrayList();
|
||||
final ObservableList<String> acceptedLanguageCodes = FXCollections.observableArrayList();
|
||||
final ObservableList<Arbitrator> acceptedArbitrators = FXCollections.observableArrayList();
|
||||
final ObservableList<PaymentAccount> paymentAccounts = FXCollections.observableArrayList();
|
||||
|
||||
private PaymentAccount paymentAccount;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -110,23 +101,24 @@ class CreateOfferDataModel implements Activatable, DataModel {
|
|||
|
||||
|
||||
@Inject
|
||||
CreateOfferDataModel(OpenOfferManager openOfferManager, WalletService walletService,
|
||||
AccountSettings accountSettings, Preferences preferences, User user, BSFormatter formatter) {
|
||||
CreateOfferDataModel(OpenOfferManager openOfferManager, WalletService walletService, TradeWalletService tradeWalletService,
|
||||
Preferences preferences, User user, KeyRing keyRing, P2PService p2PService,
|
||||
WalletPasswordPopup walletPasswordPopup, BSFormatter formatter) {
|
||||
this.openOfferManager = openOfferManager;
|
||||
this.walletService = walletService;
|
||||
this.accountSettings = accountSettings;
|
||||
this.tradeWalletService = tradeWalletService;
|
||||
this.preferences = preferences;
|
||||
this.user = user;
|
||||
this.keyRing = keyRing;
|
||||
this.walletPasswordPopup = walletPasswordPopup;
|
||||
this.formatter = formatter;
|
||||
this.offerId = UUID.randomUUID().toString();
|
||||
|
||||
addressEntry = walletService.getAddressEntry(offerId);
|
||||
|
||||
offerFeeAsCoin.set(FeePolicy.CREATE_OFFER_FEE);
|
||||
networkFeeAsCoin.set(FeePolicy.TX_FEE);
|
||||
|
||||
// we need to set it here already as it is used before activate
|
||||
securityDepositAsCoin.set(accountSettings.getSecurityDeposit());
|
||||
address = p2PService.getAddress();
|
||||
offerId = UUID.randomUUID().toString();
|
||||
addressEntry = walletService.getAddressEntryByOfferId(offerId);
|
||||
offerFeeAsCoin = FeePolicy.CREATE_OFFER_FEE;
|
||||
networkFeeAsCoin = FeePolicy.TX_FEE;
|
||||
securityDepositAsCoin = FeePolicy.SECURITY_DEPOSIT;
|
||||
|
||||
balanceListener = new BalanceListener(getAddressEntry().getAddress()) {
|
||||
@Override
|
||||
|
@ -135,32 +127,24 @@ class CreateOfferDataModel implements Activatable, DataModel {
|
|||
}
|
||||
};
|
||||
|
||||
currentFiatAccountListener = (observable, oldValue, newValue) -> {
|
||||
applyBankAccount(newValue);
|
||||
};
|
||||
paymentAccountsChangeListener = change -> paymentAccounts.setAll(user.getPaymentAccounts());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void activate() {
|
||||
protected void activate() {
|
||||
addBindings();
|
||||
addListeners();
|
||||
|
||||
// might be changed after screen change
|
||||
if (accountSettings != null) {
|
||||
// set it here again to cover the case of an securityDeposit change after a screen change
|
||||
securityDepositAsCoin.set(accountSettings.getSecurityDeposit());
|
||||
|
||||
acceptedCountries.setAll(accountSettings.getAcceptedCountries());
|
||||
acceptedLanguageCodes.setAll(accountSettings.getAcceptedLanguageLocaleCodes());
|
||||
acceptedArbitrators.setAll(accountSettings.getAcceptedArbitrators());
|
||||
}
|
||||
|
||||
paymentAccounts.setAll(user.getPaymentAccounts());
|
||||
updateBalance(walletService.getBalanceForAddress(getAddressEntry().getAddress()));
|
||||
applyBankAccount(user.currentFiatAccountProperty().get());
|
||||
|
||||
if (direction == Offer.Direction.BUY)
|
||||
calculateTotalToPay();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
protected void deactivate() {
|
||||
removeBindings();
|
||||
removeListeners();
|
||||
}
|
||||
|
@ -175,13 +159,12 @@ class CreateOfferDataModel implements Activatable, DataModel {
|
|||
|
||||
private void addListeners() {
|
||||
walletService.addBalanceListener(balanceListener);
|
||||
user.currentFiatAccountProperty().addListener(currentFiatAccountListener);
|
||||
|
||||
user.getPaymentAccountsAsObservable().addListener(paymentAccountsChangeListener);
|
||||
}
|
||||
|
||||
private void removeListeners() {
|
||||
walletService.removeBalanceListener(balanceListener);
|
||||
user.currentFiatAccountProperty().removeListener(currentFiatAccountListener);
|
||||
user.getPaymentAccountsAsObservable().removeListener(paymentAccountsChangeListener);
|
||||
}
|
||||
|
||||
|
||||
|
@ -189,8 +172,14 @@ class CreateOfferDataModel implements Activatable, DataModel {
|
|||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void initWithData(Offer.Direction direction) {
|
||||
void initWithData(Offer.Direction direction, TradeCurrency tradeCurrency) {
|
||||
this.direction = direction;
|
||||
this.tradeCurrency = tradeCurrency;
|
||||
|
||||
tradeCurrencyCode.set(tradeCurrency.getCode());
|
||||
PaymentAccount account = user.findFirstPaymentAccountWithCurrency(tradeCurrency);
|
||||
if (account != null)
|
||||
paymentAccount = account;
|
||||
}
|
||||
|
||||
|
||||
|
@ -198,18 +187,47 @@ class CreateOfferDataModel implements Activatable, DataModel {
|
|||
// UI actions
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void onPlaceOffer() {
|
||||
// data validation is done in the trade domain
|
||||
openOfferManager.onPlaceOffer(offerId,
|
||||
Offer getOffer() {
|
||||
long fiatPrice = priceAsFiat.get() != null ? priceAsFiat.get().getValue() : 0L;
|
||||
long amount = amountAsCoin.get() != null ? amountAsCoin.get().getValue() : 0L;
|
||||
long minAmount = minAmountAsCoin.get() != null ? minAmountAsCoin.get().getValue() : 0L;
|
||||
|
||||
List<String> acceptedCountryCodes = null;
|
||||
if (paymentAccount instanceof SepaAccount)
|
||||
acceptedCountryCodes = ((SepaAccount) paymentAccount).getAcceptedCountryCodes();
|
||||
|
||||
// That is optional and set to null if not supported (AltCoins, OKPay,...)
|
||||
Country country = paymentAccount.getCountry();
|
||||
|
||||
return new Offer(offerId,
|
||||
address,
|
||||
keyRing.getPubKeyRing(),
|
||||
direction,
|
||||
priceAsFiat.get(),
|
||||
amountAsCoin.get(),
|
||||
minAmountAsCoin.get(),
|
||||
(transaction) -> {
|
||||
transactionId.set(transaction.getHashAsString());
|
||||
requestPlaceOfferSuccess.set(true);
|
||||
},
|
||||
requestPlaceOfferErrorMessage::set
|
||||
fiatPrice,
|
||||
amount,
|
||||
minAmount,
|
||||
paymentAccount.getPaymentMethod().getId(),
|
||||
tradeCurrencyCode.get(),
|
||||
country,
|
||||
paymentAccount.getId(),
|
||||
user.getAcceptedArbitratorAddresses(),
|
||||
acceptedCountryCodes);
|
||||
}
|
||||
|
||||
void onPlaceOffer(Offer offer, TransactionResultHandler resultHandler) {
|
||||
if (walletService.getWallet().isEncrypted() && tradeWalletService.getAesKey() == null) {
|
||||
walletPasswordPopup.show().onAesKey(aesKey -> {
|
||||
tradeWalletService.setAesKey(aesKey);
|
||||
doPlaceOffer(offer, resultHandler);
|
||||
});
|
||||
} else {
|
||||
doPlaceOffer(offer, resultHandler);
|
||||
}
|
||||
}
|
||||
|
||||
private void doPlaceOffer(Offer offer, TransactionResultHandler resultHandler) {
|
||||
openOfferManager.onPlaceOffer(offer,
|
||||
resultHandler
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -218,6 +236,21 @@ class CreateOfferDataModel implements Activatable, DataModel {
|
|||
}
|
||||
|
||||
|
||||
public void onPaymentAccountSelected(PaymentAccount paymentAccount) {
|
||||
if (paymentAccount != null)
|
||||
this.paymentAccount = paymentAccount;
|
||||
}
|
||||
|
||||
public void onCurrencySelected(TradeCurrency tradeCurrency) {
|
||||
if (tradeCurrency != null) {
|
||||
this.tradeCurrency = tradeCurrency;
|
||||
tradeCurrencyCode.set(tradeCurrency.getCode());
|
||||
|
||||
paymentAccount.setSelectedTradeCurrency(tradeCurrency);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getters
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -234,10 +267,6 @@ class CreateOfferDataModel implements Activatable, DataModel {
|
|||
return direction;
|
||||
}
|
||||
|
||||
WalletService getWalletService() {
|
||||
return walletService;
|
||||
}
|
||||
|
||||
String getOfferId() {
|
||||
return offerId;
|
||||
}
|
||||
|
@ -250,6 +279,17 @@ class CreateOfferDataModel implements Activatable, DataModel {
|
|||
return preferences.getDisplaySecurityDepositInfo();
|
||||
}
|
||||
|
||||
public TradeCurrency getTradeCurrency() {
|
||||
return tradeCurrency;
|
||||
}
|
||||
|
||||
public PaymentAccount getPaymentAccount() {
|
||||
return paymentAccount;
|
||||
}
|
||||
|
||||
boolean hasAcceptedArbitrators() {
|
||||
return user.getAcceptedArbitrators().size() > 0;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
|
@ -277,11 +317,11 @@ class CreateOfferDataModel implements Activatable, DataModel {
|
|||
}
|
||||
|
||||
void calculateTotalToPay() {
|
||||
if (securityDepositAsCoin.get() != null) {
|
||||
if (securityDepositAsCoin != null) {
|
||||
if (direction == Offer.Direction.BUY)
|
||||
totalToPayAsCoin.set(offerFeeAsCoin.get().add(networkFeeAsCoin.get()).add(securityDepositAsCoin.get()));
|
||||
totalToPayAsCoin.set(offerFeeAsCoin.add(networkFeeAsCoin).add(securityDepositAsCoin));
|
||||
else
|
||||
totalToPayAsCoin.set(offerFeeAsCoin.get().add(networkFeeAsCoin.get()).add(securityDepositAsCoin.get()).add(amountAsCoin.get()));
|
||||
totalToPayAsCoin.set(offerFeeAsCoin.add(networkFeeAsCoin).add(securityDepositAsCoin).add(amountAsCoin.get()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -289,13 +329,27 @@ class CreateOfferDataModel implements Activatable, DataModel {
|
|||
isWalletFunded.set(totalToPayAsCoin.get() != null && balance.compareTo(totalToPayAsCoin.get()) >= 0);
|
||||
}
|
||||
|
||||
private void applyBankAccount(FiatAccount fiatAccount) {
|
||||
if (fiatAccount != null) {
|
||||
bankAccountType.set(fiatAccount.type.toString());
|
||||
bankAccountCurrency.set(fiatAccount.currencyCode);
|
||||
bankAccountCounty.set(fiatAccount.country.name);
|
||||
public Coin getOfferFeeAsCoin() {
|
||||
return offerFeeAsCoin;
|
||||
}
|
||||
|
||||
fiatCode.set(fiatAccount.currencyCode);
|
||||
}
|
||||
public Coin getNetworkFeeAsCoin() {
|
||||
return networkFeeAsCoin;
|
||||
}
|
||||
|
||||
public Coin getSecurityDepositAsCoin() {
|
||||
return securityDepositAsCoin;
|
||||
}
|
||||
|
||||
public List<Arbitrator> getArbitrators() {
|
||||
return user.getAcceptedArbitrators();
|
||||
}
|
||||
|
||||
public void setShowPlaceOfferConfirmation(boolean selected) {
|
||||
preferences.setShowPlaceOfferConfirmation(selected);
|
||||
}
|
||||
|
||||
public boolean getShowPlaceOfferConfirmation() {
|
||||
return preferences.getShowPlaceOfferConfirmation();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,267 +17,7 @@
|
|||
~ along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<?import io.bitsquare.gui.components.AddressTextField?>
|
||||
<?import io.bitsquare.gui.components.BalanceTextField?>
|
||||
<?import io.bitsquare.gui.components.InfoDisplay?>
|
||||
<?import io.bitsquare.gui.components.InputTextField?>
|
||||
<?import io.bitsquare.gui.components.TitledGroupBg?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.image.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.text.*?>
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
<AnchorPane fx:id="root" fx:controller="io.bitsquare.gui.main.offer.createoffer.CreateOfferView"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
|
||||
<ScrollPane fx:id="scrollPane" onScroll="#onScroll" hbarPolicy="NEVER" vbarPolicy="NEVER" fitToWidth="true" fitToHeight="true"
|
||||
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"
|
||||
AnchorPane.bottomAnchor="0.0">
|
||||
|
||||
<!--suppress JavaFxUnresolvedFxIdReference -->
|
||||
<GridPane fx:id="gridPane" hgap="5.0" vgap="5.0">
|
||||
<padding>
|
||||
<Insets bottom="-10.0" left="25.0" top="30.0" right="25"/>
|
||||
</padding>
|
||||
|
||||
<!--
|
||||
Amount/Price group
|
||||
-->
|
||||
<TitledGroupBg fx:id="priceAmountPane" text="%createOffer.amountPriceBox.title"
|
||||
GridPane.rowSpan="3" GridPane.columnSpan="3"/>
|
||||
|
||||
<VBox alignment="CENTER" spacing="6" GridPane.rowSpan="2">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="20.0"/>
|
||||
</GridPane.margin>
|
||||
<ImageView fx:id="imageView" pickOnBounds="true"/>
|
||||
<Label fx:id="buyLabel" id="direction-icon-label"
|
||||
alignment="CENTER">
|
||||
<padding>
|
||||
<Insets top="-5.0"/>
|
||||
</padding>
|
||||
</Label>
|
||||
</VBox>
|
||||
|
||||
<HBox GridPane.columnIndex="1" alignment="CENTER_LEFT" spacing="5">
|
||||
<GridPane.margin>
|
||||
<Insets right="10.0" top="20.0"/>
|
||||
</GridPane.margin>
|
||||
<VBox spacing="4">
|
||||
<Label fx:id="amountToTradeLabel" id="input-description-label" text="%createOffer.amountPriceBox.amountDescription"
|
||||
prefWidth="170"/>
|
||||
<HBox>
|
||||
<InputTextField fx:id="amountTextField" id="text-input-with-currency-text-field"
|
||||
promptText="%createOffer.amount.prompt" prefWidth="170"
|
||||
alignment="CENTER_RIGHT"/>
|
||||
<Label fx:id="amountBtcLabel" id="currency-info-label"/>
|
||||
</HBox>
|
||||
</VBox>
|
||||
<Label text="x">
|
||||
<font>
|
||||
<Font name="Helvetica-Bold" size="20.0"/>
|
||||
</font>
|
||||
<padding>
|
||||
<Insets top="14.0" left="3" right="3"/>
|
||||
</padding>
|
||||
</Label>
|
||||
|
||||
<VBox spacing="4">
|
||||
<Label fx:id="priceDescriptionLabel" id="input-description-label" prefWidth="170"/>
|
||||
<HBox>
|
||||
<InputTextField fx:id="priceTextField" id="text-input-with-currency-text-field"
|
||||
promptText="%createOffer.price.prompt" prefWidth="170"
|
||||
alignment="CENTER_RIGHT"/>
|
||||
<Label fx:id="priceFiatLabel" id="currency-info-label" alignment="CENTER"/>
|
||||
</HBox>
|
||||
</VBox>
|
||||
|
||||
<Label text="=">
|
||||
<font>
|
||||
<Font name="Helvetica-Bold" size="20.0"/>
|
||||
</font>
|
||||
<padding>
|
||||
<Insets top="14.0" left="2" right="2"/>
|
||||
</padding>
|
||||
</Label>
|
||||
|
||||
<VBox spacing="4">
|
||||
<Label fx:id="volumeDescriptionLabel" id="input-description-label" prefWidth="170"/>
|
||||
<HBox>
|
||||
<InputTextField fx:id="volumeTextField" id="text-input-with-currency-text-field"
|
||||
prefWidth="170" alignment="CENTER_RIGHT"/>
|
||||
<Label fx:id="volumeFiatLabel" id="currency-info-label"/>
|
||||
</HBox>
|
||||
</VBox>
|
||||
</HBox>
|
||||
|
||||
<VBox GridPane.columnIndex="1" GridPane.rowIndex="1" spacing="4">
|
||||
<GridPane.margin>
|
||||
<Insets right="10.0" top="5.0" bottom="5.0"/>
|
||||
</GridPane.margin>
|
||||
<Label id="input-description-label" text="%createOffer.amountPriceBox.minAmountDescription"
|
||||
prefWidth="170.0"/>
|
||||
<HBox>
|
||||
<InputTextField fx:id="minAmountTextField" id="text-input-with-currency-text-field"
|
||||
promptText="%createOffer.minAmount.prompt" alignment="CENTER_RIGHT"
|
||||
prefWidth="170.0"/>
|
||||
<Label fx:id="minAmountBtcLabel" id="currency-info-label"/>
|
||||
</HBox>
|
||||
</VBox>
|
||||
|
||||
<InfoDisplay fx:id="amountPriceBoxInfo" gridPane="$gridPane" onAction="#onOpenGeneralHelp" rowIndex="2"/>
|
||||
|
||||
<Button fx:id="showPaymentInfoScreenButton" text="%createOffer.amountPriceBox.next" id="show-details-button"
|
||||
GridPane.columnIndex="1" GridPane.rowIndex="3" defaultButton="true"
|
||||
onAction="#onShowPayFundsScreen">
|
||||
<GridPane.margin>
|
||||
<Insets top="15.0"/>
|
||||
</GridPane.margin>
|
||||
</Button>
|
||||
|
||||
<!--
|
||||
Pay funds group
|
||||
-->
|
||||
<TitledGroupBg fx:id="payFundsPane" text="%createOffer.fundsBox.title" GridPane.rowIndex="4"
|
||||
GridPane.rowSpan="4" GridPane.columnSpan="3" visible="false"/>
|
||||
|
||||
<HBox GridPane.rowIndex="4" spacing="4" alignment="CENTER_RIGHT">
|
||||
<Label fx:id="totalToPayLabel" text="%createOffer.fundsBox.totalsNeeded" visible="false"/>
|
||||
<Label fx:id="totalToPayInfoIconLabel" visible="false"/>
|
||||
<GridPane.margin>
|
||||
<Insets top="10.0"/>
|
||||
</GridPane.margin>
|
||||
</HBox>
|
||||
<TextField fx:id="totalToPayTextField" promptText="%createOffer.fundsBox.totalsNeeded.prompt"
|
||||
GridPane.columnIndex="1" GridPane.rowIndex="4"
|
||||
editable="false" focusTraversable="false" visible="false">
|
||||
<GridPane.margin>
|
||||
<Insets top="10.0"/>
|
||||
</GridPane.margin>
|
||||
</TextField>
|
||||
|
||||
<Label fx:id="addressLabel" text="%createOffer.fundsBox.address" GridPane.rowIndex="5" visible="false"/>
|
||||
<AddressTextField fx:id="addressTextField" GridPane.columnIndex="1" GridPane.rowIndex="5"
|
||||
focusTraversable="true" visible="false"/>
|
||||
|
||||
<Label fx:id="balanceLabel" text="%createOffer.fundsBox.balance" GridPane.rowIndex="6" visible="false">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0"/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<BalanceTextField fx:id="balanceTextField" GridPane.columnIndex="1" GridPane.rowIndex="6"
|
||||
focusTraversable="false" visible="false">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0"/>
|
||||
</GridPane.margin>
|
||||
</BalanceTextField>
|
||||
|
||||
<InfoDisplay fx:id="fundsBoxInfoDisplay" gridPane="$gridPane" onAction="#onOpenFundingHelp" rowIndex="7"
|
||||
text="%createOffer.fundsBox.info" visible="false"/>
|
||||
|
||||
<HBox spacing="10" GridPane.columnIndex="1" GridPane.rowIndex="8">
|
||||
<Button fx:id="showAdvancedSettingsButton" text="%createOffer.fundsBox.showAdvanced"
|
||||
onAction="#onToggleShowAdvancedSettings" visible="false"/>
|
||||
<Button fx:id="placeOfferButton" text="%createOffer.fundsBox.placeOffer" visible="false"
|
||||
defaultButton="true"
|
||||
onAction="#onPlaceOffer"/>
|
||||
<ProgressIndicator fx:id="placeOfferSpinner" progress="0" visible="false" prefHeight="24"
|
||||
prefWidth="24"/>
|
||||
<Label fx:id="placeOfferSpinnerInfoLabel" text="%createOffer.fundsBox.placeOfferSpinnerInfo"
|
||||
visible="false">
|
||||
<padding>
|
||||
<Insets top="5.0"/>
|
||||
</padding>
|
||||
</Label>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="30" top="15.0"/>
|
||||
</GridPane.margin>
|
||||
</HBox>
|
||||
|
||||
<!--
|
||||
Advanced settings group
|
||||
-->
|
||||
<TitledGroupBg fx:id="showDetailsPane" text="%createOffer.advancedBox.title" GridPane.columnSpan="3"
|
||||
GridPane.rowIndex="9" GridPane.rowSpan="7" visible="false"/>
|
||||
|
||||
<HBox GridPane.rowIndex="9" spacing="4" alignment="CENTER_RIGHT">
|
||||
<Label fx:id="acceptedCountriesLabel" text="%createOffer.advancedBox.countries" visible="false"/>
|
||||
<Label fx:id="acceptedCountriesLabelIcon" visible="false"/>
|
||||
<GridPane.margin>
|
||||
<Insets top="0.0"/>
|
||||
</GridPane.margin>
|
||||
</HBox>
|
||||
<TextField fx:id="acceptedCountriesTextField" GridPane.columnIndex="1" GridPane.rowIndex="9"
|
||||
visible="false"
|
||||
mouseTransparent="true" editable="false" focusTraversable="false"/>
|
||||
|
||||
<HBox GridPane.rowIndex="10" spacing="4" alignment="CENTER_RIGHT">
|
||||
<Label fx:id="acceptedLanguagesLabel" text="%createOffer.advancedBox.languages" visible="false"/>
|
||||
<Label fx:id="acceptedLanguagesLabelIcon" visible="false"/>
|
||||
</HBox>
|
||||
<TextField fx:id="acceptedLanguagesTextField" GridPane.columnIndex="1" GridPane.rowIndex="10"
|
||||
mouseTransparent="true" editable="false" focusTraversable="false" visible="false"/>
|
||||
|
||||
<HBox GridPane.rowIndex="11" spacing="4" alignment="CENTER_RIGHT">
|
||||
<Label fx:id="acceptedArbitratorsLabel" text="%createOffer.advancedBox.arbitrators" visible="false"/>
|
||||
<Label fx:id="acceptedArbitratorsLabelIcon" visible="false"/>
|
||||
</HBox>
|
||||
<TextField fx:id="acceptedArbitratorsTextField" GridPane.columnIndex="1" GridPane.rowIndex="11"
|
||||
mouseTransparent="true" editable="false" focusTraversable="false" visible="false"/>
|
||||
|
||||
<Label fx:id="bankAccountTypeLabel" text="%createOffer.advancedBox.txType" GridPane.rowIndex="12"
|
||||
visible="false"/>
|
||||
<TextField fx:id="bankAccountTypeTextField" GridPane.columnIndex="1" GridPane.rowIndex="12"
|
||||
mouseTransparent="true" editable="false" focusTraversable="false" visible="false"/>
|
||||
|
||||
<Label fx:id="bankAccountCurrencyLabel" text="%createOffer.advancedBox.currency" GridPane.rowIndex="13"
|
||||
visible="false"/>
|
||||
<TextField fx:id="bankAccountCurrencyTextField" GridPane.rowIndex="13" GridPane.columnIndex="1"
|
||||
mouseTransparent="true" editable="false" focusTraversable="false" visible="false"/>
|
||||
|
||||
<Label fx:id="bankAccountCountyLabel" text="%createOffer.advancedBox.county" GridPane.rowIndex="14"
|
||||
visible="false">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0"/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<TextField fx:id="bankAccountCountyTextField" GridPane.rowIndex="14" GridPane.columnIndex="1"
|
||||
mouseTransparent="true" editable="false" focusTraversable="false" visible="false">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0"/>
|
||||
</GridPane.margin>
|
||||
</TextField>
|
||||
|
||||
<InfoDisplay fx:id="advancedInfoDisplay" gridPane="$gridPane" onAction="#onOpenAdvancedSettingsHelp"
|
||||
rowIndex="15" visible="false"
|
||||
text="%createOffer.advancedBox.info">
|
||||
</InfoDisplay>
|
||||
|
||||
<columnConstraints>
|
||||
<ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES" minWidth="200"/>
|
||||
<ColumnConstraints hgrow="ALWAYS"/>
|
||||
</columnConstraints>
|
||||
|
||||
<rowConstraints>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints minHeight="35"/>
|
||||
</rowConstraints>
|
||||
|
||||
</GridPane>
|
||||
</ScrollPane>
|
||||
</AnchorPane>
|
||||
|
|
|
@ -17,92 +17,76 @@
|
|||
|
||||
package io.bitsquare.gui.main.offer.createoffer;
|
||||
|
||||
import de.jensd.fx.fontawesome.AwesomeDude;
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
import io.bitsquare.app.BitsquareApp;
|
||||
import io.bitsquare.common.util.Tuple2;
|
||||
import io.bitsquare.common.util.Tuple3;
|
||||
import io.bitsquare.gui.Navigation;
|
||||
import io.bitsquare.gui.OverlayManager;
|
||||
import io.bitsquare.gui.common.view.ActivatableViewAndModel;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.components.AddressTextField;
|
||||
import io.bitsquare.gui.components.BalanceTextField;
|
||||
import io.bitsquare.gui.components.InfoDisplay;
|
||||
import io.bitsquare.gui.components.InputTextField;
|
||||
import io.bitsquare.gui.components.Popups;
|
||||
import io.bitsquare.gui.components.TitledGroupBg;
|
||||
import io.bitsquare.gui.main.MainView;
|
||||
import io.bitsquare.gui.main.account.AccountView;
|
||||
import io.bitsquare.gui.main.account.content.restrictions.RestrictionsView;
|
||||
import io.bitsquare.gui.main.account.content.arbitratorselection.ArbitratorSelectionView;
|
||||
import io.bitsquare.gui.main.account.settings.AccountSettingsView;
|
||||
import io.bitsquare.gui.main.help.Help;
|
||||
import io.bitsquare.gui.main.help.HelpId;
|
||||
import io.bitsquare.gui.main.offer.OfferView;
|
||||
import io.bitsquare.gui.main.portfolio.PortfolioView;
|
||||
import io.bitsquare.gui.main.portfolio.openoffer.OpenOffersView;
|
||||
import io.bitsquare.gui.util.ImageUtil;
|
||||
import io.bitsquare.gui.popups.OfferDetailsPopup;
|
||||
import io.bitsquare.gui.popups.Popup;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.gui.util.FormBuilder;
|
||||
import io.bitsquare.gui.util.Layout;
|
||||
import io.bitsquare.locale.BSResources;
|
||||
import io.bitsquare.locale.TradeCurrency;
|
||||
import io.bitsquare.payment.PaymentAccount;
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.utils.Fiat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.geometry.*;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.text.Font;
|
||||
import javafx.stage.Window;
|
||||
import javafx.util.StringConverter;
|
||||
import org.controlsfx.control.PopOver;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.HPos;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Orientation;
|
||||
import javafx.geometry.Point2D;
|
||||
import javafx.geometry.VPos;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.image.*;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.stage.Window;
|
||||
|
||||
import de.jensd.fx.fontawesome.AwesomeDude;
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
|
||||
import org.controlsfx.control.PopOver;
|
||||
import org.controlsfx.control.action.AbstractAction;
|
||||
import org.controlsfx.control.action.Action;
|
||||
import org.controlsfx.dialog.Dialog;
|
||||
|
||||
import static io.bitsquare.gui.util.FormBuilder.*;
|
||||
import static javafx.beans.binding.Bindings.createStringBinding;
|
||||
|
||||
// TODO Implement other positioning method in InoutTextField to display it over the field instead of right side
|
||||
// priceAmountHBox is too large after redesign as to be used as layoutReference.
|
||||
@FxmlView
|
||||
public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateOfferViewModel> {
|
||||
|
||||
private final Navigation navigation;
|
||||
private final BSFormatter formatter;
|
||||
private final OfferDetailsPopup offerDetailsPopup;
|
||||
|
||||
@FXML ScrollPane scrollPane;
|
||||
@FXML ImageView imageView;
|
||||
@FXML AddressTextField addressTextField;
|
||||
@FXML BalanceTextField balanceTextField;
|
||||
@FXML ProgressIndicator placeOfferSpinner;
|
||||
@FXML InfoDisplay amountPriceBoxInfo, advancedInfoDisplay, fundsBoxInfoDisplay;
|
||||
@FXML TitledGroupBg priceAmountPane, payFundsPane, showDetailsPane;
|
||||
@FXML Button showPaymentInfoScreenButton, showAdvancedSettingsButton, placeOfferButton;
|
||||
@FXML InputTextField amountTextField, minAmountTextField, priceTextField, volumeTextField;
|
||||
@FXML TextField acceptedArbitratorsTextField, totalToPayTextField, bankAccountTypeTextField,
|
||||
bankAccountCurrencyTextField, bankAccountCountyTextField, acceptedCountriesTextField,
|
||||
acceptedLanguagesTextField;
|
||||
@FXML Label buyLabel, amountToTradeLabel, addressLabel, balanceLabel, totalToPayLabel, totalToPayInfoIconLabel, bankAccountTypeLabel,
|
||||
bankAccountCurrencyLabel, bankAccountCountyLabel, acceptedCountriesLabel, acceptedCountriesLabelIcon,
|
||||
acceptedLanguagesLabel, acceptedLanguagesLabelIcon, acceptedArbitratorsLabel,
|
||||
acceptedArbitratorsLabelIcon, amountBtcLabel, priceFiatLabel, volumeFiatLabel, minAmountBtcLabel,
|
||||
priceDescriptionLabel, volumeDescriptionLabel,
|
||||
placeOfferSpinnerInfoLabel;
|
||||
private ScrollPane scrollPane;
|
||||
private GridPane gridPane;
|
||||
private ImageView imageView;
|
||||
private AddressTextField addressTextField;
|
||||
private BalanceTextField balanceTextField;
|
||||
private ProgressIndicator placeOfferSpinner;
|
||||
private TitledGroupBg payFundsPane;
|
||||
private Button showPaymentButton, placeOfferButton;
|
||||
private InputTextField amountTextField, minAmountTextField, priceTextField, volumeTextField;
|
||||
private TextField totalToPayTextField, currencyTextField;
|
||||
private Label buyLabel, amountDescriptionLabel, addressLabel, balanceLabel, totalToPayLabel, totalToPayInfoIconLabel, amountBtcLabel, priceCurrencyLabel,
|
||||
volumeCurrencyLabel, minAmountBtcLabel, priceDescriptionLabel, volumeDescriptionLabel, placeOfferSpinnerInfoLabel, currencyTextFieldLabel,
|
||||
currencyComboBoxLabel;
|
||||
private ComboBox<PaymentAccount> paymentAccountsComboBox;
|
||||
private ComboBox<TradeCurrency> currencyComboBox;
|
||||
|
||||
private ImageView expand;
|
||||
private ImageView collapse;
|
||||
private PopOver totalToPayInfoPopover;
|
||||
private boolean detailsVisible;
|
||||
private boolean advancedScreenInited;
|
||||
|
||||
private OfferView.CloseHandler closeHandler;
|
||||
|
||||
|
@ -113,9 +97,13 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
|||
private ChangeListener<Boolean> showWarningInvalidBtcDecimalPlacesListener;
|
||||
private ChangeListener<Boolean> showWarningInvalidFiatDecimalPlacesPlacesListener;
|
||||
private ChangeListener<Boolean> showWarningAdjustedVolumeListener;
|
||||
private ChangeListener<String> requestPlaceOfferErrorMessageListener;
|
||||
private ChangeListener<String> errorMessageListener;
|
||||
private ChangeListener<Boolean> isPlaceOfferSpinnerVisibleListener;
|
||||
private ChangeListener<Boolean> showTransactionPublishedScreen;
|
||||
private EventHandler<ActionEvent> paymentAccountsComboBoxSelectionHandler;
|
||||
private EventHandler<ActionEvent> currencyComboBoxSelectionHandler;
|
||||
|
||||
private int gridRow = 0;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -123,64 +111,204 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
private CreateOfferView(CreateOfferViewModel model, Navigation navigation) {
|
||||
private CreateOfferView(CreateOfferViewModel model, Navigation navigation, BSFormatter formatter, OfferDetailsPopup offerDetailsPopup) {
|
||||
super(model);
|
||||
|
||||
this.navigation = navigation;
|
||||
this.formatter = formatter;
|
||||
this.offerDetailsPopup = offerDetailsPopup;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize() {
|
||||
addScrollPane();
|
||||
addGridPane();
|
||||
addPaymentGroup();
|
||||
addAmountPriceGroup();
|
||||
addFundingGroup();
|
||||
|
||||
createListeners();
|
||||
|
||||
balanceTextField.setup(model.getWalletService(), model.address.get(), model.getFormatter());
|
||||
volumeTextField.setPromptText(BSResources.get("createOffer.volume.prompt", model.fiatCode.get()));
|
||||
balanceTextField.setup(model.address.get(), model.getFormatter());
|
||||
paymentAccountsComboBox.setConverter(new StringConverter<PaymentAccount>() {
|
||||
@Override
|
||||
public String toString(PaymentAccount paymentAccount) {
|
||||
return paymentAccount.getAccountName() + " (" + paymentAccount.getSingleTradeCurrency().getCode() + ", " +
|
||||
BSResources.get(paymentAccount.getPaymentMethod().getId()) + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaymentAccount fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doActivate() {
|
||||
protected void activate() {
|
||||
addBindings();
|
||||
addListeners();
|
||||
|
||||
buyLabel.setText(model.getDirectionLabel());
|
||||
amountDescriptionLabel.setText(model.getAmountDescription());
|
||||
addressTextField.setAddress(model.getAddressAsString());
|
||||
addressTextField.setPaymentLabel(model.getPaymentLabel());
|
||||
|
||||
paymentAccountsComboBox.setItems(model.getPaymentAccounts());
|
||||
paymentAccountsComboBox.getSelectionModel().select(model.getPaymentAccount());
|
||||
|
||||
onPaymentAccountsComboBoxSelected();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doDeactivate() {
|
||||
protected void deactivate() {
|
||||
removeBindings();
|
||||
removeListeners();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void initWithData(Offer.Direction direction, TradeCurrency tradeCurrency) {
|
||||
model.initWithData(direction, tradeCurrency);
|
||||
|
||||
if (direction == Offer.Direction.BUY) {
|
||||
imageView.setId("image-buy-large");
|
||||
} else {
|
||||
imageView.setId("image-sell-large");
|
||||
// only needed for sell
|
||||
totalToPayTextField.setPromptText(BSResources.get("createOffer.fundsBox.totalsNeeded.prompt"));
|
||||
}
|
||||
}
|
||||
|
||||
// called form parent as the view does not get notified when the tab is closed
|
||||
public void onClose() {
|
||||
// we use model.requestPlaceOfferSuccess to not react on close caused by placeOffer
|
||||
if (model.dataModel.isWalletFunded.get() && !model.requestPlaceOfferSuccess.get())
|
||||
new Popup().warning("You have already funds paid in.\nIn the <Funds/Open for withdrawal> section you can withdraw those funds.").show();
|
||||
}
|
||||
|
||||
public void setCloseHandler(OfferView.CloseHandler closeHandler) {
|
||||
this.closeHandler = closeHandler;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// UI actions
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void onPlaceOffer() {
|
||||
Offer offer = model.getOffer();
|
||||
if (model.getShowPlaceOfferConfirmation()) {
|
||||
offerDetailsPopup.onPlaceOffer(o -> model.onPlaceOffer(o)).show(offer);
|
||||
} else {
|
||||
if (model.hasAcceptedArbitrators()) {
|
||||
model.onPlaceOffer(offer);
|
||||
} else {
|
||||
new Popup().warning("You have no arbitrator selected.\n" +
|
||||
"Please select at least one arbitrator.").show();
|
||||
|
||||
navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class, ArbitratorSelectionView.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onShowFundsScreen() {
|
||||
if (!BitsquareApp.DEV_MODE) {
|
||||
if (model.getDisplaySecurityDepositInfo()) {
|
||||
new Popup().information("To ensure that both traders behave fair they need to pay a security deposit.\n\n" +
|
||||
"The deposit will stay in your local trading wallet until the offer gets accepted by another trader.\n" +
|
||||
"It will be refunded to you after the trade has successfully completed.").show();
|
||||
|
||||
model.onSecurityDepositInfoDisplayed();
|
||||
}
|
||||
}
|
||||
|
||||
showPaymentButton.setVisible(false);
|
||||
|
||||
payFundsPane.setVisible(true);
|
||||
totalToPayLabel.setVisible(true);
|
||||
totalToPayInfoIconLabel.setVisible(true);
|
||||
totalToPayTextField.setVisible(true);
|
||||
addressLabel.setVisible(true);
|
||||
addressTextField.setVisible(true);
|
||||
balanceLabel.setVisible(true);
|
||||
balanceTextField.setVisible(true);
|
||||
|
||||
setupTotalToPayInfoIconLabel();
|
||||
|
||||
model.onShowPayFundsScreen();
|
||||
}
|
||||
|
||||
private void onPaymentAccountsComboBoxSelected() {
|
||||
PaymentAccount paymentAccount = paymentAccountsComboBox.getSelectionModel().getSelectedItem();
|
||||
if (paymentAccount != null) {
|
||||
currencyComboBox.setVisible(paymentAccount.hasMultipleCurrencies());
|
||||
if (paymentAccount.hasMultipleCurrencies()) {
|
||||
currencyComboBox.setItems(FXCollections.observableArrayList(paymentAccount.getTradeCurrencies()));
|
||||
|
||||
// we select combobox following the user currency, if user currency not available in account, we select first
|
||||
TradeCurrency tradeCurrency = model.getTradeCurrency();
|
||||
if (paymentAccount.getTradeCurrencies().contains(tradeCurrency))
|
||||
currencyComboBox.getSelectionModel().select(tradeCurrency);
|
||||
else
|
||||
currencyComboBox.getSelectionModel().select(paymentAccount.getTradeCurrencies().get(0));
|
||||
|
||||
model.onPaymentAccountSelected(paymentAccount);
|
||||
} else {
|
||||
currencyTextField.setText(paymentAccount.getSingleTradeCurrency().getCodeAndName());
|
||||
model.onPaymentAccountSelected(paymentAccount);
|
||||
model.onCurrencySelected(paymentAccount.getSingleTradeCurrency());
|
||||
}
|
||||
} else {
|
||||
currencyComboBox.setVisible(false);
|
||||
currencyTextField.setText("");
|
||||
}
|
||||
}
|
||||
|
||||
private void onCurrencyComboBoxSelected() {
|
||||
model.onCurrencySelected(currencyComboBox.getSelectionModel().getSelectedItem());
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Navigation
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void close() {
|
||||
if (closeHandler != null)
|
||||
closeHandler.close();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Bindings, Listeners
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
private void addBindings() {
|
||||
amountBtcLabel.textProperty().bind(model.btcCode);
|
||||
priceFiatLabel.textProperty().bind(model.fiatCode);
|
||||
volumeFiatLabel.textProperty().bind(model.fiatCode);
|
||||
priceCurrencyLabel.textProperty().bind(createStringBinding(() ->
|
||||
model.tradeCurrencyCode.get() + "/" + model.btcCode.get(), model.btcCode, model.tradeCurrencyCode));
|
||||
volumeCurrencyLabel.textProperty().bind(model.tradeCurrencyCode);
|
||||
minAmountBtcLabel.textProperty().bind(model.btcCode);
|
||||
|
||||
priceDescriptionLabel.textProperty().bind(createStringBinding(() ->
|
||||
BSResources.get("createOffer.amountPriceBox.priceDescription", model.fiatCode.get()), model.fiatCode));
|
||||
BSResources.get("createOffer.amountPriceBox.priceDescription", model.tradeCurrencyCode.get()), model.tradeCurrencyCode));
|
||||
|
||||
volumeDescriptionLabel.textProperty().bind(createStringBinding(() -> model.volumeDescriptionLabel.get(), model.fiatCode, model.volumeDescriptionLabel));
|
||||
volumeDescriptionLabel.textProperty().bind(createStringBinding(model.volumeDescriptionLabel::get, model.tradeCurrencyCode, model
|
||||
.volumeDescriptionLabel));
|
||||
|
||||
buyLabel.textProperty().bind(model.directionLabel);
|
||||
amountToTradeLabel.textProperty().bind(model.amountToTradeLabel);
|
||||
amountTextField.textProperty().bindBidirectional(model.amount);
|
||||
minAmountTextField.textProperty().bindBidirectional(model.minAmount);
|
||||
priceTextField.textProperty().bindBidirectional(model.price);
|
||||
volumeTextField.textProperty().bindBidirectional(model.volume);
|
||||
amountPriceBoxInfo.textProperty().bind(model.amountPriceBoxInfo);
|
||||
volumeTextField.promptTextProperty().bind(model.volumePromptLabel);
|
||||
|
||||
totalToPayTextField.textProperty().bind(model.totalToPay);
|
||||
|
||||
addressTextField.amountAsCoinProperty().bind(model.totalToPayAsCoin);
|
||||
addressTextField.paymentLabelProperty().bind(model.paymentLabel);
|
||||
addressTextField.addressProperty().bind(model.addressAsString);
|
||||
|
||||
bankAccountTypeTextField.textProperty().bind(model.bankAccountType);
|
||||
bankAccountCurrencyTextField.textProperty().bind(model.bankAccountCurrency);
|
||||
bankAccountCountyTextField.textProperty().bind(model.bankAccountCounty);
|
||||
|
||||
acceptedCountriesTextField.textProperty().bind(model.acceptedCountries);
|
||||
acceptedLanguagesTextField.textProperty().bind(model.acceptedLanguages);
|
||||
acceptedArbitratorsTextField.textProperty().bind(model.acceptedArbitrators);
|
||||
|
||||
// Validation
|
||||
amountTextField.validationResultProperty().bind(model.amountValidationResult);
|
||||
|
@ -193,32 +321,31 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
|||
placeOfferButton.disableProperty().bind(model.isPlaceOfferButtonDisabled);
|
||||
|
||||
placeOfferSpinnerInfoLabel.visibleProperty().bind(model.isPlaceOfferSpinnerVisible);
|
||||
|
||||
// payment account
|
||||
currencyComboBox.prefWidthProperty().bind(paymentAccountsComboBox.widthProperty());
|
||||
currencyComboBox.managedProperty().bind(currencyComboBox.visibleProperty());
|
||||
currencyComboBoxLabel.visibleProperty().bind(currencyComboBox.visibleProperty());
|
||||
currencyComboBoxLabel.managedProperty().bind(currencyComboBox.visibleProperty());
|
||||
currencyTextField.visibleProperty().bind(currencyComboBox.visibleProperty().not());
|
||||
currencyTextField.managedProperty().bind(currencyComboBox.visibleProperty().not());
|
||||
currencyTextFieldLabel.visibleProperty().bind(currencyComboBox.visibleProperty().not());
|
||||
currencyTextFieldLabel.managedProperty().bind(currencyComboBox.visibleProperty().not());
|
||||
}
|
||||
|
||||
private void removeBindings() {
|
||||
amountBtcLabel.textProperty().unbind();
|
||||
priceFiatLabel.textProperty().unbind();
|
||||
volumeFiatLabel.textProperty().unbind();
|
||||
priceCurrencyLabel.textProperty().unbind();
|
||||
volumeCurrencyLabel.textProperty().unbind();
|
||||
minAmountBtcLabel.textProperty().unbind();
|
||||
priceDescriptionLabel.textProperty().unbind();
|
||||
volumeDescriptionLabel.textProperty().unbind();
|
||||
buyLabel.textProperty().unbind();
|
||||
amountToTradeLabel.textProperty().unbind();
|
||||
amountTextField.textProperty().unbindBidirectional(model.amount);
|
||||
minAmountTextField.textProperty().unbindBidirectional(model.minAmount);
|
||||
priceTextField.textProperty().unbindBidirectional(model.price);
|
||||
volumeTextField.textProperty().unbindBidirectional(model.volume);
|
||||
amountPriceBoxInfo.textProperty().unbind();
|
||||
totalToPayTextField.textProperty().unbind();
|
||||
addressTextField.amountAsCoinProperty().unbind();
|
||||
addressTextField.paymentLabelProperty().unbind();
|
||||
addressTextField.addressProperty().unbind();
|
||||
bankAccountTypeTextField.textProperty().unbind();
|
||||
bankAccountCurrencyTextField.textProperty().unbind();
|
||||
bankAccountCountyTextField.textProperty().unbind();
|
||||
acceptedCountriesTextField.textProperty().unbind();
|
||||
acceptedLanguagesTextField.textProperty().unbind();
|
||||
acceptedArbitratorsTextField.textProperty().unbind();
|
||||
amountTextField.validationResultProperty().unbind();
|
||||
minAmountTextField.validationResultProperty().unbind();
|
||||
priceTextField.validationResultProperty().unbind();
|
||||
|
@ -226,6 +353,15 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
|||
placeOfferButton.visibleProperty().unbind();
|
||||
placeOfferButton.disableProperty().unbind();
|
||||
placeOfferSpinnerInfoLabel.visibleProperty().unbind();
|
||||
currencyComboBox.managedProperty().unbind();
|
||||
currencyComboBoxLabel.visibleProperty().unbind();
|
||||
currencyComboBoxLabel.managedProperty().unbind();
|
||||
currencyTextField.visibleProperty().unbind();
|
||||
currencyTextField.managedProperty().unbind();
|
||||
currencyTextFieldLabel.visibleProperty().unbind();
|
||||
currencyTextFieldLabel.managedProperty().unbind();
|
||||
currencyComboBox.prefWidthProperty().unbind();
|
||||
volumeTextField.promptTextProperty().unbind();
|
||||
}
|
||||
|
||||
private void createListeners() {
|
||||
|
@ -233,6 +369,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
|||
model.onFocusOutAmountTextField(oldValue, newValue, amountTextField.getText());
|
||||
amountTextField.setText(model.amount.get());
|
||||
};
|
||||
|
||||
minAmountFocusedListener = (o, oldValue, newValue) -> {
|
||||
model.onFocusOutMinAmountTextField(oldValue, newValue, minAmountTextField.getText());
|
||||
minAmountTextField.setText(model.minAmount.get());
|
||||
|
@ -247,35 +384,38 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
|||
};
|
||||
showWarningInvalidBtcDecimalPlacesListener = (o, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
Popups.openWarningPopup(BSResources.get("shared.warning"), BSResources.get("createOffer.amountPriceBox.warning.invalidBtcDecimalPlaces"));
|
||||
new Popup().warning(BSResources.get("createOffer.amountPriceBox.warning.invalidBtcDecimalPlaces")).show();
|
||||
model.showWarningInvalidBtcDecimalPlaces.set(false);
|
||||
}
|
||||
};
|
||||
showWarningInvalidFiatDecimalPlacesPlacesListener = (o, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
Popups.openWarningPopup(BSResources.get("shared.warning"), BSResources.get("createOffer.amountPriceBox.warning.invalidFiatDecimalPlaces"));
|
||||
new Popup().warning(BSResources.get("createOffer.amountPriceBox.warning.invalidFiatDecimalPlaces")).show();
|
||||
model.showWarningInvalidFiatDecimalPlaces.set(false);
|
||||
}
|
||||
};
|
||||
showWarningAdjustedVolumeListener = (o, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
Popups.openWarningPopup(BSResources.get("shared.warning"), BSResources.get("createOffer.amountPriceBox.warning.adjustedVolume"));
|
||||
new Popup().warning(BSResources.get("createOffer.amountPriceBox.warning.adjustedVolume")).show();
|
||||
model.showWarningAdjustedVolume.set(false);
|
||||
volumeTextField.setText(model.volume.get());
|
||||
}
|
||||
};
|
||||
requestPlaceOfferErrorMessageListener = (o, oldValue, newValue) -> {
|
||||
if (newValue != null) {
|
||||
Popups.openErrorPopup(BSResources.get("shared.error"), BSResources.get("createOffer.amountPriceBox.error.message",
|
||||
model.requestPlaceOfferErrorMessage.get()));
|
||||
Popups.removeBlurContent();
|
||||
}
|
||||
errorMessageListener = (o, oldValue, newValue) -> {
|
||||
if (newValue != null)
|
||||
new Popup().error(BSResources.get("createOffer.amountPriceBox.error.message", model.errorMessage.get()) +
|
||||
"\n\nThere have no funds left your wallet yet.\n" +
|
||||
"Please try to restart you application and check your network connection to see if you can resolve the issue.")
|
||||
.show();
|
||||
};
|
||||
isPlaceOfferSpinnerVisibleListener = (ov, oldValue, newValue) -> {
|
||||
placeOfferSpinner.setProgress(newValue ? -1 : 0);
|
||||
placeOfferSpinner.setVisible(newValue);
|
||||
};
|
||||
|
||||
paymentAccountsComboBoxSelectionHandler = e -> onPaymentAccountsComboBoxSelected();
|
||||
currencyComboBoxSelectionHandler = e -> onCurrencyComboBoxSelected();
|
||||
|
||||
showTransactionPublishedScreen = (o, oldValue, newValue) -> {
|
||||
if (BitsquareApp.DEV_MODE) {
|
||||
newValue = false;
|
||||
|
@ -283,36 +423,11 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
|||
navigation.navigateTo(MainView.class, PortfolioView.class, OpenOffersView.class);
|
||||
}
|
||||
|
||||
if (newValue) {
|
||||
OverlayManager.blurContent();
|
||||
|
||||
// Dialogs are a bit limited. There is no callback for the InformationDialog button click, so we added
|
||||
// our own actions.
|
||||
List<Action> actions = new ArrayList<>();
|
||||
/* actions.add(new AbstractAction(BSResources.get("shared.copyTxId")) {
|
||||
@Override
|
||||
public void handle(ActionEvent actionEvent) {
|
||||
getProperties().put("type", "COPY");
|
||||
Utilities.copyToClipboard(model.transactionId.get());
|
||||
}
|
||||
});*/
|
||||
actions.add(new AbstractAction(BSResources.get("shared.close")) {
|
||||
@Override
|
||||
public void handle(ActionEvent actionEvent) {
|
||||
getProperties().put("type", "CLOSE");
|
||||
try {
|
||||
close();
|
||||
navigation.navigateTo(MainView.class, PortfolioView.class, OpenOffersView.class);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Dialog.Actions.CLOSE.handle(actionEvent);
|
||||
}
|
||||
});
|
||||
Popups.openInfoPopup(BSResources.get("createOffer.success.headline"),
|
||||
BSResources.get("createOffer.success.info"),
|
||||
actions);
|
||||
}
|
||||
if (newValue)
|
||||
new Popup().headLine(BSResources.get("createOffer.success.headline"))
|
||||
.message(BSResources.get("createOffer.success.info"))
|
||||
.onClose(() -> close())
|
||||
.show();
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -327,10 +442,14 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
|||
model.showWarningInvalidBtcDecimalPlaces.addListener(showWarningInvalidBtcDecimalPlacesListener);
|
||||
model.showWarningInvalidFiatDecimalPlaces.addListener(showWarningInvalidFiatDecimalPlacesPlacesListener);
|
||||
model.showWarningAdjustedVolume.addListener(showWarningAdjustedVolumeListener);
|
||||
model.requestPlaceOfferErrorMessage.addListener(requestPlaceOfferErrorMessageListener);
|
||||
model.errorMessage.addListener(errorMessageListener);
|
||||
model.isPlaceOfferSpinnerVisible.addListener(isPlaceOfferSpinnerVisibleListener);
|
||||
|
||||
model.showTransactionPublishedScreen.addListener(showTransactionPublishedScreen);
|
||||
model.requestPlaceOfferSuccess.addListener(showTransactionPublishedScreen);
|
||||
|
||||
// UI actions
|
||||
paymentAccountsComboBox.setOnAction(paymentAccountsComboBoxSelectionHandler);
|
||||
currencyComboBox.setOnAction(currencyComboBoxSelectionHandler);
|
||||
}
|
||||
|
||||
private void removeListeners() {
|
||||
|
@ -344,222 +463,221 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
|||
model.showWarningInvalidBtcDecimalPlaces.removeListener(showWarningInvalidBtcDecimalPlacesListener);
|
||||
model.showWarningInvalidFiatDecimalPlaces.removeListener(showWarningInvalidFiatDecimalPlacesPlacesListener);
|
||||
model.showWarningAdjustedVolume.removeListener(showWarningAdjustedVolumeListener);
|
||||
model.requestPlaceOfferErrorMessage.removeListener(requestPlaceOfferErrorMessageListener);
|
||||
model.errorMessage.removeListener(errorMessageListener);
|
||||
model.isPlaceOfferSpinnerVisible.removeListener(isPlaceOfferSpinnerVisibleListener);
|
||||
|
||||
model.showTransactionPublishedScreen.removeListener(showTransactionPublishedScreen);
|
||||
model.requestPlaceOfferSuccess.removeListener(showTransactionPublishedScreen);
|
||||
|
||||
// UI actions
|
||||
paymentAccountsComboBox.setOnAction(null);
|
||||
currencyComboBox.setOnAction(null);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
// Build UI elements
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void initWithData(Offer.Direction direction, Coin amount, Fiat price) {
|
||||
model.initWithData(direction, amount, price);
|
||||
|
||||
if (direction == Offer.Direction.BUY)
|
||||
imageView.setId("image-buy-large");
|
||||
else
|
||||
imageView.setId("image-sell-large");
|
||||
}
|
||||
|
||||
public void setCloseHandler(OfferView.CloseHandler closeHandler) {
|
||||
this.closeHandler = closeHandler;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// UI actions
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@FXML
|
||||
void onPlaceOffer() {
|
||||
model.onPlaceOffer();
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onScroll() {
|
||||
InputTextField.hideErrorMessageDisplay();
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onShowPayFundsScreen() {
|
||||
if (!BitsquareApp.DEV_MODE) {
|
||||
if (model.getDisplaySecurityDepositInfo()) {
|
||||
OverlayManager.blurContent();
|
||||
List<Action> actions = new ArrayList<>();
|
||||
actions.add(new AbstractAction(BSResources.get("shared.close")) {
|
||||
@Override
|
||||
public void handle(ActionEvent actionEvent) {
|
||||
getProperties().put("type", "CLOSE");
|
||||
Dialog.Actions.CLOSE.handle(actionEvent);
|
||||
}
|
||||
});
|
||||
Popups.openInfoPopup("To ensure that both traders behave fair they need to pay a security deposit.",
|
||||
"The deposit will stay in your local trading wallet until the offer gets accepted by " +
|
||||
"another trader. " +
|
||||
"\nIt will be refunded to you after the trade has successfully completed.",
|
||||
actions);
|
||||
|
||||
model.onSecurityDepositInfoDisplayed();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
priceAmountPane.setInactive();
|
||||
|
||||
showPaymentInfoScreenButton.setVisible(false);
|
||||
|
||||
payFundsPane.setVisible(true);
|
||||
totalToPayLabel.setVisible(true);
|
||||
totalToPayInfoIconLabel.setVisible(true);
|
||||
totalToPayTextField.setVisible(true);
|
||||
addressLabel.setVisible(true);
|
||||
addressTextField.setVisible(true);
|
||||
balanceLabel.setVisible(true);
|
||||
balanceTextField.setVisible(true);
|
||||
fundsBoxInfoDisplay.setVisible(true);
|
||||
|
||||
// for irc demo
|
||||
//showAdvancedSettingsButton.setVisible(true);
|
||||
showAdvancedSettingsButton.setManaged(false);
|
||||
|
||||
if (expand == null) {
|
||||
expand = ImageUtil.getImageViewById(ImageUtil.EXPAND);
|
||||
collapse = ImageUtil.getImageViewById(ImageUtil.COLLAPSE);
|
||||
}
|
||||
showAdvancedSettingsButton.setGraphic(expand);
|
||||
|
||||
setupTotalToPayInfoIconLabel();
|
||||
|
||||
model.onShowPayFundsScreen();
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onToggleShowAdvancedSettings() {
|
||||
detailsVisible = !detailsVisible;
|
||||
if (detailsVisible) {
|
||||
showAdvancedSettingsButton.setText(BSResources.get("createOffer.fundsBox.hideAdvanced"));
|
||||
showAdvancedSettingsButton.setGraphic(collapse);
|
||||
showDetailsScreen();
|
||||
}
|
||||
else {
|
||||
showAdvancedSettingsButton.setText(BSResources.get("createOffer.fundsBox.showAdvanced"));
|
||||
showAdvancedSettingsButton.setGraphic(expand);
|
||||
hideDetailsScreen();
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onOpenGeneralHelp() {
|
||||
Help.openWindow(HelpId.CREATE_OFFER_GENERAL);
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onOpenFundingHelp() {
|
||||
Help.openWindow(HelpId.CREATE_OFFER_FUNDING);
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onOpenAdvancedSettingsHelp() {
|
||||
Help.openWindow(HelpId.CREATE_OFFER_ADVANCED);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Navigation
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void openAccountSettings() {
|
||||
navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class, RestrictionsView.class);
|
||||
}
|
||||
|
||||
private void close() {
|
||||
if (closeHandler != null)
|
||||
closeHandler.close();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// State
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void showDetailsScreen() {
|
||||
payFundsPane.setInactive();
|
||||
|
||||
private void addScrollPane() {
|
||||
scrollPane = new ScrollPane();
|
||||
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
|
||||
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
|
||||
scrollPane.layout();
|
||||
|
||||
if (!advancedScreenInited) {
|
||||
initEditIcons();
|
||||
advancedScreenInited = true;
|
||||
}
|
||||
|
||||
toggleDetailsScreen(true);
|
||||
scrollPane.setFitToWidth(true);
|
||||
scrollPane.setFitToHeight(true);
|
||||
scrollPane.setOnScroll(e -> InputTextField.hideErrorMessageDisplay());
|
||||
AnchorPane.setLeftAnchor(scrollPane, 0d);
|
||||
AnchorPane.setTopAnchor(scrollPane, 0d);
|
||||
AnchorPane.setRightAnchor(scrollPane, 0d);
|
||||
AnchorPane.setBottomAnchor(scrollPane, 0d);
|
||||
root.getChildren().add(scrollPane);
|
||||
}
|
||||
|
||||
private void hideDetailsScreen() {
|
||||
payFundsPane.setActive();
|
||||
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
|
||||
scrollPane.layout();
|
||||
toggleDetailsScreen(false);
|
||||
private void addGridPane() {
|
||||
gridPane = new GridPane();
|
||||
gridPane.setPadding(new Insets(30, 25, -1, 25));
|
||||
gridPane.setHgap(5);
|
||||
gridPane.setVgap(5);
|
||||
ColumnConstraints columnConstraints1 = new ColumnConstraints();
|
||||
columnConstraints1.setHalignment(HPos.RIGHT);
|
||||
columnConstraints1.setHgrow(Priority.SOMETIMES);
|
||||
columnConstraints1.setMinWidth(200);
|
||||
ColumnConstraints columnConstraints2 = new ColumnConstraints();
|
||||
columnConstraints2.setHgrow(Priority.ALWAYS);
|
||||
gridPane.getColumnConstraints().addAll(columnConstraints1, columnConstraints2);
|
||||
scrollPane.setContent(gridPane);
|
||||
}
|
||||
|
||||
private void toggleDetailsScreen(boolean visible) {
|
||||
scrollPane.setOnScroll(scrollEvent -> {
|
||||
if (!visible)
|
||||
scrollEvent.consume();
|
||||
private void addPaymentGroup() {
|
||||
addTitledGroupBg(gridPane, gridRow, 2, "Select payment account");
|
||||
|
||||
paymentAccountsComboBox = addLabelComboBox(gridPane, gridRow, "Payment account:", Layout.FIRST_ROW_DISTANCE).second;
|
||||
paymentAccountsComboBox.setPromptText("Select payment account");
|
||||
|
||||
// we display either currencyComboBox (multi currency account) or currencyTextField (single)
|
||||
Tuple2<Label, ComboBox> currencyComboBoxTuple = addLabelComboBox(gridPane, ++gridRow, "Currency:");
|
||||
currencyComboBoxLabel = currencyComboBoxTuple.first;
|
||||
currencyComboBox = currencyComboBoxTuple.second;
|
||||
currencyComboBox.setPromptText("Select currency");
|
||||
currencyComboBox.setConverter(new StringConverter<TradeCurrency>() {
|
||||
@Override
|
||||
public String toString(TradeCurrency tradeCurrency) {
|
||||
return tradeCurrency.getCodeAndName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TradeCurrency fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// deactivate mouse wheel scrolling if hidden
|
||||
scrollPane.setVmax(visible ? scrollPane.getHeight() : 0);
|
||||
scrollPane.setVvalue(visible ? scrollPane.getHeight() : 0);
|
||||
Tuple2<Label, TextField> currencyTextFieldTuple = addLabelTextField(gridPane, gridRow, "Currency:", "", 5);
|
||||
currencyTextFieldLabel = currencyTextFieldTuple.first;
|
||||
currencyTextField = currencyTextFieldTuple.second;
|
||||
}
|
||||
|
||||
showDetailsPane.setVisible(visible);
|
||||
private void addAmountPriceGroup() {
|
||||
addTitledGroupBg(gridPane, ++gridRow, 2, "Set amount and price", Layout.GROUP_DISTANCE);
|
||||
|
||||
acceptedCountriesLabel.setVisible(visible);
|
||||
acceptedCountriesLabelIcon.setVisible(visible);
|
||||
acceptedCountriesTextField.setVisible(visible);
|
||||
acceptedLanguagesLabel.setVisible(visible);
|
||||
acceptedLanguagesLabelIcon.setVisible(visible);
|
||||
acceptedLanguagesTextField.setVisible(visible);
|
||||
acceptedArbitratorsLabel.setVisible(visible);
|
||||
acceptedArbitratorsLabelIcon.setVisible(visible);
|
||||
acceptedArbitratorsTextField.setVisible(visible);
|
||||
imageView = new ImageView();
|
||||
imageView.setPickOnBounds(true);
|
||||
buyLabel = new Label();
|
||||
buyLabel.setId("direction-icon-label");
|
||||
buyLabel.setAlignment(Pos.CENTER);
|
||||
buyLabel.setPadding(new Insets(-5, 0, 0, 0));
|
||||
VBox imageVBox = new VBox();
|
||||
imageVBox.setAlignment(Pos.CENTER);
|
||||
imageVBox.setSpacing(6);
|
||||
imageVBox.getChildren().addAll(imageView, buyLabel);
|
||||
GridPane.setRowIndex(imageVBox, gridRow);
|
||||
GridPane.setRowSpan(imageVBox, 2);
|
||||
GridPane.setMargin(imageVBox, new Insets(Layout.FIRST_ROW_AND_GROUP_DISTANCE, 10, 10, 10));
|
||||
gridPane.getChildren().add(imageVBox);
|
||||
|
||||
bankAccountTypeLabel.setVisible(visible);
|
||||
bankAccountTypeTextField.setVisible(visible);
|
||||
bankAccountCurrencyLabel.setVisible(visible);
|
||||
bankAccountCurrencyTextField.setVisible(visible);
|
||||
bankAccountCountyLabel.setVisible(visible);
|
||||
bankAccountCountyTextField.setVisible(visible);
|
||||
addAmountPriceFields();
|
||||
|
||||
advancedInfoDisplay.setVisible(visible);
|
||||
addMinAmountBox();
|
||||
|
||||
showPaymentButton = addButton(gridPane, ++gridRow, BSResources.get("createOffer.amountPriceBox.next"));
|
||||
GridPane.setMargin(showPaymentButton, new Insets(-35, 0, 0, 0));
|
||||
showPaymentButton.setId("show-details-button");
|
||||
showPaymentButton.setOnAction(e -> onShowFundsScreen());
|
||||
}
|
||||
|
||||
private void addFundingGroup() {
|
||||
// don't increase gridRow as we removed button when this gets visible
|
||||
payFundsPane = addTitledGroupBg(gridPane, gridRow, 3, BSResources.get("createOffer.fundsBox.title"), Layout.GROUP_DISTANCE);
|
||||
payFundsPane.setVisible(false);
|
||||
|
||||
totalToPayLabel = new Label(BSResources.get("createOffer.fundsBox.totalsNeeded"));
|
||||
totalToPayLabel.setVisible(false);
|
||||
totalToPayInfoIconLabel = new Label();
|
||||
totalToPayInfoIconLabel.setVisible(false);
|
||||
HBox totalToPayBox = new HBox();
|
||||
totalToPayBox.setSpacing(4);
|
||||
totalToPayBox.setAlignment(Pos.CENTER_RIGHT);
|
||||
totalToPayBox.getChildren().addAll(totalToPayLabel, totalToPayInfoIconLabel);
|
||||
GridPane.setMargin(totalToPayBox, new Insets(Layout.FIRST_ROW_AND_GROUP_DISTANCE, 0, 0, 0));
|
||||
GridPane.setRowIndex(totalToPayBox, gridRow);
|
||||
gridPane.getChildren().add(totalToPayBox);
|
||||
totalToPayTextField = new TextField();
|
||||
totalToPayTextField.setEditable(false);
|
||||
totalToPayTextField.setFocusTraversable(false);
|
||||
totalToPayTextField.setVisible(false);
|
||||
GridPane.setRowIndex(totalToPayTextField, gridRow);
|
||||
GridPane.setColumnIndex(totalToPayTextField, 1);
|
||||
GridPane.setMargin(totalToPayTextField, new Insets(Layout.FIRST_ROW_AND_GROUP_DISTANCE, 0, 0, 0));
|
||||
gridPane.getChildren().add(totalToPayTextField);
|
||||
|
||||
Tuple2<Label, AddressTextField> addressTuple = addLabelAddressTextField(gridPane, ++gridRow, BSResources.get("createOffer.fundsBox.address"));
|
||||
addressLabel = addressTuple.first;
|
||||
addressLabel.setVisible(false);
|
||||
addressTextField = addressTuple.second;
|
||||
addressTextField.setVisible(false);
|
||||
|
||||
Tuple2<Label, BalanceTextField> balanceTuple = addLabelBalanceTextField(gridPane, ++gridRow, BSResources.get("createOffer.fundsBox.balance"));
|
||||
balanceLabel = balanceTuple.first;
|
||||
balanceLabel.setVisible(false);
|
||||
balanceTextField = balanceTuple.second;
|
||||
balanceTextField.setVisible(false);
|
||||
|
||||
Tuple3<Button, ProgressIndicator, Label> placeOfferTuple = addButtonWithStatus(gridPane, ++gridRow, BSResources.get("createOffer.fundsBox.placeOffer"));
|
||||
placeOfferButton = placeOfferTuple.first;
|
||||
placeOfferButton.setVisible(false);
|
||||
placeOfferButton.setOnAction(e -> onPlaceOffer());
|
||||
placeOfferSpinner = placeOfferTuple.second;
|
||||
placeOfferSpinnerInfoLabel = placeOfferTuple.third;
|
||||
placeOfferSpinnerInfoLabel.setText(BSResources.get("createOffer.fundsBox.placeOfferSpinnerInfo"));
|
||||
placeOfferSpinnerInfoLabel.setVisible(false);
|
||||
}
|
||||
|
||||
private void addAmountPriceFields() {
|
||||
// amountBox
|
||||
Tuple3<HBox, InputTextField, Label> amountValueCurrencyBoxTuple = FormBuilder.getValueCurrencyBox(BSResources.get("createOffer.amount.prompt"));
|
||||
HBox amountValueCurrencyBox = amountValueCurrencyBoxTuple.first;
|
||||
amountTextField = amountValueCurrencyBoxTuple.second;
|
||||
amountBtcLabel = amountValueCurrencyBoxTuple.third;
|
||||
Tuple2<Label, VBox> amountInputBoxTuple = getTradeInputBox(amountValueCurrencyBox, model.getAmountDescription());
|
||||
amountDescriptionLabel = amountInputBoxTuple.first;
|
||||
VBox amountBox = amountInputBoxTuple.second;
|
||||
|
||||
// x
|
||||
Label xLabel = new Label("x");
|
||||
xLabel.setFont(Font.font("Helvetica-Bold", 20));
|
||||
xLabel.setPadding(new Insets(14, 3, 0, 3));
|
||||
|
||||
// price
|
||||
Tuple3<HBox, InputTextField, Label> priceValueCurrencyBoxTuple = FormBuilder.getValueCurrencyBox(BSResources.get("createOffer.price.prompt"));
|
||||
HBox priceValueCurrencyBox = priceValueCurrencyBoxTuple.first;
|
||||
priceTextField = priceValueCurrencyBoxTuple.second;
|
||||
priceCurrencyLabel = priceValueCurrencyBoxTuple.third;
|
||||
Tuple2<Label, VBox> priceInputBoxTuple = getTradeInputBox(priceValueCurrencyBox, BSResources.get("createOffer.amountPriceBox.priceDescription"));
|
||||
priceDescriptionLabel = priceInputBoxTuple.first;
|
||||
VBox priceBox = priceInputBoxTuple.second;
|
||||
|
||||
// =
|
||||
Label resultLabel = new Label("=");
|
||||
resultLabel.setFont(Font.font("Helvetica-Bold", 20));
|
||||
resultLabel.setPadding(new Insets(14, 2, 0, 2));
|
||||
|
||||
// volume
|
||||
Tuple3<HBox, InputTextField, Label> volumeValueCurrencyBoxTuple = FormBuilder.getValueCurrencyBox(BSResources.get("createOffer.volume.prompt"));
|
||||
HBox volumeValueCurrencyBox = volumeValueCurrencyBoxTuple.first;
|
||||
volumeTextField = volumeValueCurrencyBoxTuple.second;
|
||||
volumeCurrencyLabel = volumeValueCurrencyBoxTuple.third;
|
||||
Tuple2<Label, VBox> volumeInputBoxTuple = getTradeInputBox(volumeValueCurrencyBox, model.volumeDescriptionLabel.get());
|
||||
volumeDescriptionLabel = volumeInputBoxTuple.first;
|
||||
VBox volumeBox = volumeInputBoxTuple.second;
|
||||
|
||||
HBox hBox = new HBox();
|
||||
hBox.setSpacing(5);
|
||||
hBox.setAlignment(Pos.CENTER_LEFT);
|
||||
hBox.getChildren().addAll(amountBox, xLabel, priceBox, resultLabel, volumeBox);
|
||||
GridPane.setRowIndex(hBox, gridRow);
|
||||
GridPane.setColumnIndex(hBox, 1);
|
||||
GridPane.setMargin(hBox, new Insets(Layout.FIRST_ROW_AND_GROUP_DISTANCE, 10, 0, 0));
|
||||
gridPane.getChildren().add(hBox);
|
||||
}
|
||||
|
||||
private void addMinAmountBox() {
|
||||
Tuple3<HBox, InputTextField, Label> amountValueCurrencyBoxTuple = getValueCurrencyBox(BSResources.get("createOffer.amount.prompt"));
|
||||
HBox amountValueCurrencyBox = amountValueCurrencyBoxTuple.first;
|
||||
minAmountTextField = amountValueCurrencyBoxTuple.second;
|
||||
minAmountBtcLabel = amountValueCurrencyBoxTuple.third;
|
||||
|
||||
Tuple2<Label, VBox> amountInputBoxTuple = getTradeInputBox(amountValueCurrencyBox, BSResources.get("createOffer.amountPriceBox" +
|
||||
".minAmountDescription"));
|
||||
VBox box = amountInputBoxTuple.second;
|
||||
GridPane.setRowIndex(box, ++gridRow);
|
||||
GridPane.setColumnIndex(box, 1);
|
||||
GridPane.setMargin(box, new Insets(5, 10, 5, 0));
|
||||
gridPane.getChildren().add(box);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
// PayInfo
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void initEditIcons() {
|
||||
acceptedCountriesLabelIcon.setId("clickable-icon");
|
||||
AwesomeDude.setIcon(acceptedCountriesLabelIcon, AwesomeIcon.EDIT_SIGN);
|
||||
Tooltip.install(acceptedCountriesLabelIcon, new Tooltip(BSResources.get("shared.openSettings")));
|
||||
acceptedCountriesLabelIcon.setOnMouseClicked(e -> openAccountSettings());
|
||||
|
||||
acceptedLanguagesLabelIcon.setId("clickable-icon");
|
||||
AwesomeDude.setIcon(acceptedLanguagesLabelIcon, AwesomeIcon.EDIT_SIGN);
|
||||
Tooltip.install(acceptedLanguagesLabelIcon, new Tooltip(BSResources.get("shared.openSettings")));
|
||||
acceptedLanguagesLabelIcon.setOnMouseClicked(e -> openAccountSettings());
|
||||
|
||||
acceptedArbitratorsLabelIcon.setId("clickable-icon");
|
||||
AwesomeDude.setIcon(acceptedArbitratorsLabelIcon, AwesomeIcon.EDIT_SIGN);
|
||||
Tooltip.install(acceptedArbitratorsLabelIcon, new Tooltip(BSResources.get("shared.openSettings")));
|
||||
acceptedArbitratorsLabelIcon.setOnMouseClicked(e -> openAccountSettings());
|
||||
}
|
||||
|
||||
private void setupTotalToPayInfoIconLabel() {
|
||||
totalToPayInfoIconLabel.setId("clickable-icon");
|
||||
AwesomeDude.setIcon(totalToPayInfoIconLabel, AwesomeIcon.QUESTION_SIGN);
|
||||
|
@ -579,25 +697,18 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
|||
infoGridPane.setPadding(new Insets(10, 10, 10, 10));
|
||||
|
||||
int i = 0;
|
||||
if (model.isSeller()) {
|
||||
addPayInfoEntry(infoGridPane, i++,
|
||||
BSResources.get("createOffer.fundsBox.tradeAmount"),
|
||||
model.tradeAmount.get());
|
||||
}
|
||||
addPayInfoEntry(infoGridPane, i++,
|
||||
BSResources.get("createOffer.fundsBox.securityDeposit"),
|
||||
model.securityDeposit.get());
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("createOffer.fundsBox.offerFee"),
|
||||
model.offerFee.get());
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("createOffer.fundsBox.networkFee"),
|
||||
model.networkFee.get());
|
||||
if (model.isSeller())
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("createOffer.fundsBox.tradeAmount"), model.tradeAmount.get());
|
||||
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("createOffer.fundsBox.securityDeposit"), model.getSecurityDeposit());
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("createOffer.fundsBox.offerFee"), model.getOfferFee());
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("createOffer.fundsBox.networkFee"), model.getNetworkFee());
|
||||
Separator separator = new Separator();
|
||||
separator.setOrientation(Orientation.HORIZONTAL);
|
||||
separator.setStyle("-fx-background: #666;");
|
||||
GridPane.setConstraints(separator, 1, i++);
|
||||
infoGridPane.getChildren().add(separator);
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("createOffer.fundsBox.total"),
|
||||
model.totalToPay.get());
|
||||
addPayInfoEntry(infoGridPane, i, BSResources.get("createOffer.fundsBox.total"), model.totalToPay.get());
|
||||
totalToPayInfoPopover = new PopOver(infoGridPane);
|
||||
if (totalToPayInfoIconLabel.getScene() != null) {
|
||||
totalToPayInfoPopover.setDetachable(false);
|
||||
|
@ -622,7 +733,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
|||
private Point2D getPopupPosition() {
|
||||
Window window = totalToPayInfoIconLabel.getScene().getWindow();
|
||||
Point2D point = totalToPayInfoIconLabel.localToScene(0, 0);
|
||||
double x = point.getX() + window.getX() + totalToPayInfoIconLabel.getWidth() - 3;
|
||||
double x = point.getX() + window.getX() + totalToPayInfoIconLabel.getWidth() + 2;
|
||||
double y = point.getY() + window.getY() + Math.floor(totalToPayInfoIconLabel.getHeight() / 2) - 9;
|
||||
return new Point2D(x, y);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
package io.bitsquare.gui.main.offer.createoffer;
|
||||
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.arbitration.Arbitrator;
|
||||
import io.bitsquare.gui.common.model.ActivatableWithDataModel;
|
||||
import io.bitsquare.gui.common.model.ViewModel;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
|
@ -25,57 +25,42 @@ import io.bitsquare.gui.util.validation.BtcValidator;
|
|||
import io.bitsquare.gui.util.validation.FiatValidator;
|
||||
import io.bitsquare.gui.util.validation.InputValidator;
|
||||
import io.bitsquare.locale.BSResources;
|
||||
import io.bitsquare.locale.TradeCurrency;
|
||||
import io.bitsquare.payment.PaymentAccount;
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
|
||||
import javafx.beans.property.*;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.utils.Fiat;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import java.util.List;
|
||||
|
||||
import static javafx.beans.binding.Bindings.createStringBinding;
|
||||
|
||||
class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel> implements ViewModel {
|
||||
|
||||
private final BtcValidator btcValidator;
|
||||
private final BSFormatter formatter;
|
||||
private final FiatValidator fiatValidator;
|
||||
|
||||
private String amountDescription;
|
||||
private String directionLabel;
|
||||
private String addressAsString;
|
||||
private final String paymentLabel;
|
||||
|
||||
final StringProperty amount = new SimpleStringProperty();
|
||||
final StringProperty minAmount = new SimpleStringProperty();
|
||||
final StringProperty price = new SimpleStringProperty();
|
||||
final StringProperty volume = new SimpleStringProperty();
|
||||
final StringProperty volumeDescriptionLabel = new SimpleStringProperty();
|
||||
final StringProperty amountPriceBoxInfo = new SimpleStringProperty();
|
||||
final StringProperty securityDeposit = new SimpleStringProperty();
|
||||
final StringProperty volumePromptLabel = new SimpleStringProperty();
|
||||
final StringProperty tradeAmount = new SimpleStringProperty();
|
||||
final StringProperty totalToPay = new SimpleStringProperty();
|
||||
final StringProperty directionLabel = new SimpleStringProperty();
|
||||
final StringProperty amountToTradeLabel = new SimpleStringProperty();
|
||||
final StringProperty offerFee = new SimpleStringProperty();
|
||||
final StringProperty networkFee = new SimpleStringProperty();
|
||||
final StringProperty bankAccountType = new SimpleStringProperty();
|
||||
final StringProperty bankAccountCurrency = new SimpleStringProperty();
|
||||
final StringProperty bankAccountCounty = new SimpleStringProperty();
|
||||
final StringProperty acceptedCountries = new SimpleStringProperty();
|
||||
final StringProperty acceptedLanguages = new SimpleStringProperty();
|
||||
final StringProperty acceptedArbitrators = new SimpleStringProperty();
|
||||
final StringProperty addressAsString = new SimpleStringProperty();
|
||||
final StringProperty paymentLabel = new SimpleStringProperty();
|
||||
final StringProperty transactionId = new SimpleStringProperty();
|
||||
final StringProperty requestPlaceOfferErrorMessage = new SimpleStringProperty();
|
||||
final StringProperty errorMessage = new SimpleStringProperty();
|
||||
final StringProperty btcCode = new SimpleStringProperty();
|
||||
final StringProperty fiatCode = new SimpleStringProperty();
|
||||
final StringProperty tradeCurrencyCode = new SimpleStringProperty();
|
||||
|
||||
final BooleanProperty isPlaceOfferButtonVisible = new SimpleBooleanProperty(false);
|
||||
final BooleanProperty isPlaceOfferButtonDisabled = new SimpleBooleanProperty(true);
|
||||
|
@ -83,7 +68,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
final BooleanProperty showWarningAdjustedVolume = new SimpleBooleanProperty();
|
||||
final BooleanProperty showWarningInvalidFiatDecimalPlaces = new SimpleBooleanProperty();
|
||||
final BooleanProperty showWarningInvalidBtcDecimalPlaces = new SimpleBooleanProperty();
|
||||
final BooleanProperty showTransactionPublishedScreen = new SimpleBooleanProperty();
|
||||
final BooleanProperty requestPlaceOfferSuccess = new SimpleBooleanProperty();
|
||||
|
||||
final ObjectProperty<InputValidator.ValidationResult> amountValidationResult = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<InputValidator.ValidationResult> minAmountValidationResult = new
|
||||
|
@ -106,9 +91,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
private ChangeListener<Boolean> isWalletFundedListener;
|
||||
private ChangeListener<Boolean> requestPlaceOfferSuccessListener;
|
||||
private ChangeListener<String> requestPlaceOfferErrorMessageListener;
|
||||
private InvalidationListener acceptedCountriesListener;
|
||||
private InvalidationListener acceptedLanguageCodesListener;
|
||||
private InvalidationListener acceptedArbitratorsListener;
|
||||
private ChangeListener<String> errorMessageListener;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -124,71 +107,72 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
this.btcValidator = btcValidator;
|
||||
this.formatter = formatter;
|
||||
|
||||
paymentLabel.set(BSResources.get("createOffer.fundsBox.paymentLabel", dataModel.getOfferId()));
|
||||
paymentLabel = BSResources.get("createOffer.fundsBox.paymentLabel", dataModel.getOfferId());
|
||||
|
||||
if (dataModel.getAddressEntry() != null) {
|
||||
addressAsString.set(dataModel.getAddressEntry().getAddress().toString());
|
||||
addressAsString = dataModel.getAddressEntry().getAddress().toString();
|
||||
address.set(dataModel.getAddressEntry().getAddress());
|
||||
}
|
||||
createListeners();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doActivate() {
|
||||
protected void activate() {
|
||||
addBindings();
|
||||
addListeners();
|
||||
|
||||
updateButtonDisableState();
|
||||
|
||||
if (dataModel.getDirection() == Offer.Direction.BUY) {
|
||||
directionLabel = BSResources.get("shared.buyBitcoin");
|
||||
amountDescription = BSResources.get("createOffer.amountPriceBox.amountDescription", BSResources.get("shared.buy"));
|
||||
} else {
|
||||
directionLabel = BSResources.get("shared.sellBitcoin");
|
||||
amountDescription = BSResources.get("createOffer.amountPriceBox.amountDescription", BSResources.get("shared.sell"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doDeactivate() {
|
||||
protected void deactivate() {
|
||||
removeBindings();
|
||||
removeListeners();
|
||||
}
|
||||
|
||||
private void addBindings() {
|
||||
if (dataModel.getDirection() == Offer.Direction.BUY) {
|
||||
volumeDescriptionLabel.bind(createStringBinding(
|
||||
() -> BSResources.get("createOffer.amountPriceBox.buy.volumeDescription", dataModel.tradeCurrencyCode.get()),
|
||||
dataModel.tradeCurrencyCode));
|
||||
} else {
|
||||
volumeDescriptionLabel.bind(createStringBinding(
|
||||
() -> BSResources.get("createOffer.amountPriceBox.sell.volumeDescription", dataModel.tradeCurrencyCode.get()),
|
||||
dataModel.tradeCurrencyCode));
|
||||
}
|
||||
volumePromptLabel.bind(createStringBinding(
|
||||
() -> BSResources.get("createOffer.volume.prompt", dataModel.tradeCurrencyCode.get()),
|
||||
dataModel.tradeCurrencyCode));
|
||||
|
||||
totalToPay.bind(createStringBinding(() -> formatter.formatCoinWithCode(dataModel.totalToPayAsCoin.get()),
|
||||
dataModel.totalToPayAsCoin));
|
||||
securityDeposit.bind(createStringBinding(() -> formatter.formatCoinWithCode(dataModel.securityDepositAsCoin.get()),
|
||||
dataModel.securityDepositAsCoin));
|
||||
|
||||
|
||||
tradeAmount.bind(createStringBinding(() -> formatter.formatCoinWithCode(dataModel.amountAsCoin.get()),
|
||||
dataModel.amountAsCoin));
|
||||
|
||||
totalToPayAsCoin.bind(dataModel.totalToPayAsCoin);
|
||||
|
||||
offerFee.bind(createStringBinding(() -> formatter.formatCoinWithCode(dataModel.offerFeeAsCoin.get()),
|
||||
dataModel.offerFeeAsCoin));
|
||||
networkFee.bind(createStringBinding(() -> formatter.formatCoinWithCode(dataModel.networkFeeAsCoin.get()),
|
||||
dataModel.offerFeeAsCoin));
|
||||
|
||||
bankAccountType.bind(createStringBinding(() -> BSResources.get(dataModel.bankAccountType.get()),
|
||||
dataModel.bankAccountType));
|
||||
bankAccountCurrency.bind(dataModel.bankAccountCurrency);
|
||||
bankAccountCounty.bind(dataModel.bankAccountCounty);
|
||||
|
||||
requestPlaceOfferErrorMessage.bind(dataModel.requestPlaceOfferErrorMessage);
|
||||
showTransactionPublishedScreen.bind(dataModel.requestPlaceOfferSuccess);
|
||||
transactionId.bind(dataModel.transactionId);
|
||||
|
||||
btcCode.bind(dataModel.btcCode);
|
||||
fiatCode.bind(dataModel.fiatCode);
|
||||
tradeCurrencyCode.bind(dataModel.tradeCurrencyCode);
|
||||
}
|
||||
|
||||
private void removeBindings() {
|
||||
totalToPay.unbind();
|
||||
securityDeposit.unbind();
|
||||
tradeAmount.unbind();
|
||||
totalToPayAsCoin.unbind();
|
||||
offerFee.unbind();
|
||||
networkFee.unbind();
|
||||
bankAccountType.unbind();
|
||||
bankAccountCurrency.unbind();
|
||||
bankAccountCounty.unbind();
|
||||
requestPlaceOfferErrorMessage.unbind();
|
||||
showTransactionPublishedScreen.unbind();
|
||||
transactionId.unbind();
|
||||
btcCode.unbind();
|
||||
fiatCode.unbind();
|
||||
tradeCurrencyCode.unbind();
|
||||
volumeDescriptionLabel.unbind();
|
||||
volumePromptLabel.unbind();
|
||||
}
|
||||
|
||||
private void createListeners() {
|
||||
|
@ -226,24 +210,15 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
priceAsFiatListener = (ov, oldValue, newValue) -> price.set(formatter.formatFiat(newValue));
|
||||
volumeAsFiatListener = (ov, oldValue, newValue) -> volume.set(formatter.formatFiat(newValue));
|
||||
|
||||
isWalletFundedListener = (ov, oldValue, newValue) -> {
|
||||
updateButtonDisableState();
|
||||
};
|
||||
isWalletFundedListener = (ov, oldValue, newValue) -> updateButtonDisableState();
|
||||
requestPlaceOfferSuccessListener = (ov, oldValue, newValue) -> {
|
||||
isPlaceOfferButtonVisible.set(!newValue);
|
||||
isPlaceOfferSpinnerVisible.set(false);
|
||||
};
|
||||
requestPlaceOfferErrorMessageListener = (ov, oldValue, newValue) -> {
|
||||
if (newValue != null) {
|
||||
if (newValue != null)
|
||||
isPlaceOfferSpinnerVisible.set(false);
|
||||
}
|
||||
};
|
||||
|
||||
acceptedCountriesListener = (Observable o) ->
|
||||
acceptedCountries.set(formatter.countryLocalesToString(dataModel.acceptedCountries));
|
||||
acceptedLanguageCodesListener = (Observable o) -> acceptedLanguages.set(formatter.languageCodesToString(dataModel.acceptedLanguageCodes));
|
||||
acceptedArbitratorsListener = (Observable o) -> acceptedArbitrators.set(formatter.arbitratorsToNames(dataModel.acceptedArbitrators));
|
||||
|
||||
}
|
||||
|
||||
private void addListeners() {
|
||||
|
@ -261,13 +236,9 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
dataModel.volumeAsFiat.addListener(volumeAsFiatListener);
|
||||
|
||||
dataModel.isWalletFunded.addListener(isWalletFundedListener);
|
||||
dataModel.requestPlaceOfferSuccess.addListener(requestPlaceOfferSuccessListener);
|
||||
dataModel.requestPlaceOfferErrorMessage.addListener(requestPlaceOfferErrorMessageListener);
|
||||
requestPlaceOfferSuccess.addListener(requestPlaceOfferSuccessListener);
|
||||
errorMessage.addListener(requestPlaceOfferErrorMessageListener);
|
||||
|
||||
// ObservableLists
|
||||
dataModel.acceptedCountries.addListener(acceptedCountriesListener);
|
||||
dataModel.acceptedLanguageCodes.addListener(acceptedLanguageCodesListener);
|
||||
dataModel.acceptedArbitrators.addListener(acceptedArbitratorsListener);
|
||||
}
|
||||
|
||||
private void removeListeners() {
|
||||
|
@ -283,13 +254,12 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
dataModel.volumeAsFiat.removeListener(volumeAsFiatListener);
|
||||
|
||||
dataModel.isWalletFunded.removeListener(isWalletFundedListener);
|
||||
dataModel.requestPlaceOfferSuccess.removeListener(requestPlaceOfferSuccessListener);
|
||||
dataModel.requestPlaceOfferErrorMessage.removeListener(requestPlaceOfferErrorMessageListener);
|
||||
requestPlaceOfferSuccess.removeListener(requestPlaceOfferSuccessListener);
|
||||
errorMessage.removeListener(requestPlaceOfferErrorMessageListener);
|
||||
|
||||
// ObservableLists
|
||||
dataModel.acceptedCountries.removeListener(acceptedCountriesListener);
|
||||
dataModel.acceptedLanguageCodes.removeListener(acceptedLanguageCodesListener);
|
||||
dataModel.acceptedArbitrators.removeListener(acceptedArbitratorsListener);
|
||||
Offer offer = dataModel.getOffer();
|
||||
if (offer != null && errorMessageListener != null)
|
||||
offer.errorMessageProperty().removeListener(errorMessageListener);
|
||||
}
|
||||
|
||||
|
||||
|
@ -297,43 +267,8 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void initWithData(Offer.Direction direction, Coin amountAsCoin, Fiat priceAsFiat) {
|
||||
addListeners();
|
||||
|
||||
dataModel.initWithData(direction);
|
||||
|
||||
if (dataModel.getDirection() == Offer.Direction.BUY) {
|
||||
directionLabel.set(BSResources.get("shared.buyBitcoin"));
|
||||
amountToTradeLabel.set(BSResources.get("createOffer.amountPriceBox.amountDescription", BSResources.get("shared.buy")));
|
||||
volumeDescriptionLabel.set(BSResources.get("createOffer.amountPriceBox.buy.volumeDescription", fiatCode.get()));
|
||||
amountPriceBoxInfo.set(BSResources.get("createOffer.amountPriceBox.buy.info"));
|
||||
}
|
||||
else {
|
||||
directionLabel.set(BSResources.get("shared.sellBitcoin"));
|
||||
amountToTradeLabel.set(BSResources.get("createOffer.amountPriceBox.amountDescription", BSResources.get("shared.sell")));
|
||||
volumeDescriptionLabel.set(BSResources.get("createOffer.amountPriceBox.sell.volumeDescription", fiatCode.get()));
|
||||
amountPriceBoxInfo.set(BSResources.get("createOffer.amountPriceBox.sell.info"));
|
||||
}
|
||||
|
||||
|
||||
// apply only if valid
|
||||
boolean amountValid = false;
|
||||
if (amountAsCoin != null && isBtcInputValid(amountAsCoin.toPlainString())
|
||||
.isValid) {
|
||||
dataModel.amountAsCoin.set(amountAsCoin);
|
||||
dataModel.minAmountAsCoin.set(amountAsCoin);
|
||||
amountValid = true;
|
||||
}
|
||||
|
||||
// apply only if valid
|
||||
boolean priceValid = false;
|
||||
if (priceAsFiat != null && isBtcInputValid(priceAsFiat.toPlainString()).isValid) {
|
||||
dataModel.priceAsFiat.set(formatter.parseToFiatWith2Decimals(priceAsFiat.toPlainString()));
|
||||
priceValid = true;
|
||||
}
|
||||
|
||||
if (amountValid && priceValid)
|
||||
dataModel.calculateTotalToPay();
|
||||
void initWithData(Offer.Direction direction, TradeCurrency tradeCurrency) {
|
||||
dataModel.initWithData(direction, tradeCurrency);
|
||||
}
|
||||
|
||||
|
||||
|
@ -341,16 +276,26 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
// UI actions
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void onPlaceOffer() {
|
||||
dataModel.requestPlaceOfferErrorMessage.set(null);
|
||||
dataModel.requestPlaceOfferSuccess.set(false);
|
||||
|
||||
void onPlaceOffer(Offer offer) {
|
||||
errorMessage.set(null);
|
||||
isPlaceOfferSpinnerVisible.set(true);
|
||||
requestPlaceOfferSuccess.set(false);
|
||||
|
||||
dataModel.onPlaceOffer();
|
||||
errorMessageListener = (observable, oldValue, newValue) -> {
|
||||
if (newValue != null) {
|
||||
if (offer.getState() == Offer.State.OFFER_FEE_PAID)
|
||||
this.errorMessage.set(newValue +
|
||||
"\n\nThe create offer fee is already paid. In the worst case you have lost that fee. " +
|
||||
"We are sorry about that but keep in mind it is a very small amount.\n" +
|
||||
"Please try to restart you application and check your network connection to see if you can resolve the issue.");
|
||||
else
|
||||
this.errorMessage.set(newValue);
|
||||
}
|
||||
};
|
||||
offer.errorMessageProperty().addListener(errorMessageListener);
|
||||
dataModel.onPlaceOffer(offer, (transaction) -> requestPlaceOfferSuccess.set(true));
|
||||
}
|
||||
|
||||
|
||||
void onShowPayFundsScreen() {
|
||||
isPlaceOfferButtonVisible.set(true);
|
||||
}
|
||||
|
@ -359,6 +304,14 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
dataModel.onSecurityDepositInfoDisplayed();
|
||||
}
|
||||
|
||||
public void onPaymentAccountSelected(PaymentAccount paymentAccount) {
|
||||
dataModel.onPaymentAccountSelected(paymentAccount);
|
||||
}
|
||||
|
||||
public void onCurrencySelected(TradeCurrency tradeCurrency) {
|
||||
dataModel.onCurrencySelected(tradeCurrency);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Handle focus
|
||||
|
@ -420,7 +373,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
boolean isValid = result.isValid;
|
||||
priceValidationResult.set(result);
|
||||
if (isValid) {
|
||||
showWarningInvalidFiatDecimalPlaces.set(!formatter.hasFiatValidDecimals(userInput));
|
||||
showWarningInvalidFiatDecimalPlaces.set(!formatter.hasFiatValidDecimals(userInput, dataModel.tradeCurrencyCode.get()));
|
||||
setPriceToModel();
|
||||
price.set(formatter.formatFiat(dataModel.priceAsFiat.get()));
|
||||
|
||||
|
@ -431,10 +384,10 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
|
||||
void onFocusOutVolumeTextField(boolean oldValue, boolean newValue, String userInput) {
|
||||
if (oldValue && !newValue) {
|
||||
InputValidator.ValidationResult result = isBtcInputValid(volume.get());
|
||||
InputValidator.ValidationResult result = isFiatInputValid(volume.get());
|
||||
volumeValidationResult.set(result);
|
||||
if (result.isValid) {
|
||||
showWarningInvalidFiatDecimalPlaces.set(!formatter.hasFiatValidDecimals(userInput));
|
||||
showWarningInvalidFiatDecimalPlaces.set(!formatter.hasFiatValidDecimals(userInput, dataModel.tradeCurrencyCode.get()));
|
||||
setVolumeToModel();
|
||||
volume.set(formatter.formatFiat(dataModel.volumeAsFiat.get()));
|
||||
|
||||
|
@ -442,7 +395,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
|
||||
// must be placed after calculateAmount (btc value has been adjusted in case the calculation leads to
|
||||
// invalid decimal places for the amount value
|
||||
showWarningAdjustedVolume.set(!formatter.formatFiat(formatter.parseToFiatWith2Decimals(userInput))
|
||||
showWarningAdjustedVolume.set(!formatter.formatFiat(formatter.parseToFiatWith2Decimals(userInput, dataModel.tradeCurrencyCode.get()))
|
||||
.equals(volume
|
||||
.get()));
|
||||
}
|
||||
|
@ -454,10 +407,6 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
// Getters
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
WalletService getWalletService() {
|
||||
return dataModel.getWalletService();
|
||||
}
|
||||
|
||||
BSFormatter getFormatter() {
|
||||
return formatter;
|
||||
}
|
||||
|
@ -470,6 +419,53 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
return dataModel.getDirection() == Offer.Direction.SELL;
|
||||
}
|
||||
|
||||
public ObservableList<PaymentAccount> getPaymentAccounts() {
|
||||
return dataModel.paymentAccounts;
|
||||
}
|
||||
|
||||
public TradeCurrency getTradeCurrency() {
|
||||
return dataModel.getTradeCurrency();
|
||||
}
|
||||
|
||||
public String getOfferFee() {
|
||||
return formatter.formatCoinWithCode(dataModel.getOfferFeeAsCoin());
|
||||
}
|
||||
|
||||
public String getNetworkFee() {
|
||||
return formatter.formatCoinWithCode(dataModel.getNetworkFeeAsCoin());
|
||||
}
|
||||
|
||||
public String getSecurityDeposit() {
|
||||
return formatter.formatCoinWithCode(dataModel.getSecurityDepositAsCoin());
|
||||
}
|
||||
|
||||
public PaymentAccount getPaymentAccount() {
|
||||
return dataModel.getPaymentAccount();
|
||||
}
|
||||
|
||||
public String getAmountDescription() {
|
||||
return amountDescription;
|
||||
}
|
||||
|
||||
public String getDirectionLabel() {
|
||||
return directionLabel;
|
||||
}
|
||||
|
||||
public String getAddressAsString() {
|
||||
return addressAsString;
|
||||
}
|
||||
|
||||
public String getPaymentLabel() {
|
||||
return paymentLabel;
|
||||
}
|
||||
|
||||
public Offer getOffer() {
|
||||
return dataModel.getOffer();
|
||||
}
|
||||
|
||||
boolean hasAcceptedArbitrators() {
|
||||
return dataModel.hasAcceptedArbitrators();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
|
@ -501,6 +497,10 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
|
||||
private void setAmountToModel() {
|
||||
dataModel.amountAsCoin.set(formatter.parseToCoinWith4Decimals(amount.get()));
|
||||
if (dataModel.minAmountAsCoin.get() == null || dataModel.minAmountAsCoin.get().equals(Coin.ZERO)) {
|
||||
minAmount.set(amount.get());
|
||||
setMinAmountToModel();
|
||||
}
|
||||
}
|
||||
|
||||
private void setMinAmountToModel() {
|
||||
|
@ -508,21 +508,11 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
}
|
||||
|
||||
private void setPriceToModel() {
|
||||
dataModel.priceAsFiat.set(formatter.parseToFiatWith2Decimals(price.get()));
|
||||
dataModel.priceAsFiat.set(formatter.parseToFiatWith2Decimals(price.get(), dataModel.tradeCurrencyCode.get()));
|
||||
}
|
||||
|
||||
private void setVolumeToModel() {
|
||||
dataModel.volumeAsFiat.set(formatter.parseToFiatWith2Decimals(volume.get()));
|
||||
}
|
||||
|
||||
private void updateButtonDisableState() {
|
||||
isPlaceOfferButtonDisabled.set(!(isBtcInputValid(amount.get()).isValid &&
|
||||
isBtcInputValid(minAmount.get()).isValid &&
|
||||
isBtcInputValid(price.get()).isValid &&
|
||||
isBtcInputValid(volume.get()).isValid &&
|
||||
dataModel.isMinAmountLessOrEqualAmount() &&
|
||||
dataModel.isWalletFunded.get())
|
||||
);
|
||||
dataModel.volumeAsFiat.set(formatter.parseToFiatWith2Decimals(volume.get(), dataModel.tradeCurrencyCode.get()));
|
||||
}
|
||||
|
||||
private InputValidator.ValidationResult isBtcInputValid(String input) {
|
||||
|
@ -533,4 +523,25 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
return fiatValidator.validate(input);
|
||||
}
|
||||
|
||||
private void updateButtonDisableState() {
|
||||
isPlaceOfferButtonDisabled.set(!(isBtcInputValid(amount.get()).isValid &&
|
||||
isBtcInputValid(minAmount.get()).isValid &&
|
||||
isFiatInputValid(price.get()).isValid &&
|
||||
isFiatInputValid(volume.get()).isValid &&
|
||||
dataModel.isMinAmountLessOrEqualAmount() &&
|
||||
dataModel.isWalletFunded.get())
|
||||
);
|
||||
}
|
||||
|
||||
public List<Arbitrator> getArbitrators() {
|
||||
return dataModel.getArbitrators();
|
||||
}
|
||||
|
||||
public void setShowPlaceOfferConfirmation(boolean selected) {
|
||||
dataModel.setShowPlaceOfferConfirmation(selected);
|
||||
}
|
||||
|
||||
public boolean getShowPlaceOfferConfirmation() {
|
||||
return dataModel.getShowPlaceOfferConfirmation();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,27 +17,21 @@
|
|||
|
||||
package io.bitsquare.gui.main.offer.offerbook;
|
||||
|
||||
import io.bitsquare.fiat.FiatAccount;
|
||||
import io.bitsquare.locale.Country;
|
||||
import io.bitsquare.locale.CurrencyUtil;
|
||||
import io.bitsquare.p2p.storage.HashSetChangedListener;
|
||||
import io.bitsquare.p2p.storage.data.ProtectedData;
|
||||
import io.bitsquare.trade.TradeManager;
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
import io.bitsquare.trade.offer.OfferBookService;
|
||||
import io.bitsquare.user.User;
|
||||
import io.bitsquare.util.Utilities;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Holds and manages the unsorted and unfiltered offerbook list of both buy and sell offers.
|
||||
* It is handled as singleton by Guice and is used by 2 instances of OfferBookDataModel (one for Buy one for Sell).
|
||||
|
@ -46,121 +40,62 @@ import org.slf4j.LoggerFactory;
|
|||
* package for that.
|
||||
*/
|
||||
public class OfferBook {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(OfferBook.class);
|
||||
private static final int POLLING_INTERVAL = 2000; // in ms
|
||||
|
||||
private final OfferBookService offerBookService;
|
||||
private final User user;
|
||||
private final ChangeListener<FiatAccount> bankAccountChangeListener;
|
||||
private final ChangeListener<Number> invalidationListener;
|
||||
private final OfferBookService.Listener offerBookServiceListener;
|
||||
|
||||
private final ObservableList<OfferBookListItem> offerBookListItems = FXCollections.observableArrayList();
|
||||
|
||||
private String fiatCode;
|
||||
private Timer pollingTimer;
|
||||
private Country country;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
OfferBook(OfferBookService offerBookService, User user, TradeManager tradeManager) {
|
||||
OfferBook(OfferBookService offerBookService, TradeManager tradeManager) {
|
||||
this.offerBookService = offerBookService;
|
||||
this.user = user;
|
||||
|
||||
bankAccountChangeListener = (observableValue, oldValue, newValue) -> setBankAccount(newValue);
|
||||
invalidationListener = (ov, oldValue, newValue) -> offerBookService.getOffers(fiatCode);
|
||||
|
||||
offerBookServiceListener = new OfferBookService.Listener() {
|
||||
offerBookService.addHashSetChangedListener(new HashSetChangedListener() {
|
||||
@Override
|
||||
public void onOfferAdded(Offer offer) {
|
||||
addOfferToOfferBookListItems(offer);
|
||||
public void onAdded(ProtectedData entry) {
|
||||
log.debug("onAdded " + entry);
|
||||
Serializable data = entry.expirablePayload;
|
||||
if (data instanceof Offer) {
|
||||
Offer offer = (Offer) data;
|
||||
OfferBookListItem offerBookListItem = new OfferBookListItem(offer);
|
||||
if (!offerBookListItems.contains(offerBookListItem))
|
||||
offerBookListItems.add(offerBookListItem);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOffersReceived(List<Offer> offers) {
|
||||
//TODO use deltas instead replacing the whole list
|
||||
offerBookListItems.clear();
|
||||
offers.stream().forEach(OfferBook.this::addOfferToOfferBookListItems);
|
||||
public void onRemoved(ProtectedData entry) {
|
||||
log.debug("onRemoved " + entry);
|
||||
if (entry.expirablePayload instanceof Offer) {
|
||||
Offer offer = (Offer) entry.expirablePayload;
|
||||
|
||||
// Update state in case that that offer is used in the take offer screen, so it gets updated correctly
|
||||
offer.setState(Offer.State.REMOVED);
|
||||
|
||||
// clean up possible references in openOfferManager
|
||||
tradeManager.onOfferRemovedFromRemoteOfferBook(offer);
|
||||
|
||||
offerBookListItems.removeIf(item -> item.getOffer().getId().equals(offer.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOfferRemoved(Offer offer) {
|
||||
// Update state in case that that offer is used in the take offer screen, so it gets updated correctly
|
||||
offer.setState(Offer.State.REMOVED);
|
||||
|
||||
// clean up possible references in openOfferManager
|
||||
tradeManager.onOfferRemovedFromRemoteOfferBook(offer);
|
||||
|
||||
offerBookListItems.removeIf(item -> item.getOffer().getId().equals(offer.getId()));
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void startPolling() {
|
||||
addListeners();
|
||||
setBankAccount(user.currentFiatAccountProperty().get());
|
||||
pollingTimer = Utilities.setInterval(POLLING_INTERVAL, () -> offerBookService.requestInvalidationTimeStampFromDHT(fiatCode));
|
||||
offerBookService.getOffers(fiatCode);
|
||||
}
|
||||
|
||||
void stopPolling() {
|
||||
pollingTimer.cancel();
|
||||
removeListeners();
|
||||
}
|
||||
|
||||
private void addListeners() {
|
||||
user.currentFiatAccountProperty().addListener(bankAccountChangeListener);
|
||||
offerBookService.addListener(offerBookServiceListener);
|
||||
offerBookService.invalidationTimestampProperty().addListener(invalidationListener);
|
||||
}
|
||||
|
||||
private void removeListeners() {
|
||||
user.currentFiatAccountProperty().removeListener(bankAccountChangeListener);
|
||||
offerBookService.removeListener(offerBookServiceListener);
|
||||
offerBookService.invalidationTimestampProperty().removeListener(invalidationListener);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getter
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ObservableList<OfferBookListItem> getOfferBookListItems() {
|
||||
public ObservableList<OfferBookListItem> getOfferBookListItems() {
|
||||
return offerBookListItems;
|
||||
}
|
||||
|
||||
public void fillOfferBookListItems() {
|
||||
log.debug("fillOfferBookListItems");
|
||||
List<Offer> offers = offerBookService.getOffers();
|
||||
CopyOnWriteArrayList<OfferBookListItem> list = new CopyOnWriteArrayList<>();
|
||||
offers.stream().forEach(e -> list.add(new OfferBookListItem(e)));
|
||||
offerBookListItems.clear();
|
||||
offerBookListItems.addAll(list);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void setBankAccount(FiatAccount fiatAccount) {
|
||||
log.debug("setBankAccount " + fiatAccount);
|
||||
if (fiatAccount != null) {
|
||||
country = fiatAccount.country;
|
||||
fiatCode = fiatAccount.currencyCode;
|
||||
|
||||
// TODO check why that was used (probably just for update triggering, if so refactor that)
|
||||
//offerBookListItems.stream().forEach(e -> e.setBankAccountCountry(country));
|
||||
}
|
||||
else {
|
||||
fiatCode = CurrencyUtil.getDefaultCurrencyAsCode();
|
||||
}
|
||||
}
|
||||
|
||||
private void addOfferToOfferBookListItems(Offer offer) {
|
||||
if (offer != null) {
|
||||
offerBookListItems.add(new OfferBookListItem(offer, country));
|
||||
}
|
||||
log.debug("offerBookListItems " + offerBookListItems.size());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,324 +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.main.offer.offerbook;
|
||||
|
||||
import io.bitsquare.common.handlers.ErrorMessageHandler;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.fiat.FiatAccount;
|
||||
import io.bitsquare.gui.common.model.Activatable;
|
||||
import io.bitsquare.gui.common.model.DataModel;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.locale.Country;
|
||||
import io.bitsquare.locale.CurrencyUtil;
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
import io.bitsquare.trade.offer.OpenOfferManager;
|
||||
import io.bitsquare.user.Preferences;
|
||||
import io.bitsquare.user.User;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.utils.ExchangeRate;
|
||||
import org.bitcoinj.utils.Fiat;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.collections.transformation.FilteredList;
|
||||
import javafx.collections.transformation.SortedList;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
/**
|
||||
* It holds the scope specific domain data for either a buy or sell UI screen.
|
||||
*/
|
||||
class OfferBookDataModel implements Activatable, DataModel {
|
||||
private static final Logger log = LoggerFactory.getLogger(OfferBookDataModel.class);
|
||||
|
||||
private final OpenOfferManager openOfferManager;
|
||||
private final User user;
|
||||
private final OfferBook offerBook;
|
||||
private final Preferences preferences;
|
||||
private final BSFormatter formatter;
|
||||
|
||||
private final FilteredList<OfferBookListItem> filteredItems;
|
||||
private final SortedList<OfferBookListItem> sortedItems;
|
||||
|
||||
private ChangeListener<FiatAccount> bankAccountChangeListener;
|
||||
|
||||
private final ObjectProperty<Coin> amountAsCoin = new SimpleObjectProperty<>();
|
||||
private final ObjectProperty<Fiat> priceAsFiat = new SimpleObjectProperty<>();
|
||||
private final ObjectProperty<Fiat> volumeAsFiat = new SimpleObjectProperty<>();
|
||||
|
||||
final StringProperty restrictionsInfo = new SimpleStringProperty();
|
||||
final StringProperty fiatCode = new SimpleStringProperty();
|
||||
final StringProperty btcCode = new SimpleStringProperty();
|
||||
final ObjectProperty<Country> bankAccountCountry = new SimpleObjectProperty<>();
|
||||
private Offer.Direction direction;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor, lifecycle
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public OfferBookDataModel(User user, OpenOfferManager openOfferManager, OfferBook offerBook, Preferences preferences,
|
||||
BSFormatter formatter) {
|
||||
this.openOfferManager = openOfferManager;
|
||||
this.user = user;
|
||||
this.offerBook = offerBook;
|
||||
this.preferences = preferences;
|
||||
this.formatter = formatter;
|
||||
|
||||
this.filteredItems = new FilteredList<>(offerBook.getOfferBookListItems());
|
||||
this.sortedItems = new SortedList<>(filteredItems);
|
||||
|
||||
createListeners();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activate() {
|
||||
amountAsCoin.set(null);
|
||||
priceAsFiat.set(null);
|
||||
volumeAsFiat.set(null);
|
||||
|
||||
addBindings();
|
||||
addListeners();
|
||||
|
||||
setBankAccount(user.currentFiatAccountProperty().get());
|
||||
|
||||
offerBook.startPolling();
|
||||
applyFilter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
removeBindings();
|
||||
removeListeners();
|
||||
|
||||
offerBook.stopPolling();
|
||||
}
|
||||
|
||||
private void addBindings() {
|
||||
btcCode.bind(preferences.btcDenominationProperty());
|
||||
}
|
||||
|
||||
private void removeBindings() {
|
||||
btcCode.unbind();
|
||||
}
|
||||
|
||||
private void createListeners() {
|
||||
this.bankAccountChangeListener = (observableValue, oldValue, newValue) -> setBankAccount(newValue);
|
||||
}
|
||||
|
||||
private void addListeners() {
|
||||
user.currentFiatAccountProperty().addListener(bankAccountChangeListener);
|
||||
}
|
||||
|
||||
private void removeListeners() {
|
||||
user.currentFiatAccountProperty().removeListener(bankAccountChangeListener);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
void setDirection(Offer.Direction direction) {
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// UI actions
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void onCancelOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
openOfferManager.onCancelOpenOffer(offer, resultHandler, errorMessageHandler);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getters
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
boolean isTradable(Offer offer) {
|
||||
// if user has not registered yet we display all
|
||||
FiatAccount currentFiatAccount = user.currentFiatAccountProperty().get();
|
||||
if (currentFiatAccount == null)
|
||||
return true;
|
||||
|
||||
boolean countryResult = offer.getAcceptedCountries().contains(currentFiatAccount.country);
|
||||
// for IRC test version deactivate the check
|
||||
countryResult = true;
|
||||
if (!countryResult)
|
||||
restrictionsInfo.set("This offer requires that the payments account resides in one of those countries:\n" +
|
||||
formatter.countryLocalesToString(offer.getAcceptedCountries()) +
|
||||
"\n\nThe country of your payments account (" + user.currentFiatAccountProperty().get().country.name +
|
||||
") is not included in that list." +
|
||||
"\n\n Do you want to edit your preferences now?");
|
||||
|
||||
|
||||
// TODO Not so clear how the restrictions will be handled
|
||||
// we might get rid of languages (handles viy arbitrators)
|
||||
/*
|
||||
// disjoint returns true if the two specified collections have no elements in common.
|
||||
boolean languageResult = !Collections.disjoint(preferences.getAcceptedLanguageLocales(),
|
||||
offer.getAcceptedLanguageLocales());
|
||||
if (!languageResult)
|
||||
restrictionsInfo.set("This offer requires that the payments account resides in one of those languages:\n" +
|
||||
BSFormatter.languageLocalesToString(offer.getAcceptedLanguageLocales()) +
|
||||
"\n\nThe country of your payments account (" + user.getCurrentBankAccount().getCountry().getName() +
|
||||
") is not included in that list.");
|
||||
|
||||
boolean arbitratorResult = !Collections.disjoint(preferences.getAcceptedArbitrators(),
|
||||
offer.getArbitrators());*/
|
||||
|
||||
return countryResult;
|
||||
}
|
||||
|
||||
SortedList<OfferBookListItem> getOfferList() {
|
||||
return sortedItems;
|
||||
}
|
||||
|
||||
boolean isRegistered() {
|
||||
return user.isRegistered();
|
||||
}
|
||||
|
||||
boolean isMyOffer(Offer offer) {
|
||||
return openOfferManager.isMyOffer(offer);
|
||||
}
|
||||
|
||||
Coin getAmountAsCoin() {
|
||||
return amountAsCoin.get();
|
||||
}
|
||||
|
||||
ObjectProperty<Coin> amountAsCoinProperty() {
|
||||
return amountAsCoin;
|
||||
}
|
||||
|
||||
Fiat getPriceAsFiat() {
|
||||
return priceAsFiat.get();
|
||||
}
|
||||
|
||||
ObjectProperty<Fiat> priceAsFiatProperty() {
|
||||
return priceAsFiat;
|
||||
}
|
||||
|
||||
Fiat getVolumeAsFiat() {
|
||||
return volumeAsFiat.get();
|
||||
}
|
||||
|
||||
ObjectProperty<Fiat> volumeAsFiatProperty() {
|
||||
return volumeAsFiat;
|
||||
}
|
||||
|
||||
Offer.Direction getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void calculateVolume() {
|
||||
try {
|
||||
if (priceAsFiat.get() != null &&
|
||||
amountAsCoin.get() != null &&
|
||||
!amountAsCoin.get().isZero() &&
|
||||
!priceAsFiat.get().isZero()) {
|
||||
volumeAsFiat.set(new ExchangeRate(priceAsFiat.get()).coinToFiat(amountAsCoin.get()));
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
// Should be never reached
|
||||
log.error(t.toString());
|
||||
}
|
||||
}
|
||||
|
||||
void calculateAmount() {
|
||||
try {
|
||||
if (volumeAsFiat.get() != null &&
|
||||
priceAsFiat.get() != null &&
|
||||
!volumeAsFiat.get().isZero() &&
|
||||
!priceAsFiat.get().isZero()) {
|
||||
// If we got a btc value with more then 4 decimals we convert it to max 4 decimals
|
||||
amountAsCoin.set(formatter.reduceTo4Decimals(new ExchangeRate(priceAsFiat.get()).fiatToCoin
|
||||
(volumeAsFiat.get())));
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
// Should be never reached
|
||||
log.error(t.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void setAmount(Coin amount) {
|
||||
amountAsCoin.set(amount);
|
||||
applyFilter();
|
||||
}
|
||||
|
||||
void setPrice(Fiat price) {
|
||||
priceAsFiat.set(price);
|
||||
applyFilter();
|
||||
}
|
||||
|
||||
void setVolume(Fiat volume) {
|
||||
volumeAsFiat.set(volume);
|
||||
applyFilter();
|
||||
}
|
||||
|
||||
private void setBankAccount(FiatAccount fiatAccount) {
|
||||
if (fiatAccount != null) {
|
||||
fiatCode.set(fiatAccount.currencyCode);
|
||||
bankAccountCountry.set(fiatAccount.country);
|
||||
sortedItems.stream().forEach(e -> e.setBankAccountCountry(fiatAccount.country));
|
||||
}
|
||||
else {
|
||||
fiatCode.set(CurrencyUtil.getDefaultCurrencyAsCode());
|
||||
}
|
||||
}
|
||||
|
||||
void applyFilter() {
|
||||
filteredItems.setPredicate(offerBookListItem -> {
|
||||
Offer offer = offerBookListItem.getOffer();
|
||||
|
||||
boolean directionResult = offer.getDirection() != direction;
|
||||
|
||||
boolean amountResult = true;
|
||||
if (amountAsCoin.get() != null && amountAsCoin.get().isPositive())
|
||||
amountResult = amountAsCoin.get().compareTo(offer.getAmount()) <= 0 &&
|
||||
amountAsCoin.get().compareTo(offer.getMinAmount()) >= 0;
|
||||
|
||||
boolean priceResult = true;
|
||||
if (priceAsFiat.get() != null && priceAsFiat.get().isPositive()) {
|
||||
if (offer.getDirection() == Offer.Direction.SELL)
|
||||
priceResult = priceAsFiat.get().compareTo(offer.getPrice()) >= 0;
|
||||
else
|
||||
priceResult = priceAsFiat.get().compareTo(offer.getPrice()) <= 0;
|
||||
}
|
||||
|
||||
return directionResult && amountResult && priceResult;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -17,35 +17,17 @@
|
|||
|
||||
package io.bitsquare.gui.main.offer.offerbook;
|
||||
|
||||
import io.bitsquare.locale.Country;
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
|
||||
public class OfferBookListItem {
|
||||
private final Offer offer;
|
||||
private final ObjectProperty<Country> bankAccountCountry = new SimpleObjectProperty<>();
|
||||
|
||||
public OfferBookListItem(Offer offer, Country bankAccountCountry) {
|
||||
public OfferBookListItem(Offer offer) {
|
||||
this.offer = offer;
|
||||
setBankAccountCountry(bankAccountCountry);
|
||||
}
|
||||
|
||||
void setBankAccountCountry(Country bankAccountCountry) {
|
||||
this.bankAccountCountry.set(bankAccountCountry);
|
||||
}
|
||||
|
||||
public Offer getOffer() {
|
||||
return offer;
|
||||
}
|
||||
|
||||
Country getBankAccountCountry() {
|
||||
return bankAccountCountry.get();
|
||||
}
|
||||
|
||||
ObjectProperty<Country> bankAccountCountryProperty() {
|
||||
return bankAccountCountry;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,142 +17,20 @@
|
|||
~ along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<?import io.bitsquare.gui.components.InputTextField?>
|
||||
<?import io.bitsquare.gui.components.TitledGroupBg?>
|
||||
<?import io.bitsquare.gui.components.TitledSeparator?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.text.*?>
|
||||
<GridPane fx:id="root" fx:controller="io.bitsquare.gui.main.offer.offerbook.OfferBookView"
|
||||
hgap="5.0" vgap="0"
|
||||
hgap="5.0" vgap="5"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
<padding>
|
||||
<Insets bottom="20.0" left="25.0" top="30.0" right="25"/>
|
||||
</padding>
|
||||
|
||||
<TitledGroupBg text="Filter offers" GridPane.rowIndex="0" GridPane.columnIndex="0"
|
||||
GridPane.rowSpan="5" GridPane.columnSpan="2"/>
|
||||
|
||||
<Label text="Filter by amount or price:" GridPane.rowIndex="0" GridPane.valignment="TOP">
|
||||
<padding>
|
||||
<Insets top="21.0"/>
|
||||
</padding>
|
||||
</Label>
|
||||
<HBox alignment="CENTER_LEFT" spacing="5" GridPane.rowIndex="0" GridPane.columnIndex="1">
|
||||
<VBox spacing="4">
|
||||
<Label id="input-description-label" text="Filter by Amount in BTC"
|
||||
prefWidth="137"/>
|
||||
<HBox>
|
||||
<InputTextField fx:id="amountTextField" id="text-input-with-currency-text-field"
|
||||
promptText="Amount in BTC" prefWidth="137"
|
||||
alignment="CENTER_RIGHT"/>
|
||||
<Label fx:id="amountBtcLabel" id="currency-info-label"/>
|
||||
</HBox>
|
||||
</VBox>
|
||||
<Label text="x">
|
||||
<font>
|
||||
<Font name="Helvetica-Bold" size="20.0"/>
|
||||
</font>
|
||||
<padding>
|
||||
<Insets top="14.0" left="3" right="3"/>
|
||||
</padding>
|
||||
</Label>
|
||||
|
||||
<VBox spacing="4">
|
||||
<Label fx:id="priceDescriptionLabel" id="input-description-label" prefWidth="123"/>
|
||||
<HBox>
|
||||
<InputTextField fx:id="priceTextField" id="text-input-with-currency-text-field"
|
||||
promptText="Filter by price" prefWidth="123"
|
||||
alignment="CENTER_RIGHT"/>
|
||||
<Label fx:id="priceFiatLabel" id="currency-info-label" alignment="CENTER"/>
|
||||
</HBox>
|
||||
</VBox>
|
||||
|
||||
<Label text="=">
|
||||
<font>
|
||||
<Font name="Helvetica-Bold" size="20.0"/>
|
||||
</font>
|
||||
<padding>
|
||||
<Insets top="14.0" left="2" right="2"/>
|
||||
</padding>
|
||||
</Label>
|
||||
|
||||
<VBox spacing="4">
|
||||
<Label fx:id="volumeDescriptionLabel" id="input-description-label" prefWidth="135"/>
|
||||
<HBox>
|
||||
<InputTextField fx:id="volumeTextField" id="text-input-with-currency-text-field"
|
||||
prefWidth="135" alignment="CENTER_RIGHT"/>
|
||||
<Label fx:id="volumeFiatLabel" id="currency-info-label"/>
|
||||
</HBox>
|
||||
</VBox>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5"/>
|
||||
</GridPane.margin>
|
||||
</HBox>
|
||||
|
||||
<Label fx:id="extendedCheckBoxLabel" text="Show only matching offers:" GridPane.columnIndex="0"
|
||||
GridPane.rowIndex="2" visible="false" managed="false"/>
|
||||
<CheckBox fx:id="showOnlyMatchingCheckBox" onAction="#onShowOnlyMatching" visible="false" managed="false"
|
||||
GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="2"/>
|
||||
|
||||
<Label fx:id="extendedButton1Label" text="Payments account countries:" GridPane.columnIndex="0"
|
||||
GridPane.rowIndex="3" visible="false" managed="false"/>
|
||||
<Button fx:id="openCountryFilterButton" text="Open countries filter"
|
||||
onAction="#onOpenCountryFilter" GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="3" visible="false" managed="false"/>
|
||||
|
||||
<Label fx:id="extendedButton2Label" text="Payments account methods:" GridPane.columnIndex="0"
|
||||
GridPane.rowIndex="4" visible="false" managed="false"/>
|
||||
<Button fx:id="openPaymentMethodsFilterButton" text="Open payment methods filter"
|
||||
onAction="#onOpenPaymentMethodsFilter" GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="4" visible="false" managed="false">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="-5"/>
|
||||
</GridPane.margin>
|
||||
</Button>
|
||||
|
||||
<HBox spacing="10" GridPane.columnIndex="1" GridPane.rowIndex="5">
|
||||
<Button fx:id="showAdvancedSettingsButton" text="Open advanced filter"
|
||||
onAction="#onToggleShowAdvancedSettings"/>
|
||||
<Button fx:id="createOfferButton" text="Create new offer" defaultButton="true" onAction="#createOffer"/>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="30" top="20"/>
|
||||
</GridPane.margin>
|
||||
</HBox>
|
||||
|
||||
<TitledSeparator text="Offers" GridPane.rowIndex="6" GridPane.columnIndex="0"
|
||||
GridPane.columnSpan="2"/>
|
||||
|
||||
<TableView fx:id="table" GridPane.rowIndex="6" GridPane.columnIndex="0" GridPane.columnSpan="2"
|
||||
prefHeight="1500">
|
||||
<GridPane.margin>
|
||||
<Insets top="10.0" left="-10" right="-10" bottom="-15"/>
|
||||
</GridPane.margin>
|
||||
<columns>
|
||||
<TableColumn text="Amount in BTC (Min.)" fx:id="amountColumn" minWidth="130"/>
|
||||
<TableColumn text="Price" fx:id="priceColumn" minWidth="130"/>
|
||||
<TableColumn text="Amount in EUR (Min.)" fx:id="volumeColumn" minWidth="130"/>
|
||||
<!--<TableColumn text="Country" fx:id="countryColumn" minWidth="60"/>-->
|
||||
<TableColumn text="Bank transfer type" fx:id="bankAccountTypeColumn" minWidth="130"/>
|
||||
<TableColumn text="" fx:id="directionColumn" minWidth="80" sortable="false"/>
|
||||
</columns>
|
||||
</TableView>
|
||||
|
||||
<columnConstraints>
|
||||
<ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES" minWidth="200"/>
|
||||
<ColumnConstraints hgrow="ALWAYS"/>
|
||||
</columnConstraints>
|
||||
|
||||
<rowConstraints>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints vgrow="ALWAYS"/>
|
||||
</rowConstraints>
|
||||
|
||||
<columnConstraints>
|
||||
<ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES" minWidth="200"/>
|
||||
<ColumnConstraints hgrow="ALWAYS"/>
|
||||
</columnConstraints>
|
||||
</GridPane>
|
|
@ -18,76 +18,51 @@
|
|||
package io.bitsquare.gui.main.offer.offerbook;
|
||||
|
||||
import io.bitsquare.gui.Navigation;
|
||||
import io.bitsquare.gui.OverlayManager;
|
||||
import io.bitsquare.gui.common.view.ActivatableViewAndModel;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.components.InputTextField;
|
||||
import io.bitsquare.gui.components.Popups;
|
||||
import io.bitsquare.gui.components.TableGroupHeadline;
|
||||
import io.bitsquare.gui.main.MainView;
|
||||
import io.bitsquare.gui.main.account.AccountView;
|
||||
import io.bitsquare.gui.main.account.content.restrictions.RestrictionsView;
|
||||
import io.bitsquare.gui.main.account.content.arbitratorselection.ArbitratorSelectionView;
|
||||
import io.bitsquare.gui.main.account.content.paymentsaccount.PaymentAccountView;
|
||||
import io.bitsquare.gui.main.account.settings.AccountSettingsView;
|
||||
import io.bitsquare.gui.main.account.setup.AccountSetupWizard;
|
||||
import io.bitsquare.gui.main.funds.FundsView;
|
||||
import io.bitsquare.gui.main.funds.withdrawal.WithdrawalView;
|
||||
import io.bitsquare.gui.main.offer.OfferView;
|
||||
import io.bitsquare.gui.util.ImageUtil;
|
||||
import io.bitsquare.gui.util.validation.OptionalBtcValidator;
|
||||
import io.bitsquare.gui.util.validation.OptionalFiatValidator;
|
||||
import io.bitsquare.gui.popups.OfferDetailsPopup;
|
||||
import io.bitsquare.gui.popups.Popup;
|
||||
import io.bitsquare.gui.util.Layout;
|
||||
import io.bitsquare.locale.BSResources;
|
||||
import io.bitsquare.locale.Country;
|
||||
import io.bitsquare.locale.TradeCurrency;
|
||||
import io.bitsquare.payment.PaymentMethod;
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.util.Callback;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.image.*;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.util.Callback;
|
||||
|
||||
import org.controlsfx.control.action.AbstractAction;
|
||||
import org.controlsfx.control.action.Action;
|
||||
import org.controlsfx.dialog.Dialog;
|
||||
|
||||
import static io.bitsquare.gui.util.FormBuilder.*;
|
||||
import static javafx.beans.binding.Bindings.createStringBinding;
|
||||
|
||||
@FxmlView
|
||||
public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookViewModel> {
|
||||
|
||||
@FXML CheckBox showOnlyMatchingCheckBox;
|
||||
@FXML TableView<OfferBookListItem> table;
|
||||
@FXML InputTextField volumeTextField, amountTextField, priceTextField;
|
||||
@FXML Button createOfferButton, showAdvancedSettingsButton, openCountryFilterButton, openPaymentMethodsFilterButton;
|
||||
@FXML TableColumn<OfferBookListItem, OfferBookListItem> priceColumn, amountColumn, volumeColumn, directionColumn,
|
||||
/*countryColumn,*/ bankAccountTypeColumn;
|
||||
@FXML Label amountBtcLabel, priceDescriptionLabel, priceFiatLabel, volumeDescriptionLabel, volumeFiatLabel,
|
||||
extendedButton1Label, extendedButton2Label, extendedCheckBoxLabel;
|
||||
|
||||
private ImageView expand;
|
||||
private ImageView collapse;
|
||||
|
||||
private boolean detailsVisible;
|
||||
private boolean advancedScreenInited;
|
||||
|
||||
|
||||
private final Navigation navigation;
|
||||
private final OptionalBtcValidator optionalBtcValidator;
|
||||
private final OptionalFiatValidator optionalFiatValidator;
|
||||
private OfferView.OfferActionHandler offerActionHandler;
|
||||
private final OfferDetailsPopup offerDetailsPopup;
|
||||
|
||||
private ChangeListener<String> amountTextFieldListener;
|
||||
private ChangeListener<String> priceTextFieldListener;
|
||||
private ChangeListener<String> volumeTextFieldListener;
|
||||
private ChangeListener<Boolean> amountTextFieldFocusedListener;
|
||||
private ChangeListener<Boolean> priceTextFieldFocusedListener;
|
||||
private ChangeListener<Boolean> volumeTextFieldFocusedListener;
|
||||
private ComboBox<TradeCurrency> currencyComboBox;
|
||||
private ComboBox<PaymentMethod> paymentMethodComboBox;
|
||||
private Button createOfferButton;
|
||||
private TableColumn<OfferBookListItem, OfferBookListItem> amountColumn, volumeColumn, priceColumn, paymentMethodColumn;
|
||||
private TableView<OfferBookListItem> tableView;
|
||||
|
||||
private OfferView.OfferActionHandler offerActionHandler;
|
||||
private int gridRow = 0;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -95,174 +70,113 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
OfferBookView(OfferBookViewModel model,
|
||||
Navigation navigation,
|
||||
OptionalBtcValidator optionalBtcValidator,
|
||||
OptionalFiatValidator optionalFiatValidator) {
|
||||
OfferBookView(OfferBookViewModel model, Navigation navigation, OfferDetailsPopup offerDetailsPopup) {
|
||||
super(model);
|
||||
|
||||
this.navigation = navigation;
|
||||
this.optionalBtcValidator = optionalBtcValidator;
|
||||
this.optionalFiatValidator = optionalFiatValidator;
|
||||
this.offerDetailsPopup = offerDetailsPopup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
// init table
|
||||
setAmountColumnCellFactory();
|
||||
setPriceColumnCellFactory();
|
||||
setVolumeColumnCellFactory();
|
||||
// TODO: countryColumn is deactivated in alpha version
|
||||
// setCountryColumnCellFactory();
|
||||
setBankAccountTypeColumnCellFactory();
|
||||
setDirectionColumnCellFactory();
|
||||
root.setPadding(new Insets(30, 25, -1, 25));
|
||||
addTitledGroupBg(root, gridRow, 2, "Filter offer book");
|
||||
|
||||
table.getSortOrder().add(priceColumn);
|
||||
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
currencyComboBox = addLabelComboBox(root, gridRow, "Filter by currency:", Layout.FIRST_ROW_DISTANCE).second;
|
||||
currencyComboBox.setPromptText("Select currency");
|
||||
currencyComboBox.setConverter(new StringConverter<TradeCurrency>() {
|
||||
@Override
|
||||
public String toString(TradeCurrency tradeCurrency) {
|
||||
return tradeCurrency.getCodeAndName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TradeCurrency fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
paymentMethodComboBox = addLabelComboBox(root, ++gridRow, "Filter by payment method:").second;
|
||||
paymentMethodComboBox.setPromptText("Select payment method");
|
||||
paymentMethodComboBox.setConverter(new StringConverter<PaymentMethod>() {
|
||||
@Override
|
||||
public String toString(PaymentMethod paymentMethod) {
|
||||
return paymentMethod.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaymentMethod fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// createOfferButton
|
||||
createOfferButton = addButtonAfterGroup(root, ++gridRow, "Create new offer");
|
||||
|
||||
TableGroupHeadline offerBookTitle = new TableGroupHeadline("Offer book");
|
||||
GridPane.setRowIndex(offerBookTitle, ++gridRow);
|
||||
GridPane.setColumnSpan(offerBookTitle, 2);
|
||||
GridPane.setMargin(offerBookTitle, new Insets(20, -10, -10, -10));
|
||||
root.getChildren().add(offerBookTitle);
|
||||
|
||||
tableView = new TableView<>();
|
||||
GridPane.setRowIndex(tableView, gridRow);
|
||||
GridPane.setColumnSpan(tableView, 2);
|
||||
GridPane.setMargin(tableView, new Insets(40, -10, -15, -10));
|
||||
root.getChildren().add(tableView);
|
||||
amountColumn = getAmountColumn();
|
||||
tableView.getColumns().add(amountColumn);
|
||||
priceColumn = getPriceColumn();
|
||||
tableView.getColumns().add(priceColumn);
|
||||
volumeColumn = getVolumeColumn();
|
||||
tableView.getColumns().add(volumeColumn);
|
||||
paymentMethodColumn = getPaymentMethodColumn();
|
||||
tableView.getColumns().add(paymentMethodColumn);
|
||||
tableView.getColumns().add(getActionColumn());
|
||||
|
||||
tableView.getSortOrder().add(priceColumn);
|
||||
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
Label placeholder = new Label("Currently there are no offers available");
|
||||
placeholder.setWrapText(true);
|
||||
table.setPlaceholder(placeholder);
|
||||
tableView.setPlaceholder(placeholder);
|
||||
|
||||
setupValidators();
|
||||
setupComparators();
|
||||
|
||||
expand = ImageUtil.getImageViewById(ImageUtil.EXPAND);
|
||||
collapse = ImageUtil.getImageViewById(ImageUtil.COLLAPSE);
|
||||
showAdvancedSettingsButton.setGraphic(expand);
|
||||
|
||||
// for irc demo
|
||||
showAdvancedSettingsButton.setVisible(false);
|
||||
showAdvancedSettingsButton.setManaged(false);
|
||||
|
||||
createListeners();
|
||||
priceColumn.setComparator((o1, o2) -> o1.getOffer().getPrice().compareTo(o2.getOffer().getPrice()));
|
||||
amountColumn.setComparator((o1, o2) -> o1.getOffer().getAmount().compareTo(o2.getOffer().getAmount()));
|
||||
volumeColumn.setComparator((o1, o2) -> o1.getOffer().getOfferVolume().compareTo(o2.getOffer().getOfferVolume()));
|
||||
paymentMethodColumn.setComparator((o1, o2) -> o1.getOffer().getPaymentMethod().compareTo(o2.getOffer().getPaymentMethod()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doActivate() {
|
||||
// reset filter values
|
||||
amountTextField.setText("");
|
||||
priceTextField.setText("");
|
||||
volumeTextField.setText("");
|
||||
protected void activate() {
|
||||
currencyComboBox.setItems(model.getTradeCurrencies());
|
||||
currencyComboBox.getSelectionModel().select(model.getTradeCurrency());
|
||||
currencyComboBox.setVisibleRowCount(Math.min(currencyComboBox.getItems().size(), 25));
|
||||
paymentMethodComboBox.setItems(model.getPaymentMethods());
|
||||
paymentMethodComboBox.getSelectionModel().select(0);
|
||||
|
||||
amountTextField.resetValidation();
|
||||
priceTextField.resetValidation();
|
||||
volumeTextField.resetValidation();
|
||||
currencyComboBox.setOnAction(e -> model.onSetTradeCurrency(currencyComboBox.getSelectionModel().getSelectedItem()));
|
||||
paymentMethodComboBox.setOnAction(e -> model.onSetPaymentMethod(paymentMethodComboBox.getSelectionModel().getSelectedItem()));
|
||||
createOfferButton.setOnAction(e -> onCreateOffer());
|
||||
volumeColumn.textProperty().bind(createStringBinding(
|
||||
() -> BSResources.get("Amount in {0} (Min.)", model.tradeCurrencyCode.get()), model.tradeCurrencyCode));
|
||||
model.getOfferList().comparatorProperty().bind(tableView.comparatorProperty());
|
||||
|
||||
addListeners();
|
||||
addBindings();
|
||||
|
||||
// setOfferBookInfo has been called before
|
||||
table.setItems(model.getOfferList());
|
||||
tableView.setItems(model.getOfferList());
|
||||
priceColumn.setSortType((model.getDirection() == Offer.Direction.BUY) ? TableColumn.SortType.ASCENDING : TableColumn.SortType.DESCENDING);
|
||||
table.sort();
|
||||
tableView.sort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doDeactivate() {
|
||||
removeBindings();
|
||||
removeListeners();
|
||||
}
|
||||
|
||||
private void createListeners() {
|
||||
amountTextFieldListener = (observable, oldValue, newValue) -> model.amount.set(newValue);
|
||||
priceTextFieldListener = (observable, oldValue, newValue) -> model.price.set(newValue);
|
||||
volumeTextFieldListener = (observable, oldValue, newValue) -> model.volume.set(newValue);
|
||||
|
||||
amountTextFieldFocusedListener = (ov, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
amountTextField.textProperty().unbind();
|
||||
amountTextField.textProperty().addListener(amountTextFieldListener);
|
||||
}
|
||||
else {
|
||||
amountTextField.textProperty().removeListener(amountTextFieldListener);
|
||||
amountTextField.textProperty().bind(model.amount);
|
||||
}
|
||||
};
|
||||
priceTextFieldFocusedListener = (ov, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
priceTextField.textProperty().unbind();
|
||||
priceTextField.textProperty().addListener(priceTextFieldListener);
|
||||
}
|
||||
else {
|
||||
priceTextField.textProperty().removeListener(priceTextFieldListener);
|
||||
priceTextField.textProperty().bind(model.price);
|
||||
}
|
||||
};
|
||||
volumeTextFieldFocusedListener = (ov, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
volumeTextField.textProperty().unbind();
|
||||
volumeTextField.textProperty().addListener(volumeTextFieldListener);
|
||||
}
|
||||
else {
|
||||
volumeTextField.textProperty().removeListener(volumeTextFieldListener);
|
||||
volumeTextField.textProperty().bind(model.volume);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void addListeners() {
|
||||
amountTextField.focusedProperty().addListener(amountTextFieldFocusedListener);
|
||||
priceTextField.focusedProperty().addListener(priceTextFieldFocusedListener);
|
||||
volumeTextField.focusedProperty().addListener(volumeTextFieldFocusedListener);
|
||||
}
|
||||
|
||||
private void removeListeners() {
|
||||
amountTextField.focusedProperty().removeListener(amountTextFieldFocusedListener);
|
||||
priceTextField.focusedProperty().removeListener(priceTextFieldFocusedListener);
|
||||
volumeTextField.focusedProperty().removeListener(volumeTextFieldFocusedListener);
|
||||
|
||||
amountTextField.textProperty().removeListener(amountTextFieldListener);
|
||||
priceTextField.textProperty().removeListener(priceTextFieldListener);
|
||||
volumeTextField.textProperty().removeListener(volumeTextFieldListener);
|
||||
}
|
||||
|
||||
private void addBindings() {
|
||||
// those bindings are unbinded by focus change handlers
|
||||
amountTextField.textProperty().bind(model.amount);
|
||||
priceTextField.textProperty().bind(model.price);
|
||||
volumeTextField.textProperty().bind(model.volume);
|
||||
|
||||
amountBtcLabel.textProperty().bind(model.btcCode);
|
||||
priceFiatLabel.textProperty().bind(model.fiatCode);
|
||||
volumeFiatLabel.textProperty().bind(model.fiatCode);
|
||||
priceDescriptionLabel.textProperty().bind(createStringBinding(() -> BSResources.get("Filter by price in {0}", model.fiatCode.get()), model.fiatCode));
|
||||
volumeDescriptionLabel.textProperty().bind(createStringBinding(() -> BSResources.get("Filter by amount in {0}", model.fiatCode.get()), model.fiatCode));
|
||||
volumeTextField.promptTextProperty().bind(createStringBinding(() -> BSResources.get("Amount in {0}", model.fiatCode.get()), model.fiatCode));
|
||||
|
||||
model.getOfferList().comparatorProperty().bind(table.comparatorProperty());
|
||||
}
|
||||
|
||||
private void removeBindings() {
|
||||
amountTextField.textProperty().unbind();
|
||||
priceTextField.textProperty().unbind();
|
||||
volumeTextField.textProperty().unbind();
|
||||
amountBtcLabel.textProperty().unbind();
|
||||
priceFiatLabel.textProperty().unbind();
|
||||
volumeFiatLabel.textProperty().unbind();
|
||||
priceDescriptionLabel.textProperty().unbind();
|
||||
volumeDescriptionLabel.textProperty().unbind();
|
||||
volumeTextField.promptTextProperty().unbind();
|
||||
|
||||
protected void deactivate() {
|
||||
currencyComboBox.setOnAction(null);
|
||||
paymentMethodComboBox.setOnAction(null);
|
||||
createOfferButton.setOnAction(null);
|
||||
volumeColumn.textProperty().unbind();
|
||||
model.getOfferList().comparatorProperty().unbind();
|
||||
}
|
||||
|
||||
private void setupValidators() {
|
||||
amountTextField.setValidator(optionalBtcValidator);
|
||||
priceTextField.setValidator(optionalFiatValidator);
|
||||
volumeTextField.setValidator(optionalFiatValidator);
|
||||
}
|
||||
|
||||
private void setupComparators() {
|
||||
priceColumn.setComparator((o1, o2) -> o1.getOffer().getPrice().compareTo(o2.getOffer().getPrice()));
|
||||
amountColumn.setComparator((o1, o2) -> o1.getOffer().getAmount().compareTo(o2.getOffer().getAmount()));
|
||||
volumeColumn.setComparator((o1, o2) ->
|
||||
o1.getOffer().getOfferVolume().compareTo(o2.getOffer().getOfferVolume()));
|
||||
/* countryColumn.setComparator((o1, o2) -> o1.getOffer().getBankAccountCountry().getName().compareTo(o2
|
||||
.getOffer()
|
||||
.getBankAccountCountry().getName()));*/
|
||||
bankAccountTypeColumn.setComparator((o1, o2) -> o1.getOffer().getFiatAccountType().compareTo(o2.getOffer()
|
||||
.getFiatAccountType()));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
|
@ -285,175 +199,75 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||
// UI actions
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@FXML
|
||||
void createOffer() {
|
||||
if (model.isRegistered()) {
|
||||
private void onCreateOffer() {
|
||||
if (!model.hasPaymentAccount()) {
|
||||
showWarning("You don't have setup a payment account yet.",
|
||||
"You need to setup your payment account before you can trade.\nDo you want to do this now?", PaymentAccountView.class);
|
||||
} else if (!model.hasPaymentAccountForCurrency()) {
|
||||
showWarning("You don't have a payment account with that selected currency.",
|
||||
"You need to setup a payment account for the selected currency to be able to trade in that currency.\n" +
|
||||
"Do you want to do this now?", PaymentAccountView.class);
|
||||
} else if (!model.hasAcceptedArbitrators()) {
|
||||
showWarning("You don't have an arbitrator selected.",
|
||||
"You need to setup at least one arbitrator to be able to trade.\n" +
|
||||
"Do you want to do this now?", ArbitratorSelectionView.class);
|
||||
}
|
||||
else {
|
||||
createOfferButton.setDisable(true);
|
||||
offerActionHandler.createOffer(model.getAmountAsCoin(), model.getPriceAsCoin());
|
||||
}
|
||||
else {
|
||||
openSetupScreen();
|
||||
offerActionHandler.onCreateOffer(model.getTradeCurrency());
|
||||
}
|
||||
}
|
||||
|
||||
private void openSetupScreen() {
|
||||
OverlayManager.blurContent();
|
||||
List<Action> actions = new ArrayList<>();
|
||||
actions.add(new AbstractAction(BSResources.get("shared.ok")) {
|
||||
@Override
|
||||
public void handle(ActionEvent actionEvent) {
|
||||
getProperties().put("type", "OK");
|
||||
Dialog.Actions.OK.handle(actionEvent);
|
||||
navigation.setReturnPath(navigation.getCurrentPath());
|
||||
navigation.navigateTo(MainView.class, AccountView.class, AccountSetupWizard.class);
|
||||
}
|
||||
});
|
||||
Popups.openInfoPopup("You don't have setup a trading account.",
|
||||
"You need to setup your trading account before you can trade.",
|
||||
actions);
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onToggleShowAdvancedSettings() {
|
||||
detailsVisible = !detailsVisible;
|
||||
if (detailsVisible) {
|
||||
showAdvancedSettingsButton.setText(BSResources.get("Hide extended filter options"));
|
||||
showAdvancedSettingsButton.setGraphic(collapse);
|
||||
showDetailsScreen();
|
||||
}
|
||||
else {
|
||||
showAdvancedSettingsButton.setText(BSResources.get("Show more filter options"));
|
||||
showAdvancedSettingsButton.setGraphic(expand);
|
||||
hideDetailsScreen();
|
||||
private void onShowInfo(boolean isPaymentAccountValidForOffer, boolean hasMatchingArbitrator) {
|
||||
if (!hasMatchingArbitrator) {
|
||||
showWarning("You don't have an arbitrator selected.",
|
||||
"You need to setup at least one arbitrator to be able to trade.\n" +
|
||||
"Do you want to do this now?", ArbitratorSelectionView.class);
|
||||
} else if (!isPaymentAccountValidForOffer) {
|
||||
showWarning("You don't have a payment account with the payment method required for that offer.",
|
||||
"You need to setup a payment account with that payment method if you want to take that offer.\n" +
|
||||
"Do you want to do this now?", PaymentAccountView.class);
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onShowOnlyMatching() {
|
||||
Popups.openWarningPopup("Under construction", "This feature is not implemented yet.");
|
||||
private void onTakeOffer(Offer offer) {
|
||||
offerActionHandler.onTakeOffer(offer);
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onOpenCountryFilter() {
|
||||
Popups.openWarningPopup("Under construction", "This feature is not implemented yet.");
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onOpenPaymentMethodsFilter() {
|
||||
Popups.openWarningPopup("Under construction", "This feature is not implemented yet.");
|
||||
}
|
||||
|
||||
private void takeOffer(Offer offer) {
|
||||
if (model.isRegistered()) {
|
||||
if (offer.getDirection() == Offer.Direction.BUY) {
|
||||
offerActionHandler.takeOffer(model.getAmountAsCoin(), model.getPriceAsCoin(), offer);
|
||||
}
|
||||
else {
|
||||
offerActionHandler.takeOffer(model.getAmountAsCoin(), model.getPriceAsCoin(), offer);
|
||||
}
|
||||
}
|
||||
else {
|
||||
openSetupScreen();
|
||||
}
|
||||
}
|
||||
|
||||
private void onCancelOpenOffer(Offer offer) {
|
||||
model.onCancelOpenOffer(offer,
|
||||
private void onRemoveOpenOffer(Offer offer) {
|
||||
model.onRemoveOpenOffer(offer,
|
||||
() -> {
|
||||
log.debug("Remove offer was successful");
|
||||
Popups.openInfoPopup("You can withdraw the funds you paid in from the funds screens.");
|
||||
new Popup().information("You can withdraw the funds you paid in from the funds screens.").show();
|
||||
navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class);
|
||||
},
|
||||
(message) -> {
|
||||
log.error(message);
|
||||
Popups.openWarningPopup("Remove offer failed", message);
|
||||
new Popup().warning("Remove offer failed:\n" + message).show();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// State
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void showDetailsScreen() {
|
||||
if (!advancedScreenInited) {
|
||||
advancedScreenInited = true;
|
||||
}
|
||||
|
||||
toggleDetailsScreen(true);
|
||||
private void showWarning(String masthead, String message, Class target) {
|
||||
new Popup().information(masthead + "\n\n" + message)
|
||||
.onAction(() -> {
|
||||
navigation.setReturnPath(navigation.getCurrentPath());
|
||||
navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class, target);
|
||||
}).show();
|
||||
}
|
||||
|
||||
private void hideDetailsScreen() {
|
||||
toggleDetailsScreen(false);
|
||||
}
|
||||
|
||||
private void toggleDetailsScreen(boolean visible) {
|
||||
root.setVgap(visible ? 5 : 0);
|
||||
|
||||
extendedButton1Label.setVisible(visible);
|
||||
extendedButton1Label.setManaged(visible);
|
||||
|
||||
extendedButton2Label.setVisible(visible);
|
||||
extendedButton2Label.setManaged(visible);
|
||||
|
||||
extendedCheckBoxLabel.setVisible(visible);
|
||||
extendedCheckBoxLabel.setManaged(visible);
|
||||
|
||||
openCountryFilterButton.setVisible(visible);
|
||||
openCountryFilterButton.setManaged(visible);
|
||||
|
||||
openPaymentMethodsFilterButton.setVisible(visible);
|
||||
openPaymentMethodsFilterButton.setManaged(visible);
|
||||
|
||||
showOnlyMatchingCheckBox.setVisible(visible);
|
||||
showOnlyMatchingCheckBox.setManaged(visible);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void openRestrictionsWarning(String restrictionsInfo) {
|
||||
OverlayManager.blurContent();
|
||||
List<Action> actions = new ArrayList<>();
|
||||
actions.add(new AbstractAction(BSResources.get("shared.yes")) {
|
||||
@Override
|
||||
public void handle(ActionEvent actionEvent) {
|
||||
getProperties().put("type", "YES");
|
||||
Dialog.Actions.YES.handle(actionEvent);
|
||||
}
|
||||
});
|
||||
actions.add(new AbstractAction(BSResources.get("shared.no")) {
|
||||
@Override
|
||||
public void handle(ActionEvent actionEvent) {
|
||||
getProperties().put("type", "NO");
|
||||
Dialog.Actions.NO.handle(actionEvent);
|
||||
}
|
||||
});
|
||||
|
||||
Action response = Popups.openConfirmPopup("Information",
|
||||
"You do not fulfill the requirements for that offer.",
|
||||
restrictionsInfo,
|
||||
actions);
|
||||
|
||||
Popups.removeBlurContent();
|
||||
|
||||
if (Popups.isYes(response))
|
||||
navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class, RestrictionsView.class);
|
||||
else
|
||||
table.getSelectionModel().clearSelection();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Table
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void setAmountColumnCellFactory() {
|
||||
amountColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
|
||||
amountColumn.setCellFactory(
|
||||
private TableColumn<OfferBookListItem, OfferBookListItem> getAmountColumn() {
|
||||
TableColumn<OfferBookListItem, OfferBookListItem> column = new TableColumn<OfferBookListItem, OfferBookListItem>("Amount in BTC (Min.)") {
|
||||
{
|
||||
setMinWidth(130);
|
||||
}
|
||||
};
|
||||
column.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
|
||||
column.setCellFactory(
|
||||
new Callback<TableColumn<OfferBookListItem, OfferBookListItem>, TableCell<OfferBookListItem,
|
||||
OfferBookListItem>>() {
|
||||
@Override
|
||||
|
@ -463,16 +277,25 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||
@Override
|
||||
public void updateItem(final OfferBookListItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
setText(model.getAmount(item));
|
||||
if (item != null && !empty)
|
||||
setText(model.getAmount(item));
|
||||
else
|
||||
setText("");
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
return column;
|
||||
}
|
||||
|
||||
private void setPriceColumnCellFactory() {
|
||||
priceColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
|
||||
priceColumn.setCellFactory(
|
||||
private TableColumn<OfferBookListItem, OfferBookListItem> getPriceColumn() {
|
||||
TableColumn<OfferBookListItem, OfferBookListItem> column = new TableColumn<OfferBookListItem, OfferBookListItem>("Price") {
|
||||
{
|
||||
setMinWidth(130);
|
||||
}
|
||||
};
|
||||
column.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
|
||||
column.setCellFactory(
|
||||
new Callback<TableColumn<OfferBookListItem, OfferBookListItem>, TableCell<OfferBookListItem,
|
||||
OfferBookListItem>>() {
|
||||
@Override
|
||||
|
@ -482,16 +305,25 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||
@Override
|
||||
public void updateItem(final OfferBookListItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
setText(model.getPrice(item));
|
||||
if (item != null && !empty)
|
||||
setText(model.getPrice(item));
|
||||
else
|
||||
setText("");
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
return column;
|
||||
}
|
||||
|
||||
private void setVolumeColumnCellFactory() {
|
||||
volumeColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
|
||||
volumeColumn.setCellFactory(
|
||||
private TableColumn<OfferBookListItem, OfferBookListItem> getVolumeColumn() {
|
||||
TableColumn<OfferBookListItem, OfferBookListItem> column = new TableColumn<OfferBookListItem, OfferBookListItem>() {
|
||||
{
|
||||
setMinWidth(130);
|
||||
}
|
||||
};
|
||||
column.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
|
||||
column.setCellFactory(
|
||||
new Callback<TableColumn<OfferBookListItem, OfferBookListItem>, TableCell<OfferBookListItem,
|
||||
OfferBookListItem>>() {
|
||||
@Override
|
||||
|
@ -501,101 +333,42 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||
@Override
|
||||
public void updateItem(final OfferBookListItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
setText(model.getVolume(item));
|
||||
if (item != null && !empty)
|
||||
setText(model.getVolume(item));
|
||||
else
|
||||
setText("");
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
return column;
|
||||
}
|
||||
|
||||
private void setDirectionColumnCellFactory() {
|
||||
directionColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
|
||||
directionColumn.setCellFactory(
|
||||
new Callback<TableColumn<OfferBookListItem, OfferBookListItem>, TableCell<OfferBookListItem,
|
||||
OfferBookListItem>>() {
|
||||
|
||||
private TableColumn<OfferBookListItem, OfferBookListItem> getPaymentMethodColumn() {
|
||||
TableColumn<OfferBookListItem, OfferBookListItem> column = new TableColumn<OfferBookListItem, OfferBookListItem>("Payment method") {
|
||||
{
|
||||
setMinWidth(130);
|
||||
}
|
||||
};
|
||||
column.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
|
||||
column.setCellFactory(
|
||||
new Callback<TableColumn<OfferBookListItem, OfferBookListItem>, TableCell<OfferBookListItem, OfferBookListItem>>() {
|
||||
@Override
|
||||
public TableCell<OfferBookListItem, OfferBookListItem> call(
|
||||
TableColumn<OfferBookListItem, OfferBookListItem> column) {
|
||||
public TableCell<OfferBookListItem, OfferBookListItem> call(TableColumn<OfferBookListItem, OfferBookListItem> column) {
|
||||
return new TableCell<OfferBookListItem, OfferBookListItem>() {
|
||||
final ImageView iconView = new ImageView();
|
||||
final Button button = new Button();
|
||||
ChangeListener<Country> countryChangeListener;
|
||||
OfferBookListItem item;
|
||||
|
||||
{
|
||||
button.setGraphic(iconView);
|
||||
button.setMinWidth(70);
|
||||
}
|
||||
|
||||
private void verifyIfTradable(final OfferBookListItem item) {
|
||||
boolean isMatchingRestrictions = model.isTradable(item
|
||||
.getOffer());
|
||||
button.setDisable(!isMatchingRestrictions);
|
||||
|
||||
TableRow tableRow = getTableRow();
|
||||
if (tableRow != null)
|
||||
tableRow.setOpacity(isMatchingRestrictions ? 1 : 0.4);
|
||||
|
||||
if (isMatchingRestrictions) {
|
||||
button.setDefaultButton(getIndex() == 0);
|
||||
if (tableRow != null) {
|
||||
getTableRow().setOnMouseClicked(null);
|
||||
getTableRow().setTooltip(null);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
button.setDefaultButton(false);
|
||||
if (tableRow != null) {
|
||||
getTableRow().setTooltip(new Tooltip("Click for more information."));
|
||||
getTableRow().setOnMouseClicked((e) -> openRestrictionsWarning
|
||||
(model.restrictionsInfo.get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(final OfferBookListItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (item != null) {
|
||||
String title;
|
||||
Offer offer = item.getOffer();
|
||||
|
||||
if (model.isMyOffer(offer)) {
|
||||
iconView.setId("image-remove");
|
||||
title = "Remove";
|
||||
button.setOnAction(event -> onCancelOpenOffer(item.getOffer()));
|
||||
}
|
||||
else {
|
||||
if (offer.getDirection() == Offer.Direction.SELL)
|
||||
iconView.setId("image-buy");
|
||||
else
|
||||
iconView.setId("image-sell");
|
||||
title = model.getDirectionLabel(offer);
|
||||
button.setOnAction(event -> takeOffer(item.getOffer()));
|
||||
}
|
||||
|
||||
|
||||
if (countryChangeListener != null && this.item != null)
|
||||
item.bankAccountCountryProperty().removeListener(countryChangeListener);
|
||||
|
||||
this.item = item;
|
||||
countryChangeListener = (ov, o, n) -> verifyIfTradable(this.item);
|
||||
item.bankAccountCountryProperty().addListener(countryChangeListener);
|
||||
|
||||
|
||||
verifyIfTradable(item);
|
||||
|
||||
button.setText(title);
|
||||
setGraphic(button);
|
||||
}
|
||||
else {
|
||||
if (this.item != null) {
|
||||
this.item.bankAccountCountryProperty().removeListener(countryChangeListener);
|
||||
this.item = null;
|
||||
countryChangeListener = null;
|
||||
Hyperlink hyperlink = null;
|
||||
if (item != null && !empty) {
|
||||
hyperlink = new Hyperlink(model.getPaymentMethod(item));
|
||||
hyperlink.setTooltip(new Tooltip(model.getPaymentMethodToolTip(item)));
|
||||
hyperlink.setOnAction(event -> offerDetailsPopup.show(item.getOffer()));
|
||||
setGraphic(hyperlink);
|
||||
} else {
|
||||
if (hyperlink != null) {
|
||||
hyperlink.setText("");
|
||||
hyperlink.setTooltip(null);
|
||||
}
|
||||
setGraphic(null);
|
||||
}
|
||||
|
@ -603,62 +376,96 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||
};
|
||||
}
|
||||
});
|
||||
return column;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* private void setCountryColumnCellFactory() {
|
||||
countryColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
|
||||
countryColumn.setCellFactory(
|
||||
private TableColumn<OfferBookListItem, OfferBookListItem> getActionColumn() {
|
||||
TableColumn<OfferBookListItem, OfferBookListItem> column = new TableColumn<OfferBookListItem, OfferBookListItem>("") {
|
||||
{
|
||||
setMinWidth(80);
|
||||
setSortable(false);
|
||||
}
|
||||
};
|
||||
column.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
|
||||
column.setCellFactory(
|
||||
new Callback<TableColumn<OfferBookListItem, OfferBookListItem>, TableCell<OfferBookListItem,
|
||||
OfferBookListItem>>() {
|
||||
|
||||
@Override
|
||||
public TableCell<OfferBookListItem, OfferBookListItem> call(
|
||||
TableColumn<OfferBookListItem, OfferBookListItem> column) {
|
||||
public TableCell<OfferBookListItem, OfferBookListItem> call(TableColumn<OfferBookListItem, OfferBookListItem> column) {
|
||||
return new TableCell<OfferBookListItem, OfferBookListItem>() {
|
||||
final HBox hBox = new HBox();
|
||||
final ImageView iconView = new ImageView();
|
||||
final Button button = new Button();
|
||||
boolean isTradable;
|
||||
boolean isPaymentAccountValidForOffer;
|
||||
boolean hasMatchingArbitrator;
|
||||
|
||||
{
|
||||
hBox.setSpacing(3);
|
||||
hBox.setAlignment(Pos.CENTER);
|
||||
setGraphic(hBox);
|
||||
button.setGraphic(iconView);
|
||||
button.setMinWidth(70);
|
||||
}
|
||||
|
||||
private void verifyIfTradable(final Offer offer) {
|
||||
TableRow tableRow = getTableRow();
|
||||
if (tableRow != null) {
|
||||
isPaymentAccountValidForOffer = model.isPaymentAccountValidForOffer(offer);
|
||||
hasMatchingArbitrator = model.hasMatchingArbitrator(offer);
|
||||
isTradable = isPaymentAccountValidForOffer && hasMatchingArbitrator;
|
||||
|
||||
tableRow.setOpacity(isTradable ? 1 : 0.4);
|
||||
|
||||
if (isTradable) {
|
||||
// set first row button as default
|
||||
button.setDefaultButton(getIndex() == 0);
|
||||
tableRow.setOnMouseClicked(null);
|
||||
// tableRow.setTooltip(null);
|
||||
} else {
|
||||
button.setDefaultButton(false);
|
||||
tableRow.setOnMouseClicked(e -> onShowInfo(isPaymentAccountValidForOffer, hasMatchingArbitrator));
|
||||
/* tableRow.setTooltip(
|
||||
new Tooltip(hasMatchingArbitrator ? "No matching payment account." : "No matching accepted arbitrators."));*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(final OfferBookListItem offerBookListItem, boolean empty) {
|
||||
super.updateItem(offerBookListItem, empty);
|
||||
public void updateItem(final OfferBookListItem newItem, boolean empty) {
|
||||
super.updateItem(newItem, empty);
|
||||
|
||||
hBox.getChildren().clear();
|
||||
if (offerBookListItem != null) {
|
||||
Country country = offerBookListItem.getOffer().getBankAccountCountry();
|
||||
hBox.getChildren().add(ImageUtil.getCountryIconImageView(offerBookListItem
|
||||
.getOffer().getBankAccountCountry()));
|
||||
Tooltip.install(this, new Tooltip(country.name));
|
||||
if (newItem != null && !empty) {
|
||||
final Offer offer = newItem.getOffer();
|
||||
verifyIfTradable(offer);
|
||||
String title;
|
||||
if (isTradable) {
|
||||
if (model.isMyOffer(offer)) {
|
||||
iconView.setId("image-remove");
|
||||
title = "Remove";
|
||||
button.setOnAction(e -> onRemoveOpenOffer(offer));
|
||||
} else {
|
||||
iconView.setId(offer.getDirection() == Offer.Direction.SELL ? "image-buy" : "image-sell");
|
||||
title = model.getDirectionLabel(offer);
|
||||
button.setOnAction(e -> onTakeOffer(offer));
|
||||
}
|
||||
}
|
||||
else {
|
||||
title = "Not matching";
|
||||
iconView.setId(null);
|
||||
button.setOnAction(e -> onShowInfo(isPaymentAccountValidForOffer, hasMatchingArbitrator));
|
||||
}
|
||||
|
||||
button.setText(title);
|
||||
setGraphic(button);
|
||||
}
|
||||
else {
|
||||
setGraphic(null);
|
||||
TableRow tableRow = getTableRow();
|
||||
if (tableRow != null) tableRow.setOpacity(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}*/
|
||||
|
||||
private void setBankAccountTypeColumnCellFactory() {
|
||||
bankAccountTypeColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
|
||||
bankAccountTypeColumn.setCellFactory(
|
||||
new Callback<TableColumn<OfferBookListItem, OfferBookListItem>, TableCell<OfferBookListItem,
|
||||
OfferBookListItem>>() {
|
||||
@Override
|
||||
public TableCell<OfferBookListItem, OfferBookListItem> call(
|
||||
TableColumn<OfferBookListItem, OfferBookListItem> column) {
|
||||
return new TableCell<OfferBookListItem, OfferBookListItem>() {
|
||||
@Override
|
||||
public void updateItem(final OfferBookListItem offerBookListItem, boolean empty) {
|
||||
super.updateItem(offerBookListItem, empty);
|
||||
setText(model.getBankAccountType(offerBookListItem));
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
return column;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,153 +17,89 @@
|
|||
|
||||
package io.bitsquare.gui.main.offer.offerbook;
|
||||
|
||||
import io.bitsquare.app.BitsquareApp;
|
||||
import com.google.inject.Inject;
|
||||
import io.bitsquare.common.handlers.ErrorMessageHandler;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.gui.common.model.ActivatableWithDataModel;
|
||||
import io.bitsquare.gui.common.model.ViewModel;
|
||||
import io.bitsquare.gui.common.model.ActivatableViewModel;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.gui.util.validation.InputValidator;
|
||||
import io.bitsquare.gui.util.validation.OptionalBtcValidator;
|
||||
import io.bitsquare.gui.util.validation.OptionalFiatValidator;
|
||||
import io.bitsquare.locale.BSResources;
|
||||
import io.bitsquare.locale.CountryUtil;
|
||||
import io.bitsquare.locale.CurrencyUtil;
|
||||
import io.bitsquare.locale.TradeCurrency;
|
||||
import io.bitsquare.p2p.Address;
|
||||
import io.bitsquare.payment.PaymentMethod;
|
||||
import io.bitsquare.payment.SepaAccount;
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.utils.Fiat;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import io.bitsquare.trade.offer.OpenOfferManager;
|
||||
import io.bitsquare.user.Preferences;
|
||||
import io.bitsquare.user.User;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.FilteredList;
|
||||
import javafx.collections.transformation.SortedList;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
class OfferBookViewModel extends ActivatableWithDataModel<OfferBookDataModel> implements ViewModel {
|
||||
private static final Logger log = LoggerFactory.getLogger(OfferBookViewModel.class);
|
||||
|
||||
private final OptionalBtcValidator optionalBtcValidator;
|
||||
class OfferBookViewModel extends ActivatableViewModel {
|
||||
private final OpenOfferManager openOfferManager;
|
||||
private final User user;
|
||||
private final OfferBook offerBook;
|
||||
private final Preferences preferences;
|
||||
private final BSFormatter formatter;
|
||||
private final OptionalFiatValidator optionalFiatValidator;
|
||||
|
||||
final StringProperty amount = new SimpleStringProperty();
|
||||
final StringProperty price = new SimpleStringProperty();
|
||||
final StringProperty volume = new SimpleStringProperty();
|
||||
final StringProperty btcCode = new SimpleStringProperty();
|
||||
final StringProperty fiatCode = new SimpleStringProperty();
|
||||
final StringProperty restrictionsInfo = new SimpleStringProperty();
|
||||
private final FilteredList<OfferBookListItem> filteredItems;
|
||||
private final SortedList<OfferBookListItem> sortedItems;
|
||||
private TradeCurrency tradeCurrency;
|
||||
|
||||
private ChangeListener<String> amountListener;
|
||||
private ChangeListener<String> priceListener;
|
||||
private ChangeListener<String> volumeListener;
|
||||
private ChangeListener<Coin> amountAsCoinListener;
|
||||
private ChangeListener<Fiat> priceAsFiatListener;
|
||||
private ChangeListener<Fiat> volumeAsFiatListener;
|
||||
private Offer.Direction direction;
|
||||
|
||||
private final StringProperty btcCode = new SimpleStringProperty();
|
||||
final StringProperty tradeCurrencyCode = new SimpleStringProperty();
|
||||
private PaymentMethod paymentMethod = new AllPaymentMethodsEntry();
|
||||
private final ObservableList<OfferBookListItem> offerBookListItems;
|
||||
private final ListChangeListener<OfferBookListItem> listChangeListener;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor, lifecycle
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public OfferBookViewModel(OfferBookDataModel dataModel, OptionalFiatValidator optionalFiatValidator,
|
||||
OptionalBtcValidator optionalBtcValidator, BSFormatter formatter) {
|
||||
super(dataModel);
|
||||
public OfferBookViewModel(User user, OpenOfferManager openOfferManager, OfferBook offerBook,
|
||||
Preferences preferences,
|
||||
BSFormatter formatter) {
|
||||
super();
|
||||
|
||||
this.optionalFiatValidator = optionalFiatValidator;
|
||||
this.optionalBtcValidator = optionalBtcValidator;
|
||||
this.openOfferManager = openOfferManager;
|
||||
this.user = user;
|
||||
this.offerBook = offerBook;
|
||||
this.preferences = preferences;
|
||||
this.formatter = formatter;
|
||||
|
||||
createListeners();
|
||||
offerBookListItems = offerBook.getOfferBookListItems();
|
||||
listChangeListener = c -> filterList();
|
||||
|
||||
this.filteredItems = new FilteredList<>(offerBookListItems);
|
||||
this.sortedItems = new SortedList<>(filteredItems);
|
||||
|
||||
tradeCurrency = CurrencyUtil.getDefaultFiatCurrency();
|
||||
tradeCurrencyCode.set(tradeCurrency.getCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doActivate() {
|
||||
amount.set("");
|
||||
price.set("");
|
||||
volume.set("");
|
||||
|
||||
addBindings();
|
||||
addListeners();
|
||||
protected void activate() {
|
||||
btcCode.bind(preferences.btcDenominationProperty());
|
||||
offerBookListItems.addListener(listChangeListener);
|
||||
offerBook.fillOfferBookListItems();
|
||||
filterList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doDeactivate() {
|
||||
removeBindings();
|
||||
removeListeners();
|
||||
}
|
||||
|
||||
private void addBindings() {
|
||||
btcCode.bind(dataModel.btcCode);
|
||||
fiatCode.bind(dataModel.fiatCode);
|
||||
restrictionsInfo.bind(dataModel.restrictionsInfo);
|
||||
}
|
||||
|
||||
private void removeBindings() {
|
||||
protected void deactivate() {
|
||||
btcCode.unbind();
|
||||
fiatCode.unbind();
|
||||
restrictionsInfo.unbind();
|
||||
}
|
||||
|
||||
private void createListeners() {
|
||||
amountAsCoinListener = (ov, oldValue, newValue) -> amount.set(formatter.formatCoin(newValue));
|
||||
priceAsFiatListener = (ov, oldValue, newValue) -> price.set(formatter.formatFiat(newValue));
|
||||
volumeAsFiatListener = (ov, oldValue, newValue) -> volume.set(formatter.formatFiat(newValue));
|
||||
|
||||
amountListener = (ov, oldValue, newValue) -> {
|
||||
if (isBtcInputValid(newValue).isValid) {
|
||||
setAmountToModel();
|
||||
setPriceToModel();
|
||||
dataModel.calculateVolume();
|
||||
}
|
||||
};
|
||||
priceListener = (ov, oldValue, newValue) -> {
|
||||
if (isFiatInputValid(newValue).isValid) {
|
||||
setAmountToModel();
|
||||
setPriceToModel();
|
||||
dataModel.calculateVolume();
|
||||
}
|
||||
};
|
||||
volumeListener = (ov, oldValue, newValue) -> {
|
||||
if (isFiatInputValid(newValue).isValid) {
|
||||
setPriceToModel();
|
||||
setVolumeToModel();
|
||||
dataModel.calculateAmount();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void addListeners() {
|
||||
// Binding with Bindings.createObjectBinding does not work because of bi-directional binding
|
||||
dataModel.amountAsCoinProperty().addListener(amountAsCoinListener);
|
||||
dataModel.priceAsFiatProperty().addListener(priceAsFiatListener);
|
||||
dataModel.volumeAsFiatProperty().addListener(volumeAsFiatListener);
|
||||
|
||||
// Bidirectional bindings are used for all input fields: amount, price and volume
|
||||
// We do volume/amount calculation during input, so user has immediate feedback
|
||||
amount.addListener(amountListener);
|
||||
price.addListener(priceListener);
|
||||
volume.addListener(volumeListener);
|
||||
|
||||
if (BitsquareApp.DEV_MODE) {
|
||||
amount.set("1");
|
||||
price.set("300");
|
||||
setAmountToModel();
|
||||
setPriceToModel();
|
||||
}
|
||||
}
|
||||
|
||||
private void removeListeners() {
|
||||
amount.removeListener(amountListener);
|
||||
price.removeListener(priceListener);
|
||||
volume.removeListener(volumeListener);
|
||||
|
||||
dataModel.amountAsCoinProperty().removeListener(amountAsCoinListener);
|
||||
dataModel.priceAsFiatProperty().removeListener(priceAsFiatListener);
|
||||
dataModel.volumeAsFiatProperty().removeListener(volumeAsFiatListener);
|
||||
}
|
||||
|
||||
|
||||
|
@ -172,7 +108,7 @@ class OfferBookViewModel extends ActivatableWithDataModel<OfferBookDataModel> im
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setDirection(Offer.Direction direction) {
|
||||
dataModel.setDirection(direction);
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
|
||||
|
@ -180,8 +116,20 @@ class OfferBookViewModel extends ActivatableWithDataModel<OfferBookDataModel> im
|
|||
// UI actions
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void onCancelOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
dataModel.onCancelOpenOffer(offer, resultHandler, errorMessageHandler);
|
||||
public void onSetTradeCurrency(TradeCurrency tradeCurrency) {
|
||||
this.tradeCurrency = tradeCurrency;
|
||||
tradeCurrencyCode.set(tradeCurrency.getCode());
|
||||
/* if (!(tradeCurrency instanceof AllTradeCurrenciesEntry))*/
|
||||
//offerBook.getOffers(tradeCurrencyCode.get());
|
||||
}
|
||||
|
||||
public void onSetPaymentMethod(PaymentMethod paymentMethod) {
|
||||
this.paymentMethod = paymentMethod;
|
||||
filterList();
|
||||
}
|
||||
|
||||
void onRemoveOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
openOfferManager.onRemoveOpenOffer(offer, resultHandler, errorMessageHandler);
|
||||
}
|
||||
|
||||
|
||||
|
@ -189,22 +137,35 @@ class OfferBookViewModel extends ActivatableWithDataModel<OfferBookDataModel> im
|
|||
// Getters
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
boolean isTradable(Offer offer) {
|
||||
return dataModel.isTradable(offer);
|
||||
}
|
||||
|
||||
SortedList<OfferBookListItem> getOfferList() {
|
||||
return dataModel.getOfferList();
|
||||
}
|
||||
|
||||
boolean isRegistered() {
|
||||
return dataModel.isRegistered();
|
||||
return sortedItems;
|
||||
}
|
||||
|
||||
boolean isMyOffer(Offer offer) {
|
||||
return dataModel.isMyOffer(offer);
|
||||
return openOfferManager.isMyOffer(offer);
|
||||
}
|
||||
|
||||
Offer.Direction getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public ObservableList<TradeCurrency> getTradeCurrencies() {
|
||||
ObservableList<TradeCurrency> list = preferences.getTradeCurrenciesAsObservable();
|
||||
/* list.add(0, new AllTradeCurrenciesEntry());*/
|
||||
return list;
|
||||
}
|
||||
|
||||
public TradeCurrency getTradeCurrency() {
|
||||
return tradeCurrency;
|
||||
}
|
||||
|
||||
public ObservableList<PaymentMethod> getPaymentMethods() {
|
||||
ObservableList<PaymentMethod> list = FXCollections.observableArrayList(PaymentMethod.ALL_VALUES);
|
||||
list.add(0, paymentMethod);
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
String getAmount(OfferBookListItem item) {
|
||||
return (item != null) ? formatter.formatCoin(item.getOffer().getAmount()) +
|
||||
" (" + formatter.formatCoin(item.getOffer().getMinAmount()) + ")" : "";
|
||||
|
@ -219,49 +180,108 @@ class OfferBookViewModel extends ActivatableWithDataModel<OfferBookDataModel> im
|
|||
" (" + formatter.formatFiat(item.getOffer().getMinOfferVolume()) + ")" : "";
|
||||
}
|
||||
|
||||
String getBankAccountType(OfferBookListItem item) {
|
||||
return (item != null) ? BSResources.get(item.getOffer().getFiatAccountType().toString()) : "";
|
||||
String getPaymentMethod(OfferBookListItem item) {
|
||||
String result = "";
|
||||
if (item != null) {
|
||||
Offer offer = item.getOffer();
|
||||
String method = BSResources.get(offer.getPaymentMethod().getId());
|
||||
String methodCountryCode = offer.getPaymentMethodCountryCode();
|
||||
|
||||
if (methodCountryCode != null)
|
||||
result = method + " (" + methodCountryCode + ")";
|
||||
else
|
||||
result = method;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
String getPaymentMethodToolTip(OfferBookListItem item) {
|
||||
String result = "";
|
||||
if (item != null) {
|
||||
Offer offer = item.getOffer();
|
||||
String method = BSResources.get(offer.getPaymentMethod().getId());
|
||||
String methodCountryCode = offer.getPaymentMethodCountryCode();
|
||||
|
||||
if (methodCountryCode != null)
|
||||
result = method + "\nOfferers country of bank: " + CountryUtil.getNameByCode(methodCountryCode);
|
||||
else
|
||||
result = method;
|
||||
|
||||
List<String> acceptedCountryCodes = offer.getAcceptedCountryCodes();
|
||||
if (acceptedCountryCodes != null && acceptedCountryCodes.size() > 0) {
|
||||
if (CountryUtil.containsAllSepaEuroCountries(acceptedCountryCodes))
|
||||
result += "\nAccepted taker countries: All Euro countries";
|
||||
else
|
||||
result += "\nAccepted taker countries: " + CountryUtil.getNamesByCodesString(acceptedCountryCodes);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
String getDirectionLabel(Offer offer) {
|
||||
return formatter.formatDirection(offer.getMirroredDirection());
|
||||
}
|
||||
|
||||
Offer.Direction getDirection() {
|
||||
return dataModel.getDirection();
|
||||
}
|
||||
|
||||
Coin getAmountAsCoin() {
|
||||
return dataModel.getAmountAsCoin();
|
||||
}
|
||||
|
||||
Fiat getPriceAsCoin() {
|
||||
return dataModel.getPriceAsFiat();
|
||||
return formatter.getDirection(offer.getMirroredDirection());
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
// Checks
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private InputValidator.ValidationResult isBtcInputValid(String input) {
|
||||
return optionalBtcValidator.validate(input);
|
||||
boolean hasPaymentAccount() {
|
||||
return user.currentPaymentAccountProperty().get() != null;
|
||||
}
|
||||
|
||||
private InputValidator.ValidationResult isFiatInputValid(String input) {
|
||||
return optionalFiatValidator.validate(input);
|
||||
boolean isPaymentAccountValidForOffer(Offer offer) {
|
||||
|
||||
Optional<TradeCurrency> result1 = user.getPaymentAccounts().stream()
|
||||
.filter(paymentAccount -> paymentAccount.getPaymentMethod().equals(offer.getPaymentMethod()))
|
||||
.filter(paymentAccount -> {
|
||||
List<String> offerAcceptedCountryCodes = offer.getAcceptedCountryCodes();
|
||||
if (offerAcceptedCountryCodes != null && paymentAccount instanceof SepaAccount) {
|
||||
return ((SepaAccount) paymentAccount).getAcceptedCountryCodes().stream()
|
||||
.filter(offerAcceptedCountryCodes::contains)
|
||||
.findAny().isPresent();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
.flatMap(paymentAccount -> paymentAccount.getTradeCurrencies().stream())
|
||||
.filter(currency -> currency.getCode().equals(offer.getCurrencyCode())).findAny();
|
||||
return result1.isPresent();
|
||||
}
|
||||
|
||||
private void setAmountToModel() {
|
||||
dataModel.setAmount(formatter.parseToCoinWith4Decimals(amount.get()));
|
||||
public boolean hasPaymentAccountForCurrency() {
|
||||
return user.hasPaymentAccountForCurrency(tradeCurrency);
|
||||
}
|
||||
|
||||
private void setPriceToModel() {
|
||||
dataModel.setPrice(formatter.parseToFiatWith2Decimals(price.get()));
|
||||
boolean hasAcceptedArbitrators() {
|
||||
return user.getAcceptedArbitrators() != null && !user.getAcceptedArbitrators().isEmpty();
|
||||
}
|
||||
|
||||
private void setVolumeToModel() {
|
||||
dataModel.setVolume(formatter.parseToFiatWith2Decimals(volume.get()));
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Filters
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void filterList() {
|
||||
filteredItems.setPredicate(offerBookListItem -> {
|
||||
Offer offer = offerBookListItem.getOffer();
|
||||
boolean directionResult = offer.getDirection() != direction;
|
||||
boolean paymentMethodResult = true;
|
||||
if (!(paymentMethod instanceof AllPaymentMethodsEntry))
|
||||
paymentMethodResult = offer.getPaymentMethod().equals(paymentMethod);
|
||||
|
||||
return directionResult && paymentMethodResult;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public boolean hasMatchingArbitrator(Offer offer) {
|
||||
for (Address offerArbitratorAddress : offer.getArbitratorAddresses()) {
|
||||
for (Address acceptedArbitratorAddress : user.getAcceptedArbitratorAddresses()) {
|
||||
if (offerArbitratorAddress.equals(acceptedArbitratorAddress))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,90 +17,217 @@
|
|||
|
||||
package io.bitsquare.gui.main.offer.takeoffer;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import io.bitsquare.arbitration.Arbitrator;
|
||||
import io.bitsquare.btc.AddressEntry;
|
||||
import io.bitsquare.btc.FeePolicy;
|
||||
import io.bitsquare.btc.TradeWalletService;
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.btc.listeners.BalanceListener;
|
||||
import io.bitsquare.gui.common.model.Activatable;
|
||||
import io.bitsquare.gui.common.model.DataModel;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.gui.common.model.ActivatableDataModel;
|
||||
import io.bitsquare.gui.popups.WalletPasswordPopup;
|
||||
import io.bitsquare.locale.TradeCurrency;
|
||||
import io.bitsquare.payment.PaymentAccount;
|
||||
import io.bitsquare.payment.PaymentMethod;
|
||||
import io.bitsquare.trade.TradeManager;
|
||||
import io.bitsquare.trade.handlers.TakeOfferResultHandler;
|
||||
import io.bitsquare.trade.handlers.TradeResultHandler;
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
import io.bitsquare.user.Preferences;
|
||||
|
||||
import io.bitsquare.user.User;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.utils.ExchangeRate;
|
||||
import org.bitcoinj.utils.Fiat;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class TakeOfferDataModel implements Activatable, DataModel {
|
||||
private static final Logger log = LoggerFactory.getLogger(TakeOfferDataModel.class);
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Domain for that UI element.
|
||||
* Note that the create offer domain has a deeper scope in the application domain (TradeManager).
|
||||
* That model is just responsible for the domain specific parts displayed needed in that UI element.
|
||||
*/
|
||||
class TakeOfferDataModel extends ActivatableDataModel {
|
||||
private final TradeManager tradeManager;
|
||||
private final TradeWalletService tradeWalletService;
|
||||
private final WalletService walletService;
|
||||
private final User user;
|
||||
private final WalletPasswordPopup walletPasswordPopup;
|
||||
private final Preferences preferences;
|
||||
|
||||
private final Coin offerFeeAsCoin;
|
||||
private final Coin networkFeeAsCoin;
|
||||
private final Coin securityDepositAsCoin;
|
||||
|
||||
private Offer offer;
|
||||
private AddressEntry addressEntry;
|
||||
|
||||
final StringProperty btcCode = new SimpleStringProperty();
|
||||
|
||||
final BooleanProperty isWalletFunded = new SimpleBooleanProperty();
|
||||
final BooleanProperty useMBTC = new SimpleBooleanProperty();
|
||||
|
||||
final BooleanProperty isWalletFunded = new SimpleBooleanProperty();
|
||||
final ObjectProperty<Coin> amountAsCoin = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Fiat> volumeAsFiat = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Coin> totalToPayAsCoin = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Coin> securityDepositAsCoin = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Coin> offerFeeAsCoin = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Coin> networkFeeAsCoin = new SimpleObjectProperty<>();
|
||||
|
||||
private BalanceListener balanceListener;
|
||||
private PaymentAccount paymentAccount;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor, lifecycle
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@Inject
|
||||
public TakeOfferDataModel(TradeManager tradeManager,
|
||||
WalletService walletService,
|
||||
Preferences preferences) {
|
||||
TakeOfferDataModel(TradeManager tradeManager, TradeWalletService tradeWalletService,
|
||||
WalletService walletService, User user, WalletPasswordPopup walletPasswordPopup,
|
||||
Preferences preferences) {
|
||||
this.tradeManager = tradeManager;
|
||||
this.tradeWalletService = tradeWalletService;
|
||||
this.walletService = walletService;
|
||||
this.user = user;
|
||||
this.walletPasswordPopup = walletPasswordPopup;
|
||||
this.preferences = preferences;
|
||||
|
||||
offerFeeAsCoin.set(FeePolicy.CREATE_OFFER_FEE);
|
||||
networkFeeAsCoin.set(FeePolicy.TX_FEE);
|
||||
offerFeeAsCoin = FeePolicy.CREATE_OFFER_FEE;
|
||||
networkFeeAsCoin = FeePolicy.TX_FEE;
|
||||
securityDepositAsCoin = FeePolicy.SECURITY_DEPOSIT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activate() {
|
||||
protected void activate() {
|
||||
// when leaving screen we reset state
|
||||
offer.setState(Offer.State.UNDEFINED);
|
||||
|
||||
addBindings();
|
||||
addListeners();
|
||||
updateBalance(walletService.getBalanceForAddress(addressEntry.getAddress()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
protected void deactivate() {
|
||||
removeBindings();
|
||||
removeListeners();
|
||||
tradeManager.onCancelAvailabilityRequest(offer);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// called before activate
|
||||
void initWithData(Offer offer) {
|
||||
this.offer = offer;
|
||||
|
||||
ObservableList<PaymentAccount> possiblePaymentAccounts = getPossiblePaymentAccounts();
|
||||
checkArgument(!possiblePaymentAccounts.isEmpty(), "possiblePaymentAccounts.isEmpty()");
|
||||
paymentAccount = possiblePaymentAccounts.get(0);
|
||||
|
||||
amountAsCoin.set(offer.getAmount());
|
||||
|
||||
calculateVolume();
|
||||
calculateTotalToPay();
|
||||
|
||||
addressEntry = walletService.getAddressEntryByOfferId(offer.getId());
|
||||
checkNotNull(addressEntry, "addressEntry must not be null");
|
||||
|
||||
balanceListener = new BalanceListener(addressEntry.getAddress()) {
|
||||
@Override
|
||||
public void onBalanceChanged(@NotNull Coin balance) {
|
||||
updateBalance(balance);
|
||||
}
|
||||
};
|
||||
|
||||
offer.resetState();
|
||||
}
|
||||
|
||||
void checkOfferAvailability(ResultHandler resultHandler) {
|
||||
tradeManager.checkOfferAvailability(offer, resultHandler);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// UI actions
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// errorMessageHandler is used only in the check availability phase. As soon we have a trade we write the error msg in the trade object as we want to
|
||||
// have it persisted as well.
|
||||
void onTakeOffer(TradeResultHandler tradeResultHandler) {
|
||||
if (walletService.getWallet().isEncrypted() && tradeWalletService.getAesKey() == null) {
|
||||
walletPasswordPopup.show().onAesKey(aesKey -> {
|
||||
tradeWalletService.setAesKey(aesKey);
|
||||
doTakeOffer(tradeResultHandler);
|
||||
});
|
||||
} else {
|
||||
doTakeOffer(tradeResultHandler);
|
||||
}
|
||||
}
|
||||
|
||||
private void doTakeOffer(TradeResultHandler tradeResultHandler) {
|
||||
tradeManager.onTakeOffer(amountAsCoin.get(),
|
||||
offer,
|
||||
paymentAccount.getId(),
|
||||
tradeResultHandler
|
||||
);
|
||||
}
|
||||
|
||||
void onSecurityDepositInfoDisplayed() {
|
||||
preferences.setDisplaySecurityDepositInfo(false);
|
||||
}
|
||||
|
||||
public void onPaymentAccountSelected(PaymentAccount paymentAccount) {
|
||||
if (paymentAccount != null)
|
||||
this.paymentAccount = paymentAccount;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getters
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Offer.Direction getDirection() {
|
||||
return offer.getDirection();
|
||||
}
|
||||
|
||||
boolean getDisplaySecurityDepositInfo() {
|
||||
return preferences.getDisplaySecurityDepositInfo();
|
||||
}
|
||||
|
||||
public Offer getOffer() {
|
||||
return offer;
|
||||
}
|
||||
|
||||
ObservableList<PaymentAccount> getPossiblePaymentAccounts() {
|
||||
ObservableList<PaymentAccount> paymentAccounts = FXCollections.observableArrayList(new ArrayList<>());
|
||||
for (PaymentAccount paymentAccount : user.getPaymentAccounts()) {
|
||||
if (paymentAccount.getPaymentMethod().equals(offer.getPaymentMethod())) {
|
||||
for (TradeCurrency tradeCurrency : paymentAccount.getTradeCurrencies()) {
|
||||
if (tradeCurrency.getCode().equals(offer.getCurrencyCode())) {
|
||||
paymentAccounts.add(paymentAccount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return paymentAccounts;
|
||||
}
|
||||
|
||||
boolean hasAcceptedArbitrators() {
|
||||
return user.getAcceptedArbitrators().size() > 0;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Bindings, listeners
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void addBindings() {
|
||||
btcCode.bind(preferences.btcDenominationProperty());
|
||||
}
|
||||
|
@ -114,54 +241,7 @@ class TakeOfferDataModel implements Activatable, DataModel {
|
|||
}
|
||||
|
||||
private void removeListeners() {
|
||||
if (addressEntry != null)
|
||||
walletService.removeBalanceListener(balanceListener);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void initWithData(Coin amount, Offer offer) {
|
||||
this.offer = offer;
|
||||
securityDepositAsCoin.set(offer.getSecurityDeposit());
|
||||
|
||||
if (amount != null && !amount.isGreaterThan(offer.getAmount()) && !offer.getMinAmount().isGreaterThan(amount))
|
||||
amountAsCoin.set(amount);
|
||||
else
|
||||
amountAsCoin.set(offer.getAmount());
|
||||
|
||||
calculateVolume();
|
||||
calculateTotalToPay();
|
||||
|
||||
addressEntry = walletService.getAddressEntry(offer.getId());
|
||||
assert addressEntry != null;
|
||||
|
||||
balanceListener = new BalanceListener(addressEntry.getAddress()) {
|
||||
@Override
|
||||
public void onBalanceChanged(@NotNull Coin balance) {
|
||||
updateBalance(balance);
|
||||
}
|
||||
};
|
||||
updateBalance(walletService.getBalanceForAddress(addressEntry.getAddress()));
|
||||
|
||||
// delay a bit to get the listeners called
|
||||
offer.resetState();
|
||||
Platform.runLater(() -> tradeManager.onCheckOfferAvailability(offer));
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// UI calls
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void onTakeOffer(TakeOfferResultHandler handler) {
|
||||
tradeManager.onTakeOffer(amountAsCoin.get(), offer, handler);
|
||||
}
|
||||
|
||||
void onSecurityDepositInfoDisplayed() {
|
||||
preferences.setDisplaySecurityDepositInfo(false);
|
||||
walletService.removeBalanceListener(balanceListener);
|
||||
}
|
||||
|
||||
|
||||
|
@ -180,25 +260,15 @@ class TakeOfferDataModel implements Activatable, DataModel {
|
|||
|
||||
void calculateTotalToPay() {
|
||||
if (getDirection() == Offer.Direction.SELL)
|
||||
totalToPayAsCoin.set(offerFeeAsCoin.get().add(networkFeeAsCoin.get()).add(securityDepositAsCoin.get()));
|
||||
totalToPayAsCoin.set(offerFeeAsCoin.add(networkFeeAsCoin).add(securityDepositAsCoin));
|
||||
else
|
||||
totalToPayAsCoin.set(offerFeeAsCoin.get().add(networkFeeAsCoin.get()).add(securityDepositAsCoin.get()).add(amountAsCoin.get()));
|
||||
totalToPayAsCoin.set(offerFeeAsCoin.add(networkFeeAsCoin).add(securityDepositAsCoin).add(amountAsCoin.get()));
|
||||
}
|
||||
|
||||
private void updateBalance(@NotNull Coin balance) {
|
||||
isWalletFunded.set(totalToPayAsCoin.get() != null && balance.compareTo(totalToPayAsCoin.get()) >= 0);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getters
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Offer.Direction getDirection() {
|
||||
return offer.getDirection();
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
boolean isMinAmountLessOrEqualAmount() {
|
||||
//noinspection SimplifiableIfStatement
|
||||
if (offer != null && offer.getMinAmount() != null && amountAsCoin.get() != null)
|
||||
|
@ -213,15 +283,39 @@ class TakeOfferDataModel implements Activatable, DataModel {
|
|||
return true;
|
||||
}
|
||||
|
||||
boolean getDisplaySecurityDepositInfo() {
|
||||
return preferences.getDisplaySecurityDepositInfo();
|
||||
public PaymentMethod getPaymentMethod() {
|
||||
return offer.getPaymentMethod();
|
||||
}
|
||||
|
||||
WalletService getWalletService() {
|
||||
return walletService;
|
||||
public TradeCurrency getTradeCurrency() {
|
||||
return new TradeCurrency(offer.getCurrencyCode());
|
||||
}
|
||||
|
||||
AddressEntry getAddressEntry() {
|
||||
public Coin getSecurityDepositAsCoin() {
|
||||
return securityDepositAsCoin;
|
||||
}
|
||||
|
||||
public Coin getOfferFeeAsCoin() {
|
||||
return offerFeeAsCoin;
|
||||
}
|
||||
|
||||
public Coin getNetworkFeeAsCoin() {
|
||||
return networkFeeAsCoin;
|
||||
}
|
||||
|
||||
public AddressEntry getAddressEntry() {
|
||||
return addressEntry;
|
||||
}
|
||||
|
||||
public boolean getShowTakeOfferConfirmation() {
|
||||
return preferences.getShowTakeOfferConfirmation();
|
||||
}
|
||||
|
||||
public void setShowTakeOfferConfirmation(boolean selected) {
|
||||
preferences.setShowTakeOfferConfirmation(selected);
|
||||
}
|
||||
|
||||
public List<Arbitrator> getArbitrators() {
|
||||
return user.getAcceptedArbitrators();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,265 +17,8 @@
|
|||
~ along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<?import io.bitsquare.gui.components.AddressTextField?>
|
||||
<?import io.bitsquare.gui.components.BalanceTextField?>
|
||||
<?import io.bitsquare.gui.components.InfoDisplay?>
|
||||
<?import io.bitsquare.gui.components.InputTextField?>
|
||||
<?import io.bitsquare.gui.components.TitledGroupBg?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.image.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.text.*?>
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
<AnchorPane fx:id="root" fx:controller="io.bitsquare.gui.main.offer.takeoffer.TakeOfferView"
|
||||
AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="10.0"
|
||||
AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
|
||||
<ScrollPane fx:id="scrollPane" onScroll="#onScroll" hbarPolicy="NEVER" vbarPolicy="NEVER" fitToWidth="true" fitToHeight="true"
|
||||
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"
|
||||
AnchorPane.bottomAnchor="0.0">
|
||||
|
||||
<!--suppress JavaFxUnresolvedFxIdReference -->
|
||||
<GridPane fx:id="gridPane" hgap="5.0" vgap="5.0">
|
||||
<padding>
|
||||
<Insets bottom="-10.0" left="25.0" top="30.0" right="25"/>
|
||||
</padding>
|
||||
|
||||
<!--
|
||||
Amount/Price group
|
||||
-->
|
||||
<TitledGroupBg fx:id="priceAmountPane" text="%takeOffer.amountPriceBox.title"
|
||||
GridPane.rowSpan="3" GridPane.columnSpan="3"/>
|
||||
|
||||
<VBox alignment="CENTER" spacing="6" GridPane.rowSpan="2">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="20.0"/>
|
||||
</GridPane.margin>
|
||||
<ImageView fx:id="imageView" pickOnBounds="true"/>
|
||||
<Label fx:id="buyLabel" id="direction-icon-label" text="%takeOffer.amountPriceBox.subTitle"
|
||||
alignment="CENTER">
|
||||
<padding>
|
||||
<Insets top="-5.0"/>
|
||||
</padding>
|
||||
</Label>
|
||||
</VBox>
|
||||
|
||||
<HBox GridPane.columnIndex="1" alignment="CENTER_LEFT" spacing="5"
|
||||
>
|
||||
<GridPane.margin>
|
||||
<Insets right="10.0" top="20.0"/>
|
||||
</GridPane.margin>
|
||||
<VBox spacing="4">
|
||||
<Label fx:id="amountDescriptionLabel" id="input-description-label" prefWidth="170"/>
|
||||
<HBox>
|
||||
<InputTextField fx:id="amountTextField" id="text-input-with-currency-text-field"
|
||||
promptText="%takeOffer.amount.prompt" prefWidth="170"
|
||||
alignment="CENTER_RIGHT"/>
|
||||
<Label fx:id="amountBtcLabel" id="currency-info-label"/>
|
||||
</HBox>
|
||||
</VBox>
|
||||
<Label text="x">
|
||||
<font>
|
||||
<Font name="Helvetica-Bold" size="20.0"/>
|
||||
</font>
|
||||
<padding>
|
||||
<Insets top="14.0" left="3" right="3"/>
|
||||
</padding>
|
||||
</Label>
|
||||
|
||||
<VBox spacing="4">
|
||||
<Label fx:id="priceDescriptionLabel" id="input-description-label" prefWidth="170"/>
|
||||
<TextField fx:id="priceTextField" id="text-input-with-currency-text-field"
|
||||
prefWidth="170" alignment="CENTER_RIGHT" mouseTransparent="true" editable="false"
|
||||
focusTraversable="false"/>
|
||||
</VBox>
|
||||
|
||||
<Label text="=">
|
||||
<font>
|
||||
<Font name="Helvetica-Bold" size="20.0"/>
|
||||
</font>
|
||||
<padding>
|
||||
<Insets top="14.0" left="2" right="2"/>
|
||||
</padding>
|
||||
</Label>
|
||||
|
||||
<VBox spacing="4">
|
||||
<Label fx:id="volumeDescriptionLabel" id="input-description-label" prefWidth="170"/>
|
||||
<TextField fx:id="volumeTextField" id="text-input-with-currency-text-field"
|
||||
prefWidth="170" alignment="CENTER_RIGHT" mouseTransparent="true" editable="false"
|
||||
focusTraversable="false"/>
|
||||
</VBox>
|
||||
</HBox>
|
||||
|
||||
<VBox GridPane.columnIndex="1" GridPane.rowIndex="1" spacing="4"
|
||||
>
|
||||
<GridPane.margin>
|
||||
<Insets right="10.0" top="5.0" bottom="5.0"/>
|
||||
</GridPane.margin>
|
||||
<Label id="input-description-label" text="%takeOffer.amountPriceBox.amountRangeDescription"
|
||||
prefWidth="170" textAlignment="CENTER"/>
|
||||
<Label fx:id="amountRangeTextField" id="label-with-background" prefWidth="170"
|
||||
style=" -fx-alignment: center;"/>
|
||||
</VBox>
|
||||
|
||||
<InfoDisplay fx:id="amountPriceBoxInfoDisplay" gridPane="$gridPane" onAction="#onOpenGeneralHelp" rowIndex="2"/>
|
||||
|
||||
<Label fx:id="isOfferAvailableLabel" text="%takeOffer.fundsBox.isOfferAvailable" GridPane.rowIndex="3">
|
||||
<GridPane.margin>
|
||||
<Insets top="15.0"/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<ProgressIndicator fx:id="isOfferAvailableProgressIndicator" progress="-1" maxWidth="24" maxHeight="24"
|
||||
GridPane.rowIndex="3" GridPane.columnIndex="1" GridPane.halignment="LEFT">
|
||||
<GridPane.margin>
|
||||
<Insets top="15.0"/>
|
||||
</GridPane.margin>
|
||||
</ProgressIndicator>
|
||||
<Button fx:id="showPaymentInfoScreenButton" text="%takeOffer.amountPriceBox.next" id="show-details-button"
|
||||
GridPane.columnIndex="1" GridPane.rowIndex="3" defaultButton="true" visible="false"
|
||||
onAction="#onShowPaymentScreen">
|
||||
<GridPane.margin>
|
||||
<Insets top="15.0"/>
|
||||
</GridPane.margin>
|
||||
</Button>
|
||||
|
||||
<!--
|
||||
Pay funds group
|
||||
-->
|
||||
<TitledGroupBg fx:id="payFundsPane" text="%takeOffer.fundsBox.title" GridPane.rowIndex="4"
|
||||
GridPane.rowSpan="4" GridPane.columnSpan="3" visible="false"/>
|
||||
|
||||
<HBox GridPane.rowIndex="4" spacing="4" alignment="CENTER_RIGHT">
|
||||
<Label fx:id="totalToPayLabel" text="%takeOffer.fundsBox.totalsNeeded" visible="false"/>
|
||||
<Label fx:id="totalToPayInfoIconLabel" visible="false"/>
|
||||
<GridPane.margin>
|
||||
<Insets top="10.0"/>
|
||||
</GridPane.margin>
|
||||
</HBox>
|
||||
<TextField fx:id="totalToPayTextField" promptText="%takeOffer.fundsBox.totalsNeeded.prompt"
|
||||
GridPane.columnIndex="1" GridPane.rowIndex="4" editable="false"
|
||||
focusTraversable="false" visible="false">
|
||||
<GridPane.margin>
|
||||
<Insets top="10.0"/>
|
||||
</GridPane.margin>
|
||||
</TextField>
|
||||
|
||||
<Label fx:id="addressLabel" text="%takeOffer.fundsBox.address" GridPane.rowIndex="5" visible="false"/>
|
||||
<AddressTextField fx:id="addressTextField" GridPane.columnIndex="1" GridPane.rowIndex="5"
|
||||
focusTraversable="true" visible="false"/>
|
||||
|
||||
<Label fx:id="balanceLabel" text="%takeOffer.fundsBox.balance" GridPane.rowIndex="6" visible="false">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0"/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<BalanceTextField fx:id="balanceTextField" GridPane.columnIndex="1" GridPane.rowIndex="6"
|
||||
focusTraversable="false" visible="false">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0"/>
|
||||
</GridPane.margin>
|
||||
</BalanceTextField>
|
||||
|
||||
<InfoDisplay fx:id="fundsBoxInfoDisplay" gridPane="$gridPane" onAction="#onOpenFundingHelp" rowIndex="7" visible="false"/>
|
||||
|
||||
<HBox spacing="10" GridPane.columnIndex="1" GridPane.rowIndex="8">
|
||||
<Button fx:id="showAdvancedSettingsButton" text="%takeOffer.fundsBox.showAdvanced"
|
||||
onAction="#onToggleShowAdvancedSettings" visible="false"/>
|
||||
<Button fx:id="takeOfferButton" text="%takeOffer.fundsBox.takeOffer" visible="false"
|
||||
defaultButton="true"
|
||||
onAction="#onTakeOffer"/>
|
||||
<ProgressIndicator fx:id="takeOfferSpinner" progress="0" visible="false" prefHeight="24"
|
||||
prefWidth="24"/>
|
||||
<Label fx:id="takeOfferSpinnerInfoLabel" text="%takeOffer.fundsBox.takeOfferSpinnerInfo"
|
||||
visible="false">
|
||||
<padding>
|
||||
<Insets top="5.0"/>
|
||||
</padding>
|
||||
</Label>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="30" top="15.0"/>
|
||||
</GridPane.margin>
|
||||
</HBox>
|
||||
|
||||
<!--
|
||||
Advanced settings group
|
||||
-->
|
||||
<TitledGroupBg fx:id="showDetailsPane" text="%takeOffer.advancedBox.title" GridPane.columnSpan="3"
|
||||
GridPane.rowIndex="9" GridPane.rowSpan="7" visible="false"/>
|
||||
|
||||
<Label fx:id="acceptedCountriesLabel" text="%takeOffer.advancedBox.countries" GridPane.rowIndex="9"
|
||||
visible="false">
|
||||
<GridPane.margin>
|
||||
<Insets top="0.0"/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<TextField fx:id="acceptedCountriesTextField" GridPane.columnIndex="1" GridPane.rowIndex="9"
|
||||
mouseTransparent="true" visible="false" editable="false" focusTraversable="false"/>
|
||||
|
||||
<Label fx:id="acceptedLanguagesLabel" text="%takeOffer.advancedBox.languages" GridPane.rowIndex="10"
|
||||
visible="false"/>
|
||||
<TextField fx:id="acceptedLanguagesTextField" GridPane.columnIndex="1" GridPane.rowIndex="10"
|
||||
mouseTransparent="true" editable="false" focusTraversable="false" visible="false"/>
|
||||
|
||||
<Label fx:id="acceptedArbitratorsLabel" text="%takeOffer.advancedBox.arbitrators" GridPane.rowIndex="11"
|
||||
visible="false"/>
|
||||
<TextField fx:id="acceptedArbitratorsTextField" GridPane.columnIndex="1" GridPane.rowIndex="11"
|
||||
mouseTransparent="true" editable="false" focusTraversable="false" visible="false"/>
|
||||
|
||||
<Label fx:id="bankAccountTypeLabel" text="%takeOffer.advancedBox.txType" GridPane.rowIndex="12"
|
||||
visible="false"/>
|
||||
<TextField fx:id="bankAccountTypeTextField" GridPane.columnIndex="1" GridPane.rowIndex="12" editable="false"
|
||||
mouseTransparent="true" focusTraversable="false" visible="false"/>
|
||||
|
||||
<Label fx:id="bankAccountCurrencyLabel" text="%takeOffer.advancedBox.currency" GridPane.rowIndex="13"
|
||||
visible="false"/>
|
||||
<TextField fx:id="bankAccountCurrencyTextField" GridPane.rowIndex="13" GridPane.columnIndex="1"
|
||||
mouseTransparent="true" editable="false" focusTraversable="false" visible="false"/>
|
||||
|
||||
<Label fx:id="bankAccountCountyLabel" text="%takeOffer.advancedBox.county" GridPane.rowIndex="14"
|
||||
visible="false">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0"/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<TextField fx:id="bankAccountCountyTextField" GridPane.rowIndex="14" GridPane.columnIndex="1"
|
||||
mouseTransparent="true" editable="false" focusTraversable="false" visible="false">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0"/>
|
||||
</GridPane.margin>
|
||||
</TextField>
|
||||
|
||||
<InfoDisplay fx:id="advancedInfoDisplay" gridPane="$gridPane" onAction="#onOpenAdvancedSettingsHelp"
|
||||
rowIndex="15" visible="false"
|
||||
text="%takeOffer.advancedBox.info">
|
||||
</InfoDisplay>
|
||||
|
||||
<columnConstraints>
|
||||
<ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES" minWidth="200"/>
|
||||
<ColumnConstraints hgrow="ALWAYS"/>
|
||||
</columnConstraints>
|
||||
|
||||
<rowConstraints>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints/>
|
||||
<RowConstraints minHeight="35"/>
|
||||
</rowConstraints>
|
||||
|
||||
</GridPane>
|
||||
</ScrollPane>
|
||||
</AnchorPane>
|
||||
|
|
|
@ -17,94 +17,98 @@
|
|||
|
||||
package io.bitsquare.gui.main.offer.takeoffer;
|
||||
|
||||
|
||||
import de.jensd.fx.fontawesome.AwesomeDude;
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
import io.bitsquare.app.BitsquareApp;
|
||||
import io.bitsquare.common.util.Tuple2;
|
||||
import io.bitsquare.common.util.Tuple3;
|
||||
import io.bitsquare.gui.Navigation;
|
||||
import io.bitsquare.gui.OverlayManager;
|
||||
import io.bitsquare.gui.common.view.ActivatableViewAndModel;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.components.AddressTextField;
|
||||
import io.bitsquare.gui.components.BalanceTextField;
|
||||
import io.bitsquare.gui.components.InfoDisplay;
|
||||
import io.bitsquare.gui.components.InputTextField;
|
||||
import io.bitsquare.gui.components.Popups;
|
||||
import io.bitsquare.gui.components.TitledGroupBg;
|
||||
import io.bitsquare.gui.main.MainView;
|
||||
import io.bitsquare.gui.main.help.Help;
|
||||
import io.bitsquare.gui.main.help.HelpId;
|
||||
import io.bitsquare.gui.main.account.AccountView;
|
||||
import io.bitsquare.gui.main.account.content.arbitratorselection.ArbitratorSelectionView;
|
||||
import io.bitsquare.gui.main.account.settings.AccountSettingsView;
|
||||
import io.bitsquare.gui.main.offer.OfferView;
|
||||
import io.bitsquare.gui.main.portfolio.PortfolioView;
|
||||
import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesView;
|
||||
import io.bitsquare.gui.util.ImageUtil;
|
||||
import io.bitsquare.gui.popups.OfferDetailsPopup;
|
||||
import io.bitsquare.gui.popups.Popup;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.gui.util.Layout;
|
||||
import io.bitsquare.locale.BSResources;
|
||||
import io.bitsquare.payment.PaymentAccount;
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.HPos;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Orientation;
|
||||
import javafx.geometry.Point2D;
|
||||
import javafx.geometry.VPos;
|
||||
import javafx.geometry.*;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.image.*;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.text.Font;
|
||||
import javafx.stage.Window;
|
||||
|
||||
import de.jensd.fx.fontawesome.AwesomeDude;
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
|
||||
import javafx.util.StringConverter;
|
||||
import org.controlsfx.control.PopOver;
|
||||
import org.controlsfx.control.action.AbstractAction;
|
||||
import org.controlsfx.control.action.Action;
|
||||
import org.controlsfx.dialog.Dialog;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import org.fxmisc.easybind.Subscription;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static io.bitsquare.gui.util.FormBuilder.*;
|
||||
import static javafx.beans.binding.Bindings.createStringBinding;
|
||||
|
||||
// TODO Implement other positioning method in InoutTextField to display it over the field instead of right side
|
||||
// priceAmountHBox is too large after redesign as to be used as layoutReference.
|
||||
@FxmlView
|
||||
public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOfferViewModel> {
|
||||
|
||||
// TODO convert unneeded properties to static fields
|
||||
|
||||
private final Navigation navigation;
|
||||
private final BSFormatter formatter;
|
||||
private final OfferDetailsPopup offerDetailsPopup;
|
||||
|
||||
@FXML ScrollPane scrollPane;
|
||||
@FXML ImageView imageView;
|
||||
@FXML InputTextField amountTextField;
|
||||
@FXML AddressTextField addressTextField;
|
||||
@FXML BalanceTextField balanceTextField;
|
||||
@FXML ProgressIndicator takeOfferSpinner, isOfferAvailableProgressIndicator;
|
||||
@FXML InfoDisplay amountPriceBoxInfoDisplay, advancedInfoDisplay, fundsBoxInfoDisplay;
|
||||
@FXML TitledGroupBg priceAmountPane, payFundsPane, showDetailsPane;
|
||||
@FXML Button showPaymentInfoScreenButton, showAdvancedSettingsButton, takeOfferButton;
|
||||
@FXML TextField priceTextField, volumeTextField, acceptedArbitratorsTextField, totalToPayTextField,
|
||||
bankAccountTypeTextField, bankAccountCurrencyTextField, bankAccountCountyTextField,
|
||||
acceptedCountriesTextField, acceptedLanguagesTextField;
|
||||
@FXML Label isOfferAvailableLabel, buyLabel, addressLabel, amountDescriptionLabel, amountRangeTextField, balanceLabel, totalToPayLabel,
|
||||
totalToPayInfoIconLabel,
|
||||
bankAccountTypeLabel, bankAccountCurrencyLabel, bankAccountCountyLabel, acceptedCountriesLabel,
|
||||
acceptedLanguagesLabel, acceptedArbitratorsLabel, amountBtcLabel, priceDescriptionLabel,
|
||||
volumeDescriptionLabel, takeOfferSpinnerInfoLabel;
|
||||
private ScrollPane scrollPane;
|
||||
private GridPane gridPane;
|
||||
private ImageView imageView;
|
||||
private AddressTextField addressTextField;
|
||||
private BalanceTextField balanceTextField;
|
||||
private ProgressIndicator takeOfferSpinner;
|
||||
private TitledGroupBg payFundsPane;
|
||||
private Button showPaymentButton, takeOfferButton;
|
||||
private InputTextField amountTextField;
|
||||
private TextField paymentMethodTextField, currencyTextField, totalToPayTextField, priceTextField, volumeTextField, amountRangeTextField;
|
||||
private Label buyLabel, amountDescriptionLabel, addressLabel, balanceLabel, totalToPayLabel, totalToPayInfoIconLabel,
|
||||
amountBtcLabel, priceCurrencyLabel,
|
||||
volumeCurrencyLabel, amountRangeBtcLabel, priceDescriptionLabel, volumeDescriptionLabel, takeOfferSpinnerInfoLabel;
|
||||
|
||||
private ImageView expand;
|
||||
private ImageView collapse;
|
||||
private PopOver totalToPayInfoPopover;
|
||||
|
||||
private OfferView.CloseHandler closeHandler;
|
||||
|
||||
private ChangeListener<String> errorMessageChangeListener;
|
||||
private ChangeListener<Boolean> amountFocusedListener;
|
||||
private ChangeListener<Boolean> isTakeOfferSpinnerVisibleListener;
|
||||
private ChangeListener<TakeOfferViewModel.State> stateListener;
|
||||
private ChangeListener<Boolean> showWarningInvalidBtcDecimalPlacesListener;
|
||||
private ChangeListener<Boolean> showTransactionPublishedScreenListener;
|
||||
|
||||
private int gridRow = 0;
|
||||
private ComboBox<PaymentAccount> paymentAccountsComboBox;
|
||||
private Label paymentAccountsLabel;
|
||||
private Label paymentMethodLabel;
|
||||
private Subscription offerWarningSubscription;
|
||||
private Subscription errorMessageSubscription;
|
||||
private Subscription isTakeOfferSpinnerVisibleSubscription;
|
||||
private Subscription showWarningInvalidBtcDecimalPlacesSubscription;
|
||||
private Subscription showTransactionPublishedScreenSubscription;
|
||||
private Subscription showCheckAvailabilityPopupSubscription;
|
||||
private SimpleBooleanProperty errorPopupDisplayed;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -112,151 +116,151 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
private TakeOfferView(TakeOfferViewModel model, Navigation navigation) {
|
||||
private TakeOfferView(TakeOfferViewModel model, Navigation navigation, BSFormatter formatter, OfferDetailsPopup offerDetailsPopup) {
|
||||
super(model);
|
||||
|
||||
this.navigation = navigation;
|
||||
|
||||
createListeners();
|
||||
this.formatter = formatter;
|
||||
this.offerDetailsPopup = offerDetailsPopup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
protected void initialize() {
|
||||
addScrollPane();
|
||||
addGridPane();
|
||||
addPaymentGroup();
|
||||
addAmountPriceGroup();
|
||||
addFundingGroup();
|
||||
|
||||
amountFocusedListener = (o, oldValue, newValue) -> {
|
||||
model.onFocusOutAmountTextField(oldValue, newValue, amountTextField.getText());
|
||||
amountTextField.setText(model.amount.get());
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doActivate() {
|
||||
addListeners();
|
||||
addBindings();
|
||||
}
|
||||
protected void activate() {
|
||||
paymentAccountsComboBox.setOnAction(e -> onPaymentAccountsComboBoxSelected());
|
||||
|
||||
@Override
|
||||
protected void doDeactivate() {
|
||||
removeBindings();
|
||||
removeListeners();
|
||||
}
|
||||
amountTextField.focusedProperty().addListener(amountFocusedListener);
|
||||
|
||||
private void addBindings() {
|
||||
amountBtcLabel.textProperty().bind(model.btcCode);
|
||||
amountTextField.textProperty().bindBidirectional(model.amount);
|
||||
volumeTextField.textProperty().bindBidirectional(model.volume);
|
||||
totalToPayTextField.textProperty().bind(model.totalToPay);
|
||||
addressTextField.amountAsCoinProperty().bind(model.totalToPayAsCoin);
|
||||
amountDescriptionLabel.textProperty().bind(model.amountDescription);
|
||||
amountTextField.validationResultProperty().bind(model.amountValidationResult);
|
||||
takeOfferButton.disableProperty().bind(model.takeOfferButtonDisabled);
|
||||
takeOfferButton.disableProperty().bind(model.isTakeOfferButtonDisabled);
|
||||
takeOfferSpinnerInfoLabel.visibleProperty().bind(model.isTakeOfferSpinnerVisible);
|
||||
|
||||
priceCurrencyLabel.textProperty().bind(createStringBinding(() ->
|
||||
model.getTradeCurrency().getCode() + "/" + model.btcCode.get(), model.btcCode));
|
||||
|
||||
volumeCurrencyLabel.setText(model.getTradeCurrency().getCode());
|
||||
amountRangeBtcLabel.textProperty().bind(model.btcCode);
|
||||
|
||||
priceDescriptionLabel.setText(BSResources.get("createOffer.amountPriceBox.priceDescription", model.getTradeCurrency().getCode()));
|
||||
volumeDescriptionLabel.setText(model.volumeDescriptionLabel.get());
|
||||
|
||||
errorPopupDisplayed = new SimpleBooleanProperty();
|
||||
offerWarningSubscription = EasyBind.subscribe(model.offerWarning, newValue -> {
|
||||
if (newValue != null) {
|
||||
new Popup().warning(newValue).onClose(() -> {
|
||||
errorPopupDisplayed.set(true);
|
||||
model.resetOfferWarning();
|
||||
close();
|
||||
}).show();
|
||||
}
|
||||
});
|
||||
|
||||
errorMessageSubscription = EasyBind.subscribe(model.errorMessage, newValue -> {
|
||||
if (newValue != null) {
|
||||
new Popup().error(BSResources.get("takeOffer.error.message", newValue)).onClose(() -> {
|
||||
errorPopupDisplayed.set(true);
|
||||
model.resetErrorMessage();
|
||||
close();
|
||||
}).show();
|
||||
}
|
||||
});
|
||||
|
||||
showCheckAvailabilityPopupSubscription = EasyBind.combine(model.offerWarning,
|
||||
model.errorMessage,
|
||||
errorPopupDisplayed,
|
||||
model.isOfferAvailable,
|
||||
(a, b, c, d) -> a == null && b == null && !c && !d)
|
||||
.subscribe((observable, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
new Popup().message(BSResources.get("takeOffer.fundsBox.isOfferAvailable", newValue))
|
||||
.closeButtonText("Cancel")
|
||||
.showProgressIndicator()
|
||||
.width(200)
|
||||
.show()
|
||||
.onClose(() -> {
|
||||
model.resetErrorMessage();
|
||||
close();
|
||||
}).show();
|
||||
}
|
||||
});
|
||||
|
||||
isTakeOfferSpinnerVisibleSubscription = EasyBind.subscribe(model.isTakeOfferSpinnerVisible, newValue -> {
|
||||
takeOfferSpinner.setProgress(newValue ? -1 : 0);
|
||||
takeOfferSpinner.setVisible(newValue);
|
||||
});
|
||||
|
||||
showWarningInvalidBtcDecimalPlacesSubscription = EasyBind.subscribe(model.showWarningInvalidBtcDecimalPlaces, newValue -> {
|
||||
if (newValue) {
|
||||
new Popup().warning(BSResources.get("takeOffer.amountPriceBox.warning.invalidBtcDecimalPlaces")).show();
|
||||
model.showWarningInvalidBtcDecimalPlaces.set(false);
|
||||
}
|
||||
});
|
||||
showTransactionPublishedScreenSubscription = EasyBind.subscribe(model.showTransactionPublishedScreen, newValue -> {
|
||||
if (newValue && BitsquareApp.DEV_MODE) {
|
||||
newValue = false;
|
||||
close();
|
||||
navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class);
|
||||
}
|
||||
|
||||
if (newValue && model.getTrade() != null && model.getTrade().errorMessageProperty().get() == null)
|
||||
new Popup().information(BSResources.get("takeOffer.success.info"))
|
||||
.onClose(() -> {
|
||||
navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class);
|
||||
close();
|
||||
})
|
||||
.show();
|
||||
});
|
||||
|
||||
if (model.getPossiblePaymentAccounts().size() > 1) {
|
||||
paymentAccountsComboBox.setItems(model.getPossiblePaymentAccounts());
|
||||
paymentAccountsComboBox.getSelectionModel().select(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeBindings() {
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
paymentAccountsComboBox.setOnAction(null);
|
||||
|
||||
amountTextField.focusedProperty().removeListener(amountFocusedListener);
|
||||
|
||||
amountBtcLabel.textProperty().unbind();
|
||||
amountTextField.textProperty().unbindBidirectional(model.amount);
|
||||
volumeTextField.textProperty().unbindBidirectional(model.volume);
|
||||
totalToPayTextField.textProperty().unbind();
|
||||
addressTextField.amountAsCoinProperty().unbind();
|
||||
amountDescriptionLabel.textProperty().unbind();
|
||||
amountTextField.validationResultProperty().unbind();
|
||||
takeOfferButton.disableProperty().unbind();
|
||||
takeOfferSpinnerInfoLabel.visibleProperty().unbind();
|
||||
}
|
||||
priceCurrencyLabel.textProperty().unbind();
|
||||
volumeCurrencyLabel.textProperty().unbind();
|
||||
amountRangeBtcLabel.textProperty().unbind();
|
||||
priceDescriptionLabel.textProperty().unbind();
|
||||
volumeDescriptionLabel.textProperty().unbind();
|
||||
|
||||
|
||||
private void createListeners() {
|
||||
errorMessageChangeListener = (o, oldValue, newValue) -> {
|
||||
if (newValue != null) {
|
||||
Popups.openErrorPopup(BSResources.get("shared.error"), BSResources.get("takeOffer.error.message", model.errorMessage.get()));
|
||||
Popups.removeBlurContent();
|
||||
Platform.runLater(this::close);
|
||||
}
|
||||
};
|
||||
amountFocusedListener = (o, oldValue, newValue) -> {
|
||||
model.onFocusOutAmountTextField(oldValue, newValue, amountTextField.getText());
|
||||
amountTextField.setText(model.amount.get());
|
||||
};
|
||||
isTakeOfferSpinnerVisibleListener = (ov, oldValue, newValue) -> {
|
||||
takeOfferSpinner.setProgress(newValue ? -1 : 0);
|
||||
takeOfferSpinner.setVisible(newValue);
|
||||
};
|
||||
stateListener = (ov, oldValue, newValue) -> {
|
||||
switch (newValue) {
|
||||
case CHECK_AVAILABILITY:
|
||||
showCheckAvailabilityScreen();
|
||||
break;
|
||||
case AMOUNT_SCREEN:
|
||||
showAmountScreen();
|
||||
break;
|
||||
case PAYMENT_SCREEN:
|
||||
setupPaymentScreen();
|
||||
break;
|
||||
case DETAILS_SCREEN:
|
||||
showDetailsScreen();
|
||||
break;
|
||||
}
|
||||
};
|
||||
showWarningInvalidBtcDecimalPlacesListener = (o, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
Popups.openWarningPopup(BSResources.get("shared.warning"),
|
||||
BSResources.get("takeOffer.amountPriceBox.warning.invalidBtcDecimalPlaces"));
|
||||
model.showWarningInvalidBtcDecimalPlaces.set(false);
|
||||
}
|
||||
};
|
||||
showTransactionPublishedScreenListener = (o, oldValue, newValue) -> {
|
||||
if (BitsquareApp.DEV_MODE) {
|
||||
newValue = false;
|
||||
close();
|
||||
navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class);
|
||||
}
|
||||
|
||||
if (newValue) {
|
||||
OverlayManager.blurContent();
|
||||
|
||||
// Dialogs are a bit limited. There is no callback for the InformationDialog button click, so we added
|
||||
// our own actions.
|
||||
List<Action> actions = new ArrayList<>();
|
||||
/* actions.add(new AbstractAction(BSResources.get("shared.copyTxId")) {
|
||||
@Override
|
||||
public void handle(ActionEvent actionEvent) {
|
||||
getProperties().put("type", "COPY");
|
||||
Utilities.copyToClipboard(model.transactionId.get());
|
||||
}
|
||||
});*/
|
||||
actions.add(new AbstractAction(BSResources.get("shared.close")) {
|
||||
@Override
|
||||
public void handle(ActionEvent actionEvent) {
|
||||
getProperties().put("type", "CLOSE");
|
||||
try {
|
||||
close();
|
||||
navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Dialog.Actions.CLOSE.handle(actionEvent);
|
||||
}
|
||||
});
|
||||
|
||||
Popups.openInfoPopup(BSResources.get("takeOffer.success.headline"),
|
||||
BSResources.get("takeOffer.success.info"),
|
||||
actions);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void addListeners() {
|
||||
amountTextField.focusedProperty().addListener(amountFocusedListener);
|
||||
model.isTakeOfferSpinnerVisible.addListener(isTakeOfferSpinnerVisibleListener);
|
||||
model.state.addListener(stateListener);
|
||||
model.showWarningInvalidBtcDecimalPlaces.addListener(showWarningInvalidBtcDecimalPlacesListener);
|
||||
model.errorMessage.addListener(errorMessageChangeListener);
|
||||
model.showTransactionPublishedScreen.addListener(showTransactionPublishedScreenListener);
|
||||
}
|
||||
|
||||
private void removeListeners() {
|
||||
amountTextField.focusedProperty().removeListener(amountFocusedListener);
|
||||
model.isTakeOfferSpinnerVisible.removeListener(isTakeOfferSpinnerVisibleListener);
|
||||
model.state.removeListener(stateListener);
|
||||
model.showWarningInvalidBtcDecimalPlaces.removeListener(showWarningInvalidBtcDecimalPlacesListener);
|
||||
model.errorMessage.removeListener(errorMessageChangeListener);
|
||||
model.showTransactionPublishedScreen.removeListener(showTransactionPublishedScreenListener);
|
||||
offerWarningSubscription.unsubscribe();
|
||||
errorMessageSubscription.unsubscribe();
|
||||
isTakeOfferSpinnerVisibleSubscription.unsubscribe();
|
||||
showWarningInvalidBtcDecimalPlacesSubscription.unsubscribe();
|
||||
showTransactionPublishedScreenSubscription.unsubscribe();
|
||||
showCheckAvailabilityPopupSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
|
||||
|
@ -264,115 +268,77 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void initWithData(Coin amount, Offer offer) {
|
||||
model.initWithData(amount, offer);
|
||||
public void initWithData(Offer offer) {
|
||||
model.initWithData(offer);
|
||||
|
||||
if (offer.getDirection() == Offer.Direction.SELL)
|
||||
if (model.getOffer().getDirection() == Offer.Direction.SELL)
|
||||
imageView.setId("image-buy-large");
|
||||
else
|
||||
imageView.setId("image-sell-large");
|
||||
|
||||
priceDescriptionLabel.setText(BSResources.get("takeOffer.amountPriceBox.priceDescription", model.getFiatCode()));
|
||||
volumeDescriptionLabel.setText(BSResources.get("takeOffer.amountPriceBox.volumeDescription", model.getFiatCode()));
|
||||
volumeDescriptionLabel.textProperty().bind(createStringBinding(() -> model.volumeDescriptionLabel.get(), model.fiatCode, model.volumeDescriptionLabel));
|
||||
|
||||
balanceTextField.setup(model.getWalletService(), model.address.get(), model.getFormatter());
|
||||
balanceTextField.setup(model.address.get(), model.getFormatter());
|
||||
|
||||
boolean showComboBox = model.getPossiblePaymentAccounts().size() > 1;
|
||||
paymentAccountsLabel.setVisible(showComboBox);
|
||||
paymentAccountsLabel.setManaged(showComboBox);
|
||||
paymentAccountsComboBox.setVisible(showComboBox);
|
||||
paymentAccountsComboBox.setManaged(showComboBox);
|
||||
paymentMethodTextField.setVisible(!showComboBox);
|
||||
paymentMethodTextField.setManaged(!showComboBox);
|
||||
paymentMethodLabel.setVisible(!showComboBox);
|
||||
paymentMethodLabel.setManaged(!showComboBox);
|
||||
if (!showComboBox)
|
||||
paymentMethodTextField.setText(BSResources.get(model.getPaymentMethod().getId()));
|
||||
currencyTextField.setText(model.getTradeCurrency().getCodeAndName());
|
||||
buyLabel.setText(model.getDirectionLabel());
|
||||
amountDescriptionLabel.setText(model.getAmountDescription());
|
||||
amountRangeTextField.setText(model.getAmountRange());
|
||||
priceTextField.setText(model.getPrice());
|
||||
addressTextField.setPaymentLabel(model.getPaymentLabel());
|
||||
addressTextField.setAddress(model.getAddressAsString());
|
||||
bankAccountTypeTextField.setText(model.getBankAccountType());
|
||||
bankAccountTypeTextField.setText(model.getBankAccountType());
|
||||
bankAccountCurrencyTextField.setText(model.getBankAccountCurrency());
|
||||
bankAccountCountyTextField.setText(model.getBankAccountCounty());
|
||||
acceptedCountriesTextField.setText(model.getAcceptedCountries());
|
||||
acceptedLanguagesTextField.setText(model.getAcceptedLanguages());
|
||||
acceptedArbitratorsTextField.setText(model.getAcceptedArbitratorIds());
|
||||
|
||||
amountPriceBoxInfoDisplay.textProperty().bind(model.amountPriceBoxInfo);
|
||||
fundsBoxInfoDisplay.textProperty().bind(model.fundsBoxInfoDisplay);
|
||||
showCheckAvailabilityScreen();
|
||||
}
|
||||
|
||||
public void setCloseHandler(OfferView.CloseHandler closeHandler) {
|
||||
this.closeHandler = closeHandler;
|
||||
}
|
||||
|
||||
// called form parent as the view does not get notified when the tab is closed
|
||||
public void onClose() {
|
||||
// TODO need other implementation as it is displayed laso if there are old funds in the wallet
|
||||
/*
|
||||
if (model.dataModel.isWalletFunded.get())
|
||||
new Popup().warning("You have already funds paid in.\nIn the <Funds/Open for withdrawal> section you can withdraw those funds.").show();*/
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// UI actions
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@FXML
|
||||
void onTakeOffer() {
|
||||
model.onTakeOffer();
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onShowPaymentScreen() {
|
||||
model.onShowPaymentScreen();
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onScroll() {
|
||||
InputTextField.hideErrorMessageDisplay();
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onToggleShowAdvancedSettings() {
|
||||
model.onToggleShowAdvancedSettings();
|
||||
if (model.isDetailsVisible()) {
|
||||
showAdvancedSettingsButton.setText(BSResources.get("takeOffer.fundsBox.hideAdvanced"));
|
||||
showAdvancedSettingsButton.setGraphic(collapse);
|
||||
showDetailsScreen();
|
||||
private void onTakeOffer() {
|
||||
Offer offer = model.getOffer();
|
||||
if (model.getShowTakeOfferConfirmation()) {
|
||||
offerDetailsPopup.onTakeOffer(() -> model.onTakeOffer()).show(offer);
|
||||
}
|
||||
else {
|
||||
showAdvancedSettingsButton.setText(BSResources.get("takeOffer.fundsBox.showAdvanced"));
|
||||
showAdvancedSettingsButton.setGraphic(expand);
|
||||
hideDetailsScreen();
|
||||
if (model.hasAcceptedArbitrators()) {
|
||||
model.onTakeOffer();
|
||||
} else {
|
||||
new Popup().warning("You have no arbitrator selected.\n" +
|
||||
"Please select at least one arbitrator.").show();
|
||||
|
||||
navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class, ArbitratorSelectionView.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onOpenGeneralHelp() {
|
||||
Help.openWindow(HelpId.TAKE_OFFER_GENERAL);
|
||||
private void onPaymentAccountsComboBoxSelected() {
|
||||
model.onPaymentAccountSelected(paymentAccountsComboBox.getSelectionModel().getSelectedItem());
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onOpenFundingHelp() {
|
||||
Help.openWindow(HelpId.TAKE_OFFER_FUNDING);
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onOpenAdvancedSettingsHelp() {
|
||||
Help.openWindow(HelpId.TAKE_OFFER_ADVANCED);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// States/screens
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void showCheckAvailabilityScreen() {
|
||||
|
||||
}
|
||||
|
||||
private void showAmountScreen() {
|
||||
isOfferAvailableLabel.setVisible(false);
|
||||
isOfferAvailableLabel.setManaged(false);
|
||||
isOfferAvailableProgressIndicator.setProgress(0);
|
||||
isOfferAvailableProgressIndicator.setVisible(false);
|
||||
isOfferAvailableProgressIndicator.setManaged(false);
|
||||
|
||||
showPaymentInfoScreenButton.setVisible(true);
|
||||
}
|
||||
|
||||
private void setupPaymentScreen() {
|
||||
private void onShowPayFundsScreen() {
|
||||
if (!BitsquareApp.DEV_MODE) {
|
||||
if (model.getDisplaySecurityDepositInfo()) {
|
||||
OverlayManager.blurContent();
|
||||
MainView.blur();
|
||||
List<Action> actions = new ArrayList<>();
|
||||
actions.add(new AbstractAction(BSResources.get("shared.close")) {
|
||||
@Override
|
||||
|
@ -381,18 +347,16 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||
Dialog.Actions.CLOSE.handle(actionEvent);
|
||||
}
|
||||
});
|
||||
Popups.openInfoPopup("To ensure that both traders behave fair they need to pay a security deposit.",
|
||||
"The deposit will stay in your local trading wallet until the offer gets accepted by another trader. " +
|
||||
"\nIt will be refunded to you after the trade has successfully completed.",
|
||||
actions);
|
||||
new Popup().information("To ensure that both traders behave fair they need to pay a security deposit.\n\n" +
|
||||
"The deposit will stay in your local trading wallet until the offer gets accepted by another trader.\n" +
|
||||
"It will be refunded to you after the trade has successfully completed.").show();
|
||||
|
||||
model.onSecurityDepositInfoDisplayed();
|
||||
}
|
||||
}
|
||||
|
||||
priceAmountPane.setInactive();
|
||||
showPaymentButton.setVisible(false);
|
||||
takeOfferButton.setVisible(true);
|
||||
showPaymentInfoScreenButton.setVisible(false);
|
||||
|
||||
payFundsPane.setVisible(true);
|
||||
totalToPayLabel.setVisible(true);
|
||||
|
@ -402,64 +366,218 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||
addressTextField.setVisible(true);
|
||||
balanceLabel.setVisible(true);
|
||||
balanceTextField.setVisible(true);
|
||||
fundsBoxInfoDisplay.setVisible(true);
|
||||
|
||||
// for irc demo
|
||||
//showAdvancedSettingsButton.setVisible(true);
|
||||
showAdvancedSettingsButton.setManaged(false);
|
||||
|
||||
if (expand == null) {
|
||||
expand = ImageUtil.getImageViewById(ImageUtil.EXPAND);
|
||||
collapse = ImageUtil.getImageViewById(ImageUtil.COLLAPSE);
|
||||
}
|
||||
showAdvancedSettingsButton.setGraphic(expand);
|
||||
|
||||
setupTotalToPayInfoIconLabel();
|
||||
}
|
||||
|
||||
private void showDetailsScreen() {
|
||||
payFundsPane.setInactive();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Navigation
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void close() {
|
||||
if (closeHandler != null)
|
||||
closeHandler.close();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Build UI elements
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void addScrollPane() {
|
||||
scrollPane = new ScrollPane();
|
||||
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
|
||||
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
|
||||
scrollPane.layout();
|
||||
|
||||
toggleDetailsScreen(true);
|
||||
scrollPane.setFitToWidth(true);
|
||||
scrollPane.setFitToHeight(true);
|
||||
scrollPane.setOnScroll(e -> InputTextField.hideErrorMessageDisplay());
|
||||
AnchorPane.setLeftAnchor(scrollPane, 0d);
|
||||
AnchorPane.setTopAnchor(scrollPane, 0d);
|
||||
AnchorPane.setRightAnchor(scrollPane, 0d);
|
||||
AnchorPane.setBottomAnchor(scrollPane, 0d);
|
||||
root.getChildren().add(scrollPane);
|
||||
}
|
||||
|
||||
private void hideDetailsScreen() {
|
||||
payFundsPane.setActive();
|
||||
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
|
||||
scrollPane.layout();
|
||||
toggleDetailsScreen(false);
|
||||
private void addGridPane() {
|
||||
gridPane = new GridPane();
|
||||
gridPane.setPadding(new Insets(30, 25, -1, 25));
|
||||
gridPane.setHgap(5);
|
||||
gridPane.setVgap(5);
|
||||
ColumnConstraints columnConstraints1 = new ColumnConstraints();
|
||||
columnConstraints1.setHalignment(HPos.RIGHT);
|
||||
columnConstraints1.setHgrow(Priority.SOMETIMES);
|
||||
columnConstraints1.setMinWidth(200);
|
||||
ColumnConstraints columnConstraints2 = new ColumnConstraints();
|
||||
columnConstraints2.setHgrow(Priority.ALWAYS);
|
||||
gridPane.getColumnConstraints().addAll(columnConstraints1, columnConstraints2);
|
||||
scrollPane.setContent(gridPane);
|
||||
}
|
||||
|
||||
private void toggleDetailsScreen(boolean visible) {
|
||||
scrollPane.setOnScroll(scrollEvent -> {
|
||||
if (!visible)
|
||||
scrollEvent.consume();
|
||||
private void addPaymentGroup() {
|
||||
addTitledGroupBg(gridPane, gridRow, 2, "Payment info");
|
||||
Tuple2<Label, ComboBox> tuple = addLabelComboBox(gridPane, gridRow, "Payment account:", Layout.FIRST_ROW_DISTANCE);
|
||||
paymentAccountsLabel = tuple.first;
|
||||
paymentAccountsLabel.setVisible(false);
|
||||
paymentAccountsLabel.setManaged(false);
|
||||
paymentAccountsComboBox = tuple.second;
|
||||
paymentAccountsComboBox.setPromptText("Select payment account");
|
||||
paymentAccountsComboBox.setConverter(new StringConverter<PaymentAccount>() {
|
||||
@Override
|
||||
public String toString(PaymentAccount paymentAccount) {
|
||||
return paymentAccount.getAccountName() + " (" + paymentAccount.getSingleTradeCurrency().getCode() + ", " +
|
||||
BSResources.get(paymentAccount.getPaymentMethod().getId()) + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaymentAccount fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
paymentAccountsComboBox.setVisible(false);
|
||||
paymentAccountsComboBox.setManaged(false);
|
||||
Tuple2<Label, TextField> tuple2 = addLabelTextField(gridPane, gridRow, "Payment method:", "", Layout.FIRST_ROW_DISTANCE);
|
||||
paymentMethodLabel = tuple2.first;
|
||||
paymentMethodTextField = tuple2.second;
|
||||
currencyTextField = addLabelTextField(gridPane, ++gridRow, "Trade currency:", "").second;
|
||||
}
|
||||
|
||||
// deactivate mouse wheel scrolling if hidden
|
||||
scrollPane.setVmax(visible ? scrollPane.getHeight() : 0);
|
||||
scrollPane.setVvalue(visible ? scrollPane.getHeight() : 0);
|
||||
private void addAmountPriceGroup() {
|
||||
addTitledGroupBg(gridPane, ++gridRow, 2, "Set amount and price", Layout.GROUP_DISTANCE);
|
||||
|
||||
showDetailsPane.setVisible(visible);
|
||||
imageView = new ImageView();
|
||||
imageView.setPickOnBounds(true);
|
||||
buyLabel = new Label();
|
||||
buyLabel.setId("direction-icon-label");
|
||||
buyLabel.setAlignment(Pos.CENTER);
|
||||
buyLabel.setPadding(new Insets(-5, 0, 0, 0));
|
||||
VBox imageVBox = new VBox();
|
||||
imageVBox.setAlignment(Pos.CENTER);
|
||||
imageVBox.setSpacing(6);
|
||||
imageVBox.getChildren().addAll(imageView, buyLabel);
|
||||
GridPane.setRowIndex(imageVBox, gridRow);
|
||||
GridPane.setRowSpan(imageVBox, 2);
|
||||
GridPane.setMargin(imageVBox, new Insets(Layout.FIRST_ROW_AND_GROUP_DISTANCE, 10, 10, 10));
|
||||
gridPane.getChildren().add(imageVBox);
|
||||
|
||||
acceptedCountriesLabel.setVisible(visible);
|
||||
acceptedCountriesTextField.setVisible(visible);
|
||||
acceptedLanguagesLabel.setVisible(visible);
|
||||
acceptedLanguagesTextField.setVisible(visible);
|
||||
acceptedArbitratorsLabel.setVisible(visible);
|
||||
acceptedArbitratorsTextField.setVisible(visible);
|
||||
addAmountPriceFields();
|
||||
|
||||
bankAccountTypeLabel.setVisible(visible);
|
||||
bankAccountTypeTextField.setVisible(visible);
|
||||
bankAccountCurrencyLabel.setVisible(visible);
|
||||
bankAccountCurrencyTextField.setVisible(visible);
|
||||
bankAccountCountyLabel.setVisible(visible);
|
||||
bankAccountCountyTextField.setVisible(visible);
|
||||
addAmountRangeBox();
|
||||
|
||||
advancedInfoDisplay.setVisible(visible);
|
||||
showPaymentButton = addButton(gridPane, ++gridRow, BSResources.get("takeOffer.amountPriceBox.next"));
|
||||
GridPane.setMargin(showPaymentButton, new Insets(-35, 0, 0, 0));
|
||||
showPaymentButton.setId("show-details-button");
|
||||
showPaymentButton.setOnAction(e -> onShowPayFundsScreen());
|
||||
}
|
||||
|
||||
private void addFundingGroup() {
|
||||
// don't increase gridRow as we removed button when this gets visible
|
||||
payFundsPane = addTitledGroupBg(gridPane, gridRow, 3, BSResources.get("takeOffer.fundsBox.title"), Layout.GROUP_DISTANCE);
|
||||
payFundsPane.setVisible(false);
|
||||
|
||||
totalToPayLabel = new Label(BSResources.get("takeOffer.fundsBox.totalsNeeded"));
|
||||
totalToPayLabel.setVisible(false);
|
||||
totalToPayInfoIconLabel = new Label();
|
||||
totalToPayInfoIconLabel.setVisible(false);
|
||||
HBox totalToPayBox = new HBox();
|
||||
totalToPayBox.setSpacing(4);
|
||||
totalToPayBox.setAlignment(Pos.CENTER_RIGHT);
|
||||
totalToPayBox.getChildren().addAll(totalToPayLabel, totalToPayInfoIconLabel);
|
||||
GridPane.setMargin(totalToPayBox, new Insets(Layout.FIRST_ROW_AND_GROUP_DISTANCE, 0, 0, 0));
|
||||
GridPane.setRowIndex(totalToPayBox, gridRow);
|
||||
gridPane.getChildren().add(totalToPayBox);
|
||||
totalToPayTextField = new TextField();
|
||||
totalToPayTextField.setEditable(false);
|
||||
totalToPayTextField.setFocusTraversable(false);
|
||||
totalToPayTextField.setVisible(false);
|
||||
totalToPayTextField.setPromptText(BSResources.get("createOffer.fundsBox.totalsNeeded.prompt"));
|
||||
GridPane.setRowIndex(totalToPayTextField, gridRow);
|
||||
GridPane.setColumnIndex(totalToPayTextField, 1);
|
||||
GridPane.setMargin(totalToPayTextField, new Insets(Layout.FIRST_ROW_AND_GROUP_DISTANCE, 0, 0, 0));
|
||||
gridPane.getChildren().add(totalToPayTextField);
|
||||
|
||||
Tuple2<Label, AddressTextField> addressTuple = addLabelAddressTextField(gridPane, ++gridRow, BSResources.get("takeOffer.fundsBox.address"));
|
||||
addressLabel = addressTuple.first;
|
||||
addressLabel.setVisible(false);
|
||||
addressTextField = addressTuple.second;
|
||||
addressTextField.setVisible(false);
|
||||
|
||||
Tuple2<Label, BalanceTextField> balanceTuple = addLabelBalanceTextField(gridPane, ++gridRow, BSResources.get("takeOffer.fundsBox.balance"));
|
||||
balanceLabel = balanceTuple.first;
|
||||
balanceLabel.setVisible(false);
|
||||
balanceTextField = balanceTuple.second;
|
||||
balanceTextField.setVisible(false);
|
||||
|
||||
Tuple3<Button, ProgressIndicator, Label> takeOfferTuple = addButtonWithStatus(gridPane, ++gridRow, BSResources.get("takeOffer.fundsBox.takeOffer"));
|
||||
takeOfferButton = takeOfferTuple.first;
|
||||
takeOfferButton.setVisible(false);
|
||||
takeOfferButton.setOnAction(e -> onTakeOffer());
|
||||
takeOfferSpinner = takeOfferTuple.second;
|
||||
takeOfferSpinnerInfoLabel = takeOfferTuple.third;
|
||||
takeOfferSpinnerInfoLabel.setText(BSResources.get("takeOffer.fundsBox.takeOfferSpinnerInfo"));
|
||||
takeOfferSpinnerInfoLabel.setVisible(false);
|
||||
}
|
||||
|
||||
private void addAmountPriceFields() {
|
||||
// amountBox
|
||||
Tuple3<HBox, InputTextField, Label> amountValueCurrencyBoxTuple = getAmountCurrencyBox(BSResources.get("takeOffer.amount.prompt"));
|
||||
HBox amountValueCurrencyBox = amountValueCurrencyBoxTuple.first;
|
||||
amountTextField = amountValueCurrencyBoxTuple.second;
|
||||
amountBtcLabel = amountValueCurrencyBoxTuple.third;
|
||||
Tuple2<Label, VBox> amountInputBoxTuple = getTradeInputBox(amountValueCurrencyBox, model.getAmountDescription());
|
||||
amountDescriptionLabel = amountInputBoxTuple.first;
|
||||
VBox amountBox = amountInputBoxTuple.second;
|
||||
|
||||
// x
|
||||
Label xLabel = new Label("x");
|
||||
xLabel.setFont(Font.font("Helvetica-Bold", 20));
|
||||
xLabel.setPadding(new Insets(14, 3, 0, 3));
|
||||
|
||||
// price
|
||||
Tuple3<HBox, TextField, Label> priceValueCurrencyBoxTuple = getValueCurrencyBox();
|
||||
HBox priceValueCurrencyBox = priceValueCurrencyBoxTuple.first;
|
||||
priceTextField = priceValueCurrencyBoxTuple.second;
|
||||
priceCurrencyLabel = priceValueCurrencyBoxTuple.third;
|
||||
Tuple2<Label, VBox> priceInputBoxTuple = getTradeInputBox(priceValueCurrencyBox, BSResources.get("takeOffer.amountPriceBox.priceDescription"));
|
||||
priceDescriptionLabel = priceInputBoxTuple.first;
|
||||
VBox priceBox = priceInputBoxTuple.second;
|
||||
|
||||
// =
|
||||
Label resultLabel = new Label("=");
|
||||
resultLabel.setFont(Font.font("Helvetica-Bold", 20));
|
||||
resultLabel.setPadding(new Insets(14, 2, 0, 2));
|
||||
|
||||
// volume
|
||||
Tuple3<HBox, TextField, Label> volumeValueCurrencyBoxTuple = getValueCurrencyBox();
|
||||
HBox volumeValueCurrencyBox = volumeValueCurrencyBoxTuple.first;
|
||||
volumeTextField = volumeValueCurrencyBoxTuple.second;
|
||||
volumeCurrencyLabel = volumeValueCurrencyBoxTuple.third;
|
||||
Tuple2<Label, VBox> volumeInputBoxTuple = getTradeInputBox(volumeValueCurrencyBox, model.volumeDescriptionLabel.get());
|
||||
volumeDescriptionLabel = volumeInputBoxTuple.first;
|
||||
VBox volumeBox = volumeInputBoxTuple.second;
|
||||
|
||||
HBox hBox = new HBox();
|
||||
hBox.setSpacing(5);
|
||||
hBox.setAlignment(Pos.CENTER_LEFT);
|
||||
hBox.getChildren().addAll(amountBox, xLabel, priceBox, resultLabel, volumeBox);
|
||||
GridPane.setRowIndex(hBox, gridRow);
|
||||
GridPane.setColumnIndex(hBox, 1);
|
||||
GridPane.setMargin(hBox, new Insets(Layout.FIRST_ROW_AND_GROUP_DISTANCE, 10, 0, 0));
|
||||
gridPane.getChildren().add(hBox);
|
||||
}
|
||||
|
||||
private void addAmountRangeBox() {
|
||||
Tuple3<HBox, TextField, Label> amountValueCurrencyBoxTuple = getValueCurrencyBox();
|
||||
HBox amountValueCurrencyBox = amountValueCurrencyBoxTuple.first;
|
||||
amountRangeTextField = amountValueCurrencyBoxTuple.second;
|
||||
amountRangeBtcLabel = amountValueCurrencyBoxTuple.third;
|
||||
|
||||
Tuple2<Label, VBox> amountInputBoxTuple = getTradeInputBox(amountValueCurrencyBox, BSResources.get("takeOffer.amountPriceBox.amountRangeDescription"));
|
||||
VBox box = amountInputBoxTuple.second;
|
||||
GridPane.setRowIndex(box, ++gridRow);
|
||||
GridPane.setColumnIndex(box, 1);
|
||||
GridPane.setMargin(box, new Insets(5, 10, 5, 0));
|
||||
gridPane.getChildren().add(box);
|
||||
}
|
||||
|
||||
|
||||
|
@ -467,9 +585,47 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||
// Utils
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void close() {
|
||||
if (closeHandler != null)
|
||||
closeHandler.close();
|
||||
private Tuple2<Label, VBox> getTradeInputBox(HBox amountValueBox, String promptText) {
|
||||
Label descriptionLabel = new Label(promptText);
|
||||
descriptionLabel.setId("input-description-label");
|
||||
descriptionLabel.setPrefWidth(170);
|
||||
|
||||
VBox box = new VBox();
|
||||
box.setSpacing(4);
|
||||
box.getChildren().addAll(descriptionLabel, amountValueBox);
|
||||
return new Tuple2<>(descriptionLabel, box);
|
||||
}
|
||||
|
||||
private Tuple3<HBox, InputTextField, Label> getAmountCurrencyBox(String promptText) {
|
||||
InputTextField input = new InputTextField();
|
||||
input.setPrefWidth(170);
|
||||
input.setAlignment(Pos.CENTER_RIGHT);
|
||||
input.setId("text-input-with-currency-text-field");
|
||||
input.setPromptText(promptText);
|
||||
|
||||
Label currency = new Label();
|
||||
currency.setId("currency-info-label");
|
||||
|
||||
HBox box = new HBox();
|
||||
box.getChildren().addAll(input, currency);
|
||||
return new Tuple3<>(box, input, currency);
|
||||
}
|
||||
|
||||
private Tuple3<HBox, TextField, Label> getValueCurrencyBox() {
|
||||
TextField textField = new InputTextField();
|
||||
textField.setPrefWidth(170);
|
||||
textField.setAlignment(Pos.CENTER_RIGHT);
|
||||
textField.setId("text-input-with-currency-text-field");
|
||||
textField.setMouseTransparent(true);
|
||||
textField.setEditable(false);
|
||||
textField.setFocusTraversable(false);
|
||||
|
||||
Label currency = new Label();
|
||||
currency.setId("currency-info-label-disabled");
|
||||
|
||||
HBox box = new HBox();
|
||||
box.getChildren().addAll(textField, currency);
|
||||
return new Tuple3<>(box, textField, currency);
|
||||
}
|
||||
|
||||
private void setupTotalToPayInfoIconLabel() {
|
||||
|
@ -491,26 +647,18 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||
infoGridPane.setPadding(new Insets(10, 10, 10, 10));
|
||||
|
||||
int i = 0;
|
||||
if (model.isSeller()) {
|
||||
addPayInfoEntry(infoGridPane, i++,
|
||||
BSResources.get("takeOffer.fundsBox.amount"),
|
||||
model.getAmount());
|
||||
}
|
||||
if (model.isSeller())
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("takeOffer.fundsBox.tradeAmount"), model.getAmount());
|
||||
|
||||
|
||||
addPayInfoEntry(infoGridPane, i++,
|
||||
BSResources.get("takeOffer.fundsBox.securityDeposit"),
|
||||
model.securityDeposit.get());
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("takeOffer.fundsBox.offerFee"),
|
||||
model.getOfferFee());
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("takeOffer.fundsBox.networkFee"),
|
||||
model.getNetworkFee());
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("takeOffer.fundsBox.securityDeposit"), model.getSecurityDeposit());
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("takeOffer.fundsBox.offerFee"), model.getOfferFee());
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("takeOffer.fundsBox.networkFee"), model.getNetworkFee());
|
||||
Separator separator = new Separator();
|
||||
separator.setOrientation(Orientation.HORIZONTAL);
|
||||
separator.setStyle("-fx-background: #666;");
|
||||
GridPane.setConstraints(separator, 1, i++);
|
||||
infoGridPane.getChildren().add(separator);
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("takeOffer.fundsBox.total"),
|
||||
addPayInfoEntry(infoGridPane, i, BSResources.get("takeOffer.fundsBox.total"),
|
||||
model.totalToPay.get());
|
||||
totalToPayInfoPopover = new PopOver(infoGridPane);
|
||||
if (totalToPayInfoIconLabel.getScene() != null) {
|
||||
|
@ -536,7 +684,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||
private Point2D getPopupPosition() {
|
||||
Window window = totalToPayInfoIconLabel.getScene().getWindow();
|
||||
Point2D point = totalToPayInfoIconLabel.localToScene(0, 0);
|
||||
double x = point.getX() + window.getX() + totalToPayInfoIconLabel.getWidth() - 3;
|
||||
double x = point.getX() + window.getX() + totalToPayInfoIconLabel.getWidth() + 2;
|
||||
double y = point.getY() + window.getY() + Math.floor(totalToPayInfoIconLabel.getHeight() / 2) - 9;
|
||||
return new Point2D(x, y);
|
||||
}
|
||||
|
|
|
@ -17,105 +17,76 @@
|
|||
|
||||
package io.bitsquare.gui.main.offer.takeoffer;
|
||||
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.arbitration.Arbitrator;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.gui.common.model.ActivatableWithDataModel;
|
||||
import io.bitsquare.gui.common.model.ViewModel;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.gui.util.validation.BtcValidator;
|
||||
import io.bitsquare.gui.util.validation.InputValidator;
|
||||
import io.bitsquare.locale.BSResources;
|
||||
import io.bitsquare.locale.CurrencyUtil;
|
||||
import io.bitsquare.trade.BuyerAsTakerTrade;
|
||||
import io.bitsquare.trade.SellerAsTakerTrade;
|
||||
import io.bitsquare.locale.TradeCurrency;
|
||||
import io.bitsquare.payment.PaymentAccount;
|
||||
import io.bitsquare.payment.PaymentMethod;
|
||||
import io.bitsquare.trade.Trade;
|
||||
import io.bitsquare.trade.TradeState;
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
|
||||
import javafx.beans.property.*;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static javafx.beans.binding.Bindings.createStringBinding;
|
||||
|
||||
class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> implements ViewModel {
|
||||
private static final Logger log = LoggerFactory.getLogger(TakeOfferViewModel.class);
|
||||
|
||||
public enum State {
|
||||
CHECK_AVAILABILITY,
|
||||
AMOUNT_SCREEN,
|
||||
PAYMENT_SCREEN,
|
||||
DETAILS_SCREEN
|
||||
}
|
||||
|
||||
private final BtcValidator btcValidator;
|
||||
private final BSFormatter formatter;
|
||||
private final String offerFee;
|
||||
private final String networkFee;
|
||||
|
||||
// static fields
|
||||
|
||||
private String amountRange;
|
||||
private String price;
|
||||
private String directionLabel;
|
||||
private String bankAccountType;
|
||||
private String bankAccountCurrency;
|
||||
private String bankAccountCounty;
|
||||
private String acceptedCountries;
|
||||
private String acceptedLanguages;
|
||||
private String acceptedArbitratorIds;
|
||||
private String addressAsString;
|
||||
private String paymentLabel;
|
||||
private boolean detailsVisible;
|
||||
|
||||
private boolean takeOfferRequested;
|
||||
private Trade trade;
|
||||
private Offer offer;
|
||||
private String price;
|
||||
private String directionLabel;
|
||||
private String amountDescription;
|
||||
|
||||
// TODO convert unneeded properties to static fields
|
||||
// dynamic fields
|
||||
final StringProperty amount = new SimpleStringProperty();
|
||||
final StringProperty volume = new SimpleStringProperty();
|
||||
final StringProperty securityDeposit = new SimpleStringProperty();
|
||||
final StringProperty totalToPay = new SimpleStringProperty();
|
||||
final StringProperty transactionId = new SimpleStringProperty();
|
||||
final StringProperty errorMessage = new SimpleStringProperty();
|
||||
final StringProperty btcCode = new SimpleStringProperty();
|
||||
final StringProperty amountDescription = new SimpleStringProperty();
|
||||
final StringProperty volumeDescriptionLabel = new SimpleStringProperty();
|
||||
final StringProperty fiatCode = new SimpleStringProperty();
|
||||
final StringProperty amountPriceBoxInfo = new SimpleStringProperty();
|
||||
final StringProperty fundsBoxInfoDisplay = new SimpleStringProperty();
|
||||
final StringProperty totalToPay = new SimpleStringProperty();
|
||||
final StringProperty errorMessage = new SimpleStringProperty();
|
||||
final StringProperty offerWarning = new SimpleStringProperty();
|
||||
final StringProperty btcCode = new SimpleStringProperty();
|
||||
|
||||
final BooleanProperty takeOfferButtonDisabled = new SimpleBooleanProperty(false);
|
||||
final BooleanProperty isTakeOfferSpinnerVisible = new SimpleBooleanProperty(false);
|
||||
final BooleanProperty isOfferAvailable = new SimpleBooleanProperty();
|
||||
final BooleanProperty isTakeOfferButtonDisabled = new SimpleBooleanProperty(true);
|
||||
final BooleanProperty isTakeOfferSpinnerVisible = new SimpleBooleanProperty();
|
||||
final BooleanProperty showWarningInvalidBtcDecimalPlaces = new SimpleBooleanProperty();
|
||||
final BooleanProperty showTransactionPublishedScreen = new SimpleBooleanProperty();
|
||||
|
||||
|
||||
// Needed for the addressTextField
|
||||
final ObjectProperty<Address> address = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Coin> totalToPayAsCoin = new SimpleObjectProperty<>();
|
||||
|
||||
final ObjectProperty<State> state = new SimpleObjectProperty<>(TakeOfferViewModel.State.CHECK_AVAILABILITY);
|
||||
final ObjectProperty<InputValidator.ValidationResult> amountValidationResult = new SimpleObjectProperty<>();
|
||||
|
||||
private boolean takeOfferRequested;
|
||||
// Those are needed for the addressTextField
|
||||
final ObjectProperty<Coin> totalToPayAsCoin = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Address> address = new SimpleObjectProperty<>();
|
||||
|
||||
// listeners
|
||||
private ChangeListener<String> amountChangeListener;
|
||||
private ChangeListener<Boolean> isWalletFundedChangeListener;
|
||||
private ChangeListener<Coin> amountAsCoinChangeListener;
|
||||
private ChangeListener<Offer.State> offerStateChangeListener;
|
||||
private ChangeListener<TradeState> tradeStateChangeListener;
|
||||
// Offer and trade are stored only for remove listener at deactivate
|
||||
private Offer offer;
|
||||
private Trade trade;
|
||||
private ChangeListener<String> amountListener;
|
||||
private ChangeListener<Coin> amountAsCoinListener;
|
||||
private ChangeListener<Boolean> isWalletFundedListener;
|
||||
private ChangeListener<Trade.State> tradeStateListener;
|
||||
private ChangeListener<String> tradeErrorListener;
|
||||
private ChangeListener<Offer.State> offerStateListener;
|
||||
private ChangeListener<String> offerErrorListener;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -123,134 +94,77 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public TakeOfferViewModel(TakeOfferDataModel dataModel, BtcValidator btcValidator, BSFormatter formatter) {
|
||||
public TakeOfferViewModel(TakeOfferDataModel dataModel, BtcValidator btcValidator,
|
||||
BSFormatter formatter) {
|
||||
super(dataModel);
|
||||
|
||||
this.btcValidator = btcValidator;
|
||||
this.formatter = formatter;
|
||||
|
||||
this.offerFee = formatter.formatCoinWithCode(dataModel.offerFeeAsCoin.get());
|
||||
this.networkFee = formatter.formatCoinWithCode(dataModel.networkFeeAsCoin.get());
|
||||
|
||||
createListeners();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doActivate() {
|
||||
protected void activate() {
|
||||
addBindings();
|
||||
addListeners();
|
||||
|
||||
amount.set(formatter.formatCoin(dataModel.amountAsCoin.get()));
|
||||
isTakeOfferSpinnerVisible.set(false);
|
||||
showTransactionPublishedScreen.set(false);
|
||||
|
||||
// when getting back to an open screen we want to re-check again
|
||||
isOfferAvailable.set(false);
|
||||
checkNotNull(offer, "offer must not be null");
|
||||
|
||||
offer.stateProperty().addListener(offerStateListener);
|
||||
applyOfferState(offer.stateProperty().get());
|
||||
|
||||
// when getting back to an open screen we want to re-check again
|
||||
UserThread.execute(() -> dataModel.checkOfferAvailability(() -> {
|
||||
}));
|
||||
updateButtonDisableState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doDeactivate() {
|
||||
protected void deactivate() {
|
||||
removeBindings();
|
||||
removeListeners();
|
||||
}
|
||||
|
||||
private void addBindings() {
|
||||
volume.bind(createStringBinding(() -> formatter.formatFiatWithCode(dataModel.volumeAsFiat.get()), dataModel.volumeAsFiat));
|
||||
totalToPay.bind(createStringBinding(() -> formatter.formatCoinWithCode(dataModel.totalToPayAsCoin.get()), dataModel.totalToPayAsCoin));
|
||||
securityDeposit.bind(createStringBinding(() -> formatter.formatCoinWithCode(dataModel.securityDepositAsCoin.get()), dataModel.securityDepositAsCoin));
|
||||
totalToPayAsCoin.bind(dataModel.totalToPayAsCoin);
|
||||
btcCode.bind(dataModel.btcCode);
|
||||
}
|
||||
|
||||
private void removeBindings() {
|
||||
volume.unbind();
|
||||
totalToPay.unbind();
|
||||
securityDeposit.unbind();
|
||||
totalToPayAsCoin.unbind();
|
||||
btcCode.unbind();
|
||||
}
|
||||
|
||||
private void createListeners() {
|
||||
amountChangeListener = (ov, oldValue, newValue) -> {
|
||||
if (isBtcInputValid(newValue).isValid) {
|
||||
setAmountToModel();
|
||||
calculateVolume();
|
||||
dataModel.calculateTotalToPay();
|
||||
}
|
||||
evaluateViewState();
|
||||
};
|
||||
isWalletFundedChangeListener = (ov, oldValue, newValue) -> evaluateViewState();
|
||||
amountAsCoinChangeListener = (ov, oldValue, newValue) -> amount.set(formatter.formatCoin(newValue));
|
||||
offerStateChangeListener = (ov, oldValue, newValue) -> applyOfferState(newValue);
|
||||
tradeStateChangeListener = (ov, oldValue, newValue) -> applyTradeState(newValue);
|
||||
}
|
||||
|
||||
private void addListeners() {
|
||||
// Bidirectional bindings are used for all input fields: amount, price, volume and minAmount
|
||||
// We do volume/amount calculation during input, so user has immediate feedback
|
||||
amount.addListener(amountChangeListener);
|
||||
dataModel.isWalletFunded.addListener(isWalletFundedChangeListener);
|
||||
// Binding with Bindings.createObjectBinding does not work because of bi-directional binding
|
||||
dataModel.amountAsCoin.addListener(amountAsCoinChangeListener);
|
||||
|
||||
amountChangeListener.changed(null, null, amount.get());
|
||||
isWalletFundedChangeListener.changed(null, null, dataModel.isWalletFunded.get());
|
||||
amountAsCoinChangeListener.changed(null, null, dataModel.amountAsCoin.get());
|
||||
}
|
||||
|
||||
private void removeListeners() {
|
||||
amount.removeListener(amountChangeListener);
|
||||
dataModel.isWalletFunded.removeListener(isWalletFundedChangeListener);
|
||||
dataModel.amountAsCoin.removeListener(amountAsCoinChangeListener);
|
||||
|
||||
if (offer != null)
|
||||
offer.stateProperty().removeListener(offerStateChangeListener);
|
||||
|
||||
if (trade != null)
|
||||
trade.tradeStateProperty().removeListener(tradeStateChangeListener);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void initWithData(Coin amount, Offer offer) {
|
||||
dataModel.initWithData(amount, offer);
|
||||
|
||||
// called before doActivate
|
||||
void initWithData(Offer offer) {
|
||||
dataModel.initWithData(offer);
|
||||
this.offer = offer;
|
||||
|
||||
directionLabel = offer.getDirection() == Offer.Direction.SELL ? BSResources.get("shared.buyBitcoin") : BSResources.get("shared.sellBitcoin");
|
||||
|
||||
fiatCode.set(offer.getCurrencyCode());
|
||||
if (!dataModel.isMinAmountLessOrEqualAmount())
|
||||
amountValidationResult.set(new InputValidator.ValidationResult(false, BSResources.get("takeOffer.validation.amountSmallerThanMinAmount")));
|
||||
|
||||
if (dataModel.getDirection() == Offer.Direction.BUY) {
|
||||
amountDescription.set(BSResources.get("takeOffer.amountPriceBox.buy.amountDescription", offer.getId()));
|
||||
volumeDescriptionLabel.set(BSResources.get("takeOffer.amountPriceBox.buy.volumeDescription", fiatCode.get()));
|
||||
amountPriceBoxInfo.set(BSResources.get("takeOffer.amountPriceBox.buy.info"));
|
||||
fundsBoxInfoDisplay.set(BSResources.get("takeOffer.fundsBox.buy.info"));
|
||||
}
|
||||
else {
|
||||
amountDescription.set(BSResources.get("takeOffer.amountPriceBox.sell.amountDescription", offer.getId()));
|
||||
volumeDescriptionLabel.set(BSResources.get("takeOffer.amountPriceBox.sell.volumeDescription", fiatCode.get()));
|
||||
amountPriceBoxInfo.set(BSResources.get("takeOffer.amountPriceBox.sell.info"));
|
||||
fundsBoxInfoDisplay.set(BSResources.get("takeOffer.fundsBox.sell.info"));
|
||||
if (offer.getDirection() == Offer.Direction.BUY) {
|
||||
directionLabel = BSResources.get("shared.sellBitcoin");
|
||||
amountDescription = BSResources.get("takeOffer.amountPriceBox.buy.amountDescription");
|
||||
} else {
|
||||
directionLabel = BSResources.get("shared.buyBitcoin");
|
||||
amountDescription = BSResources.get("takeOffer.amountPriceBox.sell.amountDescription");
|
||||
}
|
||||
|
||||
amountRange = formatter.formatCoinWithCode(offer.getMinAmount()) + " - " + formatter.formatCoinWithCode(offer.getAmount());
|
||||
price = formatter.formatFiatWithCode(offer.getPrice());
|
||||
|
||||
amountRange = formatter.formatCoin(offer.getMinAmount()) + " - " + formatter.formatCoin(offer.getAmount());
|
||||
price = formatter.formatFiat(offer.getPrice());
|
||||
paymentLabel = BSResources.get("takeOffer.fundsBox.paymentLabel", offer.getId());
|
||||
assert dataModel.getAddressEntry() != null;
|
||||
|
||||
checkNotNull(dataModel.getAddressEntry(), "dataModel.getAddressEntry() must not be null");
|
||||
|
||||
addressAsString = dataModel.getAddressEntry().getAddress().toString();
|
||||
address.set(dataModel.getAddressEntry().getAddress());
|
||||
|
||||
acceptedCountries = formatter.countryLocalesToString(offer.getAcceptedCountries());
|
||||
acceptedLanguages = formatter.languageCodesToString(offer.getAcceptedLanguageCodes());
|
||||
acceptedArbitratorIds = formatter.arbitratorIdsToNames(offer.getArbitratorIds());
|
||||
bankAccountType = BSResources.get(offer.getFiatAccountType().toString());
|
||||
bankAccountCurrency = BSResources.get(CurrencyUtil.getDisplayName(offer.getCurrencyCode()));
|
||||
bankAccountCounty = BSResources.get(offer.getBankAccountCountry().name);
|
||||
|
||||
offer.stateProperty().addListener(offerStateChangeListener);
|
||||
applyOfferState(offer.stateProperty().get());
|
||||
offerErrorListener = (observable, oldValue, newValue) -> {
|
||||
if (newValue != null)
|
||||
errorMessage.set(newValue);
|
||||
};
|
||||
offer.errorMessageProperty().addListener(offerErrorListener);
|
||||
errorMessage.set(offer.errorMessageProperty().get());
|
||||
}
|
||||
|
||||
|
||||
|
@ -263,162 +177,27 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
applyOnTakeOfferResult(false);
|
||||
|
||||
isTakeOfferSpinnerVisible.set(true);
|
||||
dataModel.onTakeOffer((trade) -> {
|
||||
dataModel.onTakeOffer(trade -> {
|
||||
this.trade = trade;
|
||||
trade.tradeStateProperty().addListener(tradeStateChangeListener);
|
||||
applyTradeState(trade.tradeStateProperty().get());
|
||||
evaluateViewState();
|
||||
trade.stateProperty().addListener(tradeStateListener);
|
||||
applyTradeState(trade.getState());
|
||||
trade.errorMessageProperty().addListener(tradeErrorListener);
|
||||
applyTradeErrorMessage(trade.errorMessageProperty().get());
|
||||
updateButtonDisableState();
|
||||
});
|
||||
}
|
||||
|
||||
void onShowPaymentScreen() {
|
||||
state.set(TakeOfferViewModel.State.PAYMENT_SCREEN);
|
||||
public void onPaymentAccountSelected(PaymentAccount paymentAccount) {
|
||||
dataModel.onPaymentAccountSelected(paymentAccount);
|
||||
}
|
||||
|
||||
void onToggleShowAdvancedSettings() {
|
||||
detailsVisible = !detailsVisible;
|
||||
void onSecurityDepositInfoDisplayed() {
|
||||
dataModel.onSecurityDepositInfoDisplayed();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// States
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void applyOfferState(Offer.State state) {
|
||||
log.debug("applyOfferState state = " + state);
|
||||
|
||||
switch (state) {
|
||||
case UNDEFINED:
|
||||
// TODO set spinner?
|
||||
break;
|
||||
case AVAILABLE:
|
||||
this.state.set(TakeOfferViewModel.State.AMOUNT_SCREEN);
|
||||
break;
|
||||
case NOT_AVAILABLE:
|
||||
if (takeOfferRequested)
|
||||
errorMessage.set("Take offer request failed because offer is not available anymore. " +
|
||||
"Maybe another trader has taken the offer in the meantime.");
|
||||
else
|
||||
errorMessage.set("You cannot take that offer because the offer was already taken by another trader.");
|
||||
takeOfferRequested = false;
|
||||
break;
|
||||
case REMOVED:
|
||||
if (!takeOfferRequested)
|
||||
errorMessage.set("You cannot take that offer because the offer has been removed in the meantime.");
|
||||
|
||||
takeOfferRequested = false;
|
||||
break;
|
||||
case OFFERER_OFFLINE:
|
||||
if (takeOfferRequested)
|
||||
errorMessage.set("Take offer request failed because offerer is not online anymore.");
|
||||
else
|
||||
errorMessage.set("You cannot take that offer because the offerer is offline.");
|
||||
takeOfferRequested = false;
|
||||
break;
|
||||
case FAULT:
|
||||
if (takeOfferRequested)
|
||||
errorMessage.set("Take offer request failed.");
|
||||
else
|
||||
errorMessage.set("The check for the offer availability failed.");
|
||||
takeOfferRequested = false;
|
||||
break;
|
||||
case TIMEOUT:
|
||||
if (takeOfferRequested)
|
||||
errorMessage.set("Take offer request failed due a timeout.");
|
||||
else
|
||||
errorMessage.set("The check for the offer availability failed due a timeout.");
|
||||
takeOfferRequested = false;
|
||||
break;
|
||||
default:
|
||||
log.error("Unhandled offer state: " + state);
|
||||
break;
|
||||
}
|
||||
|
||||
if (errorMessage.get() != null) {
|
||||
isTakeOfferSpinnerVisible.set(false);
|
||||
}
|
||||
|
||||
evaluateViewState();
|
||||
}
|
||||
|
||||
private void applyTradeState(TradeState tradeState) {
|
||||
log.debug("applyTradeState state = " + tradeState);
|
||||
|
||||
String msg = "An error occurred.";
|
||||
if (trade.getErrorMessage() != null)
|
||||
msg = "Error message: " + trade.getErrorMessage();
|
||||
|
||||
if (trade instanceof SellerAsTakerTrade) {
|
||||
switch ((TradeState.SellerState) tradeState) {
|
||||
case PREPARATION:
|
||||
break;
|
||||
case DEPOSIT_PUBLISHED_MSG_RECEIVED:
|
||||
assert trade.getDepositTx() != null;
|
||||
transactionId.set(trade.getDepositTx().getHashAsString());
|
||||
applyOnTakeOfferResult(true);
|
||||
break;
|
||||
case DEPOSIT_CONFIRMED:
|
||||
case FIAT_PAYMENT_STARTED_MSG_RECEIVED:
|
||||
case FIAT_PAYMENT_RECEIPT:
|
||||
case FIAT_PAYMENT_RECEIPT_MSG_SENT:
|
||||
case PAYOUT_TX_RECEIVED:
|
||||
case PAYOUT_TX_COMMITTED:
|
||||
case PAYOUT_BROAD_CASTED:
|
||||
break;
|
||||
/* case TIMEOUT:
|
||||
errorMessage.set("A timeout occurred. Maybe there are connection problems. " +
|
||||
"Please try later again.\n" + msg);
|
||||
takeOfferRequested = false;
|
||||
break;
|
||||
case FAULT:
|
||||
errorMessage.set(msg);
|
||||
takeOfferRequested = false;
|
||||
break;*/
|
||||
default:
|
||||
log.warn("Unhandled trade state: " + tradeState);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (trade instanceof BuyerAsTakerTrade) {
|
||||
switch ((TradeState.BuyerState) tradeState) {
|
||||
case PREPARATION:
|
||||
break;
|
||||
case DEPOSIT_PUBLISHED:
|
||||
assert trade.getDepositTx() != null;
|
||||
transactionId.set(trade.getDepositTx().getHashAsString());
|
||||
applyOnTakeOfferResult(true);
|
||||
break;
|
||||
case DEPOSIT_PUBLISHED_MSG_SENT:
|
||||
case DEPOSIT_CONFIRMED:
|
||||
case FIAT_PAYMENT_STARTED:
|
||||
case FIAT_PAYMENT_STARTED_MSG_SENT:
|
||||
case FIAT_PAYMENT_RECEIPT_MSG_RECEIVED:
|
||||
case PAYOUT_TX_COMMITTED:
|
||||
case PAYOUT_TX_SENT:
|
||||
case PAYOUT_BROAD_CASTED:
|
||||
break;
|
||||
/* case TIMEOUT:
|
||||
errorMessage.set("A timeout occurred. Maybe there are connection problems. " +
|
||||
"Please try later again.\n" + msg);
|
||||
takeOfferRequested = false;
|
||||
break;
|
||||
case FAULT:
|
||||
errorMessage.set(msg);
|
||||
takeOfferRequested = false;
|
||||
break;*/
|
||||
default:
|
||||
log.warn("Unhandled trade state: " + tradeState);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (errorMessage.get() != null)
|
||||
isTakeOfferSpinnerVisible.set(false);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Focus handling
|
||||
// Handle focus
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// On focus out we do validation and apply the data to the model
|
||||
|
@ -448,91 +227,199 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getters
|
||||
// States
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
boolean isSeller() {
|
||||
return dataModel.getDirection() == Offer.Direction.BUY;
|
||||
private void applyOfferState(Offer.State state) {
|
||||
log.debug("applyOfferState state = " + state);
|
||||
offerWarning.set(null);
|
||||
|
||||
// We have 2 situations handled here:
|
||||
// 1. when clicking take offer in the offerbook screen, we do the availability check
|
||||
// 2. Before actually taking the offer in the take offer screen, we check again the availability as some time might have passed in the meantime
|
||||
// So we use the takeOfferRequested flag to display different messages depending on the context.
|
||||
switch (state) {
|
||||
case UNDEFINED:
|
||||
break;
|
||||
case OFFER_FEE_PAID:
|
||||
// irrelevant for taker
|
||||
break;
|
||||
case AVAILABLE:
|
||||
isOfferAvailable.set(true);
|
||||
break;
|
||||
case NOT_AVAILABLE:
|
||||
if (takeOfferRequested)
|
||||
offerWarning.set("Take offer request failed because offer is not available anymore. " +
|
||||
"Maybe another trader has taken the offer in the meantime.");
|
||||
else
|
||||
offerWarning.set("You cannot take that offer because the offer was already taken by another trader.");
|
||||
takeOfferRequested = false;
|
||||
break;
|
||||
case REMOVED:
|
||||
if (!takeOfferRequested)
|
||||
offerWarning.set("You cannot take that offer because the offer has been removed in the meantime.");
|
||||
|
||||
takeOfferRequested = false;
|
||||
break;
|
||||
case OFFERER_OFFLINE:
|
||||
if (takeOfferRequested)
|
||||
offerWarning.set("Take offer request failed because offerer is not online anymore.");
|
||||
else
|
||||
offerWarning.set("You cannot take that offer because the offerer is offline.");
|
||||
takeOfferRequested = false;
|
||||
break;
|
||||
default:
|
||||
log.error("Unhandled offer state: " + state);
|
||||
break;
|
||||
}
|
||||
|
||||
if (offerWarning != null)
|
||||
isTakeOfferSpinnerVisible.set(false);
|
||||
|
||||
updateButtonDisableState();
|
||||
}
|
||||
|
||||
void onSecurityDepositInfoDisplayed() {
|
||||
dataModel.onSecurityDepositInfoDisplayed();
|
||||
private void applyTradeErrorMessage(String errorMessage) {
|
||||
if (errorMessage != null) {
|
||||
String appendMsg = "";
|
||||
switch (trade.getState().getPhase()) {
|
||||
case PREPARATION:
|
||||
appendMsg = "\n\nThere have no funds left your wallet yet.\n" +
|
||||
"Please try to restart you application and check your network connection to see if you can resolve the issue.";
|
||||
break;
|
||||
case TAKER_FEE_PAID:
|
||||
appendMsg = "\n\nThe take offer fee is already paid. In the worst case you have lost that fee. " +
|
||||
"We are sorry about that but keep in mind it is a very small amount.\n" +
|
||||
"Please try to restart you application and check your network connection to see if you can resolve the issue.";
|
||||
break;
|
||||
case DEPOSIT_PAID:
|
||||
case FIAT_SENT:
|
||||
case FIAT_RECEIVED:
|
||||
appendMsg = "\n\nThe deposit transaction is already published.\n" +
|
||||
"Please try to restart you application and check your network connection to see if you can resolve the issue.\n" +
|
||||
"If the problem still remains please contact the developers for support.";
|
||||
break;
|
||||
case PAYOUT_PAID:
|
||||
case WITHDRAWN:
|
||||
appendMsg = "\n\nThe payout transaction is already published.\n" +
|
||||
"Please try to restart you application and check your network connection to see if you can resolve the issue.\n" +
|
||||
"If the problem still remains please contact the developers for support.";
|
||||
break;
|
||||
case DISPUTE:
|
||||
appendMsg = "\n\nThe trade is handled already by an arbitrator.\n" +
|
||||
"Please try to restart you application and check your network connection to see if you can resolve the issue.\n" +
|
||||
"If the problem still remains please contact the arbitrator or the developers for support.";
|
||||
break;
|
||||
}
|
||||
this.errorMessage.set(errorMessage + appendMsg);
|
||||
} else {
|
||||
this.errorMessage.set(null);
|
||||
}
|
||||
}
|
||||
|
||||
WalletService getWalletService() {
|
||||
return dataModel.getWalletService();
|
||||
private void applyTradeState(Trade.State tradeState) {
|
||||
log.debug("applyTradeState state = " + tradeState);
|
||||
|
||||
if (trade.getState() == Trade.State.DEPOSIT_PUBLISHED
|
||||
|| trade.getState() == Trade.State.DEPOSIT_SEEN_IN_NETWORK
|
||||
|| trade.getState() == Trade.State.DEPOSIT_PUBLISHED_MSG_SENT
|
||||
|| trade.getState() == Trade.State.DEPOSIT_PUBLISHED_MSG_RECEIVED) {
|
||||
if (trade.getDepositTx() != null)
|
||||
applyOnTakeOfferResult(true);
|
||||
else
|
||||
log.error("trade.getDepositTx() == null. That must not happen");
|
||||
}
|
||||
|
||||
if (errorMessage.get() != null)
|
||||
isTakeOfferSpinnerVisible.set(false);
|
||||
}
|
||||
|
||||
BSFormatter getFormatter() {
|
||||
return formatter;
|
||||
private void applyOnTakeOfferResult(boolean success) {
|
||||
isTakeOfferSpinnerVisible.set(false);
|
||||
showTransactionPublishedScreen.set(success);
|
||||
}
|
||||
|
||||
String getOfferFee() {
|
||||
return offerFee;
|
||||
private void updateButtonDisableState() {
|
||||
isTakeOfferButtonDisabled.set(!(isBtcInputValid(amount.get()).isValid
|
||||
&& dataModel.isMinAmountLessOrEqualAmount()
|
||||
&& !dataModel.isAmountLargerThanOfferAmount()
|
||||
&& dataModel.isWalletFunded.get()
|
||||
&& !takeOfferRequested)
|
||||
);
|
||||
}
|
||||
|
||||
String getNetworkFee() {
|
||||
return networkFee;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Bindings, listeners
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void addBindings() {
|
||||
volume.bind(createStringBinding(() -> formatter.formatFiat(dataModel.volumeAsFiat.get()), dataModel.volumeAsFiat));
|
||||
|
||||
|
||||
if (dataModel.getDirection() == Offer.Direction.BUY) {
|
||||
volumeDescriptionLabel.set(BSResources.get("createOffer.amountPriceBox.buy.volumeDescription", dataModel.getTradeCurrency().getCode()));
|
||||
} else {
|
||||
volumeDescriptionLabel.set(BSResources.get("createOffer.amountPriceBox.sell.volumeDescription", dataModel.getTradeCurrency().getCode()));
|
||||
}
|
||||
totalToPay.bind(createStringBinding(() -> formatter.formatCoinWithCode(dataModel.totalToPayAsCoin.get()), dataModel.totalToPayAsCoin));
|
||||
totalToPayAsCoin.bind(dataModel.totalToPayAsCoin);
|
||||
btcCode.bind(dataModel.btcCode);
|
||||
}
|
||||
|
||||
String getFiatCode() {
|
||||
return fiatCode.get();
|
||||
|
||||
private void removeBindings() {
|
||||
volumeDescriptionLabel.unbind();
|
||||
volume.unbind();
|
||||
totalToPay.unbind();
|
||||
totalToPayAsCoin.unbind();
|
||||
btcCode.unbind();
|
||||
}
|
||||
|
||||
String getAmount() {
|
||||
return formatter.formatCoinWithCode(dataModel.amountAsCoin.get());
|
||||
private void createListeners() {
|
||||
amountListener = (ov, oldValue, newValue) -> {
|
||||
if (isBtcInputValid(newValue).isValid) {
|
||||
setAmountToModel();
|
||||
calculateVolume();
|
||||
dataModel.calculateTotalToPay();
|
||||
}
|
||||
updateButtonDisableState();
|
||||
};
|
||||
amountAsCoinListener = (ov, oldValue, newValue) -> amount.set(formatter.formatCoin(newValue));
|
||||
isWalletFundedListener = (ov, oldValue, newValue) -> updateButtonDisableState();
|
||||
tradeStateListener = (ov, oldValue, newValue) -> applyTradeState(newValue);
|
||||
tradeErrorListener = (ov, oldValue, newValue) -> applyTradeErrorMessage(newValue);
|
||||
offerStateListener = (ov, oldValue, newValue) -> applyOfferState(newValue);
|
||||
}
|
||||
|
||||
String getAmountRange() {
|
||||
return amountRange;
|
||||
private void addListeners() {
|
||||
// Bidirectional bindings are used for all input fields: amount, price, volume and minAmount
|
||||
// We do volume/amount calculation during input, so user has immediate feedback
|
||||
amount.addListener(amountListener);
|
||||
|
||||
// Binding with Bindings.createObjectBinding does not work because of bi-directional binding
|
||||
dataModel.amountAsCoin.addListener(amountAsCoinListener);
|
||||
|
||||
dataModel.isWalletFunded.addListener(isWalletFundedListener);
|
||||
|
||||
}
|
||||
|
||||
String getPrice() {
|
||||
return price;
|
||||
}
|
||||
private void removeListeners() {
|
||||
amount.removeListener(amountListener);
|
||||
|
||||
String getDirectionLabel() {
|
||||
return directionLabel;
|
||||
}
|
||||
// Binding with Bindings.createObjectBinding does not work because of bi-directional binding
|
||||
dataModel.amountAsCoin.removeListener(amountAsCoinListener);
|
||||
|
||||
String getBankAccountType() {
|
||||
return bankAccountType;
|
||||
}
|
||||
dataModel.isWalletFunded.removeListener(isWalletFundedListener);
|
||||
if (offer != null) {
|
||||
offer.stateProperty().removeListener(offerStateListener);
|
||||
offer.errorMessageProperty().addListener(offerErrorListener);
|
||||
}
|
||||
|
||||
String getBankAccountCurrency() {
|
||||
return bankAccountCurrency;
|
||||
}
|
||||
|
||||
String getBankAccountCounty() {
|
||||
return bankAccountCounty;
|
||||
}
|
||||
|
||||
String getAcceptedCountries() {
|
||||
return acceptedCountries;
|
||||
}
|
||||
|
||||
String getAcceptedLanguages() {
|
||||
return acceptedLanguages;
|
||||
}
|
||||
|
||||
String getAcceptedArbitratorIds() {
|
||||
return acceptedArbitratorIds;
|
||||
}
|
||||
|
||||
String getAddressAsString() {
|
||||
return addressAsString;
|
||||
}
|
||||
|
||||
String getPaymentLabel() {
|
||||
return paymentLabel;
|
||||
}
|
||||
|
||||
boolean getDisplaySecurityDepositInfo() {
|
||||
return dataModel.getDisplaySecurityDepositInfo();
|
||||
}
|
||||
|
||||
boolean isDetailsVisible() {
|
||||
return detailsVisible;
|
||||
if (trade != null) {
|
||||
trade.stateProperty().removeListener(tradeStateListener);
|
||||
trade.errorMessageProperty().removeListener(tradeErrorListener);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -540,11 +427,6 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
// Utils
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void applyOnTakeOfferResult(boolean success) {
|
||||
isTakeOfferSpinnerVisible.set(false);
|
||||
showTransactionPublishedScreen.set(success);
|
||||
}
|
||||
|
||||
private void calculateVolume() {
|
||||
setAmountToModel();
|
||||
dataModel.calculateVolume();
|
||||
|
@ -554,20 +436,104 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
dataModel.amountAsCoin.set(formatter.parseToCoinWith4Decimals(amount.get()));
|
||||
}
|
||||
|
||||
private void evaluateViewState() {
|
||||
boolean isAmountAndPriceValidAndWalletFunded = isBtcInputValid(amount.get()).isValid &&
|
||||
dataModel.isMinAmountLessOrEqualAmount() &&
|
||||
!dataModel.isAmountLargerThanOfferAmount() &&
|
||||
dataModel.isWalletFunded.get();
|
||||
|
||||
if (isAmountAndPriceValidAndWalletFunded && state.get() != TakeOfferViewModel.State.CHECK_AVAILABILITY)
|
||||
state.set(TakeOfferViewModel.State.PAYMENT_SCREEN);
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getters
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
takeOfferButtonDisabled.set(!isAmountAndPriceValidAndWalletFunded || takeOfferRequested);
|
||||
BSFormatter getFormatter() {
|
||||
return formatter;
|
||||
}
|
||||
|
||||
boolean getDisplaySecurityDepositInfo() {
|
||||
return dataModel.getDisplaySecurityDepositInfo();
|
||||
}
|
||||
|
||||
boolean isSeller() {
|
||||
return dataModel.getDirection() == Offer.Direction.BUY;
|
||||
}
|
||||
|
||||
private InputValidator.ValidationResult isBtcInputValid(String input) {
|
||||
return btcValidator.validate(input);
|
||||
}
|
||||
|
||||
public Offer getOffer() {
|
||||
return dataModel.getOffer();
|
||||
}
|
||||
|
||||
public String getAmountRange() {
|
||||
return amountRange;
|
||||
}
|
||||
|
||||
public String getPaymentLabel() {
|
||||
return paymentLabel;
|
||||
}
|
||||
|
||||
public String getAddressAsString() {
|
||||
return addressAsString;
|
||||
}
|
||||
|
||||
public String getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public String getDirectionLabel() {
|
||||
return directionLabel;
|
||||
}
|
||||
|
||||
public String getAmountDescription() {
|
||||
return amountDescription;
|
||||
}
|
||||
|
||||
String getAmount() {
|
||||
return formatter.formatCoinWithCode(dataModel.amountAsCoin.get());
|
||||
}
|
||||
|
||||
String getOfferFee() {
|
||||
return formatter.formatCoinWithCode(dataModel.getOfferFeeAsCoin());
|
||||
}
|
||||
|
||||
String getNetworkFee() {
|
||||
return formatter.formatCoinWithCode(dataModel.getNetworkFeeAsCoin());
|
||||
}
|
||||
|
||||
public String getSecurityDeposit() {
|
||||
return formatter.formatCoinWithCode(dataModel.getSecurityDepositAsCoin());
|
||||
}
|
||||
|
||||
public PaymentMethod getPaymentMethod() {
|
||||
return dataModel.getPaymentMethod();
|
||||
}
|
||||
|
||||
ObservableList<PaymentAccount> getPossiblePaymentAccounts() {
|
||||
return dataModel.getPossiblePaymentAccounts();
|
||||
}
|
||||
|
||||
public TradeCurrency getTradeCurrency() {
|
||||
return dataModel.getTradeCurrency();
|
||||
}
|
||||
|
||||
public boolean getShowTakeOfferConfirmation() {
|
||||
return dataModel.getShowTakeOfferConfirmation();
|
||||
}
|
||||
|
||||
public List<Arbitrator> getArbitrators() {
|
||||
return dataModel.getArbitrators();
|
||||
}
|
||||
|
||||
boolean hasAcceptedArbitrators() {
|
||||
return dataModel.hasAcceptedArbitrators();
|
||||
}
|
||||
|
||||
public void resetOfferWarning() {
|
||||
offerWarning.set(null);
|
||||
}
|
||||
|
||||
public Trade getTrade() {
|
||||
return trade;
|
||||
}
|
||||
|
||||
public void resetErrorMessage() {
|
||||
offer.setErrorMessage(null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,11 +19,7 @@ package io.bitsquare.gui.main.portfolio;
|
|||
|
||||
import io.bitsquare.gui.Navigation;
|
||||
import io.bitsquare.gui.common.model.Activatable;
|
||||
import io.bitsquare.gui.common.view.ActivatableViewAndModel;
|
||||
import io.bitsquare.gui.common.view.CachingViewLoader;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.common.view.View;
|
||||
import io.bitsquare.gui.common.view.ViewLoader;
|
||||
import io.bitsquare.gui.common.view.*;
|
||||
import io.bitsquare.gui.main.MainView;
|
||||
import io.bitsquare.gui.main.portfolio.closedtrades.ClosedTradesView;
|
||||
import io.bitsquare.gui.main.portfolio.failedtrades.FailedTradesView;
|
||||
|
@ -31,26 +27,26 @@ import io.bitsquare.gui.main.portfolio.openoffer.OpenOffersView;
|
|||
import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesView;
|
||||
import io.bitsquare.trade.Trade;
|
||||
import io.bitsquare.trade.failed.FailedTradesManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.control.Tab;
|
||||
import javafx.scene.control.TabPane;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@FxmlView
|
||||
public class PortfolioView extends ActivatableViewAndModel<TabPane, Activatable> {
|
||||
|
||||
@FXML Tab openOffersTab, pendingTradesTab, closedTradesTab;
|
||||
private Tab failedTradesTab = new Tab("Failed");
|
||||
private final Tab failedTradesTab = new Tab("Failed");
|
||||
private Tab currentTab;
|
||||
private Navigation.Listener navigationListener;
|
||||
private ChangeListener<Tab> tabChangeListener;
|
||||
|
||||
private final ViewLoader viewLoader;
|
||||
private final Navigation navigation;
|
||||
private FailedTradesManager failedTradesManager;
|
||||
private final FailedTradesManager failedTradesManager;
|
||||
|
||||
@Inject
|
||||
public PortfolioView(CachingViewLoader viewLoader, Navigation navigation, FailedTradesManager failedTradesManager) {
|
||||
|
@ -62,13 +58,11 @@ public class PortfolioView extends ActivatableViewAndModel<TabPane, Activatable>
|
|||
@Override
|
||||
public void initialize() {
|
||||
navigationListener = viewPath -> {
|
||||
log.debug("navigationListener");
|
||||
if (viewPath.size() == 3 && viewPath.indexOf(PortfolioView.class) == 1)
|
||||
loadView(viewPath.tip());
|
||||
};
|
||||
|
||||
tabChangeListener = (ov, oldValue, newValue) -> {
|
||||
log.debug("tabChangeListener");
|
||||
if (newValue == openOffersTab)
|
||||
navigation.navigateTo(MainView.class, PortfolioView.class, OpenOffersView.class);
|
||||
else if (newValue == pendingTradesTab)
|
||||
|
@ -81,17 +75,14 @@ public class PortfolioView extends ActivatableViewAndModel<TabPane, Activatable>
|
|||
}
|
||||
|
||||
@Override
|
||||
public void doActivate() {
|
||||
protected void activate() {
|
||||
failedTradesManager.getFailedTrades().addListener((ListChangeListener<Trade>) c -> {
|
||||
if (failedTradesManager.getFailedTrades().size() > 0 && root.getTabs().size() == 3)
|
||||
root.getTabs().add(failedTradesTab);
|
||||
else
|
||||
root.getTabs().remove(failedTradesTab);
|
||||
});
|
||||
if (failedTradesManager.getFailedTrades().size() > 0 && root.getTabs().size() == 3)
|
||||
root.getTabs().add(failedTradesTab);
|
||||
else
|
||||
root.getTabs().remove(failedTradesTab);
|
||||
|
||||
root.getSelectionModel().selectedItemProperty().addListener(tabChangeListener);
|
||||
navigation.addListener(navigationListener);
|
||||
|
||||
|
@ -106,7 +97,7 @@ public class PortfolioView extends ActivatableViewAndModel<TabPane, Activatable>
|
|||
}
|
||||
|
||||
@Override
|
||||
public void doDeactivate() {
|
||||
protected void deactivate() {
|
||||
root.getSelectionModel().selectedItemProperty().removeListener(tabChangeListener);
|
||||
navigation.removeListener(navigationListener);
|
||||
currentTab = null;
|
||||
|
|
|
@ -17,25 +17,21 @@
|
|||
|
||||
package io.bitsquare.gui.main.portfolio.closedtrades;
|
||||
|
||||
import io.bitsquare.gui.common.model.Activatable;
|
||||
import io.bitsquare.gui.common.model.DataModel;
|
||||
import com.google.inject.Inject;
|
||||
import io.bitsquare.gui.common.model.ActivatableDataModel;
|
||||
import io.bitsquare.trade.Tradable;
|
||||
import io.bitsquare.trade.closed.ClosedTradableManager;
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
class ClosedTradesDataModel implements Activatable, DataModel {
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
class ClosedTradesDataModel extends ActivatableDataModel {
|
||||
private final ClosedTradableManager closedTradableManager;
|
||||
|
||||
private final ObservableList<ClosedTradesListItem> list = FXCollections.observableArrayList();
|
||||
private final ObservableList<ClosedTradableListItem> list = FXCollections.observableArrayList();
|
||||
private final ListChangeListener<Tradable> tradesListChangeListener;
|
||||
|
||||
@Inject
|
||||
|
@ -46,17 +42,17 @@ class ClosedTradesDataModel implements Activatable, DataModel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void activate() {
|
||||
protected void activate() {
|
||||
applyList();
|
||||
closedTradableManager.getClosedTrades().addListener(tradesListChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
protected void deactivate() {
|
||||
closedTradableManager.getClosedTrades().removeListener(tradesListChangeListener);
|
||||
}
|
||||
|
||||
public ObservableList<ClosedTradesListItem> getList() {
|
||||
public ObservableList<ClosedTradableListItem> getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
|
@ -67,7 +63,7 @@ class ClosedTradesDataModel implements Activatable, DataModel {
|
|||
private void applyList() {
|
||||
list.clear();
|
||||
|
||||
list.addAll(closedTradableManager.getClosedTrades().stream().map(ClosedTradesListItem::new).collect(Collectors.toList()));
|
||||
list.addAll(closedTradableManager.getClosedTrades().stream().map(ClosedTradableListItem::new).collect(Collectors.toList()));
|
||||
|
||||
// we sort by date, earliest first
|
||||
list.sort((o1, o2) -> o2.getTradable().getDate().compareTo(o1.getTradable().getDate()));
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue