Merge branch 'develop' into f/new_ui

This commit is contained in:
DieGurke 2020-07-16 22:11:52 +02:00
commit a3d368762e
4 changed files with 90 additions and 15 deletions

View File

@ -0,0 +1,36 @@
package envoy.client.ui;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
/**
* This is a utility class that provides access to a refreshing mechanism for
* elements that were added without notifying the underlying {@link ListView}.
* <p>
* Project: <strong>envoy-client</strong><br>
* File: <strong>ListViewRefresh.java</strong><br>
* Created: <strong>16.07.2020</strong><br>
*
* @author Leon Hofmeister
* @since Envoy Client v0.1-beta
*/
public final class ListViewRefresh {
private ListViewRefresh() {}
/**
* Deeply refreshes a {@code listview}, meaning it recomputes every single of
* its {@link ListCell}s.
* <p>
* While it does work, it is <b>not the most efficient algorithm</b> possible.
*
* @param toRefresh the listView to refresh
* @param <T> the type of its {@code listcells}
* @since Envoy Client v0.1-beta
*/
public static <T> void deepRefresh(ListView<T> toRefresh) {
final var items = toRefresh.getItems();
toRefresh.setItems(null);
toRefresh.setItems(items);
}
}

View File

@ -31,9 +31,7 @@ import envoy.client.data.audio.AudioRecorder;
import envoy.client.event.MessageCreationEvent; import envoy.client.event.MessageCreationEvent;
import envoy.client.net.Client; import envoy.client.net.Client;
import envoy.client.net.WriteProxy; import envoy.client.net.WriteProxy;
import envoy.client.ui.IconUtil; import envoy.client.ui.*;
import envoy.client.ui.Restorable;
import envoy.client.ui.SceneContext;
import envoy.client.ui.listcell.ChatControl; import envoy.client.ui.listcell.ChatControl;
import envoy.client.ui.listcell.ListCellFactory; import envoy.client.ui.listcell.ListCellFactory;
import envoy.client.ui.listcell.MessageControl; import envoy.client.ui.listcell.MessageControl;
@ -154,7 +152,7 @@ public final class ChatScene implements Restorable {
} catch (final IOException e1) { } catch (final IOException e1) {
logger.log(Level.WARNING, "Could not read current chat: ", e1); logger.log(Level.WARNING, "Could not read current chat: ", e1);
} }
Platform.runLater(() -> { messageList.refresh(); scrollToMessageListEnd(); }); Platform.runLater(() -> { ListViewRefresh.deepRefresh(messageList); scrollToMessageListEnd(); });
} else chat.incrementUnreadAmount(); } else chat.incrementUnreadAmount();
// Moving chat with most recent unreadMessages to the top // Moving chat with most recent unreadMessages to the top
Platform.runLater(() -> { Platform.runLater(() -> {
@ -168,8 +166,9 @@ public final class ChatScene implements Restorable {
// Listen to message status changes // Listen to message status changes
eventBus.register(MessageStatusChange.class, e -> localDB.getMessage(e.getID()).ifPresent(message -> { eventBus.register(MessageStatusChange.class, e -> localDB.getMessage(e.getID()).ifPresent(message -> {
message.setStatus(e.get()); message.setStatus(e.get());
// Update UI if in current chat // Update UI if in current chat and the current user was the sender of the
if (currentChat != null && message.getSenderID() == currentChat.getRecipient().getID()) Platform.runLater(messageList::refresh); // message
if (currentChat != null && message.getSenderID() == client.getSender().getID()) Platform.runLater(messageList::refresh);
})); }));
eventBus.register(GroupMessageStatusChange.class, e -> localDB.getMessage(e.getID()).ifPresent(groupMessage -> { eventBus.register(GroupMessageStatusChange.class, e -> localDB.getMessage(e.getID()).ifPresent(groupMessage -> {
@ -186,7 +185,7 @@ public final class ChatScene implements Restorable {
.filter(c -> c.getRecipient().getID() == e.getID()) .filter(c -> c.getRecipient().getID() == e.getID())
.findAny() .findAny()
.map(Chat::getRecipient) .map(Chat::getRecipient)
.ifPresent(u -> { ((User) u).setStatus(e.get()); Platform.runLater(chatList::refresh); })); .ifPresent(u -> { ((User) u).setStatus(e.get()); Platform.runLater(() -> ListViewRefresh.deepRefresh(chatList)); }));
// Listen to contacts changes // Listen to contacts changes
eventBus.register(ContactOperation.class, e -> { eventBus.register(ContactOperation.class, e -> {
@ -377,6 +376,7 @@ public final class ChatScene implements Restorable {
try { try {
final var fileBytes = Files.readAllBytes(file.toPath()); final var fileBytes = Files.readAllBytes(file.toPath());
pendingAttachment = new Attachment(fileBytes, type); pendingAttachment = new Attachment(fileBytes, type);
checkPostConditions(false);
// Setting the preview image as image of the attachmentView // Setting the preview image as image of the attachmentView
if (type == AttachmentType.PICTURE) if (type == AttachmentType.PICTURE)
attachmentView.setImage(new Image(new ByteArrayInputStream(fileBytes), DEFAULT_ICON_SIZE, DEFAULT_ICON_SIZE, true, true)); attachmentView.setImage(new Image(new ByteArrayInputStream(fileBytes), DEFAULT_ICON_SIZE, DEFAULT_ICON_SIZE, true, true));
@ -435,9 +435,9 @@ public final class ChatScene implements Restorable {
|| !settings.isEnterToSend() && e.getCode() == KeyCode.ENTER && e.isControlDown()); || !settings.isEnterToSend() && e.getCode() == KeyCode.ENTER && e.isControlDown());
} }
private void checkPostConditions(boolean sendKeyPressed) { private void checkPostConditions(boolean postMessage) {
if (!postingPermanentlyDisabled) { if (!postingPermanentlyDisabled) {
if (!postButton.isDisabled() && sendKeyPressed) postMessage(); if (!postButton.isDisabled() && postMessage) postMessage();
postButton.setDisable(messageTextArea.getText().isBlank() && pendingAttachment == null || currentChat == null); postButton.setDisable(messageTextArea.getText().isBlank() && pendingAttachment == null || currentChat == null);
} else { } else {
final var noMoreMessaging = "Go online to send messages"; final var noMoreMessaging = "Go online to send messages";
@ -520,7 +520,7 @@ public final class ChatScene implements Restorable {
localDB.getChats().remove(currentChat); localDB.getChats().remove(currentChat);
localDB.getChats().add(0, currentChat); localDB.getChats().add(0, currentChat);
}); });
messageList.refresh(); ListViewRefresh.deepRefresh(messageList);
scrollToMessageListEnd(); scrollToMessageListEnd();
// Request a new ID generator if all IDs were used // Request a new ID generator if all IDs were used

View File

@ -118,7 +118,8 @@ public class ContactSearchScene {
final var event = new ContactOperation(user, ElementOperation.ADD); final var event = new ContactOperation(user, ElementOperation.ADD);
// Sends the event to the server // Sends the event to the server
eventBus.dispatch(new SendEvent(event)); eventBus.dispatch(new SendEvent(event));
// Updates the UI // Removes the chosen user and updates the UI
userList.getItems().remove(user);
eventBus.dispatch(event); eventBus.dispatch(event);
logger.log(Level.INFO, "Added user " + user); logger.log(Level.INFO, "Added user " + user);
})); }));

View File

@ -16,6 +16,8 @@ import envoy.client.ui.ClearableTextField;
import envoy.client.ui.SceneContext; import envoy.client.ui.SceneContext;
import envoy.client.ui.listcell.ContactControl; import envoy.client.ui.listcell.ContactControl;
import envoy.client.ui.listcell.ListCellFactory; import envoy.client.ui.listcell.ListCellFactory;
import envoy.data.Contact;
import envoy.data.Group;
import envoy.data.User; import envoy.data.User;
import envoy.event.EventBus; import envoy.event.EventBus;
import envoy.event.GroupCreation; import envoy.event.GroupCreation;
@ -50,6 +52,8 @@ public class GroupCreationScene {
private SceneContext sceneContext; private SceneContext sceneContext;
private LocalDB localDB;
private static final EventBus eventBus = EventBus.getInstance(); private static final EventBus eventBus = EventBus.getInstance();
@FXML @FXML
@ -67,6 +71,7 @@ public class GroupCreationScene {
*/ */
public void initializeData(SceneContext sceneContext, LocalDB localDB) { public void initializeData(SceneContext sceneContext, LocalDB localDB) {
this.sceneContext = sceneContext; this.sceneContext = sceneContext;
this.localDB = localDB;
Platform.runLater(() -> userList.getItems() Platform.runLater(() -> userList.getItems()
.addAll(localDB.getChats() .addAll(localDB.getChats()
.stream() .stream()
@ -109,12 +114,45 @@ public class GroupCreationScene {
if (!Bounds.isValidContactName(name)) { if (!Bounds.isValidContactName(name)) {
new Alert(AlertType.ERROR, "The entered group name is not valid (" + Bounds.CONTACT_NAME_PATTERN + ")").showAndWait(); new Alert(AlertType.ERROR, "The entered group name is not valid (" + Bounds.CONTACT_NAME_PATTERN + ")").showAndWait();
groupNameField.getTextField().clear(); groupNameField.getTextField().clear();
} else if (groupNameAlreadyPresent(name)) {
final var alert = new Alert(AlertType.WARNING, "You already have a group with that name.", ButtonType.OK, ButtonType.CANCEL);
alert.setTitle("Create Group?");
alert.setHeaderText("Proceed?");
alert.showAndWait().filter(btn -> btn == ButtonType.OK).ifPresent(btn -> createGroup(name));
} else { } else {
new Alert(AlertType.INFORMATION, String.format("Group '%s' successfully created.", name)).showAndWait();
createGroup(name);
}
}
/**
* Creates a new group with the given name and all selected members.<br>
* Additionally pops the scene automatically.
*
* @param name the chosen group name
* @since Envoy Client v0.1-beta
*/
private void createGroup(String name) {
eventBus.dispatch(new SendEvent( eventBus.dispatch(new SendEvent(
new GroupCreation(name, userList.getSelectionModel().getSelectedItems().stream().map(User::getID).collect(Collectors.toSet())))); 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(); sceneContext.pop();
} }
/**
* 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);
} }
@FXML @FXML