Add error display for lost connections

This commit is contained in:
Manfred Karrer 2015-04-17 13:42:29 +02:00
parent 24d29eaa83
commit 0344235a23
9 changed files with 434 additions and 323 deletions

View File

@ -25,11 +25,17 @@ import io.bitsquare.crypto.CryptoService;
import org.bitcoinj.core.AbstractWalletEventListener;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Block;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.DownloadProgressTracker;
import org.bitcoinj.core.FilteredBlock;
import org.bitcoinj.core.GetDataMessage;
import org.bitcoinj.core.InsufficientMoneyException;
import org.bitcoinj.core.Message;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.Peer;
import org.bitcoinj.core.PeerAddress;
import org.bitcoinj.core.PeerEventListener;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionConfidence;
import org.bitcoinj.core.TransactionInput;
@ -63,12 +69,17 @@ import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyIntegerProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import org.jetbrains.annotations.NotNull;
@ -107,6 +118,7 @@ public class WalletService {
private Wallet wallet;
private AddressEntry registrationAddressEntry;
private AddressEntry arbitratorDepositAddressEntry;
private final IntegerProperty numPeers = new SimpleIntegerProperty(0);
///////////////////////////////////////////////////////////////////////////////////////////
@ -184,12 +196,9 @@ public class WalletService {
e.printStackTrace();
log.error(e.toString());
}
// As an example!
// walletAppKit.useTor();
}
else if (params == TestNet3Params.get()) {
walletAppKit.setCheckpoints(getClass().getResourceAsStream("/wallet/checkpoints.testnet"));
//walletAppKit.useTor();
}
walletAppKit.setDownloadListener(downloadListener)
@ -210,7 +219,6 @@ public class WalletService {
}
}, Threading.USER_THREAD);
walletAppKit.startAsync();
return status.timeout(30, TimeUnit.SECONDS);
}
@ -220,6 +228,47 @@ public class WalletService {
addressEntryList.onWalletReady(wallet);
registrationAddressEntry = addressEntryList.getRegistrationAddressEntry();
walletAppKit.peerGroup().addEventListener(new PeerEventListener() {
@Override
public void onPeersDiscovered(Set<PeerAddress> peerAddresses) {
}
@Override
public void onBlocksDownloaded(Peer peer, Block block, FilteredBlock filteredBlock, int blocksLeft) {
}
@Override
public void onChainDownloadStarted(Peer peer, int blocksLeft) {
}
@Override
public void onPeerConnected(Peer peer, int peerCount) {
log.trace("onPeerConnected " + peerCount);
Threading.USER_THREAD.execute(() -> numPeers.set(peerCount));
}
@Override
public void onPeerDisconnected(Peer peer, int peerCount) {
log.trace("onPeerDisconnected " + peerCount);
Threading.USER_THREAD.execute(() -> numPeers.set(peerCount));
}
@Override
public Message onPreMessageReceived(Peer peer, Message m) {
return null;
}
@Override
public void onTransaction(Peer peer, Transaction t) {
}
@Nullable
@Override
public List<Message> getData(Peer peer, GetDataMessage m) {
return null;
}
});
}
public void shutDown() {
@ -534,6 +583,14 @@ public class WalletService {
}
}
public int getNumPeers() {
return numPeers.get();
}
public ReadOnlyIntegerProperty numPeersProperty() {
return numPeers;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Inner classes
///////////////////////////////////////////////////////////////////////////////////////////

View File

@ -29,6 +29,10 @@ public class BaseP2PService implements P2PService {
BaseP2PService.userThread = userThread;
}
public static Executor getUserThread() {
return userThread;
}
protected Executor executor;
protected PeerDHT peerDHT;

View File

@ -21,6 +21,8 @@ import io.bitsquare.p2p.tomp2p.BootstrappedPeerBuilder;
import java.security.KeyPair;
import javafx.beans.property.ReadOnlyIntegerProperty;
import rx.Observable;
public interface ClientNode {
@ -31,4 +33,6 @@ public interface ClientNode {
Node getBootstrapNodeAddress();
Observable<BootstrappedPeerBuilder.State> bootstrap(KeyPair keyPair);
ReadOnlyIntegerProperty numPeersProperty();
}

View File

@ -19,6 +19,7 @@ package io.bitsquare.p2p.tomp2p;
import io.bitsquare.BitsquareException;
import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.p2p.BaseP2PService;
import io.bitsquare.p2p.ClientNode;
import io.bitsquare.p2p.Node;
@ -35,8 +36,16 @@ import javax.annotation.Nullable;
import javax.inject.Inject;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ReadOnlyIntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import net.tomp2p.connection.PeerConnection;
import net.tomp2p.connection.PeerException;
import net.tomp2p.dht.PeerDHT;
import net.tomp2p.peers.PeerAddress;
import net.tomp2p.peers.PeerStatusListener;
import net.tomp2p.peers.RTT;
import org.jetbrains.annotations.NotNull;
@ -54,7 +63,8 @@ public class TomP2PNode implements ClientNode {
private BootstrappedPeerBuilder bootstrappedPeerBuilder;
private final Subject<BootstrappedPeerBuilder.State, BootstrappedPeerBuilder.State> bootstrapStateSubject;
private final List<ResultHandler> resultHandlers = new CopyOnWriteArrayList<>();
private final IntegerProperty numPeers = new SimpleIntegerProperty(0);
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
@ -92,6 +102,20 @@ public class TomP2PNode implements ClientNode {
public void onSuccess(@Nullable PeerDHT peerDHT) {
if (peerDHT != null) {
TomP2PNode.this.peerDHT = peerDHT;
peerDHT.peerBean().addPeerStatusListener(new PeerStatusListener() {
@Override
public boolean peerFailed(PeerAddress peerAddress, PeerException e) {
return false;
}
@Override
public boolean peerFound(PeerAddress peerAddress, PeerAddress peerAddress1, PeerConnection peerConnection, RTT rtt) {
BaseP2PService.getUserThread().execute(() -> numPeers.set(peerDHT.peerBean().peerMap().size()));
return false;
}
});
resultHandlers.stream().forEach(ResultHandler::handleResult);
bootstrapStateSubject.onCompleted();
}
@ -141,4 +165,12 @@ public class TomP2PNode implements ClientNode {
public void removeResultHandler(ResultHandler resultHandler) {
resultHandlers.remove(resultHandler);
}
public int getNumPeers() {
return numPeers.get();
}
public ReadOnlyIntegerProperty numPeersProperty() {
return numPeers;
}
}

View File

@ -22,6 +22,7 @@ import io.bitsquare.gui.common.view.CachingViewLoader;
import io.bitsquare.gui.common.view.View;
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.debug.DebugView;
import io.bitsquare.gui.util.ImageUtil;
@ -46,8 +47,6 @@ import javafx.stage.StageStyle;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Logger;
import com.vinumeris.crashfx.CrashFX;
import com.vinumeris.crashfx.CrashWindow;
import org.springframework.core.env.Environment;
import static io.bitsquare.app.BitsquareEnvironment.APP_NAME_KEY;
@ -80,7 +79,6 @@ public class BitsquareApp extends Application {
Paths.get(env.getProperty(BitsquareEnvironment.APP_DATA_DIR_KEY), "crashes"),
URI.create("http://188.226.179.109/crashfx/upload"));*/
// Server not setup yet, so we use client side only support
CrashFX.setup();
// Guice
bitsquareAppModule = new BitsquareAppModule(env, primaryStage);
@ -89,9 +87,10 @@ public class BitsquareApp extends Application {
// load the main view and create the main scene
CachingViewLoader viewLoader = injector.getInstance(CachingViewLoader.class);
View view = viewLoader.load(MainView.class);
MainView view = (MainView) viewLoader.load(MainView.class);
view.setExitHandler(this::stop);
scene = new Scene((Parent) view.getRoot(), 1000, 650);
scene = new Scene(view.getRoot(), 1000, 650);
scene.getStylesheets().setAll(
"/io/bitsquare/gui/bitsquare.css",
"/io/bitsquare/gui/images.css");
@ -137,7 +136,7 @@ public class BitsquareApp extends Application {
//TODO just temp.
//showDebugWindow();
} catch (Throwable t) {
CrashWindow.open(t);
Popups.openExceptionPopup(t);
}
}

View File

@ -105,9 +105,9 @@ public class UpdateProcess {
}
};
updater.progressProperty().addListener((observableValue, oldValue, newValue) -> {
/* updater.progressProperty().addListener((observableValue, oldValue, newValue) -> {
log.trace("progressProperty newValue = " + newValue);
});
});*/
log.info("Checking for updates!");
updater.setOnSucceeded(event -> {

View File

@ -42,6 +42,7 @@ 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.*;
@ -66,6 +67,19 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
private final OverlayManager overlayManager;
private final Transitions transitions;
private final String title;
private ChangeListener<String> walletServiceErrorMsgListener;
private ChangeListener<String> blockchainSyncIconIdListener;
private ChangeListener<String> bootstrapErrorMsgListener;
private ChangeListener<String> bootstrapIconIdListener;
private ChangeListener<Number> bootstrapProgressListener;
private ChangeListener<String> updateIconIdListener;
private Button restartButton;
private ProgressIndicator bootstrapIndicator;
private Label bootstrapStateLabel;
private ProgressBar blockchainSyncIndicator;
private Label blockchainSyncLabel;
private Label updateInfoLabel;
private Runnable exitHandler;
@Inject
public MainView(MainViewModel model, CachingViewLoader viewLoader, Navigation navigation, OverlayManager overlayManager, Transitions transitions,
@ -156,7 +170,7 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
navigation.navigateToPreviousVisitedView();
transitions.fadeOutAndRemove(splashScreen, 1500);
transitions.fadeOutAndRemove(splashScreen, 1500, actionEvent -> disposeSplashScreen());
}
});
@ -164,6 +178,253 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
Platform.runLater(model::initBackend);
}
public void setExitHandler(Runnable exitHandler) {
this.exitHandler = exitHandler;
}
private VBox createSplashScreen() {
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
vBox.setSpacing(0);
vBox.setId("splash");
ImageView logo = new ImageView();
logo.setId("image-splash-logo");
// createBitcoinInfoBox
blockchainSyncLabel = new Label();
blockchainSyncLabel.textProperty().bind(model.blockchainSyncInfo);
walletServiceErrorMsgListener = (ov, oldValue, newValue) -> {
blockchainSyncLabel.setId("splash-error-state-msg");
Popups.openErrorPopup("Error", "Connecting to the bitcoin network failed. \n" + newValue
+ "\nPlease check our internet connection and restart the application.");
exitHandler.run();
};
model.walletServiceErrorMsg.addListener(walletServiceErrorMsgListener);
blockchainSyncIndicator = new ProgressBar(-1);
blockchainSyncIndicator.setPrefWidth(120);
blockchainSyncIndicator.progressProperty().bind(model.blockchainSyncProgress);
ImageView blockchainSyncIcon = new ImageView();
blockchainSyncIcon.setVisible(false);
blockchainSyncIcon.setManaged(false);
blockchainSyncIconIdListener = (ov, oldValue, newValue) -> {
blockchainSyncIcon.setId(newValue);
blockchainSyncIcon.setVisible(true);
blockchainSyncIcon.setManaged(true);
blockchainSyncIndicator.setVisible(false);
blockchainSyncIndicator.setManaged(false);
};
model.blockchainSyncIconId.addListener(blockchainSyncIconIdListener);
Label bitcoinNetworkLabel = new Label();
bitcoinNetworkLabel.setText(model.bitcoinNetworkAsString);
bitcoinNetworkLabel.setId("splash-bitcoin-network-label");
HBox blockchainSyncBox = new HBox();
blockchainSyncBox.setSpacing(10);
blockchainSyncBox.setAlignment(Pos.CENTER);
blockchainSyncBox.setPadding(new Insets(40, 0, 0, 0));
blockchainSyncBox.setPrefHeight(50);
blockchainSyncBox.getChildren().addAll(blockchainSyncLabel, blockchainSyncIndicator,
blockchainSyncIcon, bitcoinNetworkLabel);
// createP2PNetworkBox
bootstrapStateLabel = new Label();
bootstrapStateLabel.setWrapText(true);
bootstrapStateLabel.setMaxWidth(500);
bootstrapStateLabel.setTextAlignment(TextAlignment.CENTER);
bootstrapStateLabel.textProperty().bind(model.bootstrapInfo);
bootstrapIndicator = new ProgressIndicator();
bootstrapIndicator.setMaxSize(24, 24);
bootstrapIndicator.progressProperty().bind(model.bootstrapProgress);
bootstrapErrorMsgListener = (ov, oldValue, newValue) -> {
bootstrapStateLabel.setId("splash-error-state-msg");
bootstrapIndicator.setVisible(false);
Popups.openErrorPopup("Error", "Connecting to the Bitsquare network failed. \n" + model.bootstrapErrorMsg.get()
+ "\nPlease check our internet connection and restart the application.");
exitHandler.run();
};
model.bootstrapErrorMsg.addListener(bootstrapErrorMsgListener);
ImageView bootstrapIcon = new ImageView();
bootstrapIcon.setVisible(false);
bootstrapIcon.setManaged(false);
bootstrapIconIdListener = (ov, oldValue, newValue) -> {
bootstrapIcon.setId(newValue);
bootstrapIcon.setVisible(true);
bootstrapIcon.setManaged(true);
};
model.bootstrapIconId.addListener(bootstrapIconIdListener);
bootstrapProgressListener = (ov, oldValue, newValue) -> {
if ((double) newValue >= 1) {
bootstrapIndicator.setVisible(false);
bootstrapIndicator.setManaged(false);
}
};
model.bootstrapProgress.addListener(bootstrapProgressListener);
HBox bootstrapBox = new HBox();
bootstrapBox.setSpacing(10);
bootstrapBox.setAlignment(Pos.CENTER);
bootstrapBox.setPrefHeight(50);
bootstrapBox.getChildren().addAll(bootstrapStateLabel, bootstrapIndicator, bootstrapIcon);
// createUpdateBox
updateInfoLabel = new Label();
updateInfoLabel.setTextAlignment(TextAlignment.RIGHT);
updateInfoLabel.textProperty().bind(model.updateInfo);
restartButton = new Button("Restart");
restartButton.setDefaultButton(true);
restartButton.visibleProperty().bind(model.showRestartButton);
restartButton.managedProperty().bind(model.showRestartButton);
restartButton.setOnAction(e -> model.restart());
ImageView updateIcon = new ImageView();
updateIcon.setId(model.updateIconId.get());
updateIconIdListener = (ov, oldValue, newValue) -> {
updateIcon.setId(newValue);
updateIcon.setVisible(true);
updateIcon.setManaged(true);
};
model.updateIconId.addListener(updateIconIdListener);
HBox updateBox = new HBox();
updateBox.setSpacing(10);
updateBox.setAlignment(Pos.CENTER);
updateBox.setPrefHeight(20);
updateBox.getChildren().addAll(updateInfoLabel, restartButton, updateIcon);
vBox.getChildren().addAll(logo, blockchainSyncBox, bootstrapBox, updateBox);
return vBox;
}
private void disposeSplashScreen() {
model.walletServiceErrorMsg.removeListener(walletServiceErrorMsgListener);
model.blockchainSyncIconId.removeListener(blockchainSyncIconIdListener);
model.bootstrapErrorMsg.removeListener(bootstrapErrorMsgListener);
model.bootstrapIconId.removeListener(bootstrapIconIdListener);
model.bootstrapProgress.removeListener(bootstrapProgressListener);
model.updateIconId.removeListener(updateIconIdListener);
blockchainSyncLabel.textProperty().unbind();
blockchainSyncIndicator.progressProperty().unbind();
bootstrapStateLabel.textProperty().unbind();
bootstrapIndicator.progressProperty().unbind();
updateInfoLabel.textProperty().unbind();
restartButton.visibleProperty().unbind();
restartButton.managedProperty().unbind();
}
private AnchorPane createFooter() {
// line
Separator separator = new Separator();
separator.setId("footer-pane-line");
separator.setPrefHeight(1);
setLeftAnchor(separator, 0d);
setRightAnchor(separator, 0d);
setTopAnchor(separator, 0d);
// BTC
Label blockchainSyncLabel = new Label();
blockchainSyncLabel.setId("footer-pane");
blockchainSyncLabel.textProperty().bind(model.blockchainSyncInfoFooter);
ProgressBar blockchainSyncIndicator = new ProgressBar(-1);
blockchainSyncIndicator.setPrefWidth(120);
blockchainSyncIndicator.setMaxHeight(10);
blockchainSyncIndicator.progressProperty().bind(model.blockchainSyncProgress);
Label bitcoinNetworkLabel = new Label();
bitcoinNetworkLabel.setId("footer-bitcoin-network-label");
bitcoinNetworkLabel.setText(model.bitcoinNetworkAsString);
model.walletServiceErrorMsg.addListener((ov, oldValue, newValue) -> {
bitcoinNetworkLabel.setId("splash-error-state-msg");
bitcoinNetworkLabel.textProperty().unbind();
bitcoinNetworkLabel.setText("Not connected");
Popups.openErrorPopup("Error", "Connecting to the bitcoin network failed. \n" + newValue
+ "\nPlease check our internet connection and restart the application.");
exitHandler.run();
});
model.blockchainSyncProgress.addListener((ov, oldValue, newValue) -> {
if ((double) newValue >= 1) {
blockchainSyncIndicator.setVisible(false);
blockchainSyncIndicator.setManaged(false);
blockchainSyncLabel.setVisible(false);
blockchainSyncLabel.setManaged(false);
}
});
HBox blockchainSyncBox = new HBox();
blockchainSyncBox.setSpacing(10);
blockchainSyncBox.setAlignment(Pos.CENTER);
blockchainSyncBox.getChildren().addAll(blockchainSyncLabel, blockchainSyncIndicator, bitcoinNetworkLabel);
setLeftAnchor(blockchainSyncBox, 10d);
setBottomAnchor(blockchainSyncBox, 7d);
// version
Label versionLabel = new Label();
versionLabel.setId("footer-pane");
versionLabel.setTextAlignment(TextAlignment.CENTER);
versionLabel.setAlignment(Pos.BASELINE_CENTER);
versionLabel.setText(model.version);
root.widthProperty().addListener((ov, oldValue, newValue) -> {
versionLabel.setLayoutX(((double) newValue - versionLabel.getWidth()) / 2);
});
setBottomAnchor(versionLabel, 7d);
// P2P
Label bootstrapLabel = new Label();
bootstrapLabel.setId("footer-pane");
setRightAnchor(bootstrapLabel, 100d);
setBottomAnchor(bootstrapLabel, 7d);
bootstrapLabel.textProperty().bind(model.bootstrapInfoFooter);
ImageView bootstrapIcon = new ImageView();
setRightAnchor(bootstrapIcon, 60d);
setBottomAnchor(bootstrapIcon, 9d);
bootstrapIcon.idProperty().bind(model.bootstrapIconId);
Label numPeersLabel = new Label();
numPeersLabel.setId("footer-num-peers");
setRightAnchor(numPeersLabel, 10d);
setBottomAnchor(numPeersLabel, 7d);
numPeersLabel.textProperty().bind(model.numDHTPeers);
model.bootstrapErrorMsg.addListener((ov, oldValue, newValue) -> {
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 our internet connection and restart the application.");
exitHandler.run();
});
AnchorPane footerContainer = new AnchorPane(separator, blockchainSyncBox, versionLabel, bootstrapLabel, bootstrapIcon, numPeersLabel) {{
setId("footer-pane");
setMinHeight(30);
setMaxHeight(30);
}};
return footerContainer;
}
private void setupNotificationIcon(Pane portfolioButtonHolder) {
Label numPendingTradesLabel = new Label();
numPendingTradesLabel.textProperty().bind(model.numPendingTradesAsString);
@ -188,308 +449,6 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
});
}
private AnchorPane createFooter() {
// BTC
Label blockchainSyncLabel = new Label();
blockchainSyncLabel.setId("footer-pane");
blockchainSyncLabel.textProperty().bind(model.blockchainSyncInfoFooter);
model.walletServiceErrorMsg.addListener((ov, oldValue, newValue) -> {
blockchainSyncLabel.setId("splash-error-state-msg");
Popups.openErrorPopup("Error", "Connecting to the bitcoin network failed. \n\nReason: " +
newValue);
});
ProgressBar blockchainSyncIndicator = new ProgressBar(-1);
blockchainSyncIndicator.setPrefWidth(120);
blockchainSyncIndicator.setMaxHeight(10);
blockchainSyncIndicator.progressProperty().bind(model.blockchainSyncProgress);
Label bitcoinNetworkLabel = new Label();
bitcoinNetworkLabel.setId("footer-bitcoin-network-label");
bitcoinNetworkLabel.setText(model.bitcoinNetworkAsString);
model.blockchainSyncProgress.addListener((ov, oldValue, newValue) -> {
if ((double) newValue >= 1) {
blockchainSyncIndicator.setVisible(false);
blockchainSyncIndicator.setManaged(false);
blockchainSyncLabel.setVisible(false);
blockchainSyncLabel.setManaged(false);
}
});
HBox blockchainSyncBox = new HBox();
blockchainSyncBox.setSpacing(10);
blockchainSyncBox.setAlignment(Pos.CENTER);
blockchainSyncBox.getChildren().addAll(blockchainSyncLabel, blockchainSyncIndicator, bitcoinNetworkLabel);
setLeftAnchor(blockchainSyncBox, 20d);
setBottomAnchor(blockchainSyncBox, 7d);
// version
Label versionLabel = new Label();
versionLabel.setId("footer-pane");
versionLabel.setTextAlignment(TextAlignment.CENTER);
versionLabel.setAlignment(Pos.BASELINE_CENTER);
versionLabel.setText(model.version);
root.widthProperty().addListener((ov, oldValue, newValue) -> {
versionLabel.setLayoutX(((double) newValue - versionLabel.getWidth()) / 2);
});
setBottomAnchor(versionLabel, 7d);
// P2P
Label bootstrapLabel = new Label();
bootstrapLabel.setId("footer-pane");
setRightAnchor(bootstrapLabel, 60d);
setBottomAnchor(bootstrapLabel, 7d);
bootstrapLabel.textProperty().bind(model.bootstrapInfoFooter);
ImageView bootstrapIcon = new ImageView();
setRightAnchor(bootstrapIcon, 20d);
setBottomAnchor(bootstrapIcon, 9d);
bootstrapIcon.idProperty().bind(model.bootstrapIconId);
// line
Separator separator = new Separator();
separator.setId("footer-pane-line");
separator.setPrefHeight(1);
setLeftAnchor(separator, 0d);
setRightAnchor(separator, 0d);
setTopAnchor(separator, 0d);
AnchorPane footerContainer = new AnchorPane(separator, blockchainSyncBox, versionLabel, bootstrapLabel, bootstrapIcon) {{
setId("footer-pane");
setMinHeight(30);
setMaxHeight(30);
}};
return footerContainer;
}
private HBox createBitcoinInfoBox() {
Label blockchainSyncLabel = new Label();
blockchainSyncLabel.textProperty().bind(model.blockchainSyncInfo);
model.walletServiceErrorMsg.addListener((ov, oldValue, newValue) -> {
blockchainSyncLabel.setId("splash-error-state-msg");
Popups.openErrorPopup("Error", "Connecting to the bitcoin network failed. \n\nReason: " +
newValue);
});
ProgressBar blockchainSyncIndicator = new ProgressBar(-1);
blockchainSyncIndicator.setPrefWidth(120);
blockchainSyncIndicator.progressProperty().bind(model.blockchainSyncProgress);
ImageView blockchainSyncIcon = new ImageView();
blockchainSyncIcon.setVisible(false);
blockchainSyncIcon.setManaged(false);
model.blockchainSyncIconId.addListener((ov, oldValue, newValue) -> {
blockchainSyncIcon.setId(newValue);
blockchainSyncIcon.setVisible(true);
blockchainSyncIcon.setManaged(true);
blockchainSyncIndicator.setVisible(false);
blockchainSyncIndicator.setManaged(false);
});
Label bitcoinNetworkLabel = new Label();
bitcoinNetworkLabel.setText(model.bitcoinNetworkAsString);
bitcoinNetworkLabel.setId("splash-bitcoin-network-label");
HBox blockchainSyncBox = new HBox();
blockchainSyncBox.setSpacing(10);
blockchainSyncBox.setAlignment(Pos.CENTER);
blockchainSyncBox.setPadding(new Insets(40, 0, 0, 0));
blockchainSyncBox.setPrefHeight(50);
blockchainSyncBox.getChildren().addAll(blockchainSyncLabel, blockchainSyncIndicator,
blockchainSyncIcon, bitcoinNetworkLabel);
return blockchainSyncBox;
}
private HBox createP2PNetworkBox() {
Label bootstrapStateLabel = new Label();
bootstrapStateLabel.setWrapText(true);
bootstrapStateLabel.setMaxWidth(500);
bootstrapStateLabel.setTextAlignment(TextAlignment.CENTER);
bootstrapStateLabel.textProperty().bind(model.bootstrapInfo);
ProgressIndicator bootstrapIndicator = new ProgressIndicator();
bootstrapIndicator.setMaxSize(24, 24);
bootstrapIndicator.progressProperty().bind(model.bootstrapProgress);
model.bootstrapErrorMsg.addListener((ov, oldValue, newValue) -> {
bootstrapStateLabel.setId("splash-error-state-msg");
bootstrapIndicator.setVisible(false);
Popups.openErrorPopup("Error", "Connecting to the Bitsquare network failed. \n\nReason: " +
model.bootstrapErrorMsg.get());
});
ImageView bootstrapIcon = new ImageView();
bootstrapIcon.setVisible(false);
bootstrapIcon.setManaged(false);
model.bootstrapIconId.addListener((ov, oldValue, newValue) -> {
bootstrapIcon.setId(newValue);
bootstrapIcon.setVisible(true);
bootstrapIcon.setManaged(true);
});
model.bootstrapProgress.addListener((ov, oldValue, newValue) -> {
if ((double) newValue >= 1) {
bootstrapIndicator.setVisible(false);
bootstrapIndicator.setManaged(false);
}
});
HBox bootstrapBox = new HBox();
bootstrapBox.setSpacing(10);
bootstrapBox.setAlignment(Pos.CENTER);
bootstrapBox.setPrefHeight(50);
bootstrapBox.getChildren().addAll(bootstrapStateLabel, bootstrapIndicator, bootstrapIcon);
return bootstrapBox;
}
private HBox createUpdateBox() {
Label updateInfoLabel = new Label();
updateInfoLabel.setTextAlignment(TextAlignment.RIGHT);
updateInfoLabel.textProperty().bind(model.updateInfo);
Button restartButton = new Button("Restart");
restartButton.setDefaultButton(true);
restartButton.visibleProperty().bind(model.showRestartButton);
restartButton.managedProperty().bind(model.showRestartButton);
restartButton.setOnAction(e -> model.restart());
ImageView updateIcon = new ImageView();
updateIcon.setId(model.updateIconId.get());
model.updateIconId.addListener((ov, oldValue, newValue) -> {
updateIcon.setId(newValue);
updateIcon.setVisible(true);
updateIcon.setManaged(true);
});
HBox updateBox = new HBox();
updateBox.setSpacing(10);
updateBox.setAlignment(Pos.CENTER);
updateBox.setPrefHeight(20);
updateBox.getChildren().addAll(updateInfoLabel, restartButton, updateIcon);
return updateBox;
}
private VBox createSplashScreen() {
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
vBox.setSpacing(0);
vBox.setId("splash");
ImageView logo = new ImageView();
logo.setId("image-splash-logo");
/*Label blockchainSyncLabel = new Label();
blockchainSyncLabel.textProperty().bind(model.blockchainSyncInfo);
model.walletServiceErrorMsg.addListener((ov, oldValue, newValue) -> {
blockchainSyncLabel.setId("splash-error-state-msg");
Popups.openErrorPopup("Error", "Connecting to the bitcoin network failed. \n\nReason: " +
newValue);
});
ProgressBar blockchainSyncIndicator = new ProgressBar(-1);
blockchainSyncIndicator.setPrefWidth(120);
blockchainSyncIndicator.progressProperty().bind(model.blockchainSyncProgress);
ImageView blockchainSyncIcon = new ImageView();
blockchainSyncIcon.setVisible(false);
blockchainSyncIcon.setManaged(false);
model.blockchainSyncIconId.addListener((ov, oldValue, newValue) -> {
blockchainSyncIcon.setId(newValue);
blockchainSyncIcon.setVisible(true);
blockchainSyncIcon.setManaged(true);
blockchainSyncIndicator.setVisible(false);
blockchainSyncIndicator.setManaged(false);
});
Label bitcoinNetworkLabel = new Label();
bitcoinNetworkLabel.setText(model.bitcoinNetworkAsString);
bitcoinNetworkLabel.setId("splash-bitcoin-network-label");
HBox blockchainSyncBox = new HBox();
blockchainSyncBox.setSpacing(10);
blockchainSyncBox.setAlignment(Pos.CENTER);
blockchainSyncBox.setPadding(new Insets(40, 0, 0, 0));
blockchainSyncBox.setPrefHeight(50);
blockchainSyncBox.getChildren().addAll(blockchainSyncLabel, blockchainSyncIndicator,
blockchainSyncIcon, bitcoinNetworkLabel);*/
/* Label bootstrapStateLabel = new Label();
bootstrapStateLabel.setWrapText(true);
bootstrapStateLabel.setMaxWidth(500);
bootstrapStateLabel.setTextAlignment(TextAlignment.CENTER);
bootstrapStateLabel.textProperty().bind(model.bootstrapInfo);
ProgressIndicator bootstrapIndicator = new ProgressIndicator();
bootstrapIndicator.setMaxSize(24, 24);
bootstrapIndicator.progressProperty().bind(model.bootstrapProgress);
model.bootstrapErrorMsg.addListener((ov, oldValue, newValue) -> {
bootstrapStateLabel.setId("splash-error-state-msg");
bootstrapIndicator.setVisible(false);
Popups.openErrorPopup("Error", "Connecting to the Bitsquare network failed. \n\nReason: " +
model.bootstrapErrorMsg.get());
});
ImageView bootstrapIcon = new ImageView();
bootstrapIcon.setVisible(false);
bootstrapIcon.setManaged(false);
model.bootstrapIconId.addListener((ov, oldValue, newValue) -> {
bootstrapIcon.setId(newValue);
bootstrapIcon.setVisible(true);
bootstrapIcon.setManaged(true);
});
model.bootstrapProgress.addListener((ov, oldValue, newValue) -> {
if ((double) newValue >= 1) {
bootstrapIndicator.setVisible(false);
bootstrapIndicator.setManaged(false);
}
});
HBox bootstrapBox = new HBox();
bootstrapBox.setSpacing(10);
bootstrapBox.setAlignment(Pos.CENTER);
bootstrapBox.setPrefHeight(50);
bootstrapBox.getChildren().addAll(bootstrapStateLabel, bootstrapIndicator, bootstrapIcon);*/
// software update
/* Label updateInfoLabel = new Label();
updateInfoLabel.setTextAlignment(TextAlignment.RIGHT);
updateInfoLabel.textProperty().bind(model.updateInfo);
Button restartButton = new Button("Restart");
restartButton.setDefaultButton(true);
restartButton.visibleProperty().bind(model.showRestartButton);
restartButton.managedProperty().bind(model.showRestartButton);
restartButton.setOnAction(e -> model.restart());
ImageView updateIcon = new ImageView();
updateIcon.setId(model.updateIconId.get());
model.updateIconId.addListener((ov, oldValue, newValue) -> {
updateIcon.setId(newValue);
updateIcon.setVisible(true);
updateIcon.setManaged(true);
});
HBox updateBox = new HBox();
updateBox.setSpacing(10);
updateBox.setAlignment(Pos.CENTER);
updateBox.setPrefHeight(20);
updateBox.getChildren().addAll(updateInfoLabel, restartButton, updateIcon);*/
vBox.getChildren().addAll(logo, createBitcoinInfoBox(), createP2PNetworkBox(), createUpdateBox());
return vBox;
}
private VBox createBankAccountComboBox() {
final ComboBox<FiatAccount> comboBox = new ComboBox<>(model.getBankAccounts());
comboBox.setLayoutY(12);
@ -523,7 +482,6 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
return vBox;
}
private void configureBlurring(Node node) {
Popups.setOverlayManager(overlayManager);
@ -540,7 +498,6 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
});
}
private class NavButton extends ToggleButton {
private final Class<? extends View> viewClass;

View File

@ -35,8 +35,12 @@ import io.bitsquare.trade.TradeManager;
import io.bitsquare.trade.offer.OpenOfferManager;
import io.bitsquare.user.User;
import org.bitcoinj.utils.Threading;
import com.google.inject.Inject;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeoutException;
import javafx.application.Platform;
@ -60,12 +64,15 @@ import rx.Observable;
class MainViewModel implements ViewModel {
private static final Logger log = LoggerFactory.getLogger(MainViewModel.class);
private static final long BLOCKCHAIN_SYNC_TIMEOUT = 30000;
// BTC network
final StringProperty blockchainSyncInfo = new SimpleStringProperty("Initializing");
final StringProperty blockchainSyncInfoFooter = new SimpleStringProperty("Initializing");
final DoubleProperty blockchainSyncProgress = new SimpleDoubleProperty(-1);
final StringProperty walletServiceErrorMsg = new SimpleStringProperty();
final StringProperty blockchainSyncIconId = new SimpleStringProperty();
final StringProperty numBTCPeers = new SimpleStringProperty();
// P2P network
final StringProperty bootstrapInfo = new SimpleStringProperty("Connecting to P2P network...");
@ -73,6 +80,7 @@ class MainViewModel implements ViewModel {
final DoubleProperty bootstrapProgress = new SimpleDoubleProperty(-1);
final StringProperty bootstrapErrorMsg = new SimpleStringProperty();
final StringProperty bootstrapIconId = new SimpleStringProperty();
final StringProperty numDHTPeers = new SimpleStringProperty();
// software update
final StringProperty updateInfo = new SimpleStringProperty();
@ -100,6 +108,8 @@ class MainViewModel implements ViewModel {
private OpenOfferManager openOfferManager;
private final UpdateProcess updateProcess;
private final BSFormatter formatter;
private Timer blockchainSyncTimeoutTimer;
@Inject
public MainViewModel(User user, KeyRing keyRing, WalletService walletService, ArbitrationRepository arbitrationRepository, ClientNode clientNode,
@ -136,20 +146,35 @@ class MainViewModel implements ViewModel {
public void initBackend() {
Platform.runLater(updateProcess::init);
startBlockchainSyncTimeout();
walletService.downloadPercentageProperty().addListener((ov, oldValue, newValue) -> {
setBitcoinNetworkSyncProgress((double) newValue);
});
setBitcoinNetworkSyncProgress(walletService.downloadPercentageProperty().get());
walletService.numPeersProperty().addListener((observable, oldValue, newValue) -> {
numBTCPeers.set(String.valueOf(newValue) + " peers");
if ((int) newValue < 1) {
walletServiceErrorMsg.set("We lost connection to the last peer.");
}
});
// Set executor for all P2PServices
BaseP2PService.setUserThread(Platform::runLater);
clientNode.numPeersProperty().addListener((observable, oldValue, newValue) -> {
numDHTPeers.set(String.valueOf(newValue) + " peers");
if ((int) newValue < 1) {
bootstrapErrorMsg.set("We lost connection to the last peer.");
}
});
Observable<BootstrappedPeerBuilder.State> bootstrapStateAsObservable = clientNode.bootstrap(keyRing.getDhtSignatureKeyPair());
bootstrapStateAsObservable.publish();
bootstrapStateAsObservable.subscribe(
state -> Platform.runLater(() -> setBootstrapState(state)),
error -> Platform.runLater(() -> {
log.error(error.toString());
bootstrapErrorMsg.set(error.getMessage());
bootstrapInfo.set("Connecting to the P2P network failed.");
bootstrapProgress.set(0);
@ -163,7 +188,6 @@ class MainViewModel implements ViewModel {
//log.trace("wallet next");
},
error -> Platform.runLater(() -> {
log.trace("wallet error");
setWalletServiceException(error);
}),
() -> {
@ -175,7 +199,6 @@ class MainViewModel implements ViewModel {
//log.trace("updateProcess next");
},
error -> {
log.trace("updateProcess error");
},
() -> {
log.trace("updateProcess completed");
@ -185,7 +208,8 @@ class MainViewModel implements ViewModel {
allServices.subscribe(
next -> {
},
error -> log.error(error.toString()),
error -> {
},
() -> Platform.runLater(this::onAllServicesInitialized)
);
}
@ -234,7 +258,6 @@ class MainViewModel implements ViewModel {
updateIconId.set("image-update-up-to-date");
break;
case FAILURE:
log.error(updateProcess.getErrorMessage());
updateInfo.set("Check for updates failed. ");
updateIconId.set("image-update-failed");
break;
@ -330,6 +353,7 @@ class MainViewModel implements ViewModel {
private void setBitcoinNetworkSyncProgress(double value) {
blockchainSyncProgress.set(value);
if (value >= 1) {
stopBlockchainSyncTimeout();
blockchainSyncInfo.set("Blockchain synchronization complete.");
blockchainSyncIconId.set("image-connection-synced");
}
@ -343,4 +367,30 @@ class MainViewModel implements ViewModel {
}
}
private void startBlockchainSyncTimeout() {
log.trace("startBlockchainSyncTimeout");
stopBlockchainSyncTimeout();
blockchainSyncTimeoutTimer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
Threading.USER_THREAD.execute(() -> {
log.trace("Timeout reached");
Platform.runLater(() -> setWalletServiceException(new TimeoutException()));
});
}
};
blockchainSyncTimeoutTimer.schedule(task, BLOCKCHAIN_SYNC_TIMEOUT);
}
private void stopBlockchainSyncTimeout() {
log.trace("stopBlockchainSyncTimeout");
if (blockchainSyncTimeoutTimer != null) {
blockchainSyncTimeoutTimer.cancel();
blockchainSyncTimeoutTimer = null;
}
}
}

View File

@ -27,6 +27,8 @@ import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.*;
import javafx.scene.effect.*;
import javafx.scene.layout.*;
@ -82,6 +84,10 @@ public class Transitions {
}
public void fadeOutAndRemove(Node node, int duration) {
fadeOutAndRemove(node, duration, null);
}
public void fadeOutAndRemove(Node node, int duration, EventHandler<ActionEvent> handler) {
if (!preferences.getUseEffects())
duration = 1;
@ -90,6 +96,8 @@ public class Transitions {
fade.setOnFinished(actionEvent -> {
((Pane) (node.getParent())).getChildren().remove(node);
Profiler.printMsgWithTime("fadeOutAndRemove");
if (handler != null)
handler.handle(actionEvent);
});
}