Completed LoginDialog implementation except for closing the dialog
This commit is contained in:
		@@ -1,7 +1,6 @@
 | 
			
		||||
package envoy.client.data;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.security.NoSuchAlgorithmException;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
import java.util.logging.Level;
 | 
			
		||||
 | 
			
		||||
@@ -110,11 +109,5 @@ public class ClientConfig extends Config {
 | 
			
		||||
	 *         the registration option
 | 
			
		||||
	 * @since Envoy Client v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public LoginCredentials getLoginCredentials() {
 | 
			
		||||
		try {
 | 
			
		||||
			return new LoginCredentials(getUser(), getPassword(), false);
 | 
			
		||||
		} catch (NoSuchAlgorithmException e) {
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	public LoginCredentials getLoginCredentials() { return new LoginCredentials(getUser(), getPassword(), false); }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,18 +0,0 @@
 | 
			
		||||
package envoy.client.event;
 | 
			
		||||
 | 
			
		||||
import envoy.event.Event;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This {@link Event} indicates that a handshake was completed successfully.
 | 
			
		||||
 *
 | 
			
		||||
 * Project: <strong>envoy-client</strong><br>
 | 
			
		||||
 * File: <strong>HandshakeSuccessfulEvent.java</strong><br>
 | 
			
		||||
 * Created: <strong>8 Feb 2020</strong><br>
 | 
			
		||||
 *
 | 
			
		||||
 * @author Leon Hofmeister
 | 
			
		||||
 * @since Envoy Client v0.3-alpha
 | 
			
		||||
 */
 | 
			
		||||
public class HandshakeSuccessfulEvent extends Event.Valueless {
 | 
			
		||||
 | 
			
		||||
	private static final long serialVersionUID = 0L;
 | 
			
		||||
}
 | 
			
		||||
@@ -107,11 +107,12 @@ public class Client implements Closeable {
 | 
			
		||||
			Thread.sleep(500);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		logger.info("Handshake completed.");
 | 
			
		||||
		online = true;
 | 
			
		||||
 | 
			
		||||
		// Remove user creation processor
 | 
			
		||||
		receiver.removeAllProcessors();
 | 
			
		||||
 | 
			
		||||
		logger.info("Handshake completed.");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
 
 | 
			
		||||
@@ -13,37 +13,61 @@
 | 
			
		||||
<?import javafx.scene.layout.VBox?>
 | 
			
		||||
<?import javafx.scene.text.Font?>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<DialogPane xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="envoy.client.ui.LoginDialog$Controller">
 | 
			
		||||
   <content>
 | 
			
		||||
      <VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0">
 | 
			
		||||
      	<children>
 | 
			
		||||
      		<Label text="User Login">
 | 
			
		||||
      			<font>
 | 
			
		||||
      				<Font size="26.0" />
 | 
			
		||||
      			</font>
 | 
			
		||||
      		</Label>
 | 
			
		||||
      		<GridPane>
 | 
			
		||||
      			<columnConstraints>
 | 
			
		||||
      				<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="40.0" prefWidth="100.0" />
 | 
			
		||||
      				<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
 | 
			
		||||
      			</columnConstraints>
 | 
			
		||||
      			<rowConstraints>
 | 
			
		||||
      				<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
 | 
			
		||||
      				<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
 | 
			
		||||
      				<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
 | 
			
		||||
      			</rowConstraints>
 | 
			
		||||
      			<children>
 | 
			
		||||
      				<Label text="User Name:" />
 | 
			
		||||
      				<Label text="Password" GridPane.rowIndex="1" />
 | 
			
		||||
      				<Label fx:id="repeatPasswordLabel" text="Repeat Password:" visible="false" GridPane.rowIndex="2" />
 | 
			
		||||
      				<TextField fx:id="userTextField" GridPane.columnIndex="1" />
 | 
			
		||||
      				<PasswordField fx:id="repeatPasswordField" GridPane.columnIndex="1" GridPane.rowIndex="1" />
 | 
			
		||||
      				<PasswordField fx:id="repeatPasswordField" visible="false" GridPane.columnIndex="1" GridPane.rowIndex="2" />
 | 
			
		||||
      			</children>
 | 
			
		||||
      		</GridPane>
 | 
			
		||||
      		<CheckBox fx:id="registerCheckBox" mnemonicParsing="false" onAction="#registerCheckboxChanged" text="Register" />
 | 
			
		||||
      	</children>
 | 
			
		||||
      </VBox>
 | 
			
		||||
   </content>
 | 
			
		||||
<DialogPane prefHeight="201.0" prefWidth="525.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>
 | 
			
		||||
				<Label text="User Login">
 | 
			
		||||
					<font>
 | 
			
		||||
						<Font size="26.0" />
 | 
			
		||||
					</font>
 | 
			
		||||
				</Label>
 | 
			
		||||
				<GridPane>
 | 
			
		||||
					<columnConstraints>
 | 
			
		||||
						<ColumnConstraints hgrow="SOMETIMES"
 | 
			
		||||
							minWidth="10.0" percentWidth="40.0" prefWidth="100.0" />
 | 
			
		||||
						<ColumnConstraints hgrow="SOMETIMES"
 | 
			
		||||
							minWidth="10.0" prefWidth="100.0" />
 | 
			
		||||
					</columnConstraints>
 | 
			
		||||
					<rowConstraints>
 | 
			
		||||
						<RowConstraints minHeight="10.0" prefHeight="30.0"
 | 
			
		||||
							vgrow="SOMETIMES" />
 | 
			
		||||
						<RowConstraints minHeight="10.0" prefHeight="30.0"
 | 
			
		||||
							vgrow="SOMETIMES" />
 | 
			
		||||
						<RowConstraints minHeight="10.0" prefHeight="30.0"
 | 
			
		||||
							vgrow="SOMETIMES" />
 | 
			
		||||
					</rowConstraints>
 | 
			
		||||
					<children>
 | 
			
		||||
						<Label text="User Name:" />
 | 
			
		||||
						<Label text="Password" GridPane.rowIndex="1" />
 | 
			
		||||
						<Label fx:id="repeatPasswordLabel" text="Repeat Password:"
 | 
			
		||||
							visible="false" GridPane.rowIndex="2" />
 | 
			
		||||
						<TextField fx:id="userTextField"
 | 
			
		||||
							GridPane.columnIndex="1" />
 | 
			
		||||
						<PasswordField fx:id="passwordField"
 | 
			
		||||
							GridPane.columnIndex="1" GridPane.rowIndex="1" />
 | 
			
		||||
						<PasswordField fx:id="repeatPasswordField"
 | 
			
		||||
							visible="false" GridPane.columnIndex="1" GridPane.rowIndex="2" />
 | 
			
		||||
					</children>
 | 
			
		||||
				</GridPane>
 | 
			
		||||
				<HBox alignment="CENTER_RIGHT" prefHeight="100.0"
 | 
			
		||||
					prefWidth="200.0">
 | 
			
		||||
					<children>
 | 
			
		||||
						<CheckBox fx:id="registerCheckBox"
 | 
			
		||||
							mnemonicParsing="false" onAction="#registerCheckboxChanged"
 | 
			
		||||
							text="Register" />
 | 
			
		||||
						<Button alignment="CENTER_RIGHT" cancelButton="true"
 | 
			
		||||
							mnemonicParsing="false" onAction="#abortLogin" text="Close" />
 | 
			
		||||
						<Button alignment="CENTER_RIGHT" defaultButton="true"
 | 
			
		||||
							mnemonicParsing="false" text="Login"
 | 
			
		||||
							onAction="#loginButtonClicked" fx:id="loginButton" />
 | 
			
		||||
					</children>
 | 
			
		||||
				</HBox>
 | 
			
		||||
			</children>
 | 
			
		||||
		</VBox>
 | 
			
		||||
	</content>
 | 
			
		||||
</DialogPane>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,26 @@
 | 
			
		||||
package envoy.client.ui;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.logging.Logger;
 | 
			
		||||
 | 
			
		||||
import javax.naming.TimeLimitExceededException;
 | 
			
		||||
 | 
			
		||||
import envoy.client.data.Cache;
 | 
			
		||||
import envoy.client.data.ClientConfig;
 | 
			
		||||
import envoy.client.data.LocalDB;
 | 
			
		||||
import envoy.client.net.Client;
 | 
			
		||||
import envoy.data.LoginCredentials;
 | 
			
		||||
import envoy.data.Message;
 | 
			
		||||
import envoy.data.User;
 | 
			
		||||
import envoy.event.EventBus;
 | 
			
		||||
import envoy.event.HandshakeRejectionEvent;
 | 
			
		||||
import envoy.exception.EnvoyException;
 | 
			
		||||
import envoy.util.EnvoyLog;
 | 
			
		||||
import javafx.application.Platform;
 | 
			
		||||
import javafx.fxml.FXML;
 | 
			
		||||
import javafx.fxml.FXMLLoader;
 | 
			
		||||
import javafx.scene.control.*;
 | 
			
		||||
import javafx.scene.control.Alert.AlertType;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Project: <strong>envoy-client</strong><br>
 | 
			
		||||
@@ -17,54 +32,125 @@ import javafx.scene.control.*;
 | 
			
		||||
 */
 | 
			
		||||
public final class LoginDialog extends Dialog<Void> {
 | 
			
		||||
 | 
			
		||||
	@FXML
 | 
			
		||||
	private TextField userTextField;
 | 
			
		||||
 | 
			
		||||
	@FXML
 | 
			
		||||
	private PasswordField passwordField;
 | 
			
		||||
 | 
			
		||||
	@FXML
 | 
			
		||||
	private PasswordField repeatPasswordField;
 | 
			
		||||
 | 
			
		||||
	@FXML
 | 
			
		||||
	private Label repeatPasswordLabel;
 | 
			
		||||
 | 
			
		||||
	@FXML
 | 
			
		||||
	private CheckBox registerCheckBox;
 | 
			
		||||
 | 
			
		||||
	@FXML
 | 
			
		||||
	private Button loginButton;
 | 
			
		||||
 | 
			
		||||
	private final Client			client;
 | 
			
		||||
	private final LocalDB			localDB;
 | 
			
		||||
	private final Cache<Message>	receivedMessageCache;
 | 
			
		||||
 | 
			
		||||
	private static final Logger			logger		= EnvoyLog.getLogger(LoginDialog.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() throws IOException {
 | 
			
		||||
		final var dialogPane = FXMLLoader.<DialogPane>load(getClass().getResource("LoginDialog.fxml"));
 | 
			
		||||
	public LoginDialog(Client client, LocalDB localDB, Cache<Message> receivedMessageCache) throws IOException {
 | 
			
		||||
		this.client					= client;
 | 
			
		||||
		this.localDB				= localDB;
 | 
			
		||||
		this.receivedMessageCache	= receivedMessageCache;
 | 
			
		||||
 | 
			
		||||
		// Configure buttons
 | 
			
		||||
		dialogPane.getButtonTypes().addAll(ButtonType.CANCEL, ButtonType.OK);
 | 
			
		||||
 | 
			
		||||
		setDialogPane(dialogPane);
 | 
			
		||||
		final var loader = new FXMLLoader(getClass().getResource("LoginDialog.fxml"));
 | 
			
		||||
		loader.setController(this);
 | 
			
		||||
		setDialogPane(loader.load());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static class Controller {
 | 
			
		||||
	@FXML
 | 
			
		||||
	private void initialize() {
 | 
			
		||||
 | 
			
		||||
		@FXML
 | 
			
		||||
		private TextField userTextField;
 | 
			
		||||
		// Close the dialog when the user exits the dialog
 | 
			
		||||
		getDialogPane().getScene().getWindow().setOnCloseRequest(e -> abortLogin());
 | 
			
		||||
 | 
			
		||||
		@FXML
 | 
			
		||||
		private PasswordField passwordField;
 | 
			
		||||
		// Show an alert after an unsuccessful handshake
 | 
			
		||||
		eventBus.register(HandshakeRejectionEvent.class,
 | 
			
		||||
				e -> Platform.runLater(() -> { clearPasswordFields(); new Alert(AlertType.ERROR, e.get()).showAndWait(); }));
 | 
			
		||||
 | 
			
		||||
		@FXML
 | 
			
		||||
		private PasswordField repeatPasswordField;
 | 
			
		||||
		// Set initial cursor
 | 
			
		||||
		userTextField.requestFocus();
 | 
			
		||||
 | 
			
		||||
		@FXML
 | 
			
		||||
		private Label repeatPasswordLabel;
 | 
			
		||||
 | 
			
		||||
		@FXML
 | 
			
		||||
		private CheckBox registerCheckBox;
 | 
			
		||||
 | 
			
		||||
		@FXML
 | 
			
		||||
		private void initialize() {
 | 
			
		||||
 | 
			
		||||
			// Set initial cursor
 | 
			
		||||
			Platform.runLater(userTextField::requestFocus);
 | 
			
		||||
		// Perform automatic login if configured
 | 
			
		||||
		if (config.hasLoginCredentials()) {
 | 
			
		||||
			performHandshake(config.getLoginCredentials());
 | 
			
		||||
			hide();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		@FXML
 | 
			
		||||
		private void registerCheckboxChanged() {
 | 
			
		||||
	@FXML
 | 
			
		||||
	private void registerCheckboxChanged() {
 | 
			
		||||
 | 
			
		||||
			// Make repeat password field and label visible / invisible
 | 
			
		||||
			repeatPasswordField.setVisible(registerCheckBox.isSelected());
 | 
			
		||||
			repeatPasswordLabel.setVisible(registerCheckBox.isSelected());
 | 
			
		||||
		// Make repeat password field and label visible / invisible
 | 
			
		||||
		repeatPasswordField.setVisible(registerCheckBox.isSelected());
 | 
			
		||||
		repeatPasswordLabel.setVisible(registerCheckBox.isSelected());
 | 
			
		||||
 | 
			
		||||
			// Clear repeat password field if registration cancelled
 | 
			
		||||
			if (!registerCheckBox.isSelected()) repeatPasswordField.clear();
 | 
			
		||||
		clearPasswordFields();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@FXML
 | 
			
		||||
	private void abortLogin() {
 | 
			
		||||
		logger.info("The login process has been cancelled. Exiting...");
 | 
			
		||||
		System.exit(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@FXML
 | 
			
		||||
	private void loginButtonClicked() {
 | 
			
		||||
 | 
			
		||||
		// 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()));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void performHandshake(LoginCredentials credentials) {
 | 
			
		||||
		try {
 | 
			
		||||
			client.performHandshake(credentials, receivedMessageCache);
 | 
			
		||||
			if (client.isOnline()) {
 | 
			
		||||
				client.initReceiver(localDB, receivedMessageCache);
 | 
			
		||||
				hide();
 | 
			
		||||
			}
 | 
			
		||||
		} catch (IOException | InterruptedException | TimeLimitExceededException e) {
 | 
			
		||||
			logger.warning("Could not connect to server. Trying offline mode...");
 | 
			
		||||
			e.printStackTrace();
 | 
			
		||||
			try {
 | 
			
		||||
				// Try entering offline mode
 | 
			
		||||
				localDB.loadUsers();
 | 
			
		||||
				User clientUser = (User) localDB.getUsers().get(credentials.getIdentifier());
 | 
			
		||||
				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.\n" + e).showAndWait();
 | 
			
		||||
				hide();
 | 
			
		||||
			} catch (Exception e1) {
 | 
			
		||||
				new Alert(AlertType.ERROR, "Client error: " + e.toString()).showAndWait();
 | 
			
		||||
				System.exit(1);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void clearPasswordFields() {
 | 
			
		||||
		passwordField.clear();
 | 
			
		||||
		repeatPasswordField.clear();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,8 +7,6 @@ import java.util.Properties;
 | 
			
		||||
import java.util.logging.Level;
 | 
			
		||||
import java.util.logging.Logger;
 | 
			
		||||
 | 
			
		||||
import javax.swing.JOptionPane;
 | 
			
		||||
 | 
			
		||||
import envoy.client.data.*;
 | 
			
		||||
import envoy.client.net.Client;
 | 
			
		||||
import envoy.client.net.WriteProxy;
 | 
			
		||||
@@ -20,6 +18,8 @@ import envoy.util.EnvoyLog;
 | 
			
		||||
import javafx.application.Application;
 | 
			
		||||
import javafx.fxml.FXMLLoader;
 | 
			
		||||
import javafx.scene.Scene;
 | 
			
		||||
import javafx.scene.control.Alert;
 | 
			
		||||
import javafx.scene.control.Alert.AlertType;
 | 
			
		||||
import javafx.scene.image.Image;
 | 
			
		||||
import javafx.scene.layout.GridPane;
 | 
			
		||||
import javafx.stage.Stage;
 | 
			
		||||
@@ -46,7 +46,7 @@ public final class Startup extends Application {
 | 
			
		||||
	 * {@inheritDoc}
 | 
			
		||||
	 */
 | 
			
		||||
	@Override
 | 
			
		||||
	public void init() throws Exception {
 | 
			
		||||
	public void start(Stage stage) throws Exception {
 | 
			
		||||
		try {
 | 
			
		||||
			// Load the configuration from client.properties first
 | 
			
		||||
			Properties properties = new Properties();
 | 
			
		||||
@@ -60,7 +60,7 @@ public final class Startup extends Application {
 | 
			
		||||
			// Check if all mandatory configuration values have been initialized
 | 
			
		||||
			if (!config.isInitialized()) throw new EnvoyException("Configuration is not fully initialized");
 | 
			
		||||
		} catch (Exception e) {
 | 
			
		||||
			JOptionPane.showMessageDialog(null, "Error loading configuration values:\n" + e, "Configuration error", JOptionPane.ERROR_MESSAGE);
 | 
			
		||||
			new Alert(AlertType.ERROR, "Error loading configuration values:\n" + e);
 | 
			
		||||
			e.printStackTrace();
 | 
			
		||||
			System.exit(1);
 | 
			
		||||
		}
 | 
			
		||||
@@ -74,15 +74,12 @@ public final class Startup extends Application {
 | 
			
		||||
		// Initialize the local database
 | 
			
		||||
		if (config.isIgnoreLocalDB()) {
 | 
			
		||||
			localDB = new TransientLocalDB();
 | 
			
		||||
			JOptionPane.showMessageDialog(null,
 | 
			
		||||
					"Ignoring local database.\nMessages will not be saved!",
 | 
			
		||||
					"Local database warning",
 | 
			
		||||
					JOptionPane.WARNING_MESSAGE);
 | 
			
		||||
			new Alert(AlertType.WARNING, "Ignoring local database.\nMessages will not be saved!").showAndWait();
 | 
			
		||||
		} else try {
 | 
			
		||||
			localDB = new PersistentLocalDB(new File(config.getHomeDirectory(), config.getLocalDB().getPath()));
 | 
			
		||||
		} catch (IOException e3) {
 | 
			
		||||
			logger.log(Level.SEVERE, "Could not initialize local database", e3);
 | 
			
		||||
			JOptionPane.showMessageDialog(null, "Could not initialize local database!\n" + e3, "Local database error", JOptionPane.ERROR_MESSAGE);
 | 
			
		||||
			new Alert(AlertType.ERROR, "Could not initialize local database!\n" + e3).showAndWait();
 | 
			
		||||
			System.exit(1);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
@@ -90,18 +87,9 @@ public final class Startup extends Application {
 | 
			
		||||
		// Initialize client and unread message cache
 | 
			
		||||
		client	= new Client();
 | 
			
		||||
		cache	= new Cache<>();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * {@inheritDoc}
 | 
			
		||||
	 */
 | 
			
		||||
	@Override
 | 
			
		||||
	public void start(Stage stage) throws Exception {
 | 
			
		||||
 | 
			
		||||
		// Try to connect to the server
 | 
			
		||||
		// new LoginDialog(client, localDB, cache);
 | 
			
		||||
		new LoginDialog().showAndWait();
 | 
			
		||||
		System.exit(0);
 | 
			
		||||
		new LoginDialog(client, localDB, cache).showAndWait();
 | 
			
		||||
 | 
			
		||||
		// Set client user in local database
 | 
			
		||||
		localDB.setUser(client.getSender());
 | 
			
		||||
@@ -114,10 +102,7 @@ public final class Startup extends Application {
 | 
			
		||||
			// The local database file has not yet been created, probably first login
 | 
			
		||||
		} catch (Exception e) {
 | 
			
		||||
			e.printStackTrace();
 | 
			
		||||
			JOptionPane.showMessageDialog(null,
 | 
			
		||||
					"Error while loading local database: " + e + "\nChats will not be stored locally.",
 | 
			
		||||
					"Local DB error",
 | 
			
		||||
					JOptionPane.WARNING_MESSAGE);
 | 
			
		||||
			new Alert(AlertType.ERROR, "Error while loading local database: " + e + "\nChats will not be stored locally.").showAndWait();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Initialize write proxy
 | 
			
		||||
 
 | 
			
		||||
@@ -1,346 +0,0 @@
 | 
			
		||||
package envoy.client.ui.container;
 | 
			
		||||
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
import java.awt.event.ItemEvent;
 | 
			
		||||
import java.awt.event.WindowAdapter;
 | 
			
		||||
import java.awt.event.WindowEvent;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.security.NoSuchAlgorithmException;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.logging.Logger;
 | 
			
		||||
 | 
			
		||||
import javax.naming.TimeLimitExceededException;
 | 
			
		||||
import javax.swing.*;
 | 
			
		||||
import javax.swing.border.EmptyBorder;
 | 
			
		||||
 | 
			
		||||
import envoy.client.data.*;
 | 
			
		||||
import envoy.client.event.HandshakeSuccessfulEvent;
 | 
			
		||||
import envoy.client.net.Client;
 | 
			
		||||
import envoy.client.ui.Theme;
 | 
			
		||||
import envoy.client.ui.primary.PrimaryButton;
 | 
			
		||||
import envoy.data.LoginCredentials;
 | 
			
		||||
import envoy.data.Message;
 | 
			
		||||
import envoy.data.User;
 | 
			
		||||
import envoy.event.EventBus;
 | 
			
		||||
import envoy.event.HandshakeRejectionEvent;
 | 
			
		||||
import envoy.exception.EnvoyException;
 | 
			
		||||
import envoy.util.EnvoyLog;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Project: <strong>envoy-client</strong><br>
 | 
			
		||||
 * File: <strong>LoginDialog.java</strong><br>
 | 
			
		||||
 * Created: <strong>01.01.2020</strong><br>
 | 
			
		||||
 *
 | 
			
		||||
 * @author Kai S. K. Engelbart
 | 
			
		||||
 * @author Maximilian Käfer
 | 
			
		||||
 * @since Envoy Client v0.3-alpha
 | 
			
		||||
 */
 | 
			
		||||
public class LoginDialog extends JDialog {
 | 
			
		||||
 | 
			
		||||
	private JPanel			contentPanel;
 | 
			
		||||
	private JTextField		textField;
 | 
			
		||||
	private JPasswordField	passwordField;
 | 
			
		||||
	private JPasswordField	repeatPasswordField;
 | 
			
		||||
 | 
			
		||||
	private JLabel	lblUserName;
 | 
			
		||||
	private JLabel	lblPassword;
 | 
			
		||||
	private JLabel	lblRepeatPassword;
 | 
			
		||||
	private JLabel	errorMessage;
 | 
			
		||||
 | 
			
		||||
	private GridBagConstraints	gbc_lblRepeatPassword;
 | 
			
		||||
	private GridBagConstraints	gbc_repeatPasswordField;
 | 
			
		||||
	private GridBagConstraints	gbc_errorMessage;
 | 
			
		||||
 | 
			
		||||
	private JPanel			buttonPane;
 | 
			
		||||
	private JTextPane		registerText;
 | 
			
		||||
	private JCheckBox		registerCheckBox;
 | 
			
		||||
	private PrimaryButton	okButton;
 | 
			
		||||
	private PrimaryButton	cancelButton;
 | 
			
		||||
 | 
			
		||||
	private LoginCredentials credentials;
 | 
			
		||||
 | 
			
		||||
	private final Client			client;
 | 
			
		||||
	private final LocalDB			localDB;
 | 
			
		||||
	private final Cache<Message>	receivedMessageCache;
 | 
			
		||||
 | 
			
		||||
	private static final ClientConfig	config				= ClientConfig.getInstance();
 | 
			
		||||
	private static final Logger			logger				= EnvoyLog.getLogger(LoginDialog.class);
 | 
			
		||||
	private static final long			serialVersionUID	= 0L;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Displays a dialog enabling the user to enter their user name and password.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param client               the client used to perform the handshake
 | 
			
		||||
	 * @param localDB              the local database in which data is persisted
 | 
			
		||||
	 * @param receivedMessageCache the cache that stored messages received during
 | 
			
		||||
	 *                             the handshake
 | 
			
		||||
	 * @since Envoy Client v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public LoginDialog(Client client, LocalDB localDB, Cache<Message> receivedMessageCache) {
 | 
			
		||||
		this.client					= client;
 | 
			
		||||
		this.localDB				= localDB;
 | 
			
		||||
		this.receivedMessageCache	= receivedMessageCache;
 | 
			
		||||
 | 
			
		||||
		// Prepare handshake
 | 
			
		||||
		localDB.loadIDGenerator();
 | 
			
		||||
 | 
			
		||||
		addWindowListener(new WindowAdapter() {
 | 
			
		||||
 | 
			
		||||
			@Override
 | 
			
		||||
			public void windowClosing(WindowEvent e) { abortLogin(); }
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		initUi();
 | 
			
		||||
 | 
			
		||||
		okButton.addActionListener((evt) -> {
 | 
			
		||||
			try {
 | 
			
		||||
				if (registerCheckBox.isSelected()) {
 | 
			
		||||
					// Check password equality
 | 
			
		||||
					if (Arrays.equals(passwordField.getPassword(), repeatPasswordField.getPassword())) {
 | 
			
		||||
						credentials = new LoginCredentials(textField.getText(), passwordField.getPassword(), true);
 | 
			
		||||
						performHandshake();
 | 
			
		||||
					} else {
 | 
			
		||||
						JOptionPane.showMessageDialog(this, "The repeated password is not the original password!");
 | 
			
		||||
						clearPasswordFields();
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					credentials = new LoginCredentials(textField.getText(), passwordField.getPassword(), false);
 | 
			
		||||
					performHandshake();
 | 
			
		||||
				}
 | 
			
		||||
			} catch (NoSuchAlgorithmException e) {
 | 
			
		||||
				e.printStackTrace();
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		// Listen to handshake rejections
 | 
			
		||||
		EventBus.getInstance()
 | 
			
		||||
			.register(HandshakeRejectionEvent.class,
 | 
			
		||||
					evt -> { clearPasswordFields(); errorMessage.setVisible(true); errorMessage.setText(evt.get()); });
 | 
			
		||||
 | 
			
		||||
		// Exit the application when the dialog is cancelled
 | 
			
		||||
		cancelButton.addActionListener(evt -> abortLogin());
 | 
			
		||||
 | 
			
		||||
		// Log in directly if configured
 | 
			
		||||
		if (config.hasLoginCredentials()) {
 | 
			
		||||
			credentials = config.getLoginCredentials();
 | 
			
		||||
			performHandshake();
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		setVisible(true);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void performHandshake() {
 | 
			
		||||
		try {
 | 
			
		||||
			client.performHandshake(credentials, receivedMessageCache);
 | 
			
		||||
			if (client.isOnline()) {
 | 
			
		||||
				client.initReceiver(localDB, receivedMessageCache);
 | 
			
		||||
				dispose();
 | 
			
		||||
			}
 | 
			
		||||
		} catch (IOException | InterruptedException | TimeLimitExceededException e) {
 | 
			
		||||
			logger.warning("Could not connect to server. Trying offline mode...");
 | 
			
		||||
			e.printStackTrace();
 | 
			
		||||
			try {
 | 
			
		||||
				// Try entering offline mode
 | 
			
		||||
				localDB.loadUsers();
 | 
			
		||||
				User clientUser = (User) localDB.getUsers().get(credentials.getIdentifier());
 | 
			
		||||
				if (clientUser == null) throw new EnvoyException("Could not enter offline mode: user name unknown");
 | 
			
		||||
				client.setSender(clientUser);
 | 
			
		||||
				JOptionPane.showMessageDialog(null,
 | 
			
		||||
						"A connection to the server could not be established. Starting in offline mode.\n" + e,
 | 
			
		||||
						"Connection error",
 | 
			
		||||
						JOptionPane.WARNING_MESSAGE);
 | 
			
		||||
				dispose();
 | 
			
		||||
			} catch (Exception e1) {
 | 
			
		||||
				JOptionPane.showMessageDialog(null, e1, "Client error", JOptionPane.ERROR_MESSAGE);
 | 
			
		||||
				System.exit(1);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void initUi() {
 | 
			
		||||
		setSize(338, 123);
 | 
			
		||||
		setLocationRelativeTo(null);
 | 
			
		||||
		setResizable(false);
 | 
			
		||||
		getContentPane().setLayout(new BorderLayout());
 | 
			
		||||
		contentPanel = new JPanel();
 | 
			
		||||
		contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
 | 
			
		||||
		getContentPane().add(contentPanel, BorderLayout.CENTER);
 | 
			
		||||
		GridBagLayout gbl_contentPanel = new GridBagLayout();
 | 
			
		||||
		gbl_contentPanel.columnWidths	= new int[] { 0, 0, 0 };
 | 
			
		||||
		gbl_contentPanel.rowHeights		= new int[] { 0, 0, 0 };
 | 
			
		||||
		gbl_contentPanel.columnWeights	= new double[] { 0.0, 1.0, Double.MIN_VALUE };
 | 
			
		||||
		gbl_contentPanel.rowWeights		= new double[] { 0.0, 0.0, Double.MIN_VALUE };
 | 
			
		||||
		contentPanel.setLayout(gbl_contentPanel);
 | 
			
		||||
 | 
			
		||||
		lblUserName = new JLabel("Username:");
 | 
			
		||||
		GridBagConstraints gbc_lblUserName = new GridBagConstraints();
 | 
			
		||||
		gbc_lblUserName.anchor	= GridBagConstraints.EAST;
 | 
			
		||||
		gbc_lblUserName.insets	= new Insets(0, 0, 5, 5);
 | 
			
		||||
		gbc_lblUserName.gridx	= 0;
 | 
			
		||||
		gbc_lblUserName.gridy	= 0;
 | 
			
		||||
		contentPanel.add(lblUserName, gbc_lblUserName);
 | 
			
		||||
 | 
			
		||||
		textField = new JTextField();
 | 
			
		||||
		textField.setBorder(null);
 | 
			
		||||
		GridBagConstraints gbc_textField = new GridBagConstraints();
 | 
			
		||||
		gbc_textField.insets	= new Insets(0, 0, 5, 0);
 | 
			
		||||
		gbc_textField.fill		= GridBagConstraints.HORIZONTAL;
 | 
			
		||||
		gbc_textField.gridx		= 1;
 | 
			
		||||
		gbc_textField.gridy		= 0;
 | 
			
		||||
		contentPanel.add(textField, gbc_textField);
 | 
			
		||||
		textField.setColumns(10);
 | 
			
		||||
 | 
			
		||||
		lblPassword = new JLabel("Password:");
 | 
			
		||||
		GridBagConstraints gbc_lblPassword = new GridBagConstraints();
 | 
			
		||||
		gbc_lblPassword.anchor	= GridBagConstraints.EAST;
 | 
			
		||||
		gbc_lblPassword.insets	= new Insets(0, 0, 0, 5);
 | 
			
		||||
		gbc_lblPassword.gridx	= 0;
 | 
			
		||||
		gbc_lblPassword.gridy	= 1;
 | 
			
		||||
		contentPanel.add(lblPassword, gbc_lblPassword);
 | 
			
		||||
 | 
			
		||||
		passwordField = new JPasswordField();
 | 
			
		||||
		passwordField.setBorder(null);
 | 
			
		||||
		GridBagConstraints gbc_passwordField = new GridBagConstraints();
 | 
			
		||||
		gbc_passwordField.fill	= GridBagConstraints.HORIZONTAL;
 | 
			
		||||
		gbc_passwordField.gridx	= 1;
 | 
			
		||||
		gbc_passwordField.gridy	= 1;
 | 
			
		||||
		contentPanel.add(passwordField, gbc_passwordField);
 | 
			
		||||
 | 
			
		||||
		lblRepeatPassword				= new JLabel("Repeat Password:");
 | 
			
		||||
		gbc_lblRepeatPassword			= new GridBagConstraints();
 | 
			
		||||
		gbc_lblRepeatPassword.anchor	= GridBagConstraints.EAST;
 | 
			
		||||
		gbc_lblRepeatPassword.insets	= new Insets(0, 0, 0, 5);
 | 
			
		||||
		gbc_lblRepeatPassword.gridx		= 0;
 | 
			
		||||
		gbc_lblRepeatPassword.gridy		= 2;
 | 
			
		||||
 | 
			
		||||
		repeatPasswordField				= new JPasswordField();
 | 
			
		||||
		gbc_repeatPasswordField			= new GridBagConstraints();
 | 
			
		||||
		gbc_repeatPasswordField.fill	= GridBagConstraints.HORIZONTAL;
 | 
			
		||||
		gbc_repeatPasswordField.gridx	= 1;
 | 
			
		||||
		gbc_repeatPasswordField.gridy	= 2;
 | 
			
		||||
 | 
			
		||||
		errorMessage			= new JLabel();
 | 
			
		||||
		gbc_errorMessage		= new GridBagConstraints();
 | 
			
		||||
		gbc_errorMessage.gridx	= 1;
 | 
			
		||||
		gbc_errorMessage.gridy	= 3;
 | 
			
		||||
		gbc_errorMessage.fill	= GridBagConstraints.HORIZONTAL;
 | 
			
		||||
		gbc_errorMessage.insets	= new Insets(5, 5, 5, 5);
 | 
			
		||||
		errorMessage.setForeground(Color.RED);
 | 
			
		||||
		errorMessage.setVisible(false);
 | 
			
		||||
		contentPanel.add(errorMessage, gbc_errorMessage);
 | 
			
		||||
 | 
			
		||||
		buttonPane = new JPanel();
 | 
			
		||||
 | 
			
		||||
		registerText = new JTextPane();
 | 
			
		||||
		registerText.setEditable(false);
 | 
			
		||||
		registerText.setText("Register?");
 | 
			
		||||
		registerText.setFont(new Font("Arial", Font.BOLD, 12));
 | 
			
		||||
		registerText.setAlignmentX(LEFT_ALIGNMENT);
 | 
			
		||||
		buttonPane.add(registerText);
 | 
			
		||||
 | 
			
		||||
		registerCheckBox = new JCheckBox();
 | 
			
		||||
		registerCheckBox.setAlignmentX(LEFT_ALIGNMENT);
 | 
			
		||||
		registerCheckBox.addItemListener(e -> {
 | 
			
		||||
			switch (e.getStateChange()) {
 | 
			
		||||
				case ItemEvent.SELECTED:
 | 
			
		||||
					contentPanel.add(lblRepeatPassword, gbc_lblRepeatPassword);
 | 
			
		||||
					contentPanel.add(repeatPasswordField, gbc_repeatPasswordField);
 | 
			
		||||
					setSize(338, 173);
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				case ItemEvent.DESELECTED:
 | 
			
		||||
					if (repeatPasswordField.getParent() == contentPanel) {
 | 
			
		||||
						contentPanel.remove(lblRepeatPassword);
 | 
			
		||||
						contentPanel.remove(repeatPasswordField);
 | 
			
		||||
						setSize(338, 148);
 | 
			
		||||
					}
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
			contentPanel.revalidate();
 | 
			
		||||
			contentPanel.repaint();
 | 
			
		||||
		});
 | 
			
		||||
		buttonPane.add(registerCheckBox);
 | 
			
		||||
 | 
			
		||||
		buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
 | 
			
		||||
		getContentPane().add(buttonPane, BorderLayout.SOUTH);
 | 
			
		||||
		okButton = new PrimaryButton("OK");
 | 
			
		||||
		okButton.setActionCommand("OK");
 | 
			
		||||
		buttonPane.add(okButton);
 | 
			
		||||
		getRootPane().setDefaultButton(okButton);
 | 
			
		||||
 | 
			
		||||
		cancelButton = new PrimaryButton("Cancel");
 | 
			
		||||
		cancelButton.setActionCommand("Cancel");
 | 
			
		||||
		buttonPane.add(cancelButton);
 | 
			
		||||
		setTheme();
 | 
			
		||||
 | 
			
		||||
		setModalityType(Dialog.DEFAULT_MODALITY_TYPE);
 | 
			
		||||
 | 
			
		||||
		EventBus.getInstance().register(HandshakeSuccessfulEvent.class, evt -> dispose());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Resets the text stored in the password fields.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @since Envoy Client v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	private void clearPasswordFields() {
 | 
			
		||||
		passwordField.setText(null);
 | 
			
		||||
		repeatPasswordField.setText(null);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void setTheme() {
 | 
			
		||||
		Theme theme = Settings.getInstance().getCurrentTheme();
 | 
			
		||||
 | 
			
		||||
		// Panels
 | 
			
		||||
		contentPanel.setBackground(theme.getBackgroundColor());
 | 
			
		||||
		contentPanel.setForeground(theme.getBackgroundColor());
 | 
			
		||||
 | 
			
		||||
		buttonPane.setBackground(theme.getBackgroundColor());
 | 
			
		||||
		buttonPane.setForeground(theme.getBackgroundColor());
 | 
			
		||||
 | 
			
		||||
		// Input Fields
 | 
			
		||||
		textField.setBackground(theme.getCellColor());
 | 
			
		||||
		textField.setForeground(theme.getUserNameColor());
 | 
			
		||||
 | 
			
		||||
		passwordField.setBackground(theme.getCellColor());
 | 
			
		||||
		passwordField.setForeground(theme.getUserNameColor());
 | 
			
		||||
 | 
			
		||||
		repeatPasswordField.setBackground(theme.getCellColor());
 | 
			
		||||
		repeatPasswordField.setForeground(theme.getUserNameColor());
 | 
			
		||||
 | 
			
		||||
		// JLabels
 | 
			
		||||
		lblUserName.setBackground(theme.getCellColor());
 | 
			
		||||
		lblUserName.setForeground(theme.getUserNameColor());
 | 
			
		||||
 | 
			
		||||
		lblPassword.setBackground(theme.getCellColor());
 | 
			
		||||
		lblPassword.setForeground(theme.getUserNameColor());
 | 
			
		||||
 | 
			
		||||
		lblRepeatPassword.setBackground(theme.getCellColor());
 | 
			
		||||
		lblRepeatPassword.setForeground(theme.getUserNameColor());
 | 
			
		||||
 | 
			
		||||
		// Register
 | 
			
		||||
		registerText.setBackground(theme.getCellColor());
 | 
			
		||||
		registerText.setForeground(theme.getUserNameColor());
 | 
			
		||||
 | 
			
		||||
		registerCheckBox.setBackground(theme.getCellColor());
 | 
			
		||||
 | 
			
		||||
		// Buttons
 | 
			
		||||
		okButton.setBackground(theme.getInteractableBackgroundColor());
 | 
			
		||||
		okButton.setForeground(theme.getInteractableForegroundColor());
 | 
			
		||||
 | 
			
		||||
		cancelButton.setBackground(theme.getInteractableBackgroundColor());
 | 
			
		||||
		cancelButton.setForeground(theme.getInteractableForegroundColor());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Shuts the system down properly if the login was aborted.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	private void abortLogin() {
 | 
			
		||||
		logger.info("The login process has been cancelled. Exiting...");
 | 
			
		||||
		System.exit(0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,13 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * This package contains all graphical Containers, like Dialogs and Frames.<br>
 | 
			
		||||
 * <br>
 | 
			
		||||
 * Project: <strong>envoy-client</strong><br>
 | 
			
		||||
 * File: <strong>package-info.java</strong><br>
 | 
			
		||||
 * Created: <strong>16 Mar 2020</strong><br>
 | 
			
		||||
 *
 | 
			
		||||
 * @author Leon Hofmeister
 | 
			
		||||
 * @author Kai S. K. Engelbart
 | 
			
		||||
 * @author Maximilian Käfer
 | 
			
		||||
 * @since Envoy Client v0.1-beta
 | 
			
		||||
 */
 | 
			
		||||
package envoy.client.ui.container;
 | 
			
		||||
		Reference in New Issue
	
	Block a user