Merge branch 'develop' into f/handshake_sync
This commit is contained in:
commit
714892dbd4
36
client/src/main/java/envoy/client/ui/ListViewRefresh.java
Normal file
36
client/src/main/java/envoy/client/ui/ListViewRefresh.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -33,9 +33,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;
|
||||||
@ -152,7 +150,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(); });
|
||||||
// TODO: Increment unread counter for group messages with status < RECEIVED
|
// TODO: Increment unread counter for group messages with status < RECEIVED
|
||||||
} else if (message.getSenderID() != localDB.getUser().getID() && message.getStatus() == RECEIVED) chat.incrementUnreadAmount();
|
} else if (message.getSenderID() != localDB.getUser().getID() && message.getStatus() == RECEIVED) chat.incrementUnreadAmount();
|
||||||
|
|
||||||
@ -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 -> {
|
||||||
@ -365,6 +364,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));
|
||||||
@ -423,9 +423,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";
|
||||||
@ -508,7 +508,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
|
||||||
|
@ -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);
|
||||||
}));
|
}));
|
||||||
|
@ -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
|
||||||
@ -66,7 +70,8 @@ public class GroupCreationScene {
|
|||||||
* @since Envoy Client v0.1-beta
|
* @since Envoy Client v0.1-beta
|
||||||
*/
|
*/
|
||||||
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,14 +114,47 @@ 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 {
|
||||||
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();
|
new Alert(AlertType.INFORMATION, String.format("Group '%s' successfully created.", name)).showAndWait();
|
||||||
sceneContext.pop();
|
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(
|
||||||
|
new GroupCreation(name, userList.getSelectionModel().getSelectedItems().stream().map(User::getID).collect(Collectors.toSet()))));
|
||||||
|
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
|
||||||
private void backButtonClicked() { sceneContext.pop(); }
|
private void backButtonClicked() { sceneContext.pop(); }
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user