diff --git a/client/src/main/java/envoy/client/net/Client.java b/client/src/main/java/envoy/client/net/Client.java
index afa423a..a01468f 100644
--- a/client/src/main/java/envoy/client/net/Client.java
+++ b/client/src/main/java/envoy/client/net/Client.java
@@ -12,7 +12,7 @@ import envoy.client.event.SendEvent;
import envoy.data.*;
import envoy.event.*;
import envoy.event.contact.ContactOperation;
-import envoy.event.contact.ContactSearchResult;
+import envoy.event.contact.UserSearchResult;
import envoy.util.EnvoyLog;
import envoy.util.SerializationUtils;
@@ -147,7 +147,7 @@ public class Client implements Closeable {
receiver.registerProcessor(NameChange.class, evt -> { localDB.replaceContactName(evt); eventBus.dispatch(evt); });
// Process contact searches
- receiver.registerProcessor(ContactSearchResult.class, eventBus::dispatch);
+ receiver.registerProcessor(UserSearchResult.class, eventBus::dispatch);
// Process contact operations
receiver.registerProcessor(ContactOperation.class, eventBus::dispatch);
diff --git a/client/src/main/java/envoy/client/ui/controller/ChatScene.java b/client/src/main/java/envoy/client/ui/controller/ChatScene.java
index fd0e5c8..92f9e83 100644
--- a/client/src/main/java/envoy/client/ui/controller/ChatScene.java
+++ b/client/src/main/java/envoy/client/ui/controller/ChatScene.java
@@ -34,7 +34,7 @@ import envoy.client.net.WriteProxy;
import envoy.client.ui.IconUtil;
import envoy.client.ui.Restorable;
import envoy.client.ui.SceneContext;
-import envoy.client.ui.listcell.ContactListCellFactory;
+import envoy.client.ui.listcell.ChatListCellFactory;
import envoy.client.ui.listcell.MessageControl;
import envoy.client.ui.listcell.MessageListCellFactory;
import envoy.data.*;
@@ -125,7 +125,7 @@ public final class ChatScene implements Restorable {
// Initialize message and user rendering
messageList.setCellFactory(MessageListCellFactory::new);
- chatList.setCellFactory(ContactListCellFactory::new);
+ chatList.setCellFactory(ChatListCellFactory::new);
settingsButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("settings", DEFAULT_ICON_SIZE)));
voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", DEFAULT_ICON_SIZE)));
@@ -151,8 +151,6 @@ public final class ChatScene implements Restorable {
chatList.getItems().remove(chat);
chatList.getItems().add(0, chat);
if (chat.equals(currentChat)) chatList.getSelectionModel().select(0);
- localDB.getChats().remove(chat);
- localDB.getChats().add(0, chat);
});
});
});
@@ -187,13 +185,11 @@ public final class ChatScene implements Restorable {
case ADD:
localDB.getUsers().put(contact.getName(), contact);
Chat chat = contact instanceof User ? new Chat(contact) : new GroupChat(client.getSender(), contact);
- localDB.getChats().add(chat);
Platform.runLater(() -> chatList.getItems().add(chat));
break;
case REMOVE:
localDB.getUsers().remove(contact.getName());
- localDB.getChats().removeIf(c -> c.getRecipient().getID() == contact.getID());
- Platform.runLater(() -> chatList.getItems().removeIf(c -> c.getRecipient().getID() == contact.getID()));
+ Platform.runLater(() -> chatList.getItems().removeIf(c -> c.getRecipient().equals(contact)));
break;
}
});
@@ -233,6 +229,8 @@ public final class ChatScene implements Restorable {
*/
@FXML
private void chatListClicked() {
+ if (chatList.getSelectionModel().isEmpty()) return;
+
final Contact user = chatList.getSelectionModel().getSelectedItem().getRecipient();
if (user != null && (currentChat == null || !user.equals(currentChat.getRecipient()))) {
diff --git a/client/src/main/java/envoy/client/ui/controller/ContactSearchScene.java b/client/src/main/java/envoy/client/ui/controller/ContactSearchScene.java
index a86d79d..b3bf823 100644
--- a/client/src/main/java/envoy/client/ui/controller/ContactSearchScene.java
+++ b/client/src/main/java/envoy/client/ui/controller/ContactSearchScene.java
@@ -2,7 +2,6 @@ package envoy.client.ui.controller;
import java.util.logging.Level;
import java.util.logging.Logger;
-import java.util.stream.Collectors;
import javafx.application.Platform;
import javafx.fxml.FXML;
@@ -11,22 +10,31 @@ import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ListView;
-import envoy.client.data.Chat;
import envoy.client.data.LocalDB;
import envoy.client.event.SendEvent;
import envoy.client.ui.ClearableTextField;
import envoy.client.ui.SceneContext;
import envoy.client.ui.listcell.ContactListCellFactory;
+import envoy.data.User;
import envoy.event.ElementOperation;
import envoy.event.EventBus;
import envoy.event.contact.ContactOperation;
-import envoy.event.contact.ContactSearchRequest;
-import envoy.event.contact.ContactSearchResult;
+import envoy.event.contact.UserSearchRequest;
+import envoy.event.contact.UserSearchResult;
import envoy.util.EnvoyLog;
/**
+ * Provides a search bar in which a user name (substring) can be entered. The
+ * users with a matching name are then displayed inside a list view. A
+ * {@link UserSearchRequest} is sent on every keystroke.
+ *
+ * The actual search algorithm is implemented on the server.
+ *
+ * To create a group, a button is available that loads the
+ * {@link GroupCreationScene}.
+ *
* Project: envoy-client
- * File: ContactSearchSceneController.java
+ * File: ContactSearchScene.java
* Created: 07.06.2020
*
* @author Leon Hofmeister
@@ -38,7 +46,7 @@ public class ContactSearchScene {
private ClearableTextField searchBar;
@FXML
- private ListView chatList;
+ private ListView userList;
private SceneContext sceneContext;
@@ -59,12 +67,12 @@ public class ContactSearchScene {
@FXML
private void initialize() {
- chatList.setCellFactory(ContactListCellFactory::new);
- searchBar.setClearButtonListener(e -> { searchBar.getTextField().clear(); chatList.getItems().clear(); });
- eventBus.register(ContactSearchResult.class,
+ userList.setCellFactory(new ContactListCellFactory<>());
+ searchBar.setClearButtonListener(e -> { searchBar.getTextField().clear(); userList.getItems().clear(); });
+ eventBus.register(UserSearchResult.class,
response -> Platform.runLater(() -> {
- chatList.getItems().clear();
- chatList.getItems().addAll(response.get().stream().map(Chat::new).collect(Collectors.toList()));
+ userList.getItems().clear();
+ userList.getItems().addAll(response.get());
}));
}
@@ -76,8 +84,8 @@ public class ContactSearchScene {
@FXML
private void sendRequest() {
final var text = searchBar.getTextField().getText().strip();
- if (!text.isBlank()) eventBus.dispatch(new SendEvent(new ContactSearchRequest(text)));
- else chatList.getItems().clear();
+ if (!text.isBlank()) eventBus.dispatch(new SendEvent(new UserSearchRequest(text)));
+ else userList.getItems().clear();
}
/**
@@ -89,7 +97,7 @@ public class ContactSearchScene {
@FXML
private void clear() {
searchBar.getTextField().setText(null);
- chatList.getItems().clear();
+ userList.getItems().clear();
}
/**
@@ -100,21 +108,21 @@ public class ContactSearchScene {
*/
@FXML
private void chatListClicked() {
- final var chat = chatList.getSelectionModel().getSelectedItem();
- if (chat != null) {
+ final var user = userList.getSelectionModel().getSelectedItem();
+ if (user != null) {
final var alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("Add Contact to Contact List");
- alert.setHeaderText("Add the user " + chat.getRecipient().getName() + " to your contact list?");
+ alert.setHeaderText("Add the user " + user.getName() + " to your contact list?");
// Normally, this would be total BS (we are already on the FX Thread), however
// it could be proven that the creation of this dialog wrapped in
// Platform.runLater is less error-prone than without it
Platform.runLater(() -> alert.showAndWait().filter(btn -> btn == ButtonType.OK).ifPresent(btn -> {
- final var event = new ContactOperation(chat.getRecipient(), ElementOperation.ADD);
+ final var event = new ContactOperation(user, ElementOperation.ADD);
// Sends the event to the server
eventBus.dispatch(new SendEvent(event));
// Updates the UI
eventBus.dispatch(event);
- logger.log(Level.INFO, "Added contact " + chat.getRecipient());
+ logger.log(Level.INFO, "Added user " + user);
}));
}
}
diff --git a/client/src/main/java/envoy/client/ui/controller/GroupCreationScene.java b/client/src/main/java/envoy/client/ui/controller/GroupCreationScene.java
index e7bbf93..3bf4265 100644
--- a/client/src/main/java/envoy/client/ui/controller/GroupCreationScene.java
+++ b/client/src/main/java/envoy/client/ui/controller/GroupCreationScene.java
@@ -1,5 +1,7 @@
package envoy.client.ui.controller;
+import static java.util.function.Predicate.not;
+
import java.util.stream.Collectors;
import javafx.application.Platform;
@@ -13,14 +15,22 @@ import envoy.client.event.SendEvent;
import envoy.client.ui.ClearableTextField;
import envoy.client.ui.SceneContext;
import envoy.client.ui.listcell.ContactListCellFactory;
-import envoy.data.Group;
+import envoy.data.User;
import envoy.event.EventBus;
import envoy.event.GroupCreation;
import envoy.util.Bounds;
/**
+ * 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).
+ *
* Project: envoy-client
- * File: ContactSearchSceneController.java
+ * File: GroupCreationScene.java
* Created: 07.06.2020
*
* @author Maximilian Käfer
@@ -35,7 +45,7 @@ public class GroupCreationScene {
private ClearableTextField groupNameField;
@FXML
- private ListView chatList;
+ private ListView userList;
private SceneContext sceneContext;
@@ -43,8 +53,8 @@ public class GroupCreationScene {
@FXML
private void initialize() {
- chatList.setCellFactory(ContactListCellFactory::new);
- chatList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
+ userList.setCellFactory(new ContactListCellFactory<>());
+ userList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
groupNameField.setClearButtonListener(e -> { groupNameField.getTextField().clear(); createButton.setDisable(true); });
}
@@ -56,11 +66,13 @@ public class GroupCreationScene {
*/
public void initializeData(SceneContext sceneContext, LocalDB localDB) {
this.sceneContext = sceneContext;
- Platform.runLater(() -> chatList.getItems()
+ Platform.runLater(() -> userList.getItems()
.addAll(localDB.getChats()
.stream()
- .filter(c -> !(c.getRecipient() instanceof Group))
- .filter(c -> c.getRecipient().getID() != localDB.getUser().getID())
+ .map(Chat::getRecipient)
+ .filter(User.class::isInstance)
+ .filter(not(localDB.getUser()::equals))
+ .map(User.class::cast)
.collect(Collectors.toList())));
}
@@ -71,12 +83,12 @@ public class GroupCreationScene {
*/
@FXML
private void chatListClicked() {
- createButton.setDisable(chatList.getSelectionModel().isEmpty() || groupNameField.getTextField().getText().isBlank());
+ createButton.setDisable(userList.getSelectionModel().isEmpty() || groupNameField.getTextField().getText().isBlank());
}
/**
* Checks, whether the {@code createButton} can be enabled because text is
- * present in the textfield.
+ * present in the text field.
*
* @since Envoy Client v0.1-beta
*/
@@ -97,8 +109,8 @@ public class GroupCreationScene {
new Alert(AlertType.ERROR, "The entered group name is not valid (" + Bounds.CONTACT_NAME_PATTERN + ")").showAndWait();
groupNameField.getTextField().clear();
} else {
- eventBus.dispatch(new SendEvent(new GroupCreation(name,
- chatList.getSelectionModel().getSelectedItems().stream().map(c -> c.getRecipient().getID()).collect(Collectors.toSet()))));
+ eventBus.dispatch(new SendEvent(
+ new GroupCreation(name, userList.getSelectionModel().getSelectedItems().stream().map(User::getID).collect(Collectors.toSet()))));
new Alert(AlertType.INFORMATION, String.format("Group '%s' successfully created.", name)).showAndWait();
sceneContext.pop();
}
diff --git a/client/src/main/java/envoy/client/ui/listcell/ChatListCellFactory.java b/client/src/main/java/envoy/client/ui/listcell/ChatListCellFactory.java
new file mode 100644
index 0000000..01b09ae
--- /dev/null
+++ b/client/src/main/java/envoy/client/ui/listcell/ChatListCellFactory.java
@@ -0,0 +1,44 @@
+package envoy.client.ui.listcell;
+
+import javafx.scene.control.ListCell;
+import javafx.scene.control.ListView;
+
+import envoy.client.data.Chat;
+
+/**
+ * Project: envoy-client
+ * File: ChatListCellFactory.java
+ * Created: 28.03.2020
+ *
+ * @author Kai S. K. Engelbart
+ * @since Envoy Client v0.1-beta
+ */
+public class ChatListCellFactory extends ListCell {
+
+ private final ListView listView;
+
+ /**
+ * @param listView the list view inside which this cell is contained
+ * @since Envoy Client v0.1-beta
+ */
+ public ChatListCellFactory(ListView listView) { this.listView = listView; }
+
+ /**
+ * Displays the name of a contact. If the contact is a user, their online status
+ * is displayed as well.
+ *
+ * @since Envoy Client v0.1-beta
+ */
+ @Override
+ protected void updateItem(Chat chat, boolean empty) {
+ super.updateItem(chat, empty);
+ if (empty || chat.getRecipient() == null) {
+ setText(null);
+ setGraphic(null);
+ } else {
+ final var control = new ChatControl(chat);
+ prefWidthProperty().bind(listView.widthProperty().subtract(40));
+ setGraphic(control);
+ }
+ }
+}
diff --git a/client/src/main/java/envoy/client/ui/listcell/ContactControl.java b/client/src/main/java/envoy/client/ui/listcell/ContactControl.java
index c573e69..c8f9107 100644
--- a/client/src/main/java/envoy/client/ui/listcell/ContactControl.java
+++ b/client/src/main/java/envoy/client/ui/listcell/ContactControl.java
@@ -28,7 +28,6 @@ public class ContactControl extends VBox {
// Name label
final var nameLabel = new Label(contact.getName());
- nameLabel.setWrapText(true);
getChildren().add(nameLabel);
// Online status (user) or member count (group)
diff --git a/client/src/main/java/envoy/client/ui/listcell/ContactListCellFactory.java b/client/src/main/java/envoy/client/ui/listcell/ContactListCellFactory.java
index 3d34aa9..5e00c1a 100644
--- a/client/src/main/java/envoy/client/ui/listcell/ContactListCellFactory.java
+++ b/client/src/main/java/envoy/client/ui/listcell/ContactListCellFactory.java
@@ -2,43 +2,51 @@ package envoy.client.ui.listcell;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
+import javafx.util.Callback;
-import envoy.client.data.Chat;
+import envoy.data.Contact;
/**
* Project: envoy-client
- * File: UserListCell.java
- * Created: 28.03.2020
+ * File: ContactListCellFactory.java
+ * Created: 13.07.2020
*
* @author Kai S. K. Engelbart
+ * @param the type of contact to display
* @since Envoy Client v0.1-beta
*/
-public class ContactListCellFactory extends ListCell {
-
- private final ListView listView;
+public class ContactListCellFactory implements Callback, ListCell> {
/**
- * @param listView the list view inside which this cell is contained
- * @since Envoy Client v0.1-beta
- */
- public ContactListCellFactory(ListView listView) { this.listView = listView; }
-
- /**
- * Displays the name of a contact. If the contact is a user, their online status
- * is displayed as well.
+ * Wraps a the {@link ContactControl} inside a list cell.
*
+ * @param the type of contact to display
* @since Envoy Client v0.1-beta
*/
- @Override
- protected void updateItem(Chat chat, boolean empty) {
- super.updateItem(chat, empty);
- if (empty || chat.getRecipient() == null) {
- setText(null);
- setGraphic(null);
- } else {
- final var control = new ChatControl(chat);
- prefWidthProperty().bind(listView.widthProperty().subtract(40));
- setGraphic(control);
+ public static final class ContactListCell extends ListCell {
+
+ private final ListView listView;
+
+ /**
+ * @param listView the list view containing this list cell
+ * @since Envoy Client v0.1-beta
+ */
+ public ContactListCell(ListView listView) { this.listView = listView; }
+
+ @Override
+ protected void updateItem(T contact, boolean empty) {
+ super.updateItem(contact, empty);
+ if (empty || contact == null) {
+ setText(null);
+ setGraphic(null);
+ } else {
+ final var control = new ContactControl(contact);
+ prefWidthProperty().bind(listView.widthProperty().subtract(40));
+ setGraphic(control);
+ }
}
}
+
+ @Override
+ public ListCell call(ListView listView) { return new ContactListCell<>(listView); }
}
diff --git a/client/src/main/resources/fxml/ContactSearchScene.fxml b/client/src/main/resources/fxml/ContactSearchScene.fxml
index a88430c..a77dfd6 100644
--- a/client/src/main/resources/fxml/ContactSearchScene.fxml
+++ b/client/src/main/resources/fxml/ContactSearchScene.fxml
@@ -8,19 +8,12 @@
-
+
-
-
+
+
@@ -29,14 +22,10 @@
-
+
-
-
+
@@ -56,8 +43,7 @@
-
+
@@ -65,9 +51,7 @@
-
+
diff --git a/client/src/main/resources/fxml/GroupCreationScene.fxml b/client/src/main/resources/fxml/GroupCreationScene.fxml
index 0085b03..897955b 100644
--- a/client/src/main/resources/fxml/GroupCreationScene.fxml
+++ b/client/src/main/resources/fxml/GroupCreationScene.fxml
@@ -11,18 +11,12 @@
-
+
-
+
@@ -30,9 +24,7 @@
-
+
@@ -45,9 +37,7 @@
-
+
@@ -57,16 +47,12 @@
-
+
-
+
@@ -74,10 +60,7 @@
-
+
diff --git a/common/src/main/java/envoy/event/contact/ContactSearchRequest.java b/common/src/main/java/envoy/event/contact/ContactSearchRequest.java
deleted file mode 100644
index e7c4f22..0000000
--- a/common/src/main/java/envoy/event/contact/ContactSearchRequest.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package envoy.event.contact;
-
-import envoy.event.Event;
-
-/**
- * Requests a contact search from the server.
- *
- * Project: envoy-common
- * File: ContactSearchRequest.java
- * Created: 05.02.2020
- *
- * @author Maximilian Käfer
- * @since Envoy Common v0.2-alpha
- */
-public class ContactSearchRequest extends Event {
-
- private static final long serialVersionUID = 0L;
-
- /**
- * Initializes a {@link ContactSearchRequest}.
- *
- * @param searchPhrase the search phrase to use in the contact search
- * @since Envoy Common v0.2-alpha
- */
- public ContactSearchRequest(String searchPhrase) { super(searchPhrase); }
-}
diff --git a/common/src/main/java/envoy/event/contact/UserSearchRequest.java b/common/src/main/java/envoy/event/contact/UserSearchRequest.java
new file mode 100644
index 0000000..51889d7
--- /dev/null
+++ b/common/src/main/java/envoy/event/contact/UserSearchRequest.java
@@ -0,0 +1,26 @@
+package envoy.event.contact;
+
+import envoy.event.Event;
+
+/**
+ * Requests a user search from the server.
+ *
+ * Project: envoy-common
+ * File: UserSearchRequest.java
+ * Created: 05.02.2020
+ *
+ * @author Maximilian Käfer
+ * @since Envoy Common v0.2-alpha
+ */
+public class UserSearchRequest extends Event {
+
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * Initializes a {@link UserSearchRequest}.
+ *
+ * @param searchPhrase the search phrase to use in the user search
+ * @since Envoy Common v0.2-alpha
+ */
+ public UserSearchRequest(String searchPhrase) { super(searchPhrase); }
+}
diff --git a/common/src/main/java/envoy/event/contact/ContactSearchResult.java b/common/src/main/java/envoy/event/contact/UserSearchResult.java
similarity index 53%
rename from common/src/main/java/envoy/event/contact/ContactSearchResult.java
rename to common/src/main/java/envoy/event/contact/UserSearchResult.java
index aed8b12..a2aa4f7 100644
--- a/common/src/main/java/envoy/event/contact/ContactSearchResult.java
+++ b/common/src/main/java/envoy/event/contact/UserSearchResult.java
@@ -2,28 +2,28 @@ package envoy.event.contact;
import java.util.List;
-import envoy.data.Contact;
+import envoy.data.User;
import envoy.event.Event;
/**
- * Contains a list of {@link Contact}s for which a search was performed.
- *
+ * Contains a list of users for which a search has been requested.
+ *
* Project: envoy-common
- * File: ContactSearchResult.java
+ * File: UserSearchResult.java
* Created: 11 Feb 2020
*
* @author Kai S. K. Engelbart
* @since Envoy Common v0.2-alpha
*/
-public class ContactSearchResult extends Event> {
+public class UserSearchResult extends Event> {
private static final long serialVersionUID = 0L;
/**
- * Creates an instance of {@link ContactSearchResult}.
+ * Creates an instance of {@link UserSearchResult}.
*
* @param users the users found during the search
* @since Envoy Common v0.2-alpha
*/
- public ContactSearchResult(List users) { super(users); }
+ public UserSearchResult(List users) { super(users); }
}
diff --git a/server/src/main/java/envoy/server/processors/ContactSearchProcessor.java b/server/src/main/java/envoy/server/processors/ContactSearchProcessor.java
index 6118276..a00d72b 100755
--- a/server/src/main/java/envoy/server/processors/ContactSearchProcessor.java
+++ b/server/src/main/java/envoy/server/processors/ContactSearchProcessor.java
@@ -4,8 +4,8 @@ import java.io.IOException;
import java.util.stream.Collectors;
import envoy.data.Contact;
-import envoy.event.contact.ContactSearchRequest;
-import envoy.event.contact.ContactSearchResult;
+import envoy.event.contact.UserSearchRequest;
+import envoy.event.contact.UserSearchResult;
import envoy.server.data.PersistenceManager;
import envoy.server.data.User;
import envoy.server.net.ConnectionManager;
@@ -20,7 +20,7 @@ import envoy.server.net.ObjectWriteProxy;
* @author Maximilian Käfer
* @since Envoy Server Standalone v0.1-alpha
*/
-public class ContactSearchProcessor implements ObjectProcessor {
+public class ContactSearchProcessor implements ObjectProcessor {
/**
* Writes a list of contacts to the client containing all {@link Contact}s
@@ -30,9 +30,9 @@ public class ContactSearchProcessor implements ObjectProcessor getInputClass() { return ContactSearchRequest.class; }
+ public Class getInputClass() { return UserSearchRequest.class; }
}