Merge pull request #132 from informatik-ag-ngl/f/login_dialog
JavaFX LoginDialog
This commit is contained in:
commit
488d878a0c
@ -1,7 +1,6 @@
|
|||||||
package envoy.client.data;
|
package envoy.client.data;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
@ -110,11 +109,5 @@ public class ClientConfig extends Config {
|
|||||||
* the registration option
|
* the registration option
|
||||||
* @since Envoy Client v0.3-alpha
|
* @since Envoy Client v0.3-alpha
|
||||||
*/
|
*/
|
||||||
public LoginCredentials getLoginCredentials() {
|
public LoginCredentials getLoginCredentials() { return new LoginCredentials(getUser(), getPassword(), false); }
|
||||||
try {
|
|
||||||
return new LoginCredentials(getUser(), getPassword(), false);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
|
@ -46,9 +46,10 @@ public class Client implements Closeable {
|
|||||||
private volatile Set<? extends Contact> contacts;
|
private volatile Set<? extends Contact> contacts;
|
||||||
private volatile boolean rejected;
|
private volatile boolean rejected;
|
||||||
|
|
||||||
// Configuration and logging
|
// Configuration, logging and event management
|
||||||
private static final ClientConfig config = ClientConfig.getInstance();
|
private static final ClientConfig config = ClientConfig.getInstance();
|
||||||
private static final Logger logger = EnvoyLog.getLogger(Client.class);
|
private static final Logger logger = EnvoyLog.getLogger(Client.class);
|
||||||
|
private static final EventBus eventBus = EventBus.getInstance();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enters the online mode by acquiring a user ID from the server. As a
|
* Enters the online mode by acquiring a user ID from the server. As a
|
||||||
@ -80,7 +81,7 @@ public class Client implements Closeable {
|
|||||||
// Register user creation processor, contact list processor and message cache
|
// Register user creation processor, contact list processor and message cache
|
||||||
receiver.registerProcessor(User.class, sender -> { this.sender = sender; contacts = sender.getContacts(); });
|
receiver.registerProcessor(User.class, sender -> { this.sender = sender; contacts = sender.getContacts(); });
|
||||||
receiver.registerProcessor(Message.class, receivedMessageCache);
|
receiver.registerProcessor(Message.class, receivedMessageCache);
|
||||||
receiver.registerProcessor(HandshakeRejectionEvent.class, evt -> { rejected = true; EventBus.getInstance().dispatch(evt); });
|
receiver.registerProcessor(HandshakeRejectionEvent.class, evt -> { rejected = true; eventBus.dispatch(evt); });
|
||||||
|
|
||||||
rejected = false;
|
rejected = false;
|
||||||
|
|
||||||
@ -106,11 +107,12 @@ public class Client implements Closeable {
|
|||||||
Thread.sleep(500);
|
Thread.sleep(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Handshake completed.");
|
|
||||||
online = true;
|
online = true;
|
||||||
|
|
||||||
// Remove user creation processor
|
// Remove user creation processor
|
||||||
receiver.removeAllProcessors();
|
receiver.removeAllProcessors();
|
||||||
|
|
||||||
|
logger.info("Handshake completed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -146,20 +148,19 @@ public class Client implements Closeable {
|
|||||||
receiver.registerProcessor(IDGenerator.class, localDB::setIDGenerator);
|
receiver.registerProcessor(IDGenerator.class, localDB::setIDGenerator);
|
||||||
|
|
||||||
// Process name changes
|
// Process name changes
|
||||||
receiver.registerProcessor(NameChangeEvent.class, evt -> { localDB.replaceContactName(evt); EventBus.getInstance().dispatch(evt); });
|
receiver.registerProcessor(NameChangeEvent.class, evt -> { localDB.replaceContactName(evt); eventBus.dispatch(evt); });
|
||||||
|
|
||||||
// Process contact searches
|
// Process contact searches
|
||||||
receiver.registerProcessor(ContactSearchResult.class, EventBus.getInstance()::dispatch);
|
receiver.registerProcessor(ContactSearchResult.class, eventBus::dispatch);
|
||||||
|
|
||||||
receiver.registerProcessor(Contact.class,
|
receiver.registerProcessor(Contact.class,
|
||||||
contacts -> EventBus.getInstance()
|
contacts -> eventBus.dispatch(new ContactOperationEvent(contacts.getContacts().iterator().next(), ElementOperation.ADD)));
|
||||||
.dispatch(new ContactOperationEvent(contacts.getContacts().iterator().next(), ElementOperation.ADD)));
|
|
||||||
|
|
||||||
// Process group size changes
|
// Process group size changes
|
||||||
receiver.registerProcessor(GroupResizeEvent.class, evt -> { localDB.updateGroup(evt); EventBus.getInstance().dispatch(evt); });
|
receiver.registerProcessor(GroupResizeEvent.class, evt -> { localDB.updateGroup(evt); eventBus.dispatch(evt); });
|
||||||
|
|
||||||
// Send event
|
// Send event
|
||||||
EventBus.getInstance().register(SendEvent.class, evt -> {
|
eventBus.register(SendEvent.class, evt -> {
|
||||||
try {
|
try {
|
||||||
sendEvent(evt.get());
|
sendEvent(evt.get());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
62
src/main/java/envoy/client/ui/LoginDialog.fxml
Normal file
62
src/main/java/envoy/client/ui/LoginDialog.fxml
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?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?>
|
||||||
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
|
<?import javafx.scene.layout.GridPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<CheckBox fx:id="registerCheckBox" mnemonicParsing="false"
|
||||||
|
onAction="#registerCheckboxChanged" text="Register" />
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</content>
|
||||||
|
</DialogPane>
|
162
src/main/java/envoy/client/ui/LoginDialog.java
Normal file
162
src/main/java/envoy/client/ui/LoginDialog.java
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
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.event.ActionEvent;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.scene.control.*;
|
||||||
|
import javafx.scene.control.Alert.AlertType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Project: <strong>envoy-client</strong><br>
|
||||||
|
* File: <strong>LoginDialog.java</strong><br>
|
||||||
|
* Created: <strong>03.04.2020</strong><br>
|
||||||
|
*
|
||||||
|
* @author Kai S. K. Engelbart
|
||||||
|
* @since Envoy Client v0.1-beta
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
|
||||||
|
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(Client client, LocalDB localDB, Cache<Message> receivedMessageCache) throws IOException {
|
||||||
|
this.client = client;
|
||||||
|
this.localDB = localDB;
|
||||||
|
this.receivedMessageCache = receivedMessageCache;
|
||||||
|
|
||||||
|
final var loader = new FXMLLoader(getClass().getResource("LoginDialog.fxml"));
|
||||||
|
loader.setController(this);
|
||||||
|
final var dialogPane = loader.<DialogPane>load();
|
||||||
|
|
||||||
|
// 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
|
||||||
|
performHandshake(new LoginCredentials(userTextField.getText(), passwordField.getText().toCharArray(), registerCheckBox.isSelected()));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Perform automatic login if configured
|
||||||
|
setOnShown(e -> { if (config.hasLoginCredentials()) performHandshake(config.getLoginCredentials()); });
|
||||||
|
|
||||||
|
setDialogPane(dialogPane);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void initialize() {
|
||||||
|
|
||||||
|
// Show an alert after an unsuccessful handshake
|
||||||
|
eventBus.register(HandshakeRejectionEvent.class,
|
||||||
|
e -> Platform.runLater(() -> { clearPasswordFields(); new Alert(AlertType.ERROR, e.get()).showAndWait(); }));
|
||||||
|
|
||||||
|
// Set initial cursor
|
||||||
|
userTextField.requestFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void registerCheckboxChanged() {
|
||||||
|
|
||||||
|
// Make repeat password field and label visible / invisible
|
||||||
|
repeatPasswordField.setVisible(registerCheckBox.isSelected());
|
||||||
|
repeatPasswordLabel.setVisible(registerCheckBox.isSelected());
|
||||||
|
|
||||||
|
clearPasswordFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
} 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);
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
new Alert(AlertType.WARNING, "A connection to the server could not be established. Starting in offline mode.\n" + e)
|
||||||
|
.showAndWait();
|
||||||
|
hide();
|
||||||
|
});
|
||||||
|
} catch (Exception e1) {
|
||||||
|
Platform.runLater(() -> new Alert(AlertType.ERROR, "Client error: " + e.toString()).showAndWait());
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearPasswordFields() {
|
||||||
|
passwordField.clear();
|
||||||
|
repeatPasswordField.clear();
|
||||||
|
}
|
||||||
|
}
|
@ -7,12 +7,9 @@ import java.util.Properties;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
|
|
||||||
import envoy.client.data.*;
|
import envoy.client.data.*;
|
||||||
import envoy.client.net.Client;
|
import envoy.client.net.Client;
|
||||||
import envoy.client.net.WriteProxy;
|
import envoy.client.net.WriteProxy;
|
||||||
import envoy.client.ui.container.LoginDialog;
|
|
||||||
import envoy.data.Message;
|
import envoy.data.Message;
|
||||||
import envoy.data.User;
|
import envoy.data.User;
|
||||||
import envoy.data.User.UserStatus;
|
import envoy.data.User.UserStatus;
|
||||||
@ -21,6 +18,8 @@ import envoy.util.EnvoyLog;
|
|||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.control.Alert;
|
||||||
|
import javafx.scene.control.Alert.AlertType;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
@ -47,7 +46,7 @@ public final class Startup extends Application {
|
|||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void init() throws Exception {
|
public void start(Stage stage) throws Exception {
|
||||||
try {
|
try {
|
||||||
// Load the configuration from client.properties first
|
// Load the configuration from client.properties first
|
||||||
Properties properties = new Properties();
|
Properties properties = new Properties();
|
||||||
@ -61,7 +60,7 @@ public final class Startup extends Application {
|
|||||||
// Check if all mandatory configuration values have been initialized
|
// Check if all mandatory configuration values have been initialized
|
||||||
if (!config.isInitialized()) throw new EnvoyException("Configuration is not fully initialized");
|
if (!config.isInitialized()) throw new EnvoyException("Configuration is not fully initialized");
|
||||||
} catch (Exception e) {
|
} 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();
|
e.printStackTrace();
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
@ -75,15 +74,12 @@ public final class Startup extends Application {
|
|||||||
// Initialize the local database
|
// Initialize the local database
|
||||||
if (config.isIgnoreLocalDB()) {
|
if (config.isIgnoreLocalDB()) {
|
||||||
localDB = new TransientLocalDB();
|
localDB = new TransientLocalDB();
|
||||||
JOptionPane.showMessageDialog(null,
|
new Alert(AlertType.WARNING, "Ignoring local database.\nMessages will not be saved!").showAndWait();
|
||||||
"Ignoring local database.\nMessages will not be saved!",
|
|
||||||
"Local database warning",
|
|
||||||
JOptionPane.WARNING_MESSAGE);
|
|
||||||
} else try {
|
} else try {
|
||||||
localDB = new PersistentLocalDB(new File(config.getHomeDirectory(), config.getLocalDB().getPath()));
|
localDB = new PersistentLocalDB(new File(config.getHomeDirectory(), config.getLocalDB().getPath()));
|
||||||
} catch (IOException e3) {
|
} catch (IOException e3) {
|
||||||
logger.log(Level.SEVERE, "Could not initialize local database", 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);
|
System.exit(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -93,7 +89,7 @@ public final class Startup extends Application {
|
|||||||
cache = new Cache<>();
|
cache = new Cache<>();
|
||||||
|
|
||||||
// Try to connect to the server
|
// Try to connect to the server
|
||||||
new LoginDialog(client, localDB, cache);
|
new LoginDialog(client, localDB, cache).showAndWait();
|
||||||
|
|
||||||
// Set client user in local database
|
// Set client user in local database
|
||||||
localDB.setUser(client.getSender());
|
localDB.setUser(client.getSender());
|
||||||
@ -106,10 +102,7 @@ public final class Startup extends Application {
|
|||||||
// The local database file has not yet been created, probably first login
|
// The local database file has not yet been created, probably first login
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
JOptionPane.showMessageDialog(null,
|
new Alert(AlertType.ERROR, "Error while loading local database: " + e + "\nChats will not be stored locally.").showAndWait();
|
||||||
"Error while loading local database: " + e + "\nChats will not be stored locally.",
|
|
||||||
"Local DB error",
|
|
||||||
JOptionPane.WARNING_MESSAGE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize write proxy
|
// Initialize write proxy
|
||||||
@ -128,13 +121,6 @@ public final class Startup extends Application {
|
|||||||
.filter(u -> u instanceof User && u != localDB.getUser())
|
.filter(u -> u instanceof User && u != localDB.getUser())
|
||||||
.map(User.class::cast)
|
.map(User.class::cast)
|
||||||
.forEach(u -> u.setStatus(UserStatus.OFFLINE));
|
.forEach(u -> u.setStatus(UserStatus.OFFLINE));
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void start(Stage stage) throws Exception {
|
|
||||||
|
|
||||||
// Prepare stage and load ChatScene
|
// Prepare stage and load ChatScene
|
||||||
var loader = new FXMLLoader(getClass().getResource("ChatScene.fxml"));
|
var loader = new FXMLLoader(getClass().getResource("ChatScene.fxml"));
|
||||||
|
@ -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