From c0d814ed38bdbc0d98fb74885eb8a59e0cbe64bd Mon Sep 17 00:00:00 2001 From: CyB3RC0nN0R Date: Mon, 8 Jun 2020 11:58:57 +0200 Subject: [PATCH] Converted the login dialog into a scene --- .../java/envoy/client/ui/SceneContext.java | 63 ++++++- src/main/java/envoy/client/ui/Startup.java | 55 +----- .../envoy/client/ui/controller/ChatScene.java | 24 +-- .../LoginScene.java} | 177 ++++++++++-------- .../{LoginDialog.fxml => LoginScene.fxml} | 26 +-- 5 files changed, 187 insertions(+), 158 deletions(-) rename src/main/java/envoy/client/ui/{LoginDialog.java => controller/LoginScene.java} (57%) rename src/main/resources/fxml/{LoginDialog.fxml => LoginScene.fxml} (70%) diff --git a/src/main/java/envoy/client/ui/SceneContext.java b/src/main/java/envoy/client/ui/SceneContext.java index 42dbc59..0c40bf3 100644 --- a/src/main/java/envoy/client/ui/SceneContext.java +++ b/src/main/java/envoy/client/ui/SceneContext.java @@ -29,10 +29,45 @@ import envoy.event.EventBus; */ public final class SceneContext { + /** + * Contains information about different scenes and their FXML resource files. + * + * @author Kai S. K. Engelbart + * @since Envoy Client v0.1-beta + */ public static enum SceneInfo { - CHAT_SCENE("/fxml/ChatScene.fxml"), SETTINGS_SCENE("/fxml/SettingsScene.fxml"), CONTACT_SEARCH_SCENE("/fxml/ContactSearchScene.fxml"); + /** + * The main scene in which chats are displayed. + * + * @since Envoy Client v0.1-beta + */ + CHAT_SCENE("/fxml/ChatScene.fxml"), + /** + * The scene in which settings are displayed. + * + * @since Envoy Client v0.1-beta + */ + SETTINGS_SCENE("/fxml/SettingsScene.fxml"), + + /** + * The scene in which the contact search is displayed. + * + * @since Envoy Client v0.1-beta + */ + CONTACT_SEARCH_SCENE("/fxml/ContactSearchScene.fxml"), + + /** + * The scene in which the login screen is displayed. + * + * @since Envoy Client v0.1-beta + */ + LOGIN_SCENE("/fxml/LoginScene.fxml"); + + /** + * The path to the FXML resource. + */ public final String path; SceneInfo(String path) { this.path = path; } @@ -59,20 +94,24 @@ public final class SceneContext { * Loads a new scene specified by a scene info. * * @param sceneInfo specifies the scene to load - * @throws IOException if the loading process fails + * @throws RuntimeException if the loading process fails * @since Envoy Client v0.1-beta */ - public void load(SceneInfo sceneInfo) throws IOException { + public void load(SceneInfo sceneInfo) { loader.setRoot(null); loader.setController(null); - final var rootNode = (Parent) loader.load(getClass().getResourceAsStream(sceneInfo.path)); - final var scene = new Scene(rootNode); + try { + final var rootNode = (Parent) loader.load(getClass().getResourceAsStream(sceneInfo.path)); + final var scene = new Scene(rootNode); - sceneStack.push(scene); - stage.setScene(scene); - applyCSS(); - stage.show(); + sceneStack.push(scene); + stage.setScene(scene); + applyCSS(); + stage.show(); + } catch (IOException e) { + throw new RuntimeException(e); + } } /** @@ -104,4 +143,10 @@ public final class SceneContext { * @since Envoy Client v0.1-beta */ public T getController() { return loader.getController(); } + + /** + * @return the stage in which the scenes are displayed + * @since Envoy Client v0.1-beta + */ + public Stage getStage() { return stage; } } diff --git a/src/main/java/envoy/client/ui/Startup.java b/src/main/java/envoy/client/ui/Startup.java index 8464ae6..ce5afb1 100644 --- a/src/main/java/envoy/client/ui/Startup.java +++ b/src/main/java/envoy/client/ui/Startup.java @@ -1,7 +1,6 @@ package envoy.client.ui; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.util.Properties; import java.util.logging.Level; @@ -14,11 +13,9 @@ import javafx.stage.Stage; import envoy.client.data.*; import envoy.client.net.Client; -import envoy.client.net.WriteProxy; -import envoy.client.ui.controller.ChatScene; +import envoy.client.ui.SceneContext.SceneInfo; +import envoy.client.ui.controller.LoginScene; import envoy.data.Message; -import envoy.data.User; -import envoy.data.User.UserStatus; import envoy.exception.EnvoyException; import envoy.util.EnvoyLog; @@ -34,7 +31,6 @@ public final class Startup extends Application { private LocalDB localDB; private Client client; - private WriteProxy writeProxy; private Cache cache; private static final ClientConfig config = ClientConfig.getInstance(); @@ -86,53 +82,12 @@ public final class Startup extends Application { client = new Client(); cache = new Cache<>(); - // Try to connect to the server - new LoginDialog(client, localDB, cache).showAndWait(); - - // Set client user in local database - localDB.setUser(client.getSender()); - - // Initialize chats in local database - try { - localDB.initializeUserStorage(); - localDB.loadUserData(); - } catch (final FileNotFoundException e) { - // The local database file has not yet been created, probably first login - } catch (final Exception e) { - e.printStackTrace(); - new Alert(AlertType.ERROR, "Error while loading local database: " + e + "\nChats will not be stored locally.").showAndWait(); - } - - // Initialize write proxy - writeProxy = client.createWriteProxy(localDB); - - if (client.isOnline()) { - - // Save all users to the local database and flush cache - localDB.setUsers(client.getUsers()); - writeProxy.flushCache(); - } else - // Set all contacts to offline mode - localDB.getUsers() - .values() - .stream() - .filter(User.class::isInstance) - .map(User.class::cast) - .forEach(u -> u.setStatus(UserStatus.OFFLINE)); - - // Prepare stage and load ChatScene - final var sceneContext = new SceneContext(stage); - sceneContext.load(SceneContext.SceneInfo.CHAT_SCENE); - sceneContext.getController().initializeData(sceneContext, localDB, client, writeProxy); - stage.setTitle("Envoy"); - stage.setMinHeight(400); - stage.setMinWidth(350); stage.getIcons().add(IconUtil.load("/icons/envoy_logo.png")); - stage.show(); - // Relay unread messages from cache - if (cache != null && client.isOnline()) cache.relay(); + final var sceneContext = new SceneContext(stage); + sceneContext.load(SceneInfo.LOGIN_SCENE); + sceneContext.getController().initializeData(client, localDB, cache, sceneContext); } /** diff --git a/src/main/java/envoy/client/ui/controller/ChatScene.java b/src/main/java/envoy/client/ui/controller/ChatScene.java index ea59c90..e406060 100644 --- a/src/main/java/envoy/client/ui/controller/ChatScene.java +++ b/src/main/java/envoy/client/ui/controller/ChatScene.java @@ -119,6 +119,14 @@ public final class ChatScene { }); } + /** + * @param sceneContext the scene context used to load other scenes + * @param localDB the local database form which chats and users are loaded + * @param client the client used to request ID generators + * @param writeProxy the write proxy used to send messages and other data to + * the server + * @since Envoy Client v0.1-beta + */ public void initializeData(SceneContext sceneContext, LocalDB localDB, Client client, WriteProxy writeProxy) { this.sceneContext = sceneContext; this.localDB = localDB; @@ -158,22 +166,14 @@ public final class ChatScene { @FXML private void settingsButtonClicked() { - try { - sceneContext.load(SceneContext.SceneInfo.SETTINGS_SCENE); - sceneContext.getController().initializeData(sceneContext); - } catch (final IOException e) { - e.printStackTrace(); - } + sceneContext.load(SceneContext.SceneInfo.SETTINGS_SCENE); + sceneContext.getController().initializeData(sceneContext); } @FXML private void addContactButtonClicked() { - try { - sceneContext.load(SceneContext.SceneInfo.CONTACT_SEARCH_SCENE); - sceneContext.getController().initializeData(sceneContext); - } catch (final IOException e) { - e.printStackTrace(); - } + sceneContext.load(SceneContext.SceneInfo.CONTACT_SEARCH_SCENE); + sceneContext.getController().initializeData(sceneContext); } @FXML diff --git a/src/main/java/envoy/client/ui/LoginDialog.java b/src/main/java/envoy/client/ui/controller/LoginScene.java similarity index 57% rename from src/main/java/envoy/client/ui/LoginDialog.java rename to src/main/java/envoy/client/ui/controller/LoginScene.java index 2968727..4f13149 100644 --- a/src/main/java/envoy/client/ui/LoginDialog.java +++ b/src/main/java/envoy/client/ui/controller/LoginScene.java @@ -1,24 +1,24 @@ -package envoy.client.ui; +package envoy.client.ui.controller; +import java.io.FileNotFoundException; import java.io.IOException; import java.util.concurrent.TimeoutException; import java.util.logging.Logger; import javafx.application.Platform; -import javafx.event.ActionEvent; import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; import javafx.scene.control.*; import javafx.scene.control.Alert.AlertType; -import javafx.stage.Stage; import envoy.client.data.Cache; import envoy.client.data.ClientConfig; import envoy.client.data.LocalDB; import envoy.client.net.Client; +import envoy.client.ui.SceneContext; import envoy.data.LoginCredentials; import envoy.data.Message; import envoy.data.User; +import envoy.data.User.UserStatus; import envoy.event.EventBus; import envoy.event.HandshakeRejectionEvent; import envoy.exception.EnvoyException; @@ -32,7 +32,7 @@ import envoy.util.EnvoyLog; * @author Kai S. K. Engelbart * @since Envoy Client v0.1-beta */ -public final class LoginDialog extends Dialog { +public final class LoginScene { @FXML private TextField userTextField; @@ -49,77 +49,18 @@ public final class LoginDialog extends Dialog { @FXML private CheckBox registerCheckBox; - @FXML - private CheckBox offlineCheckBox; - @FXML private Label connectionLabel; - private final Client client; - private final LocalDB localDB; - private final Cache receivedMessageCache; + private Client client; + private LocalDB localDB; + private Cache receivedMessageCache; + private SceneContext sceneContext; - private static final Logger logger = EnvoyLog.getLogger(LoginDialog.class); + private static final Logger logger = EnvoyLog.getLogger(LoginScene.class); private static final EventBus eventBus = EventBus.getInstance(); private static final ClientConfig config = ClientConfig.getInstance(); - /** - * Loads the login dialog using the FXML file {@code LoginDialog.fxml}. - * - * @param client the client used to perform the handshake - * @param localDB the local database used for offline login - * @param receivedMessageCache the cache storing messages received during - * the handshake - * @throws IOException if an exception occurs during loading - * @since Envoy Client v0.1-beta - */ - public LoginDialog(Client client, LocalDB localDB, Cache receivedMessageCache) throws IOException { - this.client = client; - this.localDB = localDB; - this.receivedMessageCache = receivedMessageCache; - - // Prepare handshake - localDB.loadIDGenerator(); - - final var loader = new FXMLLoader(getClass().getResource("/fxml/LoginDialog.fxml")); - loader.setController(this); - final var dialogPane = loader.load(); - - ((Stage) getDialogPane().getScene().getWindow()).getIcons().add(IconUtil.load("/icons/envoy_logo.png")); - - // Configure dialog buttons - dialogPane.getButtonTypes().addAll(ButtonType.CLOSE, ButtonType.OK); - - // Close button - dialogPane.lookupButton(ButtonType.CLOSE).addEventHandler(ActionEvent.ACTION, e -> abortLogin()); - - // Login button - final var loginButton = (Button) dialogPane.lookupButton(ButtonType.OK); - loginButton.setText("Login"); - loginButton.addEventFilter(ActionEvent.ACTION, e -> { - e.consume(); - - // Prevent registration with unequal passwords - if (registerCheckBox.isSelected() && !passwordField.getText().equals(repeatPasswordField.getText())) { - clearPasswordFields(); - new Alert(AlertType.ERROR, "The entered password is unequal to the repeated one").showAndWait(); - } else { - final var credentials = new LoginCredentials(userTextField.getText(), passwordField.getText().toCharArray(), - registerCheckBox.isSelected()); - if (!offlineCheckBox.isSelected()) performHandshake(credentials); - else attemptOfflineMode(credentials); - } - }); - - // Perform automatic login if configured - setOnShown(e -> { if (config.hasLoginCredentials()) performHandshake(config.getLoginCredentials()); }); - - setDialogPane(dialogPane); - - // Set initial cursor - Platform.runLater(userTextField::requestFocus); - } - @FXML private void initialize() { connectionLabel.setText("Server: " + config.getServer() + ":" + config.getPort()); @@ -129,6 +70,50 @@ public final class LoginDialog extends Dialog { e -> Platform.runLater(() -> { clearPasswordFields(); new Alert(AlertType.ERROR, e.get()).showAndWait(); })); } + /** + * Loads the login dialog using the FXML file {@code LoginDialog.fxml}. + * + * @param client the client used to perform the handshake + * @param localDB the local database used for offline login + * @param receivedMessageCache the cache storing messages received during + * the handshake + * @param sceneContext the scene context used to initialize the chat + * scene + * @since Envoy Client v0.1-beta + */ + public void initializeData(Client client, LocalDB localDB, Cache receivedMessageCache, SceneContext sceneContext) { + this.client = client; + this.localDB = localDB; + this.receivedMessageCache = receivedMessageCache; + this.sceneContext = sceneContext; + + // Prepare handshake + localDB.loadIDGenerator(); + + // Set initial cursor + userTextField.requestFocus(); + + // Perform automatic login if configured + if (config.hasLoginCredentials()) performHandshake(config.getLoginCredentials()); + } + + @FXML + private void loginButtonPressed() { + + // Prevent registration with unequal passwords + if (registerCheckBox.isSelected() && !passwordField.getText().equals(repeatPasswordField.getText())) { + clearPasswordFields(); + new Alert(AlertType.ERROR, "The entered password is unequal to the repeated one").showAndWait(); + } else { + performHandshake(new LoginCredentials(userTextField.getText(), passwordField.getText().toCharArray(), registerCheckBox.isSelected())); + } + } + + @FXML + private void offlineModeButtonPressed() { + attemptOfflineMode(new LoginCredentials(userTextField.getText(), passwordField.getText().toCharArray(), false)); + } + @FXML private void registerCheckboxChanged() { @@ -139,12 +124,18 @@ public final class LoginDialog extends Dialog { clearPasswordFields(); } + @FXML + private void abortLogin() { + logger.info("The login process has been cancelled. Exiting..."); + System.exit(0); + } + private void performHandshake(LoginCredentials credentials) { try { client.performHandshake(credentials, receivedMessageCache); if (client.isOnline()) { client.initReceiver(localDB, receivedMessageCache); - Platform.runLater(this::hide); + loadChatScene(); } } catch (IOException | InterruptedException | TimeoutException e) { logger.warning("Could not connect to server: " + e); @@ -161,20 +152,54 @@ public final class LoginDialog extends Dialog { if (clientUser == null) throw new EnvoyException("Could not enter offline mode: user name unknown"); client.setSender(clientUser); new Alert(AlertType.WARNING, "A connection to the server could not be established. Starting in offline mode.").showAndWait(); - hide(); + loadChatScene(); } catch (Exception e) { new Alert(AlertType.ERROR, "Client error: " + e).showAndWait(); System.exit(1); } } + private void loadChatScene() { + + // Set client user in local database + localDB.setUser(client.getSender()); + + // Initialize chats in local database + try { + localDB.initializeUserStorage(); + localDB.loadUserData(); + } catch (final FileNotFoundException e) { + // The local database file has not yet been created, probably first login + } catch (final Exception e) { + e.printStackTrace(); + new Alert(AlertType.ERROR, "Error while loading local database: " + e + "\nChats will not be stored locally.").showAndWait(); + } + + // Initialize write proxy + final var writeProxy = client.createWriteProxy(localDB); + + if (client.isOnline()) { + + // Save all users to the local database and flush cache + localDB.setUsers(client.getUsers()); + writeProxy.flushCache(); + } else + // Set all contacts to offline mode + localDB.getUsers().values().stream().filter(User.class::isInstance).map(User.class::cast).forEach(u -> u.setStatus(UserStatus.OFFLINE)); + + // Load ChatScene + sceneContext.pop(); + sceneContext.getStage().setMinHeight(400); + sceneContext.getStage().setMinWidth(350); + sceneContext.load(SceneContext.SceneInfo.CHAT_SCENE); + sceneContext.getController().initializeData(sceneContext, localDB, client, writeProxy); + + // Relay unread messages from cache + if (receivedMessageCache != null && client.isOnline()) receivedMessageCache.relay(); + } + private void clearPasswordFields() { passwordField.clear(); repeatPasswordField.clear(); } - - private void abortLogin() { - logger.info("The login process has been cancelled. Exiting..."); - System.exit(0); - } } diff --git a/src/main/resources/fxml/LoginDialog.fxml b/src/main/resources/fxml/LoginScene.fxml similarity index 70% rename from src/main/resources/fxml/LoginDialog.fxml rename to src/main/resources/fxml/LoginScene.fxml index 3da8595..a3a8018 100644 --- a/src/main/resources/fxml/LoginDialog.fxml +++ b/src/main/resources/fxml/LoginScene.fxml @@ -1,7 +1,8 @@ + + - @@ -11,10 +12,9 @@ - - - - + + + - - - - +