Add connection icons, handleError cases in one method
|
@ -139,10 +139,9 @@ public class Main extends Application {
|
|||
// For now we exit when closing/quit the app.
|
||||
// Later we will only hide the window (systemTray.hideStage()) and use the exit item in the system tray for
|
||||
// shut down.
|
||||
if (new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN).match(keyEvent))
|
||||
systemTray.exit();
|
||||
if (new KeyCodeCombination(KeyCode.Q, KeyCombination.SHORTCUT_DOWN).match(keyEvent))
|
||||
systemTray.exit();
|
||||
if (new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN).match(keyEvent) ||
|
||||
new KeyCodeCombination(KeyCode.Q, KeyCombination.SHORTCUT_DOWN).match(keyEvent))
|
||||
stop();
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -94,12 +94,7 @@ public class SystemTray {
|
|||
}
|
||||
});
|
||||
|
||||
exitItem.addActionListener(e -> exit());
|
||||
}
|
||||
|
||||
public void exit() {
|
||||
java.awt.SystemTray.getSystemTray().remove(trayIcon);
|
||||
onExit.run();
|
||||
exitItem.addActionListener(e -> onExit.run());
|
||||
}
|
||||
|
||||
public void hideStage() {
|
||||
|
|
|
@ -122,3 +122,20 @@
|
|||
#image-arrow-grey {
|
||||
-fx-image: url("../../../images/arrow_grey.png");
|
||||
}
|
||||
|
||||
/* connection state*/
|
||||
#image-connection-direct {
|
||||
-fx-image: url("../../../images/connection/direct.png");
|
||||
}
|
||||
|
||||
#image-connection-nat {
|
||||
-fx-image: url("../../../images/connection/nat.png");
|
||||
}
|
||||
|
||||
#image-connection-relay {
|
||||
-fx-image: url("../../../images/connection/relay.png");
|
||||
}
|
||||
|
||||
#image-connection-synced {
|
||||
-fx-image: url("../../../images/connection/synced.png");
|
||||
}
|
|
@ -48,16 +48,19 @@ class MainPM extends PresentationModel<MainModel> {
|
|||
final BooleanProperty backendReady = new SimpleBooleanProperty();
|
||||
final StringProperty bankAccountsComboBoxPrompt = new SimpleStringProperty();
|
||||
final BooleanProperty bankAccountsComboBoxDisable = new SimpleBooleanProperty();
|
||||
final StringProperty blockchainSyncState = new SimpleStringProperty("Initializing");
|
||||
final IntegerProperty numPendingTrades = new SimpleIntegerProperty();
|
||||
|
||||
final StringProperty blockchainSyncState = new SimpleStringProperty("Initializing");
|
||||
final DoubleProperty blockchainSyncProgress = new SimpleDoubleProperty();
|
||||
final BooleanProperty blockchainSyncIndicatorVisible = new SimpleBooleanProperty(true);
|
||||
final StringProperty blockchainSyncIconId = new SimpleStringProperty();
|
||||
final StringProperty walletFacadeErrorMsg = new SimpleStringProperty();
|
||||
|
||||
final DoubleProperty bootstrapProgress = new SimpleDoubleProperty(-1);
|
||||
final BooleanProperty bootstrapFailed = new SimpleBooleanProperty();
|
||||
final BooleanProperty bootstrapIndicatorVisible = new SimpleBooleanProperty(true);
|
||||
final StringProperty bootstrapState = new SimpleStringProperty();
|
||||
final StringProperty bootstrapErrorMsg = new SimpleStringProperty();
|
||||
final StringProperty walletFacadeErrorMsg = new SimpleStringProperty();
|
||||
final StringProperty bootstrapIconId = new SimpleStringProperty();
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -88,8 +91,14 @@ class MainPM extends PresentationModel<MainModel> {
|
|||
newValue == BootstrapState.NAT_SUCCESS ||
|
||||
newValue == BootstrapState.RELAY_SUCCESS) {
|
||||
bootstrapState.set("Successfully connected to P2P network: " + newValue.getMessage());
|
||||
bootstrapIndicatorVisible.set(false);
|
||||
bootstrapProgress.set(1);
|
||||
|
||||
if (newValue == BootstrapState.DIRECT_SUCCESS)
|
||||
bootstrapIconId.set("image-connection-direct");
|
||||
else if (newValue == BootstrapState.NAT_SUCCESS)
|
||||
bootstrapIconId.set("image-connection-nat");
|
||||
else if (newValue == BootstrapState.RELAY_SUCCESS)
|
||||
bootstrapIconId.set("image-connection-relay");
|
||||
}
|
||||
else if (newValue == BootstrapState.PEER_CREATION_FAILED ||
|
||||
newValue == BootstrapState.DIRECT_FAILED ||
|
||||
|
@ -98,7 +107,6 @@ class MainPM extends PresentationModel<MainModel> {
|
|||
|
||||
bootstrapErrorMsg.set(newValue.getMessage());
|
||||
bootstrapState.set("Connection to P2P network failed.");
|
||||
bootstrapIndicatorVisible.set(false);
|
||||
bootstrapProgress.set(0);
|
||||
bootstrapFailed.set(true);
|
||||
}
|
||||
|
@ -115,9 +123,15 @@ class MainPM extends PresentationModel<MainModel> {
|
|||
walletFacadeErrorMsg.set(((Throwable) newValue).getMessage());
|
||||
});
|
||||
|
||||
model.networkSyncProgress.addListener((ov, oldValue, newValue) -> setNetworkSyncProgress((double) newValue));
|
||||
model.networkSyncProgress.addListener((ov, oldValue, newValue) -> {
|
||||
setNetworkSyncProgress((double) newValue);
|
||||
|
||||
if ((double) newValue >= 1)
|
||||
blockchainSyncIconId.set("image-connection-synced");
|
||||
});
|
||||
setNetworkSyncProgress(model.networkSyncProgress.get());
|
||||
|
||||
|
||||
model.getBankAccounts().addListener((ListChangeListener<BankAccount>) change -> {
|
||||
bankAccountsComboBoxDisable.set(change.getList().isEmpty());
|
||||
bankAccountsComboBoxPrompt.set(change.getList().isEmpty() ? "No accounts" : "");
|
||||
|
|
|
@ -297,39 +297,64 @@ public class MainViewCB extends ViewCB<MainPM> {
|
|||
ProgressBar blockchainSyncIndicator = new ProgressBar(-1);
|
||||
blockchainSyncIndicator.setPrefWidth(120);
|
||||
blockchainSyncIndicator.progressProperty().bind(presentationModel.blockchainSyncProgress);
|
||||
blockchainSyncIndicator.visibleProperty().bind(presentationModel.blockchainSyncIndicatorVisible);
|
||||
blockchainSyncIndicator.managedProperty().bind(presentationModel.blockchainSyncIndicatorVisible);
|
||||
|
||||
ImageView blockchainSyncIcon = new ImageView();
|
||||
blockchainSyncIcon.setVisible(false);
|
||||
blockchainSyncIcon.setManaged(false);
|
||||
|
||||
presentationModel.blockchainSyncIconId.addListener((ov, oldValue, newValue) -> {
|
||||
blockchainSyncIcon.setId(newValue);
|
||||
blockchainSyncIcon.setVisible(true);
|
||||
blockchainSyncIcon.setManaged(true);
|
||||
|
||||
blockchainSyncIndicator.setVisible(false);
|
||||
blockchainSyncIndicator.setManaged(false);
|
||||
});
|
||||
|
||||
HBox blockchainSyncBox = new HBox();
|
||||
blockchainSyncBox.setSpacing(10);
|
||||
blockchainSyncBox.setAlignment(Pos.CENTER);
|
||||
blockchainSyncBox.setPadding(new Insets(60, 0, 0, 0));
|
||||
blockchainSyncBox.getChildren().addAll(blockchainSyncLabel, blockchainSyncIndicator);
|
||||
blockchainSyncBox.getChildren().addAll(blockchainSyncLabel, blockchainSyncIndicator, blockchainSyncIcon);
|
||||
|
||||
Label bootstrapStateLabel = new Label();
|
||||
bootstrapStateLabel.setWrapText(true);
|
||||
bootstrapStateLabel.setMaxWidth(500);
|
||||
bootstrapStateLabel.setTextAlignment(TextAlignment.CENTER);
|
||||
bootstrapStateLabel.textProperty().bind(presentationModel.bootstrapState);
|
||||
|
||||
ProgressIndicator bootstrapIndicator = new ProgressIndicator();
|
||||
bootstrapIndicator.setMaxSize(24, 24);
|
||||
bootstrapIndicator.progressProperty().bind(presentationModel.bootstrapProgress);
|
||||
|
||||
presentationModel.bootstrapFailed.addListener((ov, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
bootstrapStateLabel.setId("splash-error-state-msg");
|
||||
bootstrapIndicator.setVisible(false);
|
||||
|
||||
Popups.openErrorPopup("Error", "Cannot connect to P2P network. \n\nError message:\n" +
|
||||
presentationModel.bootstrapErrorMsg.get());
|
||||
}
|
||||
});
|
||||
|
||||
ProgressIndicator bootstrapIndicator = new ProgressIndicator();
|
||||
bootstrapIndicator.setMaxSize(24, 24);
|
||||
bootstrapIndicator.progressProperty().bind(presentationModel.bootstrapProgress);
|
||||
bootstrapIndicator.visibleProperty().bind(presentationModel.bootstrapIndicatorVisible);
|
||||
bootstrapIndicator.managedProperty().bind(presentationModel.bootstrapIndicatorVisible);
|
||||
ImageView bootstrapIcon = new ImageView();
|
||||
bootstrapIcon.setVisible(false);
|
||||
bootstrapIcon.setManaged(false);
|
||||
|
||||
presentationModel.bootstrapIconId.addListener((ov, oldValue, newValue) -> {
|
||||
bootstrapIcon.setId(newValue);
|
||||
bootstrapIcon.setVisible(true);
|
||||
bootstrapIcon.setManaged(true);
|
||||
|
||||
bootstrapIndicator.setVisible(false);
|
||||
bootstrapIndicator.setManaged(false);
|
||||
});
|
||||
|
||||
HBox bootstrapBox = new HBox();
|
||||
bootstrapBox.setSpacing(10);
|
||||
bootstrapBox.setAlignment(Pos.CENTER);
|
||||
bootstrapBox.setPadding(new Insets(10, 0, 0, 0));
|
||||
bootstrapBox.getChildren().addAll(bootstrapStateLabel, bootstrapIndicator);
|
||||
bootstrapBox.getChildren().addAll(bootstrapStateLabel, bootstrapIndicator, bootstrapIcon);
|
||||
|
||||
vBox.getChildren().addAll(logo, blockchainSyncBox, bootstrapBox);
|
||||
return vBox;
|
||||
|
|
|
@ -152,42 +152,44 @@ class BootstrappedPeerFactory {
|
|||
});
|
||||
|
||||
// We save last successful bootstrap method.
|
||||
// Reset it to "default" after 5 start ups.
|
||||
// Reset it to BootstrapState.DIRECT_SUCCESS after 5 start ups.
|
||||
Object bootstrapCounterObject = persistence.read(this, "bootstrapCounter");
|
||||
int bootstrapCounter = 0;
|
||||
if (bootstrapCounterObject instanceof Integer)
|
||||
bootstrapCounter = (int) bootstrapCounterObject + 1;
|
||||
|
||||
if (bootstrapCounter > 5) {
|
||||
persistence.write(this, "lastSuccessfulBootstrap", "default");
|
||||
persistence.write(this, "lastSuccessfulBootstrap", BootstrapState.DIRECT_SUCCESS);
|
||||
bootstrapCounter = 0;
|
||||
}
|
||||
persistence.write(this, "bootstrapCounter", bootstrapCounter);
|
||||
|
||||
String lastSuccessfulBootstrap = (String) persistence.read(this, "lastSuccessfulBootstrap");
|
||||
if (lastSuccessfulBootstrap == null)
|
||||
lastSuccessfulBootstrap = "default";
|
||||
BootstrapState lastSuccessfulBootstrap = BootstrapState.DIRECT_SUCCESS;
|
||||
Object lastSuccessfulBootstrapObject = persistence.read(this, "lastSuccessfulBootstrap");
|
||||
if (lastSuccessfulBootstrapObject instanceof BootstrapState)
|
||||
lastSuccessfulBootstrap = (BootstrapState) lastSuccessfulBootstrapObject;
|
||||
else
|
||||
persistence.write(this, "lastSuccessfulBootstrap", lastSuccessfulBootstrap);
|
||||
|
||||
log.debug("lastSuccessfulBootstrap = " + lastSuccessfulBootstrap);
|
||||
|
||||
// just temporary while port forwarding is not working
|
||||
lastSuccessfulBootstrap = "default";
|
||||
// just temporary always start with trying direct connection
|
||||
lastSuccessfulBootstrap = BootstrapState.DIRECT_SUCCESS;
|
||||
|
||||
switch (lastSuccessfulBootstrap) {
|
||||
case "relay":
|
||||
case RELAY_SUCCESS:
|
||||
bootstrapWithRelay();
|
||||
break;
|
||||
case "portForwarding":
|
||||
case NAT_SUCCESS:
|
||||
tryPortForwarding();
|
||||
break;
|
||||
case "default":
|
||||
case DIRECT_SUCCESS:
|
||||
default:
|
||||
discover();
|
||||
break;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
setState(BootstrapState.PEER_CREATION, "Cannot create peer with port: " + port + ". Exeption: " + e, false);
|
||||
settableFuture.setException(e);
|
||||
handleError(BootstrapState.PEER_CREATION, "Cannot create peer with port: " + port + ". Exeption: " + e);
|
||||
}
|
||||
|
||||
return settableFuture;
|
||||
|
@ -202,7 +204,7 @@ class BootstrappedPeerFactory {
|
|||
public void operationComplete(BaseFuture future) throws Exception {
|
||||
if (future.isSuccess()) {
|
||||
setState(BootstrapState.DIRECT_SUCCESS, "We are directly connected and visible to other peers.");
|
||||
bootstrap("default");
|
||||
bootstrap(BootstrapState.DIRECT_SUCCESS);
|
||||
}
|
||||
else {
|
||||
setState(BootstrapState.DIRECT_NOT_SUCCEEDED, "We are probably behind a NAT and not reachable to " +
|
||||
|
@ -213,9 +215,7 @@ class BootstrappedPeerFactory {
|
|||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception {
|
||||
setState(BootstrapState.DIRECT_FAILED, "Exception at discover: " + t.getMessage(), false);
|
||||
peerDHT.shutdown();
|
||||
settableFuture.setException(t);
|
||||
handleError(BootstrapState.DIRECT_FAILED, "Exception at discover: " + t.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -244,9 +244,7 @@ class BootstrappedPeerFactory {
|
|||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception {
|
||||
setState(BootstrapState.NAT_FAILED, "Exception at port forwarding: " + t.getMessage(), false);
|
||||
peerDHT.shutdown();
|
||||
settableFuture.setException(t);
|
||||
handleError(BootstrapState.NAT_FAILED, "Exception at port forwarding: " + t.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -259,24 +257,17 @@ class BootstrappedPeerFactory {
|
|||
public void operationComplete(BaseFuture future) throws Exception {
|
||||
if (future.isSuccess()) {
|
||||
setState(BootstrapState.NAT_SUCCESS, "Discover with automatic port forwarding was successful.");
|
||||
bootstrap("portForwarding");
|
||||
bootstrap(BootstrapState.NAT_SUCCESS);
|
||||
}
|
||||
else {
|
||||
setState(BootstrapState.NAT_FAILED, "Discover with automatic port forwarding has failed " +
|
||||
futureDiscover.failedReason(), false);
|
||||
persistence.write(BootstrappedPeerFactory.this, "lastSuccessfulBootstrap", "default");
|
||||
peerDHT.shutdown();
|
||||
settableFuture.setException(new Exception("Discover with automatic port forwarding failed " +
|
||||
futureDiscover.failedReason()));
|
||||
handleError(BootstrapState.NAT_FAILED, "Discover with automatic port forwarding has failed " +
|
||||
futureDiscover.failedReason());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception {
|
||||
setState(BootstrapState.NAT_FAILED, "Exception at discover: " + t, false);
|
||||
persistence.write(BootstrappedPeerFactory.this, "lastSuccessfulBootstrap", "default");
|
||||
peerDHT.shutdown();
|
||||
settableFuture.setException(t);
|
||||
handleError(BootstrapState.NAT_FAILED, "Exception at discover: " + t.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -293,50 +284,40 @@ class BootstrappedPeerFactory {
|
|||
public void operationComplete(BaseFuture future) throws Exception {
|
||||
if (future.isSuccess()) {
|
||||
setState(BootstrapState.RELAY_SUCCESS, "Bootstrap using relay was successful.");
|
||||
bootstrap("relay");
|
||||
bootstrap(BootstrapState.RELAY_SUCCESS);
|
||||
}
|
||||
else {
|
||||
setState(BootstrapState.RELAY_FAILED, "Bootstrap using relay has failed " + futureRelayNAT
|
||||
.failedReason(), false);
|
||||
persistence.write(BootstrappedPeerFactory.this, "lastSuccessfulBootstrap", "default");
|
||||
futureRelayNAT.shutdown();
|
||||
peerDHT.shutdown();
|
||||
settableFuture.setException(new Exception("Bootstrap using relay failed " +
|
||||
futureRelayNAT.failedReason()));
|
||||
handleError(BootstrapState.RELAY_FAILED, "Bootstrap using relay has failed " +
|
||||
futureRelayNAT.failedReason());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception {
|
||||
setState(BootstrapState.RELAY_FAILED, "Exception at bootstrapWithRelay: " + t, false);
|
||||
persistence.write(BootstrappedPeerFactory.this, "lastSuccessfulBootstrap", "default");
|
||||
futureRelayNAT.shutdown();
|
||||
peerDHT.shutdown();
|
||||
settableFuture.setException(t);
|
||||
handleError(BootstrapState.RELAY_FAILED, "Exception at bootstrapWithRelay: " + t.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void bootstrap(String state) {
|
||||
private void bootstrap(BootstrapState state) {
|
||||
FutureBootstrap futureBootstrap = peer.bootstrap().peerAddress(getBootstrapAddress()).start();
|
||||
futureBootstrap.addListener(new BaseFutureListener<BaseFuture>() {
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception {
|
||||
if (futureBootstrap.isSuccess()) {
|
||||
setState(BootstrapState.DIRECT_SUCCESS, "Bootstrap successful.");
|
||||
setState(state, "Bootstrap successful.");
|
||||
persistence.write(BootstrappedPeerFactory.this, "lastSuccessfulBootstrap", state);
|
||||
settableFuture.set(peerDHT);
|
||||
}
|
||||
else {
|
||||
setState(BootstrapState.DIRECT_NOT_SUCCEEDED, "Bootstrapping failed.");
|
||||
handleError(BootstrapState.DIRECT_NOT_SUCCEEDED, "Bootstrapping failed. " +
|
||||
futureBootstrap.failedReason());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception {
|
||||
setState(BootstrapState.DIRECT_FAILED, "Exception at bootstrap: " + t.getMessage(), false);
|
||||
peerDHT.shutdown();
|
||||
settableFuture.setException(t);
|
||||
handleError(BootstrapState.DIRECT_FAILED, "Exception at bootstrap: " + t.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -366,4 +347,11 @@ class BootstrappedPeerFactory {
|
|||
bootstrapState.setMessage(message);
|
||||
Platform.runLater(() -> this.bootstrapState.set(bootstrapState));
|
||||
}
|
||||
|
||||
private void handleError(BootstrapState state, String errorMessage) {
|
||||
setState(state, errorMessage, false);
|
||||
persistence.write(this, "lastSuccessfulBootstrap", BootstrapState.DIRECT_SUCCESS);
|
||||
peerDHT.shutdown();
|
||||
settableFuture.setException(new Exception(errorMessage));
|
||||
}
|
||||
}
|
||||
|
|
BIN
src/main/resources/images/connection/direct.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
src/main/resources/images/connection/direct@2x.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
src/main/resources/images/connection/nat.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
src/main/resources/images/connection/nat@2x.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
src/main/resources/images/connection/relay.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
src/main/resources/images/connection/relay@2x.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
src/main/resources/images/connection/synced.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
src/main/resources/images/connection/synced@2x.png
Normal file
After Width: | Height: | Size: 3.7 KiB |