package envoy.client.ui.controller; import static java.util.function.Predicate.not; import java.util.stream.Collectors; import javafx.application.Platform; import javafx.fxml.FXML; import javafx.scene.control.*; import javafx.scene.input.MouseEvent; import javafx.scene.layout.HBox; import envoy.client.data.*; import envoy.client.event.BackEvent; import envoy.client.ui.control.*; import envoy.client.ui.listcell.ListCellFactory; import envoy.data.*; import envoy.event.GroupCreation; import envoy.event.contact.ContactOperation; import envoy.util.Bounds; import dev.kske.eventbus.*; /** * Provides a group creation interface. A group name can be entered in the text * field at the top. Available users (local chat recipients) are displayed * inside a list and can be selected (multiple selection available). *

* When the group creation button is pressed, a {@link GroupCreation} is sent to * the server. This controller enforces a valid group name and a non-empty * member list (excluding the client user). * * @author Maximilian Käfer * @since Envoy Client v0.1-beta */ public class GroupCreationTab implements EventListener { @FXML private Button createButton; @FXML private Button cancelButton; @FXML private TextArea groupNameField; @FXML private ListView userList; @FXML private Label errorMessageLabel; @FXML private Button proceedDuplicateButton; @FXML private Button cancelDuplicateButton; @FXML private HBox errorProceedBox; @FXML private ListView quickSelectList; private String name; private final LocalDB localDB = Context.getInstance().getLocalDB(); private static final EventBus eventBus = EventBus.getInstance(); @FXML private void initialize() { userList.setCellFactory(new ListCellFactory<>(ContactControl::new)); createButton.setDisable(true); eventBus.registerListener(this); userList.getItems() .addAll(localDB.getChats() .stream() .map(Chat::getRecipient) .filter(User.class::isInstance) .filter(not(localDB.getUser()::equals)) .map(User.class::cast) .collect(Collectors.toList())); resizeQuickSelectSpace(0); quickSelectList.addEventFilter(MouseEvent.MOUSE_PRESSED, evt -> evt.consume()); } /** * Enables the {@code createButton} if at least one contact is selected. * * @since Envoy Client v0.1-beta */ @FXML private void userListClicked() { if (userList.getSelectionModel().getSelectedItem() != null) { quickSelectList.getItems().add(new QuickSelectControl(userList.getSelectionModel().getSelectedItem(), this::removeFromQuickSelection)); createButton.setDisable(quickSelectList.getItems().isEmpty() || groupNameField.getText().isBlank()); resizeQuickSelectSpace(60); userList.getItems().remove(userList.getSelectionModel().getSelectedItem()); userList.getSelectionModel().clearSelection(); } } /** * Checks, whether the {@code createButton} can be enabled because text is * present in the text field. * * @since Envoy Client v0.1-beta */ @FXML private void textUpdated() { createButton.setDisable(quickSelectList.getItems().isEmpty() || groupNameField.getText().isBlank()); } /** * Sends a {@link GroupCreation} to the server and closes this scene. *

* If the given group name is not valid, an error is displayed instead. * * @since Envoy Client v0.1-beta */ @FXML private void createButtonClicked() { name = groupNameField.getText(); if (!Bounds.isValidContactName(name)) { setErrorMessageLabelSize(30); errorMessageLabel.setText("The group name is not valid!"); groupNameField.clear(); } else if (groupNameAlreadyPresent(name)) { setErrorMessageLabelSize(30); errorMessageLabel.setText("Name does already exist! Proceed anyways?"); setProcessPaneSize(30); createButton.setDisable(true); cancelButton.setDisable(true); } else { createGroup(name); eventBus.dispatch(new BackEvent()); // Restoring the original design as tabs will always be reused setErrorMessageLabelSize(0); groupNameField.clear(); quickSelectList.getItems().forEach(q -> userList.getItems().add(q.getUser())); quickSelectList.getItems().clear(); resizeQuickSelectSpace(0); } } /** * Creates a new group with the given name and all selected members.
* Additionally pops the scene automatically. * * @param name the chosen group name * @since Envoy Client v0.1-beta */ private void createGroup(String name) { Context.getInstance() .getClient() .send(new GroupCreation(name, quickSelectList.getItems().stream().map(q -> q.getUser().getID()).collect(Collectors.toSet()))); } /** * Returns true if the proposed group name is already present in the users * {@code LocalDB}. * * @param newName the chosen group name * @return true if this name is already present * @since Envoy Client v0.1-beta */ public boolean groupNameAlreadyPresent(String newName) { return localDB.getChats().stream().map(Chat::getRecipient).filter(Group.class::isInstance).map(Contact::getName).anyMatch(newName::equals); } /** * Removes an element from the quickSelectList. * * @param element the element to be removed. * @since Envoy Client v0.3-beta */ public void removeFromQuickSelection(QuickSelectControl element) { quickSelectList.getItems().remove(element); userList.getItems().add(element.getUser()); if (quickSelectList.getItems().isEmpty()) { resizeQuickSelectSpace(0); createButton.setDisable(true); } } private void resizeQuickSelectSpace(int value) { quickSelectList.setPrefHeight(value); quickSelectList.setMaxHeight(value); quickSelectList.setMinHeight(value); } @FXML private void backButtonClicked() { eventBus.dispatch(new BackEvent()); setErrorMessageLabelSize(0); setProcessPaneSize(0); } @FXML private void proceedOnNameDuplication() { createButton.setDisable(false); cancelButton.setDisable(false); createGroup(name); eventBus.dispatch(new BackEvent()); setErrorMessageLabelSize(0); setProcessPaneSize(0); groupNameField.clear(); } @FXML private void cancelOnNameDuplication() { createButton.setDisable(false); cancelButton.setDisable(false); setErrorMessageLabelSize(0); setProcessPaneSize(0); groupNameField.clear(); } private void setErrorMessageLabelSize(int value) { errorMessageLabel.setPrefHeight(value); errorMessageLabel.setMinHeight(value); errorMessageLabel.setMaxHeight(value); } private void setProcessPaneSize(int value) { proceedDuplicateButton.setPrefHeight(value); proceedDuplicateButton.setMinHeight(value); proceedDuplicateButton.setMaxHeight(value); cancelDuplicateButton.setPrefHeight(value); cancelDuplicateButton.setMinHeight(value); cancelDuplicateButton.setMaxHeight(value); errorProceedBox.setPrefHeight(value); errorProceedBox.setMinHeight(value); errorProceedBox.setMaxHeight(value); } @Event private void onContactOperation(ContactOperation operation) { if (operation.get() instanceof User) Platform.runLater(() -> { switch (operation.getOperationType()) { case ADD: userList.getItems().add((User) operation.get()); break; case REMOVE: userList.getItems().removeIf(operation.get()::equals); break; } }); } }