mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-08-04 20:54:13 -04:00
245 lines
9.1 KiB
Java
245 lines
9.1 KiB
Java
/*
|
|
* This file is part of Bisq.
|
|
*
|
|
* Bisq is free software: you can redistribute it and/or modify it
|
|
* under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or (at
|
|
* your option) any later version.
|
|
*
|
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
* License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package haveno.desktop.app;
|
|
|
|
import haveno.common.UserThread;
|
|
import haveno.common.app.AppModule;
|
|
import haveno.common.app.Version;
|
|
import haveno.common.crypto.IncorrectPasswordException;
|
|
import haveno.core.app.AvoidStandbyModeService;
|
|
import haveno.core.app.HavenoExecutable;
|
|
import haveno.desktop.common.UITimer;
|
|
import haveno.desktop.common.view.guice.InjectorViewFactory;
|
|
import haveno.desktop.setup.DesktopPersistedDataHost;
|
|
import haveno.desktop.util.ImageUtil;
|
|
import javafx.application.Application;
|
|
import javafx.application.Platform;
|
|
import javafx.scene.control.ButtonBar;
|
|
import javafx.scene.control.ButtonType;
|
|
import javafx.scene.control.Dialog;
|
|
import javafx.scene.control.Label;
|
|
import javafx.scene.control.PasswordField;
|
|
import javafx.scene.image.ImageView;
|
|
import javafx.scene.layout.VBox;
|
|
import javafx.scene.paint.Color;
|
|
import javafx.stage.Stage;
|
|
import javafx.stage.Window;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
import java.util.Optional;
|
|
import java.util.concurrent.CompletableFuture;
|
|
import java.util.concurrent.ExecutionException;
|
|
|
|
@Slf4j
|
|
public class HavenoAppMain extends HavenoExecutable {
|
|
|
|
private HavenoApp application;
|
|
|
|
public HavenoAppMain() {
|
|
super("Haveno Desktop", "haveno-desktop", HavenoExecutable.DEFAULT_APP_NAME, Version.VERSION);
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
// For some reason the JavaFX launch process results in us losing the thread
|
|
// context class loader: reset it. In order to work around a bug in JavaFX 8u25
|
|
// and below, you must include the following code as the first line of your
|
|
// realMain method:
|
|
Thread.currentThread().setContextClassLoader(HavenoAppMain.class.getClassLoader());
|
|
|
|
new HavenoAppMain().execute(args);
|
|
}
|
|
|
|
@Override
|
|
public void onSetupComplete() {
|
|
log.debug("onSetupComplete");
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// First synchronous execution tasks
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
@Override
|
|
protected void configUserThread() {
|
|
UserThread.setExecutor(Platform::runLater);
|
|
UserThread.setTimerClass(UITimer.class);
|
|
}
|
|
|
|
@Override
|
|
protected void launchApplication() {
|
|
HavenoApp.setAppLaunchedHandler(application -> {
|
|
HavenoAppMain.this.application = (HavenoApp) application;
|
|
// Map to user thread!
|
|
UserThread.execute(this::onApplicationLaunched);
|
|
});
|
|
|
|
Application.launch(HavenoApp.class);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// As application is a JavaFX application we need to wait for onApplicationLaunched
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
@Override
|
|
protected void onApplicationLaunched() {
|
|
super.onApplicationLaunched();
|
|
application.setGracefulShutDownHandler(this);
|
|
}
|
|
|
|
@Override
|
|
public void handleUncaughtException(Throwable throwable, boolean doShutDown) {
|
|
application.handleUncaughtException(throwable, doShutDown);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// We continue with a series of synchronous execution tasks
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
@Override
|
|
protected AppModule getModule() {
|
|
return new HavenoAppModule(config);
|
|
}
|
|
|
|
@Override
|
|
protected void applyInjector() {
|
|
super.applyInjector();
|
|
|
|
application.setInjector(injector);
|
|
injector.getInstance(InjectorViewFactory.class).setInjector(injector);
|
|
}
|
|
|
|
@Override
|
|
protected void readAllPersisted(Runnable completeHandler) {
|
|
super.readAllPersisted(DesktopPersistedDataHost.getPersistedDataHosts(injector), completeHandler);
|
|
}
|
|
|
|
@Override
|
|
protected void setupAvoidStandbyMode() {
|
|
injector.getInstance(AvoidStandbyModeService.class).init();
|
|
}
|
|
|
|
@Override
|
|
protected void startApplication() {
|
|
// We need to be in user thread! We mapped at launchApplication already. Once
|
|
// the UI is ready we get onApplicationStarted called and start the setup there.
|
|
application.startApplication(this::onApplicationStarted);
|
|
}
|
|
|
|
@Override
|
|
protected void onApplicationStarted() {
|
|
super.onApplicationStarted();
|
|
|
|
// Relevant to have this in the logs, for support cases
|
|
// This can only be called after JavaFX is initialized, otherwise the version logged will be null
|
|
// Therefore, calling this as part of onApplicationStarted()
|
|
log.info("Using JavaFX {}", System.getProperty("javafx.version"));
|
|
}
|
|
|
|
@Override
|
|
protected CompletableFuture<Boolean> loginAccount() {
|
|
|
|
// attempt default login
|
|
CompletableFuture<Boolean> result = super.loginAccount();
|
|
try {
|
|
if (result.get()) return result;
|
|
} catch (InterruptedException | ExecutionException e) {
|
|
throw new IllegalStateException(e);
|
|
}
|
|
|
|
// login using dialog
|
|
CompletableFuture<Boolean> dialogResult = new CompletableFuture<>();
|
|
Platform.setImplicitExit(false);
|
|
Platform.runLater(() -> {
|
|
|
|
// show password dialog until account open
|
|
String errorMessage = null;
|
|
while (!accountService.isAccountOpen()) {
|
|
|
|
// create the password dialog
|
|
PasswordDialog passwordDialog = new PasswordDialog(errorMessage);
|
|
|
|
// wait for user to enter password
|
|
Optional<String> passwordResult = passwordDialog.showAndWait();
|
|
if (passwordResult.isPresent()) {
|
|
try {
|
|
accountService.openAccount(passwordResult.get());
|
|
dialogResult.complete(accountService.isAccountOpen());
|
|
} catch (IncorrectPasswordException e) {
|
|
errorMessage = "Incorrect password";
|
|
}
|
|
} else {
|
|
// if the user cancelled the dialog, complete the passwordFuture exceptionally
|
|
dialogResult.completeExceptionally(new Exception("Password dialog cancelled"));
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
return dialogResult;
|
|
}
|
|
|
|
private class PasswordDialog extends Dialog<String> {
|
|
|
|
public PasswordDialog(String errorMessage) {
|
|
setTitle("Enter Password");
|
|
setHeaderText("Please enter your Haveno password:");
|
|
|
|
// Add an icon to the dialog
|
|
Stage stage = (Stage) getDialogPane().getScene().getWindow();
|
|
stage.getIcons().add(ImageUtil.getImageByPath("lock.png"));
|
|
|
|
// Create the password field
|
|
PasswordField passwordField = new PasswordField();
|
|
passwordField.setPromptText("Password");
|
|
|
|
// Create the error message field
|
|
Label errorMessageField = new Label(errorMessage);
|
|
errorMessageField.setTextFill(Color.color(1, 0, 0));
|
|
|
|
// Set the dialog content
|
|
VBox vbox = new VBox(10);
|
|
vbox.getChildren().addAll(new ImageView(ImageUtil.getImageByPath("logo_splash.png")), passwordField, errorMessageField);
|
|
getDialogPane().setContent(vbox);
|
|
|
|
// Add OK and Cancel buttons
|
|
ButtonType okButton = new ButtonType("OK", ButtonBar.ButtonData.OK_DONE);
|
|
ButtonType cancelButton = new ButtonType("Cancel", ButtonBar.ButtonData.CANCEL_CLOSE);
|
|
getDialogPane().getButtonTypes().addAll(okButton, cancelButton);
|
|
|
|
// Convert the result to a string when the OK button is clicked
|
|
setResultConverter(buttonType -> {
|
|
if (buttonType == okButton) {
|
|
return passwordField.getText();
|
|
} else {
|
|
new Thread(() -> HavenoApp.getShutDownHandler().run()).start();
|
|
return null;
|
|
}
|
|
});
|
|
|
|
// Focus the password field when dialog is shown
|
|
Window window = getDialogPane().getScene().getWindow();
|
|
if (window instanceof Stage) {
|
|
Stage dialogStage = (Stage) window;
|
|
dialogStage.focusedProperty().addListener((observable, oldValue, newValue) -> {
|
|
if (newValue) {
|
|
passwordField.requestFocus();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|