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