Converted the login dialog into a scene
This commit is contained in:
parent
232439a564
commit
c0d814ed38
@ -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> 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; }
|
||||
}
|
||||
|
@ -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<Message> 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.<ChatScene>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.<LoginScene>getController().initializeData(client, localDB, cache, sceneContext);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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.<SettingsScene>getController().initializeData(sceneContext);
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
sceneContext.load(SceneContext.SceneInfo.SETTINGS_SCENE);
|
||||
sceneContext.<SettingsScene>getController().initializeData(sceneContext);
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void addContactButtonClicked() {
|
||||
try {
|
||||
sceneContext.load(SceneContext.SceneInfo.CONTACT_SEARCH_SCENE);
|
||||
sceneContext.<ContactSearchScene>getController().initializeData(sceneContext);
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
sceneContext.load(SceneContext.SceneInfo.CONTACT_SEARCH_SCENE);
|
||||
sceneContext.<ContactSearchScene>getController().initializeData(sceneContext);
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
@ -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<Void> {
|
||||
public final class LoginScene {
|
||||
|
||||
@FXML
|
||||
private TextField userTextField;
|
||||
@ -49,77 +49,18 @@ public final class LoginDialog extends Dialog<Void> {
|
||||
@FXML
|
||||
private CheckBox registerCheckBox;
|
||||
|
||||
@FXML
|
||||
private CheckBox offlineCheckBox;
|
||||
|
||||
@FXML
|
||||
private Label connectionLabel;
|
||||
|
||||
private final Client client;
|
||||
private final LocalDB localDB;
|
||||
private final Cache<Message> receivedMessageCache;
|
||||
private Client client;
|
||||
private LocalDB localDB;
|
||||
private Cache<Message> 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<Message> 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.<DialogPane>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<Void> {
|
||||
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<Message> 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<Void> {
|
||||
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<Void> {
|
||||
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.<ChatScene>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);
|
||||
}
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ButtonBar?>
|
||||
<?import javafx.scene.control.CheckBox?>
|
||||
<?import javafx.scene.control.DialogPane?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.PasswordField?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
@ -11,10 +12,9 @@
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.text.Font?>
|
||||
|
||||
<DialogPane prefHeight="265.0" prefWidth="545.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<content>
|
||||
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="217.0" prefWidth="545.0">
|
||||
<children>
|
||||
|
||||
<VBox prefHeight="206.0" prefWidth="440.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="envoy.client.ui.controller.LoginScene">
|
||||
<children>
|
||||
<Label text="User Login">
|
||||
<font>
|
||||
<Font size="26.0" />
|
||||
@ -40,9 +40,13 @@
|
||||
</children>
|
||||
</GridPane>
|
||||
<CheckBox fx:id="registerCheckBox" mnemonicParsing="true" onAction="#registerCheckboxChanged" prefHeight="17.0" prefWidth="181.0" text="_Register" />
|
||||
<Label fx:id="connectionLabel" />
|
||||
<CheckBox fx:id="offlineCheckBox" layoutX="20.0" layoutY="144.0" mnemonicParsing="true" prefHeight="17.0" prefWidth="181.0" text="_Offline mode" />
|
||||
</children>
|
||||
</VBox>
|
||||
</content>
|
||||
</DialogPane>
|
||||
<Label fx:id="connectionLabel" />
|
||||
<ButtonBar prefHeight="40.0" prefWidth="200.0">
|
||||
<buttons>
|
||||
<Button mnemonicParsing="false" onAction="#abortLogin" text="Close" />
|
||||
<Button mnemonicParsing="false" onAction="#offlineModeButtonPressed" text="Offline mode" />
|
||||
<Button defaultButton="true" mnemonicParsing="false" onAction="#loginButtonPressed" text="Login" />
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
</children>
|
||||
</VBox>
|
Reference in New Issue
Block a user