261 lines
7.9 KiB
Java
261 lines
7.9 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.geometry.Insets;
|
|
import javafx.scene.control.*;
|
|
import javafx.scene.control.Alert.AlertType;
|
|
import javafx.scene.image.ImageView;
|
|
|
|
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äfer
|
|
* @since Envoy Client v0.1-beta
|
|
*/
|
|
public final class LoginScene {
|
|
|
|
@FXML
|
|
private TextField userTextField;
|
|
|
|
@FXML
|
|
private PasswordField passwordField;
|
|
|
|
@FXML
|
|
private PasswordField repeatPasswordField;
|
|
|
|
@FXML
|
|
private Button registerSwitch;
|
|
|
|
@FXML
|
|
private Label connectionLabel;
|
|
|
|
@FXML
|
|
private Button loginButton;
|
|
|
|
@FXML
|
|
private Button offlineModeButton;
|
|
|
|
@FXML
|
|
private Label registerTextLabel;
|
|
|
|
@FXML
|
|
private ImageView logo;
|
|
|
|
private Client client;
|
|
private LocalDB localDB;
|
|
private CacheMap cacheMap;
|
|
private SceneContext sceneContext;
|
|
|
|
private boolean registration = false;
|
|
|
|
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(); }));
|
|
|
|
logo.setImage(IconUtil.loadIcon("envoy_logo"));
|
|
}
|
|
|
|
/**
|
|
* 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 (registration && !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.getText())) {
|
|
new Alert(AlertType.ERROR, "The entered user name is not valid (" + Bounds.CONTACT_NAME_PATTERN + ")").showAndWait();
|
|
userTextField.clear();
|
|
} else performHandshake(new LoginCredentials(userTextField.getText(), passwordField.getText(), registration,
|
|
Startup.VERSION, loadLastSync(userTextField.getText())));
|
|
}
|
|
|
|
@FXML
|
|
private void offlineModeButtonPressed() {
|
|
attemptOfflineMode(new LoginCredentials(userTextField.getText(), passwordField.getText(), false, Startup.VERSION,
|
|
loadLastSync(userTextField.getText())));
|
|
}
|
|
|
|
@FXML
|
|
private void registerSwitchPressed() {
|
|
if (!registration) {
|
|
// case if the current mode is login
|
|
loginButton.setText("Register");
|
|
loginButton.setPadding(new Insets(2, 116, 2, 116));
|
|
registerTextLabel.setText("Already an account?");
|
|
registerSwitch.setText("Login");
|
|
} else {
|
|
// case if the current mode is registration
|
|
loginButton.setText("Login");
|
|
loginButton.setPadding(new Insets(2, 125, 2, 125));
|
|
registerTextLabel.setText("No account yet?");
|
|
registerSwitch.setText("Register");
|
|
}
|
|
registration = !registration;
|
|
// Make repeat password field and label visible / invisible
|
|
repeatPasswordField.setVisible(registration);
|
|
offlineModeButton.setDisable(registration);
|
|
}
|
|
|
|
@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(843);
|
|
sceneContext.load(SceneContext.SceneInfo.CHAT_SCENE);
|
|
sceneContext.<ChatScene>getController().initializeData(sceneContext, localDB, client, writeProxy);
|
|
sceneContext.getStage().centerOnScreen();
|
|
|
|
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();
|
|
});
|
|
}
|
|
}
|
|
}
|