From 6f8859c3fd9f7c2d24e73c9e6fbdd14364f20a00 Mon Sep 17 00:00:00 2001 From: delvh Date: Sat, 25 Jul 2020 16:26:13 +0200 Subject: [PATCH] Added IsWriting event on common, server and partially on client side MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit additionally fixed NullPointerException in ContactSearchScene and typo in Javadoc PS: this is the 1000th commit in Envoy! 🥳 🎉 --- .../src/main/java/envoy/client/data/Chat.java | 36 +++++++++---- .../main/java/envoy/client/net/Client.java | 3 ++ .../envoy/client/ui/controller/ChatScene.java | 21 +++++++- .../ui/controller/ContactSearchScene.java | 6 ++- .../client/ui/controller/LoginScene.java | 6 +-- .../envoy/client/ui/listcell/ChatControl.java | 4 +- .../src/main/java/envoy/event/IsWriting.java | 53 +++++++++++++++++++ .../src/main/java/envoy/server/Startup.java | 3 +- .../server/processors/IsWritingProcessor.java | 37 +++++++++++++ 9 files changed, 151 insertions(+), 18 deletions(-) create mode 100644 common/src/main/java/envoy/event/IsWriting.java create mode 100644 server/src/main/java/envoy/server/processors/IsWritingProcessor.java diff --git a/client/src/main/java/envoy/client/data/Chat.java b/client/src/main/java/envoy/client/data/Chat.java index b1c2beb..d35993a 100644 --- a/client/src/main/java/envoy/client/data/Chat.java +++ b/client/src/main/java/envoy/client/data/Chat.java @@ -9,6 +9,7 @@ import java.util.Objects; import envoy.client.net.WriteProxy; import envoy.data.*; import envoy.data.Message.MessageStatus; +import envoy.event.IsWriting; import envoy.event.MessageStatusChange; /** @@ -31,6 +32,12 @@ public class Chat implements Serializable { protected int unreadAmount; + /** + * Counts the milliseconds until another {@link IsWriting} event will be sent if + * something is typed. + */ + protected transient long lastWritingEvent; + private static final long serialVersionUID = 1L; /** @@ -41,16 +48,14 @@ public class Chat implements Serializable { * @param recipient the user who receives the messages * @since Envoy Client v0.1-alpha */ - public Chat(Contact recipient) { - this.recipient = recipient; - } + public Chat(Contact recipient) { this.recipient = recipient; } @Override public String toString() { return String.format("Chat[recipient=%s,messages=%d]", recipient, messages.size()); } /** * Generates a hash code based on the recipient. - * + * * @since Envoy Client v0.1-beta */ @Override @@ -58,14 +63,14 @@ public class Chat implements Serializable { /** * Tests equality to another object based on the recipient. - * + * * @since Envoy Client v0.1-beta */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof Chat)) return false; - Chat other = (Chat) obj; + final Chat other = (Chat) obj; return Objects.equals(recipient, other.recipient); } @@ -101,7 +106,7 @@ public class Chat implements Serializable { /** * Inserts a message at the correct place according to its creation date. - * + * * @param message the message to insert * @since Envoy Client v0.1-beta */ @@ -116,13 +121,13 @@ public class Chat implements Serializable { /** * Increments the amount of unread messages. - * + * * @since Envoy Client v0.1-beta */ public void incrementUnreadAmount() { unreadAmount++; } /** - * @return the amount of unread mesages in this chat + * @return the amount of unread messages in this chat * @since Envoy Client v0.1-beta */ public int getUnreadAmount() { return unreadAmount; } @@ -150,4 +155,17 @@ public class Chat implements Serializable { * @since Envoy Client v0.1-beta */ public boolean isGroupChat() { return recipient instanceof Group; } + + /** + * @return the last known time a {@link IsWriting} event has been sent + * @since Envoy Client v0.2-beta + */ + public long getLastWritingEvent() { return lastWritingEvent; } + + /** + * Sets the {@code lastWritingEvent} to {@code System#currentTimeMillis()}. + * + * @since Envoy Client v0.2-beta + */ + public void lastWritingEventWasNow() { lastWritingEvent = System.currentTimeMillis(); } } diff --git a/client/src/main/java/envoy/client/net/Client.java b/client/src/main/java/envoy/client/net/Client.java index a01468f..dbbe5e2 100644 --- a/client/src/main/java/envoy/client/net/Client.java +++ b/client/src/main/java/envoy/client/net/Client.java @@ -155,6 +155,9 @@ public class Client implements Closeable { // Process group size changes receiver.registerProcessor(GroupResize.class, evt -> { localDB.updateGroup(evt); eventBus.dispatch(evt); }); + // Process IsWriting events + receiver.registerProcessor(IsWriting.class, eventBus::dispatch); + // Send event eventBus.register(SendEvent.class, evt -> { try { 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 a05150c..38c94ff 100644 --- a/client/src/main/java/envoy/client/ui/controller/ChatScene.java +++ b/client/src/main/java/envoy/client/ui/controller/ChatScene.java @@ -33,6 +33,7 @@ import envoy.client.data.audio.AudioRecorder; import envoy.client.data.commands.SystemCommandBuilder; import envoy.client.data.commands.SystemCommandsMap; import envoy.client.event.MessageCreationEvent; +import envoy.client.event.SendEvent; import envoy.client.net.Client; import envoy.client.net.WriteProxy; import envoy.client.ui.*; @@ -444,10 +445,28 @@ public final class ChatScene implements Restorable { private void checkKeyCombination(KeyEvent e) { // Checks whether the text is too long messageTextUpdated(); + // Sending an IsWriting event if none has been sent for + // IsWriting#millisecondsActive + if (client.isOnline() && currentChat.getLastWritingEvent() + IsWriting.millisecondsActive <= System.currentTimeMillis()) { + eventBus.dispatch(new SendEvent(new IsWriting(getChatID(), currentChat.getRecipient().getID(), client.getSender().getName()))); + currentChat.lastWritingEventWasNow(); + } // Automatic sending of messages via (ctrl +) enter checkPostConditions(e); } + /** + * Returns the id that should be used to send things to the server: + * the id of 'our' {@link User} if the recipient of that object is another User, + * else the id of the {@link Group} 'our' user is sending to. + * + * @return an id that can be sent to the server + * @since Envoy Client v0.2-beta + */ + private long getChatID() { + return currentChat.getRecipient() instanceof User ? client.getSender().getID() : currentChat.getRecipient().getID(); + } + /** * @param e the keys that have been pressed * @since Envoy Client v0.1-beta @@ -515,7 +534,7 @@ public final class ChatScene implements Restorable { updateInfoLabel("You need to go online to send more messages", "infoLabel-error"); return; } - final var text = messageTextArea.getText().strip(); + final var text = messageTextArea.getText().strip(); if (!messageTextAreaCommands.executeIfAnyPresent(text)) try { // Creating the message and its metadata final var builder = new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator()) 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 4583aad..3e6071a 100644 --- a/client/src/main/java/envoy/client/ui/controller/ContactSearchScene.java +++ b/client/src/main/java/envoy/client/ui/controller/ContactSearchScene.java @@ -60,8 +60,10 @@ public class ContactSearchScene { private final Consumer handler = e -> { final var contact = e.get(); - if (e.getOperationType() == ElementOperation.ADD) Platform - .runLater(() -> { userList.getItems().remove(contact); if (currentlySelectedUser.equals(contact) && alert.isShowing()) alert.close(); }); + if (e.getOperationType() == ElementOperation.ADD) Platform.runLater(() -> { + userList.getItems().remove(contact); + if (currentlySelectedUser != null && currentlySelectedUser.equals(contact) && alert.isShowing()) alert.close(); + }); }; private static final EventBus eventBus = EventBus.getInstance(); diff --git a/client/src/main/java/envoy/client/ui/controller/LoginScene.java b/client/src/main/java/envoy/client/ui/controller/LoginScene.java index 177524b..2b03a5b 100644 --- a/client/src/main/java/envoy/client/ui/controller/LoginScene.java +++ b/client/src/main/java/envoy/client/ui/controller/LoginScene.java @@ -138,7 +138,7 @@ public final class LoginScene { localDB.setUser(localDB.getUsers().get(identifier)); localDB.initializeUserStorage(); localDB.loadUserData(); - } catch (Exception e) { + } catch (final Exception e) { // User storage empty, wrong user name etc. -> default lastSync } return localDB.getLastSync(); @@ -161,7 +161,7 @@ public final class LoginScene { try { // Try entering offline mode localDB.loadUsers(); - final User clientUser = (User) localDB.getUsers().get(credentials.getIdentifier()); + final User clientUser = localDB.getUsers().get(credentials.getIdentifier()); if (clientUser == null) throw new EnvoyException("Could not enter offline mode: user name unknown"); client.setSender(clientUser); loadChatScene(); @@ -223,7 +223,7 @@ public final class LoginScene { // Initialize status tray icon final var trayIcon = new StatusTrayIcon(sceneContext.getStage()); settings.getItems().get("hideOnClose").setChangeHandler(c -> { - if (((Boolean) c)) trayIcon.show(); + if ((Boolean) c) trayIcon.show(); else trayIcon.hide(); }); } diff --git a/client/src/main/java/envoy/client/ui/listcell/ChatControl.java b/client/src/main/java/envoy/client/ui/listcell/ChatControl.java index c2e942c..9e20eb7 100644 --- a/client/src/main/java/envoy/client/ui/listcell/ChatControl.java +++ b/client/src/main/java/envoy/client/ui/listcell/ChatControl.java @@ -31,12 +31,12 @@ public class ChatControl extends HBox { // Unread messages if (chat.getUnreadAmount() != 0) { - Region spacing = new Region(); + final Region spacing = new Region(); setHgrow(spacing, Priority.ALWAYS); getChildren().add(spacing); final var unreadMessagesLabel = new Label(Integer.toString(chat.getUnreadAmount())); unreadMessagesLabel.setMinSize(15, 15); - var vBox2 = new VBox(); + final var vBox2 = new VBox(); vBox2.setAlignment(Pos.CENTER_RIGHT); unreadMessagesLabel.setAlignment(Pos.CENTER); unreadMessagesLabel.getStyleClass().add("unreadMessagesAmount"); diff --git a/common/src/main/java/envoy/event/IsWriting.java b/common/src/main/java/envoy/event/IsWriting.java new file mode 100644 index 0000000..7efe32b --- /dev/null +++ b/common/src/main/java/envoy/event/IsWriting.java @@ -0,0 +1,53 @@ +package envoy.event; + +/** + * This event should be sent when a user wrote anything in a chat. + *

+ * Project: envoy-client
+ * File: IsWriting.java
+ * Created: 24.07.2020
+ * + * @author Leon Hofmeister + * @since Envoy Client v0.2-beta + */ +public class IsWriting extends Event { + + private final long destinationID; + + private final String displayName; + + private static final long serialVersionUID = 1L; + + /** + * The number of milliseconds that this event will be active.
+ * Currently set to 3.5 seconds. + */ + public static final int millisecondsActive = 3500; + + /** + * Creates a new {@code IsWriting} with originator and recipient. + * + * @param sourceID the id of the originator + * @param displayName the name of the originator + * @param destinationID the id of the contact the user wrote to + * @since Envoy Client v0.2-beta + */ + public IsWriting(Long sourceID, long destinationID, String displayName) { + super(sourceID); + this.destinationID = destinationID; + this.displayName = displayName; + } + + /** + * @return the id of the contact in whose chat the user wrote something + * @since Envoy Client v0.2-beta + */ + public long getDestinationID() { return destinationID; } + + /** + * @return the name of the originator to display + * @since Envoy Common v0.2-beta + */ + public String getDisplayName() { return displayName; } + +} diff --git a/server/src/main/java/envoy/server/Startup.java b/server/src/main/java/envoy/server/Startup.java index 489a060..a906d0e 100755 --- a/server/src/main/java/envoy/server/Startup.java +++ b/server/src/main/java/envoy/server/Startup.java @@ -69,7 +69,8 @@ public class Startup { new UserStatusChangeProcessor(), new IDGeneratorRequestProcessor(), new UserSearchProcessor(), - new ContactOperationProcessor()))); + new ContactOperationProcessor(), + new IsWritingProcessor()))); // Initialize the current message ID final PersistenceManager persistenceManager = PersistenceManager.getInstance(); diff --git a/server/src/main/java/envoy/server/processors/IsWritingProcessor.java b/server/src/main/java/envoy/server/processors/IsWritingProcessor.java new file mode 100644 index 0000000..aec1c24 --- /dev/null +++ b/server/src/main/java/envoy/server/processors/IsWritingProcessor.java @@ -0,0 +1,37 @@ +package envoy.server.processors; + +import java.io.IOException; + +import envoy.event.IsWriting; +import envoy.server.data.PersistenceManager; +import envoy.server.data.User; +import envoy.server.net.ConnectionManager; +import envoy.server.net.ObjectWriteProxy; + +/** + * This processor handles incoming {@link IsWriting}s. + *

+ * Project: envoy-server-standalone
+ * File: IsWritingProcessor.java
+ * Created: 24.07.2020
+ * + * @author Leon Hofmeister + * @since Envoy Server v0.2-beta + */ +public class IsWritingProcessor implements ObjectProcessor { + + private static final ConnectionManager connectionManager = ConnectionManager.getInstance(); + private static final PersistenceManager persistenceManager = PersistenceManager.getInstance(); + + @Override + public Class getInputClass() { return IsWriting.class; } + + @Override + public void process(IsWriting input, long socketID, ObjectWriteProxy writeProxy) throws IOException { + final var contact = persistenceManager.getContactByID(input.get()); + if (contact instanceof User) { + final var destinationID = input.getDestinationID(); + if (connectionManager.isOnline(destinationID)) writeProxy.write(connectionManager.getSocketID(destinationID), input); + } else writeProxy.writeToOnlineContacts(contact.getContacts(), input); + } +}