diff --git a/src/main/java/envoy/client/Chat.java b/src/main/java/envoy/client/Chat.java index b75175f..e67eb34 100644 --- a/src/main/java/envoy/client/Chat.java +++ b/src/main/java/envoy/client/Chat.java @@ -1,11 +1,14 @@ package envoy.client; +import java.io.IOException; import java.io.Serializable; +import envoy.client.net.Client; import envoy.client.ui.list.ComponentListModel; import envoy.data.Message; import envoy.data.Message.MessageStatus; import envoy.data.User; +import envoy.event.MessageStatusChangeEvent; /** * Represents a chat between two {@link User}s
@@ -49,14 +52,25 @@ public class Chat implements Serializable { * {@code READ} starting from the bottom and stopping once a read message is * found. * + * @param client the client instance used to notify the server about the message + * status changes + * @throws IOException if a {@link MessageStatusChangeEvent} could not be + * delivered to the server * @since Envoy v0.3-alpha */ - public void read() { - for (int i = model.size() - 1; i >= 0; --i) - if (model.get(i).getSenderId() == recipient.getId()) { - if (model.get(i).getStatus() == MessageStatus.READ) break; - else model.get(i).setStatus(MessageStatus.READ); + public void read(Client client) throws IOException { + for (int i = model.size() - 1; i >= 0; --i) { + final Message m = model.get(i); + if (m.getSenderId() == recipient.getId()) { + if (m.getStatus() == MessageStatus.READ) break; + else { + m.setStatus(MessageStatus.READ); + + // TODO: Cache events in offline mode + client.sendEvent(new MessageStatusChangeEvent(m)); + } } + } } /** diff --git a/src/main/java/envoy/client/net/Client.java b/src/main/java/envoy/client/net/Client.java index 0920aab..31b1d59 100644 --- a/src/main/java/envoy/client/net/Client.java +++ b/src/main/java/envoy/client/net/Client.java @@ -13,7 +13,9 @@ import envoy.client.Config; import envoy.client.database.LocalDb; import envoy.client.util.EnvoyLog; import envoy.data.*; +import envoy.event.Event; import envoy.event.IdGeneratorRequest; +import envoy.event.MessageStatusChangeEvent; import envoy.util.SerializationUtils; /** @@ -42,7 +44,7 @@ public class Client implements Closeable { // Configuration and logging private static final Config config = Config.getInstance(); - private static final Logger logger = EnvoyLog.getLogger(Client.class.getSimpleName()); + private static final Logger logger = EnvoyLog.getLogger(Client.class.getSimpleName()); /** * Enters the online mode by acquiring a user ID from the server. As a @@ -103,19 +105,21 @@ public class Client implements Closeable { // Relay cached unread messages cache.setProcessor(receivedMessageProcessor); - // TODO: Status handling + // Process message status changes + receiver.registerProcessor(MessageStatusChangeEvent.class, new MessageStatusChangeEventProcessor()); // Process message ID generation receiver.registerProcessor(IdGenerator.class, localDb::setIdGenerator); - // Request a generator if none is present + // Request a generator if none is present or the existing one is consumed if (!localDb.hasIdGenerator() || !localDb.getIdGenerator().hasNext()) requestIdGenerator(); return cache; } /** - * Sends a message to the server. + * Sends a message to the server. The message's status will be incremented once + * it was delivered successfully. * * @param message the message to send * @throws IOException if the message does not reach the server @@ -126,6 +130,14 @@ public class Client implements Closeable { message.nextStatus(); } + /** + * Sends an event to the server. + * + * @param evt the event to send + * @throws IOException if the event did not reach the server + */ + public void sendEvent(Event evt) throws IOException { writeObject(evt); } + /** * Requests a new {@link IdGenerator} from the server. * diff --git a/src/main/java/envoy/client/net/MessageStatusChangeEventProcessor.java b/src/main/java/envoy/client/net/MessageStatusChangeEventProcessor.java new file mode 100644 index 0000000..1ceecf1 --- /dev/null +++ b/src/main/java/envoy/client/net/MessageStatusChangeEventProcessor.java @@ -0,0 +1,38 @@ +package envoy.client.net; + +import java.util.function.Consumer; +import java.util.logging.Logger; + +import envoy.client.util.EnvoyLog; +import envoy.data.Message.MessageStatus; +import envoy.event.EventBus; +import envoy.event.MessageStatusChangeEvent; + +/** + * Project: envoy-client
+ * File: MessageStatusChangeEventProcessor.java
+ * Created: 4 Feb 2020
+ * + * @author Kai S. K. Engelbart + * @since Envoy v0.3-alpha + */ +public class MessageStatusChangeEventProcessor implements Consumer { + + private static final Logger logger = EnvoyLog.getLogger(MessageStatusChangeEventProcessor.class.getSimpleName()); + + /** + * Dispatches a {@link MessageStatusChangeEvent} if the status is + * {@code RECEIVED} or {@code READ}. + * + * @param evt the status change event + * @since Envoy v0.3-alpha + */ + @Override + public void accept(MessageStatusChangeEvent evt) { + if (evt.get().ordinal() <= MessageStatus.RECEIVED.ordinal()) logger.info("Received invalid message status change " + evt); + else { + logger.info("Received " + evt.toString()); + EventBus.getInstance().dispatch(evt); + } + } +} diff --git a/src/main/java/envoy/client/ui/ChatWindow.java b/src/main/java/envoy/client/ui/ChatWindow.java index b92446a..aba5726 100644 --- a/src/main/java/envoy/client/ui/ChatWindow.java +++ b/src/main/java/envoy/client/ui/ChatWindow.java @@ -3,6 +3,7 @@ package envoy.client.ui; import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; +import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; @@ -19,9 +20,11 @@ import envoy.client.ui.list.ComponentList; import envoy.client.ui.settings.SettingsScreen; import envoy.client.util.EnvoyLog; import envoy.data.Message; +import envoy.data.Message.MessageStatus; import envoy.data.MessageBuilder; import envoy.data.User; import envoy.event.EventBus; +import envoy.event.MessageStatusChangeEvent; /** * Project: envoy-client
@@ -173,7 +176,12 @@ public class ChatWindow extends JFrame { currentChat = localDb.getChats().stream().filter(chat -> chat.getRecipient().getId() == user.getId()).findFirst().get(); // Read current Chat - currentChat.read(); + try { + currentChat.read(client); + } catch (IOException e) { + e.printStackTrace(); + logger.log(Level.WARNING, "Could notify server about message status change", e); + } // Set chat title textPane.setText(currentChat.getRecipient().getName()); @@ -213,6 +221,26 @@ public class ChatWindow extends JFrame { repaint(); }); + // Listen to message status changes + EventBus.getInstance().register(MessageStatusChangeEvent.class, (evt) -> { + final long id = ((MessageStatusChangeEvent) evt).getId(); + final MessageStatus status = (MessageStatus) evt.get(); + + for (Chat c : localDb.getChats()) + for (Message m : c.getModel()) + if (m.getId() == id) { + + // Update message status + m.setStatus(status); + + // Update model and scroll down if current chat + if (c == currentChat) { + messageList.setModel(currentChat.getModel()); + scrollPane.setChatOpened(true); + } + } + }); + revalidate(); } @@ -286,10 +314,7 @@ public class ChatWindow extends JFrame { if (!localDb.getIdGenerator().hasNext()) client.requestIdGenerator(); } catch (Exception e) { - JOptionPane.showMessageDialog(this, - "Error sending message:\n" + e.toString(), - "Message sending error", - JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(this, "Error sending message:\n" + e.toString(), "Message sending error", JOptionPane.ERROR_MESSAGE); e.printStackTrace(); } } @@ -320,9 +345,7 @@ public class ChatWindow extends JFrame { * @param client the {@link Client} used to send and receive messages * @since Envoy v0.2-alpha */ - public void setClient(Client client) { - this.client = client; - } + public void setClient(Client client) { this.client = client; } /** * Sets the {@link LocalDb} used by this {@link ChatWindow}. After