Improve usability, fix bugs

This commit is contained in:
Manfred Karrer 2014-10-02 14:30:05 +02:00
parent a334beb1a7
commit b9b7d4b3e0
29 changed files with 322 additions and 152 deletions

View File

@ -1,6 +1,6 @@
#Mon Sep 29 14:09:23 CEST 2014
#Tue Sep 30 23:44:09 CEST 2014
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-all.zip

View File

@ -22,9 +22,9 @@ import io.bitsquare.di.BitSquareModule;
import io.bitsquare.gui.AWTSystemTray;
import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.components.Popups;
import io.bitsquare.gui.util.ImageUtil;
import io.bitsquare.gui.util.Profiler;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.SeedNodeAddress;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.settings.Settings;
import io.bitsquare.user.User;
@ -38,10 +38,10 @@ import com.google.inject.Injector;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.image.*;
import javafx.scene.input.*;
import javafx.stage.Stage;
@ -67,9 +67,35 @@ public class BitSquare extends Application {
if (args.length > 0)
APP_NAME = APP_NAME + "_" + args[0];
/*Thread seedNodeThread = new Thread(new Runnable() {
@Override
public void run() {
startSeedNode();
}
});
seedNodeThread.start();*/
launch(args);
}
private static void startSeedNode() {
List<SeedNodeAddress.StaticSeedNodeAddresses> staticSedNodeAddresses = SeedNodeAddress
.StaticSeedNodeAddresses.getAllSeedNodeAddresses();
SeedNode seedNode = new SeedNode(new SeedNodeAddress(staticSedNodeAddresses.get(0)));
seedNode.setDaemon(true);
seedNode.start();
try {
// keep main thread up
Thread.sleep(Long.MAX_VALUE);
log.debug("Localhost seed node started");
} catch (InterruptedException e) {
log.error(e.toString());
}
}
public static Stage getPrimaryStage() {
return primaryStage;
}
@ -113,12 +139,6 @@ public class BitSquare extends Application {
settings.applyPersistedSettings((Settings) persistence.read(settings.getClass().getName()));
primaryStage.setTitle("BitSquare (" + APP_NAME + ")");
if (ImageUtil.isRetina())
primaryStage.getIcons().add(new Image(BitSquare.class.getResourceAsStream
("/images/window_icon@2x.png")));
else
primaryStage.getIcons().add(new Image(BitSquare.class.getResourceAsStream
("/images/window_icon.png")));
ViewLoader.setInjector(injector);
@ -127,7 +147,7 @@ public class BitSquare extends Application {
try {
final Parent view = loader.load();
final Scene scene = new Scene(view, 1000, 800);
final Scene scene = new Scene(view, 1000, 900);
scene.getStylesheets().setAll(getClass().getResource("/io/bitsquare/gui/bitsquare.css").toExternalForm(),
getClass().getResource("/io/bitsquare/gui/images.css").toExternalForm());

View File

@ -133,11 +133,13 @@ public class SeedNode extends Thread {
@Override
public void peerUpdated(PeerAddress peerAddress, PeerStatatistic peerStatistics) {
log.debug("Peer updated: peerAddress=" + peerAddress + ", peerStatistics=" + peerStatistics);
// log.debug("Peer updated: peerAddress=" + peerAddress + ", peerStatistics=" + peerStatistics);
}
});
} catch (IOException e) {
e.printStackTrace();
log.info("If the second client has been started that message is ok, as we cannot start the seed node twice."
+ e.getMessage());
// e.printStackTrace();
}
}

View File

@ -91,11 +91,18 @@ class AddressBasedCoinSelector extends DefaultCoinSelector {
// Pick chain-included transactions and transactions that are pending.
TransactionConfidence confidence = tx.getConfidence();
TransactionConfidence.ConfidenceType type = confidence.getConfidenceType();
return type.equals(TransactionConfidence.ConfidenceType.BUILDING) || type.equals(TransactionConfidence
.ConfidenceType.PENDING) &&
// In regtest mode we expect to have only one peer, so we won't see transactions propagate.
// TODO: The value 1 below dates from a time when transactions we broadcast *to* were counted, set to 0
(confidence.numBroadcastPeers() > 1 || tx.getParams() == RegTestParams.get());
// TODO It might be risky to accept 0 confirmation tx from the network with only > 1 numBroadcastPeers
// Need to be tested in testnet and mainnet
// We need to handle cases when malleability happens or tx get lost and have not been successful propagated
return type.equals(TransactionConfidence.ConfidenceType.BUILDING) ||
type.equals(TransactionConfidence.ConfidenceType.PENDING) &&
// we accept network tx without confirmations and numBroadcastPeers > 0
/*confidence.getSource().equals(TransactionConfidence.Source.SELF) &&*/
// In regtest mode we expect to have only one peer, so we won't see transactions propagate.
// TODO: The value 1 below dates from a time when transactions we broadcast *to* were
// counted, set to 0
(confidence.numBroadcastPeers() > 1 || tx.getParams() == RegTestParams.get());
}
private static boolean isInBlockChain(Transaction tx) {

View File

@ -105,20 +105,19 @@ class StaticSeedNodeAddressesProvider implements Provider<SeedNodeAddress.Static
if (BitSquareModule.properties == null)
BitSquareModule.properties = ConfigLoader.loadConfig();
log.info("defaultSeedNode = " + BitSquareModule.properties.getProperty("defaultSeedNode"));
String defaultSeedNodeFromConfig = BitSquareModule.properties.getProperty("defaultSeedNode");
log.info("seedNode from config file: " + BitSquareModule.properties.getProperty("defaultSeedNode"));
String seedNodeFromConfig = BitSquareModule.properties.getProperty("defaultSeedNode");
// Set default
SeedNodeAddress.StaticSeedNodeAddresses defaultSeedNode = SeedNodeAddress.StaticSeedNodeAddresses.LOCALHOST;
/* SeedNodeAddress.StaticSeedNodeAddresses defaultSeedNode = SeedNodeAddress.StaticSeedNodeAddresses
.DIGITAL_OCEAN;*/
SeedNodeAddress.StaticSeedNodeAddresses seedNode = SeedNodeAddress.StaticSeedNodeAddresses.LOCALHOST;
// SeedNodeAddress.StaticSeedNodeAddresses seedNode = SeedNodeAddress.StaticSeedNodeAddresses.DIGITAL_OCEAN;
// if defined in config we override the above
if (defaultSeedNodeFromConfig != null)
defaultSeedNode = defaultSeedNodeFromConfig.equals("localhost") ?
if (seedNodeFromConfig != null)
seedNode = seedNodeFromConfig.equals("localhost") ?
SeedNodeAddress.StaticSeedNodeAddresses.LOCALHOST :
SeedNodeAddress.StaticSeedNodeAddresses.DIGITAL_OCEAN;
return defaultSeedNode;
return seedNode;
}
}
@ -132,12 +131,12 @@ class NetworkParametersProvider implements Provider<NetworkParameters> {
if (BitSquareModule.properties == null)
BitSquareModule.properties = ConfigLoader.loadConfig();
log.info("networkType = " + BitSquareModule.properties.getProperty("networkType"));
log.info("networkType from config file: " + BitSquareModule.properties.getProperty("networkType"));
String networkTypeFromConfig = BitSquareModule.properties.getProperty("networkType");
// Set default
// String networkType= WalletFacade.MAIN_NET;
// String networkType= WalletFacade.TEST_NET;
// String networkType = WalletFacade.TEST_NET;
String networkType = WalletFacade.REG_TEST_NET;
if (networkTypeFromConfig != null)

View File

@ -31,16 +31,18 @@ lower gradient color on tab: dddddd
-fx-selection-bar: derive(-fx-accent,50%);
}
/* Splash */
#splash {
-fx-background-color: #ffffff;
}
/* Main UI */
#logo-sub-title-label {
-fx-font-weight: bold;
-fx-font-size: 24;
-fx-text-fill: -bs-grey;
-fx-text-fill: #333333;
}
#base-content-container {
@ -68,8 +70,8 @@ lower gradient color on tab: dddddd
-fx-fill: red;
}
/* main nav */
/* Main navigation */
#nav-button {
-fx-cursor: hand;
-fx-background-color: transparent;

View File

@ -39,13 +39,6 @@ import org.controlsfx.dialog.Dialogs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Collection of controlsfx Popups
* <p>
* TODO Replace it with custom views as they are graphically more flexible and the Dialogs are a bit limited from the
* API
*/
@Deprecated
public class Popups {
private static final Logger log = LoggerFactory.getLogger(Popups.class);
@ -58,16 +51,12 @@ public class Popups {
// Information
public static void openInfo(String message) {
openInfo(message, null);
openInfo(null, message);
}
// Supports blurring the content background
public static void openInfo(String message, String masthead) {
public static void openInfo(String masthead, String message) {
overlayManager.blurContent();
List<Action> actions = new ArrayList<>();
// Dialogs are a bit limited. There is no callback for the InformationDialog button click, so we added
// our own actions.
actions.add(new AbstractAction(BSResources.get("shared.close")) {
@Override
public void handle(ActionEvent actionEvent) {
@ -75,11 +64,10 @@ public class Popups {
overlayManager.removeBlurContent();
}
});
openInfo(message, masthead, actions);
openInfo(masthead, message, actions);
}
public static void openInfo(String message, String masthead, List<Action> actions) {
public static void openInfo(String masthead, String message, List<Action> actions) {
Dialogs.create()
.owner(BitSquare.getPrimaryStage())
.message(message)
@ -90,17 +78,30 @@ public class Popups {
// Confirm
public static Action openConfirmPopup(String title, String message) {
return openConfirmPopup(title, message, null);
return openConfirmPopup(title, null, message);
}
public static Action openConfirmPopup(String title, String message, String masthead) {
public static Action openConfirmPopup(String title, String masthead, String message) {
overlayManager.blurContent();
List<Action> actions = new ArrayList<>();
actions.add(Dialog.Actions.OK);
actions.add(Dialog.Actions.CANCEL);
return openConfirmPopup(title, message, masthead, actions);
actions.add(new AbstractAction(BSResources.get("shared.ok")) {
@Override
public void handle(ActionEvent actionEvent) {
Dialog.Actions.OK.handle(actionEvent);
overlayManager.removeBlurContent();
}
});
actions.add(new AbstractAction(BSResources.get("shared.cancel")) {
@Override
public void handle(ActionEvent actionEvent) {
Dialog.Actions.CANCEL.handle(actionEvent);
overlayManager.removeBlurContent();
}
});
return openConfirmPopup(title, masthead, message, actions);
}
public static Action openConfirmPopup(String title, String message, String masthead, List<Action> actions) {
public static Action openConfirmPopup(String title, String masthead, String message, List<Action> actions) {
return Dialogs.create()
.owner(BitSquare.getPrimaryStage())
.title(title)
@ -112,19 +113,33 @@ public class Popups {
// Warning
public static void openWarningPopup(String message) {
openWarningPopup("Warning", message, null);
openWarningPopup("Warning", message);
}
public static void openWarningPopup(String title, String message) {
openWarningPopup(title, message, null);
openWarningPopup(title, null, message);
}
public static void openWarningPopup(String title, String message, String masthead) {
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) {
Dialog.Actions.CLOSE.handle(actionEvent);
overlayManager.removeBlurContent();
}
});
openWarningPopup(title, masthead, message, actions);
}
private static void openWarningPopup(String title, String masthead, String message, List<Action> actions) {
Dialogs.create()
.owner(BitSquare.getPrimaryStage())
.title(title)
.message(message)
.masthead(masthead)
.actions(actions)
.showWarning();
}
@ -134,15 +149,29 @@ public class Popups {
}
public static Action openErrorPopup(String title, String message) {
return openErrorPopup(title, message, null);
return openErrorPopup(title, null, message);
}
public static Action openErrorPopup(String title, String message, String masthead) {
public static Action 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) {
Dialog.Actions.CLOSE.handle(actionEvent);
overlayManager.removeBlurContent();
}
});
return openErrorPopup(title, masthead, message, actions);
}
private static Action openErrorPopup(String title, String masthead, String message, List<Action> actions) {
return Dialogs.create()
.owner(BitSquare.getPrimaryStage())
.title(title)
.message(message)
.masthead(masthead)
.actions(actions)
.showError();
}
@ -152,15 +181,25 @@ public class Popups {
}
public static Action openExceptionPopup(Throwable throwable, String title, String message) {
return openExceptionPopup(throwable, title, message, null);
return openExceptionPopup(throwable, title, null, message);
}
public static Action openExceptionPopup(Throwable throwable, String title, String message, String masthead) {
private static Action 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) {
Dialog.Actions.CLOSE.handle(actionEvent);
overlayManager.removeBlurContent();
}
});
return Dialogs.create()
.owner(BitSquare.getPrimaryStage())
.title(title)
.message(message)
.masthead(masthead)
.actions(actions)
.showException(throwable);
}
@ -173,35 +212,30 @@ public class Popups {
Runnable runnable = () -> {
if (Throwables.getRootCause(throwable) instanceof BlockStoreException) {
Action response = Popups.openErrorPopup("Application already running",
"This application is already running and cannot be started twice.", "");
if (response == Dialog.Actions.OK) Platform.exit();
Action response = Popups.openErrorPopup("Error", "Application already running",
"This application is already running and cannot be started twice.\n\n " +
"Check your system tray to reopen the window of the running application.");
if (response == Dialog.Actions.OK)
Platform.exit();
}
else {
Action response = Popups.openExceptionPopup(throwable, "Exception", "",
"A critical error has occurred.\nPlease copy the exception details and send a bug report to " +
"bugs@bitsquare.io.");
if (response == Dialog.Actions.OK) Platform.exit();
Action response = Popups.openExceptionPopup(throwable, "Exception", "A critical error has occurred.",
"Please copy the exception details and open a bug report at:\n " +
"https://github.com/bitsquare/bitsquare/issues.");
if (response == Dialog.Actions.OK)
Platform.exit();
}
};
if (Platform.isFxApplicationThread()) runnable.run();
else Platform.runLater(runnable);
if (Platform.isFxApplicationThread())
runnable.run();
else
Platform.runLater(runnable);
}
// custom
public static void openInsufficientMoneyPopup() {
openWarningPopup("Not enough money available", "There is not enough money available. Please pay in first to " +
"your wallet.", null);
}
public static Action openRegistrationMissingPopup(String title, String message, String masthead,
List<Dialogs.CommandLink> commandLinks, int selectedIndex) {
return Dialogs.create()
.owner(BitSquare.getPrimaryStage())
.title(title)
.message(message)
.masthead(masthead)
.showCommandLinks(commandLinks.get(selectedIndex), commandLinks);
openWarningPopup("Warning", "There is not enough money available",
"Please pay in first to your wallet.");
}
}

View File

@ -165,10 +165,10 @@ class ProcessStepBarSkin<T> extends BehaviorSkinBase<ProcessStepBar<T>, Behavior
public void current() {
log.debug("select " + processStepItem.getLabel());
BorderStroke borderStroke = new BorderStroke(Colors.BLUE, BorderStrokeStyle.SOLID, null,
BorderStroke borderStroke = new BorderStroke(Colors.GREEN, BorderStrokeStyle.SOLID, null,
new BorderWidths(borderWidth, borderWidth, borderWidth, borderWidth), Insets.EMPTY);
this.setBorder(new Border(borderStroke));
setTextFill(Colors.BLUE);
setTextFill(Colors.GREEN);
}
public void past() {

View File

@ -25,6 +25,7 @@ import io.bitsquare.gui.ViewCB;
import io.bitsquare.gui.components.NetworkSyncPane;
import io.bitsquare.gui.components.Popups;
import io.bitsquare.gui.components.SystemNotification;
import io.bitsquare.gui.util.ImageUtil;
import io.bitsquare.gui.util.Profiler;
import io.bitsquare.gui.util.Transitions;
import io.bitsquare.trade.TradeManager;
@ -221,6 +222,13 @@ public class MainViewCB extends ViewCB<MainPM> {
if (ordersButtonButtonPane.getChildren().size() > 1)
ordersButtonButtonPane.getChildren().remove(1);
}
if (ImageUtil.isRetina())
BitSquare.getPrimaryStage().getIcons().add(new Image(getClass().getResourceAsStream
("/images/window_icon@2x.png")));
else
BitSquare.getPrimaryStage().getIcons().add(new Image(getClass().getResourceAsStream("/images/window_icon" +
".png")));
}
private void onMainNavigationAdded() {

View File

@ -20,6 +20,7 @@ package io.bitsquare.gui.main.account.content.fiat;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.bank.BankAccountType;
import io.bitsquare.gui.CachedViewCB;
import io.bitsquare.gui.OverlayManager;
import io.bitsquare.gui.components.InputTextField;
import io.bitsquare.gui.components.Popups;
import io.bitsquare.gui.main.account.MultiStepNavigation;
@ -27,6 +28,7 @@ import io.bitsquare.gui.main.account.content.ContextAware;
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;
@ -40,10 +42,12 @@ import java.util.ResourceBundle;
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;
@ -64,6 +68,7 @@ public class FiatAccountViewCB extends CachedViewCB<FiatAccountPm> implements Co
@FXML ComboBox<BankAccount> selectionComboBox;
@FXML ComboBox<BankAccountType> typesComboBox;
@FXML ComboBox<Currency> currencyComboBox;
private OverlayManager overlayManager;
///////////////////////////////////////////////////////////////////////////////////////////
@ -71,8 +76,10 @@ public class FiatAccountViewCB extends CachedViewCB<FiatAccountPm> implements Co
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
FiatAccountViewCB(FiatAccountPm presentationModel) {
FiatAccountViewCB(FiatAccountPm presentationModel, OverlayManager overlayManager) {
super(presentationModel);
this.overlayManager = overlayManager;
}
@ -170,8 +177,8 @@ public class FiatAccountViewCB extends CachedViewCB<FiatAccountPm> implements Co
InputValidator.ValidationResult result = presentationModel.requestSaveBankAccount();
if (result.isValid) {
selectionComboBox.getSelectionModel().select(null);
Popups.openInfo("You can add more accounts or continue to the next step.",
"Your payments account has been saved.");
Popups.openInfo("Your payments account has been saved.",
"You can add more accounts or continue to the next step.");
}
}
@ -219,8 +226,13 @@ public class FiatAccountViewCB extends CachedViewCB<FiatAccountPm> implements Co
presentationModel.country.addListener((ov, oldValue, newValue) -> {
if (newValue != null) {
regionComboBox.getSelectionModel().select(regionComboBox.getItems().indexOf(newValue.getRegion()));
countryComboBox.getSelectionModel().select(countryComboBox.getItems().indexOf(newValue));
int regionIndex = regionComboBox.getItems().indexOf(newValue.getRegion());
if (regionIndex >= 0 && regionIndex < regionComboBox.getItems().size())
regionComboBox.getSelectionModel().select(regionComboBox.getItems().indexOf(newValue.getRegion()));
int countryIndex = countryComboBox.getItems().indexOf(newValue);
if (countryIndex >= 0 && countryIndex < countryComboBox.getItems().size())
countryComboBox.getSelectionModel().select(countryIndex);
}
else {
regionComboBox.getSelectionModel().clearSelection();
@ -230,14 +242,27 @@ public class FiatAccountViewCB extends CachedViewCB<FiatAccountPm> implements Co
presentationModel.getCountryNotInAcceptedCountriesList().addListener((ov, oldValue, newValue) -> {
if (newValue) {
overlayManager.blurContent();
List<Action> actions = new ArrayList<>();
actions.add(Dialog.Actions.YES);
actions.add(Dialog.Actions.NO);
actions.add(new AbstractAction(BSResources.get("shared.no")) {
@Override
public void handle(ActionEvent actionEvent) {
Dialog.Actions.NO.handle(actionEvent);
overlayManager.removeBlurContent();
}
});
Action response = Popups.openConfirmPopup("Warning",
"The country of your bank account is not included in the accepted countries in the " +
"general settings.\n\nDo you want to add it automatically?",
null,
actions.add(new AbstractAction(BSResources.get("shared.yes")) {
@Override
public void handle(ActionEvent actionEvent) {
Dialog.Actions.YES.handle(actionEvent);
overlayManager.removeBlurContent();
}
});
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 (response == Dialog.Actions.YES)

View File

@ -96,8 +96,8 @@ public class RegistrationViewCB extends CachedViewCB<RegistrationPM> implements
presentationModel.requestPlaceOfferErrorMessage.addListener((o, oldValue, newValue) -> {
if (newValue != null) {
Popups.openErrorPopup(BSResources.get("shared.error"),
BSResources.get("createOffer.amountPriceBox.error.message",
presentationModel.requestPlaceOfferErrorMessage.get()));
BSResources.get("An error occurred when paying the registration fee"),
newValue);
}
});
@ -129,10 +129,10 @@ public class RegistrationViewCB extends CachedViewCB<RegistrationPM> implements
}
});
Popups.openInfo(BSResources.get("The Transaction ID for the offer payment is:\n" +
Popups.openInfo(BSResources.get("You have been successfully registered."),
BSResources.get("The transaction ID for the registration fee payment is:\n\n" +
presentationModel.getTransactionId() +
"\n\n You can now start trading."),
BSResources.get("You have been successfully registered."),
actions);
}
});

View File

@ -171,7 +171,7 @@ public class AccountSetupViewCB extends ViewCB implements MultiStepNavigation {
if (navigation.getItemsForReturning() != null)
navigation.navigationTo(navigation.getItemsForReturning());
else
navigation.navigationTo(Navigation.Item.MAIN, Navigation.Item.HOME);
navigation.navigationTo(Navigation.Item.MAIN, Navigation.Item.BUY);
}
}

View File

@ -37,9 +37,9 @@
<columns>
<TableColumn text="Trade ID" fx:id="tradeIdColumn" minWidth="100" sortable="false"/>
<TableColumn text="Date" fx:id="dateColumn" minWidth="130"/>
<TableColumn text="Amount in BTC (Min.)" fx:id="amountColumn" minWidth="130" sortable="false"/>
<TableColumn text="Trade amount in BTC" fx:id="amountColumn" minWidth="130" sortable="false"/>
<TableColumn text="Price" fx:id="priceColumn" minWidth="100" sortable="false"/>
<TableColumn text="Amount in EUR (Min.)" fx:id="volumeColumn" minWidth="130" sortable="false"/>
<TableColumn text="Trade amount in EUR" fx:id="volumeColumn" minWidth="130" sortable="false"/>
<TableColumn text="Trade type" fx:id="directionColumn" minWidth="80" sortable="false"/>
</columns>
</TableView>

View File

@ -307,15 +307,19 @@ class PendingTradesModel extends UIModel {
}
Coin getAmountToWithdraw() {
/*
AddressEntry addressEntry = walletFacade.getAddressInfoByTradeID(getTrade().getId());
return walletFacade.getBalanceForAddress(addressEntry.getAddress());
*/
AddressEntry addressEntry = walletFacade.getAddressInfoByTradeID(getTrade().getId());
log.debug("trade id " + getTrade().getId());
log.debug("getAddressString " + addressEntry.getAddressString());
log.debug("funds " + walletFacade.getBalanceForAddress(addressEntry.getAddress()).subtract(FeePolicy
.TX_FEE).toString());
// return walletFacade.getBalanceForAddress(addressEntry.getAddress()).subtract(FeePolicy.TX_FEE);
// TODO handle overpaid collateral
if (isOfferer())
return getTrade().getTradeAmount().add(getTrade().getCollateralAmount());
return getTrade().getTradeAmount().add(getTrade().getOffer().getCollateralAmount()).subtract(FeePolicy
.TX_FEE);
else
return getTrade().getCollateralAmount();
return getTrade().getCollateralAmount().subtract(FeePolicy.TX_FEE);
}
///////////////////////////////////////////////////////////////////////////////////////////

View File

@ -237,7 +237,7 @@ public class PendingTradesPM extends PresentationModel<PendingTradesModel> {
}
String getCollateral() {
return formatter.formatCoinWithCode(model.getTrade().getCollateralAmount());
return formatter.formatCoinWithCode(model.getTrade().getOffer().getCollateralAmount());
}
BtcAddressValidator getBtcAddressValidator() {

View File

@ -207,8 +207,8 @@
</GridPane.margin>
</Label>
<TextField fx:id="withdrawAmountTextField" GridPane.rowIndex="10" GridPane.columnIndex="1" editable="false"
focusTraversable="false" mouseTransparent="true" managed="false" visible="false">
<GridPane.margin>
focusTraversable="false" managed="false" visible="false">
<GridPane.margin>
<Insets top="60.0"/>
</GridPane.margin>
</TextField>

View File

@ -26,6 +26,7 @@ import io.bitsquare.btc.listeners.BalanceListener;
import io.bitsquare.gui.UIModel;
import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.locale.Country;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.settings.Settings;
import io.bitsquare.trade.Direction;
import io.bitsquare.trade.TradeManager;
@ -71,6 +72,7 @@ class CreateOfferModel extends UIModel {
private final WalletFacade walletFacade;
private final Settings settings;
private final User user;
private Persistence persistence;
private BSFormatter formatter;
private final String offerId;
@ -113,11 +115,12 @@ class CreateOfferModel extends UIModel {
// non private for testing
@Inject
public CreateOfferModel(TradeManager tradeManager, WalletFacade walletFacade, Settings settings, User user,
BSFormatter formatter) {
Persistence persistence, BSFormatter formatter) {
this.tradeManager = tradeManager;
this.walletFacade = walletFacade;
this.settings = settings;
this.user = user;
this.persistence = persistence;
this.formatter = formatter;
offerId = UUID.randomUUID().toString();
@ -192,7 +195,7 @@ class CreateOfferModel extends UIModel {
///////////////////////////////////////////////////////////////////////////////////////////
// Public
// Methods
///////////////////////////////////////////////////////////////////////////////////////////
void placeOffer() {
@ -274,6 +277,10 @@ class CreateOfferModel extends UIModel {
return true;
}
void securityDepositInfoDisplayed() {
persistence.write("displaySecurityDepositInfo", false);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Setter/Getter
@ -299,6 +306,14 @@ class CreateOfferModel extends UIModel {
return offerId;
}
Boolean displaySecurityDepositInfo() {
Object securityDepositInfoDisplayedObject = persistence.read("displaySecurityDepositInfo");
if (securityDepositInfoDisplayedObject instanceof Boolean)
return (Boolean) securityDepositInfoDisplayedObject;
else
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private

View File

@ -287,6 +287,10 @@ class CreateOfferPM extends PresentationModel<CreateOfferModel> {
}
}
void securityDepositInfoDisplayed() {
model.securityDepositInfoDisplayed();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getters
@ -300,6 +304,10 @@ class CreateOfferPM extends PresentationModel<CreateOfferModel> {
return formatter;
}
Boolean displaySecurityDepositInfo() {
return model.displaySecurityDepositInfo();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
@ -459,5 +467,4 @@ class CreateOfferPM extends PresentationModel<CreateOfferModel> {
return fiatValidator.validate(input);
}
}

View File

@ -185,8 +185,22 @@ public class CreateOfferViewCB extends CachedViewCB<CreateOfferPM> {
@FXML
void onShowPayFundsScreen() {
Popups.openInfo("To ensure that both traders are behaving fair you need to put in a security deposit to an " +
"offer. That will be refunded to you after the trade has successful completed.");
if (presentationModel.displaySecurityDepositInfo()) {
overlayManager.blurContent();
List<Action> actions = new ArrayList<>();
actions.add(new AbstractAction(BSResources.get("shared.close")) {
@Override
public void handle(ActionEvent actionEvent) {
Dialog.Actions.CLOSE.handle(actionEvent);
overlayManager.removeBlurContent();
}
});
Popups.openInfo("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);
/*
Popups.openInfo("To ensure that both traders are behaving fair you need to put in a security deposit to an " +
"offer. That will be refunded to you after the trade has successful completed. In case of a " +
@ -196,6 +210,10 @@ public class CreateOfferViewCB extends CachedViewCB<CreateOfferPM> {
"the security deposit will not leave your trading wallet, and will be refunded when you cancel your " +
"offer.");
*/
}
presentationModel.securityDepositInfoDisplayed();
priceAmountPane.setInactive();
showPaymentInfoScreenButton.setVisible(false);
@ -361,9 +379,8 @@ public class CreateOfferViewCB extends CachedViewCB<CreateOfferPM> {
}
});
Popups.openInfo(BSResources.get("createOffer.success.info",
presentationModel.transactionId.get()),
BSResources.get("createOffer.success.headline"),
Popups.openInfo(BSResources.get("createOffer.success.headline"),
BSResources.get("createOffer.success.info", presentationModel.transactionId.get()),
actions);
}
});

View File

@ -185,7 +185,8 @@ class OrderBookModel extends UIModel {
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.getCurrentBankAccount().getCountry().getName() +
") is not included in that list.");
") 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)

View File

@ -231,7 +231,7 @@ public class OrderBookViewCB extends CachedViewCB<OrderBookPM> {
void onOpenPaymentMethodsFilter() {
Popups.openWarningPopup("Under construction", "This feature is not implemented yet.");
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private methods
@ -250,8 +250,9 @@ public class OrderBookViewCB extends CachedViewCB<OrderBookPM> {
Navigation.Item.ACCOUNT_SETUP);
}
});
Popups.openInfo("You need to setup your trading account before you can trade.",
"You don't have a trading account.", actions);
Popups.openInfo("You don't have setup a trading account.",
"You need to setup your trading account before you can trade.",
actions);
}
private void takeOffer(Offer offer) {
@ -265,13 +266,26 @@ public class OrderBookViewCB extends CachedViewCB<OrderBookPM> {
}
private void openRestrictionsWarning(String restrictionsInfo) {
overlayManager.blurContent();
List<Action> actions = new ArrayList<>();
actions.add(Dialog.Actions.YES);
actions.add(Dialog.Actions.CLOSE);
actions.add(new AbstractAction(BSResources.get("shared.yes")) {
@Override
public void handle(ActionEvent actionEvent) {
Dialog.Actions.YES.handle(actionEvent);
overlayManager.removeBlurContent();
}
});
actions.add(new AbstractAction(BSResources.get("shared.no")) {
@Override
public void handle(ActionEvent actionEvent) {
Dialog.Actions.NO.handle(actionEvent);
overlayManager.removeBlurContent();
}
});
Action response = Popups.openConfirmPopup("Information",
"You do not fulfill the requirements for that offer.",
restrictionsInfo,
"You do not fulfill the requirements of that offer.",
actions);
if (response == Dialog.Actions.YES)

View File

@ -155,6 +155,7 @@ class TakeOfferModel extends UIModel {
void takeOffer() {
Trade trade = tradeManager.takeOffer(amountAsCoin.get(), offer);
trade.stateProperty().addListener((ov, oldValue, newValue) -> {
log.debug("trade state = " + newValue);
switch (newValue) {
case DEPOSIT_PUBLISHED:
transactionId.set(trade.getDepositTx().getHashAsString());
@ -166,10 +167,10 @@ class TakeOfferModel extends UIModel {
case OFFERER_REJECTED:
requestTakeOfferErrorMessage.set("Take offer request got rejected.");
break;
default:
log.error("Unhandled trade state: " + newValue);
break;
}
});
}

View File

@ -329,9 +329,8 @@ public class TakeOfferViewCB extends CachedViewCB<TakeOfferPM> {
}
});
Popups.openInfo(BSResources.get("takeOffer.success.info",
presentationModel.transactionId.get()),
BSResources.get("takeOffer.success.headline"),
Popups.openInfo(BSResources.get("takeOffer.success.headline"),
BSResources.get("takeOffer.success.info", presentationModel.transactionId.get()),
actions);
}
});

View File

@ -24,6 +24,6 @@ public class Colors {
public static final Paint LIGHT_GREY = Color.valueOf("#CCCCCC");
public static final Paint MID_GREY = Color.valueOf("#666666");
public static final Paint DARK_GREY = Color.valueOf("#333333");
public static final Paint GREEN = Color.valueOf("#008800");
public static final Paint GREEN = Color.valueOf("#00AA00");
}

View File

@ -34,6 +34,7 @@ public class Transitions {
private static final Logger log = LoggerFactory.getLogger(Transitions.class);
public static final int DURATION = 400;
private static Timeline removeBlurTimeline;
public static void fadeIn(Node node) {
fadeIn(node, DURATION);
@ -77,6 +78,9 @@ public class Transitions {
}
public static Timeline blur(Node node, int duration, boolean useDarken, boolean removeNode) {
if (removeBlurTimeline != null)
removeBlurTimeline.stop();
GaussianBlur blur = new GaussianBlur(0.0);
Timeline timeline = new Timeline();
KeyValue kv1 = new KeyValue(blur.radiusProperty(), 15.0);
@ -108,25 +112,29 @@ public class Transitions {
public static void removeBlur(Node node, int duration, boolean useDarken) {
if (node != null) {
GaussianBlur blur = (GaussianBlur) node.getEffect();
Timeline timeline = new Timeline();
KeyValue kv1 = new KeyValue(blur.radiusProperty(), 0.0);
KeyFrame kf1 = new KeyFrame(Duration.millis(DURATION), kv1);
if (blur != null) {
removeBlurTimeline = new Timeline();
KeyValue kv1 = new KeyValue(blur.radiusProperty(), 0.0);
KeyFrame kf1 = new KeyFrame(Duration.millis(DURATION), kv1);
if (useDarken) {
ColorAdjust darken = (ColorAdjust) blur.getInput();
if (useDarken) {
ColorAdjust darken = (ColorAdjust) blur.getInput();
KeyValue kv2 = new KeyValue(darken.brightnessProperty(), 0.0);
KeyFrame kf2 = new KeyFrame(Duration.millis(duration), kv2);
timeline.getKeyFrames().addAll(kf1, kf2);
KeyValue kv2 = new KeyValue(darken.brightnessProperty(), 0.0);
KeyFrame kf2 = new KeyFrame(Duration.millis(duration), kv2);
removeBlurTimeline.getKeyFrames().addAll(kf1, kf2);
}
else {
removeBlurTimeline.getKeyFrames().addAll(kf1);
}
removeBlurTimeline.setOnFinished(actionEvent -> {
node.setEffect(null);
removeBlurTimeline = null;
});
removeBlurTimeline.play();
}
else {
timeline.getKeyFrames().addAll(kf1);
}
timeline.setOnFinished(actionEvent -> node.setEffect(null));
timeline.play();
}
}
}

View File

@ -190,6 +190,10 @@ public class Offer implements Serializable {
return collateral;
}
public Coin getCollateralAmount() {
return amount.multiply(collateral).divide(1000L);
}
public String getBankAccountId() {
return bankAccountUID;
}

View File

@ -373,6 +373,7 @@ public class TradeManager {
// probably not needed
@Override
public void onWaitingForPeerResponse(SellerTakesOfferProtocol.State state) {
log.error("onWaitingForPeerResponse");
}
};

View File

@ -6,6 +6,8 @@ shared.error=Error
shared.close=Close
shared.cancel=Cancel
shared.ok=OK
shared.yes=Yes
shared.no=No
shared.openSettings=Open settings for editing
shared.buy=Buy Bitcoin
@ -35,7 +37,7 @@ createOffer.amountPriceBox.amountDescription=Amount of Bitcoin to buy
createOffer.amountPriceBox.priceDescription=Price per Bitcoin in {0}
createOffer.amountPriceBox.volumeDescription=Amount in {0} to spend
createOffer.amountPriceBox.minAmountDescription=Minimum amount of Bitcoin
createOffer.amountPriceBox.info=Define a price for which you want to buy Bitcoin and either enter the amount or the trade volume. With the minimum amount you can attract more potential traders with giving them more flexibility. But note that there is no automatic creation of a new offer for the remaining amount in the case that a trader takes your offer with a lower amount as defined in the amount field. Your offer will be removed from the orderbook once a trader has taken your offer.
createOffer.amountPriceBox.info=Define a price for which you want to buy Bitcoin and either enter the amount or the trade volume. With the minimum amount you can attract more potential traders with giving them more flexibility. But note that there is no automatic creation of a new offer for the remaining amount in the case that a trader takes your offer with a lower amount as defined in the amount field. Your offer will be removed from the offerbook once a trader has taken your offer.
createOffer.amountPriceBox.next=Next step
createOffer.amountPriceBox.warning.invalidBtcDecimalPlaces=The amount you have entered exceeds the number of allowed decimal places.\nThe amount has been adjusted to 4 decimal places.
createOffer.amountPriceBox.warning.invalidFiatDecimalPlaces=The amount you have entered exceeds the number of allowed decimal places. The amount has been adjusted to 2 decimal places.
@ -68,7 +70,7 @@ createOffer.advancedBox.currency=Currency:
createOffer.advancedBox.county=Payments account country:
createOffer.advancedBox.info=Your trading partners must fulfill your offer restrictions. You can edit the accepted countries, languages and arbitrators in the settings. The payments account details are used from your current selected payments account (if you have multiple payments accounts).
createOffer.success.headline=Your offer has been successfully published to the distributed orderbook.
createOffer.success.headline=Your offer has been successfully published to the distributed offerbook.
createOffer.success.info=The Transaction ID for the offer payment is: {0}
createOffer.error.message=An error occurred when placing the offer.\n{0}
@ -120,8 +122,8 @@ takeOffer.advancedBox.info=These are the offer restrictions your trading partner
takeOffer.success.headline=Your have successfully published the deposit.
takeOffer.success.info=You need to wait now for the Bitcoin buyer to transfer the money to you. \nYou will get a \
notification when he has started the EUR payment.You can see the status of your trade in the orders section under \
pending orders.\n\nThe Transaction ID for the deposit payment is: {0}
notification when he has started the EUR payment. You can see the status of your trade in the trades section under \
pending trades.\n\nThe Transaction ID for the deposit payment is: {0}
takeOffer.error.message=An error occurred when taking the offer.\n{0}

View File

@ -48,7 +48,7 @@ public class CreateOfferPMTest {
BSFormatter formatter = new BSFormatter(new User());
formatter.setLocale(Locale.US);
formatter.setFiatCurrencyCode("USD");
model = new CreateOfferModel(null, null, null, null, formatter);
model = new CreateOfferModel(null, null, null, null, null, formatter);
presenter = new CreateOfferPM(model, new FiatValidator(null), new BtcValidator(), formatter);
presenter.initialize();
@ -93,7 +93,7 @@ public class CreateOfferPMTest {
assertEquals(Fiat.valueOf("USD", 300 * 10000), model.priceAsFiat.get());
assertEquals(Fiat.valueOf("USD", 9999900), model.volumeAsFiat.get());
model.bankAccountType.set(BankAccountType.SEPA.toString());
assertEquals("Sepa", presenter.bankAccountType.get());