Listening to message status changes, sending READ status updates
This commit is contained in:
		| @@ -1,11 +1,14 @@ | |||||||
| package envoy.client; | package envoy.client; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
| import java.io.Serializable; | import java.io.Serializable; | ||||||
|  |  | ||||||
|  | import envoy.client.net.Client; | ||||||
| import envoy.client.ui.list.ComponentListModel; | import envoy.client.ui.list.ComponentListModel; | ||||||
| import envoy.data.Message; | import envoy.data.Message; | ||||||
| import envoy.data.Message.MessageStatus; | import envoy.data.Message.MessageStatus; | ||||||
| import envoy.data.User; | import envoy.data.User; | ||||||
|  | import envoy.event.MessageStatusChangeEvent; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Represents a chat between two {@link User}s <br> |  * Represents a chat between two {@link User}s <br> | ||||||
| @@ -49,14 +52,25 @@ public class Chat implements Serializable { | |||||||
| 	 * {@code READ} starting from the bottom and stopping once a read message is | 	 * {@code READ} starting from the bottom and stopping once a read message is | ||||||
| 	 * found. | 	 * 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 | 	 * @since Envoy v0.3-alpha | ||||||
| 	 */ | 	 */ | ||||||
| 	public void read() { | 	public void read(Client client) throws IOException { | ||||||
| 		for (int i = model.size() - 1; i >= 0; --i) | 		for (int i = model.size() - 1; i >= 0; --i) { | ||||||
| 			if (model.get(i).getSenderId() == recipient.getId()) { | 			final Message m = model.get(i); | ||||||
| 				if (model.get(i).getStatus() == MessageStatus.READ) break; | 			if (m.getSenderId() == recipient.getId()) { | ||||||
| 				else model.get(i).setStatus(MessageStatus.READ); | 				if (m.getStatus() == MessageStatus.READ) break; | ||||||
|  | 				else { | ||||||
|  | 					m.setStatus(MessageStatus.READ); | ||||||
|  |  | ||||||
|  | 					// TODO: Cache events in offline mode | ||||||
|  | 					client.sendEvent(new MessageStatusChangeEvent(m)); | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
|   | |||||||
| @@ -13,7 +13,9 @@ import envoy.client.Config; | |||||||
| import envoy.client.database.LocalDb; | import envoy.client.database.LocalDb; | ||||||
| import envoy.client.util.EnvoyLog; | import envoy.client.util.EnvoyLog; | ||||||
| import envoy.data.*; | import envoy.data.*; | ||||||
|  | import envoy.event.Event; | ||||||
| import envoy.event.IdGeneratorRequest; | import envoy.event.IdGeneratorRequest; | ||||||
|  | import envoy.event.MessageStatusChangeEvent; | ||||||
| import envoy.util.SerializationUtils; | import envoy.util.SerializationUtils; | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -42,7 +44,7 @@ public class Client implements Closeable { | |||||||
|  |  | ||||||
| 	// Configuration and logging | 	// Configuration and logging | ||||||
| 	private static final Config	config	= Config.getInstance(); | 	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 | 	 * 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 | 		// Relay cached unread messages | ||||||
| 		cache.setProcessor(receivedMessageProcessor); | 		cache.setProcessor(receivedMessageProcessor); | ||||||
|  |  | ||||||
| 		// TODO: Status handling | 		// Process message status changes | ||||||
|  | 		receiver.registerProcessor(MessageStatusChangeEvent.class, new MessageStatusChangeEventProcessor()); | ||||||
|  |  | ||||||
| 		// Process message ID generation | 		// Process message ID generation | ||||||
| 		receiver.registerProcessor(IdGenerator.class, localDb::setIdGenerator); | 		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(); | 		if (!localDb.hasIdGenerator() || !localDb.getIdGenerator().hasNext()) requestIdGenerator(); | ||||||
|  |  | ||||||
| 		return cache; | 		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 | 	 * @param message the message to send | ||||||
| 	 * @throws IOException if the message does not reach the server | 	 * @throws IOException if the message does not reach the server | ||||||
| @@ -126,6 +130,14 @@ public class Client implements Closeable { | |||||||
| 		message.nextStatus(); | 		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. | 	 * Requests a new {@link IdGenerator} from the server. | ||||||
| 	 * | 	 * | ||||||
|   | |||||||
| @@ -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: <strong>envoy-client</strong><br> | ||||||
|  |  * File: <strong>MessageStatusChangeEventProcessor.java</strong><br> | ||||||
|  |  * Created: <strong>4 Feb 2020</strong><br> | ||||||
|  |  * | ||||||
|  |  * @author Kai S. K. Engelbart | ||||||
|  |  * @since Envoy v0.3-alpha | ||||||
|  |  */ | ||||||
|  | public class MessageStatusChangeEventProcessor implements Consumer<MessageStatusChangeEvent> { | ||||||
|  |  | ||||||
|  | 	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); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -3,6 +3,7 @@ package envoy.client.ui; | |||||||
| import java.awt.*; | import java.awt.*; | ||||||
| import java.awt.event.KeyAdapter; | import java.awt.event.KeyAdapter; | ||||||
| import java.awt.event.KeyEvent; | import java.awt.event.KeyEvent; | ||||||
|  | import java.io.IOException; | ||||||
| import java.util.logging.Level; | import java.util.logging.Level; | ||||||
| import java.util.logging.Logger; | import java.util.logging.Logger; | ||||||
|  |  | ||||||
| @@ -19,9 +20,11 @@ import envoy.client.ui.list.ComponentList; | |||||||
| import envoy.client.ui.settings.SettingsScreen; | import envoy.client.ui.settings.SettingsScreen; | ||||||
| import envoy.client.util.EnvoyLog; | import envoy.client.util.EnvoyLog; | ||||||
| import envoy.data.Message; | import envoy.data.Message; | ||||||
|  | import envoy.data.Message.MessageStatus; | ||||||
| import envoy.data.MessageBuilder; | import envoy.data.MessageBuilder; | ||||||
| import envoy.data.User; | import envoy.data.User; | ||||||
| import envoy.event.EventBus; | import envoy.event.EventBus; | ||||||
|  | import envoy.event.MessageStatusChangeEvent; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Project: <strong>envoy-client</strong><br> |  * Project: <strong>envoy-client</strong><br> | ||||||
| @@ -173,7 +176,12 @@ public class ChatWindow extends JFrame { | |||||||
| 				currentChat = localDb.getChats().stream().filter(chat -> chat.getRecipient().getId() == user.getId()).findFirst().get(); | 				currentChat = localDb.getChats().stream().filter(chat -> chat.getRecipient().getId() == user.getId()).findFirst().get(); | ||||||
|  |  | ||||||
| 				// Read current Chat | 				// 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 | 				// Set chat title | ||||||
| 				textPane.setText(currentChat.getRecipient().getName()); | 				textPane.setText(currentChat.getRecipient().getName()); | ||||||
| @@ -213,6 +221,26 @@ public class ChatWindow extends JFrame { | |||||||
| 			repaint(); | 			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(); | 		revalidate(); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -286,10 +314,7 @@ public class ChatWindow extends JFrame { | |||||||
| 			if (!localDb.getIdGenerator().hasNext()) client.requestIdGenerator(); | 			if (!localDb.getIdGenerator().hasNext()) client.requestIdGenerator(); | ||||||
|  |  | ||||||
| 		} catch (Exception e) { | 		} catch (Exception e) { | ||||||
| 			JOptionPane.showMessageDialog(this, | 			JOptionPane.showMessageDialog(this, "Error sending message:\n" + e.toString(), "Message sending error", JOptionPane.ERROR_MESSAGE); | ||||||
| 					"Error sending message:\n" + e.toString(), |  | ||||||
| 					"Message sending error", |  | ||||||
| 					JOptionPane.ERROR_MESSAGE); |  | ||||||
| 			e.printStackTrace(); | 			e.printStackTrace(); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -320,9 +345,7 @@ public class ChatWindow extends JFrame { | |||||||
| 	 * @param client the {@link Client} used to send and receive messages | 	 * @param client the {@link Client} used to send and receive messages | ||||||
| 	 * @since Envoy v0.2-alpha | 	 * @since Envoy v0.2-alpha | ||||||
| 	 */ | 	 */ | ||||||
| 	public void setClient(Client client) { | 	public void setClient(Client client) { this.client = client; } | ||||||
| 		this.client = client; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Sets the {@link LocalDb} used by this {@link ChatWindow}. After | 	 * Sets the {@link LocalDb} used by this {@link ChatWindow}. After | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user