package envoy.client.ui.settings; import java.io.*; import java.nio.file.Files; import java.util.logging.*; import javafx.event.EventHandler; import javafx.geometry.Pos; import javafx.scene.Cursor; import javafx.scene.control.*; import javafx.scene.control.Alert.AlertType; import javafx.scene.image.*; import javafx.scene.input.InputEvent; import javafx.scene.layout.HBox; import javafx.stage.FileChooser; import javafx.util.Duration; import dev.kske.eventbus.core.EventBus; import envoy.event.*; import envoy.util.*; import envoy.client.ui.control.ProfilePicImageView; import envoy.client.util.*; /** * @author Leon Hofmeister * @since Envoy Client v0.2-beta */ public final class UserSettingsPane extends OnlineOnlySettingsPane { private boolean profilePicChanged, usernameChanged, validPassword; private byte[] currentImageBytes; private String newUsername, newPassword = ""; private final ImageView profilePic = new ProfilePicImageView(null, 60); private final TextField usernameTextField = new TextField(); private final PasswordField currentPasswordField = new PasswordField(); private final PasswordField newPasswordField = new PasswordField(); private final PasswordField repeatNewPasswordField = new PasswordField(); private final Button saveButton = new Button("Save"); private final Button deleteAccountButton = new Button("Delete Account (Locally)"); private static final EventBus eventBus = EventBus.getInstance(); private static final Logger logger = EnvoyLog.getLogger(UserSettingsPane.class); /** * Creates a new {@code UserSettingsPane}. * * @since Envoy Client v0.2-beta */ public UserSettingsPane() { super("User"); setSpacing(10); // Display of profile pic change mechanism final var hbox = new HBox(); // TODO: display current profile pic profilePic.setImage(IconUtil.loadIconThemeSensitive("user_icon")); profilePic.setCursor(Cursor.HAND); profilePic.setFitWidth(60); profilePic.setFitHeight(60); profilePic.setOnMouseClicked(e -> { if (!client.isOnline()) return; final var pictureChooser = new FileChooser(); pictureChooser.setTitle("Select a new profile pic"); pictureChooser.setInitialDirectory(new File(System.getProperty("user.home"))); pictureChooser.getExtensionFilters().add( new FileChooser.ExtensionFilter("Pictures", "*.png", "*.jpg", "*.bmp", "*.gif")); final var file = pictureChooser.showOpenDialog(context.getSceneContext().getStage()); if (file != null) { // Check max file size // TODO: Move to config if (file.length() > 5E6) { new Alert(AlertType.WARNING, "The selected file exceeds the size limit of 5MB!") .showAndWait(); return; } try { currentImageBytes = Files.readAllBytes(file.toPath()); profilePic.setImage(new Image(new ByteArrayInputStream(currentImageBytes))); profilePicChanged = true; } catch (final IOException e1) { e1.printStackTrace(); } } }); hbox.getChildren().add(profilePic); // Displaying the username change mechanism final var username = client.getSender().getName(); newUsername = username; usernameTextField.setText(username); final EventHandler textChanged = e -> { newUsername = usernameTextField.getText(); usernameChanged = newUsername != username; }; usernameTextField.setOnInputMethodTextChanged(textChanged); usernameTextField.setOnKeyTyped(textChanged); hbox.getChildren().add(usernameTextField); getChildren().add(hbox); // "Displaying" the password change mechanism final HBox[] passwordHBoxes = { new HBox(), new HBox(), new HBox() }; final Label[] passwordLabels = { new Label("Enter current password:"), new Label("Enter new password:"), new Label("Repeat new password:") }; final PasswordField[] passwordFields = { currentPasswordField, newPasswordField, repeatNewPasswordField }; final EventHandler passwordEntered = e -> { newPassword = newPasswordField .getText(); validPassword = newPassword.equals( repeatNewPasswordField .getText()) && !newPasswordField .getText() .isBlank(); }; newPasswordField.setOnInputMethodTextChanged(passwordEntered); newPasswordField.setOnKeyTyped(passwordEntered); repeatNewPasswordField.setOnInputMethodTextChanged(passwordEntered); repeatNewPasswordField.setOnKeyTyped(passwordEntered); for (int i = 0; i < passwordHBoxes.length; i++) { final var hBox2 = passwordHBoxes[i]; passwordLabels[i].setWrapText(true); hBox2.getChildren().add(passwordLabels[i]); hBox2.getChildren().add(passwordFields[i]); getChildren().add(hBox2); } // Displaying the save button saveButton .setOnAction(e -> save(currentPasswordField.getText())); saveButton.setAlignment(Pos.BOTTOM_RIGHT); getChildren().add(saveButton); // Displaying the delete account button deleteAccountButton.setAlignment(Pos.BASELINE_CENTER); deleteAccountButton.setOnAction(e -> UserUtil.deleteAccount()); deleteAccountButton.setText("Delete Account (locally)"); deleteAccountButton.setPrefHeight(25); deleteAccountButton.getStyleClass().clear(); deleteAccountButton.getStyleClass().add("danger-button"); final var tooltip = new Tooltip("Remote deletion is currently unsupported."); tooltip.setShowDelay(Duration.millis(100)); deleteAccountButton.setTooltip(tooltip); getChildren().add(deleteAccountButton); } /** * Saves the given input and sends the changed input to the server * * @param username the new username * @since Envoy Client v0.2-beta */ private void save(String oldPassword) { // The profile pic was changed if (profilePicChanged) { final var profilePicChangeEvent = new ProfilePicChange(currentImageBytes); eventBus.dispatch(profilePicChangeEvent); client.send(profilePicChangeEvent); logger.log(Level.INFO, "The user just changed his profile pic."); } // The username was changed final var validContactName = Bounds.isValidContactName(newUsername); if (usernameChanged && validContactName) { final var nameChangeEvent = new NameChange(client.getSender().getID(), newUsername); eventBus.dispatch(nameChangeEvent); client.send(nameChangeEvent); logger.log(Level.INFO, "The user just changed his name to " + newUsername + "."); } else if (!validContactName) { final var alert = new Alert(AlertType.ERROR); alert.setTitle("Invalid username"); alert.setContentText( "The entered username does not conform with the naming limitations: " + Bounds.CONTACT_NAME_PATTERN); alert.showAndWait(); logger.log(Level.INFO, "An invalid username was requested."); return; } // The password was changed if (validPassword) { client.send(new PasswordChangeRequest(newPassword, oldPassword)); logger.log(Level.INFO, "The user just tried to change his password!"); } else if (!(validPassword || newPassword.isBlank())) { final var alert = new Alert(AlertType.ERROR); alert.setTitle("Unequal Password"); alert.setContentText("Repeated password is unequal to the chosen new password"); alert.showAndWait(); } } }