This repository has been archived on 2021-12-05. You can view files and clone it, but cannot push or open issues or pull requests.
envoy/client/src/main/java/envoy/client/ui/controller/LoginScene.java

232 lines
7.3 KiB
Java

package envoy.client.ui.controller;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.time.Instant;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.control.Alert.AlertType;
import envoy.client.data.*;
import envoy.client.net.Client;
import envoy.client.net.WriteProxy;
import envoy.client.ui.*;
import envoy.data.LoginCredentials;
import envoy.data.User;
import envoy.data.User.UserStatus;
import envoy.event.EventBus;
import envoy.event.HandshakeRejection;
import envoy.exception.EnvoyException;
import envoy.util.Bounds;
import envoy.util.EnvoyLog;
/**
* Project: <strong>envoy-client</strong><br>
* File: <strong>LoginDialog.java</strong><br>
* Created: <strong>03.04.2020</strong><br>
*
* @author Kai S. K. Engelbart
* @author Maximilian K&auml;fer
* @since Envoy Client v0.1-beta
*/
public final class LoginScene {
@FXML
private ClearableTextField userTextField;
@FXML
private PasswordField passwordField;
@FXML
private PasswordField repeatPasswordField;
@FXML
private Label repeatPasswordLabel;
@FXML
private CheckBox registerCheckBox;
@FXML
private Label connectionLabel;
private Client client;
private LocalDB localDB;
private CacheMap cacheMap;
private SceneContext sceneContext;
private static final Logger logger = EnvoyLog.getLogger(LoginScene.class);
private static final EventBus eventBus = EventBus.getInstance();
private static final ClientConfig config = ClientConfig.getInstance();
private static final Settings settings = Settings.getInstance();
@FXML
private void initialize() {
connectionLabel.setText("Server: " + config.getServer() + ":" + config.getPort());
// Show an alert after an unsuccessful handshake
eventBus.register(HandshakeRejection.class, e -> Platform.runLater(() -> { 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 cacheMap the map of all caches needed
* @param sceneContext the scene context used to initialize the chat scene
* @since Envoy Client v0.1-beta
*/
public void initializeData(Client client, LocalDB localDB, CacheMap cacheMap, SceneContext sceneContext) {
this.client = client;
this.localDB = localDB;
this.cacheMap = cacheMap;
this.sceneContext = sceneContext;
// Prepare handshake
localDB.loadIDGenerator();
// Set initial cursor
userTextField.requestFocus();
// Perform automatic login if configured
if (config.hasLoginCredentials())
performHandshake(new LoginCredentials(config.getUser(), config.getPassword(), false, Startup.VERSION, loadLastSync(config.getUser())));
}
@FXML
private void loginButtonPressed() {
// Prevent registration with unequal passwords
if (registerCheckBox.isSelected() && !passwordField.getText().equals(repeatPasswordField.getText())) {
new Alert(AlertType.ERROR, "The entered password is unequal to the repeated one").showAndWait();
repeatPasswordField.clear();
} else if (!Bounds.isValidContactName(userTextField.getTextField().getText())) {
new Alert(AlertType.ERROR, "The entered user name is not valid (" + Bounds.CONTACT_NAME_PATTERN + ")").showAndWait();
userTextField.getTextField().clear();
} else performHandshake(new LoginCredentials(userTextField.getTextField().getText(), passwordField.getText(), registerCheckBox.isSelected(),
Startup.VERSION, loadLastSync(userTextField.getTextField().getText())));
}
@FXML
private void offlineModeButtonPressed() {
attemptOfflineMode(
new LoginCredentials(userTextField.getTextField().getText(), passwordField.getText(), false, Startup.VERSION, localDB.getLastSync()));
}
@FXML
private void registerCheckboxChanged() {
// Make repeat password field and label visible / invisible
repeatPasswordField.setVisible(registerCheckBox.isSelected());
repeatPasswordLabel.setVisible(registerCheckBox.isSelected());
}
@FXML
private void abortLogin() {
logger.log(Level.INFO, "The login process has been cancelled. Exiting...");
System.exit(0);
}
private Instant loadLastSync(String identifier) {
try {
localDB.loadUsers();
localDB.setUser(localDB.getUsers().get(identifier));
localDB.initializeUserStorage();
localDB.loadUserData();
} catch (final Exception e) {
// User storage empty, wrong user name etc. -> default lastSync
}
return localDB.getLastSync();
}
private void performHandshake(LoginCredentials credentials) {
try {
client.performHandshake(credentials, cacheMap);
if (client.isOnline()) {
loadChatScene();
client.initReceiver(localDB, cacheMap);
}
} catch (IOException | InterruptedException | TimeoutException e) {
logger.log(Level.INFO, "Could not connect to server. Entering offline mode...");
attemptOfflineMode(credentials);
}
}
private void attemptOfflineMode(LoginCredentials credentials) {
try {
// Try entering offline mode
localDB.loadUsers();
final User clientUser = localDB.getUsers().get(credentials.getIdentifier());
if (clientUser == null) throw new EnvoyException("Could not enter offline mode: user name unknown");
client.setSender(clientUser);
loadChatScene();
} catch (final Exception e) {
new Alert(AlertType.ERROR, "Client error: " + e).showAndWait();
logger.log(Level.SEVERE, "Offline mode could not be loaded: ", e);
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) {
new Alert(AlertType.ERROR, "Error while loading local database: " + e + "\nChats will not be stored locally.").showAndWait();
logger.log(Level.WARNING, "Could not load local database: ", e);
}
// Initialize write proxy
final var writeProxy = new WriteProxy(client, localDB);
localDB.synchronize();
if (client.isOnline()) writeProxy.flushCache();
else
// Set all contacts to offline mode
localDB.getChats()
.stream()
.map(Chat::getRecipient)
.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);
if (StatusTrayIcon.isSupported()) {
// Configure hide on close
sceneContext.getStage().setOnCloseRequest(e -> {
if (settings.isHideOnClose()) {
sceneContext.getStage().setIconified(true);
e.consume();
}
});
// Initialize status tray icon
final var trayIcon = new StatusTrayIcon(sceneContext.getStage());
settings.getItems().get("hideOnClose").setChangeHandler(c -> {
if ((Boolean) c) trayIcon.show();
else trayIcon.hide();
});
}
}
}